on xenial help sign in
Merge unity8
a0df9f6Albert Astals Cid
CMakeLists.txt+3
@@ -56,6 +56,8 @@ find_package(Qt5DBus 5.2 REQUIRED)
56find_package(Qt5Concurrent 5.2 REQUIRED)
56find_package(Qt5Concurrent 5.2 REQUIRED)
57find_package(Qt5Sql 5.2 REQUIRED)
57find_package(Qt5Sql 5.2 REQUIRED)
58
58
59+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
60+
59# Standard install paths
61# Standard install paths
60include(GNUInstallDirs)
62include(GNUInstallDirs)
61
63
@@ -111,6 +113,7 @@ set(MOCK_INDICATOR_SERVICE_APP unity-mock-indicator-service)
111include_directories(
113include_directories(
112 ${CMAKE_SOURCE_DIR}/include
114 ${CMAKE_SOURCE_DIR}/include
113 ${CMAKE_BINARY_DIR}/include
115 ${CMAKE_BINARY_DIR}/include
116+ ${APPLICATION_API_INCLUDE_DIRS}
114)
117)
115
118
116# add subdirectories to build
119# add subdirectories to build
CODING+4
@@ -158,6 +158,10 @@ To run the tests for a single component/class under gdb (so you can catch SEGFAU
158
158
159$ make gdbtestComponentName
159$ make gdbtestComponentName
160
160
161+To run a single test function of a single component/class:
162+
163+$ make testComponentName FUNCTION="TestCaseName::functionName"
164+
161To run a test application that allow you to manually test or to simply try out
165To run a test application that allow you to manually test or to simply try out
162a given component:
166a given component:
163
167
cmake/modules/QmlTest.cmake+10−8
@@ -155,10 +155,11 @@ macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS ENVVAR)
155 set(testCommand
155 set(testCommand
156 ${ENVVAR}
156 ${ENVVAR}
157 LD_LIBRARY_PATH=${LD_PATH}
157 LD_LIBRARY_PATH=${LD_PATH}
158− dbus-launch
158+ dbus-test-runner
159+ -m 300
159− ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
160+ -t ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
160− -o ${CMAKE_BINARY_DIR}/test${CLASS_NAME}.xml,xunitxml
161+ -p -o -p ${CMAKE_BINARY_DIR}/test${CLASS_NAME}.xml,xunitxml
161− -o -,txt)
162+ -p -o -p -,txt)
162
163
163 add_qmltest_target(test${CLASS_NAME} "${testCommand}" FALSE TRUE)
164 add_qmltest_target(test${CLASS_NAME} "${testCommand}" FALSE TRUE)
164 add_dependencies(test${CLASS_NAME} ${CLASS_NAME}TestExec ${DEPS})
165 add_dependencies(test${CLASS_NAME} ${CLASS_NAME}TestExec ${DEPS})
@@ -173,10 +174,11 @@ macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS ENVVAR)
173 ${ENVVAR}
174 ${ENVVAR}
174 LD_LIBRARY_PATH=${LD_PATH}
175 LD_LIBRARY_PATH=${LD_PATH}
175 xvfb-run --server-args "-screen 0 1024x768x24" --auto-servernum
176 xvfb-run --server-args "-screen 0 1024x768x24" --auto-servernum
176− dbus-launch
177+ dbus-test-runner
178+ -m 300
177− ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
179+ -t ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
178− -o ${CMAKE_BINARY_DIR}/test${CLASS_NAME}.xml,xunitxml
180+ -p -o -p ${CMAKE_BINARY_DIR}/test${CLASS_NAME}.xml,xunitxml
179− -o -,txt)
181+ -p -o -p -,txt)
180
182
181 add_qmltest_target(xvfbtest${CLASS_NAME} "${xvfbtestCommand}" FALSE TRUE)
183 add_qmltest_target(xvfbtest${CLASS_NAME} "${xvfbtestCommand}" FALSE TRUE)
182 add_dependencies(qmluitests xvfbtest${CLASS_NAME})
184 add_dependencies(qmluitests xvfbtest${CLASS_NAME})
data/unity8-dash.conf−5
@@ -18,11 +18,6 @@ respawn
18env APP_ID=unity8-dash
18env APP_ID=unity8-dash
19
19
20pre-start script
20pre-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−
26 initctl emit scope-ui-starting
21 initctl emit scope-ui-starting
27end script
22end script
28
23
data/unity8-dash.desktop.in+1
@@ -7,3 +7,4 @@ Terminal=false
7Icon=
7Icon=
8NoDisplay=true
8NoDisplay=true
9X-Ubuntu-Touch=true
9X-Ubuntu-Touch=true
10+X-Ubuntu-Supported-Orientations=primary
data/unity8.conf+1
@@ -54,4 +54,5 @@ post-stop script
54 initctl set-env --global MIR_SOCKET=$UNITY_MIR_SOCKET
54 initctl set-env --global MIR_SOCKET=$UNITY_MIR_SOCKET
55 initctl unset-env --global UNITY_MIR_SOCKET
55 initctl unset-env --global UNITY_MIR_SOCKET
56 initctl unset-env --global MIR_SERVER_PROMPT_FILE
56 initctl unset-env --global MIR_SERVER_PROMPT_FILE
57+ initctl emit application-end
57end script
58end script
debian/changelog+157
@@ -1,3 +1,160 @@
1+unity8 (8.10+15.10.20150616.1-0ubuntu1) wily; urgency=medium
2+
3+ [ Albert Astals Cid ]
4+ * Bind running to the proper object.visible
5+ * Do not use template in generated card creator code
6+ * Fix regression introduced by the concierge change
7+ * Take into account the extra margin on top when calculating the
8+ display margin
9+ * workaround oxide 1.7 issue (LP: #1459142)
10+
11+ [ CI Train Bot ]
12+ * New rebuild forced.
13+
14+ [ Federico Gimenez ]
15+ * On the autopilot helpers, swipe if the item to open from the dash is
16+ not visible. unity8-autopilot dependencies now require ubuntu-ui-
17+ toolkit-autopilot (>= 1.2.1485+15.04.20150417.1-0ubuntu1)
18+
19+ [ Florian Boucault ]
20+ * Notifications: shaped icon was stretching the icon instead of
21+ respecting its aspect ratio and filling the whole shape. (LP:
22+ #1450229)
23+
24+ [ Josh Arenson ]
25+ * Implement bare-bones shellMode=greeter and shellMode=shell
26+
27+ [ Leo Arias ]
28+ * On the autopilot helpers, swipe if the item to open from the dash is
29+ not visible. unity8-autopilot dependencies now require ubuntu-ui-
30+ toolkit-autopilot (>= 1.2.1485+15.04.20150417.1-0ubuntu1)
31+
32+ [ Leonardo Arias Fonseca ]
33+ * On the autopilot helpers, swipe if the item to open from the dash is
34+ not visible. unity8-autopilot dependencies now require ubuntu-ui-
35+ toolkit-autopilot (>= 1.2.1485+15.04.20150417.1-0ubuntu1)
36+
37+ [ Michael Zanetti ]
38+ * add info on how to run a single function in make test
39+
40+ [ Richard Huddie ]
41+ * Slow down the speed of autopilot helpers used to swipe to a
42+ different scope to improve the reliability. (LP: #1461059, #1457047)
43+
44+ -- CI Train Bot <ci-train-bot@canonical.com> Tue, 16 Jun 2015 15:12:59 +0000
45+
46+unity8 (8.10+15.10.20150612-0ubuntu2) wily; urgency=medium
47+
48+ * No-change rebuild against Qt 5.4.2.
49+
50+ -- Timo Jyrinki <timo-jyrinki@ubuntu.com> Mon, 15 Jun 2015 12:33:07 +0300
51+
52+unity8 (8.10+15.10.20150612-0ubuntu1) wily; urgency=medium
53+
54+ [ Michał Sawicz ]
55+ * Implement full-shell rotation (LP: #1210199)
56+
57+ [ CI Train Bot ]
58+ * New rebuild forced.
59+ * Resync trunk.
60+
61+ [ Daniel d'Andrada ]
62+ * Implemented autopilot-test and fake-sensors for shell-rotation.
63+
64+ [ Mirco Müller ]
65+ * Implemented autopilot-test and fake-sensors for shell-rotation.
66+
67+ -- CI Train Bot <ci-train-bot@canonical.com> Fri, 12 Jun 2015 16:07:47 +0000
68+
69+unity8 (8.02+15.10.20150603.1-0ubuntu1) wily; urgency=medium
70+
71+ [ CI Train Bot ]
72+ * New rebuild forced.
73+ * Resync trunk.
74+
75+ [ Renato Araujo Oliveira Filho ]
76+ * Set the device led color to green. (LP: #1450894)
77+
78+ -- CI Train Bot <ci-train-bot@canonical.com> Wed, 03 Jun 2015 21:42:35 +0000
79+
80+unity8 (8.02+15.10.20150518.1-0ubuntu1) wily; urgency=medium
81+
82+ [ Albert Astals Cid ]
83+ * Add overrides to override functions
84+ * Implement "rating-edit" preview widget (LP: #1318144)
85+ * Make the DashContent::test_mainNavigation test more stable (LP:
86+ #1450809)
87+ * Use art height as implicitHeight when the header is overlayed and
88+ there's no summary
89+
90+ [ CI Train Bot ]
91+ * New rebuild forced.
92+ * Resync trunk.
93+
94+ [ Daniel d'Andrada ]
95+ * Introducing FloatingFlickable
96+ * Make Ubuntu.Gestures components install TouchRegistry by themselves
97+
98+ [ Michael Terry ]
99+ * Fix a possible crash in our PAM threading code. (LP: #1425362) (LP:
100+ #1425362)
101+ * Fix the lockscreen becoming unresponsive after testing an app on the
102+ device from QtCreator. (LP: #1435364)
103+
104+ [ Nick Dedekind ]
105+ * Fixed desktop stage app focus.
106+ * Fixed issue in laggy indicator autpilot tests (LP: #1446846)
107+
108+ -- CI Train Bot <ci-train-bot@canonical.com> Mon, 18 May 2015 23:04:39 +0000
109+
110+unity8 (8.02+15.04.20150511-0ubuntu1) vivid; urgency=medium
111+
112+ [ Albert Astals Cid ]
113+ * Workarounds for concierge mode.
114+
115+ [ CI Train Bot ]
116+ * New rebuild forced.
117+ * Resync trunk.
118+
119+ -- CI Train Bot <ci-train-bot@canonical.com> Mon, 11 May 2015 08:29:58 +0000
120+
121+unity8 (8.02+15.04.20150505-0ubuntu1) vivid; urgency=medium
122+
123+ [ Albert Astals Cid ]
124+ * Make runtests fake a test error if make fails
125+ * Make the test more stable
126+ * Use dbus-test-runner instead of dbus-launch
127+
128+ [ Daniel d'Andrada ]
129+ * DirectionalDragArea: improvements & API grooming (LP: #1417920)
130+ * Fix EdgeDragEvaluator when a drag can happen both ways
131+ (Direction.Horizontal)
132+
133+ [ Josh Arenson ]
134+ * Remove panelHeight property as it is unused.
135+
136+ [ Leo Arias ]
137+ * Initial clean up of the autopilot tests set up. Removed the touch
138+ device from the test case. Moved the restart of unity to a fixture.
139+ Removed the unused DragMixin. Updated the setUpClass to use
140+ process_helpers. Removed the workaround for bug #1238417, already
141+ fixed. Use the toolkit helper to set the testability environment
142+ variable. Fixed the indicators base class that was restarting unity
143+ twice. (LP: #1238417, #1447206)
144+ * Use the base class from the toolkit in autopilot tests.
145+
146+ [ Michael Zanetti ]
147+ * emit application-stop when we're going down (LP: #1326513)
148+
149+ [ Michał Sawicz ]
150+ * UNITY_SCOPES_LIST is no more
151+
152+ [ handsome_feng<445865575@qq.com> ]
153+ * When click the favorite scope in Dash Manager , it just return to
154+ the corresponding scope page. (LP: #1447056)
155+
156+ -- CI Train Bot <ci-train-bot@canonical.com> Tue, 05 May 2015 14:47:24 +0000
157+
1unity8 (8.02+15.04.20150422-0ubuntu1) vivid; urgency=medium
158unity8 (8.02+15.04.20150422-0ubuntu1) vivid; urgency=medium
2
159
3 [ Albert Astals Cid ]
160 [ Albert Astals Cid ]
debian/control+6−5
@@ -12,6 +12,7 @@ Build-Depends: cmake,
12# append :native to g++-4.9 so we don't try to run armhf g++
12# append :native to g++-4.9 so we don't try to run armhf g++
13# on an x86 CPU for eaxmple, when cross-compiling.
13# on an x86 CPU for eaxmple, when cross-compiling.
14 g++-4.9:native,
14 g++-4.9:native,
15+ libandroid-properties-dev,
15 graphviz,
16 graphviz,
16 gsettings-ubuntu-schemas (>= 0.0.2+14.10.20140815),
17 gsettings-ubuntu-schemas (>= 0.0.2+14.10.20140815),
17 libconnectivity-qt1-dev,
18 libconnectivity-qt1-dev,
@@ -27,7 +28,7 @@ Build-Depends: cmake,
27 libqmenumodel-dev (>= 0.2.9),
28 libqmenumodel-dev (>= 0.2.9),
28 libqt5xmlpatterns5-dev,
29 libqt5xmlpatterns5-dev,
29 libsystemsettings-dev,
30 libsystemsettings-dev,
30− libunity-api-dev (>= 7.96),
31+ libunity-api-dev (>= 7.97),
31 libusermetricsoutput1-dev,
32 libusermetricsoutput1-dev,
32 libxcb1-dev,
33 libxcb1-dev,
33 pkg-config,
34 pkg-config,
@@ -94,7 +95,7 @@ Depends: gsettings-desktop-schemas,
94 qml-module-qtquick-xmllistmodel,
95 qml-module-qtquick-xmllistmodel,
95 qml-module-qtsysteminfo,
96 qml-module-qtsysteminfo,
96 qtdeclarative5-gsettings1.0,
97 qtdeclarative5-gsettings1.0,
97− qtdeclarative5-qtmir-plugin (>= 0.4.4),
98+ qtdeclarative5-qtmir-plugin (>= 0.4.5),
98 qtdeclarative5-ubuntu-telephony0.1,
99 qtdeclarative5-ubuntu-telephony0.1,
99 qtdeclarative5-ubuntu-web-plugin,
100 qtdeclarative5-ubuntu-web-plugin,
100 ubuntu-system-settings,
101 ubuntu-system-settings,
@@ -124,7 +125,7 @@ Depends: qml-module-qtquick-layouts,
124 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.1.1239) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.1.1239),
125 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.1.1239) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.1.1239),
125 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
126 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
126 ubuntu-thumbnailer-impl-0,
127 ubuntu-thumbnailer-impl-0,
127− unity-application-impl-4,
128+ unity-application-impl-6,
128 unity-notifications-impl-3,
129 unity-notifications-impl-3,
129 unity-plugin-scopes | unity-scopes-impl,
130 unity-plugin-scopes | unity-scopes-impl,
130 unity-scopes-impl-6,
131 unity-scopes-impl-6,
@@ -150,7 +151,7 @@ Depends: autopilot-qt5 (>= 1.4),
150 python3-fixtures,
151 python3-fixtures,
151 python3-gi,
152 python3-gi,
152 qttestability-autopilot (>= 1.4),
153 qttestability-autopilot (>= 1.4),
153− ubuntu-ui-toolkit-autopilot,
154+ ubuntu-ui-toolkit-autopilot (>= 1.2.1485+15.04.20150417.1-0ubuntu1),
154 unity-scope-click,
155 unity-scope-click,
155 unity8 (= ${source:Version}),
156 unity8 (= ${source:Version}),
156 unity8-fake-env (= ${source:Version}),
157 unity8-fake-env (= ${source:Version}),
@@ -168,7 +169,7 @@ Pre-Depends: ${misc:Pre-Depends},
168Depends: ${misc:Depends},
169Depends: ${misc:Depends},
169 ${shlibs:Depends},
170 ${shlibs:Depends},
170Provides: unity-application-impl,
171Provides: unity-application-impl,
171− unity-application-impl-4,
172+ unity-application-impl-6,
172Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1)
173Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1)
173Description: Fake environment for running Unity 8 shell
174Description: Fake environment for running Unity 8 shell
174 Provides fake implementations of some QML modules used by Unity 8 shell
175 Provides fake implementations of some QML modules used by Unity 8 shell
debian/unity8.install+3
@@ -8,6 +8,9 @@ usr/share/applications/unity8.desktop
8usr/share/unity8/Greeter
8usr/share/unity8/Greeter
9usr/share/unity8/Launcher
9usr/share/unity8/Launcher
10usr/share/unity8/Panel
10usr/share/unity8/Panel
11+usr/share/unity8/Rotation
12+usr/share/unity8/DeviceConfiguration.qml
13+usr/share/unity8/OrientedShell.qml
11usr/share/unity8/Shell.qml
14usr/share/unity8/Shell.qml
12usr/share/unity8/Stages
15usr/share/unity8/Stages
13usr/share/unity8/Tutorial
16usr/share/unity8/Tutorial
libs/UbuntuGestures/CMakeLists.txt+1
@@ -5,6 +5,7 @@ set(UbuntuGestures_SOURCES
5 CandidateInactivityTimer.cpp
5 CandidateInactivityTimer.cpp
6 DebugHelpers.cpp
6 DebugHelpers.cpp
7 Timer.cpp
7 Timer.cpp
8+ TimeSource.cpp
8 TouchOwnershipEvent.cpp
9 TouchOwnershipEvent.cpp
9 TouchRegistry.cpp
10 TouchRegistry.cpp
10 UnownedTouchEvent.cpp
11 UnownedTouchEvent.cpp
libs/UbuntuGestures/CandidateInactivityTimer.cpp+7−2
@@ -19,12 +19,12 @@
19namespace UbuntuGestures {
19namespace UbuntuGestures {
20
20
21CandidateInactivityTimer::CandidateInactivityTimer(int touchId, QQuickItem *candidate,
21CandidateInactivityTimer::CandidateInactivityTimer(int touchId, QQuickItem *candidate,
22− AbstractTimerFactory &timerFactory, QObject *parent)
22+ AbstractTimer *timer, QObject *parent)
23 : QObject(parent)
23 : QObject(parent)
24+ , m_timer(timer)
24 , m_touchId(touchId)
25 , m_touchId(touchId)
25 , m_candidate(candidate)
26 , m_candidate(candidate)
26{
27{
27− m_timer = timerFactory.createTimer(this);
28 connect(m_timer, &AbstractTimer::timeout,
28 connect(m_timer, &AbstractTimer::timeout,
29 this, &CandidateInactivityTimer::onTimeout);
29 this, &CandidateInactivityTimer::onTimeout);
30 m_timer->setInterval(durationMs);
30 m_timer->setInterval(durationMs);
@@ -32,6 +32,11 @@ CandidateInactivityTimer::CandidateInactivityTimer(int touchId, QQuickItem *cand
32 m_timer->start();
32 m_timer->start();
33}
33}
34
34
35+CandidateInactivityTimer::~CandidateInactivityTimer()
36+{
37+ delete m_timer;
38+}
39+
35void CandidateInactivityTimer::onTimeout()
40void CandidateInactivityTimer::onTimeout()
36{
41{
37 qWarning("[TouchRegistry] Candidate for touch %d defaulted!", m_touchId);
42 qWarning("[TouchRegistry] Candidate for touch %d defaulted!", m_touchId);
libs/UbuntuGestures/CandidateInactivityTimer.h+4−2
@@ -29,10 +29,12 @@ class UBUNTUGESTURES_EXPORT CandidateInactivityTimer : public QObject {
29 Q_OBJECT
29 Q_OBJECT
30public:
30public:
31 CandidateInactivityTimer(int touchId, QQuickItem *candidate,
31 CandidateInactivityTimer(int touchId, QQuickItem *candidate,
32− AbstractTimerFactory &timerFactory,
32+ AbstractTimer *timer,
33 QObject *parent = nullptr);
33 QObject *parent = nullptr);
34
34
35+ virtual ~CandidateInactivityTimer();
36+
35− const int durationMs = 350;
37+ const int durationMs = 1000;
36
38
37Q_SIGNALS:
39Q_SIGNALS:
38 void candidateDefaulted(int touchId, QQuickItem *candidate);
40 void candidateDefaulted(int touchId, QQuickItem *candidate);
plugins/Ubuntu/Gestures/TimeSource.cpp → libs/UbuntuGestures/TimeSource.cpprenamed

The content is the same.

plugins/Ubuntu/Gestures/TimeSource.h → libs/UbuntuGestures/TimeSource.hrenamed+14−4
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 - Canonical Ltd.
2+ * Copyright (C) 2013,2015 Canonical Ltd.
3 *
3 *
4 * This program is free software: you can redistribute it and/or modify it
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License, as
5 * under the terms of the GNU Lesser General Public License, as
@@ -21,14 +21,14 @@
21#ifndef UBUNTUGESTURES_TIMESOURCE_H
21#ifndef UBUNTUGESTURES_TIMESOURCE_H
22#define UBUNTUGESTURES_TIMESOURCE_H
22#define UBUNTUGESTURES_TIMESOURCE_H
23
23
24−#include "UbuntuGesturesQmlGlobal.h"
24+#include "UbuntuGesturesGlobal.h"
25#include <QSharedPointer>
25#include <QSharedPointer>
26
26
27namespace UbuntuGestures {
27namespace UbuntuGestures {
28/*
28/*
29 Interface for a time source.
29 Interface for a time source.
30 */
30 */
31−class UBUNTUGESTURESQML_EXPORT TimeSource {
31+class UBUNTUGESTURES_EXPORT TimeSource {
32public:
32public:
33 virtual ~TimeSource() {}
33 virtual ~TimeSource() {}
34 /* Returns the current time in milliseconds since some reference time in the past. */
34 /* Returns the current time in milliseconds since some reference time in the past. */
@@ -40,7 +40,7 @@ typedef QSharedPointer<TimeSource> SharedTimeSource;
40 Implementation of a time source
40 Implementation of a time source
41 */
41 */
42class RealTimeSourcePrivate;
42class RealTimeSourcePrivate;
43−class RealTimeSource : public TimeSource {
43+class UBUNTUGESTURES_EXPORT RealTimeSource : public TimeSource {
44public:
44public:
45 RealTimeSource();
45 RealTimeSource();
46 virtual ~RealTimeSource();
46 virtual ~RealTimeSource();
@@ -49,6 +49,16 @@ private:
49 RealTimeSourcePrivate *d;
49 RealTimeSourcePrivate *d;
50};
50};
51
51
52+/*
53+ A fake time source, useful for tests
54+ */
55+class FakeTimeSource : public TimeSource {
56+public:
57+ FakeTimeSource() { m_msecsSinceReference = 0; }
58+ qint64 msecsSinceReference() override { return m_msecsSinceReference; }
59+ qint64 m_msecsSinceReference;
60+};
61+
52} // namespace UbuntuGestures
62} // namespace UbuntuGestures
53
63
54#endif // UBUNTUGESTURES_TIMESOURCE_H
64#endif // UBUNTUGESTURES_TIMESOURCE_H
libs/UbuntuGestures/Timer.cpp+54−11
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -58,13 +58,36 @@ void Timer::setSingleShot(bool value)
58
58
59/////////////////////////////////// FakeTimer //////////////////////////////////
59/////////////////////////////////// FakeTimer //////////////////////////////////
60
60
61−FakeTimer::FakeTimer(QObject *parent)
61+FakeTimer::FakeTimer(const SharedTimeSource &timeSource, QObject *parent)
62 : UbuntuGestures::AbstractTimer(parent)
62 : UbuntuGestures::AbstractTimer(parent)
63 , m_interval(0)
63 , m_interval(0)
64 , m_singleShot(false)
64 , m_singleShot(false)
65+ , m_timeSource(timeSource)
65{
66{
66}
67}
67
68
69+void FakeTimer::update()
70+{
71+ if (!isRunning()) {
72+ return;
73+ }
74+
75+ if (m_nextTimeoutTime <= m_timeSource->msecsSinceReference()) {
76+ if (isSingleShot()) {
77+ stop();
78+ } else {
79+ m_nextTimeoutTime += interval();
80+ }
81+ Q_EMIT timeout();
82+ }
83+}
84+
85+void FakeTimer::start()
86+{
87+ AbstractTimer::start();
88+ m_nextTimeoutTime = m_timeSource->msecsSinceReference() + (qint64)interval();
89+}
90+
68int FakeTimer::interval() const
91int FakeTimer::interval() const
69{
92{
70 return m_interval;
93 return m_interval;
@@ -87,23 +110,43 @@ void FakeTimer::setSingleShot(bool value)
87
110
88/////////////////////////////////// FakeTimerFactory //////////////////////////////////
111/////////////////////////////////// FakeTimerFactory //////////////////////////////////
89
112
90−AbstractTimer *FakeTimerFactory::createTimer(QObject *parent)
113+FakeTimerFactory::FakeTimerFactory()
91{
114{
92− FakeTimer *fakeTimer = new FakeTimer(parent);
115+ m_timeSource.reset(new FakeTimeSource);
93−
94− timers.append(fakeTimer);
95−
96− return fakeTimer;
97}
116}
98
117
99−void FakeTimerFactory::makeRunningTimersTimeout()
118+void FakeTimerFactory::updateTime(qint64 targetTime)
100{
119{
120+ qint64 minTimeoutTime = targetTime;
121+
101 for (int i = 0; i < timers.count(); ++i) {
122 for (int i = 0; i < timers.count(); ++i) {
102 FakeTimer *timer = timers[i].data();
123 FakeTimer *timer = timers[i].data();
103− if (timer && timer->isRunning()) {
124+ if (timer && timer->isRunning() && timer->nextTimeoutTime() < minTimeoutTime) {
104− timer->emitTimeout();
125+ minTimeoutTime = timer->nextTimeoutTime();
105 }
126 }
106 }
127 }
128+
129+ m_timeSource->m_msecsSinceReference = minTimeoutTime;
130+
131+ for (int i = 0; i < timers.count(); ++i) {
132+ FakeTimer *timer = timers[i].data();
133+ if (timer) {
134+ timer->update();
135+ }
136+ }
137+
138+ if (m_timeSource->msecsSinceReference() < targetTime) {
139+ updateTime(targetTime);
140+ }
141+}
142+
143+AbstractTimer *FakeTimerFactory::createTimer(QObject *parent)
144+{
145+ FakeTimer *fakeTimer = new FakeTimer(m_timeSource, parent);
146+
147+ timers.append(fakeTimer);
148+
149+ return fakeTimer;
107}
150}
108
151
109} // namespace UbuntuGestures
152} // namespace UbuntuGestures
libs/UbuntuGestures/Timer.h+15−3
@@ -18,6 +18,7 @@
18#define UBUNTUGESTURES_TIMER_H
18#define UBUNTUGESTURES_TIMER_H
19
19
20#include "UbuntuGesturesGlobal.h"
20#include "UbuntuGesturesGlobal.h"
21+#include "TimeSource.h"
21
22
22#include <QObject>
23#include <QObject>
23#include <QPointer>
24#include <QPointer>
@@ -66,17 +67,21 @@ class UBUNTUGESTURES_EXPORT FakeTimer : public AbstractTimer
66{
67{
67 Q_OBJECT
68 Q_OBJECT
68public:
69public:
69− FakeTimer(QObject *parent = nullptr);
70+ FakeTimer(const SharedTimeSource &timeSource, QObject *parent = nullptr);
70
71
71− virtual void emitTimeout() { Q_EMIT timeout(); }
72+ void update();
73+ qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
72
74
73 int interval() const override;
75 int interval() const override;
74 void setInterval(int msecs) override;
76 void setInterval(int msecs) override;
77+ void start() override;
75 bool isSingleShot() const override;
78 bool isSingleShot() const override;
76 void setSingleShot(bool value) override;
79 void setSingleShot(bool value) override;
77private:
80private:
78 int m_interval;
81 int m_interval;
79 bool m_singleShot;
82 bool m_singleShot;
83+ SharedTimeSource m_timeSource;
84+ qint64 m_nextTimeoutTime;
80};
85};
81
86
82class UBUNTUGESTURES_EXPORT AbstractTimerFactory
87class UBUNTUGESTURES_EXPORT AbstractTimerFactory
@@ -95,9 +100,16 @@ public:
95class UBUNTUGESTURES_EXPORT FakeTimerFactory : public AbstractTimerFactory
100class UBUNTUGESTURES_EXPORT FakeTimerFactory : public AbstractTimerFactory
96{
101{
97public:
102public:
103+ FakeTimerFactory();
104+ virtual ~FakeTimerFactory() {}
105+
106+ void updateTime(qint64 msecsSinceReference);
107+ QSharedPointer<TimeSource> timeSource() { return m_timeSource; }
108+
98 AbstractTimer *createTimer(QObject *parent = nullptr) override;
109 AbstractTimer *createTimer(QObject *parent = nullptr) override;
99− void makeRunningTimersTimeout();
100 QList<QPointer<FakeTimer>> timers;
110 QList<QPointer<FakeTimer>> timers;
111+private:
112+ QSharedPointer<FakeTimeSource> m_timeSource;
101};
113};
102
114
103} // namespace UbuntuGestures
115} // namespace UbuntuGestures
libs/UbuntuGestures/TouchRegistry.cpp+28−26
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -41,20 +41,10 @@ using namespace UbuntuGestures;
41TouchRegistry *TouchRegistry::m_instance = nullptr;
41TouchRegistry *TouchRegistry::m_instance = nullptr;
42
42
43TouchRegistry::TouchRegistry(QObject *parent)
43TouchRegistry::TouchRegistry(QObject *parent)
44− : TouchRegistry(parent, new TimerFactory)
45−{
46−}
47−
48−TouchRegistry::TouchRegistry(QObject *parent, AbstractTimerFactory *timerFactory)
49 : QObject(parent)
44 : QObject(parent)
50 , m_inDispatchLoop(false)
45 , m_inDispatchLoop(false)
51− , m_timerFactory(timerFactory)
46+ , m_timerFactory(new TimerFactory)
52{
47{
53− if (m_instance == nullptr) {
54− m_instance = this;
55− } else {
56− qFatal("Cannot have more than one instance of TouchRegistry. It must be a singleton.");
57− }
58}
48}
59
49
60TouchRegistry::~TouchRegistry()
50TouchRegistry::~TouchRegistry()
@@ -64,6 +54,20 @@ TouchRegistry::~TouchRegistry()
64 delete m_timerFactory;
54 delete m_timerFactory;
65}
55}
66
56
57+TouchRegistry *TouchRegistry::instance()
58+{
59+ if (m_instance == nullptr) {
60+ m_instance = new TouchRegistry;
61+ }
62+ return m_instance;
63+}
64+
65+void TouchRegistry::setTimerFactory(AbstractTimerFactory *timerFactory)
66+{
67+ delete m_timerFactory;
68+ m_timerFactory = timerFactory;
69+}
70+
67void TouchRegistry::update(const QTouchEvent *event)
71void TouchRegistry::update(const QTouchEvent *event)
68{
72{
69 #if TOUCHREGISTRY_DEBUG
73 #if TOUCHREGISTRY_DEBUG
@@ -95,10 +99,6 @@ void TouchRegistry::deliverTouchUpdatesToUndecidedCandidatesAndWatchers(const QT
95 // for each point and there should not be many active points at any given moment.
99 // for each point and there should not be many active points at any given moment.
96 // But having three nested for-loops does scare.
100 // But having three nested for-loops does scare.
97
101
98− // TODO: Don't send it to the object that is already receiving the regular event
99− // because QQuickWindow is sending it to him (i.e., he's the touch owner from Qt's point of view)
100− // Problem is, we cannnot easily get this information.
101−
102 const QList<QTouchEvent::TouchPoint> &updatedTouchPoints = event->touchPoints();
102 const QList<QTouchEvent::TouchPoint> &updatedTouchPoints = event->touchPoints();
103
103
104 // Maps an item to the touches in this event he should be informed about.
104 // Maps an item to the touches in this event he should be informed about.
@@ -118,7 +118,9 @@ void TouchRegistry::deliverTouchUpdatesToUndecidedCandidatesAndWatchers(const QT
118 for (int i = 0; i < touchInfo->candidates.count(); ++i) {
118 for (int i = 0; i < touchInfo->candidates.count(); ++i) {
119 CandidateInfo &candidate = touchInfo->candidates[i];
119 CandidateInfo &candidate = touchInfo->candidates[i];
120 Q_ASSERT(!candidate.item.isNull());
120 Q_ASSERT(!candidate.item.isNull());
121+ if (candidate.state != CandidateInfo::InterimOwner) {
121− touchIdsForItems[candidate.item.data()].append(touchInfo->id);
122+ touchIdsForItems[candidate.item.data()].append(touchInfo->id);
123+ }
122 }
124 }
123 }
125 }
124
126
@@ -260,10 +262,10 @@ void TouchRegistry::addCandidateOwnerForTouch(int id, QQuickItem *candidate)
260 // TODO: Check if candidate already exists
262 // TODO: Check if candidate already exists
261
263
262 CandidateInfo candidateInfo;
264 CandidateInfo candidateInfo;
263− candidateInfo.undecided = true;
265+ candidateInfo.state = CandidateInfo::Undecided;
264 candidateInfo.item = candidate;
266 candidateInfo.item = candidate;
265 candidateInfo.inactivityTimer = new CandidateInactivityTimer(id, candidate,
267 candidateInfo.inactivityTimer = new CandidateInactivityTimer(id, candidate,
266− *m_timerFactory,
268+ m_timerFactory->createTimer(),
267 this);
269 this);
268 connect(candidateInfo.inactivityTimer, &CandidateInactivityTimer::candidateDefaulted,
270 connect(candidateInfo.inactivityTimer, &CandidateInactivityTimer::candidateDefaulted,
269 this, &TouchRegistry::rejectCandidateOwnerForTouch);
271 this, &TouchRegistry::rejectCandidateOwnerForTouch);
@@ -301,8 +303,8 @@ void TouchRegistry::removeCandidateOwnerForTouch(int id, QQuickItem *candidate)
301 for (int i = 0; i < touchInfo->candidates.count() && indexRemoved == -1; ++i) {
303 for (int i = 0; i < touchInfo->candidates.count() && indexRemoved == -1; ++i) {
302 CandidateInfo &candidateInfo = touchInfo->candidates[i];
304 CandidateInfo &candidateInfo = touchInfo->candidates[i];
303 if (candidateInfo.item == candidate) {
305 if (candidateInfo.item == candidate) {
304− Q_ASSERT(i > 0 || candidateInfo.undecided);
306+ Q_ASSERT(i > 0 || candidateInfo.state == CandidateInfo::Undecided);
305− if (i == 0 && !candidateInfo.undecided) {
307+ if (i == 0 && candidateInfo.state != CandidateInfo::Undecided) {
306 qCritical("TouchRegistry: touch owner is being removed.");
308 qCritical("TouchRegistry: touch owner is being removed.");
307 }
309 }
308 delete candidateInfo.inactivityTimer;
310 delete candidateInfo.inactivityTimer;
@@ -340,7 +342,7 @@ void TouchRegistry::requestTouchOwnership(int id, QQuickItem *candidate)
340 for (int i = 0; i < touchInfo->candidates.count(); ++i) {
342 for (int i = 0; i < touchInfo->candidates.count(); ++i) {
341 CandidateInfo &candidateInfo = touchInfo->candidates[i];
343 CandidateInfo &candidateInfo = touchInfo->candidates[i];
342 if (candidateInfo.item == candidate) {
344 if (candidateInfo.item == candidate) {
343− candidateInfo.undecided = false;
345+ candidateInfo.state = CandidateInfo::Requested;
344 delete candidateInfo.inactivityTimer;
346 delete candidateInfo.inactivityTimer;
345 candidateInfo.inactivityTimer = nullptr;
347 candidateInfo.inactivityTimer = nullptr;
346 candidateIndex = i;
348 candidateIndex = i;
@@ -351,7 +353,7 @@ void TouchRegistry::requestTouchOwnership(int id, QQuickItem *candidate)
351 // add it as a candidate if not present yet
353 // add it as a candidate if not present yet
352 if (candidateIndex < 0) {
354 if (candidateIndex < 0) {
353 CandidateInfo candidateInfo;
355 CandidateInfo candidateInfo;
354− candidateInfo.undecided = false;
356+ candidateInfo.state = CandidateInfo::InterimOwner;
355 candidateInfo.item = candidate;
357 candidateInfo.item = candidate;
356 candidateInfo.inactivityTimer = nullptr;
358 candidateInfo.inactivityTimer = nullptr;
357 touchInfo->candidates.append(candidateInfo);
359 touchInfo->candidates.append(candidateInfo);
@@ -407,8 +409,8 @@ void TouchRegistry::rejectCandidateOwnerForTouch(int id, QQuickItem *candidate)
407 for (int i = 0; i < touchInfo->candidates.count() && rejectedCandidateIndex == -1; ++i) {
409 for (int i = 0; i < touchInfo->candidates.count() && rejectedCandidateIndex == -1; ++i) {
408 CandidateInfo &candidateInfo = touchInfo->candidates[i];
410 CandidateInfo &candidateInfo = touchInfo->candidates[i];
409 if (candidateInfo.item == candidate) {
411 if (candidateInfo.item == candidate) {
410− Q_ASSERT(i > 0 || candidateInfo.undecided);
412+ Q_ASSERT(i > 0 || candidateInfo.state == CandidateInfo::Undecided);
411− if (i == 0 && !candidateInfo.undecided) {
413+ if (i == 0 && candidateInfo.state != CandidateInfo::Undecided) {
412 qCritical() << "TouchRegistry: Can't reject item (" << (void*)candidate
414 qCritical() << "TouchRegistry: Can't reject item (" << (void*)candidate
413 << ") as it already owns touch" << id;
415 << ") as it already owns touch" << id;
414 return;
416 return;
@@ -467,7 +469,7 @@ void TouchRegistry::TouchInfo::init(int id)
467
469
468bool TouchRegistry::TouchInfo::isOwned() const
470bool TouchRegistry::TouchInfo::isOwned() const
469{
471{
470− return !candidates.isEmpty() && !candidates.first().undecided;
472+ return !candidates.isEmpty() && candidates.first().state != CandidateInfo::Undecided;
471}
473}
472
474
473bool TouchRegistry::TouchInfo::ended() const
475bool TouchRegistry::TouchInfo::ended() const
libs/UbuntuGestures/TouchRegistry.h+26−10
@@ -25,6 +25,7 @@
25
25
26#include "UbuntuGesturesGlobal.h"
26#include "UbuntuGesturesGlobal.h"
27#include "CandidateInactivityTimer.h"
27#include "CandidateInactivityTimer.h"
28+#include "Timer.h"
28#include "Pool.h"
29#include "Pool.h"
29
30
30namespace UbuntuGestures {
31namespace UbuntuGestures {
@@ -79,9 +80,11 @@ namespace UbuntuGestures {
79 If an item wants ownership over touches as soon as he receives the TouchBegin for them, his step 1
80 If an item wants ownership over touches as soon as he receives the TouchBegin for them, his step 1
80 would be instead:
81 would be instead:
81 TouchRegistry::instance()->requestTouchOwnership(touchId, this);
82 TouchRegistry::instance()->requestTouchOwnership(touchId, this);
82− return true;
83+ touchEvent->accept();
84+ He won't get any UnownedTouchEvent for that touch as he is already the interim owner (ie, QQuickWindow
85+ will keep sending touch updates to him already). Eventually he will be notified once ownership has
83− He would then be notified once ownership has been granted to him, from which point onwards he could
86+ been granted to him (from TouchRegistry perspective), from which point onwards he could safely assume
84− safely assume other TouchRegistry users wouldn't snatch this touch away from him.
87+ other TouchRegistry users wouldn't snatch this touch away from him.
85
88
86 Items oblivious to TouchRegistry will lose their touch points without warning, just like in plain Qt.
89 Items oblivious to TouchRegistry will lose their touch points without warning, just like in plain Qt.
87
90
@@ -91,15 +94,10 @@ class UBUNTUGESTURES_EXPORT TouchRegistry : public QObject
91{
94{
92 Q_OBJECT
95 Q_OBJECT
93public:
96public:
94− TouchRegistry(QObject *parent = nullptr);
95− // Useful for tests, where you should feed a fake timer
96− TouchRegistry(QObject *parent, UbuntuGestures::AbstractTimerFactory *timerFactory);
97−
98 virtual ~TouchRegistry();
97 virtual ~TouchRegistry();
99
98
100 // Returns a pointer to the application's TouchRegistry instance.
99 // Returns a pointer to the application's TouchRegistry instance.
101− // If no instance has been allocated, null is returned.
102− static TouchRegistry *instance() { return m_instance; }
100+ static TouchRegistry *instance();
103
101
104 void update(const QTouchEvent *event);
102 void update(const QTouchEvent *event);
105
103
@@ -126,13 +124,31 @@ public:
126 // but would nonetheless like to be kept up-to-date on its state.
124 // but would nonetheless like to be kept up-to-date on its state.
127 void addTouchWatcher(int touchId, QQuickItem *watcherItem);
125 void addTouchWatcher(int touchId, QQuickItem *watcherItem);
128
126
127+ // Useful for tests, where you should use fake timers
128+ void setTimerFactory(UbuntuGestures::AbstractTimerFactory *timerFactory);
129+
129private Q_SLOTS:
130private Q_SLOTS:
130 void rejectCandidateOwnerForTouch(int id, QQuickItem *candidate);
131 void rejectCandidateOwnerForTouch(int id, QQuickItem *candidate);
131
132
132private:
133private:
134+ // Only instance() can cronstruct one
135+ TouchRegistry(QObject *parent = nullptr);
136+
133 class CandidateInfo {
137 class CandidateInfo {
134 public:
138 public:
135− bool undecided;
139+ enum {
140+ // A candidate owner that doesn't yet know for sure whether he wants the touch point
141+ // (gesture recognition is stilll going on)
142+ Undecided = 0,
143+ // A candidate owner that wants the touch but hasn't been granted it yet,
144+ // most likely because there's an undecided candidate with higher priority
145+ Requested = 1,
146+ // An item that is the interim owner of the touch, receiving QTouchEvents of it
147+ // from QQuickWindow. Ie, it's the actual touch owner from Qt's point of view.
148+ // It wants to keep its touch ownership but hasn't been granted it by TouchRegistry
149+ // yet because of undecided candidates higher up.
150+ InterimOwner = 2
151+ } state;
136 // TODO: Prune candidates that become null and resolve ownership accordingly.
152 // TODO: Prune candidates that become null and resolve ownership accordingly.
137 QPointer<QQuickItem> item;
153 QPointer<QQuickItem> item;
138 QPointer<UbuntuGestures::CandidateInactivityTimer> inactivityTimer;
154 QPointer<UbuntuGestures::CandidateInactivityTimer> inactivityTimer;
plugins/AccountsService/plugin.h+1−1
@@ -28,7 +28,7 @@ class AccountsServicePlugin : public QQmlExtensionPlugin
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29
29
30public:
30public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
32};
32};
33
33
34#endif
34#endif
plugins/Dash/CardCreator.js+44−15
@@ -16,6 +16,9 @@
16
16
17.pragma library
17.pragma library
18
18
19+// %1 is the template["card-background"] string
20+// %2 is the template["card-background"]["elements"][0]
21+// %3 is the template["card-background"]["elements"][1]
19var kBackgroundLoaderCode = 'Loader {\n\
22var kBackgroundLoaderCode = 'Loader {\n\
20 id: backgroundLoader; \n\
23 id: backgroundLoader; \n\
21 objectName: "backgroundLoader"; \n\
24 objectName: "backgroundLoader"; \n\
@@ -34,18 +37,14 @@ var kBackgroundLoaderCode = 'Loader {\n\
34 objectName: "backgroundImage"; \n\
37 objectName: "backgroundImage"; \n\
35 source: { \n\
38 source: { \n\
36 if (cardData && typeof cardData["background"] === "string") return cardData["background"]; \n\
39 if (cardData && typeof cardData["background"] === "string") return cardData["background"]; \n\
37− else if (template && typeof template["card-background"] === "string") return template["card-background"]; \n\
40+ else return "%1"; \n\
38− else return ""; \n\
39 } \n\
41 } \n\
40 } \n\
42 } \n\
41 function getColor(index) { \n\
43 function getColor(index) { \n\
42 if (cardData && typeof cardData["background"] === "object" \n\
44 if (cardData && typeof cardData["background"] === "object" \n\
43 && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) { \n\
45 && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) { \n\
44 return cardData["background"]["elements"][index]; \n\
46 return cardData["background"]["elements"][index]; \n\
45− } else if (template && typeof template["card-background"] === "object" \n\
47+ } else return index === 0 ? %2 : %3; \n\
46− && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) { \n\
47− return template["card-background"]["elements"][index]; \n\
48− } else return undefined; \n\
49 } \n\
48 } \n\
50 } \n\
49 } \n\
51 }\n';
50 }\n';
@@ -64,11 +63,28 @@ var kArtShapeHolderCode = 'Item { \n\
64 active: cardData && cardData["art"] || false; \n\
63 active: cardData && cardData["art"] || false; \n\
65 asynchronous: root.asynchronous; \n\
64 asynchronous: root.asynchronous; \n\
66 visible: status == Loader.Ready; \n\
65 visible: status == Loader.Ready; \n\
67− sourceComponent: UbuntuShape { \n\
66+ sourceComponent: Item { \n\
68 id: artShape; \n\
67 id: artShape; \n\
69 objectName: "artShape"; \n\
68 objectName: "artShape"; \n\
70− radius: "medium"; \n\
69+ property bool doShapeItem: components["art"]["conciergeMode"] !== true; \n\
71 visible: image.status == Image.Ready; \n\
70 visible: image.status == Image.Ready; \n\
71+ readonly property alias image: artImage.image; \n\
72+ property alias borderSource: artShapeShape.borderSource; \n\
73+ ShaderEffectSource { \n\
74+ id: artShapeSource; \n\
75+ sourceItem: artImage; \n\
76+ anchors.centerIn: parent; \n\
77+ width: 1; \n\
78+ height: 1; \n\
79+ hideSource: doShapeItem; \n\
80+ } \n\
81+ Shape { \n\
82+ id: artShapeShape; \n\
83+ image: artShapeSource; \n\
84+ anchors.fill: parent; \n\
85+ visible: doShapeItem; \n\
86+ radius: "medium"; \n\
87+ } \n\
72 readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1; \n\
88 readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1; \n\
73 readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1; \n\
89 readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1; \n\
74 Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; } \n\
90 Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; } \n\
@@ -87,11 +103,9 @@ var kArtShapeHolderCode = 'Item { \n\
87 objectName: "artImage"; \n\
103 objectName: "artImage"; \n\
88 source: cardData && cardData["art"] || ""; \n\
104 source: cardData && cardData["art"] || ""; \n\
89 asynchronous: root.asynchronous; \n\
105 asynchronous: root.asynchronous; \n\
90− visible: false; \n\
91 width: %2; \n\
106 width: %2; \n\
92 height: %3; \n\
107 height: %3; \n\
93 } \n\
108 } \n\
94− image: artImage.image; \n\
95 } \n\
109 } \n\
96 } \n\
110 } \n\
97 }\n';
111 }\n';
@@ -302,9 +316,11 @@ var kSummaryLabelCode = 'Label { \n\
302
316
303function cardString(template, components) {
317function cardString(template, components) {
304 var code;
318 var code;
319+
320+ var templateInteractive = (template == null ? true : (template["non-interactive"] !== undefined ? !template["non-interactive"] : true)) ? "true" : "false";
321+
305 code = 'AbstractButton { \n\
322 code = 'AbstractButton { \n\
306 id: root; \n\
323 id: root; \n\
307− property var template; \n\
308 property var components; \n\
324 property var components; \n\
309 property var cardData; \n\
325 property var cardData; \n\
310 property var artShapeBorderSource: undefined; \n\
326 property var artShapeBorderSource: undefined; \n\
@@ -317,12 +333,12 @@ function cardString(template, components) {
317 property bool asynchronous: true; \n\
333 property bool asynchronous: true; \n\
318 property bool showHeader: true; \n\
334 property bool showHeader: true; \n\
319 implicitWidth: childrenRect.width; \n\
335 implicitWidth: childrenRect.width; \n\
320− enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true); \n\
336+ enabled: %1; \n\
321− \n';
337+ \n'.arg(templateInteractive);
322
338
323 var hasArt = components["art"] && components["art"]["field"] || false;
339 var hasArt = components["art"] && components["art"]["field"] || false;
324 var hasSummary = components["summary"] || false;
340 var hasSummary = components["summary"] || false;
325− var artAndSummary = hasArt && hasSummary;
341+ var artAndSummary = hasArt && hasSummary && components["art"]["conciergeMode"] !== true;
326 var isHorizontal = template["card-layout"] === "horizontal";
342 var isHorizontal = template["card-layout"] === "horizontal";
327 var hasBackground = (!isHorizontal && (template["card-background"] || components["background"] || artAndSummary)) ||
343 var hasBackground = (!isHorizontal && (template["card-background"] || components["background"] || artAndSummary)) ||
328 (hasSummary && (template["card-background"] || components["background"]));
344 (hasSummary && (template["card-background"] || components["background"]));
@@ -335,7 +351,18 @@ function cardString(template, components) {
335 var hasAttributes = hasTitle && components["attributes"]["field"] || false;
351 var hasAttributes = hasTitle && components["attributes"]["field"] || false;
336
352
337 if (hasBackground) {
353 if (hasBackground) {
354+ var templateCardBackground = (template && typeof template["card-background"] === "string") ? template["card-background"] : "";
355+ var backgroundElements0;
356+ var backgroundElements1;
357+ if (template && typeof template["card-background"] === "object" && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
358+ if (template["card-background"]["elements"][0] !== undefined) {
359+ backgroundElements0 = '"%1"'.arg(template["card-background"]["elements"][0]);
360+ }
361+ if (template["card-background"]["elements"][1] !== undefined) {
362+ backgroundElements1 = '"%1"'.arg(template["card-background"]["elements"][1]);
363+ }
364+ }
338− code += kBackgroundLoaderCode;
365+ code += kBackgroundLoaderCode.arg(templateCardBackground).arg(backgroundElements0).arg(backgroundElements1);
339 }
366 }
340
367
341 if (hasArt) {
368 if (hasArt) {
@@ -631,6 +658,8 @@ function cardString(template, components) {
631 var implicitHeight = 'implicitHeight: ';
658 var implicitHeight = 'implicitHeight: ';
632 if (hasSummary) {
659 if (hasSummary) {
633 implicitHeight += 'summary.y + summary.height + units.gu(1);\n';
660 implicitHeight += 'summary.y + summary.height + units.gu(1);\n';
661+ } else if (headerAsOverlay) {
662+ implicitHeight += 'artShapeHolder.height;\n';
634 } else if (hasHeaderRow) {
663 } else if (hasHeaderRow) {
635 implicitHeight += 'row.y + row.height + units.gu(1);\n';
664 implicitHeight += 'row.y + row.height + units.gu(1);\n';
636 } else if (hasMascot) {
665 } else if (hasMascot) {
plugins/Dash/plugin.h+1−1
@@ -27,7 +27,7 @@ class DashPlugin : public QQmlExtensionPlugin
27 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28
28
29public:
29public:
30− void registerTypes(const char *uri);
30+ void registerTypes(const char *uri) override;
31};
31};
32
32
33#endif
33#endif
plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,5 +1,5 @@
1pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
2−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
2+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
3pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
3pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
4
4
5add_definitions(-DSM_BUSNAME=systemBus)
5add_definitions(-DSM_BUSNAME=systemBus)
plugins/Greeter/Unity/Launcher/launchermodelas.h+9−9
@@ -37,19 +37,19 @@ public:
37 LauncherModel(QObject *parent = 0);
37 LauncherModel(QObject *parent = 0);
38 ~LauncherModel();
38 ~LauncherModel();
39
39
40− int rowCount(const QModelIndex &parent = QModelIndex()) const;
40+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
41
41
42− QVariant data(const QModelIndex &index, int role) const;
42+ QVariant data(const QModelIndex &index, int role) const override;
43
43
44− Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const;
44+ Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const override;
45− Q_INVOKABLE void move(int oldIndex, int newIndex);
45+ Q_INVOKABLE void move(int oldIndex, int newIndex) override;
46− Q_INVOKABLE void pin(const QString &appId, int index = -1);
46+ Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
47− Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
47+ Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex) override;
48− Q_INVOKABLE void setUser(const QString &username);
48+ Q_INVOKABLE void setUser(const QString &username) override;
49 Q_INVOKABLE QString getUrlForAppId(const QString &appId) const;
49 Q_INVOKABLE QString getUrlForAppId(const QString &appId) const;
50
50
51− unity::shell::application::ApplicationManagerInterface* applicationManager() const;
51+ unity::shell::application::ApplicationManagerInterface* applicationManager() const override;
52− void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
52+ void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager) override;
53
53
54 bool onlyPinned() const override;
54 bool onlyPinned() const override;
55 void setOnlyPinned(bool onlyPinned) override;
55 void setOnlyPinned(bool onlyPinned) override;
plugins/Greeter/Unity/Launcher/plugin.h+1−1
@@ -26,7 +26,7 @@ class UnityLauncherPlugin : public QQmlExtensionPlugin
26 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27
27
28public:
28public:
29− void registerTypes(const char *uri);
29+ void registerTypes(const char *uri) override;
30};
30};
31
31
32#endif
32#endif
plugins/Greeter/Unity/Launcher/quicklistmodel.h+2−2
@@ -45,8 +45,8 @@ public:
45
45
46 QuickListEntry get(int index) const;
46 QuickListEntry get(int index) const;
47
47
48− int rowCount(const QModelIndex &parent = QModelIndex()) const;
48+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
49− QVariant data(const QModelIndex &index, int role) const;
49+ QVariant data(const QModelIndex &index, int role) const override;
50
50
51private:
51private:
52 QList<QuickListEntry> m_list;
52 QList<QuickListEntry> m_list;
plugins/LightDM/UsersModel.cpp+1−1
@@ -32,7 +32,7 @@ class MangleModel : public QSortFilterProxyModel
32public:
32public:
33 explicit MangleModel(QObject* parent=0);
33 explicit MangleModel(QObject* parent=0);
34
34
35− virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
35+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
36};
36};
37
37
38MangleModel::MangleModel(QObject* parent)
38MangleModel::MangleModel(QObject* parent)
plugins/LightDM/liblightdm/GreeterPrivate.cpp+29−5
@@ -56,7 +56,8 @@ public:
56 this, SLOT(handleMessage(pam_handle *, QString, QLightDM::Greeter::MessageType)));
56 this, SLOT(handleMessage(pam_handle *, QString, QLightDM::Greeter::MessageType)));
57 // This next connect is how we pass ResponseFutures between threads
57 // This next connect is how we pass ResponseFutures between threads
58 connect(this, SIGNAL(showPrompt(pam_handle *, QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)),
58 connect(this, SIGNAL(showPrompt(pam_handle *, QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)),
59− this, SLOT(handlePrompt(pam_handle *, QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)));
59+ this, SLOT(handlePrompt(pam_handle *, QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)),
60+ Qt::BlockingQueuedConnection);
60 }
61 }
61
62
62 ~GreeterImpl()
63 ~GreeterImpl()
@@ -68,6 +69,13 @@ public:
68 {
69 {
69 // Clear out any existing PAM interactions first
70 // Clear out any existing PAM interactions first
70 cancelPam();
71 cancelPam();
72+ if (pamHandle != nullptr) {
73+ // While we were cancelling pam above, we processed Qt events.
74+ // Which may have allowed someone to call start() on us again.
75+ // In which case, we'll bail on our current start() call.
76+ // This isn't racy because it's all in the same thread.
77+ return;
78+ }
71
79
72 AppData *appData = new AppData();
80 AppData *appData = new AppData();
73 appData->impl = this;
81 appData->impl = this;
@@ -79,7 +87,7 @@ public:
79
87
80 if (pam_start("lightdm", username.toUtf8(), &conversation, &pamHandle) == PAM_SUCCESS) {
88 if (pam_start("lightdm", username.toUtf8(), &conversation, &pamHandle) == PAM_SUCCESS) {
81 appData->handle = pamHandle;
89 appData->handle = pamHandle;
82− futureWatcher.setFuture(QtConcurrent::run(authenticateWithPam, pamHandle));
90+ futureWatcher.setFuture(QtConcurrent::mapped(QList<pam_handle*>() << pamHandle, authenticateWithPam));
83 } else {
91 } else {
84 delete appData;
92 delete appData;
85 greeterPrivate->authenticated = false;
93 greeterPrivate->authenticated = false;
@@ -88,7 +96,7 @@ public:
88 }
96 }
89 }
97 }
90
98
91− static int authenticateWithPam(pam_handle* pamHandle)
99+ static int authenticateWithPam(pam_handle* const& pamHandle)
92 {
100 {
93 int pamStatus = pam_authenticate(pamHandle, 0);
101 int pamStatus = pam_authenticate(pamHandle, 0);
94 if (pamStatus == PAM_SUCCESS) {
102 if (pamStatus == PAM_SUCCESS) {
@@ -242,11 +250,22 @@ private Q_SLOTS:
242private:
250private:
243 void cancelPam()
251 void cancelPam()
244 {
252 {
245− // Unfortunately we can't simply cancel our QFuture because QtConcurrent::run doesn't support cancel
246 if (pamHandle != nullptr) {
253 if (pamHandle != nullptr) {
254+ QFuture<int> pamFuture = futureWatcher.future();
247 pam_handle *handle = pamHandle;
255 pam_handle *handle = pamHandle;
248 pamHandle = nullptr; // to disable normal finishPam() handling
256 pamHandle = nullptr; // to disable normal finishPam() handling
257+ pamFuture.cancel();
258+
259+ // Note the empty loop, we just want to clear the futures queue.
260+ // Any further prompts from the pam thread will be immediately
261+ // responded to/dismissed in handlePrompt().
249− while (respond(QString())); // clear our local queue of QFutures
262+ while (respond(QString()));
263+
264+ // Now let signal/slot handling happen so the thread can finish
265+ while (!pamFuture.isFinished()) {
266+ QCoreApplication::processEvents();
267+ }
268+
250 pam_end(handle, PAM_CONV_ERR);
269 pam_end(handle, PAM_CONV_ERR);
251 }
270 }
252 }
271 }
@@ -266,6 +285,11 @@ GreeterPrivate::GreeterPrivate(Greeter* parent)
266{
285{
267}
286}
268
287
288+GreeterPrivate::~GreeterPrivate()
289+{
290+ delete m_impl;
291+}
292+
269void GreeterPrivate::handleAuthenticate()
293void GreeterPrivate::handleAuthenticate()
270{
294{
271 m_impl->start(authenticationUser);
295 m_impl->start(authenticationUser);
plugins/LightDM/liblightdm/GreeterPrivate.h+1−1
@@ -30,7 +30,7 @@ class GreeterPrivate
30{
30{
31public:
31public:
32 explicit GreeterPrivate(Greeter* parent=0);
32 explicit GreeterPrivate(Greeter* parent=0);
33− virtual ~GreeterPrivate() = default;
33+ virtual ~GreeterPrivate();
34
34
35 // These variables may not be used by all subclasses, that's no problem
35 // These variables may not be used by all subclasses, that's no problem
36 bool authenticated;
36 bool authenticated;
plugins/LightDM/liblightdm/UsersModel.h+2−2
@@ -53,8 +53,8 @@ public:
53 BackgroundPathRole
53 BackgroundPathRole
54 };
54 };
55
55
56− int rowCount(const QModelIndex &parent) const;
56+ int rowCount(const QModelIndex &parent) const override;
57− QVariant data(const QModelIndex &index, int role) const;
57+ QVariant data(const QModelIndex &index, int role) const override;
58
58
59private:
59private:
60 UsersModelPrivate * const d_ptr;
60 UsersModelPrivate * const d_ptr;
plugins/LightDM/plugin.h+1−1
@@ -29,7 +29,7 @@ class LightDMPlugin : public QQmlExtensionPlugin
29 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
30
30
31public:
31public:
32− void registerTypes(const char *uri);
32+ void registerTypes(const char *uri) override;
33};
33};
34
34
35#endif
35#endif
plugins/Lights/plugin.h+1−1
@@ -28,7 +28,7 @@ class LightsPlugin : public QQmlExtensionPlugin
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29
29
30public:
30public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
32};
32};
33
33
34#endif
34#endif
plugins/Powerd/plugin.h+1−1
@@ -29,7 +29,7 @@ class PowerdPlugin : public QQmlExtensionPlugin
29 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
30
30
31public:
31public:
32− void registerTypes(const char *uri);
32+ void registerTypes(const char *uri) override;
33};
33};
34
34
35#endif
35#endif
plugins/ScreenGrabber/plugin.h+1−1
@@ -27,7 +27,7 @@ class ScreenGrabberPlugin : public QQmlExtensionPlugin
27 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28
28
29public:
29public:
30− void registerTypes(const char *uri);
30+ void registerTypes(const char *uri) override;
31};
31};
32
32
33#endif // SCREENGRABBER_PLUGIN_H
33#endif // SCREENGRABBER_PLUGIN_H
plugins/SessionBroadcast/plugin.h+1−1
@@ -27,7 +27,7 @@ class SessionBroadcastPlugin : public QQmlExtensionPlugin
27 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28
28
29public:
29public:
30− void registerTypes(const char *uri);
30+ void registerTypes(const char *uri) override;
31};
31};
32
32
33#endif
33#endif
plugins/Ubuntu/DownloadDaemonListener/plugin.h+2−2
@@ -31,7 +31,7 @@ class BackendPlugin : public QQmlExtensionPlugin
31 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
31 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
32
32
33public:
33public:
34− void registerTypes(const char *uri);
34+ void registerTypes(const char *uri) override;
35− void initializeEngine(QQmlEngine *engine, const char *uri);
35+ void initializeEngine(QQmlEngine *engine, const char *uri) override;
36};
36};
37#endif // BACKEND_PLUGIN_H
37#endif // BACKEND_PLUGIN_H
plugins/Ubuntu/Gestures/CMakeLists.txt+2−1
@@ -4,10 +4,11 @@ remove_definitions(-DQT_NO_KEYWORDS)
4set(UbuntuGesturesQml_SOURCES
4set(UbuntuGesturesQml_SOURCES
5 plugin.cpp
5 plugin.cpp
6 AxisVelocityCalculator.cpp
6 AxisVelocityCalculator.cpp
7+ Damper.cpp
7 Direction.cpp
8 Direction.cpp
8 DirectionalDragArea.cpp
9 DirectionalDragArea.cpp
10+ FloatingFlickable.cpp
9 PressedOutsideNotifier.cpp
11 PressedOutsideNotifier.cpp
10− TimeSource.cpp
11 TouchDispatcher.cpp
12 TouchDispatcher.cpp
12 TouchGate.cpp
13 TouchGate.cpp
13)
14)
plugins/Ubuntu/Gestures/Damper.cppnew file+24
@@ -0,0 +1,24 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "Damper.h"
18+#include <QDebug>
19+
20+QDebug operator<<(QDebug dbg, const DampedPointF &p)
21+{
22+ dbg.nospace() << "(" << p.x() << ", " << p.y() << ")";
23+ return dbg.space();
24+}
plugins/Ubuntu/Gestures/Damper.h+2
@@ -84,4 +84,6 @@ private:
84 Damper<qreal> m_y;
84 Damper<qreal> m_y;
85};
85};
86
86
87+QDebug operator<<(QDebug dbg, const DampedPointF &p);
88+
87#endif // UBUNTU_GESTURES_DAMPER_H
89#endif // UBUNTU_GESTURES_DAMPER_H
plugins/Ubuntu/Gestures/Direction.cpp+6−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -25,12 +25,15 @@ bool Direction::isHorizontal(Direction::Type type)
25
25
26bool Direction::isVertical(Direction::Type type)
26bool Direction::isVertical(Direction::Type type)
27{
27{
28− return type == Direction::Upwards || type == Direction::Downwards;
28+ return type == Direction::Upwards
29+ || type == Direction::Downwards
30+ || type == Direction::Vertical;
29}
31}
30
32
31bool Direction::isPositive(Direction::Type type)
33bool Direction::isPositive(Direction::Type type)
32{
34{
33 return type == Rightwards
35 return type == Rightwards
34 || type == Downwards
36 || type == Downwards
35− || type == Horizontal;
37+ || type == Horizontal
38+ || type == Vertical;
36}
39}
plugins/Ubuntu/Gestures/Direction.h+3−2
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -34,7 +34,8 @@ public:
34 Leftwards, // Along the negative direction of the X axis
34 Leftwards, // Along the negative direction of the X axis
35 Downwards, // Along the positive direction of the Y axis
35 Downwards, // Along the positive direction of the Y axis
36 Upwards, // Along the negative direction of the Y axis
36 Upwards, // Along the negative direction of the Y axis
37− Horizontal // Along the X axis, in any direction
37+ Horizontal, // Along the X axis, in any direction
38+ Vertical // Along the Y axis, in any direction
38 };
39 };
39
40
40 Q_INVOKABLE static bool isHorizontal(Direction::Type type);
41 Q_INVOKABLE static bool isHorizontal(Direction::Type type);
plugins/Ubuntu/Gestures/DirectionalDragArea.cpp+324−322
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
21
21
22#include <QQuickWindow>
22#include <QQuickWindow>
23#include <QtCore/qmath.h>
23#include <QtCore/qmath.h>
24+#include <QScreen>
24#include <QDebug>
25#include <QDebug>
25
26
26#pragma GCC diagnostic push
27#pragma GCC diagnostic push
@@ -33,6 +34,8 @@
33#include "TouchRegistry.h"
34#include "TouchRegistry.h"
34#include "UnownedTouchEvent.h"
35#include "UnownedTouchEvent.h"
35
36
37+#include "DirectionalDragArea_p.h"
38+
36using namespace UbuntuGestures;
39using namespace UbuntuGestures;
37
40
38#if DIRECTIONALDRAGAREA_DEBUG
41#if DIRECTIONALDRAGAREA_DEBUG
@@ -40,11 +43,11 @@ using namespace UbuntuGestures;
40#include "DebugHelpers.h"
43#include "DebugHelpers.h"
41
44
42namespace {
45namespace {
43−const char *statusToString(DirectionalDragArea::Status status)
46+const char *statusToString(DirectionalDragAreaPrivate::Status status)
44{
47{
45− if (status == DirectionalDragArea::WaitingForTouch) {
48+ if (status == DirectionalDragAreaPrivate::WaitingForTouch) {
46 return "WaitingForTouch";
49 return "WaitingForTouch";
47− } else if (status == DirectionalDragArea::Undecided) {
50+ } else if (status == DirectionalDragAreaPrivate::Undecided) {
48 return "Undecided";
51 return "Undecided";
49 } else {
52 } else {
50 return "Recognized";
53 return "Recognized";
@@ -56,205 +59,168 @@ const char *statusToString(DirectionalDragArea::Status status)
56#define ddaDebug(params) ((void)0)
59#define ddaDebug(params) ((void)0)
57#endif // DIRECTIONALDRAGAREA_DEBUG
60#endif // DIRECTIONALDRAGAREA_DEBUG
58
61
59−
60DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
62DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
61 : QQuickItem(parent)
63 : QQuickItem(parent)
62− , m_status(WaitingForTouch)
64+ , d(new DirectionalDragAreaPrivate(this))
63− , m_sceneDistance(0)
64− , m_touchId(-1)
65− , m_direction(Direction::Rightwards)
66− , m_wideningAngle(0)
67− , m_wideningFactor(0)
68− , m_distanceThreshold(0)
69− , m_distanceThresholdSquared(0.)
70− , m_minSpeed(0)
71− , m_maxSilenceTime(200)
72− , m_silenceTime(0)
73− , m_compositionTime(60)
74− , m_numSamplesOnLastSpeedCheck(0)
75− , m_recognitionTimer(0)
76− , m_velocityCalculator(0)
77− , m_timeSource(new RealTimeSource)
78− , m_activeTouches(m_timeSource)
79−{
80− setRecognitionTimer(new Timer(this));
81− m_recognitionTimer->setInterval(60);
82− m_recognitionTimer->setSingleShot(false);
83−
84− m_velocityCalculator = new AxisVelocityCalculator(this);
85−
86− connect(this, &QQuickItem::enabledChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
87− connect(this, &QQuickItem::visibleChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
88−}
89−
90−Direction::Type DirectionalDragArea::direction() const
91−{
92− return m_direction;
93−}
94−
95−void DirectionalDragArea::setDirection(Direction::Type direction)
96{
65{
97− if (direction != m_direction) {
66+ d->setRecognitionTimer(new Timer(this));
98− m_direction = direction;
67+ d->recognitionTimer->setInterval(d->maxTime);
99− Q_EMIT directionChanged(m_direction);
68+ d->recognitionTimer->setSingleShot(true);
100− }
101−}
102
69
103−void DirectionalDragArea::setMaxDeviation(qreal value)
70+ connect(this, &QQuickItem::enabledChanged, d, &DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible);
104−{
71+ connect(this, &QQuickItem::visibleChanged, d, &DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible);
105− if (m_dampedScenePos.maxDelta() != value) {
106− m_dampedScenePos.setMaxDelta(value);
107− Q_EMIT maxDeviationChanged(value);
108− }
109}
72}
110
73
111−qreal DirectionalDragArea::wideningAngle() const
74+Direction::Type DirectionalDragArea::direction() const
112−{
113− return m_wideningAngle;
114−}
115−
116−void DirectionalDragArea::setWideningAngle(qreal angle)
117−{
118− if (angle == m_wideningAngle)
119− return;
120−
121− m_wideningAngle = angle;
122−
123− // wideningFactor = pow(cosine(angle), 2)
124− {
125− qreal angleRadians = angle * M_PI / 180.0;
126− m_wideningFactor = qCos(angleRadians);
127− m_wideningFactor = m_wideningFactor * m_wideningFactor;
128− }
129−
130− Q_EMIT wideningAngleChanged(angle);
131−}
132−
133−void DirectionalDragArea::setDistanceThreshold(qreal value)
134{
75{
135− if (m_distanceThreshold != value) {
76+ return d->direction;
136− m_distanceThreshold = value;
137− m_distanceThresholdSquared = m_distanceThreshold * m_distanceThreshold;
138− Q_EMIT distanceThresholdChanged(value);
139− }
140}
77}
141
78
142−void DirectionalDragArea::setMinSpeed(qreal value)
79+void DirectionalDragArea::setDirection(Direction::Type direction)
143{
80{
144− if (m_minSpeed != value) {
81+ if (direction != d->direction) {
145− m_minSpeed = value;
82+ d->direction = direction;
146− Q_EMIT minSpeedChanged(value);
83+ Q_EMIT directionChanged(d->direction);
147 }
84 }
148}
85}
149
86
150−void DirectionalDragArea::setMaxSilenceTime(int value)
87+void DirectionalDragAreaPrivate::setDistanceThreshold(qreal value)
151{
88{
152− if (m_maxSilenceTime != value) {
89+ if (distanceThreshold != value) {
153− m_maxSilenceTime = value;
90+ distanceThreshold = value;
154− Q_EMIT maxSilenceTimeChanged(value);
91+ distanceThresholdSquared = distanceThreshold * distanceThreshold;
155 }
92 }
156}
93}
157
94
158−void DirectionalDragArea::setCompositionTime(int value)
95+void DirectionalDragAreaPrivate::setMaxTime(int value)
159{
96{
160− if (m_compositionTime != value) {
97+ if (maxTime != value) {
161− m_compositionTime = value;
98+ maxTime = value;
162− Q_EMIT compositionTimeChanged(value);
99+ recognitionTimer->setInterval(maxTime);
163 }
100 }
164}
101}
165
102
166−void DirectionalDragArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
103+void DirectionalDragAreaPrivate::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
167{
104{
168 int interval = 0;
105 int interval = 0;
169 bool timerWasRunning = false;
106 bool timerWasRunning = false;
170 bool wasSingleShot = false;
107 bool wasSingleShot = false;
171
108
172 // can be null when called from the constructor
109 // can be null when called from the constructor
173− if (m_recognitionTimer) {
110+ if (recognitionTimer) {
174− interval = m_recognitionTimer->interval();
111+ interval = recognitionTimer->interval();
175− timerWasRunning = m_recognitionTimer->isRunning();
112+ timerWasRunning = recognitionTimer->isRunning();
176− if (m_recognitionTimer->parent() == this) {
113+ if (recognitionTimer->parent() == this) {
177− delete m_recognitionTimer;
114+ delete recognitionTimer;
178 }
115 }
179 }
116 }
180
117
181− m_recognitionTimer = timer;
118+ recognitionTimer = timer;
182 timer->setInterval(interval);
119 timer->setInterval(interval);
183 timer->setSingleShot(wasSingleShot);
120 timer->setSingleShot(wasSingleShot);
184 connect(timer, &UbuntuGestures::AbstractTimer::timeout,
121 connect(timer, &UbuntuGestures::AbstractTimer::timeout,
185− this, &DirectionalDragArea::checkSpeed);
122+ this, &DirectionalDragAreaPrivate::rejectGesture);
186 if (timerWasRunning) {
123 if (timerWasRunning) {
187− m_recognitionTimer->start();
124+ recognitionTimer->start();
188 }
125 }
189}
126}
190
127
191−void DirectionalDragArea::setTimeSource(const SharedTimeSource &timeSource)
128+void DirectionalDragAreaPrivate::setTimeSource(const SharedTimeSource &timeSource)
192{
129{
193− m_timeSource = timeSource;
130+ this->timeSource = timeSource;
194− m_velocityCalculator->setTimeSource(timeSource);
195− m_activeTouches.m_timeSource = timeSource;
131+ activeTouches.m_timeSource = timeSource;
196}
132}
197
133
198qreal DirectionalDragArea::distance() const
134qreal DirectionalDragArea::distance() const
199{
135{
200− if (Direction::isHorizontal(m_direction)) {
136+ if (Direction::isHorizontal(d->direction)) {
201− return m_previousPos.x() - m_startPos.x();
137+ return d->publicPos.x() - d->startPos.x();
202 } else {
138 } else {
203− return m_previousPos.y() - m_startPos.y();
139+ return d->publicPos.y() - d->startPos.y();
204 }
140 }
205}
141}
206
142
207−void DirectionalDragArea::updateSceneDistance()
143+void DirectionalDragAreaPrivate::updateSceneDistance()
208{
144{
209− QPointF totalMovement = m_previousScenePos - m_startScenePos;
145+ QPointF totalMovement = publicScenePos - startScenePos;
210− m_sceneDistance = projectOntoDirectionVector(totalMovement);
146+ sceneDistance = projectOntoDirectionVector(totalMovement);
211}
147}
212
148
213qreal DirectionalDragArea::sceneDistance() const
149qreal DirectionalDragArea::sceneDistance() const
214{
150{
215− return m_sceneDistance;
151+ return d->sceneDistance;
216}
152}
217
153
218qreal DirectionalDragArea::touchX() const
154qreal DirectionalDragArea::touchX() const
219{
155{
220− return m_previousPos.x();
156+ return d->publicPos.x();
221}
157}
222
158
223qreal DirectionalDragArea::touchY() const
159qreal DirectionalDragArea::touchY() const
224{
160{
225− return m_previousPos.y();
161+ return d->publicPos.y();
226}
162}
227
163
228qreal DirectionalDragArea::touchSceneX() const
164qreal DirectionalDragArea::touchSceneX() const
229{
165{
230− return m_previousScenePos.x();
166+ return d->publicScenePos.x();
231}
167}
232
168
233qreal DirectionalDragArea::touchSceneY() const
169qreal DirectionalDragArea::touchSceneY() const
234{
170{
235− return m_previousScenePos.y();
171+ return d->publicScenePos.y();
172+}
173+
174+bool DirectionalDragArea::dragging() const
175+{
176+ return d->status == DirectionalDragAreaPrivate::Recognized;
177+}
178+
179+bool DirectionalDragArea::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");
236}
202}
237
203
238bool DirectionalDragArea::event(QEvent *event)
204bool DirectionalDragArea::event(QEvent *event)
239{
205{
240 if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
206 if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
241− touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
207+ d->touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
242 return true;
208 return true;
243 } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
209 } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
244− unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
210+ d->unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
245 return true;
211 return true;
246 } else {
212 } else {
247 return QQuickItem::event(event);
213 return QQuickItem::event(event);
248 }
214 }
249}
215}
250
216
251−void DirectionalDragArea::touchOwnershipEvent(TouchOwnershipEvent *event)
217+void DirectionalDragAreaPrivate::touchOwnershipEvent(TouchOwnershipEvent *event)
252{
218{
253 if (event->gained()) {
219 if (event->gained()) {
254 QVector<int> ids;
220 QVector<int> ids;
255 ids.append(event->touchId());
221 ids.append(event->touchId());
256 ddaDebug("grabbing touch");
222 ddaDebug("grabbing touch");
257− grabTouchPoints(ids);
223+ q->grabTouchPoints(ids);
258
224
259 // Work around for Qt bug. If we grab a touch that is being used for mouse pointer
225 // Work around for Qt bug. If we grab a touch that is being used for mouse pointer
260 // emulation it will cause the emulation logic to go nuts.
226 // emulation it will cause the emulation logic to go nuts.
@@ -262,35 +228,35 @@ void DirectionalDragArea::touchOwnershipEvent(TouchOwnershipEvent *event)
262 //
228 //
263 // The fix for this bug has landed in Qt 5.4 (https://codereview.qt-project.org/96887)
229 // The fix for this bug has landed in Qt 5.4 (https://codereview.qt-project.org/96887)
264 // TODO: Remove this workaround once we start using Qt 5.4
230 // TODO: Remove this workaround once we start using Qt 5.4
265− if (window()) {
231+ if (q->window()) {
266− QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window());
232+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(q->window());
267− if (windowPrivate->touchMouseId == event->touchId() && window()->mouseGrabberItem()) {
233+ if (windowPrivate->touchMouseId == event->touchId() && q->window()->mouseGrabberItem()) {
268 ddaDebug("removing mouse grabber");
234 ddaDebug("removing mouse grabber");
269− window()->mouseGrabberItem()->ungrabMouse();
235+ q->window()->mouseGrabberItem()->ungrabMouse();
270 }
236 }
271 }
237 }
272 } else {
238 } else {
273 // We still wanna know when it ends for keeping the composition time window up-to-date
239 // 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);
275
241
276 setStatus(WaitingForTouch);
242 setStatus(WaitingForTouch);
277 }
243 }
278}
244}
279
245
280−void DirectionalDragArea::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
246+void DirectionalDragAreaPrivate::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
281{
247{
282 QTouchEvent *event = unownedTouchEvent->touchEvent();
248 QTouchEvent *event = unownedTouchEvent->touchEvent();
283
249
284 Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed));
250 Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed));
285
251
286− ddaDebug("Unowned " << m_timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
252+ ddaDebug("Unowned " << timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
287
253
288− switch (m_status) {
254+ switch (status) {
289 case WaitingForTouch:
255 case WaitingForTouch:
290 // do nothing
256 // do nothing
291 break;
257 break;
292 case Undecided:
258 case Undecided:
293− Q_ASSERT(isEnabled() && isVisible());
259+ Q_ASSERT(q->isEnabled() && q->isVisible());
294 unownedTouchEvent_undecided(unownedTouchEvent);
260 unownedTouchEvent_undecided(unownedTouchEvent);
295 break;
261 break;
296 default: // Recognized:
262 default: // Recognized:
@@ -298,18 +264,18 @@ void DirectionalDragArea::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent
298 break;
264 break;
299 }
265 }
300
266
301− m_activeTouches.update(event);
267+ activeTouches.update(event);
302}
268}
303
269
304−void DirectionalDragArea::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
270+void DirectionalDragAreaPrivate::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
305{
271{
306 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent());
272 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent());
307 if (!touchPoint) {
273 if (!touchPoint) {
308− qCritical() << "DirectionalDragArea[status=Undecided]: touch " << m_touchId
274+ qCritical() << "DirectionalDragArea[status=Undecided]: touch " << touchId
309 << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. "
275 << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. "
310 "Considering it as released.";
276 "Considering it as released.";
311
277
312− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
278+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
313 setStatus(WaitingForTouch);
279 setStatus(WaitingForTouch);
314 return;
280 return;
315 }
281 }
@@ -319,48 +285,42 @@ void DirectionalDragArea::unownedTouchEvent_undecided(UnownedTouchEvent *unowned
319 if (touchPoint->state() == Qt::TouchPointReleased) {
285 if (touchPoint->state() == Qt::TouchPointReleased) {
320 // touch has ended before recognition concluded
286 // touch has ended before recognition concluded
321 ddaDebug("Touch has ended before recognition concluded");
287 ddaDebug("Touch has ended before recognition concluded");
322− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
288+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
323− emitSignalIfTapped();
324 setStatus(WaitingForTouch);
289 setStatus(WaitingForTouch);
325 return;
290 return;
326 }
291 }
327
292
328− m_previousDampedScenePos.setX(m_dampedScenePos.x());
293+ previousDampedScenePos.setX(dampedScenePos.x());
329− m_previousDampedScenePos.setY(m_dampedScenePos.y());
294+ previousDampedScenePos.setY(dampedScenePos.y());
330− m_dampedScenePos.update(touchScenePos);
295+ dampedScenePos.update(touchScenePos);
331− updateVelocityCalculator(touchScenePos);
332−
333− if (!pointInsideAllowedArea()) {
334− ddaDebug("Rejecting gesture because touch point is outside allowed area.");
335− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
336− // We still wanna know when it ends for keeping the composition time window up-to-date
337− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
338− setStatus(WaitingForTouch);
339− return;
340− }
341
296
342 if (!movingInRightDirection()) {
297 if (!movingInRightDirection()) {
343 ddaDebug("Rejecting gesture because touch point is moving in the wrong direction.");
298 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);
345 // We still wanna know when it ends for keeping the composition time window up-to-date
300 // 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);
347 setStatus(WaitingForTouch);
302 setStatus(WaitingForTouch);
348 return;
303 return;
349 }
304 }
350
305
351− setPreviousPos(touchPoint->pos());
352− setPreviousScenePos(touchScenePos);
353−
354 if (isWithinTouchCompositionWindow()) {
306 if (isWithinTouchCompositionWindow()) {
355 // There's still time for some new touch to appear and ruin our party as it would be combined
307 // 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.
357 ddaDebug("Sill within composition window. Let's wait more.");
309 ddaDebug("Sill within composition window. Let's wait more.");
358 return;
310 return;
359 }
311 }
360
312
361− if (movedFarEnough(touchScenePos)) {
313+ if (movedFarEnoughAlongGestureAxis()) {
362− TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);
314+ TouchRegistry::instance()->requestTouchOwnership(touchId, q);
363 setStatus(Recognized);
315 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);
364 } else {
324 } else {
365 ddaDebug("Didn't move far enough yet. Let's wait more.");
325 ddaDebug("Didn't move far enough yet. Let's wait more.");
366 }
326 }
@@ -371,29 +331,29 @@ void DirectionalDragArea::touchEvent(QTouchEvent *event)
371 // TODO: Consider when more than one touch starts in the same event (although it's not possible
331 // TODO: Consider when more than one touch starts in the same event (although it's not possible
372 // with Mir's android-input). Have to track them all. Consider it a plus/bonus.
332 // with Mir's android-input). Have to track them all. Consider it a plus/bonus.
373
333
374− ddaDebug(m_timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
334+ ddaDebug(d->timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
375
335
376 if (!isEnabled() || !isVisible()) {
336 if (!isEnabled() || !isVisible()) {
377 QQuickItem::touchEvent(event);
337 QQuickItem::touchEvent(event);
378 return;
338 return;
379 }
339 }
380
340
381− switch (m_status) {
341+ switch (d->status) {
382− case WaitingForTouch:
342+ case DirectionalDragAreaPrivate::WaitingForTouch:
383− touchEvent_absent(event);
343+ d->touchEvent_absent(event);
384 break;
344 break;
385− case Undecided:
345+ case DirectionalDragAreaPrivate::Undecided:
386− touchEvent_undecided(event);
346+ d->touchEvent_undecided(event);
387 break;
347 break;
388 default: // Recognized:
348 default: // Recognized:
389− touchEvent_recognized(event);
349+ d->touchEvent_recognized(event);
390 break;
350 break;
391 }
351 }
392
352
393− m_activeTouches.update(event);
353+ d->activeTouches.update(event);
394}
354}
395
355
396−void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
356+void DirectionalDragAreaPrivate::touchEvent_absent(QTouchEvent *event)
397{
357{
398 // TODO: accept/reject is for the whole event, not per touch id. See how that affects us.
358 // TODO: accept/reject is for the whole event, not per touch id. See how that affects us.
399
359
@@ -423,36 +383,40 @@ void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
423 allGood = false;
383 allGood = false;
424 } else {
384 } else {
425 // that's our candidate
385 // that's our candidate
426− m_touchId = touchPoint.id();
427 newTouchPoint = &touchPoint;
386 newTouchPoint = &touchPoint;
428 }
387 }
429 }
388 }
430 }
389 }
431
390
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+
432 if (allGood) {
399 if (allGood) {
433 Q_ASSERT(newTouchPoint);
400 Q_ASSERT(newTouchPoint);
434
401
435− m_startPos = newTouchPoint->pos();
402+ startPos = newTouchPoint->pos();
436− m_startScenePos = newTouchPoint->scenePos();
403+ startScenePos = newTouchPoint->scenePos();
437− m_touchId = newTouchPoint->id();
404+ touchId = newTouchPoint->id();
438− m_dampedScenePos.reset(m_startScenePos);
405+ dampedScenePos.reset(startScenePos);
439− m_velocityCalculator->setTrackedPosition(0.);
406+ setPublicPos(startPos);
440− m_velocityCalculator->reset();
407+
441− m_numSamplesOnLastSpeedCheck = 0;
408+ setPublicScenePos(startScenePos);
442− m_silenceTime = 0;
443− setPreviousPos(m_startPos);
444− setPreviousScenePos(m_startScenePos);
445 updateSceneDirectionVector();
409 updateSceneDirectionVector();
446
410
447 if (recognitionIsDisabled()) {
411 if (recognitionIsDisabled()) {
448 // Behave like a dumb TouchArea
412 // Behave like a dumb TouchArea
449 ddaDebug("Gesture recognition is disabled. Requesting touch ownership immediately.");
413 ddaDebug("Gesture recognition is disabled. Requesting touch ownership immediately.");
450− TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);
414+ TouchRegistry::instance()->requestTouchOwnership(touchId, q);
451 setStatus(Recognized);
415 setStatus(Recognized);
452 event->accept();
416 event->accept();
453 } else {
417 } else {
454 // just monitor the touch points for now.
418 // just monitor the touch points for now.
455− TouchRegistry::instance()->addCandidateOwnerForTouch(m_touchId, this);
419+ TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, q);
456
420
457 setStatus(Undecided);
421 setStatus(Undecided);
458 // Let the item below have it. We will monitor it and grab it later if a gesture
422 // 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)
465 }
429 }
466}
430}
467
431
468−void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
432+void DirectionalDragAreaPrivate::touchEvent_undecided(QTouchEvent *event)
469{
433{
470− Q_ASSERT(event->type() == QEvent::TouchBegin);
471 Q_ASSERT(fetchTargetTouchPoint(event) == nullptr);
434 Q_ASSERT(fetchTargetTouchPoint(event) == nullptr);
472
435
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).
474 // But we do want to know when those new touches end for keeping the composition time
437 // But we do want to know when those new touches end for keeping the composition time
475 // window up-to-date
438 // window up-to-date
476 event->ignore();
439 event->ignore();
@@ -480,63 +443,60 @@ void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
480 // multi-finger drags are not accepted
443 // multi-finger drags are not accepted
481 ddaDebug("Multi-finger drags are not accepted");
444 ddaDebug("Multi-finger drags are not accepted");
482
445
483− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
446+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
484 // We still wanna know when it ends for keeping the composition time window up-to-date
447 // 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);
486
449
487 setStatus(WaitingForTouch);
450 setStatus(WaitingForTouch);
488 }
451 }
489}
452}
490
453
491−void DirectionalDragArea::touchEvent_recognized(QTouchEvent *event)
454+void DirectionalDragAreaPrivate::touchEvent_recognized(QTouchEvent *event)
492{
455{
493 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
456 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
494
457
495 if (!touchPoint) {
458 if (!touchPoint) {
496− qCritical() << "DirectionalDragArea[status=Recognized]: touch " << m_touchId
459+ qCritical() << "DirectionalDragArea[status=Recognized]: touch " << touchId
497 << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "
460 << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "
498 "Considering it as released.";
461 "Considering it as released.";
499 setStatus(WaitingForTouch);
462 setStatus(WaitingForTouch);
500 } else {
463 } else {
501− setPreviousPos(touchPoint->pos());
464+ setPublicPos(touchPoint->pos());
502− setPreviousScenePos(touchPoint->scenePos());
465+ setPublicScenePos(touchPoint->scenePos());
503
466
504 if (touchPoint->state() == Qt::TouchPointReleased) {
467 if (touchPoint->state() == Qt::TouchPointReleased) {
505− emitSignalIfTapped();
506 setStatus(WaitingForTouch);
468 setStatus(WaitingForTouch);
507 }
469 }
508 }
470 }
509}
471}
510
472
511−void DirectionalDragArea::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
473+void DirectionalDragAreaPrivate::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
512{
474{
513 for (int i = 0; i < touchPoints.count(); ++i) {
475 for (int i = 0; i < touchPoints.count(); ++i) {
514 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
476 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
515 if (touchPoint.state() == Qt::TouchPointPressed) {
477 if (touchPoint.state() == Qt::TouchPointPressed) {
516− TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), this);
478+ TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), q);
517 }
479 }
518 }
480 }
519}
481}
520
482
521−bool DirectionalDragArea::recognitionIsDisabled() const
483+bool DirectionalDragAreaPrivate::recognitionIsDisabled() const
522{
484{
523− return distanceThreshold() <= 0 && compositionTime() <= 0;
485+ return immediateRecognition || (distanceThreshold <= 0 && compositionTime <= 0);
524}
486}
525
487
526−void DirectionalDragArea::emitSignalIfTapped()
488+bool DirectionalDragAreaPrivate::sanityCheckRecognitionProperties()
527{
489{
528− qint64 touchDuration = m_timeSource->msecsSinceReference() - m_activeTouches.touchStartTime(m_touchId);
490+ return recognitionIsDisabled()
529− if (touchDuration <= maxTapDuration()) {
491+ || (distanceThreshold < maxDistance && compositionTime < maxTime);
530− Q_EMIT tapped();
531− }
532}
492}
533
493
534−const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouchEvent *event)
494+const QTouchEvent::TouchPoint *DirectionalDragAreaPrivate::fetchTargetTouchPoint(QTouchEvent *event)
535{
495{
536 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
496 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
537 const QTouchEvent::TouchPoint *touchPoint = 0;
497 const QTouchEvent::TouchPoint *touchPoint = 0;
538 for (int i = 0; i < touchPoints.size(); ++i) {
498 for (int i = 0; i < touchPoints.size(); ++i) {
539− if (touchPoints.at(i).id() == m_touchId) {
499+ if (touchPoints.at(i).id() == touchId) {
540 touchPoint = &touchPoints.at(i);
500 touchPoint = &touchPoints.at(i);
541 break;
501 break;
542 }
502 }
@@ -544,39 +504,13 @@ const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouch
544 return touchPoint;
504 return touchPoint;
545}
505}
546
506
547−bool DirectionalDragArea::pointInsideAllowedArea() const
507+bool DirectionalDragAreaPrivate::movingInRightDirection() const
548{
508{
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 || direction == Direction::Vertical) {
576 return true;
510 return true;
577 } else {
511 } else {
578− QPointF movementVector(m_dampedScenePos.x() - m_previousDampedScenePos.x(),
512+ QPointF movementVector(dampedScenePos.x() - previousDampedScenePos.x(),
579− m_dampedScenePos.y() - m_previousDampedScenePos.y());
513+ dampedScenePos.y() - previousDampedScenePos.y());
580
514
581 qreal scalarProjection = projectOntoDirectionVector(movementVector);
515 qreal scalarProjection = projectOntoDirectionVector(movementVector);
582
516
@@ -584,96 +518,95 @@ bool DirectionalDragArea::movingInRightDirection() const
584 }
518 }
585}
519}
586
520
587−bool DirectionalDragArea::movedFarEnough(const QPointF &point) const
521+bool DirectionalDragAreaPrivate::movedFarEnoughAlongGestureAxis() const
588{
522{
589− if (m_distanceThreshold <= 0.) {
523+ if (distanceThreshold <= 0.) {
590 // distance threshold check is disabled
524 // distance threshold check is disabled
591 return true;
525 return true;
592 } else {
526 } else {
593− QPointF totalMovement(point.x() - m_startScenePos.x(),
527+ QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
594− point.y() - m_startScenePos.y());
528+ dampedScenePos.y() - startScenePos.y());
595
529
596− qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() +
530+ qreal scalarProjection = projectOntoDirectionVector(totalMovement);
597− totalMovement.y() * totalMovement.y();
598
531
599− return squaredTotalMovSize > m_distanceThresholdSquared;
532+ ddaDebug(" movedFarEnoughAlongGestureAxis: scalarProjection=" << scalarProjection
533+ << ", distanceThreshold=" << distanceThreshold);
534+
535+ if (direction == Direction::Horizontal || direction == Direction::Vertical) {
536+ return qAbs(scalarProjection) > distanceThreshold;
537+ } else {
538+ return scalarProjection > distanceThreshold;
539+ }
600 }
540 }
601}
541}
602
542
603−void DirectionalDragArea::checkSpeed()
543+bool DirectionalDragAreaPrivate::isPastMaxDistance() const
604{
544{
605− Q_ASSERT(m_status == Undecided);
545+ QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
546+ dampedScenePos.y() - startScenePos.y());
606
547
607− if (m_velocityCalculator->numSamples() >= AxisVelocityCalculator::MIN_SAMPLES_NEEDED) {
548+ qreal squaredDistance = totalMovement.x()*totalMovement.x() + totalMovement.y()*totalMovement.y();
608− qreal speed = qFabs(m_velocityCalculator->calculate());
549+ return squaredDistance > maxDistance*maxDistance;
609− qreal minSpeedMsecs = m_minSpeed / 1000.0;
550+}
610
551
611− if (speed < minSpeedMsecs) {
552+void DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible()
612− ddaDebug("Rejecting gesture because it's below minimum speed.");
553+{
554+ if (!q->isEnabled() || !q->isVisible()) {
555+ if (status == Undecided) {
613− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
556+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
557+ // We still wanna know when it ends for keeping the composition time window up-to-date
614− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
558+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
615− setStatus(WaitingForTouch);
616 }
559 }
617− }
618−
619− if (m_velocityCalculator->numSamples() == m_numSamplesOnLastSpeedCheck) {
620− m_silenceTime += m_recognitionTimer->interval();
621
560
622− if (m_silenceTime > m_maxSilenceTime) {
561+ if (status != WaitingForTouch) {
623− ddaDebug("Rejecting gesture because its silence time has been exceeded.");
562+ ddaDebug("Resetting status because got disabled or made invisible");
624− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
625− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
626 setStatus(WaitingForTouch);
563 setStatus(WaitingForTouch);
627 }
564 }
628− } else {
629− m_silenceTime = 0;
630 }
565 }
631− m_numSamplesOnLastSpeedCheck = m_velocityCalculator->numSamples();
632}
566}
633
567
634−void DirectionalDragArea::giveUpIfDisabledOrInvisible()
568+void DirectionalDragAreaPrivate::rejectGesture()
635{
569{
636− if (!isEnabled() || !isVisible()) {
637− if (m_status == Undecided) {
570+ if (status == Undecided) {
638− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
571+ ddaDebug("Rejecting gesture because it's taking too long to drag beyond the threshold.");
639− // We still wanna know when it ends for keeping the composition time window up-to-date
640− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
641− }
642
572
643− if (m_status != WaitingForTouch) {
573+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
644− ddaDebug("Resetting status because got disabled or made invisible");
574+ // We still wanna know when it ends for keeping the composition time window up-to-date
575+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
576+
645− setStatus(WaitingForTouch);
577+ setStatus(WaitingForTouch);
646− }
647 }
578 }
648}
579}
649
580
650−void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
581+void DirectionalDragAreaPrivate::setStatus(Status newStatus)
651{
582{
652− if (newStatus == m_status)
583+ if (newStatus == status)
653 return;
584 return;
654
585
655− DirectionalDragArea::Status oldStatus = m_status;
586+ Status oldStatus = status;
656
587
657 if (oldStatus == Undecided) {
588 if (oldStatus == Undecided) {
658− m_recognitionTimer->stop();
589+ recognitionTimer->stop();
659 }
590 }
660
591
661− m_status = newStatus;
592+ status = newStatus;
662− Q_EMIT statusChanged(m_status);
593+ Q_EMIT statusChanged(status);
663
594
664 ddaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus));
595 ddaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus));
665
596
666 switch (newStatus) {
597 switch (newStatus) {
667 case WaitingForTouch:
598 case WaitingForTouch:
599+ if (oldStatus == Recognized) {
668− Q_EMIT draggingChanged(false);
600+ Q_EMIT q->draggingChanged(false);
601+ }
602+ Q_EMIT q->pressedChanged(false);
669 break;
603 break;
670 case Undecided:
604 case Undecided:
671− m_recognitionTimer->start();
605+ recognitionTimer->start();
672− Q_EMIT draggingChanged(true);
606+ Q_EMIT q->pressedChanged(true);
673 break;
607 break;
674 case Recognized:
608 case Recognized:
675− if (oldStatus == WaitingForTouch)
676− Q_EMIT draggingChanged(true);
609+ Q_EMIT q->draggingChanged(true);
677 break;
610 break;
678 default:
611 default:
679 // no-op
612 // no-op
@@ -681,77 +614,128 @@ void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
681 }
614 }
682}
615}
683
616
684−void DirectionalDragArea::setPreviousPos(const QPointF &point)
617+void DirectionalDragAreaPrivate::setPublicPos(const QPointF &point)
685{
618{
686− bool xChanged = m_previousPos.x() != point.x();
619+ bool xChanged = publicPos.x() != point.x();
687− bool yChanged = m_previousPos.y() != point.y();
620+ bool yChanged = publicPos.y() != point.y();
688
621
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+ }
690
642
691 if (xChanged) {
643 if (xChanged) {
692− Q_EMIT touchXChanged(point.x());
644+ Q_EMIT q->touchXChanged(publicPos.x());
693− if (Direction::isHorizontal(m_direction))
645+ if (Direction::isHorizontal(direction))
694− Q_EMIT distanceChanged(distance());
646+ Q_EMIT q->distanceChanged(q->distance());
695 }
647 }
696
648
697 if (yChanged) {
649 if (yChanged) {
698− Q_EMIT touchYChanged(point.y());
650+ Q_EMIT q->touchYChanged(publicPos.y());
699− if (Direction::isVertical(m_direction))
651+ if (Direction::isVertical(direction))
700− Q_EMIT distanceChanged(distance());
652+ Q_EMIT q->distanceChanged(q->distance());
701 }
653 }
702}
654}
703
655
704−void DirectionalDragArea::setPreviousScenePos(const QPointF &point)
656+void DirectionalDragAreaPrivate::setPublicScenePos(const QPointF &point)
705{
657{
706− bool xChanged = m_previousScenePos.x() != point.x();
658+ bool xChanged = publicScenePos.x() != point.x();
707− bool yChanged = m_previousScenePos.y() != point.y();
659+ bool yChanged = publicScenePos.y() != point.y();
708
660
709 if (!xChanged && !yChanged)
661 if (!xChanged && !yChanged)
710 return;
662 return;
711
663
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+
712− qreal oldSceneDistance = sceneDistance();
668+ qreal oldSceneDistance = sceneDistance;
713− m_previousScenePos = point;
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+
714 updateSceneDistance();
687 updateSceneDistance();
715
688
716− if (oldSceneDistance != sceneDistance()) {
689+ if (oldSceneDistance != sceneDistance) {
717− Q_EMIT sceneDistanceChanged(sceneDistance());
690+ Q_EMIT q->sceneDistanceChanged(sceneDistance);
718 }
691 }
719
692
720 if (xChanged) {
693 if (xChanged) {
721− Q_EMIT touchSceneXChanged(point.x());
694+ Q_EMIT q->touchSceneXChanged(publicScenePos.x());
722 }
695 }
723
696
724 if (yChanged) {
697 if (yChanged) {
725− Q_EMIT touchSceneYChanged(point.y());
698+ Q_EMIT q->touchSceneYChanged(publicScenePos.y());
726 }
699 }
727}
700}
728
701
729−void DirectionalDragArea::updateVelocityCalculator(const QPointF &scenePos)
702+bool DirectionalDragAreaPrivate::isWithinTouchCompositionWindow()
730{
703{
731− QPointF totalSceneMovement = scenePos - m_startScenePos;
704+ return
705+ compositionTime > 0 &&
706+ !activeTouches.isEmpty() &&
707+ timeSource->msecsSinceReference() <=
708+ activeTouches.mostRecentStartTime() + (qint64)compositionTime;
709+}
732
710
733− qreal scalarProjection = projectOntoDirectionVector(totalSceneMovement);
711+void DirectionalDragArea::itemChange(ItemChange change, const ItemChangeData &value)
712+{
713+ if (change == QQuickItem::ItemSceneChange) {
714+ if (value.window != nullptr) {
715+ value.window->installEventFilter(TouchRegistry::instance());
734
716
735− m_velocityCalculator->setTrackedPosition(scalarProjection);
717+ // TODO: Handle window->screen() changes (ie window changing screens)
718+ qreal pixelsPerMm = value.window->screen()->physicalDotsPerInch() / 25.4;
719+ d->setPixelsPerMm(pixelsPerMm);
720+ }
721+ }
736}
722}
737
723
738−bool DirectionalDragArea::isWithinTouchCompositionWindow()
724+void DirectionalDragAreaPrivate::setPixelsPerMm(qreal pixelsPerMm)
739{
725{
740− return
726+ dampedScenePos.setMaxDelta(1. * pixelsPerMm);
741− compositionTime() > 0 &&
727+ setDistanceThreshold(4. * pixelsPerMm);
742− !m_activeTouches.isEmpty() &&
728+ maxDistance = 10. * pixelsPerMm;
743− m_timeSource->msecsSinceReference() <=
744− m_activeTouches.mostRecentStartTime() + (qint64)compositionTime();
745}
729}
746
730
747//************************** ActiveTouchesInfo **************************
731//************************** ActiveTouchesInfo **************************
748
732
749−DirectionalDragArea::ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
733+ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
750 : m_timeSource(timeSource)
734 : m_timeSource(timeSource)
751{
735{
752}
736}
753
737
754−void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
738+void ActiveTouchesInfo::update(QTouchEvent *event)
755{
739{
756 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
740 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
757 // nothing to update
741 // nothing to update
@@ -773,7 +757,7 @@ void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
773}
757}
774
758
775#if ACTIVETOUCHESINFO_DEBUG
759#if ACTIVETOUCHESINFO_DEBUG
776−QString DirectionalDragArea::ActiveTouchesInfo::toString()
760+QString ActiveTouchesInfo::toString()
777{
761{
778 QString string = "(";
762 QString string = "(";
779
763
@@ -791,7 +775,7 @@ QString DirectionalDragArea::ActiveTouchesInfo::toString()
791}
775}
792#endif // ACTIVETOUCHESINFO_DEBUG
776#endif // ACTIVETOUCHESINFO_DEBUG
793
777
794−void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(int touchId)
778+void ActiveTouchesInfo::addTouchPoint(int touchId)
795{
779{
796 ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot();
780 ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot();
797 activeTouchInfo.id = touchId;
781 activeTouchInfo.id = touchId;
@@ -802,7 +786,7 @@ void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(int touchId)
802 #endif
786 #endif
803}
787}
804
788
805−qint64 DirectionalDragArea::ActiveTouchesInfo::touchStartTime(int touchId)
789+qint64 ActiveTouchesInfo::touchStartTime(int touchId)
806{
790{
807 qint64 result = -1;
791 qint64 result = -1;
808
792
@@ -819,7 +803,7 @@ qint64 DirectionalDragArea::ActiveTouchesInfo::touchStartTime(int touchId)
819 return result;
803 return result;
820}
804}
821
805
822−void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(int touchId)
806+void ActiveTouchesInfo::removeTouchPoint(int touchId)
823{
807{
824 m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
808 m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
825 if (touchId == touchInfo->id) {
809 if (touchId == touchInfo->id) {
@@ -835,7 +819,7 @@ void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(int touchId)
835 #endif
819 #endif
836}
820}
837
821
838−qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
822+qint64 ActiveTouchesInfo::mostRecentStartTime()
839{
823{
840 Q_ASSERT(!m_touchInfoPool.isEmpty());
824 Q_ASSERT(!m_touchInfoPool.isEmpty());
841
825
@@ -851,16 +835,17 @@ qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
851 return highestStartTime;
835 return highestStartTime;
852}
836}
853
837
854−void DirectionalDragArea::updateSceneDirectionVector()
838+void DirectionalDragAreaPrivate::updateSceneDirectionVector()
855{
839{
856 QPointF localOrigin(0., 0.);
840 QPointF localOrigin(0., 0.);
857 QPointF localDirection;
841 QPointF localDirection;
858− switch (m_direction) {
842+ switch (direction) {
859 case Direction::Upwards:
843 case Direction::Upwards:
860 localDirection.rx() = 0.;
844 localDirection.rx() = 0.;
861 localDirection.ry() = -1.;
845 localDirection.ry() = -1.;
862 break;
846 break;
863 case Direction::Downwards:
847 case Direction::Downwards:
848+ case Direction::Vertical:
864 localDirection.rx() = 0.;
849 localDirection.rx() = 0.;
865 localDirection.ry() = 1;
850 localDirection.ry() = 1;
866 break;
851 break;
@@ -873,14 +858,31 @@ void DirectionalDragArea::updateSceneDirectionVector()
873 localDirection.ry() = 0.;
858 localDirection.ry() = 0.;
874 break;
859 break;
875 }
860 }
876− QPointF sceneOrigin = mapToScene(localOrigin);
861+ QPointF sceneOrigin = q->mapToScene(localOrigin);
877− QPointF sceneDirection = mapToScene(localDirection);
862+ QPointF sceneDirection = q->mapToScene(localDirection);
878− m_sceneDirectionVector = sceneDirection - sceneOrigin;
863+ sceneDirectionVector = sceneDirection - sceneOrigin;
864+}
865+
866+qreal DirectionalDragAreaPrivate::projectOntoDirectionVector(const QPointF &sceneVector) const
867+{
868+ // same as dot product as sceneDirectionVector is a unit vector
869+ return sceneVector.x() * sceneDirectionVector.x() +
870+ sceneVector.y() * sceneDirectionVector.y();
879}
871}
880
872
881−qreal DirectionalDragArea::projectOntoDirectionVector(const QPointF &sceneVector) const
873+DirectionalDragAreaPrivate::DirectionalDragAreaPrivate(DirectionalDragArea *q)
874+ : q(q)
875+ , status(WaitingForTouch)
876+ , sceneDistance(0)
877+ , touchId(-1)
878+ , direction(Direction::Rightwards)
879+ , distanceThreshold(0)
880+ , distanceThresholdSquared(0.)
881+ , maxTime(400)
882+ , compositionTime(60)
883+ , immediateRecognition(false)
884+ , recognitionTimer(nullptr)
885+ , timeSource(new RealTimeSource)
886+ , activeTouches(timeSource)
882{
887{
883− // same as dot product as m_sceneDirectionVector is a unit vector
884− return sceneVector.x() * m_sceneDirectionVector.x() +
885− sceneVector.y() * m_sceneDirectionVector.y();
886}
888}
plugins/Ubuntu/Gestures/DirectionalDragArea.h+30−203
@@ -18,7 +18,6 @@
18#define DIRECTIONAL_DRAG_AREA_H
18#define DIRECTIONAL_DRAG_AREA_H
19
19
20#include <QtQuick/QQuickItem>
20#include <QtQuick/QQuickItem>
21−#include "AxisVelocityCalculator.h"
22#include "UbuntuGesturesQmlGlobal.h"
21#include "UbuntuGesturesQmlGlobal.h"
23#include "Damper.h"
22#include "Damper.h"
24#include "Direction.h"
23#include "Direction.h"
@@ -29,6 +28,7 @@
29
28
30class TouchOwnershipEvent;
29class TouchOwnershipEvent;
31class UnownedTouchEvent;
30class UnownedTouchEvent;
31+class DirectionalDragAreaPrivate;
32
32
33/*
33/*
34 An area that detects axis-aligned single-finger drag gestures
34 An area that detects axis-aligned single-finger drag gestures
@@ -61,95 +61,31 @@ class UBUNTUGESTURESQML_EXPORT DirectionalDragArea : public QQuickItem {
61 Q_PROPERTY(qreal touchSceneX READ touchSceneX NOTIFY touchSceneXChanged)
61 Q_PROPERTY(qreal touchSceneX READ touchSceneX NOTIFY touchSceneXChanged)
62 Q_PROPERTY(qreal touchSceneY READ touchSceneY NOTIFY touchSceneYChanged)
62 Q_PROPERTY(qreal touchSceneY READ touchSceneY NOTIFY touchSceneYChanged)
63
63
64− // The current status of the directional drag gesture area.
65− Q_PROPERTY(Status status READ status NOTIFY statusChanged)
66−
67 // Whether a drag gesture is taking place
64 // Whether a drag gesture is taking place
68− // This will be true as long as status is Undecided or Recognized
69− // When a gesture gets rejected, dragging turns to false.
70 Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged)
65 Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged)
71
66
72− /////
67+ // Whether the drag area is pressed.
73− // stuff that will be set in stone at some point
68+ Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged)
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)
109
69
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.
110 //
73 //
111− /////
74+ // It's false by default. In most cases you will not want that enabled.
112−
75+ Q_PROPERTY(bool immediateRecognition
113− // Maximum time (in milliseconds) after the start of a given touch point where
76+ READ immediateRecognition
114− // subsequent touch starts are grouped with the first one into an N-touches gesture
77+ WRITE setImmediateRecognition
115− // (e.g. a two-fingers tap or drag).
78+ NOTIFY immediateRecognitionChanged)
116− Q_PROPERTY(int compositionTime READ compositionTime
117− WRITE setCompositionTime
118− NOTIFY compositionTimeChanged)
119
79
120 Q_ENUMS(Direction)
80 Q_ENUMS(Direction)
121− Q_ENUMS(Status)
122public:
81public:
123 DirectionalDragArea(QQuickItem *parent = 0);
82 DirectionalDragArea(QQuickItem *parent = 0);
124
83
125 Direction::Type direction() const;
84 Direction::Type direction() const;
126 void setDirection(Direction::Type);
85 void setDirection(Direction::Type);
127
86
128− // Describes the state of the directional drag gesture.
129− enum Status {
130− // Waiting for a new touch point to land on this area. No gesture is being processed
131− // or tracked.
132− WaitingForTouch,
133−
134− // A touch point has landed on this area but it's not know yet whether it is
135− // performing a drag in the correct direction.
136− // If it's decided that the touch point is not performing a directional drag gesture,
137− // it will be rejected/ignored and status will return to WaitingForTouch.
138− Undecided, //Recognizing,
139−
140− // There's a touch point in this area and it performed a drag in the correct
141− // direction.
142− //
143− // Once recognized, the gesture state will move back to WaitingForTouch only once
144− // that touch point ends. The gesture will remain in the Recognized state even if
145− // the touch point starts moving in other directions or halts.
146− Recognized,
147− };
148− Status status() const { return m_status; }
149−
150 qreal distance() const;
87 qreal distance() const;
151 qreal sceneDistance() const;
88 qreal sceneDistance() const;
152− void updateSceneDistance();
153
89
154 qreal touchX() const;
90 qreal touchX() const;
155 qreal touchY() const;
91 qreal touchY() const;
@@ -157,152 +93,43 @@ public:
157 qreal touchSceneX() const;
93 qreal touchSceneX() const;
158 qreal touchSceneY() const;
94 qreal touchSceneY() const;
159
95
160− bool dragging() const { return (m_status == Undecided) || (m_status == Recognized); }
96+ bool dragging() const;
161−
162− qreal maxDeviation() const { return m_dampedScenePos.maxDelta(); }
163− void setMaxDeviation(qreal value);
164−
165− qreal wideningAngle() const;
166− void setWideningAngle(qreal value);
167−
168− qreal distanceThreshold() const { return m_distanceThreshold; }
169− void setDistanceThreshold(qreal value);
170−
171− qreal minSpeed() const { return m_minSpeed; }
172− void setMinSpeed(qreal value);
173
97
174− int maxSilenceTime() const { return m_maxSilenceTime; }
98+ bool pressed() const;
175− void setMaxSilenceTime(int value);
176
99
177− int compositionTime() const { return m_compositionTime; }
100+ bool immediateRecognition() const;
178− void setCompositionTime(int value);
101+ void setImmediateRecognition(bool enabled);
179−
180− // Replaces the existing Timer with the given one.
181− //
182− // Useful for providing a fake timer when testing.
183− void setRecognitionTimer(UbuntuGestures::AbstractTimer *timer);
184−
185− // Useful for testing, where a fake time source can be supplied
186− void setTimeSource(const UbuntuGestures::SharedTimeSource &timeSource);
187
102
188 bool event(QEvent *e) override;
103 bool event(QEvent *e) override;
189
104
190− // Maximum time, in milliseconds, between a press and a release, for a touch
105+ /*
191− // sequence to be considered a tap.
106+ In qmltests, sequences of touch events are sent all at once, unlike in "real life".
192− int maxTapDuration() const { return 300; }
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();
193
114
194Q_SIGNALS:
115Q_SIGNALS:
195 void directionChanged(Direction::Type direction);
116 void directionChanged(Direction::Type direction);
196− void statusChanged(Status value);
197 void draggingChanged(bool value);
117 void draggingChanged(bool value);
118+ void pressedChanged(bool value);
198 void distanceChanged(qreal value);
119 void distanceChanged(qreal value);
199 void sceneDistanceChanged(qreal value);
120 void sceneDistanceChanged(qreal value);
200− void maxDeviationChanged(qreal value);
201− void wideningAngleChanged(qreal value);
202− void distanceThresholdChanged(qreal value);
203− void minSpeedChanged(qreal value);
204− void maxSilenceTimeChanged(int value);
205− void compositionTimeChanged(int value);
206 void touchXChanged(qreal value);
121 void touchXChanged(qreal value);
207 void touchYChanged(qreal value);
122 void touchYChanged(qreal value);
208 void touchSceneXChanged(qreal value);
123 void touchSceneXChanged(qreal value);
209 void touchSceneYChanged(qreal value);
124 void touchSceneYChanged(qreal value);
210−
125+ void immediateRecognitionChanged(bool value);
211− // TODO: I would rather not have such signal as it has nothing to do with drag gestures.
212− // Remove when no longer used or move its implementation to the QML code that uses it
213− // See maxTapDuration()
214− void tapped();
215
126
216protected:
127protected:
217− virtual void touchEvent(QTouchEvent *event);
128+ void touchEvent(QTouchEvent *event) override;
218−
129+ void itemChange(ItemChange change, const ItemChangeData &value) override;
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
130
305− friend class tst_DirectionalDragArea;
131+public: // so tests can access it
132+ DirectionalDragAreaPrivate *d;
306};
133};
307
134
308#endif // DIRECTIONAL_DRAG_AREA_H
135#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/FloatingFlickable.cppnew file+150
@@ -0,0 +1,150 @@
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 "FloatingFlickable.h"
18+
19+#include <private/qquickflickable_p.h>
20+#include "DirectionalDragArea.h"
21+
22+#include <QDebug>
23+
24+FloatingFlickable::FloatingFlickable(QQuickItem *parent)
25+ : QQuickItem(parent)
26+ , m_mousePressed(false)
27+{
28+ m_dragArea = new DirectionalDragArea(this);
29+ m_dragArea->setWidth(width());
30+ m_dragArea->setHeight(height());
31+ m_dragArea->setDirection(Direction::Horizontal);
32+ connect(m_dragArea, &DirectionalDragArea::touchXChanged,
33+ this, &FloatingFlickable::onDragAreaTouchPosChanged);
34+ connect(m_dragArea, &DirectionalDragArea::touchYChanged,
35+ this, &FloatingFlickable::onDragAreaTouchPosChanged);
36+ connect(m_dragArea, &DirectionalDragArea::draggingChanged,
37+ this, &FloatingFlickable::onDragAreaDraggingChanged);
38+ connect(m_dragArea, &DirectionalDragArea::directionChanged, this, &FloatingFlickable::directionChanged);
39+
40+ m_flickable = new QQuickFlickable(this);
41+ m_flickable->setEnabled(false);
42+ m_flickable->setWidth(width());
43+ m_flickable->setHeight(height());
44+ connect(m_flickable, &QQuickFlickable::contentWidthChanged, this, &FloatingFlickable::contentWidthChanged);
45+ connect(m_flickable, &QQuickFlickable::contentHeightChanged, this, &FloatingFlickable::contentHeightChanged);
46+ connect(m_flickable, &QQuickFlickable::contentXChanged, this, &FloatingFlickable::contentXChanged);
47+ connect(m_flickable, &QQuickFlickable::contentYChanged, this, &FloatingFlickable::contentYChanged);
48+
49+ connect(this, &QQuickItem::widthChanged, this, &FloatingFlickable::updateChildrenWidth);
50+ connect(this, &QQuickItem::heightChanged, this, &FloatingFlickable::updateChildrenHeight);
51+}
52+
53+qreal FloatingFlickable::contentWidth() const
54+{
55+ return m_flickable->contentWidth();
56+}
57+
58+void FloatingFlickable::setContentWidth(qreal contentWidth)
59+{
60+ m_flickable->setContentWidth(contentWidth);
61+}
62+
63+qreal FloatingFlickable::contentHeight() const
64+{
65+ return m_flickable->contentHeight();
66+}
67+
68+void FloatingFlickable::setContentHeight(qreal contentHeight)
69+{
70+ m_flickable->setContentHeight(contentHeight);
71+}
72+
73+qreal FloatingFlickable::contentX() const
74+{
75+ return m_flickable->contentX();
76+}
77+
78+void FloatingFlickable::setContentX(qreal contentX)
79+{
80+ m_flickable->setContentX(contentX);
81+}
82+
83+qreal FloatingFlickable::contentY() const
84+{
85+ return m_flickable->contentY();
86+}
87+
88+void FloatingFlickable::setContentY(qreal contentY)
89+{
90+ m_flickable->setContentY(contentY);
91+}
92+
93+Direction::Type FloatingFlickable::direction() const
94+{
95+ return m_dragArea->direction();
96+}
97+
98+void FloatingFlickable::setDirection(Direction::Type direction)
99+{
100+ m_dragArea->setDirection(direction);
101+ if (Direction::isHorizontal(direction)) {
102+ m_flickable->setFlickableDirection(QQuickFlickable::HorizontalFlick);
103+ } else {
104+ m_flickable->setFlickableDirection(QQuickFlickable::VerticalFlick);
105+ }
106+}
107+
108+void FloatingFlickable::updateChildrenWidth()
109+{
110+ m_dragArea->setWidth(width());
111+ m_flickable->setWidth(width());
112+}
113+
114+void FloatingFlickable::updateChildrenHeight()
115+{
116+ m_dragArea->setHeight(height());
117+ m_flickable->setHeight(height());
118+}
119+
120+void FloatingFlickable::onDragAreaTouchPosChanged(qreal)
121+{
122+ if (m_mousePressed) {
123+ QMouseEvent mouseEvent(QEvent::MouseMove,
124+ QPointF(m_dragArea->touchX(),m_dragArea->touchY()),
125+ Qt::NoButton, Qt::LeftButton, Qt::NoModifier);
126+
127+ QCoreApplication::sendEvent(m_flickable, &mouseEvent);
128+
129+ }
130+}
131+
132+void FloatingFlickable::onDragAreaDraggingChanged(bool dragging)
133+{
134+ if (dragging && !m_mousePressed) {
135+ QMouseEvent mouseEvent(QEvent::MouseButtonPress,
136+ QPointF(m_dragArea->touchX(),m_dragArea->touchY()),
137+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
138+
139+ QCoreApplication::sendEvent(m_flickable, &mouseEvent);
140+ m_mousePressed = true;
141+
142+ } else if (!dragging && m_mousePressed) {
143+ QMouseEvent mouseEvent(QEvent::MouseButtonRelease,
144+ QPointF(m_dragArea->touchX(),m_dragArea->touchY()),
145+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
146+
147+ QCoreApplication::sendEvent(m_flickable, &mouseEvent);
148+ m_mousePressed = false;
149+ }
150+}
plugins/Ubuntu/Gestures/FloatingFlickable.hnew file+86
@@ -0,0 +1,86 @@
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 FLOATING_FLICKABLE_H
18+#define FLOATING_FLICKABLE_H
19+
20+#include <QQuickItem>
21+#include "UbuntuGesturesQmlGlobal.h"
22+#include "Direction.h"
23+
24+class DirectionalDragArea;
25+class QQuickFlickable;
26+
27+/*
28+ A Flickable that can be put in front of the item to be flicked and
29+ still have the item-to-be-flicked receive input events that are not flicks.
30+
31+ Ie, it's a Flickable that, input-wise, is transparent to non-flick gestures.
32+
33+ With a regular Flickable you would have to make the item-to-be-flicked a child
34+ of Flicakble to achieve the same result. FloatingFlickable has no such requirement
35+ or limitation.
36+ */
37+class UBUNTUGESTURESQML_EXPORT FloatingFlickable : public QQuickItem {
38+ Q_OBJECT
39+
40+ Q_PROPERTY(qreal contentWidth READ contentWidth WRITE setContentWidth NOTIFY contentWidthChanged)
41+ Q_PROPERTY(qreal contentHeight READ contentHeight WRITE setContentHeight NOTIFY contentHeightChanged)
42+ Q_PROPERTY(qreal contentX READ contentX WRITE setContentX NOTIFY contentXChanged)
43+ Q_PROPERTY(qreal contentY READ contentY WRITE setContentY NOTIFY contentYChanged)
44+
45+ Q_PROPERTY(Direction::Type direction READ direction WRITE setDirection NOTIFY directionChanged)
46+
47+Q_SIGNALS:
48+ void contentWidthChanged();
49+ void contentHeightChanged();
50+ void contentXChanged();
51+ void contentYChanged();
52+ void directionChanged();
53+
54+public:
55+ FloatingFlickable(QQuickItem *parent = nullptr);
56+
57+ qreal contentWidth() const;
58+ void setContentWidth(qreal contentWidth);
59+
60+ qreal contentHeight() const;
61+ void setContentHeight(qreal contentHeight);
62+
63+ qreal contentX() const;
64+ void setContentX(qreal contentX);
65+
66+ qreal contentY() const;
67+ void setContentY(qreal contentY);
68+
69+ Direction::Type direction() const;
70+ void setDirection(Direction::Type);
71+
72+private Q_SLOTS:
73+ void updateChildrenWidth();
74+ void updateChildrenHeight();
75+ void onDragAreaTouchPosChanged(qreal);
76+ void onDragAreaDraggingChanged(bool value);
77+
78+private:
79+ DirectionalDragArea *m_dragArea;
80+ QQuickFlickable *m_flickable;
81+ bool m_mousePressed;
82+
83+ friend class tst_FloatingFlickable;
84+};
85+
86+#endif // FLOATING_FLICKABLE_H
plugins/Ubuntu/Gestures/TouchDispatcher.cpp+88−53
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -28,8 +28,11 @@
28#define TOUCHDISPATCHER_DEBUG 0
28#define TOUCHDISPATCHER_DEBUG 0
29
29
30#if TOUCHDISPATCHER_DEBUG
30#if TOUCHDISPATCHER_DEBUG
31+#define ugDebug(params) qDebug().nospace() << "[TouchDispatcher(" << this << ")] " << params
31#include <DebugHelpers.h>
32#include <DebugHelpers.h>
32−#endif
33+#else // TOUCHDISPATCHER_DEBUG
34+#define ugDebug(params) ((void)0)
35+#endif // TOUCHDISPATCHER_DEBUG
33
36
34TouchDispatcher::TouchDispatcher()
37TouchDispatcher::TouchDispatcher()
35 : m_status(NoActiveTouch)
38 : m_status(NoActiveTouch)
@@ -44,13 +47,12 @@ void TouchDispatcher::setTargetItem(QQuickItem *target)
44 m_targetItem = target;
47 m_targetItem = target;
45 if (m_status != NoActiveTouch) {
48 if (m_status != NoActiveTouch) {
46 qWarning("[TouchDispatcher] Changing target item in the middle of a touch stream");
49 qWarning("[TouchDispatcher] Changing target item in the middle of a touch stream");
47− m_status = TargetRejectedTouches;
50+ setStatus(TargetRejectedTouches);
48 }
51 }
49 }
52 }
50}
53}
51
54
52−void TouchDispatcher::dispatch(QEvent::Type eventType,
55+void TouchDispatcher::dispatch(QTouchDevice *device,
53− QTouchDevice *device,
54 Qt::KeyboardModifiers modifiers,
56 Qt::KeyboardModifiers modifiers,
55 const QList<QTouchEvent::TouchPoint> &touchPoints,
57 const QList<QTouchEvent::TouchPoint> &touchPoints,
56 QWindow *window,
58 QWindow *window,
@@ -61,6 +63,8 @@ void TouchDispatcher::dispatch(QEvent::Type eventType,
61 return;
63 return;
62 }
64 }
63
65
66+ QEvent::Type eventType = resolveEventType(touchPoints);
67+
64 if (eventType == QEvent::TouchBegin) {
68 if (eventType == QEvent::TouchBegin) {
65 dispatchTouchBegin(device, modifiers, touchPoints, window, timestamp);
69 dispatchTouchBegin(device, modifiers, touchPoints, window, timestamp);
66
70
@@ -72,15 +76,13 @@ void TouchDispatcher::dispatch(QEvent::Type eventType,
72 dispatchAsMouse(device, modifiers, touchPoints, timestamp);
76 dispatchAsMouse(device, modifiers, touchPoints, timestamp);
73 } else {
77 } else {
74 Q_ASSERT(m_status == TargetRejectedTouches);
78 Q_ASSERT(m_status == TargetRejectedTouches);
75− #if TOUCHDISPATCHER_DEBUG
76− qDebug() << "[TouchDispatcher] Not dispatching touch event to" << m_targetItem.data()
79+ ugDebug("Not dispatching touch event to " << m_targetItem.data()
77− << "because it already rejected the touch stream.";
80+ << "because it already rejected the touch stream.");
78− #endif
79 // Do nothing
81 // Do nothing
80 }
82 }
81
83
82 if (eventType == QEvent::TouchEnd) {
84 if (eventType == QEvent::TouchEnd) {
83− m_status = NoActiveTouch;
85+ setStatus(NoActiveTouch);
84 m_touchMouseId = -1;
86 m_touchMouseId = -1;
85 }
87 }
86
88
@@ -103,10 +105,7 @@ void TouchDispatcher::dispatchTouchBegin(
103 QQuickItem *targetItem = m_targetItem.data();
105 QQuickItem *targetItem = m_targetItem.data();
104
106
105 if (!targetItem->isEnabled() || !targetItem->isVisible()) {
107 if (!targetItem->isEnabled() || !targetItem->isVisible()) {
106− #if TOUCHDISPATCHER_DEBUG
107− qDebug() << "[TouchDispatcher] Cannot dispatch touch event to" << targetItem
108− << "because it's disabled or invisible.";
108+ ugDebug("Cannot dispatch touch event to " << targetItem << " because it's disabled or invisible.");
109− #endif
110 return;
109 return;
111 }
110 }
112
111
@@ -118,62 +117,46 @@ void TouchDispatcher::dispatchTouchBegin(
118 createQTouchEvent(QEvent::TouchBegin, device, modifiers, targetTouchPoints, window, timestamp));
117 createQTouchEvent(QEvent::TouchBegin, device, modifiers, targetTouchPoints, window, timestamp));
119
118
120
119
121− #if TOUCHDISPATCHER_DEBUG
122− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(touchEventToString(touchEvent.data()))
120+ ugDebug("dispatching " << qPrintable(touchEventToString(touchEvent.data()))
123− << "to" << targetItem;
121+ << " to " << targetItem);
124− #endif
125 QCoreApplication::sendEvent(targetItem, touchEvent.data());
122 QCoreApplication::sendEvent(targetItem, touchEvent.data());
126
123
127
124
128 if (touchEvent->isAccepted()) {
125 if (touchEvent->isAccepted()) {
129− #if TOUCHDISPATCHER_DEBUG
130− qDebug() << "[TouchDispatcher] Item accepted the touch event.";
126+ ugDebug("Item accepted the touch event.");
131− #endif
132− m_status = DeliveringTouchEvents;
127+ setStatus(DeliveringTouchEvents);
133 } else if (targetItem->acceptedMouseButtons() & Qt::LeftButton) {
128 } else if (targetItem->acceptedMouseButtons() & Qt::LeftButton) {
134− #if TOUCHDISPATCHER_DEBUG
135− qDebug() << "[TouchDispatcher] Item rejected the touch event. Trying a QMouseEvent";
129+ ugDebug("Item rejected the touch event. Trying a QMouseEvent");
136− #endif
137 // NB: Arbitrarily chose the first touch point to emulate the mouse pointer
130 // NB: Arbitrarily chose the first touch point to emulate the mouse pointer
138 QScopedPointer<QMouseEvent> mouseEvent(
131 QScopedPointer<QMouseEvent> mouseEvent(
139 touchToMouseEvent(QEvent::MouseButtonPress, targetTouchPoints.at(0), timestamp,
132 touchToMouseEvent(QEvent::MouseButtonPress, targetTouchPoints.at(0), timestamp,
140 modifiers, false /* transformNeeded */));
133 modifiers, false /* transformNeeded */));
141 Q_ASSERT(targetTouchPoints.at(0).state() == Qt::TouchPointPressed);
134 Q_ASSERT(targetTouchPoints.at(0).state() == Qt::TouchPointPressed);
142
135
143− #if TOUCHDISPATCHER_DEBUG
144− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(mouseEvent.data()))
136+ ugDebug("dispatching " << qPrintable(mouseEventToString(mouseEvent.data()))
145− << "to" << m_targetItem.data();
137+ << " to " << m_targetItem.data());
146− #endif
147 QCoreApplication::sendEvent(targetItem, mouseEvent.data());
138 QCoreApplication::sendEvent(targetItem, mouseEvent.data());
148 if (mouseEvent->isAccepted()) {
139 if (mouseEvent->isAccepted()) {
149− #if TOUCHDISPATCHER_DEBUG
150− qDebug() << "[TouchDispatcher] Item accepted the QMouseEvent.";
140+ ugDebug("Item accepted the QMouseEvent.");
151− #endif
152− m_status = DeliveringMouseEvents;
141+ setStatus(DeliveringMouseEvents);
153 m_touchMouseId = targetTouchPoints.at(0).id();
142 m_touchMouseId = targetTouchPoints.at(0).id();
154
143
155 if (checkIfDoubleClicked(timestamp)) {
144 if (checkIfDoubleClicked(timestamp)) {
156 QScopedPointer<QMouseEvent> doubleClickEvent(
145 QScopedPointer<QMouseEvent> doubleClickEvent(
157 touchToMouseEvent(QEvent::MouseButtonDblClick, targetTouchPoints.at(0), timestamp,
146 touchToMouseEvent(QEvent::MouseButtonDblClick, targetTouchPoints.at(0), timestamp,
158 modifiers, false /* transformNeeded */));
147 modifiers, false /* transformNeeded */));
159− #if TOUCHDISPATCHER_DEBUG
160− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(doubleClickEvent.data()))
148+ ugDebug("dispatching " << qPrintable(mouseEventToString(doubleClickEvent.data()))
161− << "to" << m_targetItem.data();
149+ << " to " << m_targetItem.data());
162− #endif
163 QCoreApplication::sendEvent(targetItem, doubleClickEvent.data());
150 QCoreApplication::sendEvent(targetItem, doubleClickEvent.data());
164 }
151 }
165
152
166 } else {
153 } else {
167− #if TOUCHDISPATCHER_DEBUG
168− qDebug() << "[TouchDispatcher] Item rejected the QMouseEvent.";
154+ ugDebug("Item rejected the QMouseEvent.");
169− #endif
170− m_status = TargetRejectedTouches;
155+ setStatus(TargetRejectedTouches);
171 }
156 }
172 } else {
157 } else {
173− #if TOUCHDISPATCHER_DEBUG
174− qDebug() << "[TouchDispatcher] Item rejected the touch event and does not accept mouse buttons.";
158+ ugDebug("Item rejected the touch event and does not accept mouse buttons.");
175− #endif
176− m_status = TargetRejectedTouches;
159+ setStatus(TargetRejectedTouches);
177 }
160 }
178}
161}
179
162
@@ -194,10 +177,8 @@ void TouchDispatcher::dispatchAsTouch(QEvent::Type eventType,
194 createQTouchEvent(eventType, device, modifiers, targetTouchPoints, window, timestamp));
177 createQTouchEvent(eventType, device, modifiers, targetTouchPoints, window, timestamp));
195
178
196
179
197− #if TOUCHDISPATCHER_DEBUG
198− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(touchEventToString(eventForTargetItem.data()))
180+ ugDebug("dispatching " << qPrintable(touchEventToString(eventForTargetItem.data()))
199− << "to" << targetItem;
181+ << " to " << targetItem);
200− #endif
201 QCoreApplication::sendEvent(targetItem, eventForTargetItem.data());
182 QCoreApplication::sendEvent(targetItem, eventForTargetItem.data());
202}
183}
203
184
@@ -254,10 +235,8 @@ void TouchDispatcher::dispatchAsMouse(
254 QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(eventType, *touchMouse, timestamp, modifiers,
235 QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(eventType, *touchMouse, timestamp, modifiers,
255 true /* transformNeeded */));
236 true /* transformNeeded */));
256
237
257− #if TOUCHDISPATCHER_DEBUG
258− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(mouseEvent.data()))
238+ ugDebug("dispatching " << qPrintable(mouseEventToString(mouseEvent.data()))
259− << "to" << m_targetItem.data();
239+ << " to " << m_targetItem.data());
260− #endif
261 QCoreApplication::sendEvent(m_targetItem.data(), mouseEvent.data());
240 QCoreApplication::sendEvent(m_targetItem.data(), mouseEvent.data());
262 }
241 }
263}
242}
@@ -365,3 +344,59 @@ bool TouchDispatcher::checkIfDoubleClicked(ulong newPressEventTimestamp)
365
344
366 return doubleClicked;
345 return doubleClicked;
367}
346}
347+
348+void TouchDispatcher::setStatus(Status status)
349+{
350+ if (status != m_status) {
351+ #if TOUCHDISPATCHER_DEBUG
352+ switch (status) {
353+ case NoActiveTouch:
354+ ugDebug("status = NoActiveTouch");
355+ break;
356+ case DeliveringTouchEvents:
357+ ugDebug("status = DeliveringTouchEvents");
358+ break;
359+ case DeliveringMouseEvents:
360+ ugDebug("status = DeliveringMouseEvents");
361+ break;
362+ case TargetRejectedTouches:
363+ ugDebug("status = TargetRejectedTouches");
364+ break;
365+ default:
366+ ugDebug("status = " << status);
367+ break;
368+ }
369+ #endif
370+ m_status = status;
371+ }
372+}
373+
374+void TouchDispatcher::reset()
375+{
376+ setStatus(NoActiveTouch);
377+ m_touchMouseId = -1;
378+ m_touchMousePressTimestamp =0;
379+}
380+
381+QEvent::Type TouchDispatcher::resolveEventType(const QList<QTouchEvent::TouchPoint> &touchPoints)
382+{
383+ QEvent::Type eventType;
384+
385+ Qt::TouchPointStates eventStates = 0;
386+ for (int i = 0; i < touchPoints.count(); i++)
387+ eventStates |= touchPoints[i].state();
388+
389+ switch (eventStates) {
390+ case Qt::TouchPointPressed:
391+ eventType = QEvent::TouchBegin;
392+ break;
393+ case Qt::TouchPointReleased:
394+ eventType = QEvent::TouchEnd;
395+ break;
396+ default:
397+ eventType = QEvent::TouchUpdate;
398+ break;
399+ }
400+
401+ return eventType;
402+}
plugins/Ubuntu/Gestures/TouchDispatcher.h+16−9
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -36,12 +36,20 @@ public:
36 void setTargetItem(QQuickItem *target);
36 void setTargetItem(QQuickItem *target);
37 QQuickItem *targetItem() { return m_targetItem; }
37 QQuickItem *targetItem() { return m_targetItem; }
38
38
39− void dispatch(QEvent::Type eventType,
40− QTouchDevice *device,
39+ void dispatch(QTouchDevice *device,
41 Qt::KeyboardModifiers modifiers,
40 Qt::KeyboardModifiers modifiers,
42 const QList<QTouchEvent::TouchPoint> &touchPoints,
41 const QList<QTouchEvent::TouchPoint> &touchPoints,
43 QWindow *window,
42 QWindow *window,
44 ulong timestamp);
43 ulong timestamp);
44+
45+ void reset();
46+
47+ enum Status {
48+ NoActiveTouch,
49+ DeliveringTouchEvents,
50+ DeliveringMouseEvents,
51+ TargetRejectedTouches
52+ };
45private:
53private:
46 void dispatchTouchBegin(
54 void dispatchTouchBegin(
47 QTouchDevice *device,
55 QTouchDevice *device,
@@ -73,14 +81,13 @@ private:
73
81
74 bool checkIfDoubleClicked(ulong newPressEventTimestamp);
82 bool checkIfDoubleClicked(ulong newPressEventTimestamp);
75
83
84+ void setStatus(Status status);
85+
86+ static QEvent::Type resolveEventType(const QList<QTouchEvent::TouchPoint> &touchPoints);
87+
76 QPointer<QQuickItem> m_targetItem;
88 QPointer<QQuickItem> m_targetItem;
77
89
78− enum {
79− NoActiveTouch,
80− DeliveringTouchEvents,
81− DeliveringMouseEvents,
82− TargetRejectedTouches
83− } m_status;
90+ Status m_status;
84
91
85 int m_touchMouseId;
92 int m_touchMouseId;
86 ulong m_touchMousePressTimestamp;
93 ulong m_touchMousePressTimestamp;
plugins/Ubuntu/Gestures/TouchGate.cpp+94−60
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -18,13 +18,24 @@
18
18
19#include <QCoreApplication>
19#include <QCoreApplication>
20#include <QDebug>
20#include <QDebug>
21+#include <QQuickWindow>
21
22
22#include <TouchOwnershipEvent.h>
23#include <TouchOwnershipEvent.h>
23#include <TouchRegistry.h>
24#include <TouchRegistry.h>
24
25
25#if TOUCHGATE_DEBUG
26#if TOUCHGATE_DEBUG
27+#define ugDebug(params) qDebug().nospace() << "[TouchGate(" << (void*)this << ")] " << params
26#include <DebugHelpers.h>
28#include <DebugHelpers.h>
29+#else // TOUCHGATE_DEBUG
30+#define ugDebug(params) ((void)0)
27−#endif
31+#endif // TOUCHGATE_DEBUG
32+
33+TouchGate::TouchGate(QQuickItem *parent)
34+ : QQuickItem(parent)
35+{
36+ connect(this, &QQuickItem::enabledChanged,
37+ this, &TouchGate::onEnabledChanged);
38+}
28
39
29bool TouchGate::event(QEvent *e)
40bool TouchGate::event(QEvent *e)
30{
41{
@@ -38,13 +49,12 @@ bool TouchGate::event(QEvent *e)
38
49
39void TouchGate::touchEvent(QTouchEvent *event)
50void TouchGate::touchEvent(QTouchEvent *event)
40{
51{
41− #if TOUCHGATE_DEBUG
42− qDebug() << "[TouchGate] got touch event" << qPrintable(touchEventToString(event));
52+ ugDebug("got touch event" << qPrintable(touchEventToString(event)));
43− #endif
44 event->accept();
53 event->accept();
45
54
46 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
55 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
56+ QList<QTouchEvent::TouchPoint> validTouchPoints;
47− bool goodToGo = true;
57+ bool ownsAllTouches = true;
48 for (int i = 0; i < touchPoints.count(); ++i) {
58 for (int i = 0; i < touchPoints.count(); ++i) {
49 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
59 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
50
60
@@ -55,31 +65,50 @@ void TouchGate::touchEvent(QTouchEvent *event)
55 TouchRegistry::instance()->requestTouchOwnership(touchPoint.id(), this);
65 TouchRegistry::instance()->requestTouchOwnership(touchPoint.id(), this);
56 }
66 }
57
67
58− goodToGo &= m_touchInfoMap.contains(touchPoint.id())
68+ if (m_touchInfoMap.contains(touchPoint.id())) {
59− && m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted;
69+ validTouchPoints.append(touchPoint);
60
70
71+ ownsAllTouches &= m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted;
72+
61− if (touchPoint.state() == Qt::TouchPointReleased && m_touchInfoMap.contains(touchPoint.id())) {
73+ if (touchPoint.state() == Qt::TouchPointReleased) {
62− m_touchInfoMap[touchPoint.id()].ended = true;
74+ m_touchInfoMap[touchPoint.id()].ended = true;
75+ }
63 }
76 }
64
77
65 }
78 }
66
79
67− if (goodToGo) {
80+ if (validTouchPoints.isEmpty()) {
81+ // nothing to do.
82+ return;
83+ }
84+
85+ if (ownsAllTouches) {
68 if (m_storedEvents.isEmpty()) {
86 if (m_storedEvents.isEmpty()) {
69 // let it pass through
87 // let it pass through
70− dispatchTouchEventToTarget(event);
88+ removeTouchInfoForEndedTouches(validTouchPoints);
89+ m_dispatcher.dispatch(event->device(), event->modifiers(), validTouchPoints,
90+ event->window(), event->timestamp());
71 } else {
91 } else {
72 // Retain the event to ensure TouchGate dispatches them in order.
92 // Retain the event to ensure TouchGate dispatches them in order.
73 // Otherwise the current event would come before the stored ones, which are older.
93 // Otherwise the current event would come before the stored ones, which are older.
74− #if TOUCHGATE_DEBUG
94+ ugDebug("Storing event because thouches " << qPrintable(oldestPendingTouchIdsString())
75− qDebug("[TouchGate] Storing event because thouches %s are still pending ownership.",
95+ << " are still pending ownership.");
76− qPrintable(oldestPendingTouchIdsString()));
77− #endif
78− storeTouchEvent(event);
96+ storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
97+ event->window(), event->timestamp());
79 }
98 }
80 } else {
99 } else {
81 // Retain events that have unowned touches
100 // Retain events that have unowned touches
82− storeTouchEvent(event);
101+ storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
102+ event->window(), event->timestamp());
103+ }
104+}
105+
106+void TouchGate::itemChange(ItemChange change, const ItemChangeData &value)
107+{
108+ if (change == QQuickItem::ItemSceneChange) {
109+ if (value.window != nullptr) {
110+ value.window->installEventFilter(TouchRegistry::instance());
111+ }
83 }
112 }
84}
113}
85
114
@@ -88,24 +117,23 @@ void TouchGate::touchOwnershipEvent(TouchOwnershipEvent *event)
88 // TODO: Optimization: batch those actions as TouchOwnershipEvents
117 // TODO: Optimization: batch those actions as TouchOwnershipEvents
89 // might come one right after the other.
118 // might come one right after the other.
90
119
91− Q_ASSERT(m_touchInfoMap.contains(event->touchId()));
120+ if (m_touchInfoMap.contains(event->touchId())) {
121+ TouchInfo &touchInfo = m_touchInfoMap[event->touchId()];
92
122
93− TouchInfo &touchInfo = m_touchInfoMap[event->touchId()];
123+ if (event->gained()) {
124+ ugDebug("Got ownership of touch " << event->touchId());
125+ touchInfo.ownership = OwnershipGranted;
126+ } else {
127+ ugDebug("Lost ownership of touch " << event->touchId());
128+ m_touchInfoMap.remove(event->touchId());
129+ removeTouchFromStoredEvents(event->touchId());
130+ }
94
131
95− if (event->gained()) {
132+ dispatchFullyOwnedEvents();
96− #if TOUCHGATE_DEBUG
97− qDebug() << "[TouchGate] Got ownership of touch " << event->touchId();
98− #endif
99− touchInfo.ownership = OwnershipGranted;
100 } else {
133 } else {
101− #if TOUCHGATE_DEBUG
134+ // Ignore it. It probably happened because the TouchGate got disabled
102− qDebug() << "[TouchGate] Lost ownership of touch " << event->touchId();
135+ // between the time it requested ownership and the time it got it.
103− #endif
104− m_touchInfoMap.remove(event->touchId());
105− removeTouchFromStoredEvents(event->touchId());
106 }
136 }
107−
108− dispatchFullyOwnedEvents();
109}
137}
110
138
111bool TouchGate::isTouchPointOwned(int touchId) const
139bool TouchGate::isTouchPointOwned(int touchId) const
@@ -113,14 +141,15 @@ bool TouchGate::isTouchPointOwned(int touchId) const
113 return m_touchInfoMap[touchId].ownership == OwnershipGranted;
141 return m_touchInfoMap[touchId].ownership == OwnershipGranted;
114}
142}
115
143
116−void TouchGate::storeTouchEvent(const QTouchEvent *event)
144+void TouchGate::storeTouchEvent(QTouchDevice *device,
145+ Qt::KeyboardModifiers modifiers,
146+ const QList<QTouchEvent::TouchPoint> &touchPoints,
147+ QWindow *window,
148+ ulong timestamp)
117{
149{
118− #if TOUCHGATE_DEBUG
150+ ugDebug("Storing" << touchPoints);
119− qDebug() << "[TouchGate] Storing" << qPrintable(touchEventToString(event));
151+ TouchEvent event(device, modifiers, touchPoints, window, timestamp);
120− #endif
121−
122− TouchEvent clonedEvent(event);
123− m_storedEvents.append(std::move(clonedEvent));
152+ m_storedEvents.append(std::move(event));
124}
153}
125
154
126void TouchGate::removeTouchFromStoredEvents(int touchId)
155void TouchGate::removeTouchFromStoredEvents(int touchId)
@@ -193,25 +222,13 @@ void TouchGate::setTargetItem(QQuickItem *item)
193void TouchGate::dispatchTouchEventToTarget(const TouchEvent &event)
222void TouchGate::dispatchTouchEventToTarget(const TouchEvent &event)
194{
223{
195 removeTouchInfoForEndedTouches(event.touchPoints);
224 removeTouchInfoForEndedTouches(event.touchPoints);
196− m_dispatcher.dispatch(event.eventType,
225+ m_dispatcher.dispatch(event.device,
197− event.device,
198 event.modifiers,
226 event.modifiers,
199 event.touchPoints,
227 event.touchPoints,
200 event.window,
228 event.window,
201 event.timestamp);
229 event.timestamp);
202}
230}
203
231
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−
215void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints)
232void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints)
216{
233{
217 for (int i = 0; i < touchPoints.size(); ++i) {\
234 for (int i = 0; i < touchPoints.size(); ++i) {\
@@ -226,14 +243,31 @@ void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoi
226 }
243 }
227}
244}
228
245
246+void TouchGate::onEnabledChanged()
247+{
248+ ugDebug(" enabled = " << isEnabled());
249+ if (!isEnabled()) {
250+ reset();
251+ }
252+}
253+
254+void TouchGate::reset()
255+{
256+ m_storedEvents.clear();
257+ m_touchInfoMap.clear();
258+ m_dispatcher.reset();
259+}
260+
229−TouchGate::TouchEvent::TouchEvent(const QTouchEvent *event)
261+TouchGate::TouchEvent::TouchEvent(QTouchDevice *device,
230− : eventType(event->type())
262+ Qt::KeyboardModifiers modifiers,
263+ const QList<QTouchEvent::TouchPoint> &touchPoints,
264+ QWindow *window,
265+ ulong timestamp)
231− , device(event->device())
266+ : device(device)
232− , modifiers(event->modifiers())
267+ , modifiers(modifiers)
233− , touchPoints(event->touchPoints())
268+ , touchPoints(touchPoints)
234− , target(qobject_cast<QQuickItem*>(event->target()))
235− , window(event->window())
269+ , window(window)
236− , timestamp(event->timestamp())
270+ , timestamp(timestamp)
237{
271{
238}
272}
239
273
plugins/Ubuntu/Gestures/TouchGate.h+19−5
@@ -45,6 +45,8 @@ class UBUNTUGESTURESQML_EXPORT TouchGate : public QQuickItem {
45 Q_PROPERTY(QQuickItem* targetItem READ targetItem WRITE setTargetItem NOTIFY targetItemChanged)
45 Q_PROPERTY(QQuickItem* targetItem READ targetItem WRITE setTargetItem NOTIFY targetItemChanged)
46
46
47public:
47public:
48+ TouchGate(QQuickItem *parent = nullptr);
49+
48 bool event(QEvent *e) override;
50 bool event(QEvent *e) override;
49
51
50 QQuickItem *targetItem() { return m_dispatcher.targetItem(); }
52 QQuickItem *targetItem() { return m_dispatcher.targetItem(); }
@@ -55,31 +57,43 @@ Q_SIGNALS:
55
57
56protected:
58protected:
57 void touchEvent(QTouchEvent *event) override;
59 void touchEvent(QTouchEvent *event) override;
60+ void itemChange(ItemChange change, const ItemChangeData &value) override;
61+
62+private Q_SLOTS:
63+ void onEnabledChanged();
64+
58private:
65private:
66+ void reset();
67+
59 class TouchEvent {
68 class TouchEvent {
60 public:
69 public:
70+ TouchEvent(QTouchDevice *device,
71+ Qt::KeyboardModifiers modifiers,
61− TouchEvent(const QTouchEvent *event);
72+ const QList<QTouchEvent::TouchPoint> &touchPoints,
73+ QWindow *window,
74+ ulong timestamp);
62
75
63 bool removeTouch(int touchId);
76 bool removeTouch(int touchId);
64
77
65− QEvent::Type eventType;
66 QTouchDevice *device;
78 QTouchDevice *device;
67 Qt::KeyboardModifiers modifiers;
79 Qt::KeyboardModifiers modifiers;
68 QList<QTouchEvent::TouchPoint> touchPoints;
80 QList<QTouchEvent::TouchPoint> touchPoints;
69− QQuickItem *target;
70 QWindow *window;
81 QWindow *window;
71 ulong timestamp;
82 ulong timestamp;
72 };
83 };
73
84
74 void touchOwnershipEvent(TouchOwnershipEvent *event);
85 void touchOwnershipEvent(TouchOwnershipEvent *event);
75 bool isTouchPointOwned(int touchId) const;
86 bool isTouchPointOwned(int touchId) const;
76− void storeTouchEvent(const QTouchEvent *event);
87+ void storeTouchEvent(QTouchDevice *device,
88+ Qt::KeyboardModifiers modifiers,
89+ const QList<QTouchEvent::TouchPoint> &touchPoints,
90+ QWindow *window,
91+ ulong timestamp);
77 void removeTouchFromStoredEvents(int touchId);
92 void removeTouchFromStoredEvents(int touchId);
78 void dispatchFullyOwnedEvents();
93 void dispatchFullyOwnedEvents();
79 bool eventIsFullyOwned(const TouchEvent &event) const;
94 bool eventIsFullyOwned(const TouchEvent &event) const;
80
95
81 void dispatchTouchEventToTarget(const TouchEvent &event);
96 void dispatchTouchEventToTarget(const TouchEvent &event);
82− void dispatchTouchEventToTarget(QTouchEvent* event);
83
97
84 void removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints);
98 void removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints);
85
99
plugins/Ubuntu/Gestures/plugin.cpp+3−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
18#include "AxisVelocityCalculator.h"
18#include "AxisVelocityCalculator.h"
19#include "Direction.h"
19#include "Direction.h"
20#include "DirectionalDragArea.h"
20#include "DirectionalDragArea.h"
21+#include "FloatingFlickable.h"
21#include "PressedOutsideNotifier.h"
22#include "PressedOutsideNotifier.h"
22#include "TouchGate.h"
23#include "TouchGate.h"
23
24
@@ -34,6 +35,7 @@ void UbuntuGesturesQmlPlugin::registerTypes(const char *uri)
34 qmlRegisterSingletonType<Direction>(uri, 0, 1, "Direction", directionSingleton);
35 qmlRegisterSingletonType<Direction>(uri, 0, 1, "Direction", directionSingleton);
35 qmlRegisterType<DirectionalDragArea>(uri, 0, 1, "DirectionalDragArea");
36 qmlRegisterType<DirectionalDragArea>(uri, 0, 1, "DirectionalDragArea");
36 qmlRegisterType<AxisVelocityCalculator>(uri, 0, 1, "AxisVelocityCalculator");
37 qmlRegisterType<AxisVelocityCalculator>(uri, 0, 1, "AxisVelocityCalculator");
38+ qmlRegisterType<FloatingFlickable>(uri, 0, 1, "FloatingFlickable");
37 qmlRegisterType<PressedOutsideNotifier>(uri, 0, 1, "PressedOutsideNotifier");
39 qmlRegisterType<PressedOutsideNotifier>(uri, 0, 1, "PressedOutsideNotifier");
38 qmlRegisterType<TouchGate>(uri, 0, 1, "TouchGate");
40 qmlRegisterType<TouchGate>(uri, 0, 1, "TouchGate");
39}
41}
plugins/Ubuntu/Gestures/plugin.h+1−1
@@ -24,7 +24,7 @@ class UbuntuGesturesQmlPlugin : public QQmlExtensionPlugin
24 Q_OBJECT
24 Q_OBJECT
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26public:
26public:
27− void registerTypes(const char *uri);
27+ void registerTypes(const char *uri) override;
28};
28};
29
29
30
30
plugins/Ubuntu/Payments/plugin.h+1−1
@@ -25,7 +25,7 @@ class BackendPlugin : public QQmlExtensionPlugin
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26
26
27public:
27public:
28− void registerTypes(const char *uri);
28+ void registerTypes(const char *uri) override;
29};
29};
30
30
31#endif // PAYMENTS_PLUGIN_H
31#endif // PAYMENTS_PLUGIN_H
plugins/Ubuntu/SystemImage/plugin.h+1−1
@@ -25,7 +25,7 @@ class BackendPlugin : public QQmlExtensionPlugin
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26
26
27public:
27public:
28− void registerTypes(const char *uri);
28+ void registerTypes(const char *uri) override;
29};
29};
30
30
31#endif // SYSTEMIMAGE_PLUGIN_H
31#endif // SYSTEMIMAGE_PLUGIN_H
plugins/Unity/Connectivity/plugin.h+1−1
@@ -25,7 +25,7 @@ class BackendPlugin : public QQmlExtensionPlugin
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26
26
27public:
27public:
28− void registerTypes(const char *uri);
28+ void registerTypes(const char *uri) override;
29};
29};
30
30
31#endif // CONNECTIVITY_PLUGIN_H
31#endif // CONNECTIVITY_PLUGIN_H
plugins/Unity/DashCommunicator/plugin.h+1−1
@@ -28,7 +28,7 @@ class DashCommunicatorPlugin : public QQmlExtensionPlugin
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29
29
30public:
30public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
32};
32};
33
33
34#endif
34#endif
plugins/Unity/Indicators/indicatorsmodel.h+5−5
@@ -50,11 +50,11 @@ public:
50 void setProfile(const QString& profile);
50 void setProfile(const QString& profile);
51
51
52 /* QAbstractItemModel */
52 /* QAbstractItemModel */
53− QHash<int, QByteArray> roleNames() const;
53+ QHash<int, QByteArray> roleNames() const override;
54− int columnCount(const QModelIndex &parent = QModelIndex()) const;
54+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
55− QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
55+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
56− QModelIndex parent (const QModelIndex &index) const;
56+ QModelIndex parent (const QModelIndex &index) const override;
57− int rowCount(const QModelIndex &parent = QModelIndex()) const;
57+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
58
58
59
59
60Q_SIGNALS:
60Q_SIGNALS:
plugins/Unity/Indicators/menucontentactivator.cpp+4−4
@@ -28,10 +28,10 @@ public:
28 connect(&m_timer, &QTimer::timeout,
28 connect(&m_timer, &QTimer::timeout,
29 this, &UnityIndicators::AbstractTimer::timeout);
29 this, &UnityIndicators::AbstractTimer::timeout);
30 }
30 }
31− virtual int interval() const { return m_timer.interval(); }
31+ int interval() const override { return m_timer.interval(); }
32− virtual void setInterval(int msecs) { m_timer.setInterval(msecs); }
32+ void setInterval(int msecs) override { m_timer.setInterval(msecs); }
33− virtual void start() { m_timer.start(); UnityIndicators::AbstractTimer::start(); }
33+ void start() override { m_timer.start(); UnityIndicators::AbstractTimer::start(); }
34− virtual void stop() { m_timer.stop(); UnityIndicators::AbstractTimer::stop(); }
34+ void stop() override { m_timer.stop(); UnityIndicators::AbstractTimer::stop(); }
35private:
35private:
36 QTimer m_timer;
36 QTimer m_timer;
37};
37};
plugins/Unity/Indicators/plugin.h+1−1
@@ -27,7 +27,7 @@ class IndicatorsPlugin : public QQmlExtensionPlugin
27 Q_OBJECT
27 Q_OBJECT
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29public:
29public:
30− void registerTypes(const char *uri);
30+ void registerTypes(const char *uri) override;
31};
31};
32
32
33#endif // INDICATORS2_PLUGIN_H
33#endif // INDICATORS2_PLUGIN_H
plugins/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,5 +1,5 @@
1pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
2−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
2+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
3pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
3pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
4
4
5add_definitions(-DSM_BUSNAME=systemBus)
5add_definitions(-DSM_BUSNAME=systemBus)
plugins/Unity/Launcher/launchermodel.h+10−10
@@ -41,19 +41,19 @@ public:
41 LauncherModel(QObject *parent = 0);
41 LauncherModel(QObject *parent = 0);
42 ~LauncherModel();
42 ~LauncherModel();
43
43
44− int rowCount(const QModelIndex &parent = QModelIndex()) const;
44+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
45
45
46− QVariant data(const QModelIndex &index, int role) const;
46+ QVariant data(const QModelIndex &index, int role) const override;
47
47
48− Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const;
48+ Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const override;
49− Q_INVOKABLE void move(int oldIndex, int newIndex);
49+ Q_INVOKABLE void move(int oldIndex, int newIndex) override;
50− Q_INVOKABLE void pin(const QString &appId, int index = -1);
50+ Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
51− Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
51+ Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex) override;
52− Q_INVOKABLE void setUser(const QString &username);
52+ Q_INVOKABLE void setUser(const QString &username) override;
53 Q_INVOKABLE QString getUrlForAppId(const QString &appId) const;
53 Q_INVOKABLE QString getUrlForAppId(const QString &appId) const;
54
54
55− unity::shell::application::ApplicationManagerInterface* applicationManager() const;
55+ unity::shell::application::ApplicationManagerInterface* applicationManager() const override;
56− void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
56+ void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager) override;
57
57
58 bool onlyPinned() const override;
58 bool onlyPinned() const override;
59 void setOnlyPinned(bool onlyPinned) override;
59 void setOnlyPinned(bool onlyPinned) override;
@@ -61,7 +61,7 @@ public:
61 int findApplication(const QString &appId);
61 int findApplication(const QString &appId);
62
62
63public Q_SLOTS:
63public Q_SLOTS:
64− void requestRemove(const QString &appId);
64+ void requestRemove(const QString &appId) override;
65 Q_INVOKABLE void refresh();
65 Q_INVOKABLE void refresh();
66
66
67private:
67private:
plugins/Unity/Launcher/plugin.h+1−1
@@ -28,7 +28,7 @@ class UnityLauncherPlugin : public QQmlExtensionPlugin
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29
29
30public:
30public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
32};
32};
33
33
34#endif
34#endif
plugins/Unity/Launcher/quicklistmodel.h+2−2
@@ -48,8 +48,8 @@ public:
48
48
49 QuickListEntry get(int index) const;
49 QuickListEntry get(int index) const;
50
50
51− int rowCount(const QModelIndex &parent = QModelIndex()) const;
51+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
52− QVariant data(const QModelIndex &index, int role) const;
52+ QVariant data(const QModelIndex &index, int role) const override;
53
53
54private:
54private:
55 QList<QuickListEntry> m_list;
55 QList<QuickListEntry> m_list;
plugins/Unity/Session/plugin.h+1−1
@@ -28,7 +28,7 @@ class SessionPlugin : public QQmlExtensionPlugin
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29
29
30public:
30public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
32};
32};
33
33
34#endif
34#endif
plugins/Utils/CMakeLists.txt+3
@@ -9,13 +9,16 @@ include_directories(
9
9
10set(QMLPLUGIN_SRC
10set(QMLPLUGIN_SRC
11 constants.cpp
11 constants.cpp
12+ HomeKeyWatcher.cpp
12 inputwatcher.cpp
13 inputwatcher.cpp
13 qlimitproxymodelqml.cpp
14 qlimitproxymodelqml.cpp
14 unitysortfilterproxymodelqml.cpp
15 unitysortfilterproxymodelqml.cpp
15 relativetimeformatter.cpp
16 relativetimeformatter.cpp
16 timeformatter.cpp
17 timeformatter.cpp
18+ Timer.cpp
17 unitymenumodelpaths.cpp
19 unitymenumodelpaths.cpp
18 windowkeysfilter.cpp
20 windowkeysfilter.cpp
21+ windowscreenshotprovider.cpp
19 easingcurve.cpp
22 easingcurve.cpp
20 windowstatestorage.cpp
23 windowstatestorage.cpp
21 plugin.cpp
24 plugin.cpp
plugins/Utils/ElapsedTimer.hnew file+51
@@ -0,0 +1,51 @@
1+/*
2+ * Copyright (C) 2015 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU Lesser General Public License, as
6+ * published by the Free Software Foundation; either version 2.1 or 3.0
7+ * of the License.
8+ *
9+ * This program is distributed in the hope that it will be useful, but
10+ * WITHOUT ANY WARRANTY; without even the implied warranties of
11+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
12+ * PURPOSE. See the applicable version of the GNU Lesser General Public
13+ * License for more details.
14+ *
15+ * You should have received a copy of both the GNU Lesser General Public
16+ * License along with this program. If not, see <http://www.gnu.org/licenses/>
17+ */
18+
19+#ifndef UNITYUTIL_ELAPSEDTIMER_H
20+#define UNITYUTIL_ELAPSEDTIMER_H
21+
22+#include <QElapsedTimer>
23+
24+namespace UnityUtil {
25+
26+/**
27+ Interface for ElapsedTimer implementations
28+ */
29+class AbstractElapsedTimer {
30+public:
31+ virtual ~AbstractElapsedTimer() {}
32+ virtual void start() = 0;
33+ virtual qint64 msecsSinceReference() const = 0;
34+ virtual qint64 elapsed() const = 0;
35+};
36+
37+/**
38+ A QElapsedTimer wrapper
39+ */
40+class ElapsedTimer : public AbstractElapsedTimer {
41+public:
42+ void start() override { m_timer.start(); }
43+ qint64 msecsSinceReference() const override { return m_timer.msecsSinceReference(); }
44+ qint64 elapsed() const override { return m_timer.elapsed(); }
45+private:
46+ QElapsedTimer m_timer;
47+};
48+
49+} // namespace UnityUtil
50+
51+#endif // UNITYUTIL_ELAPSEDTIMER_H
plugins/Utils/HomeKeyWatcher.cppnew file+116
@@ -0,0 +1,116 @@
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 "HomeKeyWatcher.h"
18+
19+#include <QQuickWindow>
20+
21+using namespace UnityUtil;
22+
23+HomeKeyWatcher::HomeKeyWatcher(QQuickItem *parent)
24+ : HomeKeyWatcher(new Timer, new ElapsedTimer, parent)
25+{
26+}
27+
28+HomeKeyWatcher::HomeKeyWatcher(UnityUtil::AbstractTimer *timer,
29+ UnityUtil::AbstractElapsedTimer *elapsedTimer,
30+ QQuickItem *parent)
31+ : QQuickItem(parent)
32+ , m_windowBeingTouched(false)
33+ , m_homeKeyPressed(false)
34+ , m_windowLastTouchedTimer(elapsedTimer)
35+ , m_activationTimer(timer)
36+{
37+ m_windowLastTouchedTimer->start();
38+
39+ connect(this, &QQuickItem::windowChanged,
40+ this, &HomeKeyWatcher::setupFilterOnWindow);
41+
42+ connect(m_activationTimer, &UnityUtil::AbstractTimer::timeout,
43+ this, &HomeKeyWatcher::emitActivatedIfNoTouchesAround);
44+ m_activationTimer->setInterval(msecsWithoutTouches);
45+}
46+
47+HomeKeyWatcher::~HomeKeyWatcher()
48+{
49+ delete m_windowLastTouchedTimer;
50+ delete m_activationTimer;
51+}
52+
53+bool HomeKeyWatcher::eventFilter(QObject *watched, QEvent *event)
54+{
55+ Q_ASSERT(!m_filteredWindow.isNull());
56+ Q_ASSERT(watched == static_cast<QObject*>(m_filteredWindow.data()));
57+ Q_UNUSED(watched);
58+
59+ update(event);
60+
61+ // We're only monitoring, never filtering out events
62+ return false;
63+}
64+
65+void HomeKeyWatcher::update(QEvent *event)
66+{
67+ if (event->type() == QEvent::KeyPress) {
68+ QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
69+
70+ m_homeKeyPressed = true;
71+
72+ if (keyEvent->key() == Qt::Key_Super_L && !keyEvent->isAutoRepeat()
73+ && !m_windowBeingTouched
74+ && m_windowLastTouchedTimer->elapsed() >= msecsWithoutTouches) {
75+ m_activationTimer->start();
76+ }
77+
78+ } else if (event->type() == QEvent::KeyRelease) {
79+ QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
80+
81+ if (keyEvent->key() == Qt::Key_Super_L) {
82+ m_homeKeyPressed = false;
83+ }
84+
85+ } else if (event->type() == QEvent::TouchBegin) {
86+
87+ m_activationTimer->stop();
88+ m_windowBeingTouched = true;
89+
90+ } else if (event->type() == QEvent::TouchEnd) {
91+
92+ m_windowBeingTouched = false;
93+ m_windowLastTouchedTimer->start();
94+ }
95+}
96+
97+void HomeKeyWatcher::setupFilterOnWindow(QQuickWindow *window)
98+{
99+ if (!m_filteredWindow.isNull()) {
100+ m_filteredWindow->removeEventFilter(this);
101+ m_filteredWindow.clear();
102+ }
103+
104+ if (window) {
105+ window->installEventFilter(this);
106+ m_filteredWindow = window;
107+ }
108+}
109+
110+void HomeKeyWatcher::emitActivatedIfNoTouchesAround()
111+{
112+ if (!m_homeKeyPressed && !m_windowBeingTouched &&
113+ (m_windowLastTouchedTimer->elapsed() > msecsWithoutTouches)) {
114+ Q_EMIT activated();
115+ }
116+}
plugins/Utils/HomeKeyWatcher.hnew file+74
@@ -0,0 +1,74 @@
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 UNITY_HOMEKEYWATCHER_H
18+#define UNITY_HOMEKEYWATCHER_H
19+
20+#include <QQuickItem>
21+#include <QPointer>
22+
23+#include "Timer.h"
24+#include "ElapsedTimer.h"
25+
26+/*
27+ Signals when the home key seems to be have been intentionally tapped.
28+
29+ It only says the home key has been activated if it has been tapped in isolation,
30+ that is, without being accompanied by touches on the screen. Home key taps that
31+ happen along with (or immediately after, or immediately before) touches on the
32+ screen are considered to have happened unintentionally and are thus ignored.
33+
34+ Rationale being that it's easy to accidentally hit the home key while performing
35+ a swipe from a screen edge, for instance. That's particularly the case when the
36+ home key is a capacitive key.
37+ */
38+class HomeKeyWatcher : public QQuickItem
39+{
40+ Q_OBJECT
41+public:
42+
43+ HomeKeyWatcher(QQuickItem *parent = 0);
44+
45+ // for testing
46+ HomeKeyWatcher(UnityUtil::AbstractTimer *timer,
47+ UnityUtil::AbstractElapsedTimer *elapsedTimer,
48+ QQuickItem *parent = 0);
49+
50+ virtual ~HomeKeyWatcher();
51+
52+ bool eventFilter(QObject *watched, QEvent *event) override;
53+
54+ void update(QEvent *event);
55+
56+ const qint64 msecsWithoutTouches = 150;
57+
58+Q_SIGNALS:
59+ // Emitted when the home key has been intentionally tapped
60+ void activated();
61+
62+private Q_SLOTS:
63+ void setupFilterOnWindow(QQuickWindow *window);
64+ void emitActivatedIfNoTouchesAround();
65+
66+private:
67+ QPointer<QQuickWindow> m_filteredWindow;
68+ bool m_windowBeingTouched;
69+ bool m_homeKeyPressed;
70+ UnityUtil::AbstractElapsedTimer *m_windowLastTouchedTimer;
71+ UnityUtil::AbstractTimer *m_activationTimer;
72+};
73+
74+#endif // UNITY_HOMEKEYWATCHER_H
plugins/Utils/Timer.cppnew file+59
@@ -0,0 +1,59 @@
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 "Timer.h"
18+
19+namespace UnityUtil {
20+
21+Timer::Timer(QObject *parent) : AbstractTimer(parent)
22+{
23+ m_timer.setSingleShot(true);
24+ connect(&m_timer, &QTimer::timeout, this, &AbstractTimer::timeout);
25+}
26+
27+int Timer::interval() const
28+{
29+ return m_timer.interval();
30+}
31+
32+void Timer::setInterval(int msecs)
33+{
34+ m_timer.setInterval(msecs);
35+}
36+
37+void Timer::start()
38+{
39+ m_timer.start();
40+ AbstractTimer::start();
41+}
42+
43+void Timer::stop()
44+{
45+ m_timer.stop();
46+ AbstractTimer::stop();
47+}
48+
49+bool Timer::isSingleShot() const
50+{
51+ return m_timer.isSingleShot();
52+}
53+
54+void Timer::setSingleShot(bool value)
55+{
56+ m_timer.setSingleShot(value);
57+}
58+
59+} // namespace UnityUtil
plugins/Utils/Timer.hnew file+79
@@ -0,0 +1,79 @@
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 UNITYUTIL_TIMER_H
18+#define UNITYUTIL_TIMER_H
19+
20+#include "ElapsedTimer.h"
21+
22+#include <QObject>
23+#include <QPointer>
24+#include <QTimer>
25+
26+namespace UnityUtil {
27+
28+/** Defines an interface for a Timer. Useful for tests. */
29+class AbstractTimer : public QObject
30+{
31+ Q_OBJECT
32+public:
33+ AbstractTimer(QObject *parent) : QObject(parent), m_isRunning(false) {}
34+ virtual int interval() const = 0;
35+ virtual void setInterval(int msecs) = 0;
36+ virtual void start() { m_isRunning = true; }
37+ virtual void stop() { m_isRunning = false; }
38+ bool isRunning() const { return m_isRunning; }
39+ virtual bool isSingleShot() const = 0;
40+ virtual void setSingleShot(bool value) = 0;
41+Q_SIGNALS:
42+ void timeout();
43+private:
44+ bool m_isRunning;
45+};
46+
47+/** A QTimer wrapper */
48+class Timer : public AbstractTimer
49+{
50+ Q_OBJECT
51+public:
52+ Timer(QObject *parent = nullptr);
53+
54+ int interval() const override;
55+ void setInterval(int msecs) override;
56+ void start() override;
57+ void stop() override;
58+ bool isSingleShot() const override;
59+ void setSingleShot(bool value) override;
60+private:
61+ QTimer m_timer;
62+};
63+
64+class AbstractTimerFactory
65+{
66+public:
67+ virtual ~AbstractTimerFactory() {}
68+ virtual AbstractTimer *create(QObject *parent = nullptr) = 0;
69+};
70+
71+class TimerFactory : public AbstractTimerFactory
72+{
73+public:
74+ AbstractTimer *create(QObject *parent = nullptr) override { return new Timer(parent); }
75+};
76+
77+} // namespace UnityUtil
78+
79+#endif // UNITYUTIL_TIMER_H
plugins/Utils/plugin.cpp+7−5
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2012 Canonical, Ltd.
2+ * Copyright (C) 2012-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -12,8 +12,6 @@
12 *
12 *
13 * You should have received a copy of the GNU General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michał Sawicz <michal.sawicz@canonical.com>
17 */
15 */
18
16
19// Qt
17// Qt
@@ -21,11 +19,12 @@
21#include <QDBusConnection>
19#include <QDBusConnection>
22#include <QQmlContext>
20#include <QQmlContext>
23#include <QtQuick/QQuickWindow>
21#include <QtQuick/QQuickWindow>
24−#include <QDebug>
25// self
22// self
26#include "plugin.h"
23#include "plugin.h"
27
24
28// local
25// local
26+#include "easingcurve.h"
27+#include "HomeKeyWatcher.h"
29#include "inputwatcher.h"
28#include "inputwatcher.h"
30#include "qlimitproxymodelqml.h"
29#include "qlimitproxymodelqml.h"
31#include "unitysortfilterproxymodelqml.h"
30#include "unitysortfilterproxymodelqml.h"
@@ -33,7 +32,7 @@
33#include "timeformatter.h"
32#include "timeformatter.h"
34#include "unitymenumodelpaths.h"
33#include "unitymenumodelpaths.h"
35#include "windowkeysfilter.h"
34#include "windowkeysfilter.h"
36−#include "easingcurve.h"
35+#include "windowscreenshotprovider.h"
37#include "windowstatestorage.h"
36#include "windowstatestorage.h"
38#include "constants.h"
37#include "constants.h"
39
38
@@ -54,6 +53,7 @@ static QObject *createConstants(QQmlEngine *engine, QJSEngine *scriptEngine)
54void UtilsPlugin::registerTypes(const char *uri)
53void UtilsPlugin::registerTypes(const char *uri)
55{
54{
56 Q_ASSERT(uri == QLatin1String("Utils"));
55 Q_ASSERT(uri == QLatin1String("Utils"));
56+ qmlRegisterType<HomeKeyWatcher>(uri, 0, 1, "HomeKeyWatcher");
57 qmlRegisterType<QAbstractItemModel>();
57 qmlRegisterType<QAbstractItemModel>();
58 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
58 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
59 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
59 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
@@ -71,4 +71,6 @@ void UtilsPlugin::registerTypes(const char *uri)
71void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
71void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
72{
72{
73 QQmlExtensionPlugin::initializeEngine(engine, uri);
73 QQmlExtensionPlugin::initializeEngine(engine, uri);
74+
75+ engine->addImageProvider(QLatin1String("window"), new WindowScreenshotProvider);
74}
76}
plugins/Utils/plugin.h+2−2
@@ -28,8 +28,8 @@ class UtilsPlugin : public QQmlExtensionPlugin
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29
29
30public:
30public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
32− void initializeEngine(QQmlEngine *engine, const char *uri);
32+ void initializeEngine(QQmlEngine *engine, const char *uri) override;
33};
33};
34
34
35#endif
35#endif
plugins/Utils/qlimitproxymodelqml.h+2−2
@@ -32,8 +32,8 @@ public:
32
32
33 /* getters */
33 /* getters */
34 int limit() const;
34 int limit() const;
35− int rowCount(const QModelIndex &parent = QModelIndex()) const;
35+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
36− QHash<int, QByteArray> roleNames() const;
36+ QHash<int, QByteArray> roleNames() const override;
37
37
38 /* setters */
38 /* setters */
39 void setModel(QAbstractItemModel *model);
39 void setModel(QAbstractItemModel *model);
plugins/Utils/qvariantlistmodel.h+6−6
@@ -54,14 +54,14 @@ public:
54
54
55 ~QVariantListModel();
55 ~QVariantListModel();
56
56
57− int rowCount(const QModelIndex &parent = QModelIndex()) const;
57+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
58− QModelIndex sibling(int row, int column, const QModelIndex &idx) const;
58+ QModelIndex sibling(int row, int column, const QModelIndex &idx) const override;
59
59
60− QVariant data(const QModelIndex &index, int role) const;
60+ QVariant data(const QModelIndex &index, int role) const override;
61− bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
61+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
62
62
63− bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
63+ bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
64− bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
64+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
65
65
66 QVariantList variantList() const;
66 QVariantList variantList() const;
67 void setVariantList(const QVariantList &list);
67 void setVariantList(const QVariantList &list);
plugins/Utils/timeformatter.h+1−1
@@ -62,7 +62,7 @@ public:
62 GDateTimeFormatter(QObject *parent = 0);
62 GDateTimeFormatter(QObject *parent = 0);
63
63
64protected:
64protected:
65− virtual QString formatTime() const;
65+ QString formatTime() const override;
66};
66};
67
67
68#endif
68#endif
plugins/Utils/unitysortfilterproxymodelqml.h+2−2
@@ -36,12 +36,12 @@ public:
36 Q_INVOKABLE int count();
36 Q_INVOKABLE int count();
37 Q_INVOKABLE int findFirst(int role, const QVariant& value) const;
37 Q_INVOKABLE int findFirst(int role, const QVariant& value) const;
38 Q_INVOKABLE int mapRowToSource(int row);
38 Q_INVOKABLE int mapRowToSource(int row);
39− virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
39+ bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
40
40
41 /* getters */
41 /* getters */
42 int totalCount() const;
42 int totalCount() const;
43 bool invertMatch() const;
43 bool invertMatch() const;
44− QHash<int, QByteArray> roleNames() const;
44+ QHash<int, QByteArray> roleNames() const override;
45
45
46 /* setters */
46 /* setters */
47 void setModel(QAbstractItemModel *model);
47 void setModel(QAbstractItemModel *model);
plugins/Utils/windowkeysfilter.h+1−1
@@ -39,7 +39,7 @@ class WindowKeysFilter : public QQuickItem
39public:
39public:
40 WindowKeysFilter(QQuickItem *parent = 0);
40 WindowKeysFilter(QQuickItem *parent = 0);
41
41
42− bool eventFilter(QObject *watched, QEvent *event);
42+ bool eventFilter(QObject *watched, QEvent *event) override;
43
43
44private Q_SLOTS:
44private Q_SLOTS:
45 void setupFilterOnWindow(QQuickWindow *window);
45 void setupFilterOnWindow(QQuickWindow *window);
plugins/Utils/windowscreenshotprovider.cppnew file+59
@@ -0,0 +1,59 @@
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 "windowscreenshotprovider.h"
18+
19+#include <QGuiApplication>
20+#include <QQuickWindow>
21+
22+WindowScreenshotProvider::WindowScreenshotProvider()
23+ : QQuickImageProvider(QQmlImageProviderBase::Image, 0)
24+{
25+}
26+
27+// A very simple implementation where we assume that there's only one window and that it's a
28+// QQuickWindow. Thus the id parameter is irrelevant.
29+//
30+// Idea: Make the id contain the objectName of the QQuickWindow once we care about a multi-display
31+// compositor?
32+// Strictly speaking that could be the actual QWindow::winId(), but that's mostly a
33+// meaningless arbitrary number.
34+QImage WindowScreenshotProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
35+{
36+ Q_UNUSED(id);
37+ Q_UNUSED(requestedSize);
38+
39+ QWindowList windows = QGuiApplication::topLevelWindows();
40+
41+ if (windows.count() != 1) {
42+ size->rwidth() = 0;
43+ size->rheight() = 0;
44+ return QImage();
45+ }
46+
47+ QQuickWindow *quickWindow = qobject_cast<QQuickWindow *>(windows[0]);
48+
49+ if (!quickWindow) {
50+ size->rwidth() = 0;
51+ size->rheight() = 0;
52+ return QImage();
53+ }
54+
55+ QImage image = quickWindow->grabWindow();
56+ size->rwidth() = image.width();
57+ size->rheight() = image.height();
58+ return image;
59+}
plugins/Utils/windowscreenshotprovider.hnew file+31
@@ -0,0 +1,31 @@
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 WINDOW_SCREENSHOT_PROVIDER_H_
18+#define WINDOW_SCREENSHOT_PROVIDER_H_
19+
20+#include <QQuickImageProvider>
21+
22+class WindowScreenshotProvider : public QQuickImageProvider
23+{
24+public:
25+ WindowScreenshotProvider();
26+
27+ // id is ignored for now
28+ QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;
29+};
30+
31+#endif // WINDOW_SCREENSHOT_PROVIDER_H_
plugins/Wizard/plugin.h+1−1
@@ -26,7 +26,7 @@ class WizardPlugin : public QQmlExtensionPlugin
26 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27
27
28public:
28public:
29− void registerTypes(const char *uri);
29+ void registerTypes(const char *uri) override;
30};
30};
31
31
32#endif
32#endif
po/am.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-19 23:16+0000\n"
11+"PO-Revision-Date: 2015-05-24 14:07+0000\n"
12"Last-Translator: samson <Unknown>\n"
12"Last-Translator: samson <Unknown>\n"
13"Language-Team: Amharic <am@li.org>\n"
13"Language-Team: Amharic <am@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:47+0000\n"
18+"X-Launchpad-Export-Date: 2015-05-25 06:04+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
19+"X-Generator: Launchpad (build 17514)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -371,7 +371,7 @@ msgstr "እዚህ መታ ይድርጉ ለመቀጠል"
371
371
372#: qml/Tutorial/TutorialContent.qml:81
372#: qml/Tutorial/TutorialContent.qml:81
373msgid "Click here to finish."
373msgid "Click here to finish."
374−msgstr ""
374+msgstr "ለመጨረስ እዚህ ይጫኑ"
375
375
376#: qml/Tutorial/TutorialLeftFinish.qml:24
376#: qml/Tutorial/TutorialLeftFinish.qml:24
377msgid "These are the shortcuts to favorite apps"
377msgid "These are the shortcuts to favorite apps"
po/ast.po+86−86
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-02 18:12+0000\n"
11+"PO-Revision-Date: 2015-06-11 23:39+0000\n"
12−"Last-Translator: ivarela <ivarela@ubuntu.com>\n"
12+"Last-Translator: enolp <enolp@softastur.org>\n"
13"Language-Team: Asturian <ast@li.org>\n"
13"Language-Team: Asturian <ast@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"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-06-16 11:20+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
19+"X-Generator: Launchpad (build 17557)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -25,11 +25,11 @@ msgstr "Contraseña: "
25#: plugins/Unity/Launcher/launcheritem.cpp:45
25#: plugins/Unity/Launcher/launcheritem.cpp:45
26#: plugins/Unity/Launcher/launcheritem.cpp:100
26#: plugins/Unity/Launcher/launcheritem.cpp:100
27msgid "Pin shortcut"
27msgid "Pin shortcut"
28−msgstr "Anclar"
28+msgstr "Anclar atayu"
29
29
30#: plugins/Unity/Launcher/launcheritem.cpp:100
30#: plugins/Unity/Launcher/launcheritem.cpp:100
31msgid "Unpin shortcut"
31msgid "Unpin shortcut"
32−msgstr "Desanclar"
32+msgstr "Desanclar atayu"
33
33
34#: qml/Components/DelayedLockscreen.qml:49
34#: qml/Components/DelayedLockscreen.qml:49
35msgid "Device Locked"
35msgid "Device Locked"
@@ -37,7 +37,7 @@ msgstr "Preséu bloquiáu"
37
37
38#: qml/Components/DelayedLockscreen.qml:64
38#: qml/Components/DelayedLockscreen.qml:64
39msgid "You have been locked out due to too many failed passphrase attempts."
39msgid "You have been locked out due to too many failed passphrase attempts."
40−msgstr "Quedasti bloquiáu polos escesivos intentos fallíos de frase de pasu."
40+msgstr "Quedasti bloquiáu polos escesivos intentos fallíos de fras de pasu."
41
41
42#: qml/Components/DelayedLockscreen.qml:65
42#: qml/Components/DelayedLockscreen.qml:65
43msgid "You have been locked out due to too many failed passcode attempts."
43msgid "You have been locked out due to too many failed passcode attempts."
@@ -48,27 +48,27 @@ msgstr ""
48#, qt-format
48#, qt-format
49msgid "Please wait %1 minute and then try again…"
49msgid "Please wait %1 minute and then try again…"
50msgid_plural "Please wait %1 minutes and then try again…"
50msgid_plural "Please wait %1 minutes and then try again…"
51−msgstr[0] "Espera %1 minutu y vuelvi a intentalo…"
51+msgstr[0] "Por favor, espera %1 minutu y vuelvi a intentalo…"
52−msgstr[1] "Espera %1 minutos y vuelvi a intentalo…"
52+msgstr[1] "Por favor, espera %1 minutos y vuelvi a intentalo…"
53
53
54#: qml/Components/Dialogs.qml:69
54#: qml/Components/Dialogs.qml:69
55msgctxt "Title: Lock/Log out dialog"
55msgctxt "Title: Lock/Log out dialog"
56msgid "Log out"
56msgid "Log out"
57−msgstr ""
57+msgstr "Zarrar sesión"
58
58
59#: qml/Components/Dialogs.qml:70
59#: qml/Components/Dialogs.qml:70
60msgid "Are you sure you want to log out?"
60msgid "Are you sure you want to log out?"
61−msgstr "De xuru que quies zarrar la sesión?"
61+msgstr "¿De xuru que quies zarrar la sesión?"
62
62
63#: qml/Components/Dialogs.qml:72
63#: qml/Components/Dialogs.qml:72
64msgctxt "Button: Lock the system"
64msgctxt "Button: Lock the system"
65msgid "Lock"
65msgid "Lock"
66−msgstr ""
66+msgstr "Bloquiar"
67
67
68#: qml/Components/Dialogs.qml:79
68#: qml/Components/Dialogs.qml:79
69msgctxt "Button: Log out from the system"
69msgctxt "Button: Log out from the system"
70msgid "Log Out"
70msgid "Log Out"
71−msgstr ""
71+msgstr "Zarrar sesión"
72
72
73#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
73#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
74#: qml/Components/Dialogs.qml:173
74#: qml/Components/Dialogs.qml:173
@@ -78,30 +78,30 @@ msgstr "Encaboxar"
78#: qml/Components/Dialogs.qml:98
78#: qml/Components/Dialogs.qml:98
79msgctxt "Title: Reboot/Shut down dialog"
79msgctxt "Title: Reboot/Shut down dialog"
80msgid "Shut down"
80msgid "Shut down"
81−msgstr ""
81+msgstr "Apagáu"
82
82
83#: qml/Components/Dialogs.qml:99
83#: qml/Components/Dialogs.qml:99
84msgid "Are you sure you want to shut down?"
84msgid "Are you sure you want to shut down?"
85−msgstr "De xuru que quies apagar?"
85+msgstr "¿De xuru que quies apagar?"
86
86
87#: qml/Components/Dialogs.qml:101
87#: qml/Components/Dialogs.qml:101
88msgctxt "Button: Reboot the system"
88msgctxt "Button: Reboot the system"
89msgid "Reboot"
89msgid "Reboot"
90−msgstr ""
90+msgstr "Reaniciar"
91
91
92#: qml/Components/Dialogs.qml:109
92#: qml/Components/Dialogs.qml:109
93msgctxt "Button: Shut down the system"
93msgctxt "Button: Shut down the system"
94msgid "Shut down"
94msgid "Shut down"
95−msgstr ""
95+msgstr "Apagar"
96
96
97#: qml/Components/Dialogs.qml:129
97#: qml/Components/Dialogs.qml:129
98msgctxt "Title: Reboot dialog"
98msgctxt "Title: Reboot dialog"
99msgid "Reboot"
99msgid "Reboot"
100−msgstr ""
100+msgstr "Reaniciu"
101
101
102#: qml/Components/Dialogs.qml:130
102#: qml/Components/Dialogs.qml:130
103msgid "Are you sure you want to reboot?"
103msgid "Are you sure you want to reboot?"
104−msgstr "De xuru que quies reaniciar?"
104+msgstr "¿De xuru que quies reaniciar?"
105
105
106#: qml/Components/Dialogs.qml:132
106#: qml/Components/Dialogs.qml:132
107msgid "No"
107msgid "No"
@@ -121,18 +121,18 @@ msgid ""
121"Are you sure you would like\n"
121"Are you sure you would like\n"
122"to power off?"
122"to power off?"
123msgstr ""
123msgstr ""
124−"De xuru que quies\n"
124+"¿De xuru que te prestaría\n"
125"apagar?"
125"apagar?"
126
126
127#: qml/Components/Dialogs.qml:155
127#: qml/Components/Dialogs.qml:155
128msgctxt "Button: Power off the system"
128msgctxt "Button: Power off the system"
129msgid "Power off"
129msgid "Power off"
130−msgstr ""
130+msgstr "Apagar"
131
131
132#: qml/Components/Dialogs.qml:164
132#: qml/Components/Dialogs.qml:164
133msgctxt "Button: Restart the system"
133msgctxt "Button: Restart the system"
134msgid "Restart"
134msgid "Restart"
135−msgstr ""
135+msgstr "Reaniciar"
136
136
137#: qml/Components/Lockscreen.qml:245
137#: qml/Components/Lockscreen.qml:245
138msgid "Return to Call"
138msgid "Return to Call"
@@ -157,22 +157,22 @@ msgstr "Ver too"
157#: qml/Dash/GenericScopeView.qml:594
157#: qml/Dash/GenericScopeView.qml:594
158msgctxt "Label: Hint for dash search line edit"
158msgctxt "Label: Hint for dash search line edit"
159msgid "Search"
159msgid "Search"
160−msgstr ""
160+msgstr "Guetar"
161
161
162#: qml/Dash/PageHeader.qml:269
162#: qml/Dash/PageHeader.qml:269
163msgctxt "Button: Open the Ubuntu Store"
163msgctxt "Button: Open the Ubuntu Store"
164msgid "Store"
164msgid "Store"
165−msgstr ""
165+msgstr "Tienda"
166
166
167#: qml/Dash/PageHeader.qml:276
167#: qml/Dash/PageHeader.qml:276
168msgctxt "Button: Start a search in the current dash scope"
168msgctxt "Button: Start a search in the current dash scope"
169msgid "Search"
169msgid "Search"
170−msgstr ""
170+msgstr "Guetar"
171
171
172#: qml/Dash/PageHeader.qml:286
172#: qml/Dash/PageHeader.qml:286
173msgctxt "Button: Show the current dash scope settings"
173msgctxt "Button: Show the current dash scope settings"
174msgid "Settings"
174msgid "Settings"
175−msgstr ""
175+msgstr "Axustes"
176
176
177#: qml/Dash/PageHeader.qml:293
177#: qml/Dash/PageHeader.qml:293
178msgid "Remove from Favorites"
178msgid "Remove from Favorites"
@@ -204,19 +204,19 @@ msgstr "Unviar"
204
204
205#: qml/Dash/PullToRefreshScopeStyle.qml:55
205#: qml/Dash/PullToRefreshScopeStyle.qml:55
206msgid "Pull to refresh…"
206msgid "Pull to refresh…"
207−msgstr "Tirar p'anovar..."
207+msgstr "Tira p'anovar..."
208
208
209#: qml/Dash/PullToRefreshScopeStyle.qml:60
209#: qml/Dash/PullToRefreshScopeStyle.qml:60
210msgid "Release to refresh…"
210msgid "Release to refresh…"
211−msgstr "Soltar p'anovar..."
211+msgstr "Sueltar p'anovar..."
212
212
213#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
213#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
214msgid "Enable location data"
214msgid "Enable location data"
215−msgstr "Activar datos de llocalización"
215+msgstr "Habilitar datos d'allugamientu"
216
216
217#: qml/Dash/ScopesList.qml:66
217#: qml/Dash/ScopesList.qml:66
218msgid "Manage"
218msgid "Manage"
219−msgstr "Alministrar"
219+msgstr "Xestionar"
220
220
221#: qml/Dash/ScopesList.qml:112
221#: qml/Dash/ScopesList.qml:112
222msgid "Home"
222msgid "Home"
@@ -232,52 +232,52 @@ msgstr "Desbloquiar"
232
232
233#: qml/Greeter/LoginList.qml:236
233#: qml/Greeter/LoginList.qml:236
234msgid "Retry"
234msgid "Retry"
235−msgstr ""
235+msgstr "Reintentar"
236
236
237#: qml/Greeter/LoginList.qml:237
237#: qml/Greeter/LoginList.qml:237
238msgid "Tap to unlock"
238msgid "Tap to unlock"
239−msgstr ""
239+msgstr "Calca pa desbloquiar"
240
240
241#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
241#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
242msgid "Sorry, incorrect passphrase."
242msgid "Sorry, incorrect passphrase."
243−msgstr "Fras de pasu incorreuta"
243+msgstr "Perdón, fras de pasu incorreuta"
244
244
245#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
245#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
246msgid "Sorry, incorrect passcode."
246msgid "Sorry, incorrect passcode."
247−msgstr "Códigu de pasu incorreutu"
247+msgstr "Perdón, códigu de pasu incorreutu."
248
248
249#: qml/Greeter/NarrowView.qml:55
249#: qml/Greeter/NarrowView.qml:55
250msgid "This will be your last attempt."
250msgid "This will be your last attempt."
251−msgstr "Esti ye'l caberu intentu."
251+msgstr "Esti sedra'l to caberu intentu"
252
252
253#: qml/Greeter/NarrowView.qml:57
253#: qml/Greeter/NarrowView.qml:57
254msgid ""
254msgid ""
255"If passphrase is entered incorrectly, your phone will conduct a factory "
255"If passphrase is entered incorrectly, your phone will conduct a factory "
256"reset and all personal data will be deleted."
256"reset and all personal data will be deleted."
257msgstr ""
257msgstr ""
258−"Si s'inxerta la fras incorreutamente, el teléfonu va volver a la "
258+"Si s'inxerta la fras de pasu incorreutamente, el to teléfonu va reafitase y "
259−"configuración de fábrica y van perdese tolos datos personales."
259+"van desaniciase tolos datos personales."
260
260
261#: qml/Greeter/NarrowView.qml:58
261#: qml/Greeter/NarrowView.qml:58
262msgid ""
262msgid ""
263"If passcode is entered incorrectly, your phone will conduct a factory reset "
263"If passcode is entered incorrectly, your phone will conduct a factory reset "
264"and all personal data will be deleted."
264"and all personal data will be deleted."
265msgstr ""
265msgstr ""
266−"Si s'inxerta'l códigu incorreutamente, el teléfonu va volver a la "
266+"Si s'inxerta'l códigu de pasu incorreutamente, el to teléfonu va reafitase y "
267−"configuración de fábrica y van perdese tolos datos personales."
267+"van desaniciase tolos datos personales."
268
268
269#: qml/Greeter/NarrowView.qml:115
269#: qml/Greeter/NarrowView.qml:115
270#, qt-format
270#, qt-format
271msgid "Enter %1"
271msgid "Enter %1"
272−msgstr "Escribi %1"
272+msgstr "Introduz %1"
273
273
274#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
274#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
275msgid "Enter passphrase"
275msgid "Enter passphrase"
276−msgstr "Escribi la frase contraseña"
276+msgstr "Introduz la fras de pasu"
277
277
278#: qml/Greeter/NarrowView.qml:117
278#: qml/Greeter/NarrowView.qml:117
279msgid "Enter passcode"
279msgid "Enter passcode"
280−msgstr "Escribi'l códigu de pasu"
280+msgstr "Introduz el códigu de pasu"
281
281
282#: qml/Greeter/NarrowView.qml:118
282#: qml/Greeter/NarrowView.qml:118
283#, qt-format
283#, qt-format
@@ -290,7 +290,7 @@ msgstr "Sentímoslo, contraseña incorreuta"
290
290
291#: qml/Greeter/NarrowView.qml:120
291#: qml/Greeter/NarrowView.qml:120
292msgid "Please re-enter"
292msgid "Please re-enter"
293−msgstr "Vuelvi a escribila"
293+msgstr "Por favor, reintroduzla"
294
294
295#: qml/Greeter/NarrowView.qml:121
295#: qml/Greeter/NarrowView.qml:121
296msgid "Sorry, incorrect passcode"
296msgid "Sorry, incorrect passcode"
@@ -302,7 +302,7 @@ msgstr "Amosar contraseña"
302
302
303#: qml/Panel/ActiveCallHint.qml:79
303#: qml/Panel/ActiveCallHint.qml:79
304msgid "Tap to return to call..."
304msgid "Tap to return to call..."
305−msgstr "Tocar pa tornar a la llamada..."
305+msgstr "Toca pa tornar a la llamada..."
306
306
307#: qml/Panel/ActiveCallHint.qml:92
307#: qml/Panel/ActiveCallHint.qml:92
308msgid "Conference"
308msgid "Conference"
@@ -310,11 +310,11 @@ msgstr "Conferencia"
310
310
311#: qml/Panel/Indicators/MenuItemFactory.qml:639
311#: qml/Panel/Indicators/MenuItemFactory.qml:639
312msgid "Nothing is playing"
312msgid "Nothing is playing"
313−msgstr "Nun hai nada en reproducción"
313+msgstr "Nun ta reproduciéndose nada"
314
314
315#: qml/Panel/Indicators/MenuItemFactory.qml:787
315#: qml/Panel/Indicators/MenuItemFactory.qml:787
316msgid "In queue…"
316msgid "In queue…"
317−msgstr "N'espera…"
317+msgstr "Na cola..."
318
318
319#: qml/Panel/Indicators/MenuItemFactory.qml:791
319#: qml/Panel/Indicators/MenuItemFactory.qml:791
320msgid "Downloading"
320msgid "Downloading"
@@ -339,7 +339,7 @@ msgstr "Falló, toca pa reintentar"
339#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
339#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
340msgctxt "Label: Hint in message indicator line edit"
340msgctxt "Label: Hint in message indicator line edit"
341msgid "Reply"
341msgid "Reply"
342−msgstr ""
342+msgstr "Responder"
343
343
344#: qml/Panel/Indicators/ModemInfoItem.qml:105
344#: qml/Panel/Indicators/ModemInfoItem.qml:105
345msgid "Unlock SIM"
345msgid "Unlock SIM"
@@ -351,7 +351,7 @@ msgstr "Roaming"
351
351
352#: qml/Tutorial/TutorialBottomFinish.qml:24
352#: qml/Tutorial/TutorialBottomFinish.qml:24
353msgid "This action does different things for different apps"
353msgid "This action does different things for different apps"
354−msgstr "Esta aición tiene efeutos distintos en cada aplicación"
354+msgstr "Esta aición fai coses distintes p'aplicaciones estremaes"
355
355
356#: qml/Tutorial/TutorialBottomFinish.qml:25
356#: qml/Tutorial/TutorialBottomFinish.qml:25
357msgid "Tap here to finish."
357msgid "Tap here to finish."
@@ -363,7 +363,7 @@ msgstr "Abrir menús especiales"
363
363
364#: qml/Tutorial/TutorialBottom.qml:29
364#: qml/Tutorial/TutorialBottom.qml:29
365msgid "Swipe up from the bottom edge."
365msgid "Swipe up from the bottom edge."
366−msgstr ""
366+msgstr "Esliza dende'l berbesu d'embaxo."
367
367
368#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
368#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
369#: qml/Tutorial/TutorialRight.qml:144
369#: qml/Tutorial/TutorialRight.qml:144
@@ -372,11 +372,11 @@ msgstr "Toca equí pa siguir."
372
372
373#: qml/Tutorial/TutorialContent.qml:81
373#: qml/Tutorial/TutorialContent.qml:81
374msgid "Click here to finish."
374msgid "Click here to finish."
375−msgstr ""
375+msgstr "Primi equí pa finar."
376
376
377#: qml/Tutorial/TutorialLeftFinish.qml:24
377#: qml/Tutorial/TutorialLeftFinish.qml:24
378msgid "These are the shortcuts to favorite apps"
378msgid "These are the shortcuts to favorite apps"
379−msgstr ""
379+msgstr "Estos son los atayos a les aplicaciones favorites"
380
380
381#: qml/Tutorial/TutorialLeft.qml:26
381#: qml/Tutorial/TutorialLeft.qml:26
382msgid "Open the launcher"
382msgid "Open the launcher"
@@ -384,44 +384,44 @@ msgstr "Abrir el llanzador"
384
384
385#: qml/Tutorial/TutorialLeft.qml:27
385#: qml/Tutorial/TutorialLeft.qml:27
386msgid "Short swipe from the left edge."
386msgid "Short swipe from the left edge."
387−msgstr ""
387+msgstr "Esliza un poco dende'l berbesu esquierdu."
388
388
389#: qml/Tutorial/TutorialPage.qml:178
389#: qml/Tutorial/TutorialPage.qml:178
390msgid "You almost got it!"
390msgid "You almost got it!"
391−msgstr ""
391+msgstr "¡Yá cuasi finamos!"
392
392
393#: qml/Tutorial/TutorialPage.qml:195
393#: qml/Tutorial/TutorialPage.qml:195
394msgid "Try again."
394msgid "Try again."
395−msgstr ""
395+msgstr "Intentar de nueves"
396
396
397#: qml/Tutorial/TutorialRight.qml:31
397#: qml/Tutorial/TutorialRight.qml:31
398msgid "To view open apps"
398msgid "To view open apps"
399−msgstr ""
399+msgstr "Pa ver aplicaciones abiertes"
400
400
401#: qml/Tutorial/TutorialRight.qml:32
401#: qml/Tutorial/TutorialRight.qml:32
402msgid "Long swipe from the right edge."
402msgid "Long swipe from the right edge."
403−msgstr ""
403+msgstr "Eslizamientu llargu dende'l berbesu drechu."
404
404
405#: qml/Tutorial/TutorialRight.qml:122
405#: qml/Tutorial/TutorialRight.qml:122
406msgid "View all your running tasks."
406msgid "View all your running tasks."
407−msgstr ""
407+msgstr "Mira toles tos aplicaciones n'execución."
408
408
409#: qml/Wizard/Page.qml:89
409#: qml/Wizard/Page.qml:89
410msgctxt "Button: Go back one page in the Wizard"
410msgctxt "Button: Go back one page in the Wizard"
411msgid "Back"
411msgid "Back"
412−msgstr ""
412+msgstr "Atrás"
413
413
414#: qml/Wizard/Pages/10-welcome.qml:27
414#: qml/Wizard/Pages/10-welcome.qml:27
415msgid "Hi!"
415msgid "Hi!"
416−msgstr "Hola!"
416+msgstr "¡Hola!"
417
417
418#: qml/Wizard/Pages/10-welcome.qml:44
418#: qml/Wizard/Pages/10-welcome.qml:44
419msgid "Welcome to your Ubuntu phone."
419msgid "Welcome to your Ubuntu phone."
420−msgstr "Dámoste l'acoyida a Ubuntu Phone"
420+msgstr "Bienllegáu/ada al to teléfonu d'Ubuntu"
421
421
422#: qml/Wizard/Pages/10-welcome.qml:52
422#: qml/Wizard/Pages/10-welcome.qml:52
423msgid "Let’s get started."
423msgid "Let’s get started."
424−msgstr "Entamamos."
424+msgstr "Entamemos."
425
425
426#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
426#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
427#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
427#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
@@ -432,7 +432,7 @@ msgstr "Siguir"
432
432
433#: qml/Wizard/Pages/20-sim.qml:25
433#: qml/Wizard/Pages/20-sim.qml:25
434msgid "Add a SIM card and restart your device"
434msgid "Add a SIM card and restart your device"
435−msgstr "Amiesta una tarxeta SIM y reanicia'l preséu"
435+msgstr "Amiesta una tarxeta SIM y reanicia'l to preséu"
436
436
437#: qml/Wizard/Pages/20-sim.qml:59
437#: qml/Wizard/Pages/20-sim.qml:59
438msgid "Without it, you won’t be able to make calls or use text messaging."
438msgid "Without it, you won’t be able to make calls or use text messaging."
@@ -440,49 +440,49 @@ msgstr "Ensin ella, nun vas poder facer llamaes nin unviar mensaxes."
440
440
441#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
441#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
442msgid "Skip"
442msgid "Skip"
443−msgstr "Omitir"
443+msgstr "Saltar"
444
444
445#: qml/Wizard/Pages/30-passwd-type.qml:39
445#: qml/Wizard/Pages/30-passwd-type.qml:39
446msgid "Lock security"
446msgid "Lock security"
447−msgstr "Seguridá de bloquéu"
447+msgstr "Seguranza del bloquéu"
448
448
449#: qml/Wizard/Pages/30-passwd-type.qml:74
449#: qml/Wizard/Pages/30-passwd-type.qml:74
450msgid "Please select how you’d like to unlock your phone."
450msgid "Please select how you’d like to unlock your phone."
451−msgstr "Seleiciona cómo te prestaría desbloquiar el teléfonu."
451+msgstr "Por favor, esbilla como te prestaría desbloquiar el teléfonu."
452
452
453#: qml/Wizard/Pages/30-passwd-type.qml:97
453#: qml/Wizard/Pages/30-passwd-type.qml:97
454msgctxt "Label: Type of security method"
454msgctxt "Label: Type of security method"
455msgid "Swipe"
455msgid "Swipe"
456−msgstr ""
456+msgstr "Eslizar"
457
457
458#: qml/Wizard/Pages/30-passwd-type.qml:98
458#: qml/Wizard/Pages/30-passwd-type.qml:98
459msgctxt "Label: Description of security method"
459msgctxt "Label: Description of security method"
460msgid "No security"
460msgid "No security"
461−msgstr ""
461+msgstr "Ensin seguranza"
462
462
463#: qml/Wizard/Pages/30-passwd-type.qml:100
463#: qml/Wizard/Pages/30-passwd-type.qml:100
464msgctxt "Label: Type of security method"
464msgctxt "Label: Type of security method"
465msgid "Passcode"
465msgid "Passcode"
466−msgstr ""
466+msgstr "Códigu de pasu"
467
467
468#: qml/Wizard/Pages/30-passwd-type.qml:101
468#: qml/Wizard/Pages/30-passwd-type.qml:101
469msgctxt "Label: Description of security method"
469msgctxt "Label: Description of security method"
470msgid "4 digits only"
470msgid "4 digits only"
471−msgstr ""
471+msgstr "Namái 4 díxitos"
472
472
473#: qml/Wizard/Pages/30-passwd-type.qml:103
473#: qml/Wizard/Pages/30-passwd-type.qml:103
474msgctxt "Label: Type of security method"
474msgctxt "Label: Type of security method"
475msgid "Passphrase"
475msgid "Passphrase"
476−msgstr ""
476+msgstr "Fras de pasu"
477
477
478#: qml/Wizard/Pages/30-passwd-type.qml:104
478#: qml/Wizard/Pages/30-passwd-type.qml:104
479msgctxt "Label: Description of security method"
479msgctxt "Label: Description of security method"
480msgid "Numbers and letters"
480msgid "Numbers and letters"
481−msgstr ""
481+msgstr "Númberos y lletres"
482
482
483#: qml/Wizard/Pages/40-wifi.qml:29
483#: qml/Wizard/Pages/40-wifi.qml:29
484msgid "Connect to Wi‑Fi"
484msgid "Connect to Wi‑Fi"
485−msgstr "Coneutar a WiFi"
485+msgstr "Coneutase al WiFi"
486
486
487#: qml/Wizard/Pages/40-wifi.qml:168
487#: qml/Wizard/Pages/40-wifi.qml:168
488msgid "Available networks…"
488msgid "Available networks…"
@@ -494,11 +494,11 @@ msgstr "Nun hai redes disponibles"
494
494
495#: qml/Wizard/Pages/50-location.qml:27
495#: qml/Wizard/Pages/50-location.qml:27
496msgid "Location"
496msgid "Location"
497−msgstr "Llocalización"
497+msgstr "Allugamientu"
498
498
499#: qml/Wizard/Pages/50-location.qml:62
499#: qml/Wizard/Pages/50-location.qml:62
500msgid "Let the phone detect your location:"
500msgid "Let the phone detect your location:"
501−msgstr "Permitir al teléfonu deteutar la llocalización:"
501+msgstr "Dexar al teléfonu deteutar l'allugamientu:"
502
502
503#: qml/Wizard/Pages/50-location.qml:69
503#: qml/Wizard/Pages/50-location.qml:69
504msgid "Using GPS only (less accurate)"
504msgid "Using GPS only (less accurate)"
@@ -507,7 +507,7 @@ msgstr "Namái usando GPS (menos precisión)"
507#: qml/Wizard/Pages/50-location.qml:86
507#: qml/Wizard/Pages/50-location.qml:86
508msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
508msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
509msgstr ""
509msgstr ""
510−"Usando GPS, Wifi anónimes ya información de redes móviles (recomiéndase)"
510+"Usando GPS, Wi-fi anónimes ya información de redes móviles (aconseyao)"
511
511
512#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
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
513#: qml/Wizard/Pages/50-location.qml:103
@@ -515,8 +515,8 @@ msgid ""
515"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
515"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
516"conditions</a>."
516"conditions</a>."
517msgstr ""
517msgstr ""
518−"Seleicionando esta opción, aceutes los <a href='#'>términos y "
518+"Esbillando esta opción aceutes los <a href='#'>términos y condiciones</a> de "
519−"condiciones</a> de Nokia HERE."
519+"Nokia HERE."
520
520
521#: qml/Wizard/Pages/50-location.qml:112
521#: qml/Wizard/Pages/50-location.qml:112
522msgid "Not at all"
522msgid "Not at all"
@@ -528,7 +528,7 @@ msgstr "Pues cambialo dempués n'<b>Axustes del sistema</b>."
528
528
529#: qml/Wizard/Pages/60-reporting.qml:24
529#: qml/Wizard/Pages/60-reporting.qml:24
530msgid "Improving your experience"
530msgid "Improving your experience"
531−msgstr "Ameyorar la esperiencia"
531+msgstr "Ameyorando la to esperiencia"
532
532
533#: qml/Wizard/Pages/60-reporting.qml:36
533#: qml/Wizard/Pages/60-reporting.qml:36
534msgid ""
534msgid ""
@@ -543,7 +543,7 @@ msgid ""
543"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
543"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
544"Privacy</b>"
544"Privacy</b>"
545msgstr ""
545msgstr ""
546−"Esto pue desactivase nos <b>Axustes del sistema</b> en <b>Seguranza y "
546+"Esto pue deshabilitase nos <b>Axustes del sistema</b> en <b>Seguranza y "
547"privacidá</b>"
547"privacidá</b>"
548
548
549#: qml/Wizard/Pages/80-finished.qml:24
549#: qml/Wizard/Pages/80-finished.qml:24
@@ -552,11 +552,11 @@ msgstr "Too fecho"
552
552
553#: qml/Wizard/Pages/80-finished.qml:39
553#: qml/Wizard/Pages/80-finished.qml:39
554msgid "Nice work!"
554msgid "Nice work!"
555−msgstr "Bon trabayu!"
555+msgstr "¡Bon trabayu!"
556
556
557#: qml/Wizard/Pages/80-finished.qml:46
557#: qml/Wizard/Pages/80-finished.qml:46
558msgid "Your phone is now ready to use."
558msgid "Your phone is now ready to use."
559−msgstr "El teléfonu yá ta preparáu pa usar."
559+msgstr "El teléfonu yá ta tresnáu pa usase."
560
560
561#: qml/Wizard/Pages/80-finished.qml:53
561#: qml/Wizard/Pages/80-finished.qml:53
562msgid "Finish"
562msgid "Finish"
@@ -568,24 +568,24 @@ msgstr "Términos y condiciones"
568
568
569#: qml/Wizard/Pages/passwd-confirm.qml:49
569#: qml/Wizard/Pages/passwd-confirm.qml:49
570msgid "Confirm passphrase"
570msgid "Confirm passphrase"
571−msgstr "Confirmar fras secreta"
571+msgstr "Confirmar frase de pasu"
572
572
573#: qml/Wizard/Pages/passwd-confirm.qml:50
573#: qml/Wizard/Pages/passwd-confirm.qml:50
574msgid "Confirm passcode"
574msgid "Confirm passcode"
575−msgstr "Confirmar el códigu de pasu"
575+msgstr "Confirmar códigu de pasu"
576
576
577#: qml/Wizard/Pages/passwd-confirm.qml:53
577#: qml/Wizard/Pages/passwd-confirm.qml:53
578#: qml/Wizard/Pages/passwd-confirm.qml:54
578#: qml/Wizard/Pages/passwd-confirm.qml:54
579msgid "Please try again."
579msgid "Please try again."
580−msgstr "Vuelvi a intentalo."
580+msgstr "Por favor, vuelvi a intentalo."
581
581
582#: qml/Wizard/Pages/passwd-set.qml:61
582#: qml/Wizard/Pages/passwd-set.qml:61
583msgid "Choose your passcode"
583msgid "Choose your passcode"
584−msgstr "Escueyi'l códigu de pasu"
584+msgstr "Escueyi'l to códigu de pasu"
585
585
586#: qml/Wizard/Pages/passwd-set.qml:68
586#: qml/Wizard/Pages/passwd-set.qml:68
587msgid "Passphrase must be 4 characters long"
587msgid "Passphrase must be 4 characters long"
588−msgstr "La fras de contraseña tien de ser de 4 caráuteres"
588+msgstr "La fras de pasu tien de ser 4 caráuteres de llargor"
589
589
590#: qml/Wizard/StackButton.qml:39
590#: qml/Wizard/StackButton.qml:39
591#, qt-format
591#, qt-format
po/be.po+46−46
@@ -8,16 +8,16 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-02-07 19:04+0000\n"
11+"PO-Revision-Date: 2015-04-27 14:34+0000\n"
12−"Last-Translator: Mikhail_SaTuRn <mikhail_saturn@tut.by>\n"
12+"Last-Translator: Arsień Šachalevič <semen.shah@gmail.com>\n"
13"Language-Team: Belarusian <be@li.org>\n"
13"Language-Team: Belarusian <be@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
17"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
19−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19+"X-Launchpad-Export-Date: 2015-04-28 05:33+0000\n"
20−"X-Generator: Launchpad (build 17430)\n"
20+"X-Generator: Launchpad (build 17453)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
23msgid "Password: "
23msgid "Password: "
@@ -26,11 +26,11 @@ msgstr "Пароль: "
26#: plugins/Unity/Launcher/launcheritem.cpp:45
26#: plugins/Unity/Launcher/launcheritem.cpp:45
27#: plugins/Unity/Launcher/launcheritem.cpp:100
27#: plugins/Unity/Launcher/launcheritem.cpp:100
28msgid "Pin shortcut"
28msgid "Pin shortcut"
29−msgstr "Заблакаваць кнопку"
29+msgstr "Замацаваць кнопку"
30
30
31#: plugins/Unity/Launcher/launcheritem.cpp:100
31#: plugins/Unity/Launcher/launcheritem.cpp:100
32msgid "Unpin shortcut"
32msgid "Unpin shortcut"
33−msgstr "Разблакаваць кнопку"
33+msgstr "Адмацаваць кнопку"
34
34
35#: qml/Components/DelayedLockscreen.qml:49
35#: qml/Components/DelayedLockscreen.qml:49
36msgid "Device Locked"
36msgid "Device Locked"
@@ -58,21 +58,21 @@ msgstr[2] "Пачакайце %1 хвілінаў і паўтарыце спро
58#: qml/Components/Dialogs.qml:69
58#: qml/Components/Dialogs.qml:69
59msgctxt "Title: Lock/Log out dialog"
59msgctxt "Title: Lock/Log out dialog"
60msgid "Log out"
60msgid "Log out"
61−msgstr ""
61+msgstr "Выйсці"
62
62
63#: qml/Components/Dialogs.qml:70
63#: qml/Components/Dialogs.qml:70
64msgid "Are you sure you want to log out?"
64msgid "Are you sure you want to log out?"
65−msgstr "Вы насамрэч хочаце выйсці?"
65+msgstr "Вы ўпэўнены, што хочаце выйсці?"
66
66
67#: qml/Components/Dialogs.qml:72
67#: qml/Components/Dialogs.qml:72
68msgctxt "Button: Lock the system"
68msgctxt "Button: Lock the system"
69msgid "Lock"
69msgid "Lock"
70−msgstr ""
70+msgstr "Заблакаваць"
71
71
72#: qml/Components/Dialogs.qml:79
72#: qml/Components/Dialogs.qml:79
73msgctxt "Button: Log out from the system"
73msgctxt "Button: Log out from the system"
74msgid "Log Out"
74msgid "Log Out"
75−msgstr ""
75+msgstr "Выйсці"
76
76
77#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
77#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
78#: qml/Components/Dialogs.qml:173
78#: qml/Components/Dialogs.qml:173
@@ -82,7 +82,7 @@ msgstr "Скасаваць"
82#: qml/Components/Dialogs.qml:98
82#: qml/Components/Dialogs.qml:98
83msgctxt "Title: Reboot/Shut down dialog"
83msgctxt "Title: Reboot/Shut down dialog"
84msgid "Shut down"
84msgid "Shut down"
85−msgstr ""
85+msgstr "Выключыць"
86
86
87#: qml/Components/Dialogs.qml:99
87#: qml/Components/Dialogs.qml:99
88msgid "Are you sure you want to shut down?"
88msgid "Are you sure you want to shut down?"
@@ -91,17 +91,17 @@ msgstr "Вы ўпэўненыя, што жадаеце выключыць пры
91#: qml/Components/Dialogs.qml:101
91#: qml/Components/Dialogs.qml:101
92msgctxt "Button: Reboot the system"
92msgctxt "Button: Reboot the system"
93msgid "Reboot"
93msgid "Reboot"
94−msgstr ""
94+msgstr "Перазагрузіць"
95
95
96#: qml/Components/Dialogs.qml:109
96#: qml/Components/Dialogs.qml:109
97msgctxt "Button: Shut down the system"
97msgctxt "Button: Shut down the system"
98msgid "Shut down"
98msgid "Shut down"
99−msgstr ""
99+msgstr "Выключыць"
100
100
101#: qml/Components/Dialogs.qml:129
101#: qml/Components/Dialogs.qml:129
102msgctxt "Title: Reboot dialog"
102msgctxt "Title: Reboot dialog"
103msgid "Reboot"
103msgid "Reboot"
104−msgstr ""
104+msgstr "Перазагрузіць"
105
105
106#: qml/Components/Dialogs.qml:130
106#: qml/Components/Dialogs.qml:130
107msgid "Are you sure you want to reboot?"
107msgid "Are you sure you want to reboot?"
@@ -118,7 +118,7 @@ msgstr "Так"
118#: qml/Components/Dialogs.qml:152
118#: qml/Components/Dialogs.qml:152
119msgctxt "Title: Power off/Restart dialog"
119msgctxt "Title: Power off/Restart dialog"
120msgid "Power"
120msgid "Power"
121−msgstr ""
121+msgstr "Сілкаванне"
122
122
123#: qml/Components/Dialogs.qml:153
123#: qml/Components/Dialogs.qml:153
124msgid ""
124msgid ""
@@ -131,12 +131,12 @@ msgstr ""
131#: qml/Components/Dialogs.qml:155
131#: qml/Components/Dialogs.qml:155
132msgctxt "Button: Power off the system"
132msgctxt "Button: Power off the system"
133msgid "Power off"
133msgid "Power off"
134−msgstr ""
134+msgstr "Выключыць"
135
135
136#: qml/Components/Dialogs.qml:164
136#: qml/Components/Dialogs.qml:164
137msgctxt "Button: Restart the system"
137msgctxt "Button: Restart the system"
138msgid "Restart"
138msgid "Restart"
139−msgstr ""
139+msgstr "Перазапуск"
140
140
141#: qml/Components/Lockscreen.qml:245
141#: qml/Components/Lockscreen.qml:245
142msgid "Return to Call"
142msgid "Return to Call"
@@ -161,22 +161,22 @@ msgstr "Больш падрабязна"
161#: qml/Dash/GenericScopeView.qml:594
161#: qml/Dash/GenericScopeView.qml:594
162msgctxt "Label: Hint for dash search line edit"
162msgctxt "Label: Hint for dash search line edit"
163msgid "Search"
163msgid "Search"
164−msgstr ""
164+msgstr "Пошук"
165
165
166#: qml/Dash/PageHeader.qml:269
166#: qml/Dash/PageHeader.qml:269
167msgctxt "Button: Open the Ubuntu Store"
167msgctxt "Button: Open the Ubuntu Store"
168msgid "Store"
168msgid "Store"
169−msgstr ""
169+msgstr "Крама"
170
170
171#: qml/Dash/PageHeader.qml:276
171#: qml/Dash/PageHeader.qml:276
172msgctxt "Button: Start a search in the current dash scope"
172msgctxt "Button: Start a search in the current dash scope"
173msgid "Search"
173msgid "Search"
174−msgstr ""
174+msgstr "Пошук"
175
175
176#: qml/Dash/PageHeader.qml:286
176#: qml/Dash/PageHeader.qml:286
177msgctxt "Button: Show the current dash scope settings"
177msgctxt "Button: Show the current dash scope settings"
178msgid "Settings"
178msgid "Settings"
179−msgstr ""
179+msgstr "Налады"
180
180
181#: qml/Dash/PageHeader.qml:293
181#: qml/Dash/PageHeader.qml:293
182msgid "Remove from Favorites"
182msgid "Remove from Favorites"
@@ -216,7 +216,7 @@ msgstr "Адпусціце для абнаўлення ..."
216
216
217#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
217#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
218msgid "Enable location data"
218msgid "Enable location data"
219−msgstr ""
219+msgstr "Уключыць дадзеныя аб месцазнаходжанні"
220
220
221#: qml/Dash/ScopesList.qml:66
221#: qml/Dash/ScopesList.qml:66
222msgid "Manage"
222msgid "Manage"
@@ -236,11 +236,11 @@ msgstr "Разблакаваць"
236
236
237#: qml/Greeter/LoginList.qml:236
237#: qml/Greeter/LoginList.qml:236
238msgid "Retry"
238msgid "Retry"
239−msgstr ""
239+msgstr "Паспрабаваць ізноў"
240
240
241#: qml/Greeter/LoginList.qml:237
241#: qml/Greeter/LoginList.qml:237
242msgid "Tap to unlock"
242msgid "Tap to unlock"
243−msgstr ""
243+msgstr "Націсніце, каб разблакаваць"
244
244
245#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
245#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
246msgid "Sorry, incorrect passphrase."
246msgid "Sorry, incorrect passphrase."
@@ -343,7 +343,7 @@ msgstr "Памылка, націсніце для паўтору"
343#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
343#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
344msgctxt "Label: Hint in message indicator line edit"
344msgctxt "Label: Hint in message indicator line edit"
345msgid "Reply"
345msgid "Reply"
346−msgstr ""
346+msgstr "Адказаць"
347
347
348#: qml/Panel/Indicators/ModemInfoItem.qml:105
348#: qml/Panel/Indicators/ModemInfoItem.qml:105
349msgid "Unlock SIM"
349msgid "Unlock SIM"
@@ -355,65 +355,65 @@ msgstr "Роўмінг"
355
355
356#: qml/Tutorial/TutorialBottomFinish.qml:24
356#: qml/Tutorial/TutorialBottomFinish.qml:24
357msgid "This action does different things for different apps"
357msgid "This action does different things for different apps"
358−msgstr ""
358+msgstr "Гэта дзеянне мае розныя наступствы для розных праграм"
359
359
360#: qml/Tutorial/TutorialBottomFinish.qml:25
360#: qml/Tutorial/TutorialBottomFinish.qml:25
361msgid "Tap here to finish."
361msgid "Tap here to finish."
362−msgstr ""
362+msgstr "Націсніце тут, каб завяршыць."
363
363
364#: qml/Tutorial/TutorialBottom.qml:28
364#: qml/Tutorial/TutorialBottom.qml:28
365msgid "Open special menus"
365msgid "Open special menus"
366−msgstr ""
366+msgstr "Адкрыць спецыяльныя меню"
367
367
368#: qml/Tutorial/TutorialBottom.qml:29
368#: qml/Tutorial/TutorialBottom.qml:29
369msgid "Swipe up from the bottom edge."
369msgid "Swipe up from the bottom edge."
370−msgstr ""
370+msgstr "Правесці уверх ад ніжняга краю."
371
371
372#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
372#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
373#: qml/Tutorial/TutorialRight.qml:144
373#: qml/Tutorial/TutorialRight.qml:144
374msgid "Tap here to continue."
374msgid "Tap here to continue."
375−msgstr ""
375+msgstr "Націсніце тут, каб працягнуць."
376
376
377#: qml/Tutorial/TutorialContent.qml:81
377#: qml/Tutorial/TutorialContent.qml:81
378msgid "Click here to finish."
378msgid "Click here to finish."
379−msgstr ""
379+msgstr "Націсніце тут для завяршэння."
380
380
381#: qml/Tutorial/TutorialLeftFinish.qml:24
381#: qml/Tutorial/TutorialLeftFinish.qml:24
382msgid "These are the shortcuts to favorite apps"
382msgid "These are the shortcuts to favorite apps"
383−msgstr ""
383+msgstr "Гэта скарачэнні для абраных праграм"
384
384
385#: qml/Tutorial/TutorialLeft.qml:26
385#: qml/Tutorial/TutorialLeft.qml:26
386msgid "Open the launcher"
386msgid "Open the launcher"
387−msgstr ""
387+msgstr "Адкрыць панэль запуску"
388
388
389#: qml/Tutorial/TutorialLeft.qml:27
389#: qml/Tutorial/TutorialLeft.qml:27
390msgid "Short swipe from the left edge."
390msgid "Short swipe from the left edge."
391−msgstr ""
391+msgstr "Кароткі рух з левага краю."
392
392
393#: qml/Tutorial/TutorialPage.qml:178
393#: qml/Tutorial/TutorialPage.qml:178
394msgid "You almost got it!"
394msgid "You almost got it!"
395−msgstr ""
395+msgstr "Амаль усё!"
396
396
397#: qml/Tutorial/TutorialPage.qml:195
397#: qml/Tutorial/TutorialPage.qml:195
398msgid "Try again."
398msgid "Try again."
399−msgstr ""
399+msgstr "Паспрабуйце яшчэ."
400
400
401#: qml/Tutorial/TutorialRight.qml:31
401#: qml/Tutorial/TutorialRight.qml:31
402msgid "To view open apps"
402msgid "To view open apps"
403−msgstr ""
403+msgstr "Паглядзець адкрытыя праграмы"
404
404
405#: qml/Tutorial/TutorialRight.qml:32
405#: qml/Tutorial/TutorialRight.qml:32
406msgid "Long swipe from the right edge."
406msgid "Long swipe from the right edge."
407−msgstr ""
407+msgstr "Доўгі рух ад правага краю."
408
408
409#: qml/Tutorial/TutorialRight.qml:122
409#: qml/Tutorial/TutorialRight.qml:122
410msgid "View all your running tasks."
410msgid "View all your running tasks."
411−msgstr ""
411+msgstr "Паказаць усе запушчаныя задачы."
412
412
413#: qml/Wizard/Page.qml:89
413#: qml/Wizard/Page.qml:89
414msgctxt "Button: Go back one page in the Wizard"
414msgctxt "Button: Go back one page in the Wizard"
415msgid "Back"
415msgid "Back"
416−msgstr ""
416+msgstr "Вярнуцца"
417
417
418#: qml/Wizard/Pages/10-welcome.qml:27
418#: qml/Wizard/Pages/10-welcome.qml:27
419msgid "Hi!"
419msgid "Hi!"
@@ -457,32 +457,32 @@ msgstr "Калі ласка, абярыце спосаб разблакаван
457#: qml/Wizard/Pages/30-passwd-type.qml:97
457#: qml/Wizard/Pages/30-passwd-type.qml:97
458msgctxt "Label: Type of security method"
458msgctxt "Label: Type of security method"
459msgid "Swipe"
459msgid "Swipe"
460−msgstr ""
460+msgstr "Рух"
461
461
462#: qml/Wizard/Pages/30-passwd-type.qml:98
462#: qml/Wizard/Pages/30-passwd-type.qml:98
463msgctxt "Label: Description of security method"
463msgctxt "Label: Description of security method"
464msgid "No security"
464msgid "No security"
465−msgstr ""
465+msgstr "Без бяспекі"
466
466
467#: qml/Wizard/Pages/30-passwd-type.qml:100
467#: qml/Wizard/Pages/30-passwd-type.qml:100
468msgctxt "Label: Type of security method"
468msgctxt "Label: Type of security method"
469msgid "Passcode"
469msgid "Passcode"
470−msgstr ""
470+msgstr "Пароль"
471
471
472#: qml/Wizard/Pages/30-passwd-type.qml:101
472#: qml/Wizard/Pages/30-passwd-type.qml:101
473msgctxt "Label: Description of security method"
473msgctxt "Label: Description of security method"
474msgid "4 digits only"
474msgid "4 digits only"
475−msgstr ""
475+msgstr "Толькі 4 лічбы"
476
476
477#: qml/Wizard/Pages/30-passwd-type.qml:103
477#: qml/Wizard/Pages/30-passwd-type.qml:103
478msgctxt "Label: Type of security method"
478msgctxt "Label: Type of security method"
479msgid "Passphrase"
479msgid "Passphrase"
480−msgstr ""
480+msgstr "Пароль"
481
481
482#: qml/Wizard/Pages/30-passwd-type.qml:104
482#: qml/Wizard/Pages/30-passwd-type.qml:104
483msgctxt "Label: Description of security method"
483msgctxt "Label: Description of security method"
484msgid "Numbers and letters"
484msgid "Numbers and letters"
485−msgstr ""
485+msgstr "Літары і лічбы"
486
486
487#: qml/Wizard/Pages/40-wifi.qml:29
487#: qml/Wizard/Pages/40-wifi.qml:29
488msgid "Connect to Wi‑Fi"
488msgid "Connect to Wi‑Fi"
po/br.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"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"
12"Last-Translator: Fohanno Thierry <thierry.fohanno@ofis-bzh.org>\n"
12"Last-Translator: Fohanno Thierry <thierry.fohanno@ofis-bzh.org>\n"
13"Language-Team: Breton <br@li.org>\n"
13"Language-Team: Breton <br@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
17"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-28 05:33+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
19+"X-Generator: Launchpad (build 17453)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -375,7 +375,7 @@ msgstr "Pouzañ amañ evit kenderc'hel"
375
375
376#: qml/Tutorial/TutorialContent.qml:81
376#: qml/Tutorial/TutorialContent.qml:81
377msgid "Click here to finish."
377msgid "Click here to finish."
378−msgstr ""
378+msgstr "Klikit amañ evit echuiñ."
379
379
380#: qml/Tutorial/TutorialLeftFinish.qml:24
380#: qml/Tutorial/TutorialLeftFinish.qml:24
381msgid "These are the shortcuts to favorite apps"
381msgid "These are the shortcuts to favorite apps"
po/ca.po+25−25
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"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"
12"Last-Translator: David Planella <david.planella@ubuntu.com>\n"
12"Last-Translator: David Planella <david.planella@ubuntu.com>\n"
13"Language-Team: Catalan <ca@li.org>\n"
13"Language-Team: Catalan <ca@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"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-05-03 06:07+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
19+"X-Generator: Launchpad (build 17474)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -57,7 +57,7 @@ msgstr[1] "Espereu %1 minuts i torneu-ho a intentar..."
57#: qml/Components/Dialogs.qml:69
57#: qml/Components/Dialogs.qml:69
58msgctxt "Title: Lock/Log out dialog"
58msgctxt "Title: Lock/Log out dialog"
59msgid "Log out"
59msgid "Log out"
60−msgstr ""
60+msgstr "Surt"
61
61
62#: qml/Components/Dialogs.qml:70
62#: qml/Components/Dialogs.qml:70
63msgid "Are you sure you want to log out?"
63msgid "Are you sure you want to log out?"
@@ -66,12 +66,12 @@ msgstr "Segur que voleu sortir?"
66#: qml/Components/Dialogs.qml:72
66#: qml/Components/Dialogs.qml:72
67msgctxt "Button: Lock the system"
67msgctxt "Button: Lock the system"
68msgid "Lock"
68msgid "Lock"
69−msgstr ""
69+msgstr "Bloca"
70
70
71#: qml/Components/Dialogs.qml:79
71#: qml/Components/Dialogs.qml:79
72msgctxt "Button: Log out from the system"
72msgctxt "Button: Log out from the system"
73msgid "Log Out"
73msgid "Log Out"
74−msgstr ""
74+msgstr "Surt"
75
75
76#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
76#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
77#: qml/Components/Dialogs.qml:173
77#: qml/Components/Dialogs.qml:173
@@ -81,7 +81,7 @@ msgstr "Cancel·la"
81#: qml/Components/Dialogs.qml:98
81#: qml/Components/Dialogs.qml:98
82msgctxt "Title: Reboot/Shut down dialog"
82msgctxt "Title: Reboot/Shut down dialog"
83msgid "Shut down"
83msgid "Shut down"
84−msgstr ""
84+msgstr "Apaga"
85
85
86#: qml/Components/Dialogs.qml:99
86#: qml/Components/Dialogs.qml:99
87msgid "Are you sure you want to shut down?"
87msgid "Are you sure you want to shut down?"
@@ -90,17 +90,17 @@ msgstr "Segur que voleu apagar?"
90#: qml/Components/Dialogs.qml:101
90#: qml/Components/Dialogs.qml:101
91msgctxt "Button: Reboot the system"
91msgctxt "Button: Reboot the system"
92msgid "Reboot"
92msgid "Reboot"
93−msgstr ""
93+msgstr "Reinicia"
94
94
95#: qml/Components/Dialogs.qml:109
95#: qml/Components/Dialogs.qml:109
96msgctxt "Button: Shut down the system"
96msgctxt "Button: Shut down the system"
97msgid "Shut down"
97msgid "Shut down"
98−msgstr ""
98+msgstr "Apaga"
99
99
100#: qml/Components/Dialogs.qml:129
100#: qml/Components/Dialogs.qml:129
101msgctxt "Title: Reboot dialog"
101msgctxt "Title: Reboot dialog"
102msgid "Reboot"
102msgid "Reboot"
103−msgstr ""
103+msgstr "Reinicia"
104
104
105#: qml/Components/Dialogs.qml:130
105#: qml/Components/Dialogs.qml:130
106msgid "Are you sure you want to reboot?"
106msgid "Are you sure you want to reboot?"
@@ -117,7 +117,7 @@ msgstr "Sí"
117#: qml/Components/Dialogs.qml:152
117#: qml/Components/Dialogs.qml:152
118msgctxt "Title: Power off/Restart dialog"
118msgctxt "Title: Power off/Restart dialog"
119msgid "Power"
119msgid "Power"
120−msgstr ""
120+msgstr "Energia"
121
121
122#: qml/Components/Dialogs.qml:153
122#: qml/Components/Dialogs.qml:153
123msgid ""
123msgid ""
@@ -130,12 +130,12 @@ msgstr ""
130#: qml/Components/Dialogs.qml:155
130#: qml/Components/Dialogs.qml:155
131msgctxt "Button: Power off the system"
131msgctxt "Button: Power off the system"
132msgid "Power off"
132msgid "Power off"
133−msgstr ""
133+msgstr "Apaga"
134
134
135#: qml/Components/Dialogs.qml:164
135#: qml/Components/Dialogs.qml:164
136msgctxt "Button: Restart the system"
136msgctxt "Button: Restart the system"
137msgid "Restart"
137msgid "Restart"
138−msgstr ""
138+msgstr "Reinicia"
139
139
140#: qml/Components/Lockscreen.qml:245
140#: qml/Components/Lockscreen.qml:245
141msgid "Return to Call"
141msgid "Return to Call"
@@ -160,22 +160,22 @@ msgstr "Mostra-ho tot"
160#: qml/Dash/GenericScopeView.qml:594
160#: qml/Dash/GenericScopeView.qml:594
161msgctxt "Label: Hint for dash search line edit"
161msgctxt "Label: Hint for dash search line edit"
162msgid "Search"
162msgid "Search"
163−msgstr ""
163+msgstr "Cerca"
164
164
165#: qml/Dash/PageHeader.qml:269
165#: qml/Dash/PageHeader.qml:269
166msgctxt "Button: Open the Ubuntu Store"
166msgctxt "Button: Open the Ubuntu Store"
167msgid "Store"
167msgid "Store"
168−msgstr ""
168+msgstr "Botiga"
169
169
170#: qml/Dash/PageHeader.qml:276
170#: qml/Dash/PageHeader.qml:276
171msgctxt "Button: Start a search in the current dash scope"
171msgctxt "Button: Start a search in the current dash scope"
172msgid "Search"
172msgid "Search"
173−msgstr ""
173+msgstr "Cerca"
174
174
175#: qml/Dash/PageHeader.qml:286
175#: qml/Dash/PageHeader.qml:286
176msgctxt "Button: Show the current dash scope settings"
176msgctxt "Button: Show the current dash scope settings"
177msgid "Settings"
177msgid "Settings"
178−msgstr ""
178+msgstr "Paràmetres"
179
179
180#: qml/Dash/PageHeader.qml:293
180#: qml/Dash/PageHeader.qml:293
181msgid "Remove from Favorites"
181msgid "Remove from Favorites"
@@ -344,7 +344,7 @@ msgstr "Ha fallat, feu un toc per tornar-ho a intentar"
344#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
344#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
345msgctxt "Label: Hint in message indicator line edit"
345msgctxt "Label: Hint in message indicator line edit"
346msgid "Reply"
346msgid "Reply"
347−msgstr ""
347+msgstr "Respon"
348
348
349#: qml/Panel/Indicators/ModemInfoItem.qml:105
349#: qml/Panel/Indicators/ModemInfoItem.qml:105
350msgid "Unlock SIM"
350msgid "Unlock SIM"
@@ -377,7 +377,7 @@ msgstr "Feu un toc per continuar."
377
377
378#: qml/Tutorial/TutorialContent.qml:81
378#: qml/Tutorial/TutorialContent.qml:81
379msgid "Click here to finish."
379msgid "Click here to finish."
380−msgstr ""
380+msgstr "Feu un toc aquí per finalitzar"
381
381
382#: qml/Tutorial/TutorialLeftFinish.qml:24
382#: qml/Tutorial/TutorialLeftFinish.qml:24
383msgid "These are the shortcuts to favorite apps"
383msgid "These are the shortcuts to favorite apps"
@@ -414,7 +414,7 @@ msgstr "Mostra totes les tasques en execució."
414#: qml/Wizard/Page.qml:89
414#: qml/Wizard/Page.qml:89
415msgctxt "Button: Go back one page in the Wizard"
415msgctxt "Button: Go back one page in the Wizard"
416msgid "Back"
416msgid "Back"
417−msgstr ""
417+msgstr "Enrere"
418
418
419#: qml/Wizard/Pages/10-welcome.qml:27
419#: qml/Wizard/Pages/10-welcome.qml:27
420msgid "Hi!"
420msgid "Hi!"
@@ -458,17 +458,17 @@ msgstr "Seleccioneu com voleu desblocar el telèfon."
458#: qml/Wizard/Pages/30-passwd-type.qml:97
458#: qml/Wizard/Pages/30-passwd-type.qml:97
459msgctxt "Label: Type of security method"
459msgctxt "Label: Type of security method"
460msgid "Swipe"
460msgid "Swipe"
461−msgstr ""
461+msgstr "Lliscament amb el dit"
462
462
463#: qml/Wizard/Pages/30-passwd-type.qml:98
463#: qml/Wizard/Pages/30-passwd-type.qml:98
464msgctxt "Label: Description of security method"
464msgctxt "Label: Description of security method"
465msgid "No security"
465msgid "No security"
466−msgstr ""
466+msgstr "Sense seguretat"
467
467
468#: qml/Wizard/Pages/30-passwd-type.qml:100
468#: qml/Wizard/Pages/30-passwd-type.qml:100
469msgctxt "Label: Type of security method"
469msgctxt "Label: Type of security method"
470msgid "Passcode"
470msgid "Passcode"
471−msgstr ""
471+msgstr "Codi d'accés"
472
472
473#: qml/Wizard/Pages/30-passwd-type.qml:101
473#: qml/Wizard/Pages/30-passwd-type.qml:101
474msgctxt "Label: Description of security method"
474msgctxt "Label: Description of security method"
@@ -478,12 +478,12 @@ msgstr "Només 4 dígits"
478#: qml/Wizard/Pages/30-passwd-type.qml:103
478#: qml/Wizard/Pages/30-passwd-type.qml:103
479msgctxt "Label: Type of security method"
479msgctxt "Label: Type of security method"
480msgid "Passphrase"
480msgid "Passphrase"
481−msgstr ""
481+msgstr "Contrasenya"
482
482
483#: qml/Wizard/Pages/30-passwd-type.qml:104
483#: qml/Wizard/Pages/30-passwd-type.qml:104
484msgctxt "Label: Description of security method"
484msgctxt "Label: Description of security method"
485msgid "Numbers and letters"
485msgid "Numbers and letters"
486−msgstr ""
486+msgstr "Números i lletres"
487
487
488#: qml/Wizard/Pages/40-wifi.qml:29
488#: qml/Wizard/Pages/40-wifi.qml:29
489msgid "Connect to Wi‑Fi"
489msgid "Connect to Wi‑Fi"
po/cs.po+136−119
@@ -8,132 +8,138 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2014-01-08 10:11+0000\n"
11+"PO-Revision-Date: 2015-05-11 20:42+0000\n"
12"Last-Translator: Tadeáš Pařík <tadeas.parik@gmail.com>\n"
12"Last-Translator: Tadeáš Pařík <tadeas.parik@gmail.com>\n"
13"Language-Team: Czech <cs@li.org>\n"
13"Language-Team: Czech <cs@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
17"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
18+"X-Launchpad-Export-Date: 2015-05-13 05:31+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
19+"X-Generator: Launchpad (build 17493)\n"
20+"Language: cs\n"
20
21
21#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
23msgid "Password: "
23−msgstr ""
24+msgstr "Heslo: "
24
25
25#: plugins/Unity/Launcher/launcheritem.cpp:45
26#: plugins/Unity/Launcher/launcheritem.cpp:45
26#: plugins/Unity/Launcher/launcheritem.cpp:100
27#: plugins/Unity/Launcher/launcheritem.cpp:100
27msgid "Pin shortcut"
28msgid "Pin shortcut"
28−msgstr ""
29+msgstr "Připíchnout zkratku"
29
30
30#: plugins/Unity/Launcher/launcheritem.cpp:100
31#: plugins/Unity/Launcher/launcheritem.cpp:100
31msgid "Unpin shortcut"
32msgid "Unpin shortcut"
32−msgstr ""
33+msgstr "Odpíchnout zkratku"
33
34
34#: qml/Components/DelayedLockscreen.qml:49
35#: qml/Components/DelayedLockscreen.qml:49
35msgid "Device Locked"
36msgid "Device Locked"
36−msgstr ""
37+msgstr "Zařízení uzamčeno"
37
38
38#: qml/Components/DelayedLockscreen.qml:64
39#: qml/Components/DelayedLockscreen.qml:64
39msgid "You have been locked out due to too many failed passphrase attempts."
40msgid "You have been locked out due to too many failed passphrase attempts."
40msgstr ""
41msgstr ""
42+"Zařízení bylo uzamčeno z důvodu mnoha neúspěšných pokusů o přihlášení."
41
43
42#: qml/Components/DelayedLockscreen.qml:65
44#: qml/Components/DelayedLockscreen.qml:65
43msgid "You have been locked out due to too many failed passcode attempts."
45msgid "You have been locked out due to too many failed passcode attempts."
44msgstr ""
46msgstr ""
47+"Zařízení bylo uzamčeno z důvodu mnoha neúspěšných pokusů o přihlášení."
45
48
46#: qml/Components/DelayedLockscreen.qml:74
49#: qml/Components/DelayedLockscreen.qml:74
47#, qt-format
50#, qt-format
48msgid "Please wait %1 minute and then try again…"
51msgid "Please wait %1 minute and then try again…"
49msgid_plural "Please wait %1 minutes and then try again…"
52msgid_plural "Please wait %1 minutes and then try again…"
50−msgstr[0] ""
53+msgstr[0] "Počkejte, prosím, %1 minut a poté to zkuste znovu..."
51−msgstr[1] ""
54+msgstr[1] "Počkejte, prosím, %1 minuty a poté to zkuste znovu..."
55+msgstr[2] "Počkejte, prosím, %1 minut a poté to zkuste znovu..."
52
56
53#: qml/Components/Dialogs.qml:69
57#: qml/Components/Dialogs.qml:69
54msgctxt "Title: Lock/Log out dialog"
58msgctxt "Title: Lock/Log out dialog"
55msgid "Log out"
59msgid "Log out"
56−msgstr ""
60+msgstr "Odhlásit se"
57
61
58#: qml/Components/Dialogs.qml:70
62#: qml/Components/Dialogs.qml:70
59msgid "Are you sure you want to log out?"
63msgid "Are you sure you want to log out?"
60−msgstr ""
64+msgstr "Jste si jistí, že se chcete odhlásit?"
61
65
62#: qml/Components/Dialogs.qml:72
66#: qml/Components/Dialogs.qml:72
63msgctxt "Button: Lock the system"
67msgctxt "Button: Lock the system"
64msgid "Lock"
68msgid "Lock"
65−msgstr ""
69+msgstr "Uzamknout"
66
70
67#: qml/Components/Dialogs.qml:79
71#: qml/Components/Dialogs.qml:79
68msgctxt "Button: Log out from the system"
72msgctxt "Button: Log out from the system"
69msgid "Log Out"
73msgid "Log Out"
70−msgstr ""
74+msgstr "Odhlásit"
71
75
72#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
76#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
73#: qml/Components/Dialogs.qml:173
77#: qml/Components/Dialogs.qml:173
74msgid "Cancel"
78msgid "Cancel"
75−msgstr ""
79+msgstr "Zrušit"
76
80
77#: qml/Components/Dialogs.qml:98
81#: qml/Components/Dialogs.qml:98
78msgctxt "Title: Reboot/Shut down dialog"
82msgctxt "Title: Reboot/Shut down dialog"
79msgid "Shut down"
83msgid "Shut down"
80−msgstr ""
84+msgstr "Vypnout"
81
85
82#: qml/Components/Dialogs.qml:99
86#: qml/Components/Dialogs.qml:99
83msgid "Are you sure you want to shut down?"
87msgid "Are you sure you want to shut down?"
84−msgstr ""
88+msgstr "Jste si jistí, že chcete zařízení vypnout?"
85
89
86#: qml/Components/Dialogs.qml:101
90#: qml/Components/Dialogs.qml:101
87msgctxt "Button: Reboot the system"
91msgctxt "Button: Reboot the system"
88msgid "Reboot"
92msgid "Reboot"
89−msgstr ""
93+msgstr "Restartovat"
90
94
91#: qml/Components/Dialogs.qml:109
95#: qml/Components/Dialogs.qml:109
92msgctxt "Button: Shut down the system"
96msgctxt "Button: Shut down the system"
93msgid "Shut down"
97msgid "Shut down"
94−msgstr ""
98+msgstr "Vypnout"
95
99
96#: qml/Components/Dialogs.qml:129
100#: qml/Components/Dialogs.qml:129
97msgctxt "Title: Reboot dialog"
101msgctxt "Title: Reboot dialog"
98msgid "Reboot"
102msgid "Reboot"
99−msgstr ""
103+msgstr "Restartovat"
100
104
101#: qml/Components/Dialogs.qml:130
105#: qml/Components/Dialogs.qml:130
102msgid "Are you sure you want to reboot?"
106msgid "Are you sure you want to reboot?"
103−msgstr ""
107+msgstr "Jste si jistí, že chcete zařízení restartovat?"
104
108
105#: qml/Components/Dialogs.qml:132
109#: qml/Components/Dialogs.qml:132
106msgid "No"
110msgid "No"
107−msgstr ""
111+msgstr "Ne"
108
112
109#: qml/Components/Dialogs.qml:138
113#: qml/Components/Dialogs.qml:138
110msgid "Yes"
114msgid "Yes"
111−msgstr ""
115+msgstr "Ano"
112
116
113#: qml/Components/Dialogs.qml:152
117#: qml/Components/Dialogs.qml:152
114msgctxt "Title: Power off/Restart dialog"
118msgctxt "Title: Power off/Restart dialog"
115msgid "Power"
119msgid "Power"
116−msgstr ""
120+msgstr "Vypnutí"
117
121
118#: qml/Components/Dialogs.qml:153
122#: qml/Components/Dialogs.qml:153
119msgid ""
123msgid ""
120"Are you sure you would like\n"
124"Are you sure you would like\n"
121"to power off?"
125"to power off?"
122msgstr ""
126msgstr ""
127+"Jste si jistí, že chcete\n"
128+"telefon vypnout?"
123
129
124#: qml/Components/Dialogs.qml:155
130#: qml/Components/Dialogs.qml:155
125msgctxt "Button: Power off the system"
131msgctxt "Button: Power off the system"
126msgid "Power off"
132msgid "Power off"
127−msgstr ""
133+msgstr "Vypnutí"
128
134
129#: qml/Components/Dialogs.qml:164
135#: qml/Components/Dialogs.qml:164
130msgctxt "Button: Restart the system"
136msgctxt "Button: Restart the system"
131msgid "Restart"
137msgid "Restart"
132−msgstr ""
138+msgstr "Reboot"
133
139
134#: qml/Components/Lockscreen.qml:245
140#: qml/Components/Lockscreen.qml:245
135msgid "Return to Call"
141msgid "Return to Call"
136−msgstr ""
142+msgstr "Vrátit se k hovoru"
137
143
138#: qml/Components/Lockscreen.qml:245
144#: qml/Components/Lockscreen.qml:245
139msgid "Emergency Call"
145msgid "Emergency Call"
@@ -141,51 +147,51 @@ msgstr "Tísňové volání"
141
147
142#: qml/Components/Lockscreen.qml:277
148#: qml/Components/Lockscreen.qml:277
143msgid "OK"
149msgid "OK"
144−msgstr ""
150+msgstr "OK"
145
151
146#: qml/Dash/GenericScopeView.qml:532 qml/Dash/GenericScopeView.qml:690
152#: qml/Dash/GenericScopeView.qml:532 qml/Dash/GenericScopeView.qml:690
147msgid "See less"
153msgid "See less"
148−msgstr ""
154+msgstr "Zobrazit méně"
149
155
150#: qml/Dash/GenericScopeView.qml:532
156#: qml/Dash/GenericScopeView.qml:532
151msgid "See all"
157msgid "See all"
152−msgstr ""
158+msgstr "Zobrazit vše"
153
159
154#: qml/Dash/GenericScopeView.qml:594
160#: qml/Dash/GenericScopeView.qml:594
155msgctxt "Label: Hint for dash search line edit"
161msgctxt "Label: Hint for dash search line edit"
156msgid "Search"
162msgid "Search"
157−msgstr ""
163+msgstr "Hledat"
158
164
159#: qml/Dash/PageHeader.qml:269
165#: qml/Dash/PageHeader.qml:269
160msgctxt "Button: Open the Ubuntu Store"
166msgctxt "Button: Open the Ubuntu Store"
161msgid "Store"
167msgid "Store"
162−msgstr ""
168+msgstr "Obchod"
163
169
164#: qml/Dash/PageHeader.qml:276
170#: qml/Dash/PageHeader.qml:276
165msgctxt "Button: Start a search in the current dash scope"
171msgctxt "Button: Start a search in the current dash scope"
166msgid "Search"
172msgid "Search"
167−msgstr ""
173+msgstr "Hledat"
168
174
169#: qml/Dash/PageHeader.qml:286
175#: qml/Dash/PageHeader.qml:286
170msgctxt "Button: Show the current dash scope settings"
176msgctxt "Button: Show the current dash scope settings"
171msgid "Settings"
177msgid "Settings"
172−msgstr ""
178+msgstr "Nastavení"
173
179
174#: qml/Dash/PageHeader.qml:293
180#: qml/Dash/PageHeader.qml:293
175msgid "Remove from Favorites"
181msgid "Remove from Favorites"
176−msgstr ""
182+msgstr "Odstranit z oblíbených"
177
183
178#: qml/Dash/PageHeader.qml:293
184#: qml/Dash/PageHeader.qml:293
179msgid "Add to Favorites"
185msgid "Add to Favorites"
180−msgstr ""
186+msgstr "Přidat do oblíbených"
181
187
182#: qml/Dash/Previews/PreviewActionCombo.qml:35
188#: qml/Dash/Previews/PreviewActionCombo.qml:35
183msgid "More..."
189msgid "More..."
184−msgstr ""
190+msgstr "Více..."
185
191
186#: qml/Dash/Previews/PreviewActionCombo.qml:35
192#: qml/Dash/Previews/PreviewActionCombo.qml:35
187msgid "Less..."
193msgid "Less..."
188−msgstr ""
194+msgstr "Méně..."
189
195
190#: qml/Dash/Previews/PreviewRatingInput.qml:81
196#: qml/Dash/Previews/PreviewRatingInput.qml:81
191msgid "Rate this"
197msgid "Rate this"
@@ -201,93 +207,97 @@ msgstr "Odeslat"
201
207
202#: qml/Dash/PullToRefreshScopeStyle.qml:55
208#: qml/Dash/PullToRefreshScopeStyle.qml:55
203msgid "Pull to refresh…"
209msgid "Pull to refresh…"
204−msgstr ""
210+msgstr "Vytáhněte pro obnovení..."
205
211
206#: qml/Dash/PullToRefreshScopeStyle.qml:60
212#: qml/Dash/PullToRefreshScopeStyle.qml:60
207msgid "Release to refresh…"
213msgid "Release to refresh…"
208−msgstr ""
214+msgstr "Pusťte pro obnovení..."
209
215
210#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
216#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
211msgid "Enable location data"
217msgid "Enable location data"
212−msgstr ""
218+msgstr "Povolit údaje umístění"
213
219
214#: qml/Dash/ScopesList.qml:66
220#: qml/Dash/ScopesList.qml:66
215msgid "Manage"
221msgid "Manage"
216−msgstr ""
222+msgstr "Spravovat"
217
223
218#: qml/Dash/ScopesList.qml:112
224#: qml/Dash/ScopesList.qml:112
219msgid "Home"
225msgid "Home"
220−msgstr ""
226+msgstr "Domů"
221
227
222#: qml/Dash/ScopesList.qml:113
228#: qml/Dash/ScopesList.qml:113
223msgid "Also installed"
229msgid "Also installed"
224−msgstr ""
230+msgstr "Také nainstalováno"
225
231
226#: qml/Greeter/CoverPage.qml:107
232#: qml/Greeter/CoverPage.qml:107
227msgid "Unlock"
233msgid "Unlock"
228−msgstr ""
234+msgstr "Odemknout"
229
235
230#: qml/Greeter/LoginList.qml:236
236#: qml/Greeter/LoginList.qml:236
231msgid "Retry"
237msgid "Retry"
232−msgstr ""
238+msgstr "Znovu"
233
239
234#: qml/Greeter/LoginList.qml:237
240#: qml/Greeter/LoginList.qml:237
235msgid "Tap to unlock"
241msgid "Tap to unlock"
236−msgstr ""
242+msgstr "Klepnout pro odemčení"
237
243
238#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
244#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
239msgid "Sorry, incorrect passphrase."
245msgid "Sorry, incorrect passphrase."
240−msgstr ""
246+msgstr "Zadali jste špatné heslo."
241
247
242#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
248#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
243msgid "Sorry, incorrect passcode."
249msgid "Sorry, incorrect passcode."
244−msgstr ""
250+msgstr "Zadali jste špatné heslo."
245
251
246#: qml/Greeter/NarrowView.qml:55
252#: qml/Greeter/NarrowView.qml:55
247msgid "This will be your last attempt."
253msgid "This will be your last attempt."
248−msgstr ""
254+msgstr "Toto je váš poslední pokus."
249
255
250#: qml/Greeter/NarrowView.qml:57
256#: qml/Greeter/NarrowView.qml:57
251msgid ""
257msgid ""
252"If passphrase is entered incorrectly, your phone will conduct a factory "
258"If passphrase is entered incorrectly, your phone will conduct a factory "
253"reset and all personal data will be deleted."
259"reset and all personal data will be deleted."
254msgstr ""
260msgstr ""
261+"Pokud bude heslo zadáno nesprávně, bude telefon obnoven do továrního "
262+"nastavení a všechna osobní data budou smazána."
255
263
256#: qml/Greeter/NarrowView.qml:58
264#: qml/Greeter/NarrowView.qml:58
257msgid ""
265msgid ""
258"If passcode is entered incorrectly, your phone will conduct a factory reset "
266"If passcode is entered incorrectly, your phone will conduct a factory reset "
259"and all personal data will be deleted."
267"and all personal data will be deleted."
260msgstr ""
268msgstr ""
269+"Pokud bude heslo zadáno nesprávně, bude telefon obnoven do továrního "
270+"nastavení a všechna osobní data budou smazána."
261
271
262#: qml/Greeter/NarrowView.qml:115
272#: qml/Greeter/NarrowView.qml:115
263#, qt-format
273#, qt-format
264msgid "Enter %1"
274msgid "Enter %1"
265−msgstr ""
275+msgstr "Vložte %1"
266
276
267#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
277#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
268msgid "Enter passphrase"
278msgid "Enter passphrase"
269−msgstr ""
279+msgstr "Vložte heslo"
270
280
271#: qml/Greeter/NarrowView.qml:117
281#: qml/Greeter/NarrowView.qml:117
272msgid "Enter passcode"
282msgid "Enter passcode"
273−msgstr ""
283+msgstr "Vložte heslo"
274
284
275#: qml/Greeter/NarrowView.qml:118
285#: qml/Greeter/NarrowView.qml:118
276#, qt-format
286#, qt-format
277msgid "Sorry, incorrect %1"
287msgid "Sorry, incorrect %1"
278−msgstr ""
288+msgstr "Špatné %1"
279
289
280#: qml/Greeter/NarrowView.qml:119
290#: qml/Greeter/NarrowView.qml:119
281msgid "Sorry, incorrect passphrase"
291msgid "Sorry, incorrect passphrase"
282−msgstr ""
292+msgstr "Špatné heslo"
283
293
284#: qml/Greeter/NarrowView.qml:120
294#: qml/Greeter/NarrowView.qml:120
285msgid "Please re-enter"
295msgid "Please re-enter"
286−msgstr ""
296+msgstr "Vložte znovu"
287
297
288#: qml/Greeter/NarrowView.qml:121
298#: qml/Greeter/NarrowView.qml:121
289msgid "Sorry, incorrect passcode"
299msgid "Sorry, incorrect passcode"
290−msgstr ""
300+msgstr "Špatné heslo"
291
301
292#: qml/Notifications/NotificationMenuItemFactory.qml:128
302#: qml/Notifications/NotificationMenuItemFactory.qml:128
293msgid "Show password"
303msgid "Show password"
@@ -295,211 +305,211 @@ msgstr "Zobrazit heslo"
295
305
296#: qml/Panel/ActiveCallHint.qml:79
306#: qml/Panel/ActiveCallHint.qml:79
297msgid "Tap to return to call..."
307msgid "Tap to return to call..."
298−msgstr ""
308+msgstr "Klepnutím se vrátíte k hovoru..."
299
309
300#: qml/Panel/ActiveCallHint.qml:92
310#: qml/Panel/ActiveCallHint.qml:92
301msgid "Conference"
311msgid "Conference"
302−msgstr ""
312+msgstr "Konferenční hovor"
303
313
304#: qml/Panel/Indicators/MenuItemFactory.qml:639
314#: qml/Panel/Indicators/MenuItemFactory.qml:639
305msgid "Nothing is playing"
315msgid "Nothing is playing"
306−msgstr ""
316+msgstr "Nic se nepřehrává"
307
317
308#: qml/Panel/Indicators/MenuItemFactory.qml:787
318#: qml/Panel/Indicators/MenuItemFactory.qml:787
309msgid "In queue…"
319msgid "In queue…"
310−msgstr ""
320+msgstr "Ve frontě..."
311
321
312#: qml/Panel/Indicators/MenuItemFactory.qml:791
322#: qml/Panel/Indicators/MenuItemFactory.qml:791
313msgid "Downloading"
323msgid "Downloading"
314−msgstr ""
324+msgstr "Stahování"
315
325
316#: qml/Panel/Indicators/MenuItemFactory.qml:793
326#: qml/Panel/Indicators/MenuItemFactory.qml:793
317msgid "Paused, tap to resume"
327msgid "Paused, tap to resume"
318−msgstr ""
328+msgstr "Pauza, pro pokračování klepněte"
319
329
320#: qml/Panel/Indicators/MenuItemFactory.qml:795
330#: qml/Panel/Indicators/MenuItemFactory.qml:795
321msgid "Canceled"
331msgid "Canceled"
322−msgstr ""
332+msgstr "Zrušeno"
323
333
324#: qml/Panel/Indicators/MenuItemFactory.qml:797
334#: qml/Panel/Indicators/MenuItemFactory.qml:797
325msgid "Finished"
335msgid "Finished"
326−msgstr ""
336+msgstr "Dokončeno"
327
337
328#: qml/Panel/Indicators/MenuItemFactory.qml:799
338#: qml/Panel/Indicators/MenuItemFactory.qml:799
329msgid "Failed, tap to retry"
339msgid "Failed, tap to retry"
330−msgstr ""
340+msgstr "Nezdařilo se, opakujte klepnutím"
331
341
332#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
342#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
333msgctxt "Label: Hint in message indicator line edit"
343msgctxt "Label: Hint in message indicator line edit"
334msgid "Reply"
344msgid "Reply"
335−msgstr ""
345+msgstr "Odpovědět"
336
346
337#: qml/Panel/Indicators/ModemInfoItem.qml:105
347#: qml/Panel/Indicators/ModemInfoItem.qml:105
338msgid "Unlock SIM"
348msgid "Unlock SIM"
339−msgstr ""
349+msgstr "Odemknout SIM"
340
350
341#: qml/Panel/Indicators/RoamingIndication.qml:27
351#: qml/Panel/Indicators/RoamingIndication.qml:27
342msgid "Roaming"
352msgid "Roaming"
343−msgstr ""
353+msgstr "Roaming"
344
354
345#: qml/Tutorial/TutorialBottomFinish.qml:24
355#: qml/Tutorial/TutorialBottomFinish.qml:24
346msgid "This action does different things for different apps"
356msgid "This action does different things for different apps"
347−msgstr ""
357+msgstr "Tato akce vyvolá různé reakce v rozdílných aplikacích"
348
358
349#: qml/Tutorial/TutorialBottomFinish.qml:25
359#: qml/Tutorial/TutorialBottomFinish.qml:25
350msgid "Tap here to finish."
360msgid "Tap here to finish."
351−msgstr ""
361+msgstr "Klepněte zde pro dokončení."
352
362
353#: qml/Tutorial/TutorialBottom.qml:28
363#: qml/Tutorial/TutorialBottom.qml:28
354msgid "Open special menus"
364msgid "Open special menus"
355−msgstr ""
365+msgstr "Otevřít speciální menu"
356
366
357#: qml/Tutorial/TutorialBottom.qml:29
367#: qml/Tutorial/TutorialBottom.qml:29
358msgid "Swipe up from the bottom edge."
368msgid "Swipe up from the bottom edge."
359−msgstr ""
369+msgstr "Potáhněte prstem z dola nahoru."
360
370
361#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
371#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
362#: qml/Tutorial/TutorialRight.qml:144
372#: qml/Tutorial/TutorialRight.qml:144
363msgid "Tap here to continue."
373msgid "Tap here to continue."
364−msgstr ""
374+msgstr "Klepněte zde pro pokračování."
365
375
366#: qml/Tutorial/TutorialContent.qml:81
376#: qml/Tutorial/TutorialContent.qml:81
367msgid "Click here to finish."
377msgid "Click here to finish."
368−msgstr ""
378+msgstr "Klikněte zde pro dokončení."
369
379
370#: qml/Tutorial/TutorialLeftFinish.qml:24
380#: qml/Tutorial/TutorialLeftFinish.qml:24
371msgid "These are the shortcuts to favorite apps"
381msgid "These are the shortcuts to favorite apps"
372−msgstr ""
382+msgstr "Zde jsou odkazy na vaše oblíbené aplikace"
373
383
374#: qml/Tutorial/TutorialLeft.qml:26
384#: qml/Tutorial/TutorialLeft.qml:26
375msgid "Open the launcher"
385msgid "Open the launcher"
376−msgstr ""
386+msgstr "Otevřete launcher"
377
387
378#: qml/Tutorial/TutorialLeft.qml:27
388#: qml/Tutorial/TutorialLeft.qml:27
379msgid "Short swipe from the left edge."
389msgid "Short swipe from the left edge."
380−msgstr ""
390+msgstr "Potáhněte krátce z levého okraje."
381
391
382#: qml/Tutorial/TutorialPage.qml:178
392#: qml/Tutorial/TutorialPage.qml:178
383msgid "You almost got it!"
393msgid "You almost got it!"
384−msgstr ""
394+msgstr "Už to skoro máte!"
385
395
386#: qml/Tutorial/TutorialPage.qml:195
396#: qml/Tutorial/TutorialPage.qml:195
387msgid "Try again."
397msgid "Try again."
388−msgstr ""
398+msgstr "Zkuste to znovu."
389
399
390#: qml/Tutorial/TutorialRight.qml:31
400#: qml/Tutorial/TutorialRight.qml:31
391msgid "To view open apps"
401msgid "To view open apps"
392−msgstr ""
402+msgstr "K zobrazení otevřených aplikací"
393
403
394#: qml/Tutorial/TutorialRight.qml:32
404#: qml/Tutorial/TutorialRight.qml:32
395msgid "Long swipe from the right edge."
405msgid "Long swipe from the right edge."
396−msgstr ""
406+msgstr "Potáhněte dlouho z pravého okraje."
397
407
398#: qml/Tutorial/TutorialRight.qml:122
408#: qml/Tutorial/TutorialRight.qml:122
399msgid "View all your running tasks."
409msgid "View all your running tasks."
400−msgstr ""
410+msgstr "Zobrazíte všechny běžící procesy."
401
411
402#: qml/Wizard/Page.qml:89
412#: qml/Wizard/Page.qml:89
403msgctxt "Button: Go back one page in the Wizard"
413msgctxt "Button: Go back one page in the Wizard"
404msgid "Back"
414msgid "Back"
405−msgstr ""
415+msgstr "Zpět"
406
416
407#: qml/Wizard/Pages/10-welcome.qml:27
417#: qml/Wizard/Pages/10-welcome.qml:27
408msgid "Hi!"
418msgid "Hi!"
409−msgstr ""
419+msgstr "Ahoj!"
410
420
411#: qml/Wizard/Pages/10-welcome.qml:44
421#: qml/Wizard/Pages/10-welcome.qml:44
412msgid "Welcome to your Ubuntu phone."
422msgid "Welcome to your Ubuntu phone."
413−msgstr ""
423+msgstr "Vítejte ve vašem Ubuntu telefonu."
414
424
415#: qml/Wizard/Pages/10-welcome.qml:52
425#: qml/Wizard/Pages/10-welcome.qml:52
416msgid "Let’s get started."
426msgid "Let’s get started."
417−msgstr ""
427+msgstr "Začněme."
418
428
419#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
429#: 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
430#: 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
431#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
422#: qml/Wizard/Pages/passwd-set.qml:90
432#: qml/Wizard/Pages/passwd-set.qml:90
423msgid "Continue"
433msgid "Continue"
424−msgstr ""
434+msgstr "Pokračovat"
425
435
426#: qml/Wizard/Pages/20-sim.qml:25
436#: qml/Wizard/Pages/20-sim.qml:25
427msgid "Add a SIM card and restart your device"
437msgid "Add a SIM card and restart your device"
428−msgstr ""
438+msgstr "Vložte SIM kartu a restartujte své zařízení"
429
439
430#: qml/Wizard/Pages/20-sim.qml:59
440#: qml/Wizard/Pages/20-sim.qml:59
431msgid "Without it, you won’t be able to make calls or use text messaging."
441msgid "Without it, you won’t be able to make calls or use text messaging."
432−msgstr ""
442+msgstr "Bez ni nebudete moci telefonovat nebo posílat textové zprávy."
433
443
434#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
444#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
435msgid "Skip"
445msgid "Skip"
436−msgstr ""
446+msgstr "Přeskočit"
437
447
438#: qml/Wizard/Pages/30-passwd-type.qml:39
448#: qml/Wizard/Pages/30-passwd-type.qml:39
439msgid "Lock security"
449msgid "Lock security"
440−msgstr ""
450+msgstr "Bezpečnostní uzamčení"
441
451
442#: qml/Wizard/Pages/30-passwd-type.qml:74
452#: qml/Wizard/Pages/30-passwd-type.qml:74
443msgid "Please select how you’d like to unlock your phone."
453msgid "Please select how you’d like to unlock your phone."
444−msgstr ""
454+msgstr "Zvolte, prosím, jak chcete svůj telefon odemykat."
445
455
446#: qml/Wizard/Pages/30-passwd-type.qml:97
456#: qml/Wizard/Pages/30-passwd-type.qml:97
447msgctxt "Label: Type of security method"
457msgctxt "Label: Type of security method"
448msgid "Swipe"
458msgid "Swipe"
449−msgstr ""
459+msgstr "Potažením prstu"
450
460
451#: qml/Wizard/Pages/30-passwd-type.qml:98
461#: qml/Wizard/Pages/30-passwd-type.qml:98
452msgctxt "Label: Description of security method"
462msgctxt "Label: Description of security method"
453msgid "No security"
463msgid "No security"
454−msgstr ""
464+msgstr "Bez zabezpečení"
455
465
456#: qml/Wizard/Pages/30-passwd-type.qml:100
466#: qml/Wizard/Pages/30-passwd-type.qml:100
457msgctxt "Label: Type of security method"
467msgctxt "Label: Type of security method"
458msgid "Passcode"
468msgid "Passcode"
459−msgstr ""
469+msgstr "Číselné heslo"
460
470
461#: qml/Wizard/Pages/30-passwd-type.qml:101
471#: qml/Wizard/Pages/30-passwd-type.qml:101
462msgctxt "Label: Description of security method"
472msgctxt "Label: Description of security method"
463msgid "4 digits only"
473msgid "4 digits only"
464−msgstr ""
474+msgstr "Pouze 4 čísla"
465
475
466#: qml/Wizard/Pages/30-passwd-type.qml:103
476#: qml/Wizard/Pages/30-passwd-type.qml:103
467msgctxt "Label: Type of security method"
477msgctxt "Label: Type of security method"
468msgid "Passphrase"
478msgid "Passphrase"
469−msgstr ""
479+msgstr "Heslo"
470
480
471#: qml/Wizard/Pages/30-passwd-type.qml:104
481#: qml/Wizard/Pages/30-passwd-type.qml:104
472msgctxt "Label: Description of security method"
482msgctxt "Label: Description of security method"
473msgid "Numbers and letters"
483msgid "Numbers and letters"
474−msgstr ""
484+msgstr "Čísla a písmena"
475
485
476#: qml/Wizard/Pages/40-wifi.qml:29
486#: qml/Wizard/Pages/40-wifi.qml:29
477msgid "Connect to Wi‑Fi"
487msgid "Connect to Wi‑Fi"
478−msgstr ""
488+msgstr "Připojte se k Wi-Fi"
479
489
480#: qml/Wizard/Pages/40-wifi.qml:168
490#: qml/Wizard/Pages/40-wifi.qml:168
481msgid "Available networks…"
491msgid "Available networks…"
482−msgstr ""
492+msgstr "Dostupné sítě..."
483
493
484#: qml/Wizard/Pages/40-wifi.qml:169
494#: qml/Wizard/Pages/40-wifi.qml:169
485msgid "No available networks."
495msgid "No available networks."
486−msgstr ""
496+msgstr "Žádná dostupná síť."
487
497
488#: qml/Wizard/Pages/50-location.qml:27
498#: qml/Wizard/Pages/50-location.qml:27
489msgid "Location"
499msgid "Location"
490−msgstr ""
500+msgstr "Umístění"
491
501
492#: qml/Wizard/Pages/50-location.qml:62
502#: qml/Wizard/Pages/50-location.qml:62
493msgid "Let the phone detect your location:"
503msgid "Let the phone detect your location:"
494−msgstr ""
504+msgstr "Povolte telefonu zjistit, kde se zrovna nacházíte:"
495
505
496#: qml/Wizard/Pages/50-location.qml:69
506#: qml/Wizard/Pages/50-location.qml:69
497msgid "Using GPS only (less accurate)"
507msgid "Using GPS only (less accurate)"
498−msgstr ""
508+msgstr "Používat pouze GPS (méně přesné)"
499
509
500#: qml/Wizard/Pages/50-location.qml:86
510#: qml/Wizard/Pages/50-location.qml:86
501msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
511msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
502−msgstr ""
512+msgstr "Používat GPS, anonymní Wi-Fi a datovou síť (doporučeno)"
503
513
504#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
514#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
505#: qml/Wizard/Pages/50-location.qml:103
515#: qml/Wizard/Pages/50-location.qml:103
@@ -507,81 +517,88 @@ msgid ""
507"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
517"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
508"conditions</a>."
518"conditions</a>."
509msgstr ""
519msgstr ""
520+"Zvolením této možnosti souhlasíte s <a href='#'>pravidly a podmínkami</a> "
521+"Nokia HERE."
510
522
511#: qml/Wizard/Pages/50-location.qml:112
523#: qml/Wizard/Pages/50-location.qml:112
512msgid "Not at all"
524msgid "Not at all"
513−msgstr ""
525+msgstr "V žádném případě"
514
526
515#: qml/Wizard/Pages/50-location.qml:124
527#: qml/Wizard/Pages/50-location.qml:124
516msgid "You can change your mind later in <b>System Settings</b>."
528msgid "You can change your mind later in <b>System Settings</b>."
517msgstr ""
529msgstr ""
530+"Pokud změníte názor, můžete nastavení změnit v <b>Nastavení systému</b>."
518
531
519#: qml/Wizard/Pages/60-reporting.qml:24
532#: qml/Wizard/Pages/60-reporting.qml:24
520msgid "Improving your experience"
533msgid "Improving your experience"
521−msgstr ""
534+msgstr "Zlepšete své zkušenosti"
522
535
523#: qml/Wizard/Pages/60-reporting.qml:36
536#: qml/Wizard/Pages/60-reporting.qml:36
524msgid ""
537msgid ""
525"Your phone is set up to automatically report errors to Canonical and its "
538"Your phone is set up to automatically report errors to Canonical and its "
526"partners, the makers of the operating system."
539"partners, the makers of the operating system."
527msgstr ""
540msgstr ""
541+"Váš telefon je nastaven tak, aby automaticky odesílal informace o chybách "
542+"společnosti Canonical a partnerům, kteří stvořili tento operační systém."
528
543
529#: qml/Wizard/Pages/60-reporting.qml:43
544#: qml/Wizard/Pages/60-reporting.qml:43
530msgid ""
545msgid ""
531"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
546"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
532"Privacy</b>"
547"Privacy</b>"
533msgstr ""
548msgstr ""
549+"To může být vypnuto v <b>Nastavení systému</b> v <b>Zabezpečení a "
550+"soukromí</b>"
534
551
535#: qml/Wizard/Pages/80-finished.qml:24
552#: qml/Wizard/Pages/80-finished.qml:24
536msgid "All done"
553msgid "All done"
537−msgstr ""
554+msgstr "Vše je hotovo"
538
555
539#: qml/Wizard/Pages/80-finished.qml:39
556#: qml/Wizard/Pages/80-finished.qml:39
540msgid "Nice work!"
557msgid "Nice work!"
541−msgstr ""
558+msgstr "Skvělá práce!"
542
559
543#: qml/Wizard/Pages/80-finished.qml:46
560#: qml/Wizard/Pages/80-finished.qml:46
544msgid "Your phone is now ready to use."
561msgid "Your phone is now ready to use."
545−msgstr ""
562+msgstr "Váš telefon je nyní připraven k použití."
546
563
547#: qml/Wizard/Pages/80-finished.qml:53
564#: qml/Wizard/Pages/80-finished.qml:53
548msgid "Finish"
565msgid "Finish"
549−msgstr ""
566+msgstr "Dokončit"
550
567
551#: qml/Wizard/Pages/here-terms.qml:27
568#: qml/Wizard/Pages/here-terms.qml:27
552msgid "Terms & Conditions"
569msgid "Terms & Conditions"
553−msgstr ""
570+msgstr "Pravidla a podmínky"
554
571
555#: qml/Wizard/Pages/passwd-confirm.qml:49
572#: qml/Wizard/Pages/passwd-confirm.qml:49
556msgid "Confirm passphrase"
573msgid "Confirm passphrase"
557−msgstr ""
574+msgstr "Potvrdit heslo"
558
575
559#: qml/Wizard/Pages/passwd-confirm.qml:50
576#: qml/Wizard/Pages/passwd-confirm.qml:50
560msgid "Confirm passcode"
577msgid "Confirm passcode"
561−msgstr ""
578+msgstr "Potvrdit číselné heslo"
562
579
563#: qml/Wizard/Pages/passwd-confirm.qml:53
580#: qml/Wizard/Pages/passwd-confirm.qml:53
564#: qml/Wizard/Pages/passwd-confirm.qml:54
581#: qml/Wizard/Pages/passwd-confirm.qml:54
565msgid "Please try again."
582msgid "Please try again."
566−msgstr ""
583+msgstr "Zkuste to, prosím, znovu."
567
584
568#: qml/Wizard/Pages/passwd-set.qml:61
585#: qml/Wizard/Pages/passwd-set.qml:61
569msgid "Choose your passcode"
586msgid "Choose your passcode"
570−msgstr ""
587+msgstr "Zvolte své heslo"
571
588
572#: qml/Wizard/Pages/passwd-set.qml:68
589#: qml/Wizard/Pages/passwd-set.qml:68
573msgid "Passphrase must be 4 characters long"
590msgid "Passphrase must be 4 characters long"
574−msgstr ""
591+msgstr "Heslo musí být alespoň 4 znaky dlouhé"
575
592
576#: qml/Wizard/StackButton.qml:39
593#: qml/Wizard/StackButton.qml:39
577#, qt-format
594#, qt-format
578msgid "〈 %1"
595msgid "〈 %1"
579−msgstr ""
596+msgstr "〈 %1"
580
597
581#: qml/Wizard/StackButton.qml:42
598#: qml/Wizard/StackButton.qml:42
582#, qt-format
599#, qt-format
583msgid "%1 〉"
600msgid "%1 〉"
584−msgstr ""
601+msgstr "%1 〉"
585
602
586#~ msgid "Try swiping from the right edge to unlock the phone"
603#~ msgid "Try swiping from the right edge to unlock the phone"
587#~ msgstr "Pokuste se odemknout telefon potažením prstu z pravého okraje."
604#~ msgstr "Pokuste se odemknout telefon potažením prstu z pravého okraje."
po/da.po+53−51
@@ -9,19 +9,19 @@ msgstr ""
9"Project-Id-Version: unity8\n"
9"Project-Id-Version: unity8\n"
10"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
11"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11"POT-Creation-Date: 2015-04-14 16:13+0200\n"
12−"PO-Revision-Date: 2015-04-17 10:46+0000\n"
12+"PO-Revision-Date: 2015-04-28 19:14+0000\n"
13−"Last-Translator: Bluegrassin <anderslgade@gmail.com>\n"
13+"Last-Translator: Thomas Lundsgaard Hansen <thomaslhansen@live.dk>\n"
14"Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
14"Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
15"MIME-Version: 1.0\n"
15"MIME-Version: 1.0\n"
16"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Type: text/plain; charset=UTF-8\n"
17"Content-Transfer-Encoding: 8bit\n"
17"Content-Transfer-Encoding: 8bit\n"
18"Plural-Forms: nplurals=2; plural=n != 1;\n"
18"Plural-Forms: nplurals=2; plural=n != 1;\n"
19−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19+"X-Launchpad-Export-Date: 2015-04-29 05:27+0000\n"
20−"X-Generator: Launchpad (build 17430)\n"
20+"X-Generator: Launchpad (build 17453)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
23msgid "Password: "
23msgid "Password: "
24−msgstr ""
24+msgstr "Adgangskode: "
25
25
26#: plugins/Unity/Launcher/launcheritem.cpp:45
26#: plugins/Unity/Launcher/launcheritem.cpp:45
27#: plugins/Unity/Launcher/launcheritem.cpp:100
27#: plugins/Unity/Launcher/launcheritem.cpp:100
@@ -58,7 +58,7 @@ msgstr[1] "Vent venligst %1 minutter og prøv igen..."
58#: qml/Components/Dialogs.qml:69
58#: qml/Components/Dialogs.qml:69
59msgctxt "Title: Lock/Log out dialog"
59msgctxt "Title: Lock/Log out dialog"
60msgid "Log out"
60msgid "Log out"
61−msgstr ""
61+msgstr "Log af"
62
62
63#: qml/Components/Dialogs.qml:70
63#: qml/Components/Dialogs.qml:70
64msgid "Are you sure you want to log out?"
64msgid "Are you sure you want to log out?"
@@ -67,22 +67,22 @@ msgstr "Vil du logge ud?"
67#: qml/Components/Dialogs.qml:72
67#: qml/Components/Dialogs.qml:72
68msgctxt "Button: Lock the system"
68msgctxt "Button: Lock the system"
69msgid "Lock"
69msgid "Lock"
70−msgstr ""
70+msgstr "Lås"
71
71
72#: qml/Components/Dialogs.qml:79
72#: qml/Components/Dialogs.qml:79
73msgctxt "Button: Log out from the system"
73msgctxt "Button: Log out from the system"
74msgid "Log Out"
74msgid "Log Out"
75−msgstr ""
75+msgstr "Log af"
76
76
77#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
77#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
78#: qml/Components/Dialogs.qml:173
78#: qml/Components/Dialogs.qml:173
79msgid "Cancel"
79msgid "Cancel"
80−msgstr ""
80+msgstr "Annullér"
81
81
82#: qml/Components/Dialogs.qml:98
82#: qml/Components/Dialogs.qml:98
83msgctxt "Title: Reboot/Shut down dialog"
83msgctxt "Title: Reboot/Shut down dialog"
84msgid "Shut down"
84msgid "Shut down"
85−msgstr ""
85+msgstr "Luk ned"
86
86
87#: qml/Components/Dialogs.qml:99
87#: qml/Components/Dialogs.qml:99
88msgid "Are you sure you want to shut down?"
88msgid "Are you sure you want to shut down?"
@@ -91,17 +91,17 @@ msgstr "Er du sikker på du vil lukke ned?"
91#: qml/Components/Dialogs.qml:101
91#: qml/Components/Dialogs.qml:101
92msgctxt "Button: Reboot the system"
92msgctxt "Button: Reboot the system"
93msgid "Reboot"
93msgid "Reboot"
94−msgstr ""
94+msgstr "Genstart"
95
95
96#: qml/Components/Dialogs.qml:109
96#: qml/Components/Dialogs.qml:109
97msgctxt "Button: Shut down the system"
97msgctxt "Button: Shut down the system"
98msgid "Shut down"
98msgid "Shut down"
99−msgstr ""
99+msgstr "Luk ned"
100
100
101#: qml/Components/Dialogs.qml:129
101#: qml/Components/Dialogs.qml:129
102msgctxt "Title: Reboot dialog"
102msgctxt "Title: Reboot dialog"
103msgid "Reboot"
103msgid "Reboot"
104−msgstr ""
104+msgstr "Genstart"
105
105
106#: qml/Components/Dialogs.qml:130
106#: qml/Components/Dialogs.qml:130
107msgid "Are you sure you want to reboot?"
107msgid "Are you sure you want to reboot?"
@@ -109,11 +109,11 @@ msgstr "Er du sikker på du vil genstarte?"
109
109
110#: qml/Components/Dialogs.qml:132
110#: qml/Components/Dialogs.qml:132
111msgid "No"
111msgid "No"
112−msgstr ""
112+msgstr "Nej"
113
113
114#: qml/Components/Dialogs.qml:138
114#: qml/Components/Dialogs.qml:138
115msgid "Yes"
115msgid "Yes"
116−msgstr ""
116+msgstr "Ja"
117
117
118#: qml/Components/Dialogs.qml:152
118#: qml/Components/Dialogs.qml:152
119msgctxt "Title: Power off/Restart dialog"
119msgctxt "Title: Power off/Restart dialog"
@@ -131,12 +131,12 @@ msgstr ""
131#: qml/Components/Dialogs.qml:155
131#: qml/Components/Dialogs.qml:155
132msgctxt "Button: Power off the system"
132msgctxt "Button: Power off the system"
133msgid "Power off"
133msgid "Power off"
134−msgstr ""
134+msgstr "Luk ned"
135
135
136#: qml/Components/Dialogs.qml:164
136#: qml/Components/Dialogs.qml:164
137msgctxt "Button: Restart the system"
137msgctxt "Button: Restart the system"
138msgid "Restart"
138msgid "Restart"
139−msgstr ""
139+msgstr "Genstart"
140
140
141#: qml/Components/Lockscreen.qml:245
141#: qml/Components/Lockscreen.qml:245
142msgid "Return to Call"
142msgid "Return to Call"
@@ -161,30 +161,30 @@ msgstr "Se alle"
161#: qml/Dash/GenericScopeView.qml:594
161#: qml/Dash/GenericScopeView.qml:594
162msgctxt "Label: Hint for dash search line edit"
162msgctxt "Label: Hint for dash search line edit"
163msgid "Search"
163msgid "Search"
164−msgstr ""
164+msgstr "Søg"
165
165
166#: qml/Dash/PageHeader.qml:269
166#: qml/Dash/PageHeader.qml:269
167msgctxt "Button: Open the Ubuntu Store"
167msgctxt "Button: Open the Ubuntu Store"
168msgid "Store"
168msgid "Store"
169−msgstr ""
169+msgstr "Butik"
170
170
171#: qml/Dash/PageHeader.qml:276
171#: qml/Dash/PageHeader.qml:276
172msgctxt "Button: Start a search in the current dash scope"
172msgctxt "Button: Start a search in the current dash scope"
173msgid "Search"
173msgid "Search"
174−msgstr ""
174+msgstr "Søg"
175
175
176#: qml/Dash/PageHeader.qml:286
176#: qml/Dash/PageHeader.qml:286
177msgctxt "Button: Show the current dash scope settings"
177msgctxt "Button: Show the current dash scope settings"
178msgid "Settings"
178msgid "Settings"
179−msgstr ""
179+msgstr "Indstillinger"
180
180
181#: qml/Dash/PageHeader.qml:293
181#: qml/Dash/PageHeader.qml:293
182msgid "Remove from Favorites"
182msgid "Remove from Favorites"
183−msgstr ""
183+msgstr "Fjern fra Favoritter"
184
184
185#: qml/Dash/PageHeader.qml:293
185#: qml/Dash/PageHeader.qml:293
186msgid "Add to Favorites"
186msgid "Add to Favorites"
187−msgstr ""
187+msgstr "Tilføj til Favoritter"
188
188
189#: qml/Dash/Previews/PreviewActionCombo.qml:35
189#: qml/Dash/Previews/PreviewActionCombo.qml:35
190msgid "More..."
190msgid "More..."
@@ -220,11 +220,11 @@ msgstr "Aktivér lokationsdata"
220
220
221#: qml/Dash/ScopesList.qml:66
221#: qml/Dash/ScopesList.qml:66
222msgid "Manage"
222msgid "Manage"
223−msgstr ""
223+msgstr "Administrér"
224
224
225#: qml/Dash/ScopesList.qml:112
225#: qml/Dash/ScopesList.qml:112
226msgid "Home"
226msgid "Home"
227−msgstr ""
227+msgstr "Hjem"
228
228
229#: qml/Dash/ScopesList.qml:113
229#: qml/Dash/ScopesList.qml:113
230msgid "Also installed"
230msgid "Also installed"
@@ -232,15 +232,15 @@ msgstr "Også installeret"
232
232
233#: qml/Greeter/CoverPage.qml:107
233#: qml/Greeter/CoverPage.qml:107
234msgid "Unlock"
234msgid "Unlock"
235−msgstr ""
235+msgstr "Lås op"
236
236
237#: qml/Greeter/LoginList.qml:236
237#: qml/Greeter/LoginList.qml:236
238msgid "Retry"
238msgid "Retry"
239−msgstr ""
239+msgstr "Forsøg igen"
240
240
241#: qml/Greeter/LoginList.qml:237
241#: qml/Greeter/LoginList.qml:237
242msgid "Tap to unlock"
242msgid "Tap to unlock"
243−msgstr ""
243+msgstr "Tap for at låse op"
244
244
245#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
245#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
246msgid "Sorry, incorrect passphrase."
246msgid "Sorry, incorrect passphrase."
@@ -277,7 +277,7 @@ msgstr "Indtast %1"
277
277
278#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
278#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
279msgid "Enter passphrase"
279msgid "Enter passphrase"
280−msgstr ""
280+msgstr "Angiv adgangsfrase"
281
281
282#: qml/Greeter/NarrowView.qml:117
282#: qml/Greeter/NarrowView.qml:117
283msgid "Enter passcode"
283msgid "Enter passcode"
@@ -314,7 +314,7 @@ msgstr "Konference"
314
314
315#: qml/Panel/Indicators/MenuItemFactory.qml:639
315#: qml/Panel/Indicators/MenuItemFactory.qml:639
316msgid "Nothing is playing"
316msgid "Nothing is playing"
317−msgstr ""
317+msgstr "Intet afspilles"
318
318
319#: qml/Panel/Indicators/MenuItemFactory.qml:787
319#: qml/Panel/Indicators/MenuItemFactory.qml:787
320msgid "In queue…"
320msgid "In queue…"
@@ -343,7 +343,7 @@ msgstr "Mislykkedes - tryk for at prøve igen"
343#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
343#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
344msgctxt "Label: Hint in message indicator line edit"
344msgctxt "Label: Hint in message indicator line edit"
345msgid "Reply"
345msgid "Reply"
346−msgstr ""
346+msgstr "Svar"
347
347
348#: qml/Panel/Indicators/ModemInfoItem.qml:105
348#: qml/Panel/Indicators/ModemInfoItem.qml:105
349msgid "Unlock SIM"
349msgid "Unlock SIM"
@@ -351,7 +351,7 @@ msgstr "Lås SIM-kort op"
351
351
352#: qml/Panel/Indicators/RoamingIndication.qml:27
352#: qml/Panel/Indicators/RoamingIndication.qml:27
353msgid "Roaming"
353msgid "Roaming"
354−msgstr ""
354+msgstr "Roaming"
355
355
356#: qml/Tutorial/TutorialBottomFinish.qml:24
356#: qml/Tutorial/TutorialBottomFinish.qml:24
357msgid "This action does different things for different apps"
357msgid "This action does different things for different apps"
@@ -376,7 +376,7 @@ msgstr "Tryk her for at fortsætte."
376
376
377#: qml/Tutorial/TutorialContent.qml:81
377#: qml/Tutorial/TutorialContent.qml:81
378msgid "Click here to finish."
378msgid "Click here to finish."
379−msgstr ""
379+msgstr "Tryk her for at afslutte."
380
380
381#: qml/Tutorial/TutorialLeftFinish.qml:24
381#: qml/Tutorial/TutorialLeftFinish.qml:24
382msgid "These are the shortcuts to favorite apps"
382msgid "These are the shortcuts to favorite apps"
@@ -384,7 +384,7 @@ msgstr "Disse er genveje til favorit-apps"
384
384
385#: qml/Tutorial/TutorialLeft.qml:26
385#: qml/Tutorial/TutorialLeft.qml:26
386msgid "Open the launcher"
386msgid "Open the launcher"
387−msgstr ""
387+msgstr "Åbn opstarteren"
388
388
389#: qml/Tutorial/TutorialLeft.qml:27
389#: qml/Tutorial/TutorialLeft.qml:27
390msgid "Short swipe from the left edge."
390msgid "Short swipe from the left edge."
@@ -413,11 +413,11 @@ msgstr "Se alle dine kørende opgaver."
413#: qml/Wizard/Page.qml:89
413#: qml/Wizard/Page.qml:89
414msgctxt "Button: Go back one page in the Wizard"
414msgctxt "Button: Go back one page in the Wizard"
415msgid "Back"
415msgid "Back"
416−msgstr ""
416+msgstr "Tilbage"
417
417
418#: qml/Wizard/Pages/10-welcome.qml:27
418#: qml/Wizard/Pages/10-welcome.qml:27
419msgid "Hi!"
419msgid "Hi!"
420−msgstr ""
420+msgstr "Hej!"
421
421
422#: qml/Wizard/Pages/10-welcome.qml:44
422#: qml/Wizard/Pages/10-welcome.qml:44
423msgid "Welcome to your Ubuntu phone."
423msgid "Welcome to your Ubuntu phone."
@@ -432,7 +432,7 @@ msgstr "Lad os komme i gang."
432#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
432#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
433#: qml/Wizard/Pages/passwd-set.qml:90
433#: qml/Wizard/Pages/passwd-set.qml:90
434msgid "Continue"
434msgid "Continue"
435−msgstr ""
435+msgstr "Fortsæt"
436
436
437#: qml/Wizard/Pages/20-sim.qml:25
437#: qml/Wizard/Pages/20-sim.qml:25
438msgid "Add a SIM card and restart your device"
438msgid "Add a SIM card and restart your device"
@@ -441,14 +441,16 @@ msgstr "Indsæt et SIM-kort og genstart din enhed"
441#: qml/Wizard/Pages/20-sim.qml:59
441#: qml/Wizard/Pages/20-sim.qml:59
442msgid "Without it, you won’t be able to make calls or use text messaging."
442msgid "Without it, you won’t be able to make calls or use text messaging."
443msgstr ""
443msgstr ""
444+"Uden denne vil du ikke være i stand til at foretage opkald eller sende "
445+"tekstbeskeder."
444
446
445#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
447#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
446msgid "Skip"
448msgid "Skip"
447−msgstr ""
449+msgstr "Spring over"
448
450
449#: qml/Wizard/Pages/30-passwd-type.qml:39
451#: qml/Wizard/Pages/30-passwd-type.qml:39
450msgid "Lock security"
452msgid "Lock security"
451−msgstr ""
453+msgstr "Låsesikkerhed"
452
454
453#: qml/Wizard/Pages/30-passwd-type.qml:74
455#: qml/Wizard/Pages/30-passwd-type.qml:74
454msgid "Please select how you’d like to unlock your phone."
456msgid "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."
457#: qml/Wizard/Pages/30-passwd-type.qml:97
459#: qml/Wizard/Pages/30-passwd-type.qml:97
458msgctxt "Label: Type of security method"
460msgctxt "Label: Type of security method"
459msgid "Swipe"
461msgid "Swipe"
460−msgstr ""
462+msgstr "Swipe"
461
463
462#: qml/Wizard/Pages/30-passwd-type.qml:98
464#: qml/Wizard/Pages/30-passwd-type.qml:98
463msgctxt "Label: Description of security method"
465msgctxt "Label: Description of security method"
464msgid "No security"
466msgid "No security"
465−msgstr ""
467+msgstr "Ingen sikkerhed"
466
468
467#: qml/Wizard/Pages/30-passwd-type.qml:100
469#: qml/Wizard/Pages/30-passwd-type.qml:100
468msgctxt "Label: Type of security method"
470msgctxt "Label: Type of security method"
469msgid "Passcode"
471msgid "Passcode"
470−msgstr ""
472+msgstr "Adgangskode"
471
473
472#: qml/Wizard/Pages/30-passwd-type.qml:101
474#: qml/Wizard/Pages/30-passwd-type.qml:101
473msgctxt "Label: Description of security method"
475msgctxt "Label: Description of security method"
474msgid "4 digits only"
476msgid "4 digits only"
475−msgstr ""
477+msgstr "Kun 4 tal"
476
478
477#: qml/Wizard/Pages/30-passwd-type.qml:103
479#: qml/Wizard/Pages/30-passwd-type.qml:103
478msgctxt "Label: Type of security method"
480msgctxt "Label: Type of security method"
479msgid "Passphrase"
481msgid "Passphrase"
480−msgstr ""
482+msgstr "Adgangsfrase"
481
483
482#: qml/Wizard/Pages/30-passwd-type.qml:104
484#: qml/Wizard/Pages/30-passwd-type.qml:104
483msgctxt "Label: Description of security method"
485msgctxt "Label: Description of security method"
484msgid "Numbers and letters"
486msgid "Numbers and letters"
485−msgstr ""
487+msgstr "Tal og bogstaver"
486
488
487#: qml/Wizard/Pages/40-wifi.qml:29
489#: qml/Wizard/Pages/40-wifi.qml:29
488msgid "Connect to Wi‑Fi"
490msgid "Connect to Wi‑Fi"
@@ -498,7 +500,7 @@ msgstr "Ingen tilgængelige netværk."
498
500
499#: qml/Wizard/Pages/50-location.qml:27
501#: qml/Wizard/Pages/50-location.qml:27
500msgid "Location"
502msgid "Location"
501−msgstr ""
503+msgstr "Placering"
502
504
503#: qml/Wizard/Pages/50-location.qml:62
505#: qml/Wizard/Pages/50-location.qml:62
504msgid "Let the phone detect your location:"
506msgid "Let the phone detect your location:"
@@ -564,15 +566,15 @@ msgstr "Din telefon er nu klar til brug."
564
566
565#: qml/Wizard/Pages/80-finished.qml:53
567#: qml/Wizard/Pages/80-finished.qml:53
566msgid "Finish"
568msgid "Finish"
567−msgstr ""
569+msgstr "Afslut"
568
570
569#: qml/Wizard/Pages/here-terms.qml:27
571#: qml/Wizard/Pages/here-terms.qml:27
570msgid "Terms & Conditions"
572msgid "Terms & Conditions"
571−msgstr ""
573+msgstr "Betingelser og vilkår"
572
574
573#: qml/Wizard/Pages/passwd-confirm.qml:49
575#: qml/Wizard/Pages/passwd-confirm.qml:49
574msgid "Confirm passphrase"
576msgid "Confirm passphrase"
575−msgstr ""
577+msgstr "Bekæft adgangsfrase"
576
578
577#: qml/Wizard/Pages/passwd-confirm.qml:50
579#: qml/Wizard/Pages/passwd-confirm.qml:50
578msgid "Confirm passcode"
580msgid "Confirm passcode"
@@ -581,7 +583,7 @@ msgstr "Bekræft adgangskode"
581#: qml/Wizard/Pages/passwd-confirm.qml:53
583#: qml/Wizard/Pages/passwd-confirm.qml:53
582#: qml/Wizard/Pages/passwd-confirm.qml:54
584#: qml/Wizard/Pages/passwd-confirm.qml:54
583msgid "Please try again."
585msgid "Please try again."
584−msgstr ""
586+msgstr "Forsøg venligst igen."
585
587
586#: qml/Wizard/Pages/passwd-set.qml:61
588#: qml/Wizard/Pages/passwd-set.qml:61
587msgid "Choose your passcode"
589msgid "Choose your passcode"
@@ -589,7 +591,7 @@ msgstr "Vælg din adgangskode"
589
591
590#: qml/Wizard/Pages/passwd-set.qml:68
592#: qml/Wizard/Pages/passwd-set.qml:68
591msgid "Passphrase must be 4 characters long"
593msgid "Passphrase must be 4 characters long"
592−msgstr ""
594+msgstr "Adgangsfrasen skal være mindst 4 tegn"
593
595
594#: qml/Wizard/StackButton.qml:39
596#: qml/Wizard/StackButton.qml:39
595#, qt-format
597#, qt-format
po/de.po+31−31
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-06 14:24+0000\n"
11+"PO-Revision-Date: 2015-06-17 14:05+0000\n"
12−"Last-Translator: Niklas Wenzel <nikwen.developer@gmail.com>\n"
12+"Last-Translator: JkB <Unknown>\n"
13"Language-Team: German <de@li.org>\n"
13"Language-Team: German <de@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"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-06-19 05:01+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
19+"X-Generator: Launchpad (build 17570)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -51,13 +51,13 @@ msgstr ""
51#, qt-format
51#, qt-format
52msgid "Please wait %1 minute and then try again…"
52msgid "Please wait %1 minute and then try again…"
53msgid_plural "Please wait %1 minutes and then try again…"
53msgid_plural "Please wait %1 minutes and then try again…"
54−msgstr[0] "Bitte warten Sie %1 Minute und versuchen Sie es dann erneut..."
54+msgstr[0] "Bitte %1 Minute warten und es dann erneut versuchen …"
55−msgstr[1] "Bitte warten Sie %1 Minuten und versuchen Sie es dann erneut..."
55+msgstr[1] "Bitte %1 Minuten warten und es dann erneut versuchen …"
56
56
57#: qml/Components/Dialogs.qml:69
57#: qml/Components/Dialogs.qml:69
58msgctxt "Title: Lock/Log out dialog"
58msgctxt "Title: Lock/Log out dialog"
59msgid "Log out"
59msgid "Log out"
60−msgstr ""
60+msgstr "Abmelden"
61
61
62#: qml/Components/Dialogs.qml:70
62#: qml/Components/Dialogs.qml:70
63msgid "Are you sure you want to log out?"
63msgid "Are you sure you want to log out?"
@@ -66,12 +66,12 @@ msgstr "Sind Sie sicher, dass Sie sich abmelden wollen?"
66#: qml/Components/Dialogs.qml:72
66#: qml/Components/Dialogs.qml:72
67msgctxt "Button: Lock the system"
67msgctxt "Button: Lock the system"
68msgid "Lock"
68msgid "Lock"
69−msgstr ""
69+msgstr "Sperren"
70
70
71#: qml/Components/Dialogs.qml:79
71#: qml/Components/Dialogs.qml:79
72msgctxt "Button: Log out from the system"
72msgctxt "Button: Log out from the system"
73msgid "Log Out"
73msgid "Log Out"
74−msgstr ""
74+msgstr "Abmelden"
75
75
76#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
76#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
77#: qml/Components/Dialogs.qml:173
77#: qml/Components/Dialogs.qml:173
@@ -81,7 +81,7 @@ msgstr "Abbrechen"
81#: qml/Components/Dialogs.qml:98
81#: qml/Components/Dialogs.qml:98
82msgctxt "Title: Reboot/Shut down dialog"
82msgctxt "Title: Reboot/Shut down dialog"
83msgid "Shut down"
83msgid "Shut down"
84−msgstr ""
84+msgstr "Ausschalten"
85
85
86#: qml/Components/Dialogs.qml:99
86#: qml/Components/Dialogs.qml:99
87msgid "Are you sure you want to shut down?"
87msgid "Are you sure you want to shut down?"
@@ -90,17 +90,17 @@ msgstr "Sind Sie sicher, dass Sie herunterfahren wollen?"
90#: qml/Components/Dialogs.qml:101
90#: qml/Components/Dialogs.qml:101
91msgctxt "Button: Reboot the system"
91msgctxt "Button: Reboot the system"
92msgid "Reboot"
92msgid "Reboot"
93−msgstr ""
93+msgstr "Neustarten"
94
94
95#: qml/Components/Dialogs.qml:109
95#: qml/Components/Dialogs.qml:109
96msgctxt "Button: Shut down the system"
96msgctxt "Button: Shut down the system"
97msgid "Shut down"
97msgid "Shut down"
98−msgstr ""
98+msgstr "Ausschalten"
99
99
100#: qml/Components/Dialogs.qml:129
100#: qml/Components/Dialogs.qml:129
101msgctxt "Title: Reboot dialog"
101msgctxt "Title: Reboot dialog"
102msgid "Reboot"
102msgid "Reboot"
103−msgstr ""
103+msgstr "Neustarten"
104
104
105#: qml/Components/Dialogs.qml:130
105#: qml/Components/Dialogs.qml:130
106msgid "Are you sure you want to reboot?"
106msgid "Are you sure you want to reboot?"
@@ -117,7 +117,7 @@ msgstr "Ja"
117#: qml/Components/Dialogs.qml:152
117#: qml/Components/Dialogs.qml:152
118msgctxt "Title: Power off/Restart dialog"
118msgctxt "Title: Power off/Restart dialog"
119msgid "Power"
119msgid "Power"
120−msgstr ""
120+msgstr "Ausschalten"
121
121
122#: qml/Components/Dialogs.qml:153
122#: qml/Components/Dialogs.qml:153
123msgid ""
123msgid ""
@@ -130,12 +130,12 @@ msgstr ""
130#: qml/Components/Dialogs.qml:155
130#: qml/Components/Dialogs.qml:155
131msgctxt "Button: Power off the system"
131msgctxt "Button: Power off the system"
132msgid "Power off"
132msgid "Power off"
133−msgstr ""
133+msgstr "Ausschalten"
134
134
135#: qml/Components/Dialogs.qml:164
135#: qml/Components/Dialogs.qml:164
136msgctxt "Button: Restart the system"
136msgctxt "Button: Restart the system"
137msgid "Restart"
137msgid "Restart"
138−msgstr ""
138+msgstr "Neustarten"
139
139
140#: qml/Components/Lockscreen.qml:245
140#: qml/Components/Lockscreen.qml:245
141msgid "Return to Call"
141msgid "Return to Call"
@@ -160,22 +160,22 @@ msgstr "Alle anzeigen"
160#: qml/Dash/GenericScopeView.qml:594
160#: qml/Dash/GenericScopeView.qml:594
161msgctxt "Label: Hint for dash search line edit"
161msgctxt "Label: Hint for dash search line edit"
162msgid "Search"
162msgid "Search"
163−msgstr ""
163+msgstr "Suchen"
164
164
165#: qml/Dash/PageHeader.qml:269
165#: qml/Dash/PageHeader.qml:269
166msgctxt "Button: Open the Ubuntu Store"
166msgctxt "Button: Open the Ubuntu Store"
167msgid "Store"
167msgid "Store"
168−msgstr ""
168+msgstr "Speichern"
169
169
170#: qml/Dash/PageHeader.qml:276
170#: qml/Dash/PageHeader.qml:276
171msgctxt "Button: Start a search in the current dash scope"
171msgctxt "Button: Start a search in the current dash scope"
172msgid "Search"
172msgid "Search"
173−msgstr ""
173+msgstr "Suchen"
174
174
175#: qml/Dash/PageHeader.qml:286
175#: qml/Dash/PageHeader.qml:286
176msgctxt "Button: Show the current dash scope settings"
176msgctxt "Button: Show the current dash scope settings"
177msgid "Settings"
177msgid "Settings"
178−msgstr ""
178+msgstr "Einstellungen"
179
179
180#: qml/Dash/PageHeader.qml:293
180#: qml/Dash/PageHeader.qml:293
181msgid "Remove from Favorites"
181msgid "Remove from Favorites"
@@ -276,7 +276,7 @@ msgstr "%1 eingeben"
276
276
277#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
277#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
278msgid "Enter passphrase"
278msgid "Enter passphrase"
279−msgstr "Passwort eingeben"
279+msgstr "Passphrase eingeben"
280
280
281#: qml/Greeter/NarrowView.qml:117
281#: qml/Greeter/NarrowView.qml:117
282msgid "Enter passcode"
282msgid "Enter passcode"
@@ -305,7 +305,7 @@ msgstr "Passwort anzeigen"
305
305
306#: qml/Panel/ActiveCallHint.qml:79
306#: qml/Panel/ActiveCallHint.qml:79
307msgid "Tap to return to call..."
307msgid "Tap to return to call..."
308−msgstr "Tippen um zum Anruf zurückzugehen"
308+msgstr "Tippen, um zum Anruf zurückzugehen …"
309
309
310#: qml/Panel/ActiveCallHint.qml:92
310#: qml/Panel/ActiveCallHint.qml:92
311msgid "Conference"
311msgid "Conference"
@@ -342,7 +342,7 @@ msgstr "Fehlgeschlagen, tippen Sie zum Wiederholen"
342#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
342#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
343msgctxt "Label: Hint in message indicator line edit"
343msgctxt "Label: Hint in message indicator line edit"
344msgid "Reply"
344msgid "Reply"
345−msgstr ""
345+msgstr "Antworten"
346
346
347#: qml/Panel/Indicators/ModemInfoItem.qml:105
347#: qml/Panel/Indicators/ModemInfoItem.qml:105
348msgid "Unlock SIM"
348msgid "Unlock SIM"
@@ -376,7 +376,7 @@ msgstr "Zum Fortfahren hier tippen."
376
376
377#: qml/Tutorial/TutorialContent.qml:81
377#: qml/Tutorial/TutorialContent.qml:81
378msgid "Click here to finish."
378msgid "Click here to finish."
379−msgstr ""
379+msgstr "Zum Abschließen bitte hier klicken."
380
380
381#: qml/Tutorial/TutorialLeftFinish.qml:24
381#: qml/Tutorial/TutorialLeftFinish.qml:24
382msgid "These are the shortcuts to favorite apps"
382msgid "These are the shortcuts to favorite apps"
@@ -413,7 +413,7 @@ msgstr "Sehen Sie alle Ihre laufenden Anwendungen."
413#: qml/Wizard/Page.qml:89
413#: qml/Wizard/Page.qml:89
414msgctxt "Button: Go back one page in the Wizard"
414msgctxt "Button: Go back one page in the Wizard"
415msgid "Back"
415msgid "Back"
416−msgstr ""
416+msgstr "Zurück"
417
417
418#: qml/Wizard/Pages/10-welcome.qml:27
418#: qml/Wizard/Pages/10-welcome.qml:27
419msgid "Hi!"
419msgid "Hi!"
@@ -457,32 +457,32 @@ msgstr "Bitte wählen Sie aus, wie Sie Ihr Telefon entsperren wollen."
457#: qml/Wizard/Pages/30-passwd-type.qml:97
457#: qml/Wizard/Pages/30-passwd-type.qml:97
458msgctxt "Label: Type of security method"
458msgctxt "Label: Type of security method"
459msgid "Swipe"
459msgid "Swipe"
460−msgstr ""
460+msgstr "Wischen"
461
461
462#: qml/Wizard/Pages/30-passwd-type.qml:98
462#: qml/Wizard/Pages/30-passwd-type.qml:98
463msgctxt "Label: Description of security method"
463msgctxt "Label: Description of security method"
464msgid "No security"
464msgid "No security"
465−msgstr ""
465+msgstr "Keine Sicherheitssperre"
466
466
467#: qml/Wizard/Pages/30-passwd-type.qml:100
467#: qml/Wizard/Pages/30-passwd-type.qml:100
468msgctxt "Label: Type of security method"
468msgctxt "Label: Type of security method"
469msgid "Passcode"
469msgid "Passcode"
470−msgstr ""
470+msgstr "PIN"
471
471
472#: qml/Wizard/Pages/30-passwd-type.qml:101
472#: qml/Wizard/Pages/30-passwd-type.qml:101
473msgctxt "Label: Description of security method"
473msgctxt "Label: Description of security method"
474msgid "4 digits only"
474msgid "4 digits only"
475−msgstr ""
475+msgstr "nur vier Ziffern"
476
476
477#: qml/Wizard/Pages/30-passwd-type.qml:103
477#: qml/Wizard/Pages/30-passwd-type.qml:103
478msgctxt "Label: Type of security method"
478msgctxt "Label: Type of security method"
479msgid "Passphrase"
479msgid "Passphrase"
480−msgstr ""
480+msgstr "Kennwort"
481
481
482#: qml/Wizard/Pages/30-passwd-type.qml:104
482#: qml/Wizard/Pages/30-passwd-type.qml:104
483msgctxt "Label: Description of security method"
483msgctxt "Label: Description of security method"
484msgid "Numbers and letters"
484msgid "Numbers and letters"
485−msgstr ""
485+msgstr "Ziffern und Buchstaben"
486
486
487#: qml/Wizard/Pages/40-wifi.qml:29
487#: qml/Wizard/Pages/40-wifi.qml:29
488msgid "Connect to Wi‑Fi"
488msgid "Connect to Wi‑Fi"
po/el.po+11−11
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-04-22 09:03+0000\n"
11+"PO-Revision-Date: 2015-06-18 19:05+0000\n"
12"Last-Translator: Simos Xenitellis <Unknown>\n"
12"Last-Translator: Simos Xenitellis <Unknown>\n"
13"Language-Team: Greek <el@li.org>\n"
13"Language-Team: Greek <el@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"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-06-19 05:01+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
19+"X-Generator: Launchpad (build 17570)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -117,7 +117,7 @@ msgstr "Ναι"
117#: qml/Components/Dialogs.qml:152
117#: qml/Components/Dialogs.qml:152
118msgctxt "Title: Power off/Restart dialog"
118msgctxt "Title: Power off/Restart dialog"
119msgid "Power"
119msgid "Power"
120−msgstr ""
120+msgstr "Τερματισμός"
121
121
122#: qml/Components/Dialogs.qml:153
122#: qml/Components/Dialogs.qml:153
123msgid ""
123msgid ""
@@ -165,17 +165,17 @@ msgstr "Αναζήτηση"
165#: qml/Dash/PageHeader.qml:269
165#: qml/Dash/PageHeader.qml:269
166msgctxt "Button: Open the Ubuntu Store"
166msgctxt "Button: Open the Ubuntu Store"
167msgid "Store"
167msgid "Store"
168−msgstr ""
168+msgstr "Κατάστημα"
169
169
170#: qml/Dash/PageHeader.qml:276
170#: qml/Dash/PageHeader.qml:276
171msgctxt "Button: Start a search in the current dash scope"
171msgctxt "Button: Start a search in the current dash scope"
172msgid "Search"
172msgid "Search"
173−msgstr ""
173+msgstr "Αναζήτηση"
174
174
175#: qml/Dash/PageHeader.qml:286
175#: qml/Dash/PageHeader.qml:286
176msgctxt "Button: Show the current dash scope settings"
176msgctxt "Button: Show the current dash scope settings"
177msgid "Settings"
177msgid "Settings"
178−msgstr ""
178+msgstr "Ρυθμίσεις"
179
179
180#: qml/Dash/PageHeader.qml:293
180#: qml/Dash/PageHeader.qml:293
181msgid "Remove from Favorites"
181msgid "Remove from Favorites"
@@ -378,7 +378,7 @@ msgstr "Πατήστε εδώ για συνέχεια."
378
378
379#: qml/Tutorial/TutorialContent.qml:81
379#: qml/Tutorial/TutorialContent.qml:81
380msgid "Click here to finish."
380msgid "Click here to finish."
381−msgstr ""
381+msgstr "Πατήστε εδώ για τέλος."
382
382
383#: qml/Tutorial/TutorialLeftFinish.qml:24
383#: qml/Tutorial/TutorialLeftFinish.qml:24
384msgid "These are the shortcuts to favorite apps"
384msgid "These are the shortcuts to favorite apps"
@@ -510,12 +510,12 @@ msgstr "Επιτρέψετε το τηλέφωνο να εντοπίσει τη
510
510
511#: qml/Wizard/Pages/50-location.qml:69
511#: qml/Wizard/Pages/50-location.qml:69
512msgid "Using GPS only (less accurate)"
512msgid "Using GPS only (less accurate)"
513−msgstr "Χρήση μόνο GPS (λιγότερο ακριβή)"
513+msgstr "Χρήση μόνο GPS (λιγότερο ακριβής)"
514
514
515#: qml/Wizard/Pages/50-location.qml:86
515#: qml/Wizard/Pages/50-location.qml:86
516msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
516msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
517msgstr ""
517msgstr ""
518−"Χρήση GPS, ανωνυνοποιημένα δίκτυα Wi-Fi και πληροφορίες δικτύων κινητής "
518+"Χρήση GPS, ανωνυμοποιημένων δικτύων Wi-Fi και πληροφορίες δικτύων κινητής "
519"τηλεφωνίας (συνιστάται)"
519"τηλεφωνίας (συνιστάται)"
520
520
521#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
521#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
@@ -546,7 +546,7 @@ msgid ""
546"partners, the makers of the operating system."
546"partners, the makers of the operating system."
547msgstr ""
547msgstr ""
548"Το τηλέφωνό σας είναι ρυθμισμένο να στέλνει αυτόματα αναφορές σφαλμάτων στην "
548"Το τηλέφωνό σας είναι ρυθμισμένο να στέλνει αυτόματα αναφορές σφαλμάτων στην "
549−"Canonical και τους συνεργάτες της, τους δημιουργούς τους λειτουργικού "
549+"Canonical και τους συνεργάτες της, τους δημιουργούς του λειτουργικού "
550"συστήματος."
550"συστήματος."
551
551
552#: qml/Wizard/Pages/60-reporting.qml:43
552#: qml/Wizard/Pages/60-reporting.qml:43
po/en_GB.po+3−3
@@ -8,14 +8,14 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"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"
12"Last-Translator: Andi Chandler <Unknown>\n"
12"Last-Translator: Andi Chandler <Unknown>\n"
13"Language-Team: English (United Kingdom) <en_GB@li.org>\n"
13"Language-Team: English (United Kingdom) <en_GB@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"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"
19"X-Generator: Launchpad (build 17430)\n"
19"X-Generator: Launchpad (build 17430)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
@@ -371,7 +371,7 @@ msgstr "Tap here to continue."
371
371
372#: qml/Tutorial/TutorialContent.qml:81
372#: qml/Tutorial/TutorialContent.qml:81
373msgid "Click here to finish."
373msgid "Click here to finish."
374−msgstr ""
374+msgstr "Click here to finish."
375
375
376#: qml/Tutorial/TutorialLeftFinish.qml:24
376#: qml/Tutorial/TutorialLeftFinish.qml:24
377msgid "These are the shortcuts to favorite apps"
377msgid "These are the shortcuts to favorite apps"
po/eo.po+6−6
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2013-06-26 22:35+0000\n"
11+"PO-Revision-Date: 2015-06-06 23:37+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12+"Last-Translator: Eliovir <Unknown>\n"
13"Language-Team: Esperanto <eo@li.org>\n"
13"Language-Team: Esperanto <eo@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"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-06-08 05:26+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
19+"X-Generator: Launchpad (build 17540)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -197,7 +197,7 @@ msgstr ""
197
197
198#: qml/Dash/Previews/PreviewRatingInput.qml:166
198#: qml/Dash/Previews/PreviewRatingInput.qml:166
199msgid "Send"
199msgid "Send"
200−msgstr ""
200+msgstr "Sendi"
201
201
202#: qml/Dash/PullToRefreshScopeStyle.qml:55
202#: qml/Dash/PullToRefreshScopeStyle.qml:55
203msgid "Pull to refresh…"
203msgid "Pull to refresh…"
@@ -291,7 +291,7 @@ msgstr ""
291
291
292#: qml/Notifications/NotificationMenuItemFactory.qml:128
292#: qml/Notifications/NotificationMenuItemFactory.qml:128
293msgid "Show password"
293msgid "Show password"
294−msgstr ""
294+msgstr "Montri pasvorton"
295
295
296#: qml/Panel/ActiveCallHint.qml:79
296#: qml/Panel/ActiveCallHint.qml:79
297msgid "Tap to return to call..."
297msgid "Tap to return to call..."
po/es.po+4−4
@@ -8,14 +8,14 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-27 21:24+0000\n"
11+"PO-Revision-Date: 2015-04-24 17:45+0000\n"
12−"Last-Translator: Adolfo Jayme <fitoschido@gmail.com>\n"
12+"Last-Translator: Paco Molinero <paco@byasl.com>\n"
13"Language-Team: Spanish <es@li.org>\n"
13"Language-Team: Spanish <es@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"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"
19"X-Generator: Launchpad (build 17430)\n"
19"X-Generator: Launchpad (build 17430)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
@@ -375,7 +375,7 @@ msgstr "Toque aquí para continuar."
375
375
376#: qml/Tutorial/TutorialContent.qml:81
376#: qml/Tutorial/TutorialContent.qml:81
377msgid "Click here to finish."
377msgid "Click here to finish."
378−msgstr ""
378+msgstr "Pulse aquí para finalizar."
379
379
380#: qml/Tutorial/TutorialLeftFinish.qml:24
380#: qml/Tutorial/TutorialLeftFinish.qml:24
381msgid "These are the shortcuts to favorite apps"
381msgid "These are the shortcuts to favorite apps"
po/fi.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-25 07:55+0000\n"
11+"PO-Revision-Date: 2015-05-19 09:14+0000\n"
12"Last-Translator: Jiri Grönroos <Unknown>\n"
12"Last-Translator: Jiri Grönroos <Unknown>\n"
13"Language-Team: Finnish <fi@li.org>\n"
13"Language-Team: Finnish <fi@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"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-05-22 05:09+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
19+"X-Generator: Launchpad (build 17514)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -373,7 +373,7 @@ msgstr "Napauta tästä jatkaaksesi."
373
373
374#: qml/Tutorial/TutorialContent.qml:81
374#: qml/Tutorial/TutorialContent.qml:81
375msgid "Click here to finish."
375msgid "Click here to finish."
376−msgstr ""
376+msgstr "Viimeistele napsauttamalla tästä."
377
377
378#: qml/Tutorial/TutorialLeftFinish.qml:24
378#: qml/Tutorial/TutorialLeftFinish.qml:24
379msgid "These are the shortcuts to favorite apps"
379msgid "These are the shortcuts to favorite apps"
po/fr.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"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"
12"Last-Translator: Anne <anneonyme017@gmail.com>\n"
12"Last-Translator: Anne <anneonyme017@gmail.com>\n"
13"Language-Team: French <fr@li.org>\n"
13"Language-Team: French <fr@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
17"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-05-11 05:41+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
19+"X-Generator: Launchpad (build 17487)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -377,7 +377,7 @@ msgstr "Appuyez ici pour continuer."
377
377
378#: qml/Tutorial/TutorialContent.qml:81
378#: qml/Tutorial/TutorialContent.qml:81
379msgid "Click here to finish."
379msgid "Click here to finish."
380−msgstr ""
380+msgstr "Cliquez ici pour terminer."
381
381
382#: qml/Tutorial/TutorialLeftFinish.qml:24
382#: qml/Tutorial/TutorialLeftFinish.qml:24
383msgid "These are the shortcuts to favorite apps"
383msgid "These are the shortcuts to favorite apps"
po/gd.po+4−4
@@ -8,7 +8,7 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"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"
12"Last-Translator: GunChleoc <Unknown>\n"
12"Last-Translator: GunChleoc <Unknown>\n"
13"Language-Team: Fòram na Gàidhlig\n"
13"Language-Team: Fòram na Gàidhlig\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : "
17"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : "
18"(n > 2 && n < 20) ? 2 : 3;\n"
18"(n > 2 && n < 20) ? 2 : 3;\n"
19−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19+"X-Launchpad-Export-Date: 2015-05-02 05:29+0000\n"
20−"X-Generator: Launchpad (build 17430)\n"
20+"X-Generator: Launchpad (build 17474)\n"
21"Language: gd\n"
21"Language: gd\n"
22
22
23#: plugins/LightDM/Greeter.cpp:112
23#: plugins/LightDM/Greeter.cpp:112
@@ -169,7 +169,7 @@ msgstr "Lorg"
169#: qml/Dash/PageHeader.qml:269
169#: qml/Dash/PageHeader.qml:269
170msgctxt "Button: Open the Ubuntu Store"
170msgctxt "Button: Open the Ubuntu Store"
171msgid "Store"
171msgid "Store"
172−msgstr "A' bhùth"
172+msgstr "Am bùth"
173
173
174#: qml/Dash/PageHeader.qml:276
174#: qml/Dash/PageHeader.qml:276
175msgctxt "Button: Start a search in the current dash scope"
175msgctxt "Button: Start a search in the current dash scope"
po/hu.po+14−14
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"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-06-19 09:54+0000\n"
12"Last-Translator: Richard Somlói <ricsipontaz@gmail.com>\n"
12"Last-Translator: Richard Somlói <ricsipontaz@gmail.com>\n"
13"Language-Team: Hungarian <hu@li.org>\n"
13"Language-Team: Hungarian <hu@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"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-06-20 05:33+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
19+"X-Generator: Launchpad (build 17572)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -113,7 +113,7 @@ msgstr "Igen"
113#: qml/Components/Dialogs.qml:152
113#: qml/Components/Dialogs.qml:152
114msgctxt "Title: Power off/Restart dialog"
114msgctxt "Title: Power off/Restart dialog"
115msgid "Power"
115msgid "Power"
116−msgstr "Főkapcsoló"
116+msgstr "Menü"
117
117
118#: qml/Components/Dialogs.qml:153
118#: qml/Components/Dialogs.qml:153
119msgid ""
119msgid ""
@@ -239,11 +239,11 @@ msgstr "Koppintson a feloldáshoz"
239
239
240#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
240#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
241msgid "Sorry, incorrect passphrase."
241msgid "Sorry, incorrect passphrase."
242−msgstr "Sajnálom, helytelen jelmondat."
242+msgstr "Elnézést, helytelen jelmondat."
243
243
244#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
244#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
245msgid "Sorry, incorrect passcode."
245msgid "Sorry, incorrect passcode."
246−msgstr "Sajnálom, helytelen jelkód."
246+msgstr "Elnézést, helytelen jelkód."
247
247
248#: qml/Greeter/NarrowView.qml:55
248#: qml/Greeter/NarrowView.qml:55
249msgid "This will be your last attempt."
249msgid "This will be your last attempt."
@@ -281,11 +281,11 @@ msgstr "Jelkód megadása"
281#: qml/Greeter/NarrowView.qml:118
281#: qml/Greeter/NarrowView.qml:118
282#, qt-format
282#, qt-format
283msgid "Sorry, incorrect %1"
283msgid "Sorry, incorrect %1"
284−msgstr "Sajnálom, helytelen %1"
284+msgstr "Elnézést, helytelen %1"
285
285
286#: qml/Greeter/NarrowView.qml:119
286#: qml/Greeter/NarrowView.qml:119
287msgid "Sorry, incorrect passphrase"
287msgid "Sorry, incorrect passphrase"
288−msgstr "Sajnálom, helytelen jelmondat"
288+msgstr "Elnézést, helytelen jelmondat"
289
289
290#: qml/Greeter/NarrowView.qml:120
290#: qml/Greeter/NarrowView.qml:120
291msgid "Please re-enter"
291msgid "Please re-enter"
@@ -293,7 +293,7 @@ msgstr "Próbálja újra"
293
293
294#: qml/Greeter/NarrowView.qml:121
294#: qml/Greeter/NarrowView.qml:121
295msgid "Sorry, incorrect passcode"
295msgid "Sorry, incorrect passcode"
296−msgstr "Sajnálom, helytelen jelkód"
296+msgstr "Elnézést, helytelen jelkód"
297
297
298#: qml/Notifications/NotificationMenuItemFactory.qml:128
298#: qml/Notifications/NotificationMenuItemFactory.qml:128
299msgid "Show password"
299msgid "Show password"
@@ -354,7 +354,7 @@ msgstr "Ez a művelet másként viselkedhet a különböző alkalmazásoknál"
354
354
355#: qml/Tutorial/TutorialBottomFinish.qml:25
355#: qml/Tutorial/TutorialBottomFinish.qml:25
356msgid "Tap here to finish."
356msgid "Tap here to finish."
357−msgstr "Koppintson a befejezéshez."
357+msgstr "Koppintson ide a befejezéshez."
358
358
359#: qml/Tutorial/TutorialBottom.qml:28
359#: qml/Tutorial/TutorialBottom.qml:28
360msgid "Open special menus"
360msgid "Open special menus"
@@ -371,11 +371,11 @@ msgstr "Koppintson ide a folytatáshoz."
371
371
372#: qml/Tutorial/TutorialContent.qml:81
372#: qml/Tutorial/TutorialContent.qml:81
373msgid "Click here to finish."
373msgid "Click here to finish."
374−msgstr ""
374+msgstr "Kattintson ide a befejezéshez."
375
375
376#: qml/Tutorial/TutorialLeftFinish.qml:24
376#: qml/Tutorial/TutorialLeftFinish.qml:24
377msgid "These are the shortcuts to favorite apps"
377msgid "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"
379
379
380#: qml/Tutorial/TutorialLeft.qml:26
380#: qml/Tutorial/TutorialLeft.qml:26
381msgid "Open the launcher"
381msgid "Open the launcher"
@@ -395,7 +395,7 @@ msgstr "Próbálja újra."
395
395
396#: qml/Tutorial/TutorialRight.qml:31
396#: qml/Tutorial/TutorialRight.qml:31
397msgid "To view open apps"
397msgid "To view open apps"
398−msgstr "Nyitott alkalmazások megtekintéséhez"
398+msgstr "A nyitott alkalmazások megtekintéséhez"
399
399
400#: qml/Tutorial/TutorialRight.qml:32
400#: qml/Tutorial/TutorialRight.qml:32
401msgid "Long swipe from the right edge."
401msgid "Long swipe from the right edge."
@@ -403,7 +403,7 @@ msgstr "Húzza az ujját hosszan a kijelző jobboldalától indulva."
403
403
404#: qml/Tutorial/TutorialRight.qml:122
404#: qml/Tutorial/TutorialRight.qml:122
405msgid "View all your running tasks."
405msgid "View all your running tasks."
406−msgstr "Minden futó feladat megtekintése."
406+msgstr "Az összes futó alkalmazás megtekinthető."
407
407
408#: qml/Wizard/Page.qml:89
408#: qml/Wizard/Page.qml:89
409msgctxt "Button: Go back one page in the Wizard"
409msgctxt "Button: Go back one page in the Wizard"
po/ia.ponew file+684
@@ -0,0 +1,684 @@
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-06-08 18:58+0000\n"
12+"Last-Translator: karm <melo@carmu.com>\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-06-09 05:42+0000\n"
19+"X-Generator: Launchpad (build 17549)\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 a 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 tenta 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 "Clauder de session"
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 "Blocar"
68+
69+#: qml/Components/Dialogs.qml:79
70+msgctxt "Button: Log out from the system"
71+msgid "Log Out"
72+msgstr "Claude session"
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 "Stoppar"
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 "Reinitiar"
92+
93+#: qml/Components/Dialogs.qml:109
94+msgctxt "Button: Shut down the system"
95+msgid "Shut down"
96+msgstr "Stoppar"
97+
98+#: qml/Components/Dialogs.qml:129
99+msgctxt "Title: Reboot dialog"
100+msgid "Reboot"
101+msgstr "Reinitiar"
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 "Energia"
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 "Stoppar le energia"
132+
133+#: qml/Components/Dialogs.qml:164
134+msgctxt "Button: Restart the system"
135+msgid "Restart"
136+msgstr "Reinitiar"
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 "Cerca"
162+
163+#: qml/Dash/PageHeader.qml:269
164+msgctxt "Button: Open the Ubuntu Store"
165+msgid "Store"
166+msgstr "Salva"
167+
168+#: qml/Dash/PageHeader.qml:276
169+msgctxt "Button: Start a search in the current dash scope"
170+msgid "Search"
171+msgstr "Cerca"
172+
173+#: qml/Dash/PageHeader.qml:286
174+msgctxt "Button: Show the current dash scope settings"
175+msgid "Settings"
176+msgstr "Configurationes"
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 "Habilita le datos del location"
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 "Re-provar"
237+
238+#: qml/Greeter/LoginList.qml:237
239+msgid "Tap to unlock"
240+msgstr "Pulsa pro liberar"
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 "Monstrar 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 "Responde"
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 "Iste action ha effectos differente pro applicationes differente"
356+
357+#: qml/Tutorial/TutorialBottomFinish.qml:25
358+msgid "Tap here to finish."
359+msgstr "Pulsa ci pro finir"
360+
361+#: qml/Tutorial/TutorialBottom.qml:28
362+msgid "Open special menus"
363+msgstr "Aperi le menus special"
364+
365+#: qml/Tutorial/TutorialBottom.qml:29
366+msgid "Swipe up from the bottom edge."
367+msgstr "Stria in alto ex le bordo basse."
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 "Pulsa ci pro continuar"
373+
374+#: qml/Tutorial/TutorialContent.qml:81
375+msgid "Click here to finish."
376+msgstr "Clicca ci pro finir"
377+
378+#: qml/Tutorial/TutorialLeftFinish.qml:24
379+msgid "These are the shortcuts to favorite apps"
380+msgstr "Iste es le vias breve al applicationes favorite"
381+
382+#: qml/Tutorial/TutorialLeft.qml:26
383+msgid "Open the launcher"
384+msgstr "Aperi le lanceator"
385+
386+#: qml/Tutorial/TutorialLeft.qml:27
387+msgid "Short swipe from the left edge."
388+msgstr "Stria curte ex le bordo sinistre."
389+
390+#: qml/Tutorial/TutorialPage.qml:178
391+msgid "You almost got it!"
392+msgstr "Tu ha quasi obtenite lo!"
393+
394+#: qml/Tutorial/TutorialPage.qml:195
395+msgid "Try again."
396+msgstr "Prova de novo"
397+
398+#: qml/Tutorial/TutorialRight.qml:31
399+msgid "To view open apps"
400+msgstr "Aperi le applicationes pro vider"
401+
402+#: qml/Tutorial/TutorialRight.qml:32
403+msgid "Long swipe from the right edge."
404+msgstr "Stria longemente ex le bordo dextre."
405+
406+#: qml/Tutorial/TutorialRight.qml:122
407+msgid "View all your running tasks."
408+msgstr "Vide tote tu tasks fluente"
409+
410+#: qml/Wizard/Page.qml:89
411+msgctxt "Button: Go back one page in the Wizard"
412+msgid "Back"
413+msgstr "Retro"
414+
415+#: qml/Wizard/Pages/10-welcome.qml:27
416+msgid "Hi!"
417+msgstr "Salute!"
418+
419+#: qml/Wizard/Pages/10-welcome.qml:44
420+msgid "Welcome to your Ubuntu phone."
421+msgstr "Benvenite a tu telephono Ubuntu."
422+
423+#: qml/Wizard/Pages/10-welcome.qml:52
424+msgid "Let’s get started."
425+msgstr "Que nos initia."
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 "Continua"
433+
434+#: qml/Wizard/Pages/20-sim.qml:25
435+msgid "Add a SIM card and restart your device"
436+msgstr "Adde un carta SIM e reinitia tu dispositivo"
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+"Sin illo, tu non es habile a facer appellos o usar le messages de texto."
442+
443+#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
444+msgid "Skip"
445+msgstr "Salta"
446+
447+#: qml/Wizard/Pages/30-passwd-type.qml:39
448+msgid "Lock security"
449+msgstr "Bloco de securitate"
450+
451+#: qml/Wizard/Pages/30-passwd-type.qml:74
452+msgid "Please select how you’d like to unlock your phone."
453+msgstr "Per favor selige in qual maniera disblocar tu telephono."
454+
455+#: qml/Wizard/Pages/30-passwd-type.qml:97
456+msgctxt "Label: Type of security method"
457+msgid "Swipe"
458+msgstr "Striar"
459+
460+#: qml/Wizard/Pages/30-passwd-type.qml:98
461+msgctxt "Label: Description of security method"
462+msgid "No security"
463+msgstr "Nulle securitate"
464+
465+#: qml/Wizard/Pages/30-passwd-type.qml:100
466+msgctxt "Label: Type of security method"
467+msgid "Passcode"
468+msgstr "Codice-contrasigno"
469+
470+#: qml/Wizard/Pages/30-passwd-type.qml:101
471+msgctxt "Label: Description of security method"
472+msgid "4 digits only"
473+msgstr "4 cifras sol"
474+
475+#: qml/Wizard/Pages/30-passwd-type.qml:103
476+msgctxt "Label: Type of security method"
477+msgid "Passphrase"
478+msgstr "Phrase-contrasigno"
479+
480+#: qml/Wizard/Pages/30-passwd-type.qml:104
481+msgctxt "Label: Description of security method"
482+msgid "Numbers and letters"
483+msgstr "Numeros e litteras"
484+
485+#: qml/Wizard/Pages/40-wifi.qml:29
486+msgid "Connect to Wi‑Fi"
487+msgstr "Connecter al Wi‑Fi"
488+
489+#: qml/Wizard/Pages/40-wifi.qml:168
490+msgid "Available networks…"
491+msgstr "Retes disponibile..."
492+
493+#: qml/Wizard/Pages/40-wifi.qml:169
494+msgid "No available networks."
495+msgstr "Nulle rete disponibile."
496+
497+#: qml/Wizard/Pages/50-location.qml:27
498+msgid "Location"
499+msgstr "Location"
500+
501+#: qml/Wizard/Pages/50-location.qml:62
502+msgid "Let the phone detect your location:"
503+msgstr "Lassar le telephono disvelar tu position:"
504+
505+#: qml/Wizard/Pages/50-location.qml:69
506+msgid "Using GPS only (less accurate)"
507+msgstr "Usar sol le GPS (minus accurate)"
508+
509+#: qml/Wizard/Pages/50-location.qml:86
510+msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
511+msgstr ""
512+"Usar le GPS, le Wi-Fi in modo anonyme e le informationes del rete cellular "
513+"(recommendate)"
514+
515+#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
516+#: qml/Wizard/Pages/50-location.qml:103
517+msgid ""
518+"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
519+"conditions</a>."
520+msgstr ""
521+"Per le selection de iste option tu esser de accordo con le Nokia HERE <a "
522+"href='#'>terminos e conditiones</a>."
523+
524+#: qml/Wizard/Pages/50-location.qml:112
525+msgid "Not at all"
526+msgstr "No, del toto"
527+
528+#: qml/Wizard/Pages/50-location.qml:124
529+msgid "You can change your mind later in <b>System Settings</b>."
530+msgstr ""
531+"Tu pote cambiar tu desiderata plus tarde in <b>Configurationes del "
532+"systema</b>."
533+
534+#: qml/Wizard/Pages/60-reporting.qml:24
535+msgid "Improving your experience"
536+msgstr "Affinamento de tu experientia"
537+
538+#: qml/Wizard/Pages/60-reporting.qml:36
539+msgid ""
540+"Your phone is set up to automatically report errors to Canonical and its "
541+"partners, the makers of the operating system."
542+msgstr ""
543+"Tu telephono es configurate a reportar automaticamente le errores a "
544+"Canonical e su socios, le fabricantes del systema operative."
545+
546+#: qml/Wizard/Pages/60-reporting.qml:43
547+msgid ""
548+"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
549+"Privacy</b>"
550+msgstr ""
551+"Isto pote esser impedite in <b>Preparationes del systema</b> sub "
552+"<b>Securitate &amp; Confidentialitate</b>"
553+
554+#: qml/Wizard/Pages/80-finished.qml:24
555+msgid "All done"
556+msgstr "Toto preste"
557+
558+#: qml/Wizard/Pages/80-finished.qml:39
559+msgid "Nice work!"
560+msgstr "Belle obra!"
561+
562+#: qml/Wizard/Pages/80-finished.qml:46
563+msgid "Your phone is now ready to use."
564+msgstr "Tu telephono es ora preste al uso."
565+
566+#: qml/Wizard/Pages/80-finished.qml:53
567+msgid "Finish"
568+msgstr "Fin"
569+
570+#: qml/Wizard/Pages/here-terms.qml:27
571+msgid "Terms & Conditions"
572+msgstr "Terminos & Conditiones"
573+
574+#: qml/Wizard/Pages/passwd-confirm.qml:49
575+msgid "Confirm passphrase"
576+msgstr "Confirma le phrase-contrasigno"
577+
578+#: qml/Wizard/Pages/passwd-confirm.qml:50
579+msgid "Confirm passcode"
580+msgstr "Confirma le codice-contrasigno"
581+
582+#: qml/Wizard/Pages/passwd-confirm.qml:53
583+#: qml/Wizard/Pages/passwd-confirm.qml:54
584+msgid "Please try again."
585+msgstr "Reproba per favor."
586+
587+#: qml/Wizard/Pages/passwd-set.qml:61
588+msgid "Choose your passcode"
589+msgstr "Selige tu codice-contrasigno"
590+
591+#: qml/Wizard/Pages/passwd-set.qml:68
592+msgid "Passphrase must be 4 characters long"
593+msgstr "Le phrase-contrasigno debe esser longe 4 characteres"
594+
595+#: qml/Wizard/StackButton.qml:39
596+#, qt-format
597+msgid "〈 %1"
598+msgstr "〈 %1"
599+
600+#: qml/Wizard/StackButton.qml:42
601+#, qt-format
602+msgid "%1 〉"
603+msgstr "%1 〉"
604+
605+#~ msgid "Log out"
606+#~ msgstr "Clauder le Session"
607+
608+#~ msgid "Reboot"
609+#~ msgstr "Reinitiar"
610+
611+#~ msgid "Shut down"
612+#~ msgstr "Stoppar"
613+
614+#~ msgid "Power"
615+#~ msgstr "Energia"
616+
617+#~ msgid "Restart"
618+#~ msgstr "Re-Initiar"
619+
620+#~ msgid "Power off"
621+#~ msgstr "Stoppar le energia"
622+
623+#~ msgid "Top edge"
624+#~ msgstr "Bordo superior"
625+
626+#~ msgid "Try swiping from the top edge to access the indicators"
627+#~ msgstr "Proba striar le bordo superior pro acceder le indicatores"
628+
629+#~ msgid "Close"
630+#~ msgstr "Clauder"
631+
632+#~ msgid "Left edge"
633+#~ msgstr "Bordo sinistre"
634+
635+#~ msgid "Try swiping from the right edge to unlock the phone"
636+#~ msgstr "Proba striar le bordo dextere pro liberar le telephono"
637+
638+#~ msgid "Skip intro"
639+#~ msgstr "Saltar le introduction"
640+
641+#~ msgid "Right edge"
642+#~ msgstr "Bordo dextere"
643+
644+#~ msgid "Swipe up again to close the settings screen"
645+#~ msgstr "Stria novemente pro clauder le pagina del preparationes"
646+
647+#~ msgid "Swipe from the left to reveal the launcher for quick access to apps"
648+#~ msgstr ""
649+#~ "Striar ab le sinistra pro revelar le lanceator pro un celere accesso al "
650+#~ "applicationes"
651+
652+#~ msgid "Well done"
653+#~ msgstr "Ben!"
654+
655+#~ msgid "Search"
656+#~ msgstr "Cercar"
657+
658+#~ msgid "Store"
659+#~ msgstr "Salvar"
660+
661+#~ msgid "Settings"
662+#~ msgstr "Preparationes"
663+
664+#~ msgid "Confirm"
665+#~ msgstr "Confirmar"
666+
667+#~ msgid "Type or say a command"
668+#~ msgstr "Scriber o dicer un commando"
669+
670+#~ msgid "Loading. Please Wait..."
671+#~ msgstr "Cargante. Attender per favor..."
672+
673+#~ msgid "Speak Now..."
674+#~ msgstr "Parlar Ora..."
675+
676+#~ msgid "Speaking..."
677+#~ msgstr "Parlante..."
678+
679+#~ msgid ""
680+#~ "You have now mastered the edge gestures and can start using the "
681+#~ "phone<br><br>Tap on the screen to start"
682+#~ msgstr ""
683+#~ "Tu es ora maestrate le gestos del bordo e pote initiar usar le "
684+#~ "telephono<br><br>colpar sur le schermo pro initiar"
po/it.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-21 12:42+0000\n"
11+"PO-Revision-Date: 2015-06-17 09:56+0000\n"
12"Last-Translator: Claudio Arseni <claudio.arseni@gmail.com>\n"
12"Last-Translator: Claudio Arseni <claudio.arseni@gmail.com>\n"
13"Language-Team: Italian <it@li.org>\n"
13"Language-Team: Italian <it@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"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-06-19 05:01+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
19+"X-Generator: Launchpad (build 17570)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -375,7 +375,7 @@ msgstr "Toccare qui per continuare."
375
375
376#: qml/Tutorial/TutorialContent.qml:81
376#: qml/Tutorial/TutorialContent.qml:81
377msgid "Click here to finish."
377msgid "Click here to finish."
378−msgstr ""
378+msgstr "Fare clic qui per terminare."
379
379
380#: qml/Tutorial/TutorialLeftFinish.qml:24
380#: qml/Tutorial/TutorialLeftFinish.qml:24
381msgid "These are the shortcuts to favorite apps"
381msgid "These are the shortcuts to favorite apps"
po/nb.po+8−8
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-20 14:03+0000\n"
11+"PO-Revision-Date: 2015-06-03 02:43+0000\n"
12−"Last-Translator: Åka Sikrom <Unknown>\n"
12+"Last-Translator: Eirik Garatun <eikknud@gmail.com>\n"
13"Language-Team: Norwegian Bokmal <nb@li.org>\n"
13"Language-Team: Norwegian Bokmal <nb@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"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-06-05 05:58+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
19+"X-Generator: Launchpad (build 17540)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -163,17 +163,17 @@ msgstr "Søk"
163#: qml/Dash/PageHeader.qml:269
163#: qml/Dash/PageHeader.qml:269
164msgctxt "Button: Open the Ubuntu Store"
164msgctxt "Button: Open the Ubuntu Store"
165msgid "Store"
165msgid "Store"
166−msgstr ""
166+msgstr "Butikk"
167
167
168#: qml/Dash/PageHeader.qml:276
168#: qml/Dash/PageHeader.qml:276
169msgctxt "Button: Start a search in the current dash scope"
169msgctxt "Button: Start a search in the current dash scope"
170msgid "Search"
170msgid "Search"
171−msgstr ""
171+msgstr "Søk"
172
172
173#: qml/Dash/PageHeader.qml:286
173#: qml/Dash/PageHeader.qml:286
174msgctxt "Button: Show the current dash scope settings"
174msgctxt "Button: Show the current dash scope settings"
175msgid "Settings"
175msgid "Settings"
176−msgstr ""
176+msgstr "Innstillinger"
177
177
178#: qml/Dash/PageHeader.qml:293
178#: qml/Dash/PageHeader.qml:293
179msgid "Remove from Favorites"
179msgid "Remove from Favorites"
@@ -373,7 +373,7 @@ msgstr "Trykk her for å fortsette."
373
373
374#: qml/Tutorial/TutorialContent.qml:81
374#: qml/Tutorial/TutorialContent.qml:81
375msgid "Click here to finish."
375msgid "Click here to finish."
376−msgstr ""
376+msgstr "Trykk her for å fullføre."
377
377
378#: qml/Tutorial/TutorialLeftFinish.qml:24
378#: qml/Tutorial/TutorialLeftFinish.qml:24
379msgid "These are the shortcuts to favorite apps"
379msgid "These are the shortcuts to favorite apps"
po/nl.po+4−4
@@ -8,14 +8,14 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-28 21:07+0000\n"
11+"PO-Revision-Date: 2015-04-25 13:27+0000\n"
12−"Last-Translator: Sergi <philipsen.sergi@gmail.com>\n"
12+"Last-Translator: rob <linuxned@gmail.com>\n"
13"Language-Team: Dutch <nl@li.org>\n"
13"Language-Team: Dutch <nl@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"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"
19"X-Generator: Launchpad (build 17430)\n"
19"X-Generator: Launchpad (build 17430)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
@@ -377,7 +377,7 @@ msgstr "Tik hier om door te gaan."
377
377
378#: qml/Tutorial/TutorialContent.qml:81
378#: qml/Tutorial/TutorialContent.qml:81
379msgid "Click here to finish."
379msgid "Click here to finish."
380−msgstr ""
380+msgstr "Klik hier om te stoppen."
381
381
382#: qml/Tutorial/TutorialLeftFinish.qml:24
382#: qml/Tutorial/TutorialLeftFinish.qml:24
383msgid "These are the shortcuts to favorite apps"
383msgid "These are the shortcuts to favorite apps"
po/nn.ponew file+584
@@ -0,0 +1,584 @@
1+# Norwegian Nynorsk translation for unity8
2+# Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11+"PO-Revision-Date: 2015-05-12 11:32+0000\n"
12+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13+"Language-Team: Norwegian Nynorsk <nn@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2015-05-13 05:31+0000\n"
19+"X-Generator: Launchpad (build 17493)\n"
20+
21+#: plugins/LightDM/Greeter.cpp:112
22+msgid "Password: "
23+msgstr ""
24+
25+#: plugins/Unity/Launcher/launcheritem.cpp:45
26+#: plugins/Unity/Launcher/launcheritem.cpp:100
27+msgid "Pin shortcut"
28+msgstr ""
29+
30+#: plugins/Unity/Launcher/launcheritem.cpp:100
31+msgid "Unpin shortcut"
32+msgstr ""
33+
34+#: qml/Components/DelayedLockscreen.qml:49
35+msgid "Device Locked"
36+msgstr ""
37+
38+#: qml/Components/DelayedLockscreen.qml:64
39+msgid "You have been locked out due to too many failed passphrase attempts."
40+msgstr ""
41+
42+#: qml/Components/DelayedLockscreen.qml:65
43+msgid "You have been locked out due to too many failed passcode attempts."
44+msgstr ""
45+
46+#: qml/Components/DelayedLockscreen.qml:74
47+#, qt-format
48+msgid "Please wait %1 minute and then try again…"
49+msgid_plural "Please wait %1 minutes and then try again…"
50+msgstr[0] ""
51+msgstr[1] ""
52+
53+#: qml/Components/Dialogs.qml:69
54+msgctxt "Title: Lock/Log out dialog"
55+msgid "Log out"
56+msgstr ""
57+
58+#: qml/Components/Dialogs.qml:70
59+msgid "Are you sure you want to log out?"
60+msgstr ""
61+
62+#: qml/Components/Dialogs.qml:72
63+msgctxt "Button: Lock the system"
64+msgid "Lock"
65+msgstr ""
66+
67+#: qml/Components/Dialogs.qml:79
68+msgctxt "Button: Log out from the system"
69+msgid "Log Out"
70+msgstr ""
71+
72+#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
73+#: qml/Components/Dialogs.qml:173
74+msgid "Cancel"
75+msgstr ""
76+
77+#: qml/Components/Dialogs.qml:98
78+msgctxt "Title: Reboot/Shut down dialog"
79+msgid "Shut down"
80+msgstr ""
81+
82+#: qml/Components/Dialogs.qml:99
83+msgid "Are you sure you want to shut down?"
84+msgstr ""
85+
86+#: qml/Components/Dialogs.qml:101
87+msgctxt "Button: Reboot the system"
88+msgid "Reboot"
89+msgstr ""
90+
91+#: qml/Components/Dialogs.qml:109
92+msgctxt "Button: Shut down the system"
93+msgid "Shut down"
94+msgstr ""
95+
96+#: qml/Components/Dialogs.qml:129
97+msgctxt "Title: Reboot dialog"
98+msgid "Reboot"
99+msgstr ""
100+
101+#: qml/Components/Dialogs.qml:130
102+msgid "Are you sure you want to reboot?"
103+msgstr ""
104+
105+#: qml/Components/Dialogs.qml:132
106+msgid "No"
107+msgstr ""
108+
109+#: qml/Components/Dialogs.qml:138
110+msgid "Yes"
111+msgstr ""
112+
113+#: qml/Components/Dialogs.qml:152
114+msgctxt "Title: Power off/Restart dialog"
115+msgid "Power"
116+msgstr ""
117+
118+#: qml/Components/Dialogs.qml:153
119+msgid ""
120+"Are you sure you would like\n"
121+"to power off?"
122+msgstr ""
123+
124+#: qml/Components/Dialogs.qml:155
125+msgctxt "Button: Power off the system"
126+msgid "Power off"
127+msgstr ""
128+
129+#: qml/Components/Dialogs.qml:164
130+msgctxt "Button: Restart the system"
131+msgid "Restart"
132+msgstr ""
133+
134+#: qml/Components/Lockscreen.qml:245
135+msgid "Return to Call"
136+msgstr ""
137+
138+#: qml/Components/Lockscreen.qml:245
139+msgid "Emergency Call"
140+msgstr ""
141+
142+#: qml/Components/Lockscreen.qml:277
143+msgid "OK"
144+msgstr ""
145+
146+#: qml/Dash/GenericScopeView.qml:532 qml/Dash/GenericScopeView.qml:690
147+msgid "See less"
148+msgstr ""
149+
150+#: qml/Dash/GenericScopeView.qml:532
151+msgid "See all"
152+msgstr ""
153+
154+#: qml/Dash/GenericScopeView.qml:594
155+msgctxt "Label: Hint for dash search line edit"
156+msgid "Search"
157+msgstr ""
158+
159+#: qml/Dash/PageHeader.qml:269
160+msgctxt "Button: Open the Ubuntu Store"
161+msgid "Store"
162+msgstr ""
163+
164+#: qml/Dash/PageHeader.qml:276
165+msgctxt "Button: Start a search in the current dash scope"
166+msgid "Search"
167+msgstr ""
168+
169+#: qml/Dash/PageHeader.qml:286
170+msgctxt "Button: Show the current dash scope settings"
171+msgid "Settings"
172+msgstr ""
173+
174+#: qml/Dash/PageHeader.qml:293
175+msgid "Remove from Favorites"
176+msgstr ""
177+
178+#: qml/Dash/PageHeader.qml:293
179+msgid "Add to Favorites"
180+msgstr ""
181+
182+#: qml/Dash/Previews/PreviewActionCombo.qml:35
183+msgid "More..."
184+msgstr ""
185+
186+#: qml/Dash/Previews/PreviewActionCombo.qml:35
187+msgid "Less..."
188+msgstr ""
189+
190+#: qml/Dash/Previews/PreviewRatingInput.qml:81
191+msgid "Rate this"
192+msgstr ""
193+
194+#: qml/Dash/Previews/PreviewRatingInput.qml:126
195+msgid "Add a review"
196+msgstr ""
197+
198+#: qml/Dash/Previews/PreviewRatingInput.qml:166
199+msgid "Send"
200+msgstr ""
201+
202+#: qml/Dash/PullToRefreshScopeStyle.qml:55
203+msgid "Pull to refresh…"
204+msgstr ""
205+
206+#: qml/Dash/PullToRefreshScopeStyle.qml:60
207+msgid "Release to refresh…"
208+msgstr ""
209+
210+#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
211+msgid "Enable location data"
212+msgstr ""
213+
214+#: qml/Dash/ScopesList.qml:66
215+msgid "Manage"
216+msgstr ""
217+
218+#: qml/Dash/ScopesList.qml:112
219+msgid "Home"
220+msgstr ""
221+
222+#: qml/Dash/ScopesList.qml:113
223+msgid "Also installed"
224+msgstr ""
225+
226+#: qml/Greeter/CoverPage.qml:107
227+msgid "Unlock"
228+msgstr ""
229+
230+#: qml/Greeter/LoginList.qml:236
231+msgid "Retry"
232+msgstr ""
233+
234+#: qml/Greeter/LoginList.qml:237
235+msgid "Tap to unlock"
236+msgstr ""
237+
238+#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
239+msgid "Sorry, incorrect passphrase."
240+msgstr ""
241+
242+#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
243+msgid "Sorry, incorrect passcode."
244+msgstr ""
245+
246+#: qml/Greeter/NarrowView.qml:55
247+msgid "This will be your last attempt."
248+msgstr ""
249+
250+#: qml/Greeter/NarrowView.qml:57
251+msgid ""
252+"If passphrase is entered incorrectly, your phone will conduct a factory "
253+"reset and all personal data will be deleted."
254+msgstr ""
255+
256+#: qml/Greeter/NarrowView.qml:58
257+msgid ""
258+"If passcode is entered incorrectly, your phone will conduct a factory reset "
259+"and all personal data will be deleted."
260+msgstr ""
261+
262+#: qml/Greeter/NarrowView.qml:115
263+#, qt-format
264+msgid "Enter %1"
265+msgstr ""
266+
267+#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
268+msgid "Enter passphrase"
269+msgstr ""
270+
271+#: qml/Greeter/NarrowView.qml:117
272+msgid "Enter passcode"
273+msgstr ""
274+
275+#: qml/Greeter/NarrowView.qml:118
276+#, qt-format
277+msgid "Sorry, incorrect %1"
278+msgstr ""
279+
280+#: qml/Greeter/NarrowView.qml:119
281+msgid "Sorry, incorrect passphrase"
282+msgstr ""
283+
284+#: qml/Greeter/NarrowView.qml:120
285+msgid "Please re-enter"
286+msgstr ""
287+
288+#: qml/Greeter/NarrowView.qml:121
289+msgid "Sorry, incorrect passcode"
290+msgstr ""
291+
292+#: qml/Notifications/NotificationMenuItemFactory.qml:128
293+msgid "Show password"
294+msgstr ""
295+
296+#: qml/Panel/ActiveCallHint.qml:79
297+msgid "Tap to return to call..."
298+msgstr ""
299+
300+#: qml/Panel/ActiveCallHint.qml:92
301+msgid "Conference"
302+msgstr ""
303+
304+#: qml/Panel/Indicators/MenuItemFactory.qml:639
305+msgid "Nothing is playing"
306+msgstr ""
307+
308+#: qml/Panel/Indicators/MenuItemFactory.qml:787
309+msgid "In queue…"
310+msgstr ""
311+
312+#: qml/Panel/Indicators/MenuItemFactory.qml:791
313+msgid "Downloading"
314+msgstr ""
315+
316+#: qml/Panel/Indicators/MenuItemFactory.qml:793
317+msgid "Paused, tap to resume"
318+msgstr ""
319+
320+#: qml/Panel/Indicators/MenuItemFactory.qml:795
321+msgid "Canceled"
322+msgstr ""
323+
324+#: qml/Panel/Indicators/MenuItemFactory.qml:797
325+msgid "Finished"
326+msgstr ""
327+
328+#: qml/Panel/Indicators/MenuItemFactory.qml:799
329+msgid "Failed, tap to retry"
330+msgstr ""
331+
332+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
333+msgctxt "Label: Hint in message indicator line edit"
334+msgid "Reply"
335+msgstr ""
336+
337+#: qml/Panel/Indicators/ModemInfoItem.qml:105
338+msgid "Unlock SIM"
339+msgstr ""
340+
341+#: qml/Panel/Indicators/RoamingIndication.qml:27
342+msgid "Roaming"
343+msgstr ""
344+
345+#: qml/Tutorial/TutorialBottomFinish.qml:24
346+msgid "This action does different things for different apps"
347+msgstr ""
348+
349+#: qml/Tutorial/TutorialBottomFinish.qml:25
350+msgid "Tap here to finish."
351+msgstr ""
352+
353+#: qml/Tutorial/TutorialBottom.qml:28
354+msgid "Open special menus"
355+msgstr ""
356+
357+#: qml/Tutorial/TutorialBottom.qml:29
358+msgid "Swipe up from the bottom edge."
359+msgstr ""
360+
361+#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
362+#: qml/Tutorial/TutorialRight.qml:144
363+msgid "Tap here to continue."
364+msgstr ""
365+
366+#: qml/Tutorial/TutorialContent.qml:81
367+msgid "Click here to finish."
368+msgstr ""
369+
370+#: qml/Tutorial/TutorialLeftFinish.qml:24
371+msgid "These are the shortcuts to favorite apps"
372+msgstr ""
373+
374+#: qml/Tutorial/TutorialLeft.qml:26
375+msgid "Open the launcher"
376+msgstr ""
377+
378+#: qml/Tutorial/TutorialLeft.qml:27
379+msgid "Short swipe from the left edge."
380+msgstr ""
381+
382+#: qml/Tutorial/TutorialPage.qml:178
383+msgid "You almost got it!"
384+msgstr ""
385+
386+#: qml/Tutorial/TutorialPage.qml:195
387+msgid "Try again."
388+msgstr ""
389+
390+#: qml/Tutorial/TutorialRight.qml:31
391+msgid "To view open apps"
392+msgstr ""
393+
394+#: qml/Tutorial/TutorialRight.qml:32
395+msgid "Long swipe from the right edge."
396+msgstr ""
397+
398+#: qml/Tutorial/TutorialRight.qml:122
399+msgid "View all your running tasks."
400+msgstr ""
401+
402+#: qml/Wizard/Page.qml:89
403+msgctxt "Button: Go back one page in the Wizard"
404+msgid "Back"
405+msgstr ""
406+
407+#: qml/Wizard/Pages/10-welcome.qml:27
408+msgid "Hi!"
409+msgstr ""
410+
411+#: qml/Wizard/Pages/10-welcome.qml:44
412+msgid "Welcome to your Ubuntu phone."
413+msgstr ""
414+
415+#: qml/Wizard/Pages/10-welcome.qml:52
416+msgid "Let’s get started."
417+msgstr ""
418+
419+#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
420+#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
421+#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
422+#: qml/Wizard/Pages/passwd-set.qml:90
423+msgid "Continue"
424+msgstr ""
425+
426+#: qml/Wizard/Pages/20-sim.qml:25
427+msgid "Add a SIM card and restart your device"
428+msgstr ""
429+
430+#: qml/Wizard/Pages/20-sim.qml:59
431+msgid "Without it, you won’t be able to make calls or use text messaging."
432+msgstr ""
433+
434+#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
435+msgid "Skip"
436+msgstr ""
437+
438+#: qml/Wizard/Pages/30-passwd-type.qml:39
439+msgid "Lock security"
440+msgstr ""
441+
442+#: qml/Wizard/Pages/30-passwd-type.qml:74
443+msgid "Please select how you’d like to unlock your phone."
444+msgstr ""
445+
446+#: qml/Wizard/Pages/30-passwd-type.qml:97
447+msgctxt "Label: Type of security method"
448+msgid "Swipe"
449+msgstr ""
450+
451+#: qml/Wizard/Pages/30-passwd-type.qml:98
452+msgctxt "Label: Description of security method"
453+msgid "No security"
454+msgstr ""
455+
456+#: qml/Wizard/Pages/30-passwd-type.qml:100
457+msgctxt "Label: Type of security method"
458+msgid "Passcode"
459+msgstr ""
460+
461+#: qml/Wizard/Pages/30-passwd-type.qml:101
462+msgctxt "Label: Description of security method"
463+msgid "4 digits only"
464+msgstr ""
465+
466+#: qml/Wizard/Pages/30-passwd-type.qml:103
467+msgctxt "Label: Type of security method"
468+msgid "Passphrase"
469+msgstr ""
470+
471+#: qml/Wizard/Pages/30-passwd-type.qml:104
472+msgctxt "Label: Description of security method"
473+msgid "Numbers and letters"
474+msgstr ""
475+
476+#: qml/Wizard/Pages/40-wifi.qml:29
477+msgid "Connect to Wi‑Fi"
478+msgstr ""
479+
480+#: qml/Wizard/Pages/40-wifi.qml:168
481+msgid "Available networks…"
482+msgstr ""
483+
484+#: qml/Wizard/Pages/40-wifi.qml:169
485+msgid "No available networks."
486+msgstr ""
487+
488+#: qml/Wizard/Pages/50-location.qml:27
489+msgid "Location"
490+msgstr ""
491+
492+#: qml/Wizard/Pages/50-location.qml:62
493+msgid "Let the phone detect your location:"
494+msgstr ""
495+
496+#: qml/Wizard/Pages/50-location.qml:69
497+msgid "Using GPS only (less accurate)"
498+msgstr ""
499+
500+#: qml/Wizard/Pages/50-location.qml:86
501+msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
502+msgstr ""
503+
504+#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
505+#: qml/Wizard/Pages/50-location.qml:103
506+msgid ""
507+"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
508+"conditions</a>."
509+msgstr ""
510+
511+#: qml/Wizard/Pages/50-location.qml:112
512+msgid "Not at all"
513+msgstr ""
514+
515+#: qml/Wizard/Pages/50-location.qml:124
516+msgid "You can change your mind later in <b>System Settings</b>."
517+msgstr ""
518+
519+#: qml/Wizard/Pages/60-reporting.qml:24
520+msgid "Improving your experience"
521+msgstr ""
522+
523+#: qml/Wizard/Pages/60-reporting.qml:36
524+msgid ""
525+"Your phone is set up to automatically report errors to Canonical and its "
526+"partners, the makers of the operating system."
527+msgstr ""
528+
529+#: qml/Wizard/Pages/60-reporting.qml:43
530+msgid ""
531+"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
532+"Privacy</b>"
533+msgstr ""
534+
535+#: qml/Wizard/Pages/80-finished.qml:24
536+msgid "All done"
537+msgstr ""
538+
539+#: qml/Wizard/Pages/80-finished.qml:39
540+msgid "Nice work!"
541+msgstr ""
542+
543+#: qml/Wizard/Pages/80-finished.qml:46
544+msgid "Your phone is now ready to use."
545+msgstr ""
546+
547+#: qml/Wizard/Pages/80-finished.qml:53
548+msgid "Finish"
549+msgstr ""
550+
551+#: qml/Wizard/Pages/here-terms.qml:27
552+msgid "Terms & Conditions"
553+msgstr ""
554+
555+#: qml/Wizard/Pages/passwd-confirm.qml:49
556+msgid "Confirm passphrase"
557+msgstr ""
558+
559+#: qml/Wizard/Pages/passwd-confirm.qml:50
560+msgid "Confirm passcode"
561+msgstr ""
562+
563+#: qml/Wizard/Pages/passwd-confirm.qml:53
564+#: qml/Wizard/Pages/passwd-confirm.qml:54
565+msgid "Please try again."
566+msgstr ""
567+
568+#: qml/Wizard/Pages/passwd-set.qml:61
569+msgid "Choose your passcode"
570+msgstr ""
571+
572+#: qml/Wizard/Pages/passwd-set.qml:68
573+msgid "Passphrase must be 4 characters long"
574+msgstr ""
575+
576+#: qml/Wizard/StackButton.qml:39
577+#, qt-format
578+msgid "〈 %1"
579+msgstr ""
580+
581+#: qml/Wizard/StackButton.qml:42
582+#, qt-format
583+msgid "%1 〉"
584+msgstr ""
po/pa.po+3−3
@@ -8,14 +8,14 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"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"
12"Last-Translator: Gursharnjit_Singh <ubuntuser13@gmail.com>\n"
12"Last-Translator: Gursharnjit_Singh <ubuntuser13@gmail.com>\n"
13"Language-Team: Punjabi <pa@li.org>\n"
13"Language-Team: Punjabi <pa@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"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"
19"X-Generator: Launchpad (build 17430)\n"
19"X-Generator: Launchpad (build 17430)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
@@ -371,7 +371,7 @@ msgstr "ਜਾਰੀ ਰੱਖਣ ਲਈ ਇੱਥੇ ਛੂਹੋ।"
371
371
372#: qml/Tutorial/TutorialContent.qml:81
372#: qml/Tutorial/TutorialContent.qml:81
373msgid "Click here to finish."
373msgid "Click here to finish."
374−msgstr ""
374+msgstr "ਸਮਾਪਤੀ ਲਈ ਇੱਥੇ ਕਲਿੱਕ ਕਰੋ।"
375
375
376#: qml/Tutorial/TutorialLeftFinish.qml:24
376#: qml/Tutorial/TutorialLeftFinish.qml:24
377msgid "These are the shortcuts to favorite apps"
377msgid "These are the shortcuts to favorite apps"
po/pl.po+10−10
@@ -8,7 +8,7 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"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"
12"Last-Translator: GTriderXC <gtriderxc@yahoo.com>\n"
12"Last-Translator: GTriderXC <gtriderxc@yahoo.com>\n"
13"Language-Team: Polish <pl@li.org>\n"
13"Language-Team: Polish <pl@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
17"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
18"|| n%100>=20) ? 1 : 2;\n"
18"|| n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19+"X-Launchpad-Export-Date: 2015-05-03 06:07+0000\n"
20−"X-Generator: Launchpad (build 17430)\n"
20+"X-Generator: Launchpad (build 17474)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
23msgid "Password: "
23msgid "Password: "
@@ -123,7 +123,7 @@ msgid ""
123"Are you sure you would like\n"
123"Are you sure you would like\n"
124"to power off?"
124"to power off?"
125msgstr ""
125msgstr ""
126−"Jesteś pewny że chcesz\n"
126+"Czy na pewno\n"
127"wyłączyć?"
127"wyłączyć?"
128
128
129#: qml/Components/Dialogs.qml:155
129#: qml/Components/Dialogs.qml:155
@@ -361,11 +361,11 @@ msgstr "Dotknij tutaj, by zakończyć."
361
361
362#: qml/Tutorial/TutorialBottom.qml:28
362#: qml/Tutorial/TutorialBottom.qml:28
363msgid "Open special menus"
363msgid "Open special menus"
364−msgstr ""
364+msgstr "Otwórz menu specjalne"
365
365
366#: qml/Tutorial/TutorialBottom.qml:29
366#: qml/Tutorial/TutorialBottom.qml:29
367msgid "Swipe up from the bottom edge."
367msgid "Swipe up from the bottom edge."
368−msgstr ""
368+msgstr "Przeciągnij od dolnej krawędzi w górę ekranu"
369
369
370#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
370#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
371#: qml/Tutorial/TutorialRight.qml:144
371#: qml/Tutorial/TutorialRight.qml:144
@@ -374,7 +374,7 @@ msgstr "Dotknij tutaj, by kontynuować."
374
374
375#: qml/Tutorial/TutorialContent.qml:81
375#: qml/Tutorial/TutorialContent.qml:81
376msgid "Click here to finish."
376msgid "Click here to finish."
377−msgstr ""
377+msgstr "Kliknij tutaj, by zakończyć."
378
378
379#: qml/Tutorial/TutorialLeftFinish.qml:24
379#: qml/Tutorial/TutorialLeftFinish.qml:24
380msgid "These are the shortcuts to favorite apps"
380msgid "These are the shortcuts to favorite apps"
@@ -382,11 +382,11 @@ msgstr "To są skróty do ulubionych aplikacji"
382
382
383#: qml/Tutorial/TutorialLeft.qml:26
383#: qml/Tutorial/TutorialLeft.qml:26
384msgid "Open the launcher"
384msgid "Open the launcher"
385−msgstr ""
385+msgstr "Otwórz launcher"
386
386
387#: qml/Tutorial/TutorialLeft.qml:27
387#: qml/Tutorial/TutorialLeft.qml:27
388msgid "Short swipe from the left edge."
388msgid "Short swipe from the left edge."
389−msgstr ""
389+msgstr "Krótkie przeciągnięcie od lewej krawędzi."
390
390
391#: qml/Tutorial/TutorialPage.qml:178
391#: qml/Tutorial/TutorialPage.qml:178
392msgid "You almost got it!"
392msgid "You almost got it!"
@@ -402,7 +402,7 @@ msgstr "By zobaczyć otwarte aplikacje"
402
402
403#: qml/Tutorial/TutorialRight.qml:32
403#: qml/Tutorial/TutorialRight.qml:32
404msgid "Long swipe from the right edge."
404msgid "Long swipe from the right edge."
405−msgstr ""
405+msgstr "Długie przeciągnięcie od prawej krawędzi."
406
406
407#: qml/Tutorial/TutorialRight.qml:122
407#: qml/Tutorial/TutorialRight.qml:122
408msgid "View all your running tasks."
408msgid "View all your running tasks."
po/pt.po+7−7
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-04-18 02:15+0000\n"
11+"PO-Revision-Date: 2015-05-19 03:42+0000\n"
12"Last-Translator: António Miranda <Unknown>\n"
12"Last-Translator: António Miranda <Unknown>\n"
13"Language-Team: Portuguese <pt@li.org>\n"
13"Language-Team: Portuguese <pt@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"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-05-22 05:09+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
19+"X-Generator: Launchpad (build 17514)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -262,7 +262,7 @@ msgid ""
262"If passcode is entered incorrectly, your phone will conduct a factory reset "
262"If passcode is entered incorrectly, your phone will conduct a factory reset "
263"and all personal data will be deleted."
263"and all personal data will be deleted."
264msgstr ""
264msgstr ""
265−"Se o código seja introduzido incorretamente, todos os dados pessoais do seu "
265+"Se o código não for introduzido corretamente, todos os dados pessoais do seu "
266"telemóvel serão apagados."
266"telemóvel serão apagados."
267
267
268#: qml/Greeter/NarrowView.qml:115
268#: qml/Greeter/NarrowView.qml:115
@@ -276,7 +276,7 @@ msgstr "Indique a palavra-passe"
276
276
277#: qml/Greeter/NarrowView.qml:117
277#: qml/Greeter/NarrowView.qml:117
278msgid "Enter passcode"
278msgid "Enter passcode"
279−msgstr "Introduza código"
279+msgstr "Introduza o código"
280
280
281#: qml/Greeter/NarrowView.qml:118
281#: qml/Greeter/NarrowView.qml:118
282#, qt-format
282#, qt-format
@@ -293,7 +293,7 @@ msgstr "Por favor, volte a introduzir"
293
293
294#: qml/Greeter/NarrowView.qml:121
294#: qml/Greeter/NarrowView.qml:121
295msgid "Sorry, incorrect passcode"
295msgid "Sorry, incorrect passcode"
296−msgstr "Código incorreta"
296+msgstr "Código incorreto"
297
297
298#: qml/Notifications/NotificationMenuItemFactory.qml:128
298#: qml/Notifications/NotificationMenuItemFactory.qml:128
299msgid "Show password"
299msgid "Show password"
@@ -371,7 +371,7 @@ msgstr "Toque aqui para continuar."
371
371
372#: qml/Tutorial/TutorialContent.qml:81
372#: qml/Tutorial/TutorialContent.qml:81
373msgid "Click here to finish."
373msgid "Click here to finish."
374−msgstr ""
374+msgstr "Clique aqui para finalizar."
375
375
376#: qml/Tutorial/TutorialLeftFinish.qml:24
376#: qml/Tutorial/TutorialLeftFinish.qml:24
377msgid "These are the shortcuts to favorite apps"
377msgid "These are the shortcuts to favorite apps"
po/pt_BR.po+9−9
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"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"
12"Last-Translator: Tiago Hillebrandt <tiagohillebrandt@gmail.com>\n"
12"Last-Translator: Tiago Hillebrandt <tiagohillebrandt@gmail.com>\n"
13"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
13"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
17"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-05-05 05:47+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
19+"X-Generator: Launchpad (build 17474)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -122,8 +122,8 @@ msgid ""
122"Are you sure you would like\n"
122"Are you sure you would like\n"
123"to power off?"
123"to power off?"
124msgstr ""
124msgstr ""
125−"Você tem certeza que gostaria \n"
125+"Você tem certeza que deseja\n"
126−"de desligar?"
126+"desligar?"
127
127
128#: qml/Components/Dialogs.qml:155
128#: qml/Components/Dialogs.qml:155
129msgctxt "Button: Power off the system"
129msgctxt "Button: Power off the system"
@@ -163,17 +163,17 @@ msgstr "Pesquisar"
163#: qml/Dash/PageHeader.qml:269
163#: qml/Dash/PageHeader.qml:269
164msgctxt "Button: Open the Ubuntu Store"
164msgctxt "Button: Open the Ubuntu Store"
165msgid "Store"
165msgid "Store"
166−msgstr ""
166+msgstr "Loja"
167
167
168#: qml/Dash/PageHeader.qml:276
168#: qml/Dash/PageHeader.qml:276
169msgctxt "Button: Start a search in the current dash scope"
169msgctxt "Button: Start a search in the current dash scope"
170msgid "Search"
170msgid "Search"
171−msgstr ""
171+msgstr "Pesquisar"
172
172
173#: qml/Dash/PageHeader.qml:286
173#: qml/Dash/PageHeader.qml:286
174msgctxt "Button: Show the current dash scope settings"
174msgctxt "Button: Show the current dash scope settings"
175msgid "Settings"
175msgid "Settings"
176−msgstr ""
176+msgstr "Configurações"
177
177
178#: qml/Dash/PageHeader.qml:293
178#: qml/Dash/PageHeader.qml:293
179msgid "Remove from Favorites"
179msgid "Remove from Favorites"
@@ -373,7 +373,7 @@ msgstr "Toque aqui para continuar."
373
373
374#: qml/Tutorial/TutorialContent.qml:81
374#: qml/Tutorial/TutorialContent.qml:81
375msgid "Click here to finish."
375msgid "Click here to finish."
376−msgstr ""
376+msgstr "Clique aqui para finalizar."
377
377
378#: qml/Tutorial/TutorialLeftFinish.qml:24
378#: qml/Tutorial/TutorialLeftFinish.qml:24
379msgid "These are the shortcuts to favorite apps"
379msgid "These are the shortcuts to favorite apps"
po/sl.po+8−8
@@ -8,16 +8,16 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-23 07:24+0000\n"
11+"PO-Revision-Date: 2015-06-05 06:39+0000\n"
12−"Last-Translator: Damir Jerovšek <Unknown>\n"
12+"Last-Translator: Martin Srebotnjak <miles@filmsi.net>\n"
13"Language-Team: Slovenian <sl@li.org>\n"
13"Language-Team: Slovenian <sl@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
17"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
18"n%100==4 ? 3 : 0);\n"
18"n%100==4 ? 3 : 0);\n"
19−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19+"X-Launchpad-Export-Date: 2015-06-06 05:31+0000\n"
20−"X-Generator: Launchpad (build 17430)\n"
20+"X-Generator: Launchpad (build 17540)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
23msgid "Password: "
23msgid "Password: "
@@ -164,17 +164,17 @@ msgstr "Iskanje"
164#: qml/Dash/PageHeader.qml:269
164#: qml/Dash/PageHeader.qml:269
165msgctxt "Button: Open the Ubuntu Store"
165msgctxt "Button: Open the Ubuntu Store"
166msgid "Store"
166msgid "Store"
167−msgstr ""
167+msgstr "Trgovina"
168
168
169#: qml/Dash/PageHeader.qml:276
169#: qml/Dash/PageHeader.qml:276
170msgctxt "Button: Start a search in the current dash scope"
170msgctxt "Button: Start a search in the current dash scope"
171msgid "Search"
171msgid "Search"
172−msgstr ""
172+msgstr "Iskanje"
173
173
174#: qml/Dash/PageHeader.qml:286
174#: qml/Dash/PageHeader.qml:286
175msgctxt "Button: Show the current dash scope settings"
175msgctxt "Button: Show the current dash scope settings"
176msgid "Settings"
176msgid "Settings"
177−msgstr ""
177+msgstr "Nastavitve"
178
178
179#: qml/Dash/PageHeader.qml:293
179#: qml/Dash/PageHeader.qml:293
180msgid "Remove from Favorites"
180msgid "Remove from Favorites"
@@ -374,7 +374,7 @@ msgstr "Tapnite tukaj za nadaljevanje."
374
374
375#: qml/Tutorial/TutorialContent.qml:81
375#: qml/Tutorial/TutorialContent.qml:81
376msgid "Click here to finish."
376msgid "Click here to finish."
377−msgstr ""
377+msgstr "Kliknite tu za zaključek."
378
378
379#: qml/Tutorial/TutorialLeftFinish.qml:24
379#: qml/Tutorial/TutorialLeftFinish.qml:24
380msgid "These are the shortcuts to favorite apps"
380msgid "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 ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2014-12-04 11:57+0000\n"
11+"PO-Revision-Date: 2015-04-28 20:51+0000\n"
12−"Last-Translator: Mo <alimo199x@gmail.com>\n"
12+"Last-Translator: Aron Xu <Unknown>\n"
13"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
13"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
18+"X-Launchpad-Export-Date: 2015-04-29 05:27+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
19+"X-Generator: Launchpad (build 17453)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -53,7 +53,7 @@ msgstr[1] ""
53#: qml/Components/Dialogs.qml:69
53#: qml/Components/Dialogs.qml:69
54msgctxt "Title: Lock/Log out dialog"
54msgctxt "Title: Lock/Log out dialog"
55msgid "Log out"
55msgid "Log out"
56−msgstr ""
56+msgstr "退出"
57
57
58#: qml/Components/Dialogs.qml:70
58#: qml/Components/Dialogs.qml:70
59msgid "Are you sure you want to log out?"
59msgid "Are you sure you want to log out?"
@@ -62,12 +62,12 @@ msgstr ""
62#: qml/Components/Dialogs.qml:72
62#: qml/Components/Dialogs.qml:72
63msgctxt "Button: Lock the system"
63msgctxt "Button: Lock the system"
64msgid "Lock"
64msgid "Lock"
65−msgstr ""
65+msgstr "锁定"
66
66
67#: qml/Components/Dialogs.qml:79
67#: qml/Components/Dialogs.qml:79
68msgctxt "Button: Log out from the system"
68msgctxt "Button: Log out from the system"
69msgid "Log Out"
69msgid "Log Out"
70−msgstr ""
70+msgstr "退出"
71
71
72#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
72#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
73#: qml/Components/Dialogs.qml:173
73#: qml/Components/Dialogs.qml:173
@@ -77,38 +77,38 @@ msgstr "取消"
77#: qml/Components/Dialogs.qml:98
77#: qml/Components/Dialogs.qml:98
78msgctxt "Title: Reboot/Shut down dialog"
78msgctxt "Title: Reboot/Shut down dialog"
79msgid "Shut down"
79msgid "Shut down"
80−msgstr ""
80+msgstr "关机"
81
81
82#: qml/Components/Dialogs.qml:99
82#: qml/Components/Dialogs.qml:99
83msgid "Are you sure you want to shut down?"
83msgid "Are you sure you want to shut down?"
84−msgstr ""
84+msgstr "您确定要关机?"
85
85
86#: qml/Components/Dialogs.qml:101
86#: qml/Components/Dialogs.qml:101
87msgctxt "Button: Reboot the system"
87msgctxt "Button: Reboot the system"
88msgid "Reboot"
88msgid "Reboot"
89−msgstr ""
89+msgstr "重启"
90
90
91#: qml/Components/Dialogs.qml:109
91#: qml/Components/Dialogs.qml:109
92msgctxt "Button: Shut down the system"
92msgctxt "Button: Shut down the system"
93msgid "Shut down"
93msgid "Shut down"
94−msgstr ""
94+msgstr "关机"
95
95
96#: qml/Components/Dialogs.qml:129
96#: qml/Components/Dialogs.qml:129
97msgctxt "Title: Reboot dialog"
97msgctxt "Title: Reboot dialog"
98msgid "Reboot"
98msgid "Reboot"
99−msgstr ""
99+msgstr "重启"
100
100
101#: qml/Components/Dialogs.qml:130
101#: qml/Components/Dialogs.qml:130
102msgid "Are you sure you want to reboot?"
102msgid "Are you sure you want to reboot?"
103−msgstr ""
103+msgstr "您确定要重启吗?"
104
104
105#: qml/Components/Dialogs.qml:132
105#: qml/Components/Dialogs.qml:132
106msgid "No"
106msgid "No"
107−msgstr ""
107+msgstr "否"
108
108
109#: qml/Components/Dialogs.qml:138
109#: qml/Components/Dialogs.qml:138
110msgid "Yes"
110msgid "Yes"
111−msgstr ""
111+msgstr "是"
112
112
113#: qml/Components/Dialogs.qml:152
113#: qml/Components/Dialogs.qml:152
114msgctxt "Title: Power off/Restart dialog"
114msgctxt "Title: Power off/Restart dialog"
@@ -120,16 +120,18 @@ msgid ""
120"Are you sure you would like\n"
120"Are you sure you would like\n"
121"to power off?"
121"to power off?"
122msgstr ""
122msgstr ""
123+"您确定要\n"
124+"关闭电源吗?"
123
125
124#: qml/Components/Dialogs.qml:155
126#: qml/Components/Dialogs.qml:155
125msgctxt "Button: Power off the system"
127msgctxt "Button: Power off the system"
126msgid "Power off"
128msgid "Power off"
127−msgstr ""
129+msgstr "关机"
128
130
129#: qml/Components/Dialogs.qml:164
131#: qml/Components/Dialogs.qml:164
130msgctxt "Button: Restart the system"
132msgctxt "Button: Restart the system"
131msgid "Restart"
133msgid "Restart"
132−msgstr ""
134+msgstr "重启"
133
135
134#: qml/Components/Lockscreen.qml:245
136#: qml/Components/Lockscreen.qml:245
135msgid "Return to Call"
137msgid "Return to Call"
@@ -154,7 +156,7 @@ msgstr "查看全部"
154#: qml/Dash/GenericScopeView.qml:594
156#: qml/Dash/GenericScopeView.qml:594
155msgctxt "Label: Hint for dash search line edit"
157msgctxt "Label: Hint for dash search line edit"
156msgid "Search"
158msgid "Search"
157−msgstr ""
159+msgstr "搜索"
158
160
159#: qml/Dash/PageHeader.qml:269
161#: qml/Dash/PageHeader.qml:269
160msgctxt "Button: Open the Ubuntu Store"
162msgctxt "Button: Open the Ubuntu Store"
@@ -164,12 +166,12 @@ msgstr ""
164#: qml/Dash/PageHeader.qml:276
166#: qml/Dash/PageHeader.qml:276
165msgctxt "Button: Start a search in the current dash scope"
167msgctxt "Button: Start a search in the current dash scope"
166msgid "Search"
168msgid "Search"
167−msgstr ""
169+msgstr "搜索"
168
170
169#: qml/Dash/PageHeader.qml:286
171#: qml/Dash/PageHeader.qml:286
170msgctxt "Button: Show the current dash scope settings"
172msgctxt "Button: Show the current dash scope settings"
171msgid "Settings"
173msgid "Settings"
172−msgstr ""
174+msgstr "设置"
173
175
174#: qml/Dash/PageHeader.qml:293
176#: qml/Dash/PageHeader.qml:293
175msgid "Remove from Favorites"
177msgid "Remove from Favorites"
qml/CMakeLists.txt+1
@@ -13,6 +13,7 @@ set(QML_DIRS
13 Notifications
13 Notifications
14 Panel
14 Panel
15 Stages
15 Stages
16+ Rotation
16 Tutorial
17 Tutorial
17 Wizard
18 Wizard
18 )
19 )
qml/Components/DragHandle.qml+18−33
@@ -43,16 +43,8 @@ import Ubuntu.Gestures 0.1
43 }
43 }
44
44
45 */
45 */
46−EdgeDragArea {
46+DirectionalDragArea {
47 id: dragArea
47 id: dragArea
48− objectName: "dragHandle"
49−
50− // Disable gesture recognition by default when hinting is used as
51− // it conflicts with the hinting idea.
52− distanceThreshold: hintDisplacement > 0 ? 0 : defaultDistanceThreshold
53− maxSilenceTime: hintDisplacement > 0 ? 60*60*1000 : defaultMaxSilenceTime
54− maxDeviation: hintDisplacement > 0 ? 999999 : defaultMaxDeviation
55− compositionTime: hintDisplacement > 0 ? 0 : defaultCompositionTime
56
48
57 property bool stretch: false
49 property bool stretch: false
58
50
@@ -68,6 +60,9 @@ EdgeDragArea {
68 }
60 }
69
61
70 property real hintDisplacement: 0
62 property real hintDisplacement: 0
63+
64+ immediateRecognition: hintDisplacement > 0
65+
71 property var overrideStartValue: undefined
66 property var overrideStartValue: undefined
72 SmoothedAnimation {
67 SmoothedAnimation {
73 id: hintingAnimation
68 id: hintingAnimation
@@ -98,7 +93,6 @@ EdgeDragArea {
98 // Private stuff
93 // Private stuff
99 QtObject {
94 QtObject {
100 id: d
95 id: d
101− property var previousStatus: DirectionalDragArea.WaitingForTouch
102 property real startValue
96 property real startValue
103 property real minValue: {
97 property real minValue: {
104 if (direction == Direction.Horizontal) {
98 if (direction == Direction.Horizontal) {
@@ -183,7 +177,7 @@ EdgeDragArea {
183 }
177 }
184
178
185 onDistanceChanged: {
179 onDistanceChanged: {
186− if (status === DirectionalDragArea.Recognized) {
180+ if (dragging) {
187 // don't go the whole distance in order to smooth out the movement
181 // don't go the whole distance in order to smooth out the movement
188 var step = distance * 0.3;
182 var step = distance * 0.3;
189
183
@@ -193,31 +187,22 @@ EdgeDragArea {
193 }
187 }
194 }
188 }
195
189
196− onStatusChanged: {
190+ onDraggingChanged: {
197− if (status === DirectionalDragArea.WaitingForTouch) {
191+ if (dragging) {
198− hintingAnimation.stop();
192+ dragEvaluator.reset();
199− if (d.previousStatus === DirectionalDragArea.Recognized) {
193+ if (overrideStartValue !== undefined) {
200− d.onFinishedRecognizedGesture();
194+ d.startValue = overrideStartValue;
201− } else /* d.previousStatus === DirectionalDragArea.Undecided */ {
195+ } else {
202− // Gesture was rejected.
196+ d.startValue = parent[d.targetProp];
203− d.rollbackDrag();
204 }
197 }
205− } else /* Undecided || Recognized */ {
206− if (d.previousStatus === DirectionalDragArea.WaitingForTouch) {
207− dragEvaluator.reset();
208− if (overrideStartValue !== undefined) {
209− d.startValue = overrideStartValue;
210− } else {
211− d.startValue = parent[d.targetProp];
212− }
213
198
214− if (hintDisplacement > 0) {
199+ if (hintDisplacement > 0) {
215− hintingAnimation.targetValue = d.startValue;
200+ hintingAnimation.targetValue = d.startValue;
216− hintingAnimation.start();
201+ hintingAnimation.start();
217− }
218 }
202 }
203+ } else {
204+ hintingAnimation.stop();
205+ d.onFinishedRecognizedGesture();
219 }
206 }
220−
221− d.previousStatus = status;
222 }
207 }
223}
208}
qml/Components/EdgeDragArea.qmldeleted−46
@@ -1,46 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−import Ubuntu.Gestures 0.1
20−
21−/*
22− A DirectionalDragArea wrapper that provides some well-chosen defaults
23− for its gesture recognition parameters.
24−*/
25−DirectionalDragArea {
26−
27− // TODO: Re-evaluate those or even the recognition heuristics itself once
28− // we have gesture cancelling/forwarding in place.
29− //
30− // The idea here is that it's better having lax rules than false negatives.
31− // False negatives are very frustrating to the user.
32− maxDeviation: defaultMaxDeviation
33− wideningAngle: defaultWideningAngle
34− distanceThreshold: defaultDistanceThreshold
35− minSpeed: defaultMinSpeed
36− maxSilenceTime: defaultMaxSilenceTime
37− compositionTime: defaultCompositionTime
38−
39− readonly property real defaultMaxDeviation: units.gu(3)
40− readonly property real defaultWideningAngle: 50
41− readonly property real defaultDistanceThreshold: units.gu(1.5)
42− // some people were getting false negatives with it enabled.
43− readonly property real defaultMinSpeed: units.gu(0)
44− readonly property int defaultMaxSilenceTime: 200
45− readonly property int defaultCompositionTime: 60
46−}
qml/Components/EdgeDragEvaluator.qml+17−14
@@ -61,18 +61,12 @@ AxisVelocityCalculator {
61 }
61 }
62
62
63 velocity = calculate();
63 velocity = calculate();
64+ minVelocity = __calculateMinimumVelocityForAutoCompletion();
64
65
65− if (direction == Direction.Horizontal) {
66+ if (_dragDirectionIsPositive()) {
66− minVelocity = __calculateMinimumVelocityForAutoCompletion(Math.abs(deltaPos));
67− return Math.abs(velocity) >= minVelocity;
67+ return velocity >= minVelocity;
68 } else {
68 } else {
69− minVelocity = __calculateMinimumVelocityForAutoCompletion(deltaPos);
70− if (Direction.isPositive(direction) && (velocity >= minVelocity)
71− || !Direction.isPositive(direction) && (velocity <= minVelocity)) {
69+ return velocity <= minVelocity;
72− return true;
73− } else {
74− return false;
75− }
76 }
70 }
77 }
71 }
78
72
@@ -81,11 +75,20 @@ AxisVelocityCalculator {
81 // speedThreshold in pixels per millisecond
75 // speedThreshold in pixels per millisecond
82 property real __speedThresholdMs: speedThreshold / 1000.0
76 property real __speedThresholdMs: speedThreshold / 1000.0
83
77
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() {
84− // Minimum velocity when a drag total distance is zero
87+ // Minimum velocity when a drag total distance is zero
85− property real __v0: Direction.isPositive(direction) ? __speedThresholdMs : - __speedThresholdMs
88+ var v0 = _dragDirectionIsPositive() ? __speedThresholdMs : - __speedThresholdMs;
89+ var deltaPos = trackedPosition - __startPosition;
86
90
87− function __calculateMinimumVelocityForAutoCompletion(distance) {
88− return __v0 - ((__speedThresholdMs / dragThreshold) * distance);
91+ return v0 - ((__speedThresholdMs / dragThreshold) * deltaPos);
89 }
92 }
90
93
91 function reset() {
94 function reset() {
qml/Components/ResponsiveVerticalJournal.qml+7−2
@@ -39,6 +39,8 @@ import Dash 0.1
39 +-----+
39 +-----+
40*/
40*/
41Item {
41Item {
42+ id: root
43+
42 property real minimumColumnSpacing: units.gu(1)
44 property real minimumColumnSpacing: units.gu(1)
43
45
44 property alias columnWidth: verticalJournalView.columnWidth
46 property alias columnWidth: verticalJournalView.columnWidth
@@ -46,8 +48,8 @@ Item {
46 property alias model: verticalJournalView.model
48 property alias model: verticalJournalView.model
47 property alias delegate: verticalJournalView.delegate
49 property alias delegate: verticalJournalView.delegate
48 property alias cacheBuffer: verticalJournalView.cacheBuffer
50 property alias cacheBuffer: verticalJournalView.cacheBuffer
49− property alias displayMarginBeginning: verticalJournalView.displayMarginBeginning
51+ property real displayMarginBeginning: 0
50− property alias displayMarginEnd: verticalJournalView.displayMarginEnd
52+ property real displayMarginEnd: 0
51
53
52 implicitHeight: verticalJournalView.implicitHeight + rowSpacing
54 implicitHeight: verticalJournalView.implicitHeight + rowSpacing
53 clip: height < implicitHeight
55 clip: height < implicitHeight
@@ -63,6 +65,9 @@ Item {
63 bottomMargin: rowSpacing / 2
65 bottomMargin: rowSpacing / 2
64 }
66 }
65
67
68+ displayMarginBeginning: -Math.max(-root.displayMarginBeginning - anchors.topMargin, 0)
69+ displayMarginEnd:-Math.max(-root.displayMarginEnd - anchors.topMargin, 0)
70+
66 function px2gu(pixels) {
71 function px2gu(pixels) {
67 return Math.floor(pixels / units.gu(1))
72 return Math.floor(pixels / units.gu(1))
68 }
73 }
qml/Components/WindowScreenshot.qmlnew file+35
@@ -0,0 +1,35 @@
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.2
18+
19+Item {
20+ id: root
21+
22+ function take() {
23+ var timeNow = new Date().getTime();
24+ image.source = "image://window/" + timeNow;
25+ }
26+
27+ // Unload the image to free up memory
28+ function discard() {
29+ image.source = "";
30+ }
31+
32+ Image {
33+ id: image
34+ }
35+}
qml/Dash/CardCarousel.qml−1
@@ -61,7 +61,6 @@ DashRenderer {
61 item.fixedHeaderHeight = Qt.binding(function() { return carousel.headerHeight; });
61 item.fixedHeaderHeight = Qt.binding(function() { return carousel.headerHeight; });
62 item.height = Qt.binding(function() { return cardTool.cardHeight; });
62 item.height = Qt.binding(function() { return cardTool.cardHeight; });
63 item.cardData = Qt.binding(function() { return model; });
63 item.cardData = Qt.binding(function() { return model; });
64− item.template = Qt.binding(function() { return cardTool.template; });
65 item.components = Qt.binding(function() { return cardTool.components; });
64 item.components = Qt.binding(function() { return cardTool.components; });
66 item.fontScale = Qt.binding(function() { return carousel.fontScale; });
65 item.fontScale = Qt.binding(function() { return carousel.fontScale; });
67 item.showHeader = Qt.binding(function() { return loader.explicitlyScaled; });
66 item.showHeader = Qt.binding(function() { return loader.explicitlyScaled; });
qml/Dash/CardGrid.qml−1
@@ -66,7 +66,6 @@ DashRenderer {
66 item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight; });
66 item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight; });
67 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
67 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
68 item.cardData = Qt.binding(function() { return model; });
68 item.cardData = Qt.binding(function() { return model; });
69− item.template = Qt.binding(function() { return cardTool.template; });
70 item.components = Qt.binding(function() { return cardTool.components; });
69 item.components = Qt.binding(function() { return cardTool.components; });
71 item.titleAlignment = Qt.binding(function() { return cardTool.titleAlignment; });
70 item.titleAlignment = Qt.binding(function() { return cardTool.titleAlignment; });
72 item.scopeStyle = root.scopeStyle;
71 item.scopeStyle = root.scopeStyle;
qml/Dash/CardHorizontalList.qml−1
@@ -53,7 +53,6 @@ DashRenderer {
53 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
53 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
54 item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight; });
54 item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight; });
55 item.cardData = Qt.binding(function() { return model; });
55 item.cardData = Qt.binding(function() { return model; });
56− item.template = Qt.binding(function() { return cardTool.template; });
57 item.components = Qt.binding(function() { return cardTool.components; });
56 item.components = Qt.binding(function() { return cardTool.components; });
58 item.titleAlignment = Qt.binding(function() { return cardTool.titleAlignment; });
57 item.titleAlignment = Qt.binding(function() { return cardTool.titleAlignment; });
59 item.scopeStyle = root.scopeStyle;
58 item.scopeStyle = root.scopeStyle;
qml/Dash/CardTool.qml+16−15
@@ -198,8 +198,8 @@ Item {
198
198
199 Loader {
199 Loader {
200 id: cardLoader
200 id: cardLoader
201− property var fields: ["art", "mascot", "title", "subtitle", "summary", "attributes"]
201+ readonly property var fields: ["art", "mascot", "title", "subtitle", "summary", "attributes"]
202− property var maxData: {
202+ readonly property var maxData: {
203 "art": Qt.resolvedUrl("graphics/pixel.png"),
203 "art": Qt.resolvedUrl("graphics/pixel.png"),
204 "mascot": Qt.resolvedUrl("graphics/pixel.png"),
204 "mascot": Qt.resolvedUrl("graphics/pixel.png"),
205 "title": "—\n—",
205 "title": "—\n—",
@@ -211,26 +211,27 @@ Item {
211 onLoaded: {
211 onLoaded: {
212 item.objectName = "cardToolCard";
212 item.objectName = "cardToolCard";
213 item.asynchronous = false;
213 item.asynchronous = false;
214− item.template = Qt.binding(function() { return cardTool.template; });
215 item.components = Qt.binding(function() { return cardTool.components; });
214 item.components = Qt.binding(function() { return cardTool.components; });
216 item.width = Qt.binding(function() { return cardTool.cardWidth || item.implicitWidth; });
215 item.width = Qt.binding(function() { return cardTool.cardWidth || item.implicitWidth; });
217 item.height = Qt.binding(function() { return cardTool.cardHeight || item.implicitHeight; });
216 item.height = Qt.binding(function() { return cardTool.cardHeight || item.implicitHeight; });
218 }
217 }
219 Connections {
218 Connections {
220− target: cardLoader.item
219+ target: cardTool
220+ onTemplateChanged: cardLoader.updateCardData();
221− onComponentsChanged: {
221+ onComponentsChanged: cardLoader.updateCardData();
222+ }
223+ function updateCardData() {
222− var data = {};
224+ var data = {};
223− for (var k in cardLoader.fields) {
225+ for (var k in fields) {
224− var component = cardLoader.item.components[cardLoader.fields[k]];
226+ var component = cardTool.components[fields[k]];
225− var key = cardLoader.fields[k];
227+ var key = fields[k];
226− if ((typeof component === "string" && component.length > 0) ||
228+ if ((typeof component === "string" && component.length > 0) ||
227− (typeof component === "object" && component !== null
229+ (typeof component === "object" && component !== null
228− && typeof component["field"] === "string" && component["field"].length > 0)) {
230+ && typeof component["field"] === "string" && component["field"].length > 0)) {
229− data[key] = cardLoader.maxData[key];
231+ data[key] = maxData[key];
230− }
231 }
232 }
232− cardLoader.item.cardData = data;
233 }
233 }
234+ item.cardData = data;
234 }
235 }
235 }
236 }
236}
237}
qml/Dash/CardVerticalJournal.qml−1
@@ -72,7 +72,6 @@ DashRenderer {
72 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
72 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
73 item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight; });
73 item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight; });
74 item.cardData = Qt.binding(function() { return model; });
74 item.cardData = Qt.binding(function() { return model; });
75− item.template = Qt.binding(function() { return cardTool.template; });
76 item.components = Qt.binding(function() { return cardTool.components; });
75 item.components = Qt.binding(function() { return cardTool.components; });
77 item.titleAlignment = Qt.binding(function() { return cardTool.titleAlignment; });
76 item.titleAlignment = Qt.binding(function() { return cardTool.titleAlignment; });
78 item.scopeStyle = root.scopeStyle;
77 item.scopeStyle = root.scopeStyle;
qml/Dash/Dash.qml+10−12
@@ -153,7 +153,7 @@ Showable {
153 // (as expected) but would also cause the dash content flickable to move a bit, because
153 // (as expected) but would also cause the dash content flickable to move a bit, because
154 // that flickable was getting the touch events while overviewDragHandle was still undecided
154 // that flickable was getting the touch events while overviewDragHandle was still undecided
155 // about whether that touch was indeed performing a directional drag gesture.
155 // about whether that touch was indeed performing a directional drag gesture.
156− forceNonInteractive: overviewDragHandle.status != DirectionalDragArea.WaitingForTouch
156+ forceNonInteractive: overviewDragHandle.dragging
157
157
158 enabled: bottomEdgeController.progress == 0
158 enabled: bottomEdgeController.progress == 0
159 }
159 }
@@ -187,6 +187,11 @@ Showable {
187 onRequestFavoriteMoveTo: {
187 onRequestFavoriteMoveTo: {
188 scopes.moveFavoriteTo(scopeId, index);
188 scopes.moveFavoriteTo(scopeId, index);
189 }
189 }
190+ onRequestRestore: {
191+ bottomEdgeController.enableAnimation = true;
192+ bottomEdgeController.progress = 0;
193+ dash.setCurrentScope(scopeId, false, false);
194+ }
190
195
191 Binding {
196 Binding {
192 target: scopesList.scope
197 target: scopesList.scope
@@ -330,7 +335,7 @@ Showable {
330 }
335 }
331 }
336 }
332
337
333− EdgeDragArea {
338+ DirectionalDragArea {
334 id: overviewDragHandle
339 id: overviewDragHandle
335 objectName: "overviewDragHandle"
340 objectName: "overviewDragHandle"
336 z: 1
341 z: 1
@@ -346,21 +351,14 @@ Showable {
346 height: units.gu(2)
351 height: units.gu(2)
347
352
348 onSceneDistanceChanged: {
353 onSceneDistanceChanged: {
349− if (status == DirectionalDragArea.Recognized) {
354+ if (dragging) {
350 bottomEdgeController.enableAnimation = false;
355 bottomEdgeController.enableAnimation = false;
351 bottomEdgeController.progress = Math.max(0, Math.min(1, sceneDistance / fullMovement));
356 bottomEdgeController.progress = Math.max(0, Math.min(1, sceneDistance / fullMovement));
352 }
357 }
353 }
358 }
354
359
355− property int previousStatus: -1
360+ onDraggingChanged: {
356− property int currentStatus: DirectionalDragArea.WaitingForTouch
361+ if (!dragging) {
357−
358− onStatusChanged: {
359− previousStatus = currentStatus;
360− currentStatus = status;
361−
362− if (status == DirectionalDragArea.WaitingForTouch &&
363− previousStatus == DirectionalDragArea.Recognized) {
364 bottomEdgeController.enableAnimation = true;
362 bottomEdgeController.enableAnimation = true;
365 bottomEdgeController.progress = (bottomEdgeController.progress > 0.2) ? 1 : 0;
363 bottomEdgeController.progress = (bottomEdgeController.progress > 0.2) ? 1 : 0;
366 }
364 }
qml/Dash/DashApplication.qml+3−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014,2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -19,8 +19,8 @@ import Ubuntu.Components 1.0
19import Ubuntu.Thumbnailer 0.1 // Register support for image://thumbnailer/ and image://albumart/
19import Ubuntu.Thumbnailer 0.1 // Register support for image://thumbnailer/ and image://albumart/
20
20
21MainView {
21MainView {
22− width: applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
22+ implicitWidth: units.gu(40)
23− height: applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(68)
23+ implicitHeight: units.gu(68)
24
24
25 useDeprecatedToolbar: false
25 useDeprecatedToolbar: false
26
26
qml/Dash/GenericScopeView.qml+7−7
@@ -67,8 +67,8 @@ FocusScope {
67 subPageLoader.closeSubPage()
67 subPageLoader.closeSubPage()
68 }
68 }
69
69
70− function itemClicked(index, result, item, itemModel, resultsModel, limitedCategoryItemCount) {
70+ function itemClicked(index, result, item, itemModel, resultsModel, limitedCategoryItemCount, categoryId) {
71− if (itemModel.uri.indexOf("scope://") === 0 || scope.id === "clickscope") {
71+ if (itemModel.uri.indexOf("scope://") === 0 || scope.id === "clickscope" || (scope.id === "videoaggregator" && categoryId === "myvideos-getstarted")) {
72 // TODO Technically it is possible that calling activate() will make the scope emit
72 // TODO Technically it is possible that calling activate() will make the scope emit
73 // previewRequested so that we show a preview but there's no scope that does that yet
73 // previewRequested so that we show a preview but there's no scope that does that yet
74 // so it's not implemented
74 // so it's not implemented
@@ -80,8 +80,8 @@ FocusScope {
80 }
80 }
81 }
81 }
82
82
83− function itemPressedAndHeld(index, result, itemModel, resultsModel, limitedCategoryItemCount) {
83+ function itemPressedAndHeld(index, result, itemModel, resultsModel, limitedCategoryItemCount, categoryId) {
84− if (itemModel.uri.indexOf("scope://") !== 0) {
84+ if (itemModel.uri.indexOf("scope://") !== 0 && !(scope.id === "videoaggregator" && categoryId === "myvideos-getstarted")) {
85 if (scope.preview(result)) {
85 if (scope.preview(result)) {
86 openPreview(index, resultsModel, limitedCategoryItemCount);
86 openPreview(index, resultsModel, limitedCategoryItemCount);
87 }
87 }
@@ -346,11 +346,11 @@ FocusScope {
346 Connections {
346 Connections {
347 target: rendererLoader.item
347 target: rendererLoader.item
348 onClicked: {
348 onClicked: {
349− scopeView.itemClicked(index, result, item, itemModel, target.model, categoryItemCount());
349+ scopeView.itemClicked(index, result, item, itemModel, target.model, categoryItemCount(), baseItem.category);
350 }
350 }
351
351
352 onPressAndHold: {
352 onPressAndHold: {
353− scopeView.itemPressedAndHeld(index, result, itemModel, target.model, categoryItemCount());
353+ scopeView.itemPressedAndHeld(index, result, itemModel, target.model, categoryItemCount(), baseItem.category);
354 }
354 }
355
355
356 function categoryItemCount() {
356 function categoryItemCount() {
@@ -425,7 +425,7 @@ FocusScope {
425 // to the next, we set the visible range to the viewport so
425 // to the next, we set the visible range to the viewport so
426 // items are not culled (invisible) but still use no cacheBuffer
426 // items are not culled (invisible) but still use no cacheBuffer
427 // (it will be set once the scope is the current one)
427 // (it will be set once the scope is the current one)
428− var displayMarginBeginning = baseItem.y;
428+ var displayMarginBeginning = baseItem.y + rendererLoader.anchors.topMargin;
429 displayMarginBeginning = -Math.max(-displayMarginBeginning, 0);
429 displayMarginBeginning = -Math.max(-displayMarginBeginning, 0);
430 displayMarginBeginning = -Math.min(-displayMarginBeginning, baseItem.height);
430 displayMarginBeginning = -Math.min(-displayMarginBeginning, baseItem.height);
431 displayMarginBeginning = Math.round(displayMarginBeginning);
431 displayMarginBeginning = Math.round(displayMarginBeginning);
qml/Dash/Previews/PreviewRatingDisplay.qml+9−44
@@ -49,53 +49,18 @@ PreviewWidget {
49 objectName: "reviewsRepeater"
49 objectName: "reviewsRepeater"
50 model: root.widgetData["reviews"]
50 model: root.widgetData["reviews"]
51
51
52− delegate: Column {
52+ delegate: PreviewRatingSingleDisplay {
53− id: reviewItem
54 objectName: "reviewItem" + index
53 objectName: "reviewItem" + index
55− anchors { left: parent.left; right: parent.right;}
56− spacing: units.gu(1)
57
54
58− Rating {
55+ anchors { left: parent.left; right: parent.right; }
59− id: rating
60− objectName: "rating"
61− size: 5
62− value: modelData["rating"] || -1
63− visible: value >= 0
64− interactive: false
65
56
57+ rating: modelData["rating"] || -1
58+ author: modelData["author"] || ""
59+ review: modelData["review"] || ""
66− property var urlIconEmpty: widgetData["rating-icon-empty"]
60+ urlIconEmpty: widgetData["rating-icon-empty"]
67− property var urlIconFull: widgetData["rating-icon-full"]
61+ urlIconFull: widgetData["rating-icon-full"]
68− property var urlIconHalf: widgetData["rating-icon-half"]
62+ urlIconHalf: widgetData["rating-icon-half"]
69− }
70−
71− Label {
72− id: authorLabel
73− objectName: "authorLabel"
74− anchors { left: parent.left; right: parent.right }
75− color: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.baseText
76− opacity: .8
77− text: modelData["author"] || ""
78− visible: text !== ""
79− wrapMode: Text.Wrap
80− }
81−
82− Label {
83− id: reviewLabel
84− objectName: "reviewLabel"
85− anchors { left: parent.left; right: parent.right }
86− color: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.baseText
63+ labelColor: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.baseText
87− opacity: .8
88− text: modelData["review"] || ""
89− visible: text !== ""
90− wrapMode: Text.Wrap
91− }
92−
93− Item {
94− id: spacing
95− anchors { left: parent.left; right: parent.right }
96− height: units.gu(2)
97− visible: rating.visible || authorLabel.visible || reviewLabel.visible
98− }
99 }
64 }
100 }
65 }
101 }
66 }
qml/Dash/Previews/PreviewRatingEdit.qmlnew file+80
@@ -0,0 +1,80 @@
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.4
18+import Ubuntu.Components 0.1
19+import "../../Components"
20+
21+/*! \brief Preview widget for editable rating.
22+
23+ Shows a single display rating that can be switched by the user to edit mode
24+ and then behaves as a rating display widget
25+
26+ The display part uses widgetData["author"], widgetData["review"] and widgetData["rating"].
27+
28+ The edit part uses the same fields as the "rating-input" widget
29+*/
30+
31+PreviewWidget {
32+ id: root
33+ implicitHeight: display.visible ? display.implicitHeight : input.implicitHeight
34+
35+ Button {
36+ id: editIcon
37+ objectName: "editButton"
38+
39+ iconName: "edit"
40+ width: height
41+ anchors.right: parent.right
42+ visible: display.visible
43+ onClicked: display.visible = false
44+ }
45+
46+ PreviewRatingSingleDisplay {
47+ id: display
48+ objectName: "display"
49+
50+ anchors.left: parent.left
51+ anchors.right: editIcon.left
52+
53+ rating: widgetData["rating"] || -1
54+ author: widgetData["author"] || ""
55+ review: widgetData["review"] || ""
56+ urlIconEmpty: widgetData["rating-icon-empty"]
57+ urlIconFull: widgetData["rating-icon-full"]
58+ urlIconHalf: widgetData["rating-icon-half"]
59+ labelColor: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.baseText
60+ }
61+
62+ PreviewRatingInput {
63+ id: input
64+ objectName: "input"
65+
66+ visible: !display.visible
67+ width: parent.width
68+
69+ widgetId: root.widgetId
70+ widgetData: root.widgetData
71+ isCurrentPreview: root.isCurrentPreview
72+ scopeStyle: root.scopeStyle
73+
74+ ratingValue: widgetData["rating"]
75+ reviewText: widgetData["review"]
76+
77+ onTriggered: root.triggered(widgetId, actionId, data);
78+ }
79+
80+}
qml/Dash/Previews/PreviewRatingInput.qml+3
@@ -48,6 +48,9 @@ PreviewWidget {
48 }
48 }
49 }
49 }
50
50
51+ property alias ratingValue: rating.value
52+ property alias reviewText: reviewTextArea.text
53+
51 function submit() {
54 function submit() {
52 // checks rating-input requirements
55 // checks rating-input requirements
53 if (((widgetData["required"] === "both" ||
56 if (((widgetData["required"] === "both" ||
qml/Dash/Previews/PreviewRatingSingleDisplay.qmlnew file+70
@@ -0,0 +1,70 @@
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.4
18+import Ubuntu.Components 0.1
19+import "../../Components"
20+
21+Column {
22+ id: reviewItem
23+ spacing: units.gu(1)
24+
25+ property alias rating: ratingItem.value
26+ property alias author: authorLabel.text
27+ property alias review: reviewLabel.text
28+ property alias urlIconEmpty: ratingItem.urlIconEmpty
29+ property alias urlIconFull: ratingItem.urlIconFull
30+ property alias urlIconHalf: ratingItem.urlIconHalf
31+ property alias labelColor: authorLabel.color
32+
33+ Rating {
34+ id: ratingItem
35+ objectName: "rating"
36+ size: 5
37+ visible: value >= 0
38+ interactive: false
39+
40+ property var urlIconEmpty
41+ property var urlIconFull
42+ property var urlIconHalf
43+ }
44+
45+ Label {
46+ id: authorLabel
47+ objectName: "authorLabel"
48+ anchors { left: parent.left; right: parent.right }
49+ opacity: .8
50+ visible: text !== ""
51+ wrapMode: Text.Wrap
52+ }
53+
54+ Label {
55+ id: reviewLabel
56+ objectName: "reviewLabel"
57+ anchors { left: parent.left; right: parent.right }
58+ color: authorLabel.color
59+ opacity: .8
60+ visible: text !== ""
61+ wrapMode: Text.Wrap
62+ }
63+
64+ Item {
65+ id: spacing
66+ anchors { left: parent.left; right: parent.right }
67+ height: units.gu(2)
68+ visible: rating.visible || authorLabel.visible || reviewLabel.visible
69+ }
70+}
qml/Dash/Previews/PreviewWidgetFactory.qml+1
@@ -57,6 +57,7 @@ Loader {
57 case "progress": return "PreviewProgress.qml";
57 case "progress": return "PreviewProgress.qml";
58 case "payments": return "PreviewPayments.qml";
58 case "payments": return "PreviewPayments.qml";
59 case "rating-input": return "PreviewRatingInput.qml";
59 case "rating-input": return "PreviewRatingInput.qml";
60+ case "rating-edit": return "PreviewRatingEdit.qml";
60 case "reviews": return "PreviewRatingDisplay.qml";
61 case "reviews": return "PreviewRatingDisplay.qml";
61 case "table": return "PreviewTable.qml";
62 case "table": return "PreviewTable.qml";
62 case "text": return "PreviewTextSummary.qml";
63 case "text": return "PreviewTextSummary.qml";
qml/Dash/ScopesList.qml+2
@@ -31,6 +31,7 @@ Item {
31 signal storeClicked()
31 signal storeClicked()
32 signal requestFavorite(string scopeId, bool favorite)
32 signal requestFavorite(string scopeId, bool favorite)
33 signal requestFavoriteMoveTo(string scopeId, int index)
33 signal requestFavoriteMoveTo(string scopeId, int index)
34+ signal requestRestore(string scopeId)
34
35
35 state: "browse"
36 state: "browse"
36
37
@@ -124,6 +125,7 @@ Item {
124 onRequestEditMode: root.state = "edit";
125 onRequestEditMode: root.state = "edit";
125 onRequestScopeMoveTo: root.requestFavoriteMoveTo(scopeId, index);
126 onRequestScopeMoveTo: root.requestFavoriteMoveTo(scopeId, index);
126 onRequestActivate: root.scope.activate(result);
127 onRequestActivate: root.scope.activate(result);
128+ onRequestRestore: root.requestRestore(scopeId);
127 }
129 }
128 }
130 }
129 }
131 }
qml/Dash/ScopesListCategory.qml+5−1
@@ -35,6 +35,7 @@ Item {
35 signal requestEditMode()
35 signal requestEditMode()
36 signal requestScopeMoveTo(string scopeId, int index)
36 signal requestScopeMoveTo(string scopeId, int index)
37 signal requestActivate(var result)
37 signal requestActivate(var result)
38+ signal requestRestore(string scopeId)
38
39
39 implicitHeight: visible ? childrenRect.height : 0
40 implicitHeight: visible ? childrenRect.height : 0
40
41
@@ -93,7 +94,10 @@ Item {
93
94
94 onClicked: {
95 onClicked: {
95 if (!editMode) {
96 if (!editMode) {
97+ if (root.isFavoritesFeed)
98+ root.requestRestore(model.scopeId);
99+ else
96− root.requestActivate(result);
100+ root.requestActivate(result);
97 }
101 }
98 }
102 }
99 onPressAndHold: {
103 onPressAndHold: {
qml/Dash/graphics/phone/screenshots/gmail-webapp.svgnew file+343
@@ -0,0 +1,343 @@
1+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3+
4+<svg
5+ xmlns:dc="http://purl.org/dc/elements/1.1/"
6+ xmlns:cc="http://creativecommons.org/ns#"
7+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8+ xmlns:svg="http://www.w3.org/2000/svg"
9+ xmlns="http://www.w3.org/2000/svg"
10+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12+ width="768"
13+ height="1280"
14+ id="svg2"
15+ version="1.1"
16+ inkscape:version="0.48.5 r10040"
17+ sodipodi:docname="gmail-webapp.svg">
18+ <defs
19+ id="defs4" />
20+ <sodipodi:namedview
21+ id="base"
22+ pagecolor="#ffffff"
23+ bordercolor="#666666"
24+ borderopacity="1.0"
25+ inkscape:pageopacity="0.0"
26+ inkscape:pageshadow="2"
27+ inkscape:zoom="0.49497475"
28+ inkscape:cx="117.33439"
29+ inkscape:cy="668.80479"
30+ inkscape:document-units="px"
31+ inkscape:current-layer="layer1"
32+ showgrid="false"
33+ inkscape:window-width="1920"
34+ inkscape:window-height="1056"
35+ inkscape:window-x="0"
36+ inkscape:window-y="24"
37+ inkscape:window-maximized="1" />
38+ <metadata
39+ id="metadata7">
40+ <rdf:RDF>
41+ <cc:Work
42+ rdf:about="">
43+ <dc:format>image/svg+xml</dc:format>
44+ <dc:type
45+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
46+ <dc:title></dc:title>
47+ </cc:Work>
48+ </rdf:RDF>
49+ </metadata>
50+ <g
51+ inkscape:label="Layer 1"
52+ inkscape:groupmode="layer"
53+ id="layer1"
54+ transform="translate(0,227.63782)">
55+ <rect
56+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
57+ id="rect2985"
58+ width="769.73627"
59+ height="1276.8328"
60+ x="-2.0203052"
61+ y="3.1671834"
62+ transform="translate(0,-227.63782)" />
63+ <rect
64+ style="fill:#e6e6e6;fill-opacity:1;stroke:none;stroke-width:4;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
65+ id="rect3797"
66+ width="773.77686"
67+ height="129.29953"
68+ x="-6.0609155"
69+ y="-0.87342685"
70+ transform="translate(0,-227.63782)" />
71+ <text
72+ xml:space="preserve"
73+ style="font-size:72px;font-style:normal;font-weight:500;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu Medium;font-stretch:normal;font-variant:normal"
74+ x="20.203053"
75+ y="-139.61781"
76+ id="text3755"
77+ sodipodi:linespacing="125%"><tspan
78+ sodipodi:role="line"
79+ id="tspan3757"
80+ x="20.203053"
81+ y="-139.61781">GMail</tspan></text>
82+ <text
83+ xml:space="preserve"
84+ style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
85+ x="14.142137"
86+ y="-28.501045"
87+ id="text3759"
88+ sodipodi:linespacing="125%"><tspan
89+ sodipodi:role="line"
90+ id="tspan3761"
91+ x="14.142137"
92+ y="-28.501045">Inbox</tspan></text>
93+ <path
94+ sodipodi:type="arc"
95+ style="fill:#8effff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
96+ id="path3765"
97+ sodipodi:cx="107.07617"
98+ sodipodi:cy="337.52768"
99+ sodipodi:rx="64.649765"
100+ sodipodi:ry="61.619305"
101+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
102+ transform="translate(-28.284271,-251.88148)" />
103+ <path
104+ style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
105+ d="m 14.142136,237.52257 729.330134,0"
106+ id="path3769"
107+ inkscape:connector-curvature="0"
108+ transform="translate(0,-227.63782)" />
109+ <text
110+ xml:space="preserve"
111+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
112+ x="167.68533"
113+ y="78.575127"
114+ id="text3771"
115+ sodipodi:linespacing="125%"><tspan
116+ sodipodi:role="line"
117+ id="tspan3773"
118+ x="167.68533"
119+ y="78.575127">Lorem ipsum</tspan></text>
120+ <text
121+ xml:space="preserve"
122+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
123+ x="167.68532"
124+ y="131.10306"
125+ id="text3775"
126+ sodipodi:linespacing="125%"><tspan
127+ sodipodi:role="line"
128+ id="tspan3777"
129+ x="167.68532"
130+ y="131.10306"
131+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
132+ <path
133+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
134+ d="m 13.13199,165.44825 729.33013,0"
135+ id="path3769-2"
136+ inkscape:connector-curvature="0" />
137+ <path
138+ sodipodi:type="arc"
139+ style="fill:#8eff58;fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
140+ id="path3765-3"
141+ sodipodi:cx="107.07617"
142+ sodipodi:cy="337.52768"
143+ sodipodi:rx="64.649765"
144+ sodipodi:ry="61.619305"
145+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
146+ transform="translate(-26.263969,-93.102383)" />
147+ <text
148+ xml:space="preserve"
149+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
150+ x="169.70564"
151+ y="237.35422"
152+ id="text3771-9"
153+ sodipodi:linespacing="125%"><tspan
154+ sodipodi:role="line"
155+ id="tspan3773-7"
156+ x="169.70564"
157+ y="237.35422">Lorem ipsum</tspan></text>
158+ <text
159+ xml:space="preserve"
160+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
161+ x="169.70561"
162+ y="289.88217"
163+ id="text3775-1"
164+ sodipodi:linespacing="125%"><tspan
165+ sodipodi:role="line"
166+ id="tspan3777-1"
167+ x="169.70561"
168+ y="289.88217"
169+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
170+ <path
171+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
172+ d="m 15.152292,324.22735 729.330128,0"
173+ id="path3769-2-6"
174+ inkscape:connector-curvature="0" />
175+ <path
176+ sodipodi:type="arc"
177+ style="fill:#8effff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
178+ id="path3765-7"
179+ sodipodi:cx="107.07617"
180+ sodipodi:cy="337.52768"
181+ sodipodi:rx="64.649765"
182+ sodipodi:ry="61.619305"
183+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
184+ transform="translate(-28.284274,64.481414)" />
185+ <text
186+ xml:space="preserve"
187+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
188+ x="167.68533"
189+ y="394.93799"
190+ id="text3771-92"
191+ sodipodi:linespacing="125%"><tspan
192+ sodipodi:role="line"
193+ id="tspan3773-0"
194+ x="167.68533"
195+ y="394.93799">Lorem ipsum</tspan></text>
196+ <text
197+ xml:space="preserve"
198+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
199+ x="167.68532"
200+ y="447.46594"
201+ id="text3775-9"
202+ sodipodi:linespacing="125%"><tspan
203+ sodipodi:role="line"
204+ id="tspan3777-8"
205+ x="167.68532"
206+ y="447.46594"
207+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
208+ <path
209+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
210+ d="m 13.131986,481.81114 729.330124,0"
211+ id="path3769-2-3"
212+ inkscape:connector-curvature="0" />
213+ <path
214+ sodipodi:type="arc"
215+ style="fill:#8ea2ff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
216+ id="path3765-2"
217+ sodipodi:cx="107.07617"
218+ sodipodi:cy="337.52768"
219+ sodipodi:rx="64.649765"
220+ sodipodi:ry="61.619305"
221+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
222+ transform="translate(-26.263969,226.10582)" />
223+ <text
224+ xml:space="preserve"
225+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
226+ x="169.70564"
227+ y="556.56238"
228+ id="text3771-1"
229+ sodipodi:linespacing="125%"><tspan
230+ sodipodi:role="line"
231+ id="tspan3773-1"
232+ x="169.70564"
233+ y="556.56238">Lorem ipsum</tspan></text>
234+ <text
235+ xml:space="preserve"
236+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
237+ x="169.70563"
238+ y="609.09033"
239+ id="text3775-8"
240+ sodipodi:linespacing="125%"><tspan
241+ sodipodi:role="line"
242+ id="tspan3777-85"
243+ x="169.70563"
244+ y="609.09033"
245+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
246+ <path
247+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
248+ d="m 15.152292,643.43555 729.330128,0"
249+ id="path3769-2-1"
250+ inkscape:connector-curvature="0" />
251+ <path
252+ sodipodi:type="arc"
253+ style="fill:#8effff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
254+ id="path3765-8"
255+ sodipodi:cx="107.07617"
256+ sodipodi:cy="337.52768"
257+ sodipodi:rx="64.649765"
258+ sodipodi:ry="61.619305"
259+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
260+ transform="translate(-24.243664,391.77084)" />
261+ <text
262+ xml:space="preserve"
263+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
264+ x="171.72594"
265+ y="722.22742"
266+ id="text3771-7"
267+ sodipodi:linespacing="125%"><tspan
268+ sodipodi:role="line"
269+ id="tspan3773-6"
270+ x="171.72594"
271+ y="722.22742">Lorem ipsum</tspan></text>
272+ <text
273+ xml:space="preserve"
274+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
275+ x="171.72592"
276+ y="774.75537"
277+ id="text3775-5"
278+ sodipodi:linespacing="125%"><tspan
279+ sodipodi:role="line"
280+ id="tspan3777-2"
281+ x="171.72592"
282+ y="774.75537"
283+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
284+ <path
285+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
286+ d="m 17.172597,809.10057 729.330133,0"
287+ id="path3769-2-13"
288+ inkscape:connector-curvature="0" />
289+ <path
290+ sodipodi:type="arc"
291+ style="fill:#e44738;fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
292+ id="path3765-9"
293+ sodipodi:cx="107.07617"
294+ sodipodi:cy="337.52768"
295+ sodipodi:rx="64.649765"
296+ sodipodi:ry="61.619305"
297+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
298+ transform="translate(-24.243664,557.43585)" />
299+ <text
300+ xml:space="preserve"
301+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
302+ x="171.72594"
303+ y="887.89246"
304+ id="text3771-2"
305+ sodipodi:linespacing="125%"><tspan
306+ sodipodi:role="line"
307+ id="tspan3773-8"
308+ x="171.72594"
309+ y="887.89246">Lorem ipsum</tspan></text>
310+ <text
311+ xml:space="preserve"
312+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
313+ x="171.72592"
314+ y="940.42041"
315+ id="text3775-3"
316+ sodipodi:linespacing="125%"><tspan
317+ sodipodi:role="line"
318+ id="tspan3777-7"
319+ x="171.72592"
320+ y="940.42041"
321+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
322+ <path
323+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
324+ d="m 17.172597,974.76558 729.330123,0"
325+ id="path3769-2-9"
326+ inkscape:connector-curvature="0" />
327+ <path
328+ sodipodi:type="arc"
329+ style="fill:none;stroke:#000000;stroke-width:8;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
330+ id="path3929"
331+ sodipodi:cx="590.93921"
332+ sodipodi:cy="-119.06127"
333+ sodipodi:rx="33.335033"
334+ sodipodi:ry="33.335033"
335+ d="m 624.27424,-119.06127 a 33.335033,33.335033 0 1 1 -66.67006,0 33.335033,33.335033 0 1 1 66.67006,0 z"
336+ transform="matrix(0.85096826,0,0,0.85096826,175.43863,-68.81729)" />
337+ <path
338+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:3.40387297;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
339+ d="m 692.92249,-145.20602 20.63059,24.06901 12.03451,-13.75372 -20.63059,-17.19215"
340+ id="path3931"
341+ inkscape:connector-curvature="0" />
342+ </g>
343+</svg>
qml/Dash/graphics/phone/screenshots/settings@12.pngdeleted

Binary file not shown.

qml/Dash/graphics/phone/screenshots/ubuntu-weather-app.svgnew file+201
@@ -0,0 +1,201 @@
1+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3+
4+<svg
5+ xmlns:dc="http://purl.org/dc/elements/1.1/"
6+ xmlns:cc="http://creativecommons.org/ns#"
7+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8+ xmlns:svg="http://www.w3.org/2000/svg"
9+ xmlns="http://www.w3.org/2000/svg"
10+ xmlns:xlink="http://www.w3.org/1999/xlink"
11+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
12+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
13+ width="829"
14+ height="480"
15+ id="svg2740"
16+ sodipodi:version="0.32"
17+ inkscape:version="0.48.5 r10040"
18+ version="1.0"
19+ sodipodi:docname="ubuntu-weather-app.svg"
20+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
21+ <defs
22+ id="defs2742">
23+ <linearGradient
24+ id="linearGradient3824">
25+ <stop
26+ style="stop-color:#e6e6e6;stop-opacity:1;"
27+ offset="0"
28+ id="stop3826" />
29+ <stop
30+ style="stop-color:#23abff;stop-opacity:1;"
31+ offset="1"
32+ id="stop3828" />
33+ </linearGradient>
34+ <linearGradient
35+ inkscape:collect="always"
36+ xlink:href="#linearGradient3824"
37+ id="linearGradient3830"
38+ x1="348.55862"
39+ y1="343.23914"
40+ x2="348.55862"
41+ y2="-17.422215"
42+ gradientUnits="userSpaceOnUse" />
43+ </defs>
44+ <sodipodi:namedview
45+ id="base"
46+ pagecolor="#ffffff"
47+ bordercolor="#666666"
48+ borderopacity="1.0"
49+ gridtolerance="10000"
50+ guidetolerance="10"
51+ objecttolerance="10"
52+ inkscape:pageopacity="0.0"
53+ inkscape:pageshadow="2"
54+ inkscape:zoom="0.82625984"
55+ inkscape:cx="331.28234"
56+ inkscape:cy="125.54212"
57+ inkscape:document-units="px"
58+ inkscape:current-layer="layer1"
59+ showgrid="false"
60+ inkscape:window-width="1145"
61+ inkscape:window-height="847"
62+ inkscape:window-x="268"
63+ inkscape:window-y="63"
64+ inkscape:window-maximized="0" />
65+ <metadata
66+ id="metadata2745">
67+ <rdf:RDF>
68+ <cc:Work
69+ rdf:about="">
70+ <dc:format>image/svg+xml</dc:format>
71+ <dc:type
72+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
73+ </cc:Work>
74+ </rdf:RDF>
75+ </metadata>
76+ <g
77+ inkscape:label="Layer 1"
78+ inkscape:groupmode="layer"
79+ id="layer1"
80+ transform="translate(-321.13452,-104.68346)">
81+ <rect
82+ style="fill:url(#linearGradient3830);fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
83+ id="rect2990"
84+ width="832.66785"
85+ height="480.47839"
86+ x="-1.210273"
87+ y="0.73188055"
88+ transform="translate(321.13452,104.68346)" />
89+ <path
90+ d="m 310.66382,132.06057 c -1.82703,2.0443 -22.21039,-18.38308 -24.89334,-17.37811 -2.68295,1.00497 -6.73783,33.64192 -9.57527,34.01165 -2.83744,0.36973 -7.71546,-33.08881 -10.48084,-33.88783 -2.76539,-0.79903 -18.7793,19.34152 -21.17964,17.82892 -2.40034,-1.51259 5.69972,-26.09808 4.36486,-28.67057 -1.33487,-2.57249 -31.86305,-3.78032 -32.49182,-6.6501 -0.62876,-2.86978 27.66855,-10.399376 27.90248,-13.241764 0.23394,-2.842387 -18.99828,-18.90215 -17.89698,-21.393798 1.10131,-2.491647 26.85456,8.177399 28.59429,5.99159 1.73972,-2.185809 -3.39182,-29.683489 -0.75169,-30.507098 2.64013,-0.823608 18.50306,18.485327 21.32153,18.392972 2.81847,-0.09235 11.30312,-24.287685 14.18258,-23.722317 2.87947,0.565368 1.44329,25.667631 3.67226,27.34365 2.22897,1.676019 24.27161,-6.28288 25.90675,-4.004945 1.63514,2.277936 -12.63923,24.445025 -11.77955,27.07291 0.85968,2.627885 27.01183,5.1355 26.65457,7.852474 -0.35726,2.716973 -32.69913,3.875594 -34.04684,6.507203 -1.3477,2.631613 12.32368,32.410863 10.49665,34.455163 z"
91+ id="path11949"
92+ inkscape:flatsided="false"
93+ inkscape:randomized="-0.092"
94+ inkscape:rounded="0.1"
95+ sodipodi:arg1="0.79570711"
96+ sodipodi:arg2="1.144773"
97+ sodipodi:cx="275.15002"
98+ sodipodi:cy="88.090233"
99+ sodipodi:r1="57.019234"
100+ sodipodi:r2="32.818508"
101+ sodipodi:sides="9"
102+ sodipodi:type="star"
103+ style="fill:#f5ff12;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;visibility:visible;display:inline;overflow:visible"
104+ transform="matrix(2.527571,0,0,2.527571,85.214779,60.619097)" />
105+ <path
106+ d="m 194.51692,77.283737 c 0,20.659371 -17.86424,37.407103 -39.90091,37.407103 -22.03666,0 -39.90091,-16.747732 -39.90091,-37.407103 0,-20.659371 17.86425,-37.4071 39.90091,-37.4071 22.03667,0 39.90091,16.747729 39.90091,37.4071 z"
107+ id="path11951"
108+ sodipodi:cx="154.61601"
109+ sodipodi:cy="77.283737"
110+ sodipodi:rx="39.900909"
111+ sodipodi:ry="37.407101"
112+ sodipodi:type="arc"
113+ style="fill:#f5ff12;fill-opacity:1;fill-rule:nonzero;stroke:#f5ff12;stroke-width:10.27388287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:0.5;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
114+ transform="matrix(2.4568175,0,0,2.1990879,404.91256,120.05076)" />
115+ <path
116+ transform="matrix(3.2663208,0,0,3.4016021,515.92101,-254.59331)"
117+ style="fill:#6798e9;fill-opacity:1;fill-rule:nonzero;stroke:#6798e9;stroke-width:7.30059433;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.5;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
118+ sodipodi:type="inkscape:offset"
119+ inkscape:radius="0"
120+ inkscape:original="M 110.53125 144.96875 C 100.76599 144.96875 92.384060 150.03292 88.281250 157.34375 C 86.642607 156.42892 84.835190 155.87500 82.906250 155.87500 C 77.073071 155.87500 72.219030 160.56753 70.718750 166.93750 C 68.157973 165.92533 65.180470 165.31250 62.000000 165.31250 C 52.458571 165.31250 44.718750 170.53296 44.718750 176.96875 C 44.718749 182.77816 51.066430 187.55402 59.312500 188.43750 C 58.942922 189.06456 58.656250 189.68551 58.656250 190.34375 C 58.656248 196.50568 75.538140 201.53125 96.343750 201.53125 C 117.14936 201.53125 134.03126 196.50568 134.03125 190.34375 C 134.03125 189.92916 133.77393 189.55916 133.62500 189.15625 C 134.53629 189.38287 135.43860 189.62500 136.43750 189.62500 C 142.00333 189.62501 146.53125 185.84318 146.53125 181.18750 C 146.53125 176.53182 142.00332 172.75000 136.43750 172.75000 C 135.75542 172.75000 135.17544 172.98560 134.53125 173.09375 C 135.09454 171.23218 135.46875 169.33268 135.46875 167.31250 C 135.46875 154.98864 124.31330 144.96875 110.53125 144.96875 z "
121+ id="path11953"
122+ d="m 110.53125,144.96875 c -9.76526,0 -18.14719,5.06417 -22.25,12.375 -1.638643,-0.91483 -3.44606,-1.46875 -5.375,-1.46875 -5.833179,0 -10.68722,4.69253 -12.1875,11.0625 -2.560777,-1.01217 -5.53828,-1.625 -8.71875,-1.625 -9.541429,0 -17.28125,5.22046 -17.28125,11.65625 -10e-7,5.80941 6.34768,10.58527 14.59375,11.46875 -0.369578,0.62706 -0.65625,1.24801 -0.65625,1.90625 -2e-6,6.16193 16.88189,11.1875 37.6875,11.1875 20.80561,0 37.68751,-5.02557 37.6875,-11.1875 0,-0.41459 -0.25732,-0.78459 -0.40625,-1.1875 0.91129,0.22662 1.8136,0.46875 2.8125,0.46875 5.56583,1e-5 10.09375,-3.78182 10.09375,-8.4375 0,-4.65568 -4.52793,-8.4375 -10.09375,-8.4375 -0.68208,0 -1.26206,0.2356 -1.90625,0.34375 0.56329,-1.86157 0.9375,-3.76107 0.9375,-5.78125 0,-12.32386 -11.15545,-22.34375 -24.9375,-22.34375 z" />
123+ <path
124+ inkscape:connector-curvature="0"
125+ style="fill:#386195;fill-opacity:1;fill-rule:nonzero;stroke:#386195;stroke-width:24.07697487;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.5;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
126+ id="path11963"
127+ d="m 975.70833,305.75079 c -17.87875,0 -33.1897,9.10731 -40.70137,22.18913 -3.00012,-1.63696 -6.34124,-2.6392 -9.87286,-2.6392 -10.67967,0 -19.5639,8.39604 -22.31073,19.7943 -4.6884,-1.81117 -10.09942,-2.93251 -15.92242,-2.93251 -17.46892,0 -31.65122,9.35352 -31.65122,20.86955 0,10.39528 11.61748,18.9466 26.71485,20.52749 -0.67665,1.12204 -1.20995,2.24338 -1.20995,3.42121 0,11.02604 30.92124,19.98985 69.01327,19.98985 38.09195,0 69.0132,-8.96381 69.0132,-19.98985 0,-0.74183 -0.5017,-1.42952 -0.7743,-2.15046 1.6684,0.40556 3.3496,0.83088 5.1784,0.83088 10.1902,0 18.439,-6.7716 18.439,-15.10234 0,-8.33078 -8.2488,-15.05348 -18.439,-15.05348 -1.2488,0 -2.3535,0.39297 -3.5329,0.58647 1.0312,-3.33102 1.7422,-6.7466 1.7422,-10.36143 0,-22.05212 -20.4533,-39.97961 -45.68617,-39.97961 z" />
128+ <path
129+ inkscape:connector-curvature="0"
130+ style="fill:#6798e9;fill-opacity:1;fill-rule:nonzero;stroke:#6798e9;stroke-width:15.64964962;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3.20000005;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
131+ sodipodi:nodetypes="cccc"
132+ id="path11965"
133+ d="m 717.80594,482.73655 c -32.27924,28.4432 5.62151,52.69793 20.03284,13.63938 l 11.08203,-36.22961 -31.11487,22.59023 z" />
134+ <path
135+ inkscape:connector-curvature="0"
136+ style="fill:#6798e9;fill-opacity:1;fill-rule:nonzero;stroke:#6798e9;stroke-width:15.64964962;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3.20000005;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
137+ sodipodi:nodetypes="cccc"
138+ id="path11967"
139+ d="m 799.47,485.76116 c -32.27925,28.4432 5.62151,52.69792 20.03284,13.63937 l 11.08202,-36.2296 -31.11486,22.59023 z" />
140+ <text
141+ xml:space="preserve"
142+ style="font-size:32px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
143+ x="68.985565"
144+ y="92.712631"
145+ id="text2992"
146+ sodipodi:linespacing="125%"
147+ transform="translate(321.13452,104.68346)"><tspan
148+ sodipodi:role="line"
149+ id="tspan2994"
150+ x="68.985565"
151+ y="92.712631" /></text>
152+ <text
153+ xml:space="preserve"
154+ style="font-size:10px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
155+ x="-202.1156"
156+ y="155.64684"
157+ id="text2996"
158+ sodipodi:linespacing="125%"
159+ transform="translate(321.13452,104.68346)"><tspan
160+ sodipodi:role="line"
161+ id="tspan2998"
162+ x="-202.1156"
163+ y="155.64684" /></text>
164+ <text
165+ xml:space="preserve"
166+ style="font-size:24px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
167+ x="342.91943"
168+ y="155.03653"
169+ id="text3000"
170+ sodipodi:linespacing="125%"><tspan
171+ sodipodi:role="line"
172+ id="tspan3002"
173+ x="342.91943"
174+ y="155.03653"
175+ style="font-size:32px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Ubuntu;-inkscape-font-specification:Ubuntu Medium">Wheather App</tspan></text>
176+ <text
177+ xml:space="preserve"
178+ style="font-size:24px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
179+ x="460.31592"
180+ y="358.3624"
181+ id="text3004"
182+ sodipodi:linespacing="125%"><tspan
183+ sodipodi:role="line"
184+ id="tspan3006"
185+ x="460.31592"
186+ y="358.3624"
187+ style="font-size:48px;fill:#ff0000;-inkscape-font-specification:Ubuntu;font-family:Ubuntu;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal">22C</tspan></text>
188+ <text
189+ xml:space="preserve"
190+ style="font-size:24px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#0000ff;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
191+ x="460.19751"
192+ y="412.14325"
193+ id="text3004-0"
194+ sodipodi:linespacing="125%"><tspan
195+ sodipodi:role="line"
196+ id="tspan3006-4"
197+ x="460.19751"
198+ y="412.14325"
199+ style="font-size:48px;fill:#0000ff;-inkscape-font-specification:Ubuntu Medium;font-family:Ubuntu;font-weight:500;font-style:normal;font-stretch:normal;font-variant:normal">14C</tspan></text>
200+ </g>
201+</svg>
qml/DeviceConfiguration.qmlnew file+81
@@ -0,0 +1,81 @@
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+
19+StateGroup {
20+ id: root
21+
22+ readonly property int useNativeOrientation: -1
23+
24+ property int primaryOrientation: useNativeOrientation
25+
26+ property int supportedOrientations: Qt.PortraitOrientation
27+ | Qt.InvertedPortraitOrientation
28+ | Qt.LandscapeOrientation
29+ | Qt.InvertedLandscapeOrientation
30+
31+ // Supported values so far:
32+ // "phone", "tablet" or "desktop"
33+ property string category: "phone"
34+
35+
36+ property alias name: root.state
37+
38+ states: [
39+ State {
40+ name: "mako"
41+ PropertyChanges {
42+ target: root
43+ supportedOrientations: Qt.PortraitOrientation
44+ | Qt.LandscapeOrientation
45+ | Qt.InvertedLandscapeOrientation
46+ }
47+ },
48+ State {
49+ name: "krillin"
50+ PropertyChanges {
51+ target: root
52+ supportedOrientations: Qt.PortraitOrientation
53+ | Qt.LandscapeOrientation
54+ | Qt.InvertedLandscapeOrientation
55+ }
56+ },
57+ State {
58+ name: "manta"
59+ PropertyChanges {
60+ target: root
61+ category: "tablet"
62+ }
63+ },
64+ State {
65+ name: "flo"
66+ PropertyChanges {
67+ target: root
68+ primaryOrientation: Qt.InvertedLandscapeOrientation
69+ category: "tablet"
70+ }
71+ },
72+ State {
73+ name: "desktop"
74+ PropertyChanges {
75+ target: root
76+ category: "desktop"
77+ }
78+ }
79+ ]
80+
81+}
qml/Greeter/CoverPage.qml+3−2
@@ -129,13 +129,14 @@ Showable {
129
129
130 DragHandle {
130 DragHandle {
131 id: dragHandle
131 id: dragHandle
132+ objectName: "coverPageDragHandle"
132 anchors.fill: parent
133 anchors.fill: parent
133 anchors.leftMargin: root.dragHandleLeftMargin
134 anchors.leftMargin: root.dragHandleLeftMargin
134 enabled: root.draggable
135 enabled: root.draggable
135 direction: Direction.Horizontal
136 direction: Direction.Horizontal
136
137
137− onDraggingChanged: {
138+ onPressedChanged: {
138− if (dragging) {
139+ if (pressed) {
139 root.tease();
140 root.tease();
140 showLabelAnimation.start();
141 showLabelAnimation.start();
141 }
142 }
qml/Greeter/Greeter.qml+6−2
@@ -52,6 +52,8 @@ Showable {
52 property int failedLoginsDelayAttempts: 7 // number of failed logins
52 property int failedLoginsDelayAttempts: 7 // number of failed logins
53 property real failedLoginsDelayMinutes: 5 // minutes of forced waiting
53 property real failedLoginsDelayMinutes: 5 // minutes of forced waiting
54
54
55+ readonly property bool animating: loader.item ? loader.item.animating : false
56+
55 signal tease()
57 signal tease()
56 signal sessionStarted()
58 signal sessionStarted()
57 signal emergencyCall()
59 signal emergencyCall()
@@ -141,8 +143,10 @@ Showable {
141 enabled = false;
143 enabled = false;
142 if (LightDM.Greeter.startSessionSync()) {
144 if (LightDM.Greeter.startSessionSync()) {
143 sessionStarted();
145 sessionStarted();
146+ if (loader.item) {
144− loader.item.notifyAuthenticationSucceeded();
147+ loader.item.notifyAuthenticationSucceeded();
148+ }
145− } else {
149+ } else if (loader.item) {
146 loader.item.notifyAuthenticationFailed();
150 loader.item.notifyAuthenticationFailed();
147 }
151 }
148 enabled = true;
152 enabled = true;
qml/Greeter/NarrowView.qml+1
@@ -33,6 +33,7 @@ FocusScope {
33 property alias infographicModel: coverPage.infographicModel
33 property alias infographicModel: coverPage.infographicModel
34 readonly property bool fullyShown: coverPage.showProgress === 1 || lockscreen.shown
34 readonly property bool fullyShown: coverPage.showProgress === 1 || lockscreen.shown
35 readonly property bool required: coverPage.required || lockscreen.required
35 readonly property bool required: coverPage.required || lockscreen.required
36+ readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
36
37
37 signal selected(int index) // unused
38 signal selected(int index) // unused
38 signal responded(string response)
39 signal responded(string response)
qml/Greeter/ShimGreeter.qmlnew file+45
@@ -0,0 +1,45 @@
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.3
18+
19+/*
20+ * This is a shim greeter that is only used to provide a shell,
21+ * running without a greeter, the requisite information
22+ *
23+ */
24+
25+/* FIXME: this shuld be fine as a QtObject, but bug lp:1447391
26+ * dictates wrapping as an item instead
27+ */
28+Item {
29+ id: shimGreeter
30+
31+ readonly property bool active: false
32+ readonly property bool hasLockedApp: lockedApp !== ""
33+ readonly property bool locked: false
34+ readonly property bool shown: false
35+ readonly property bool waiting: false
36+
37+ property string lockedApp: ""
38+
39+ // Since ShimGreeter is never active, these can just return
40+ property var forceShow: (function() { return; })
41+ property var notifyAboutToFocusApp: (function(appId) { return; })
42+ property var notifyAppFocused: (function(appId) { return; })
43+ property var notifyShowingDashFromDrag: (function(appId) { return false; })
44+
45+}
qml/Greeter/WideView.qml+1
@@ -32,6 +32,7 @@ FocusScope {
32 property alias infographicModel: coverPage.infographicModel
32 property alias infographicModel: coverPage.infographicModel
33 readonly property bool fullyShown: coverPage.showProgress === 1
33 readonly property bool fullyShown: coverPage.showProgress === 1
34 readonly property bool required: coverPage.required
34 readonly property bool required: coverPage.required
35+ readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
35
36
36 // so that it can be replaced in tests with a mock object
37 // so that it can be replaced in tests with a mock object
37 property var inputMethod: Qt.inputMethod
38 property var inputMethod: Qt.inputMethod
qml/Launcher/Launcher.qml+7−15
@@ -210,7 +210,7 @@ Item {
210 bottom: parent.bottom
210 bottom: parent.bottom
211 }
211 }
212 x: -width
212 x: -width
213− visible: root.x > 0 || x > -width || dragArea.status === DirectionalDragArea.Undecided
213+ visible: root.x > 0 || x > -width || dragArea.pressed
214 model: LauncherModel
214 model: LauncherModel
215
215
216 property bool animate: true
216 property bool animate: true
@@ -272,7 +272,7 @@ Item {
272 }
272 }
273 }
273 }
274
274
275− EdgeDragArea {
275+ DirectionalDragArea {
276 id: dragArea
276 id: dragArea
277 objectName: "launcherDragArea"
277 objectName: "launcherDragArea"
278
278
@@ -283,20 +283,11 @@ Item {
283 width: root.dragAreaWidth
283 width: root.dragAreaWidth
284 height: root.height
284 height: root.height
285
285
286− onTouchXChanged: {
286+ onDistanceChanged: {
287− if (status !== DirectionalDragArea.Recognized || launcher.state == "visible")
287+ if (!dragging || launcher.state == "visible")
288 return;
288 return;
289
289
290− // When the gesture finally gets recognized, the finger will likely be
290+ panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
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
300 }
291 }
301
292
302 onDraggingChanged: {
293 onDraggingChanged: {
@@ -306,7 +297,8 @@ Item {
306 if (distance > minimizeDistance) {
297 if (distance > minimizeDistance) {
307 root.dash();
298 root.dash();
308 }
299 }
309− } else {
300+ } else if (root.state === "") {
301+ // didn't drag far enough. rollback
310 root.switchToNextState("")
302 root.switchToNextState("")
311 }
303 }
312 }
304 }
qml/Notifications/ShapedIcon.qml+4−3
@@ -14,8 +14,8 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.3
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.2
19
19
20Item {
20Item {
21 property string fileSource
21 property string fileSource
@@ -25,7 +25,8 @@ Item {
25 objectName: "shapedIcon"
25 objectName: "shapedIcon"
26 anchors.fill: parent
26 anchors.fill: parent
27 visible: shaped
27 visible: shaped
28− image: realImage
28+ source: realImage
29+ sourceFillMode: UbuntuShape.PreserveAspectCrop
29 }
30 }
30
31
31 Image {
32 Image {
qml/OrientedShell.qmlnew file+185
@@ -0,0 +1,185 @@
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 QtQuick.Window 2.0
19+import Unity.Session 0.1
20+import GSettings 1.0
21+import "Components"
22+import "Rotation"
23+
24+Rectangle {
25+ id: root
26+ color: "black"
27+
28+ implicitWidth: units.gu(40)
29+ implicitHeight: units.gu(71)
30+
31+ // NB: native and primary orientations here don't map exactly to their QScreen counterparts
32+ readonly property int nativeOrientation: width > height ? Qt.LandscapeOrientation : Qt.PortraitOrientation
33+
34+ readonly property int primaryOrientation:
35+ deviceConfiguration.primaryOrientation == deviceConfiguration.useNativeOrientation
36+ ? nativeOrientation : deviceConfiguration.primaryOrientation
37+
38+ DeviceConfiguration {
39+ id: deviceConfiguration
40+ name: applicationArguments.deviceName
41+ }
42+
43+ // to be overwritten by tests
44+ property var usageModeSettings: GSettings { schema.id: "com.canonical.Unity8" }
45+ property int physicalOrientation: Screen.orientation
46+ property bool orientationLocked: OrientationLock.enabled
47+ property var orientationLock: OrientationLock
48+
49+ property int orientation
50+ onPhysicalOrientationChanged: {
51+ if (!orientationLocked) {
52+ orientation = physicalOrientation;
53+ }
54+ }
55+ onOrientationLockedChanged: {
56+ if (orientationLocked) {
57+ orientationLock.savedOrientation = physicalOrientation;
58+ } else {
59+ orientation = physicalOrientation;
60+ }
61+ }
62+ Component.onCompleted: {
63+ if (orientationLocked) {
64+ orientation = orientationLock.savedOrientation;
65+ }
66+ }
67+
68+ readonly property int supportedOrientations: shell.supportedOrientations
69+ & deviceConfiguration.supportedOrientations
70+ property int acceptedOrientationAngle: {
71+ if (orientation & supportedOrientations) {
72+ return Screen.angleBetween(nativeOrientation, orientation);
73+ } else if (shell.orientation & supportedOrientations) {
74+ // stay where we are
75+ return shell.orientationAngle;
76+ } else if (angleToOrientation(shell.mainAppWindowOrientationAngle) & supportedOrientations) {
77+ return shell.mainAppWindowOrientationAngle;
78+ } else {
79+ // rotate to some supported orientation as we can't stay where we currently are
80+ // TODO: Choose the closest to the current one
81+ if (supportedOrientations & Qt.PortraitOrientation) {
82+ return Screen.angleBetween(nativeOrientation, Qt.PortraitOrientation);
83+ } else if (supportedOrientations & Qt.LandcscapeOrientation) {
84+ return Screen.angleBetween(nativeOrientation, Qt.LandscapeOrientation);
85+ } else if (supportedOrientations & Qt.InvertedPortraitOrientation) {
86+ return Screen.angleBetween(nativeOrientation, Qt.InvertedPortraitOrientation);
87+ } else if (supportedOrientations & Qt.InvertedLandscapeOrientation) {
88+ return Screen.angleBetween(nativeOrientation, Qt.InvertedLandscapeOrientation);
89+ } else {
90+ // if all fails, fallback to primary orientation
91+ return Screen.angleBetween(nativeOrientation, primaryOrientation);
92+ }
93+ }
94+ }
95+
96+ function angleToOrientation(angle) {
97+ switch (angle) {
98+ case 0:
99+ return nativeOrientation;
100+ break;
101+ case 90:
102+ return nativeOrientation === Qt.PortraitOrientation ? Qt.InvertedLandscapeOrientation
103+ : Qt.PortraitOrientation;
104+ break;
105+ case 180:
106+ return nativeOrientation === Qt.PortraitOrientation ? Qt.InvertedPortraitOrientation
107+ : Qt.InvertedLandscapeOrientation;
108+ break;
109+ case 270:
110+ return nativeOrientation === Qt.PortraitOrientation ? Qt.LandscapeOrientation
111+ : Qt.InvertedPortraitOrientation;
112+ break;
113+ default:
114+ console.warn("angleToOrientation: Invalid orientation angle: " + angle);
115+ return primaryOrientation;
116+ }
117+ }
118+
119+ RotationStates {
120+ id: rotationStates
121+ objectName: "rotationStates"
122+ orientedShell: root
123+ shell: shell
124+ shellCover: shellCover
125+ windowScreenshot: windowScreenshot
126+ }
127+
128+ Shell {
129+ id: shell
130+ objectName: "shell"
131+ width: root.width
132+ height: root.height
133+ orientation: root.angleToOrientation(orientationAngle)
134+ primaryOrientation: root.primaryOrientation
135+ nativeOrientation: root.nativeOrientation
136+ nativeWidth: root.width
137+ nativeHeight: root.height
138+
139+ // TODO: Factor in the connected input devices (eg: physical keyboard, mouse, touchscreen),
140+ // what's the output device (eg: big TV, desktop monitor, phone display), etc.
141+ usageScenario: {
142+ if (root.usageModeSettings.usageMode === "Windowed") {
143+ return "desktop";
144+ } else if (root.usageModeSettings.usageMode === "Staged"
145+ && deviceConfiguration.category === "desktop") {
146+ return "tablet";
147+ } else {
148+ return deviceConfiguration.category;
149+ }
150+ }
151+
152+ property real transformRotationAngle
153+ property real transformOriginX
154+ property real transformOriginY
155+
156+ transform: Rotation {
157+ origin.x: shell.transformOriginX; origin.y: shell.transformOriginY; axis { x: 0; y: 0; z: 1 }
158+ angle: shell.transformRotationAngle
159+ }
160+ }
161+
162+ Rectangle {
163+ id: shellCover
164+ color: "black"
165+ anchors.fill: parent
166+ visible: false
167+ }
168+
169+ WindowScreenshot {
170+ id: windowScreenshot
171+ visible: false
172+ width: root.width
173+ height: root.height
174+
175+ property real transformRotationAngle
176+ property real transformOriginX
177+ property real transformOriginY
178+
179+ transform: Rotation {
180+ origin.x: windowScreenshot.transformOriginX; origin.y: windowScreenshot.transformOriginY;
181+ axis { x: 0; y: 0; z: 1 }
182+ angle: windowScreenshot.transformRotationAngle
183+ }
184+ }
185+}
qml/Panel/Indicators/IndicatorsLight.qml+8
@@ -26,6 +26,8 @@ import Unity.Indicators 0.1 as Indicators
26QtObject {
26QtObject {
27 id: root
27 id: root
28
28
29+ property color color: "darkgreen"
30+
29 property var _actionGroup: QMenuModel.QDBusActionGroup {
31 property var _actionGroup: QMenuModel.QDBusActionGroup {
30 busType: 1
32 busType: 1
31 busName: "com.canonical.indicator.messages"
33 busName: "com.canonical.indicator.messages"
@@ -50,4 +52,10 @@ QtObject {
50 return (Powerd.status === Powerd.Off && _rootState.hasMessages) ? Lights.On : Lights.Off
52 return (Powerd.status === Powerd.Off && _rootState.hasMessages) ? Lights.On : Lights.Off
51 }
53 }
52 }
54 }
55+
56+ property var _colorBinding: Binding {
57+ target: Lights
58+ property: "color"
59+ value: root.color
60+ }
53}
61}
qml/Panel/IndicatorsMenu.qml+12−2
@@ -186,9 +186,18 @@ Showable {
186 enabled: !root.shown && root.available
186 enabled: !root.shown && root.available
187 autoCompleteDragThreshold: maxTotalDragDistance / 2
187 autoCompleteDragThreshold: maxTotalDragDistance / 2
188 stretch: true
188 stretch: true
189− distanceThreshold: enableHint ? 0 : minimizedPanelHeight
190
189
190+ onPressedChanged: {
191+ if (pressed) {
192+ touchPressTime = new Date().getTime();
193+ } else {
194+ var touchReleaseTime = new Date().getTime();
195+ if (touchReleaseTime - touchPressTime <= 300) {
191− onTapped: showTapped(Qt.point(touchSceneX, touchSceneY));
196+ root.showTapped(Qt.point(touchSceneX, touchSceneY));
197+ }
198+ }
199+ }
200+ property var touchPressTime
192
201
193 // using hint regulates minimum to hint displacement, but in fullscreen mode, we need to do it manually.
202 // using hint regulates minimum to hint displacement, but in fullscreen mode, we need to do it manually.
194 overrideStartValue: enableHint ? minimizedPanelHeight : expandedPanelHeight + handle.height
203 overrideStartValue: enableHint ? minimizedPanelHeight : expandedPanelHeight + handle.height
@@ -204,6 +213,7 @@ Showable {
204
213
205 DragHandle {
214 DragHandle {
206 id: __hideDragHandle
215 id: __hideDragHandle
216+ objectName: "hideDragHandle"
207 anchors.fill: handle
217 anchors.fill: handle
208 direction: Direction.Upwards
218 direction: Direction.Upwards
209 enabled: root.shown && root.available
219 enabled: root.shown && root.available
qml/Panel/Panel.qml+9
@@ -27,6 +27,9 @@ Item {
27 property alias indicators: __indicators
27 property alias indicators: __indicators
28 property alias callHint: __callHint
28 property alias callHint: __callHint
29 property bool fullscreenMode: false
29 property bool fullscreenMode: false
30+ property real indicatorAreaShowProgress: 1.0
31+
32+ opacity: fullscreenMode && indicators.fullyClosed ? 0.0 : 1.0
30
33
31 Rectangle {
34 Rectangle {
32 id: darkenedArea
35 id: darkenedArea
@@ -58,6 +61,12 @@ Item {
58 NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
61 NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
59 }
62 }
60
63
64+ transform: Translate {
65+ y: indicators.state === "initial"
66+ ? (1.0 - indicatorAreaShowProgress) * -d.indicatorHeight
67+ : 0
68+ }
69+
61 BorderImage {
70 BorderImage {
62 id: dropShadow
71 id: dropShadow
63 anchors {
72 anchors {
qml/Rotation/HalfLoopRotationAnimation.qmlnew file+46
@@ -0,0 +1,46 @@
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.3
18+
19+SequentialAnimation {
20+ id: root
21+
22+ // set from outside
23+ property int fromAngle
24+ property int toAngle
25+ property var info
26+ property var shell
27+
28+ readonly property bool flipShellDimensions: toAngle == 90 || toAngle == 270
29+
30+ ScriptAction { script: {
31+ info.transitioning = true;
32+ shell.orientationAngle = root.toAngle;
33+ shell.x = (orientedShell.width - shell.width) / 2
34+ shell.y = (orientedShell.height - shell.height) / 2;
35+ shell.transformOriginX = shell.width / 2;
36+ shell.transformOriginY = shell.height / 2;
37+ shell.updateFocusedAppOrientation();
38+ } }
39+ NumberAnimation {
40+ target: shell
41+ property: "transformRotationAngle"
42+ from: root.fromAngle; to: root.toAngle
43+ duration: rotationDuration; easing.type: rotationEasing
44+ }
45+ ScriptAction { script: { info.transitioning = false; } }
46+}
qml/Rotation/ImmediateRotationAction.qmlnew file+45
@@ -0,0 +1,45 @@
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.3
18+
19+ScriptAction {
20+ property var info
21+ property var shell
22+
23+ script: {
24+ info.transitioning = true;
25+ shell.orientationAngle = info.requestedOrientationAngle;
26+ shell.transformRotationAngle = info.requestedOrientationAngle;
27+
28+ // Making bindings as orientedShell's dimensions might wiggle during startup.
29+ if (info.requestedOrientationAngle === 90 || info.requestedOrientationAngle === 270) {
30+ shell.width = Qt.binding(function() { return orientedShell.height; });
31+ shell.height = Qt.binding(function() { return orientedShell.width; });
32+ } else {
33+ shell.width = Qt.binding(function() { return orientedShell.width; });
34+ shell.height = Qt.binding(function() { return orientedShell.height; });
35+ }
36+
37+ shell.x = Qt.binding(function() { return (orientedShell.width - shell.width) / 2; });
38+ shell.y = Qt.binding(function() { return (orientedShell.height - shell.height) / 2; });
39+ shell.transformOriginX = Qt.binding(function() { return shell.width / 2; });
40+ shell.transformOriginY = Qt.binding(function() { return shell.height / 2; });
41+
42+ shell.updateFocusedAppOrientation();
43+ info.transitioning = false;
44+ }
45+}
qml/Rotation/NinetyRotationAnimation.qmlnew file+90
@@ -0,0 +1,90 @@
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.3
18+
19+SequentialAnimation {
20+ id: root
21+
22+ property int fromAngle
23+ property int toAngle
24+ property var info
25+ property var shell
26+
27+ readonly property real fromY: fromAngle === 0 || fromAngle === 90 ? 0 : orientedShell.height - orientedShell.width;
28+ readonly property real toY: toAngle === 0 || toAngle === 90 ? 0 : orientedShell.height - orientedShell.width;
29+ readonly property bool flipShellDimensions: toAngle == 90 || toAngle == 270
30+
31+ ScriptAction { script: {
32+ info.transitioning = true;
33+ windowScreenshot.take();
34+ windowScreenshot.visible = true;
35+ shell.orientationAngle = root.toAngle;
36+ shell.x = 0;
37+ shell.width = flipShellDimensions ? orientedShell.height : orientedShell.width;
38+ shell.height = flipShellDimensions ? orientedShell.width : orientedShell.height;
39+ shell.transformOriginX = orientedShell.width / 2;
40+ shell.transformOriginY = orientedShell.width / 2;
41+ shell.updateFocusedAppOrientation();
42+ shellCover.visible = true;
43+
44+ windowScreenshot.transformOriginX = orientedShell.width / 2;
45+ if (fromAngle == 180 || fromAngle == 270) {
46+ windowScreenshot.transformOriginY = orientedShell.height - (orientedShell.width / 2);
47+ } else {
48+ windowScreenshot.transformOriginY = orientedShell.width / 2;
49+ }
50+ } }
51+ ParallelAnimation {
52+ NumberAnimation {
53+ target: shellCover; property: "opacity"; from: 1; to: 0;
54+ duration: rotationDuration; easing.type: rotationEasing
55+ }
56+ RotationAnimation {
57+ target: shell; property: "transformRotationAngle";
58+ from: root.fromAngle; to: root.toAngle
59+ direction: RotationAnimation.Shortest
60+ duration: rotationDuration; easing.type: rotationEasing
61+ }
62+ NumberAnimation {
63+ target: shell; property: "y"
64+ from: root.fromY; to: root.toY
65+ duration: rotationDuration; easing.type: rotationEasing
66+ }
67+
68+ NumberAnimation {
69+ target: windowScreenshot; property: "opacity"; from: 1; to: 0;
70+ duration: rotationDuration; easing.type: rotationEasing
71+ }
72+ RotationAnimation {
73+ target: windowScreenshot; property: "transformRotationAngle";
74+ from: 0; to: root.toAngle - root.fromAngle
75+ direction: RotationAnimation.Shortest
76+ duration: rotationDuration; easing.type: rotationEasing
77+ }
78+ NumberAnimation {
79+ target: windowScreenshot; property: "y"
80+ from: 0; to: root.toY - root.fromY
81+ duration: rotationDuration; easing.type: rotationEasing
82+ }
83+ }
84+ ScriptAction { script: {
85+ windowScreenshot.visible = false;
86+ windowScreenshot.discard();
87+ shellCover.visible = false;
88+ info.transitioning = false;
89+ } }
90+}
qml/Rotation/RotationStates.qmlnew file+278
@@ -0,0 +1,278 @@
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 Ubuntu.Components 1.1
19+import Powerd 0.1
20+
21+// Why the state machine is done that way:
22+// We cannot use regular PropertyChanges{} inside the State elements as steps in the
23+// transition animations must take place in a well defined order.
24+// Which means that we also cannot jump to a new state in the middle of a transition
25+// as that would make hell brake loose.
26+StateGroup {
27+ id: root
28+
29+ // to be set from the outside
30+ property Item orientedShell
31+ property Item shell
32+ property Item shellCover
33+ property Item windowScreenshot
34+
35+ property int rotationDuration: 450
36+ property int rotationEasing: Easing.InOutCubic
37+ // Those values are good for debugging/development
38+ //property int rotationDuration: 3000
39+ //property int rotationEasing: Easing.Linear
40+
41+ state: "0"
42+ states: [
43+ State { name: "0" },
44+ State { name: "90" },
45+ State { name: "180" },
46+ State { name: "270" }
47+ ]
48+
49+ property QtObject d: QtObject {
50+ id: d
51+
52+ property bool startingUp: true
53+ property var finishStartUpTimer: Timer {
54+ interval: 500
55+ onTriggered: d.startingUp = false
56+ }
57+ Component.onCompleted: {
58+ finishStartUpTimer.start();
59+ }
60+
61+ property bool transitioning: false
62+ onTransitioningChanged: {
63+ d.tryUpdateState();
64+ }
65+
66+ readonly property int requestedOrientationAngle: root.orientedShell.acceptedOrientationAngle
67+
68+ // Avoiding a direct call to tryUpdateState() as the state change might trigger an immediate
69+ // change to Shell.orientationAngle which, in its turn, causes a reevaluation of
70+ // requestedOrientationAngle (ie., OrientedShell.acceptedOrientationAngle). A reentrant evaluation
71+ // of a binding is detected by QML as a binding loop and QML will deny the reevalutation, which
72+ // will leave us in a bogus state.
73+ //
74+ // To avoid this mess we update the state in the next event loop iteration, ensuring a clean
75+ // call stack.
76+ onRequestedOrientationAngleChanged: {
77+ stateUpdateTimer.start();
78+ }
79+ property Timer stateUpdateTimer: Timer {
80+ id: stateUpdateTimer
81+ interval: 1
82+ onTriggered: { d.tryUpdateState(); }
83+ }
84+
85+ function tryUpdateState() {
86+ if (d.transitioning || (!d.startingUp && !root.shell.orientationChangesEnabled)) {
87+ return;
88+ }
89+
90+ var requestedState = d.requestedOrientationAngle.toString();
91+ if (requestedState !== root.state) {
92+ d.resolveAnimationType();
93+ root.state = requestedState;
94+ }
95+ }
96+
97+ property Connections shellConnections: Connections {
98+ target: root.shell
99+ onOrientationChangesEnabledChanged: {
100+ d.tryUpdateState();
101+ }
102+ }
103+
104+ property var shellBeingResized: Binding {
105+ target: root.shell
106+ property: "beingResized"
107+ value: d.transitioning
108+ }
109+
110+ readonly property int fullAnimation: 0
111+ readonly property int indicatorsBarAnimation: 1
112+ readonly property int noAnimation: 2
113+
114+ property int animationType
115+
116+ // animationType update *must* take place *before* the state update.
117+ // If animationType and state were updated through bindings, as with normal qml code,
118+ // there would be no guarantee in the order of the binding updates, which could then
119+ // cause the wrong transitions to be chosen for the state changes.
120+ function resolveAnimationType() {
121+ if (d.startingUp) {
122+ // During start up, inital property values are still settling while we're still
123+ // to render the very first frame
124+ 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;
131+ } else {
132+ if (!root.shell.mainApp) {
133+ // shouldn't happen but, anyway
134+ d.animationType = d.fullAnimation;
135+ return;
136+ }
137+
138+ if (root.shell.mainApp.rotatesWindowContents) {
139+ // The application will animate its own GUI, so we don't have to do anything ourselves.
140+ d.animationType = d.noAnimation;
141+ } else if (root.shell.mainAppWindowOrientationAngle == d.requestedOrientationAngle) {
142+ // The app window is already on its final orientation angle.
143+ // So we just animate the indicators bar
144+ // TODO: what if the app is fullscreen?
145+ d.animationType = d.indicatorsBarAnimation;
146+ } else {
147+ d.animationType = d.fullAnimation;
148+ }
149+ }
150+ }
151+
152+ // When an application switch takes place, d.requestedOrientationAngle and
153+ // root.shell.mainAppWindowOrientationAngle get updated separately, at different moments.
154+ // So, when one of those properties change, we shouldn't make a decision straight away
155+ // as the other might be stale and about to be changed. So let's give it a bit of time for
156+ // them to get properly updated.
157+ // This approach is indeed a bit hacky.
158+ property bool appWindowOrientationAngleNeedsUpdateUnstable:
159+ root.shell.orientationAngle === d.requestedOrientationAngle
160+ && root.shell.mainApp
161+ && root.shell.mainAppWindowOrientationAngle !== root.shell.orientationAngle
162+ && !d.transitioning
163+ onAppWindowOrientationAngleNeedsUpdateUnstableChanged: {
164+ stableTimer.restart();
165+ }
166+ property Timer stableTimer: Timer {
167+ interval: 200
168+ onTriggered: {
169+ if (d.appWindowOrientationAngleNeedsUpdateUnstable) {
170+ shell.updateFocusedAppOrientationAnimated();
171+ }
172+ }
173+ }
174+ }
175+
176+ transitions: [
177+ Transition {
178+ from: "90"; to: "0"
179+ enabled: d.animationType == d.fullAnimation
180+ NinetyRotationAnimation { fromAngle: 90; toAngle: 0
181+ info: d; shell: root.shell }
182+ },
183+ Transition {
184+ from: "0"; to: "90"
185+ enabled: d.animationType == d.fullAnimation
186+ NinetyRotationAnimation { fromAngle: 0; toAngle: 90
187+ info: d; shell: root.shell }
188+ },
189+ Transition {
190+ from: "0"; to: "270"
191+ enabled: d.animationType == d.fullAnimation
192+ NinetyRotationAnimation { fromAngle: 0; toAngle: 270
193+ info: d; shell: root.shell }
194+ },
195+ Transition {
196+ from: "270"; to: "0"
197+ enabled: d.animationType == d.fullAnimation
198+ NinetyRotationAnimation { fromAngle: 270; toAngle: 0
199+ info: d; shell: root.shell }
200+ },
201+ Transition {
202+ from: "90"; to: "180"
203+ enabled: d.animationType == d.fullAnimation
204+ NinetyRotationAnimation { fromAngle: 90; toAngle: 180
205+ info: d; shell: root.shell }
206+ },
207+ Transition {
208+ from: "180"; to: "90"
209+ enabled: d.animationType == d.fullAnimation
210+ NinetyRotationAnimation { fromAngle: 180; toAngle: 90
211+ info: d; shell: root.shell }
212+ },
213+ Transition {
214+ from: "180"; to: "270"
215+ enabled: d.animationType == d.fullAnimation
216+ NinetyRotationAnimation { fromAngle: 180; toAngle: 270
217+ info: d; shell: root.shell }
218+ },
219+ Transition {
220+ from: "270"; to: "180"
221+ enabled: d.animationType == d.fullAnimation
222+ NinetyRotationAnimation { fromAngle: 270; toAngle: 180
223+ info: d; shell: root.shell }
224+ },
225+ Transition {
226+ from: "0"; to: "180"
227+ enabled: d.animationType == d.fullAnimation
228+ HalfLoopRotationAnimation { fromAngle: 0; toAngle: 180
229+ info: d; shell: root.shell }
230+ },
231+ Transition {
232+ from: "180"; to: "0"
233+ enabled: d.animationType == d.fullAnimation
234+ HalfLoopRotationAnimation { fromAngle: 180; toAngle: 0
235+ info: d; shell: root.shell }
236+ },
237+ Transition {
238+ from: "90"; to: "270"
239+ enabled: d.animationType == d.fullAnimation
240+ HalfLoopRotationAnimation { fromAngle: 90; toAngle: 270
241+ info: d; shell: root.shell }
242+ },
243+ Transition {
244+ from: "270"; to: "90"
245+ enabled: d.animationType == d.fullAnimation
246+ HalfLoopRotationAnimation { fromAngle: 270; toAngle: 90
247+ info: d; shell: root.shell }
248+ },
249+ Transition {
250+ objectName: "immediateTransition"
251+ enabled: d.animationType == d.noAnimation
252+ ImmediateRotationAction { info: d; shell: root.shell }
253+ },
254+ Transition {
255+ enabled: d.animationType == d.indicatorsBarAnimation
256+ SequentialAnimation {
257+ ScriptAction { script: {
258+ d.transitioning = true;
259+ } }
260+ NumberAnimation {
261+ duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing
262+ target: root.shell; property: "indicatorAreaShowProgress"
263+ from: 1.0; to: 0.0
264+ }
265+ ImmediateRotationAction { info: d; shell: root.shell }
266+ NumberAnimation {
267+ duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing
268+ target: root.shell; property: "indicatorAreaShowProgress"
269+ from: 0.0; to: 1.0
270+ }
271+ ScriptAction { script: {
272+ d.transitioning = false;
273+ }}
274+ }
275+ }
276+ ]
277+
278+}
qml/Shell.qml+161−83
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -45,48 +45,80 @@ import Unity.Indicators 0.1 as Indicators
45Item {
45Item {
46 id: shell
46 id: shell
47
47
48+ // to be set from outside
49+ property int orientationAngle: 0
50+ property int orientation
51+ property int primaryOrientation
52+ property int nativeOrientation
53+ property real nativeWidth
54+ property real nativeHeight
55+ property alias indicatorAreaShowProgress: panel.indicatorAreaShowProgress
56+ property bool beingResized
57+ property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
58+ function updateFocusedAppOrientation() {
59+ applicationsDisplayLoader.item.updateFocusedAppOrientation();
60+ }
61+ function updateFocusedAppOrientationAnimated() {
62+ applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();
63+ }
64+
65+ // to be read from outside
66+ readonly property int mainAppWindowOrientationAngle:
67+ applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindowOrientationAngle : 0
68+
69+ readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
70+ && (applicationsDisplayLoader.item && applicationsDisplayLoader.item.orientationChangesEnabled)
71+ && !greeter.animating
72+
73+ readonly property bool showingGreeter: greeter.shown
74+
75+ property bool startingUp: true
76+ Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
77+
78+ property int supportedOrientations: {
79+ if (startingUp) {
80+ // Ensure we don't rotate during start up
81+ return Qt.PrimaryOrientation;
82+ } else if (greeter.shown) {
83+ return Qt.PrimaryOrientation;
84+ } else if (mainApp) {
85+ return mainApp.supportedOrientations;
86+ } else {
87+ // we just don't care
88+ return Qt.PortraitOrientation
89+ | Qt.LandscapeOrientation
90+ | Qt.InvertedPortraitOrientation
91+ | Qt.InvertedLandscapeOrientation;
92+ }
93+ }
94+
95+ // For autopilot consumption
96+ readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
97+
98+ // internal props from here onwards
99+ readonly property var mainApp:
100+ applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null
101+
48 // Disable everything while greeter is waiting, so that the user can't swipe
102 // Disable everything while greeter is waiting, so that the user can't swipe
49 // the greeter or launcher until we know whether the session is locked.
103 // the greeter or launcher until we know whether the session is locked.
50 enabled: !greeter.waiting
104 enabled: !greeter.waiting
51
105
52− // this is only here to select the width / height of the window if not running fullscreen
53− property bool tablet: false
54− width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
55− height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
56−
57 property real edgeSize: units.gu(2)
106 property real edgeSize: units.gu(2)
58 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
107 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
59 property url background: asImageTester.status == Image.Ready ? asImageTester.source
108 property url background: asImageTester.status == Image.Ready ? asImageTester.source
60 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
109 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
61− readonly property real panelHeight: panel.panelHeight
62−
63− property bool sideStageEnabled: shell.width >= units.gu(100)
64− readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
65−
66− property int orientation
67− readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
68− onDeviceOrientationAngleChanged: {
69− if (!OrientationLock.enabled) {
70− orientation = Screen.orientation;
71− }
72− }
73− readonly property bool orientationLockEnabled: OrientationLock.enabled
74− onOrientationLockEnabledChanged: {
75− if (orientationLockEnabled) {
76− OrientationLock.savedOrientation = Screen.orientation;
77− } else {
78− orientation = Screen.orientation;
79− }
80− }
81
110
82 // This is _only_ used to expose the property to autopilot tests
111 // This is _only_ used to expose the property to autopilot tests
83 readonly property string testShellMode: shellMode
112 readonly property string testShellMode: shellMode
84
113
114+ readonly property alias greeter: greeterLoader.item
115+
85 function activateApplication(appId) {
116 function activateApplication(appId) {
86 if (ApplicationManager.findApplication(appId)) {
117 if (ApplicationManager.findApplication(appId)) {
87 ApplicationManager.requestFocusApplication(appId);
118 ApplicationManager.requestFocusApplication(appId);
88 } else {
119 } else {
89− var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
120+ var execFlags = shell.usageScenario === "phone" ? ApplicationManager.ForceMainStage
121+ : ApplicationManager.NoFlag;
90 ApplicationManager.startApplication(appId, execFlags);
122 ApplicationManager.startApplication(appId, execFlags);
91 }
123 }
92 }
124 }
@@ -110,7 +142,6 @@ Item {
110
142
111 GSettings {
143 GSettings {
112 id: backgroundSettings
144 id: backgroundSettings
113− objectName: "backgroundSettings"
114 schema.id: "org.gnome.desktop.background"
145 schema.id: "org.gnome.desktop.background"
115 }
146 }
116
147
@@ -124,11 +155,6 @@ Item {
124 sourceSize.width: 0
155 sourceSize.width: 0
125 }
156 }
126
157
127− GSettings {
128− id: usageModeSettings
129− schema.id: "com.canonical.Unity8"
130− }
131−
132 Binding {
158 Binding {
133 target: LauncherModel
159 target: LauncherModel
134 property: "applicationManager"
160 property: "applicationManager"
@@ -140,9 +166,7 @@ Item {
140 if (ApplicationManager.count > 0) {
166 if (ApplicationManager.count > 0) {
141 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
167 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
142 }
168 }
143− if (orientationLockEnabled) {
169+ finishStartUpTimer.start();
144− orientation = OrientationLock.savedOrientation;
145− }
146 }
170 }
147
171
148 VolumeControl {
172 VolumeControl {
@@ -180,6 +204,10 @@ Item {
180 Keys.onReleased: physicalKeysMapper.onKeyReleased(event);
204 Keys.onReleased: physicalKeysMapper.onKeyReleased(event);
181 }
205 }
182
206
207+ HomeKeyWatcher {
208+ onActivated: { launcher.fadeOut(); shell.showHome(); }
209+ }
210+
183 Item {
211 Item {
184 id: stages
212 id: stages
185 objectName: "stages"
213 objectName: "stages"
@@ -231,9 +259,21 @@ Item {
231 // theoretical attack where user enters lockedApp mode, then makes
259 // theoretical attack where user enters lockedApp mode, then makes
232 // the screen larger (maybe connects to monitor) and tries to enter
260 // the screen larger (maybe connects to monitor) and tries to enter
233 // tablet mode.
261 // tablet mode.
262+
234− property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
263+ property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
264+ ? "phone"
265+ : shell.usageScenario
266+ source: {
267+ if(shellMode === "greeter") {
268+ return "Stages/ShimStage.qml"
269+ } else if (applicationsDisplayLoader.usageScenario === "phone") {
270+ return "Stages/PhoneStage.qml";
271+ } else if (applicationsDisplayLoader.usageScenario === "tablet") {
272+ return "Stages/TabletStage.qml";
273+ } else {
235− source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
274+ return "Stages/DesktopStage.qml";
236− : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
275+ }
276+ }
237
277
238 property bool interactive: tutorial.spreadEnabled
278 property bool interactive: tutorial.spreadEnabled
239 && !greeter.shown
279 && !greeter.shown
@@ -276,7 +316,12 @@ Item {
276 }
316 }
277 Binding {
317 Binding {
278 target: applicationsDisplayLoader.item
318 target: applicationsDisplayLoader.item
279− property: "orientation"
319+ property: "shellOrientationAngle"
320+ value: shell.orientationAngle
321+ }
322+ Binding {
323+ target: applicationsDisplayLoader.item
324+ property: "shellOrientation"
280 value: shell.orientation
325 value: shell.orientation
281 }
326 }
282 Binding {
327 Binding {
@@ -284,6 +329,31 @@ Item {
284 property: "background"
329 property: "background"
285 value: shell.background
330 value: shell.background
286 }
331 }
332+ Binding {
333+ target: applicationsDisplayLoader.item
334+ property: "shellPrimaryOrientation"
335+ value: shell.primaryOrientation
336+ }
337+ Binding {
338+ target: applicationsDisplayLoader.item
339+ property: "nativeOrientation"
340+ value: shell.nativeOrientation
341+ }
342+ Binding {
343+ target: applicationsDisplayLoader.item
344+ property: "nativeWidth"
345+ value: shell.nativeWidth
346+ }
347+ Binding {
348+ target: applicationsDisplayLoader.item
349+ property: "nativeHeight"
350+ value: shell.nativeHeight
351+ }
352+ Binding {
353+ target: applicationsDisplayLoader.item
354+ property: "beingResized"
355+ value: shell.beingResized
356+ }
287 }
357 }
288
358
289 Tutorial {
359 Tutorial {
@@ -348,50 +418,59 @@ Item {
348 }
418 }
349 }
419 }
350
420
351− Greeter {
421+ Loader {
352− id: greeter
422+ id: greeterLoader
353− objectName: "greeter"
354−
355− hides: [launcher, panel.indicators]
356− tabletMode: shell.sideStageEnabled
357− launcherOffset: launcher.progress
358− forcedUnlock: tutorial.running
359− background: shell.background
360−
361 anchors.fill: parent
423 anchors.fill: parent
362 anchors.topMargin: panel.panelHeight
424 anchors.topMargin: panel.panelHeight
425+ sourceComponent: shellMode != "shell" ? integratedGreeter :
426+ Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
427+ onLoaded: {
428+ item.objectName = "greeter"
429+ }
430+ }
363
431
364− // avoid overlapping with Launcher's edge drag area
432+ Component {
365− // FIXME: Fix TouchRegistry & friends and remove this workaround
433+ id: integratedGreeter
366− // Issue involves launcher's DDA getting disabled on a long
434+ Greeter {
367− // left-edge drag
368− dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
369
435
370− onSessionStarted: {
436+ hides: [launcher, panel.indicators]
371− launcher.hide();
437+ tabletMode: shell.sideStageEnabled
372− }
438+ launcherOffset: launcher.progress
439+ forcedUnlock: tutorial.running
440+ background: shell.background
373
441
374− onTease: {
442+ // avoid overlapping with Launcher's edge drag area
375− if (!tutorial.running) {
443+ // FIXME: Fix TouchRegistry & friends and remove this workaround
444+ // Issue involves launcher's DDA getting disabled on a long
445+ // left-edge drag
446+ dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
447+
448+ onSessionStarted: {
376− launcher.tease();
449+ launcher.hide();
377 }
450 }
378− }
379
451
380− onEmergencyCall: startLockedApp("dialer-app")
452+ onTease: {
453+ if (!tutorial.running) {
454+ launcher.tease();
455+ }
456+ }
381
457
382− Timer {
458+ onEmergencyCall: startLockedApp("dialer-app")
383− // See powerConnection for why this is useful
459+
384− id: showGreeterDelayed
460+ Binding {
385− interval: 1
461+ target: ApplicationManager
386− onTriggered: {
462+ property: "suspended"
387− greeter.forceShow();
463+ value: greeter.shown
388 }
464 }
389 }
465 }
466+ }
390
467
391− Binding {
468+ Timer {
392− target: ApplicationManager
469+ // See powerConnection for why this is useful
393− property: "suspended"
470+ id: showGreeterDelayed
471+ interval: 1
472+ onTriggered: {
394− value: greeter.shown
473+ greeter.forceShow();
395 }
474 }
396 }
475 }
397
476
@@ -480,20 +559,18 @@ Item {
480 expandedPanelHeight: units.gu(7)
559 expandedPanelHeight: units.gu(7)
481
560
482 indicatorsModel: Indicators.IndicatorsModel {
561 indicatorsModel: Indicators.IndicatorsModel {
483− // TODO: This should be sourced by device type (e.g. "desktop", "tablet", "phone"...)
562+ // tablet and phone both use the same profile
484− profile: indicatorProfile
563+ profile: shell.usageScenario === "desktop" ? "desktop" : "phone"
485− Component.onCompleted: load()
564+ Component.onCompleted: load();
486 }
565 }
487 }
566 }
567+
488 callHint {
568 callHint {
489 greeterShown: greeter.shown
569 greeterShown: greeter.shown
490 }
570 }
491
571
492− property bool topmostApplicationIsFullscreen:
572+ property bool mainAppIsFullscreen: shell.mainApp && shell.mainApp.fullscreen
493− ApplicationManager.focusedApplicationId &&
494− ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
495−
496− fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
573+ fullscreenMode: (mainAppIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
497 || greeter.hasLockedApp
574 || greeter.hasLockedApp
498 }
575 }
499
576
@@ -511,7 +588,7 @@ Item {
511 available: tutorial.launcherEnabled
588 available: tutorial.launcherEnabled
512 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
589 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
513 && !greeter.hasLockedApp
590 && !greeter.hasLockedApp
514− inverted: usageModeSettings.usageMode === "Staged"
591+ inverted: shell.usageScenario !== "desktop"
515 shadeBackground: !tutorial.running
592 shadeBackground: !tutorial.running
516
593
517 onShowDashHome: showHome()
594 onShowDashHome: showHome()
@@ -553,7 +630,7 @@ Item {
553 Rectangle {
630 Rectangle {
554 id: modalNotificationBackground
631 id: modalNotificationBackground
555
632
556− visible: notifications.useModal && (notifications.state == "narrow")
633+ visible: notifications.useModal
557 color: "#000000"
634 color: "#000000"
558 anchors.fill: parent
635 anchors.fill: parent
559 opacity: 0.9
636 opacity: 0.9
@@ -598,6 +675,7 @@ Item {
598
675
599 Dialogs {
676 Dialogs {
600 id: dialogs
677 id: dialogs
678+ objectName: "dialogs"
601 anchors.fill: parent
679 anchors.fill: parent
602 z: overlay.z + 10
680 z: overlay.z + 10
603 onPowerOffClicked: {
681 onPowerOffClicked: {
qml/Stages/ApplicationWindow.qml+53−18
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2014-2015 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
5 * it under the terms of the GNU Lesser General Public License as published by
@@ -25,10 +25,11 @@ FocusScope {
25 // to be read from outside
25 // to be read from outside
26 readonly property bool fullscreen: application ? application.fullscreen : false
26 readonly property bool fullscreen: application ? application.fullscreen : false
27 property alias interactive: sessionContainer.interactive
27 property alias interactive: sessionContainer.interactive
28+ property bool orientationChangesEnabled: d.supportsSurfaceResize ? d.surfaceOldEnoughToBeResized : true
28
29
29 // to be set from outside
30 // to be set from outside
30 property QtObject application
31 property QtObject application
31− property int orientation
32+ property int surfaceOrientationAngle
32
33
33 function removeScreenshot() {
34 function removeScreenshot() {
34 sessionGrabber.removeScreenshot();
35 sessionGrabber.removeScreenshot();
@@ -68,6 +69,15 @@ FocusScope {
68 // Remove this when possible
69 // Remove this when possible
69 property bool surfaceInitialized: false
70 property bool surfaceInitialized: false
70
71
72+ property bool supportsSurfaceResize:
73+ application &&
74+ ((application.supportedOrientations & Qt.PortraitOrientation)
75+ || (application.supportedOrientations & Qt.InvertedPortraitOrientation))
76+ &&
77+ ((application.supportedOrientations & Qt.LandscapeOrientation)
78+ || (application.supportedOrientations & Qt.InvertedLandscapeOrientation))
79+
80+ property bool surfaceOldEnoughToBeResized: false
71 }
81 }
72
82
73 Timer {
83 Timer {
@@ -76,6 +86,12 @@ FocusScope {
76 onTriggered: { if (sessionContainer.surface) {d.surfaceInitialized = true;} }
86 onTriggered: { if (sessionContainer.surface) {d.surfaceInitialized = true;} }
77 }
87 }
78
88
89+ Timer {
90+ id: surfaceIsOldTimer
91+ interval: 1000
92+ onTriggered: { if (stateGroup.state === "surface") { d.surfaceOldEnoughToBeResized = true; } }
93+ }
94+
79 Loader {
95 Loader {
80 id: splashLoader
96 id: splashLoader
81 objectName: "splashLoader"
97 objectName: "splashLoader"
@@ -137,7 +153,8 @@ FocusScope {
137 // A fake application might not even have a session property.
153 // A fake application might not even have a session property.
138 session: application && application.session ? application.session : null
154 session: application && application.session ? application.session : null
139 anchors.fill: parent
155 anchors.fill: parent
140− orientation: root.orientation
156+
157+ surfaceOrientationAngle: application && application.rotatesWindowContents ? root.surfaceOrientationAngle : 0
141
158
142 onSurfaceChanged: {
159 onSurfaceChanged: {
143 if (sessionContainer.surface) {
160 if (sessionContainer.surface) {
@@ -203,15 +220,21 @@ FocusScope {
203 UbuntuNumberAnimation { target: sessionContainer.surfaceContainer; property: "opacity";
220 UbuntuNumberAnimation { target: sessionContainer.surfaceContainer; property: "opacity";
204 from: 0.0; to: 1.0
221 from: 0.0; to: 1.0
205 duration: UbuntuAnimation.BriskDuration }
222 duration: UbuntuAnimation.BriskDuration }
223+ ScriptAction { script: {
206− PropertyAction { target: splashLoader; property: "active"; value: false }
224+ splashLoader.active = false;
225+ surfaceIsOldTimer.start();
226+ } }
207 }
227 }
208 },
228 },
209 Transition {
229 Transition {
210 from: "surface"; to: "splashScreen"
230 from: "surface"; to: "splashScreen"
211 SequentialAnimation {
231 SequentialAnimation {
232+ ScriptAction { script: {
233+ surfaceIsOldTimer.stop();
234+ d.surfaceOldEnoughToBeResized = false;
212− PropertyAction { target: splashLoader; property: "active"; value: true }
235+ splashLoader.active = true;
213− PropertyAction { target: sessionContainer.surfaceContainer
236+ sessionContainer.surfaceContainer.visible = true;
214− property: "visible"; value: true }
237+ } }
215 UbuntuNumberAnimation { target: splashLoader; property: "opacity";
238 UbuntuNumberAnimation { target: splashLoader; property: "opacity";
216 from: 0.0; to: 1.0
239 from: 0.0; to: 1.0
217 duration: UbuntuAnimation.BriskDuration }
240 duration: UbuntuAnimation.BriskDuration }
@@ -222,14 +245,18 @@ FocusScope {
222 Transition {
245 Transition {
223 from: "surface"; to: "screenshot"
246 from: "surface"; to: "screenshot"
224 SequentialAnimation {
247 SequentialAnimation {
248+ ScriptAction { script: {
249+ surfaceIsOldTimer.stop();
250+ d.surfaceOldEnoughToBeResized = false;
225− PropertyAction { target: screenshotImage
251+ screenshotImage.visible = true;
226− property: "visible"; value: true }
252+ } }
227 UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
253 UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
228 from: 0.0; to: 1.0
254 from: 0.0; to: 1.0
229 duration: UbuntuAnimation.BriskDuration }
255 duration: UbuntuAnimation.BriskDuration }
256+ ScriptAction { script: {
230− PropertyAction { target: sessionContainer.surfaceContainer
257+ sessionContainer.surfaceContainer.visible = false;
231− property: "visible"; value: false }
232− ScriptAction { script: { if (sessionContainer.session) { sessionContainer.session.release(); } } }
258+ if (sessionContainer.session) { sessionContainer.session.release(); }
259+ } }
233 }
260 }
234 },
261 },
235 Transition {
262 Transition {
@@ -240,8 +267,11 @@ FocusScope {
240 UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
267 UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
241 from: 1.0; to: 0.0
268 from: 1.0; to: 0.0
242 duration: UbuntuAnimation.BriskDuration }
269 duration: UbuntuAnimation.BriskDuration }
270+ ScriptAction { script: {
243− PropertyAction { target: screenshotImage; property: "visible"; value: false }
271+ screenshotImage.visible = false;
244− PropertyAction { target: screenshotImage; property: "source"; value: "" }
272+ screenshotImage.source = "";
273+ surfaceIsOldTimer.start();
274+ } }
245 }
275 }
246 },
276 },
247 Transition {
277 Transition {
@@ -257,10 +287,12 @@ FocusScope {
257 },
287 },
258 Transition {
288 Transition {
259 from: "surface"; to: "void"
289 from: "surface"; to: "void"
260− SequentialAnimation {
290+ ScriptAction { script: {
261− PropertyAction { target: sessionContainer.surfaceContainer; property: "visible"; value: false }
291+ surfaceIsOldTimer.stop();
292+ d.surfaceOldEnoughToBeResized = false;
293+ sessionContainer.surfaceContainer.visible = false;
262− ScriptAction { script: { if (sessionContainer.session) { sessionContainer.session.release(); } } }
294+ if (sessionContainer.session) { sessionContainer.session.release(); }
263− }
295+ } }
264 },
296 },
265 Transition {
297 Transition {
266 from: "void"; to: "surface"
298 from: "void"; to: "surface"
@@ -270,6 +302,9 @@ FocusScope {
270 UbuntuNumberAnimation { target: sessionContainer.surfaceContainer; property: "opacity";
302 UbuntuNumberAnimation { target: sessionContainer.surfaceContainer; property: "opacity";
271 from: 0.0; to: 1.0
303 from: 0.0; to: 1.0
272 duration: UbuntuAnimation.BriskDuration }
304 duration: UbuntuAnimation.BriskDuration }
305+ ScriptAction { script: {
306+ surfaceIsOldTimer.start();
307+ } }
273 }
308 }
274 }
309 }
275 ]
310 ]
qml/Stages/DecoratedWindow.qml+1
@@ -42,6 +42,7 @@ FocusScope {
42
42
43 WindowDecoration {
43 WindowDecoration {
44 id: decoration
44 id: decoration
45+ objectName: application ? "appWindowDecoration_" + application.appId : "appWindowDecoration_null"
45 anchors { left: parent.left; top: parent.top; right: parent.right }
46 anchors { left: parent.left; top: parent.top; right: parent.right }
46 height: units.gu(3)
47 height: units.gu(3)
47 title: model.name
48 title: model.name
qml/Stages/DesktopStage.qml+43−14
@@ -20,16 +20,36 @@ import QtQuick 2.3
20import Ubuntu.Components 1.1
20import Ubuntu.Components 1.1
21import Unity.Application 0.1
21import Unity.Application 0.1
22import "../Components/PanelState"
22import "../Components/PanelState"
23−import Utils 0.1
24
23
25−FocusScope {
24+Rectangle {
26 id: root
25 id: root
27
26
28 anchors.fill: parent
27 anchors.fill: parent
29
28
30− property alias background: wallpaper.source
29+ // Controls to be set from outside
30+ property int dragAreaWidth // just to comply with the interface shared between stages
31+ property real maximizedAppTopMargin
32+ property bool interactive
33+ property bool spreadEnabled // just to comply with the interface shared between stages
34+ property real inverseProgress: 0 // just to comply with the interface shared between stages
35+ property int shellOrientationAngle: 0
36+ property int shellOrientation
37+ property int shellPrimaryOrientation
38+ property int nativeOrientation
39+ property bool beingResized: false
40+
41+ // functions to be called from outside
42+ function updateFocusedAppOrientation() { /* TODO */ }
43+ function updateFocusedAppOrientationAnimated() { /* TODO */}
44+
45+ // To be read from outside
46+ readonly property var mainApp: ApplicationManager.focusedApplicationId
47+ ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
48+ : null
49+ property int mainAppWindowOrientationAngle: 0
50+ readonly property bool orientationChangesEnabled: false
31
51
32− property var windowStateStorage: WindowStateStorage
52+ property alias background: wallpaper.source
33
53
34 CrossFadeImage {
54 CrossFadeImage {
35 id: wallpaper
55 id: wallpaper
@@ -58,7 +78,16 @@ FocusScope {
58 id: priv
78 id: priv
59
79
60 readonly property string focusedAppId: ApplicationManager.focusedApplicationId
80 readonly property string focusedAppId: ApplicationManager.focusedApplicationId
61− readonly property var focusedAppDelegate: focusedAppId ? appRepeater.itemAt(indexOf(focusedAppId)) : null
81+ readonly property var focusedAppDelegate: {
82+ var index = indexOf(focusedAppId);
83+ return index >= 0 && index < appRepeater.count ? appRepeater.itemAt(index) : null
84+ }
85+
86+ onFocusedAppDelegateChanged: {
87+ if (focusedAppDelegate) {
88+ focusedAppDelegate.focus = true;
89+ }
90+ }
62
91
63 function indexOf(appId) {
92 function indexOf(appId) {
64 for (var i = 0; i < ApplicationManager.count; i++) {
93 for (var i = 0; i < ApplicationManager.count; i++) {
@@ -89,13 +118,19 @@ FocusScope {
89 id: appRepeater
118 id: appRepeater
90 model: ApplicationManager
119 model: ApplicationManager
91
120
92− delegate: Item {
121+ delegate: FocusScope {
93 id: appDelegate
122 id: appDelegate
94 z: ApplicationManager.count - index
123 z: ApplicationManager.count - index
95 y: units.gu(3)
124 y: units.gu(3)
96 width: units.gu(60)
125 width: units.gu(60)
97 height: units.gu(50)
126 height: units.gu(50)
98
127
128+ onFocusChanged: {
129+ if (focus) {
130+ ApplicationManager.requestFocusApplication(model.appId);
131+ }
132+ }
133+
99 readonly property int minWidth: units.gu(10)
134 readonly property int minWidth: units.gu(10)
100 readonly property int minHeight: units.gu(10)
135 readonly property int minHeight: units.gu(10)
101
136
@@ -119,14 +154,13 @@ FocusScope {
119 ]
154 ]
120
155
121 WindowMoveResizeArea {
156 WindowMoveResizeArea {
122− windowStateStorage: root.windowStateStorage
123 target: appDelegate
157 target: appDelegate
124 minWidth: appDelegate.minWidth
158 minWidth: appDelegate.minWidth
125 minHeight: appDelegate.minHeight
159 minHeight: appDelegate.minHeight
126 resizeHandleWidth: units.gu(0.5)
160 resizeHandleWidth: units.gu(0.5)
127 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
161 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
128
162
129− onPressed: decoratedWindow.focus = true;
163+ onPressed: appDelegate.focus = true;
130 }
164 }
131
165
132 DecoratedWindow {
166 DecoratedWindow {
@@ -135,12 +169,7 @@ FocusScope {
135 anchors.fill: parent
169 anchors.fill: parent
136 application: ApplicationManager.get(index)
170 application: ApplicationManager.get(index)
137 active: ApplicationManager.focusedApplicationId === model.appId
171 active: ApplicationManager.focusedApplicationId === model.appId
138−
139− onFocusChanged: {
140− if (focus) {
172+ focus: true
141− ApplicationManager.requestFocusApplication(model.appId);
142− }
143− }
144
173
145 onClose: ApplicationManager.stopApplication(model.appId)
174 onClose: ApplicationManager.stopApplication(model.appId)
146 onMaximize: appDelegate.state = (appDelegate.state == "maximized" ? "normal" : "maximized")
175 onMaximize: appDelegate.state = (appDelegate.state == "maximized" ? "normal" : "maximized")
qml/Stages/OrientationChangeAnimation.qmlnew file+241
@@ -0,0 +1,241 @@
1+/*
2+ * Copyright 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 Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+
19+QtObject {
20+ id: root
21+
22+ // to be set from outside
23+ property Item spreadDelegate
24+ property Item background
25+ property Item window
26+ property Item screenshot
27+
28+ function start() {
29+ if (window.orientationAngle === 0) {
30+ if (spreadDelegate.shellOrientationAngle === 90) {
31+ chosenAnimation = simple90Animation;
32+ } else if (spreadDelegate.shellOrientationAngle === 180) {
33+ chosenAnimation = halfLoopAnimation;
34+ } else if (spreadDelegate.shellOrientationAngle === 270) {
35+ chosenAnimation = moving90Animation;
36+ } else {
37+ chosenAnimation = null;
38+ }
39+ } else if (window.orientationAngle === 90) {
40+ if (spreadDelegate.shellOrientationAngle === 0) {
41+ chosenAnimation = simple90Animation;
42+ } else if (spreadDelegate.shellOrientationAngle === 180) {
43+ chosenAnimation = moving90Animation;
44+ } else if (spreadDelegate.shellOrientationAngle === 270) {
45+ chosenAnimation = halfLoopAnimation;
46+ } else {
47+ chosenAnimation = null;
48+ }
49+ } else if (window.orientationAngle === 180) {
50+ if (spreadDelegate.shellOrientationAngle === 0) {
51+ chosenAnimation = halfLoopAnimation;
52+ } else if (spreadDelegate.shellOrientationAngle === 90) {
53+ chosenAnimation = moving90Animation;
54+ } else if (spreadDelegate.shellOrientationAngle === 270) {
55+ chosenAnimation = simple90Animation;
56+ } else {
57+ chosenAnimation = null;
58+ }
59+ } else if (window.orientationAngle === 270) {
60+ if (spreadDelegate.shellOrientationAngle === 0) {
61+ chosenAnimation = moving90Animation;
62+ } else if (spreadDelegate.shellOrientationAngle === 90) {
63+ chosenAnimation = halfLoopAnimation;
64+ } else if (spreadDelegate.shellOrientationAngle === 180) {
65+ chosenAnimation = simple90Animation;
66+ } else {
67+ chosenAnimation = null;
68+ }
69+ }
70+
71+ if (chosenAnimation)
72+ chosenAnimation.start();
73+ }
74+
75+ // to be read from outside
76+ property bool running: chosenAnimation ? chosenAnimation.running : false
77+
78+ property int duration: 450
79+ property int easingType: Easing.InOutCubic
80+
81+ property int shortestDimension: spreadDelegate.width < spreadDelegate.height
82+ ? spreadDelegate.width : spreadDelegate.height
83+ property int longestDimension: spreadDelegate.width > spreadDelegate.height
84+ ? spreadDelegate.width : spreadDelegate.height
85+ property string longestAxis: spreadDelegate.width > spreadDelegate.height ? "x" : "y"
86+
87+ property QtObject chosenAnimation
88+
89+ function setup90Animation() {
90+ background.visible = true;
91+
92+ screenshot.width = window.width;
93+ screenshot.height = window.height;
94+ screenshot.window.anchors.topMargin = window.window.anchors.topMargin;
95+ screenshot.take();
96+ screenshot.transformOriginX = root.shortestDimension / 2;
97+ screenshot.transformOriginY = root.shortestDimension / 2;
98+ screenshot.visible = true;
99+
100+ window.rotation = 0;
101+ window.width = spreadDelegate.width;
102+ window.height = spreadDelegate.height;
103+ window.transformOriginX = root.shortestDimension / 2;
104+ window.transformOriginY = root.shortestDimension / 2;
105+ }
106+
107+ function tearDown90Animation() {
108+ window.orientationAngle = spreadDelegate.shellOrientationAngle;
109+ screenshot.discard();
110+ screenshot.visible = false;
111+ background.visible = false;
112+ }
113+
114+ property QtObject simple90Animation: SequentialAnimation {
115+ id: simple90Animation
116+
117+ ScriptAction { script: setup90Animation() }
118+ ParallelAnimation {
119+ RotationAnimation {
120+ target: root.window
121+ duration: root.duration
122+ easing.type: root.easingType
123+ from: window.orientationAngle - spreadDelegate.shellOrientationAngle
124+ to: 0
125+ property: "transformRotationAngle"
126+ }
127+ RotationAnimation {
128+ target: root.screenshot
129+ duration: root.duration
130+ easing.type: root.easingType
131+ from: window.orientationAngle - spreadDelegate.shellOrientationAngle
132+ to: 0
133+ property: "transformRotationAngle"
134+ }
135+ NumberAnimation {
136+ target: root.screenshot
137+ duration: root.duration
138+ easing.type: root.easingType
139+ property: "opacity"
140+ from: 1.0
141+ to: 0.0
142+ }
143+ NumberAnimation {
144+ target: root.window
145+ duration: root.duration
146+ easing.type: root.easingType
147+ property: "opacity"
148+ from: 0.0
149+ to: 1.0
150+ }
151+ }
152+ ScriptAction { script: tearDown90Animation() }
153+ }
154+
155+ property QtObject moving90Animation: SequentialAnimation {
156+ id: moving90Animation
157+
158+ ScriptAction { script: setup90Animation() }
159+ ParallelAnimation {
160+ RotationAnimation {
161+ target: root.window
162+ duration: root.duration
163+ easing.type: root.easingType
164+ direction: RotationAnimation.Shortest
165+ from: window.orientationAngle - spreadDelegate.shellOrientationAngle
166+ to: 0
167+ property: "transformRotationAngle"
168+ }
169+ RotationAnimation {
170+ target: root.screenshot
171+ duration: root.duration
172+ easing.type: root.easingType
173+ direction: RotationAnimation.Shortest
174+ from: window.orientationAngle - spreadDelegate.shellOrientationAngle
175+ to: 0
176+ property: "transformRotationAngle"
177+ }
178+ NumberAnimation {
179+ target: root.screenshot
180+ duration: root.duration
181+ easing.type: root.easingType
182+ property: "opacity"
183+ from: 1.0
184+ to: 0.0
185+ }
186+ NumberAnimation {
187+ target: root.window
188+ duration: root.duration
189+ easing.type: root.easingType
190+ property: "opacity"
191+ from: 0.0
192+ to: 1.0
193+ }
194+ NumberAnimation {
195+ target: root.window
196+ duration: root.duration
197+ easing.type: root.easingType
198+ property: root.longestAxis
199+ from: root.longestDimension - root.shortestDimension
200+ to: 0
201+ }
202+ NumberAnimation {
203+ target: root.screenshot
204+ duration: root.duration
205+ easing.type: root.easingType
206+ property: root.longestAxis
207+ from: root.longestDimension - root.shortestDimension
208+ to: 0
209+ }
210+ }
211+ ScriptAction { script: tearDown90Animation() }
212+ }
213+
214+ property QtObject halfLoopAnimation: SequentialAnimation {
215+ id: halfLoopAnimation
216+
217+ ScriptAction { script: {
218+ background.visible = true;
219+
220+ window.rotation = 0;
221+ window.width = spreadDelegate.width;
222+ window.height = spreadDelegate.height;
223+ window.transformOriginX = window.width / 2
224+ window.transformOriginY = window.height / 2
225+ } }
226+ ParallelAnimation {
227+ RotationAnimation {
228+ target: root.window
229+ duration: root.duration
230+ easing.type: root.easingType
231+ from: window.orientationAngle - spreadDelegate.shellOrientationAngle
232+ to: 0
233+ property: "transformRotationAngle"
234+ }
235+ }
236+ ScriptAction { script: {
237+ window.orientationAngle = spreadDelegate.shellOrientationAngle;
238+ background.visible = false;
239+ } }
240+ }
241+}
qml/Stages/PhoneStage.qml+171−82
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -31,12 +31,64 @@ Rectangle {
31 property bool interactive
31 property bool interactive
32 property bool spreadEnabled: true // If false, animations and right edge will be disabled
32 property bool spreadEnabled: true // If false, animations and right edge will be disabled
33 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
33 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
34− property int orientation: Qt.PortraitOrientation
35 property QtObject applicationManager: ApplicationManager
34 property QtObject applicationManager: ApplicationManager
36 property bool focusFirstApp: true // If false, focused app will appear on right edge like other apps
35 property bool focusFirstApp: true // If false, focused app will appear on right edge like other apps
37 property bool altTabEnabled: true
36 property bool altTabEnabled: true
38 property real startScale: 1.1
37 property real startScale: 1.1
39 property real endScale: 0.7
38 property real endScale: 0.7
39+ property int shellOrientationAngle: 0
40+ property int shellOrientation
41+ property int shellPrimaryOrientation
42+ property int nativeOrientation
43+ property real nativeWidth
44+ property real nativeHeight
45+ property bool beingResized: false
46+ onBeingResizedChanged: {
47+ if (beingResized) {
48+ // Brace yourselves for impact!
49+ spreadView.selectedIndex = -1;
50+ spreadView.phase = 0;
51+ spreadView.contentX = -spreadView.shift;
52+ }
53+ }
54+ function updateFocusedAppOrientation() {
55+ if (spreadRepeater.count > 0) {
56+ spreadRepeater.itemAt(0).matchShellOrientation();
57+ }
58+
59+ for (var i = 1; i < spreadRepeater.count; ++i) {
60+
61+ var spreadDelegate = spreadRepeater.itemAt(i);
62+
63+ var delta = spreadDelegate.appWindowOrientationAngle - root.shellOrientationAngle;
64+ if (delta < 0) { delta += 360; }
65+ delta = delta % 360;
66+
67+ var supportedOrientations = spreadDelegate.application.supportedOrientations;
68+ if (supportedOrientations === Qt.PrimaryOrientation) {
69+ supportedOrientations = spreadDelegate.shellPrimaryOrientation;
70+ }
71+
72+ if (delta === 180 && (supportedOrientations & spreadDelegate.shellOrientation)) {
73+ spreadDelegate.matchShellOrientation();
74+ }
75+ }
76+ }
77+ function updateFocusedAppOrientationAnimated() {
78+ if (spreadRepeater.count > 0) {
79+ spreadRepeater.itemAt(0).animateToShellOrientation();
80+ }
81+ }
82+
83+ // To be read from outside
84+ readonly property var mainApp: applicationManager.focusedApplicationId
85+ ? applicationManager.findApplication(applicationManager.focusedApplicationId)
86+ : null
87+ property int mainAppWindowOrientationAngle: 0
88+ readonly property bool orientationChangesEnabled: priv.focusedAppOrientationChangesEnabled
89+ && !priv.focusedAppDelegateIsDislocated
90+ && !(priv.focusedAppDelegate && priv.focusedAppDelegate.xBehavior.running)
91+ && spreadView.phase === 0
40
92
41 // How far left the stage has been dragged
93 // How far left the stage has been dragged
42 readonly property real dragProgress: spreadRepeater.count > 0 ? -spreadRepeater.itemAt(0).xTranslate : 0
94 readonly property real dragProgress: spreadRepeater.count > 0 ? -spreadRepeater.itemAt(0).xTranslate : 0
@@ -54,12 +106,6 @@ Rectangle {
54 spreadView.snapTo(priv.indexOf(appId));
106 spreadView.snapTo(priv.indexOf(appId));
55 }
107 }
56
108
57− onWidthChanged: {
58− spreadView.selectedIndex = -1;
59− spreadView.phase = 0;
60− spreadView.contentX = -spreadView.shift;
61− }
62−
63 onInverseProgressChanged: {
109 onInverseProgressChanged: {
64 // This can't be a simple binding because that would be triggered after this handler
110 // This can't be a simple binding because that would be triggered after this handler
65 // while we need it active before doing the anition left/right
111 // while we need it active before doing the anition left/right
@@ -73,6 +119,20 @@ Rectangle {
73 priv.oldInverseProgress = inverseProgress;
119 priv.oldInverseProgress = inverseProgress;
74 }
120 }
75
121
122+ // <FIXME-contentX> See rationale in the next comment with this tag
123+ onWidthChanged: {
124+ if (!root.beingResized) {
125+ // we're being resized without a warning (ie, the corresponding property wasn't set
126+ root.beingResized = true;
127+ beingResizedTimer.start();
128+ }
129+ }
130+ Timer {
131+ id: beingResizedTimer
132+ interval: 100
133+ onTriggered: { root.beingResized = false; }
134+ }
135+
76 Connections {
136 Connections {
77 target: applicationManager
137 target: applicationManager
78
138
@@ -115,45 +175,57 @@ Rectangle {
115 QtObject {
175 QtObject {
116 id: priv
176 id: priv
117
177
178+ property string focusedAppId: root.applicationManager.focusedApplicationId
179+ property bool focusedAppOrientationChangesEnabled: false
118 readonly property int firstSpreadIndex: root.focusFirstApp ? 1 : 0
180 readonly property int firstSpreadIndex: root.focusFirstApp ? 1 : 0
119− property string focusedAppId: applicationManager.focusedApplicationId
120− property var focusedApplication: applicationManager.findApplication(focusedAppId)
121− property var focusedAppDelegate: null
181+ readonly property var focusedAppDelegate: {
182+ var index = indexOf(focusedAppId);
183+ return index >= 0 && index < spreadRepeater.count ? spreadRepeater.itemAt(index) : null
184+ }
122
185
123 property real oldInverseProgress: 0
186 property real oldInverseProgress: 0
124 property bool animateX: false
187 property bool animateX: false
125
188
126− onFocusedAppIdChanged: focusedAppDelegate = spreadRepeater.itemAt(0);
127−
128 onFocusedAppDelegateChanged: {
189 onFocusedAppDelegateChanged: {
129 if (focusedAppDelegate) {
190 if (focusedAppDelegate) {
130 focusedAppDelegate.focus = true;
191 focusedAppDelegate.focus = true;
131 }
192 }
132 }
193 }
133
194
195+ property bool focusedAppDelegateIsDislocated: focusedAppDelegate && focusedAppDelegate.x !== 0
196+
134 function indexOf(appId) {
197 function indexOf(appId) {
135− for (var i = 0; i < applicationManager.count; i++) {
198+ for (var i = 0; i < root.applicationManager.count; i++) {
136− if (applicationManager.get(i).appId == appId) {
199+ if (root.applicationManager.get(i).appId == appId) {
137 return i;
200 return i;
138 }
201 }
139 }
202 }
140 return -1;
203 return -1;
141 }
204 }
142
205
206+ // Is more stable than "spreadView.shiftedContentX === 0" as it filters out noise caused by
207+ // Flickable.contentX changing due to resizes.
208+ property bool fullyShowingFocusedApp: true
209+ }
210+ Timer {
211+ id: fullyShowingFocusedAppUpdateTimer
212+ interval: 100
213+ onTriggered: {
214+ priv.fullyShowingFocusedApp = spreadView.shiftedContentX === 0;
215+ }
143 }
216 }
144
217
145 Flickable {
218 Flickable {
146 id: spreadView
219 id: spreadView
147 objectName: "spreadView"
220 objectName: "spreadView"
148 anchors.fill: parent
221 anchors.fill: parent
149− interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
150− && draggedDelegateCount === 0
222+ interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
151 contentWidth: spreadRow.width - shift
223 contentWidth: spreadRow.width - shift
152 contentX: -shift
224 contentX: -shift
153
225
154 // This indicates when the spreadView is active. That means, all the animations
226 // This indicates when the spreadView is active. That means, all the animations
155 // are activated and tiles need to line up for the spread.
227 // are activated and tiles need to line up for the spread.
156− readonly property bool active: shiftedContentX > 0 || spreadDragArea.status === DirectionalDragArea.Recognized || !root.focusFirstApp
228+ readonly property bool active: shiftedContentX > 0 || spreadDragArea.dragging || !root.focusFirstApp
157
229
158 // The flickable needs to fill the screen in order to get touch events all over.
230 // The flickable needs to fill the screen in order to get touch events all over.
159 // However, we don't want to the user to be able to scroll back all the way. For
231 // However, we don't want to the user to be able to scroll back all the way. For
@@ -188,9 +260,25 @@ Rectangle {
188 property int draggedDelegateCount: 0
260 property int draggedDelegateCount: 0
189 property int closingIndex: -1
261 property int closingIndex: -1
190
262
191− property bool focusChanging: false
263+ // <FIXME-contentX> Workaround Flickable's behavior of bringing contentX back between valid boundaries
264+ // when resized. The proper way to fix this is refactoring PhoneStage so that it doesn't
265+ // rely on having Flickable.contentX keeping an out-of-bounds value when it's set programatically
266+ // (as opposed to having contentX reaching an out-of-bounds value through dragging, which will trigger
267+ // the Flickable.boundsBehavior upon release).
268+ onContentXChanged: { forceItToRemainStillIfBeingResized(); }
269+ onShiftChanged: { forceItToRemainStillIfBeingResized(); }
270+ function forceItToRemainStillIfBeingResized() {
271+ if (root.beingResized && contentX != -spreadView.shift) {
272+ contentX = -spreadView.shift;
273+ }
274+ }
192
275
193 onShiftedContentXChanged: {
276 onShiftedContentXChanged: {
277+ if (root.beingResized) {
278+ // Flickabe.contentX wiggles during resizes. Don't react to it.
279+ return;
280+ }
281+
194 switch (phase) {
282 switch (phase) {
195 case 0:
283 case 0:
196 if (shiftedContentX > width * positionMarker2) {
284 if (shiftedContentX > width * positionMarker2) {
@@ -205,6 +293,7 @@ Rectangle {
205 }
293 }
206 break;
294 break;
207 }
295 }
296+ fullyShowingFocusedAppUpdateTimer.restart();
208 }
297 }
209
298
210 function snap() {
299 function snap() {
@@ -229,7 +318,7 @@ Rectangle {
229 snapAnimation.start();
318 snapAnimation.start();
230 return;
319 return;
231 }
320 }
232− if (applicationManager.count <= index) {
321+ if (root.applicationManager.count <= index) {
233 // In case we're trying to snap to some non existing app, lets snap back to the first one
322 // In case we're trying to snap to some non existing app, lets snap back to the first one
234 index = 0;
323 index = 0;
235 }
324 }
@@ -266,7 +355,7 @@ Rectangle {
266 ScriptAction {
355 ScriptAction {
267 script: {
356 script: {
268 if (spreadView.selectedIndex >= 0) {
357 if (spreadView.selectedIndex >= 0) {
269− applicationManager.focusApplication(applicationManager.get(spreadView.selectedIndex).appId);
358+ root.applicationManager.focusApplication(root.applicationManager.get(spreadView.selectedIndex).appId);
270
359
271 spreadView.selectedIndex = -1;
360 spreadView.selectedIndex = -1;
272 spreadView.phase = 0;
361 spreadView.phase = 0;
@@ -281,7 +370,7 @@ Rectangle {
281 // This width controls how much the spread can be flicked left/right. It's composed of:
370 // This width controls how much the spread can be flicked left/right. It's composed of:
282 // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)
371 // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)
283 // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen
372 // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen
284− width: Math.max(3, applicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
373+ width: Math.max(3, root.applicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
285 height: parent.height
374 height: parent.height
286 Behavior on width {
375 Behavior on width {
287 enabled: spreadView.closingIndex >= 0
376 enabled: spreadView.closingIndex >= 0
@@ -304,7 +393,7 @@ Rectangle {
304 Repeater {
393 Repeater {
305 id: spreadRepeater
394 id: spreadRepeater
306 objectName: "spreadRepeater"
395 objectName: "spreadRepeater"
307− model: applicationManager
396+ model: root.applicationManager
308 delegate: TransformedSpreadDelegate {
397 delegate: TransformedSpreadDelegate {
309 id: appDelegate
398 id: appDelegate
310 objectName: "appDelegate" + index
399 objectName: "appDelegate" + index
@@ -319,11 +408,10 @@ Rectangle {
319 selected: spreadView.selectedIndex == index
408 selected: spreadView.selectedIndex == index
320 otherSelected: spreadView.selectedIndex >= 0 && !selected
409 otherSelected: spreadView.selectedIndex >= 0 && !selected
321 interactive: !spreadView.interactive && spreadView.phase === 0
410 interactive: !spreadView.interactive && spreadView.phase === 0
322− && spreadView.shiftedContentX === 0 && root.interactive && isFocused
411+ && priv.fullyShowingFocusedApp && root.interactive && isFocused
323 swipeToCloseEnabled: spreadView.interactive && root.interactive && !snapAnimation.running
412 swipeToCloseEnabled: spreadView.interactive && root.interactive && !snapAnimation.running
324 maximizedAppTopMargin: root.maximizedAppTopMargin
413 maximizedAppTopMargin: root.maximizedAppTopMargin
325− dropShadow: spreadView.active ||
414+ dropShadow: spreadView.active || priv.focusedAppDelegateIsDislocated
326− (priv.focusedAppDelegate && priv.focusedAppDelegate.x !== 0)
327 focusFirstApp: root.focusFirstApp
415 focusFirstApp: root.focusFirstApp
328
416
329 readonly property bool isDash: model.appId == "unity8-dash"
417 readonly property bool isDash: model.appId == "unity8-dash"
@@ -346,7 +434,7 @@ Rectangle {
346 return spreadView.width + spreadIndex * spreadView.tileDistance;
434 return spreadView.width + spreadIndex * spreadView.tileDistance;
347 }
435 }
348
436
349− application: applicationManager.get(index)
437+ application: root.applicationManager.get(index)
350 closeable: !isDash
438 closeable: !isDash
351
439
352 property real behavioredIndex: index
440 property real behavioredIndex: index
@@ -362,18 +450,16 @@ Rectangle {
362 }
450 }
363 }
451 }
364
452
453+ property var xBehavior: xBehavior
365 Behavior on x {
454 Behavior on x {
455+ id: xBehavior
366 enabled: root.spreadEnabled &&
456 enabled: root.spreadEnabled &&
367 !spreadView.active &&
457 !spreadView.active &&
368 !snapAnimation.running &&
458 !snapAnimation.running &&
369− priv.animateX
459+ priv.animateX &&
460+ !root.beingResized
370 UbuntuNumberAnimation {
461 UbuntuNumberAnimation {
371 duration: UbuntuAnimation.BriskDuration
462 duration: UbuntuAnimation.BriskDuration
372− onRunningChanged: {
373− if (!running && root.inverseProgress == 0) {
374− spreadView.focusChanging = false;
375− }
376− }
377 }
463 }
378 }
464 }
379
465
@@ -413,22 +499,21 @@ Rectangle {
413 }
499 }
414
500
415 // Hiding tiles when their progress is negative or reached the maximum
501 // Hiding tiles when their progress is negative or reached the maximum
416− visible: (progress >= 0 && progress < 1.7) ||
502+ visible: (progress >= 0 && progress < 1.7)
417− (isDash && priv.focusedAppDelegate.x !== 0)
503+ || (isDash && priv.focusedAppDelegateIsDislocated)
418
504
419− Binding {
505+
420− target: appDelegate
506+ shellOrientationAngle: root.shellOrientationAngle
421− property: "orientation"
507+ shellOrientation: root.shellOrientation
422− when: appDelegate.interactive
508+ shellPrimaryOrientation: root.shellPrimaryOrientation
423− value: root.orientation
509+ nativeOrientation: root.nativeOrientation
424− }
425
510
426 onClicked: {
511 onClicked: {
427 if (root.altTabEnabled && spreadView.phase == 2) {
512 if (root.altTabEnabled && spreadView.phase == 2) {
428− if (applicationManager.focusedApplicationId == applicationManager.get(index).appId) {
513+ if (root.applicationManager.focusedApplicationId == root.applicationManager.get(index).appId) {
429 spreadView.snapTo(index);
514 spreadView.snapTo(index);
430 } else {
515 } else {
431− applicationManager.requestFocusApplication(applicationManager.get(index).appId);
516+ root.applicationManager.requestFocusApplication(root.applicationManager.get(index).appId);
432 }
517 }
433 }
518 }
434 }
519 }
@@ -443,14 +528,27 @@ Rectangle {
443
528
444 onClosed: {
529 onClosed: {
445 spreadView.closingIndex = index;
530 spreadView.closingIndex = index;
446− applicationManager.stopApplication(applicationManager.get(index).appId);
531+ root.applicationManager.stopApplication(root.applicationManager.get(index).appId);
532+ }
533+
534+ Binding {
535+ target: root
536+ when: index == 0
537+ property: "mainAppWindowOrientationAngle"
538+ value: appWindowOrientationAngle
539+ }
540+ Binding {
541+ target: priv
542+ when: index == 0
543+ property: "focusedAppOrientationChangesEnabled"
544+ value: orientationChangesEnabled
447 }
545 }
448 }
546 }
449 }
547 }
450 }
548 }
451 }
549 }
452
550
453− EdgeDragArea {
551+ DirectionalDragArea {
454 id: spreadDragArea
552 id: spreadDragArea
455 objectName: "spreadDragArea"
553 objectName: "spreadDragArea"
456 direction: Direction.Leftwards
554 direction: Direction.Leftwards
@@ -462,59 +560,50 @@ Rectangle {
462 property var gesturePoints: new Array()
560 property var gesturePoints: new Array()
463
561
464 onTouchXChanged: {
562 onTouchXChanged: {
465− if (!dragging) {
563+ if (dragging) {
466− // Initial touch. Let's reset the spreadView to the starting position.
467− spreadView.phase = 0;
468− spreadView.contentX = -spreadView.shift;
469− }
470− if (dragging && status == DirectionalDragArea.Recognized) {
471 // Gesture recognized. Let's move the spreadView with the finger
564 // Gesture recognized. Let's move the spreadView with the finger
472 var dragX = Math.min(touchX + width, width); // Prevent dragging rightwards
565 var dragX = Math.min(touchX + width, width); // Prevent dragging rightwards
473 dragX = -dragX + spreadDragArea.width - spreadView.shift;
566 dragX = -dragX + spreadDragArea.width - spreadView.shift;
474 // Don't allow dragging further than the animation crossing with phase2's animation
567 // Don't allow dragging further than the animation crossing with phase2's animation
475 var maxMovement = spreadView.width * spreadView.positionMarker4 - spreadView.shift;
568 var maxMovement = spreadView.width * spreadView.positionMarker4 - spreadView.shift;
569+
476 spreadView.contentX = Math.min(dragX, maxMovement);
570 spreadView.contentX = Math.min(dragX, maxMovement);
571+ } else {
572+ // Initial touch. Let's reset the spreadView to the starting position.
573+ spreadView.phase = 0;
574+ spreadView.contentX = -spreadView.shift;
477 }
575 }
478− gesturePoints.push(touchX);
479− }
480−
481− property int previousStatus: -1
482− property int currentStatus: DirectionalDragArea.WaitingForTouch
483
576
484− onStatusChanged: {
577+ gesturePoints.push(touchX);
485− previousStatus = currentStatus;
486− currentStatus = status;
487 }
578 }
488
579
489 onDraggingChanged: {
580 onDraggingChanged: {
490 if (dragging) {
581 if (dragging) {
491 // A potential edge-drag gesture has started. Start recording it
582 // A potential edge-drag gesture has started. Start recording it
492 gesturePoints = [];
583 gesturePoints = [];
493− return;
584+ } else {
494− }
585+ // Ok. The user released. Find out if it was a one-way movement.
586+ var oneWayFlick = true;
587+ var smallestX = spreadDragArea.width;
588+ for (var i = 0; i < gesturePoints.length; i++) {
589+ if (gesturePoints[i] >= smallestX) {
590+ oneWayFlick = false;
591+ break;
592+ }
593+ smallestX = gesturePoints[i];
594+ }
595+ gesturePoints = [];
495
596
597+ if (oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
598+ spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
496− // Ok. The user released. Find out if it was a one-way movement.
599+ // If it was a short one-way movement, do the Alt+Tab switch
497− var oneWayFlick = true;
600+ // no matter if we didn't cross positionMarker1 yet.
498− var smallestX = spreadDragArea.width;
601+ spreadView.snapTo(1);
499− for (var i = 0; i < gesturePoints.length; i++) {
602+ } else if (!dragging) {
500− if (gesturePoints[i] >= smallestX) {
603+ // otherwise snap to the closest snap position we can find
501− oneWayFlick = false;
604+ // (might be back to start, to app 1 or to spread)
502− break;
605+ spreadView.snap();
503 }
606 }
504− smallestX = gesturePoints[i];
505− }
506− gesturePoints = [];
507−
508− if (previousStatus == DirectionalDragArea.Recognized &&
509− oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
510− spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
511− // If it was a short one-way movement, do the Alt+Tab switch
512− // no matter if we didn't cross positionMarker1 yet.
513− spreadView.snapTo(1);
514− } else if (!dragging) {
515− // otherwise snap to the closest snap position we can find
516− // (might be back to start, to app 1 or to spread)
517− spreadView.snap();
518 }
607 }
519 }
608 }
520 }
609 }
qml/Stages/SessionContainer.qml+2−8
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2014-2015 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
5 * it under the terms of the GNU Lesser General Public License as published by
@@ -24,14 +24,13 @@ FocusScope {
24 readonly property var childSessions: session ? session.childSessions : null
24 readonly property var childSessions: session ? session.childSessions : null
25 readonly property alias surface: _surfaceContainer.surface
25 readonly property alias surface: _surfaceContainer.surface
26 property alias interactive: _surfaceContainer.interactive
26 property alias interactive: _surfaceContainer.interactive
27− property int orientation
27+ property alias surfaceOrientationAngle: _surfaceContainer.surfaceOrientationAngle
28
28
29 readonly property alias surfaceContainer: _surfaceContainer
29 readonly property alias surfaceContainer: _surfaceContainer
30 SurfaceContainer {
30 SurfaceContainer {
31 id: _surfaceContainer
31 id: _surfaceContainer
32 anchors.fill: parent
32 anchors.fill: parent
33 surface: session ? session.surface : null
33 surface: session ? session.surface : null
34− orientation: root.orientation
35 }
34 }
36
35
37 Repeater {
36 Repeater {
@@ -72,11 +71,6 @@ FocusScope {
72 target: item; when: item
71 target: item; when: item
73 property: "height"; value: root.height
72 property: "height"; value: root.height
74 }
73 }
75−
76− Binding {
77− target: item; when: item
78− property: "orientation"; value: root.orientation
79− }
80 }
74 }
81 }
75 }
82
76
qml/Stages/ShimStage.qmlnew file+44
@@ -0,0 +1,44 @@
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.3
18+import Ubuntu.Components 0.1
19+
20+Rectangle {
21+ id: shimStage
22+
23+ anchors.fill: parent
24+ color: UbuntuColors.lightAubergine
25+
26+ Text {
27+ id: greeterModeText
28+
29+ anchors.horizontalCenter: parent.horizontalCenter
30+ anchors.top: parent.top
31+ anchors.topMargin: units.gu(10)
32+ font.bold: true
33+ horizontalAlignment: Text.AlignHCenter
34+ text: "Shell is in \"greeter\" mode"
35+ }
36+
37+ Text {
38+ anchors.centerIn: parent
39+ color: UbuntuColors.orange
40+ font.pointSize: units.gu(8)
41+ horizontalAlignment: Text.AlignHCenter
42+ text: "Shim \nStage"
43+ }
44+}
qml/Stages/SpreadDelegate.qml+261−20
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2014-2015 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
5 * it under the terms of the GNU Lesser General Public License as published by
@@ -18,6 +18,7 @@
18 */
18 */
19
19
20import QtQuick 2.0
20import QtQuick 2.0
21+import QtQuick.Window 2.0
21import Ubuntu.Components 1.1
22import Ubuntu.Components 1.1
22import "../Components"
23import "../Components"
23
24
@@ -28,6 +29,9 @@ FocusScope {
28 readonly property bool dragged: dragArea.moving
29 readonly property bool dragged: dragArea.moving
29 signal clicked()
30 signal clicked()
30 signal closed()
31 signal closed()
32+ readonly property alias appWindowOrientationAngle: appWindowWithShadow.orientationAngle
33+ readonly property alias appWindowRotation: appWindowWithShadow.rotation
34+ readonly property alias orientationChangesEnabled: appWindow.orientationChangesEnabled
31
35
32 // to be set from outside
36 // to be set from outside
33 property bool interactive: true
37 property bool interactive: true
@@ -36,10 +40,54 @@ FocusScope {
36 property alias swipeToCloseEnabled: dragArea.enabled
40 property alias swipeToCloseEnabled: dragArea.enabled
37 property bool closeable
41 property bool closeable
38 property alias application: appWindow.application
42 property alias application: appWindow.application
39− property int orientation
43+ property int shellOrientationAngle
44+ property int shellOrientation
45+ property int shellPrimaryOrientation
46+ property int nativeOrientation
47+
48+ function matchShellOrientation() {
49+ if (!root.application)
50+ return;
51+ appWindowWithShadow.orientationAngle = root.shellOrientationAngle;
52+ }
53+
54+ function animateToShellOrientation() {
55+ if (!root.application)
56+ return;
57+
58+ if (root.application.rotatesWindowContents) {
59+ appWindowWithShadow.orientationAngle = root.shellOrientationAngle;
60+ } else {
61+ orientationChangeAnimation.start();
62+ }
63+ }
64+
65+ OrientationChangeAnimation {
66+ id: orientationChangeAnimation
67+ objectName: "orientationChangeAnimation"
68+ spreadDelegate: root
69+ background: background
70+ window: appWindowWithShadow
71+ screenshot: appWindowScreenshotWithShadow
72+ }
73+
74+ QtObject {
75+ id: priv
76+ property bool startingUp: true
77+ }
78+
79+ Component.onCompleted: { finishStartUpTimer.start(); }
80+ Timer { id: finishStartUpTimer; interval: 400; onTriggered: priv.startingUp = false }
81+
82+ Rectangle {
83+ id: background
84+ color: "black"
85+ anchors.fill: parent
86+ visible: false
87+ }
40
88
41 Item {
89 Item {
42− objectName: "appWindowWithShadow"
90+ objectName: "displacedAppWindowWithShadow"
43
91
44 readonly property real limit: root.height / 4
92 readonly property real limit: root.height / 4
45
93
@@ -52,27 +100,220 @@ FocusScope {
52 return k * (1 - Math.pow((k - 1) / k, distance))
100 return k * (1 - Math.pow((k - 1) / k, distance))
53 }
101 }
54
102
55− BorderImage {
103+ Item {
56− anchors {
104+ id: appWindowWithShadow
105+ objectName: "appWindowWithShadow"
106+
107+ property int orientationAngle
108+
109+ property real transformRotationAngle: 0
110+ property real transformOriginX
111+ property real transformOriginY
112+
57− fill: appWindow
113+ property var window: appWindow
58− margins: -units.gu(2)
114+
115+ transform: Rotation {
116+ origin.x: appWindowWithShadow.transformOriginX
117+ origin.y: appWindowWithShadow.transformOriginY
118+ axis { x: 0; y: 0; z: 1 }
119+ angle: appWindowWithShadow.transformRotationAngle
120+ }
121+
122+ state: {
123+ if (priv.startingUp) {
124+ return "startingUp";
125+ } else if (root.application && root.application.rotatesWindowContents) {
126+ return "counterRotate";
127+ } else if (orientationChangeAnimation.running) {
128+ return "animatingRotation";
129+ } else {
130+ return "keepSceneRotation";
131+ }
59 }
132 }
60− source: "graphics/dropshadow2gu.sci"
61− opacity: root.dropShadow ? .3 : 0
62− Behavior on opacity { UbuntuNumberAnimation {} }
63− }
64
133
65− ApplicationWindow {
134+ // Ensures the given angle is in the form (0,90,180,270)
66− id: appWindow
135+ function normalizeAngle(angle) {
67− objectName: application ? "appWindow_" + application.appId : "appWindow_null"
136+ while (angle < 0) {
68− focus: true
137+ angle += 360;
69− anchors {
138+ }
70− fill: parent
139+ return angle % 360;
71− topMargin: appWindow.fullscreen ? 0 : maximizedAppTopMargin
72 }
140 }
73
141
142+ states: [
143+ // Sets the initial orientationAngle of the window, when it first slides into view
144+ // (with the splash screen likely being displayed). At that point we just try to
145+ // match shell's current orientation. We need a bit of time in this state as the
146+ // information we need to decide orientationAngle may take a few cycles to
147+ // be set.
148+ State {
149+ name: "startingUp"
150+ PropertyChanges {
151+ target: appWindowWithShadow
152+ restoreEntryValues: false
153+ orientationAngle: {
154+ if (!root.application || root.application.rotatesWindowContents) {
155+ return 0;
156+ }
157+ var supportedOrientations = root.application.supportedOrientations;
158+
159+ if (supportedOrientations === Qt.PrimaryOrientation) {
160+ supportedOrientations = root.shellPrimaryOrientation;
161+ }
162+
163+ // If it doesn't support shell's current orientation
164+ // then simply pick some arbitraty one that it does support
165+ var chosenOrientation = 0;
166+ if (supportedOrientations & root.shellOrientation) {
167+ chosenOrientation = root.shellOrientation;
168+ } else if (supportedOrientations & Qt.PortraitOrientation) {
169+ chosenOrientation = Qt.PortraitOrientation;
170+ } else if (supportedOrientations & Qt.LandscapeOrientation) {
171+ chosenOrientation = Qt.LandscapeOrientation;
172+ } else if (supportedOrientations & Qt.InvertedPortraitOrientation) {
173+ chosenOrientation = Qt.InvertedPortraitOrientation;
174+ } else if (supportedOrientations & Qt.InvertedLandscapeOrientation) {
175+ chosenOrientation = Qt.InvertedLandscapeOrientation;
176+ } else {
177+ chosenOrientation = root.shellPrimaryOrientation;
178+ }
179+
180+ return Screen.angleBetween(root.nativeOrientation, chosenOrientation);
181+ }
182+
183+ rotation: normalizeAngle(appWindowWithShadow.orientationAngle - root.shellOrientationAngle)
184+ width: {
185+ if (rotation == 0 || rotation == 180) {
186+ return root.width;
187+ } else {
188+ return root.height;
189+ }
190+ }
191+ height: {
192+ if (rotation == 0 || rotation == 180)
193+ return root.height;
194+ else
195+ return root.width;
196+ }
197+ }
198+ },
199+ // In this state we stick to our currently set orientationAngle, which may change only due
200+ // to calls made to matchShellOrientation() or animateToShellOrientation()
201+ State {
202+ id: keepSceneRotationState
203+ name: "keepSceneRotation"
204+
205+ StateChangeScript { script: {
206+ // break binding
207+ appWindowWithShadow.orientationAngle = appWindowWithShadow.orientationAngle;
208+ } }
209+ PropertyChanges {
210+ target: appWindowWithShadow
211+ restoreEntryValues: false
212+ rotation: normalizeAngle(appWindowWithShadow.orientationAngle - root.shellOrientationAngle)
213+ width: {
214+ if (rotation == 0 || rotation == 180) {
215+ return root.width;
216+ } else {
217+ return root.height;
218+ }
219+ }
220+ height: {
221+ if (rotation == 0 || rotation == 180)
222+ return root.height;
223+ else
224+ return root.width;
225+ }
226+ }
227+ },
228+ // In this state we counteract any shell rotation so that the window, in scene coordinates,
229+ // remains unrotated.
230+ State {
231+ name: "counterRotate"
232+ StateChangeScript { script: {
233+ // break binding
234+ appWindowWithShadow.orientationAngle = appWindowWithShadow.orientationAngle;
235+ } }
236+ PropertyChanges {
237+ target: appWindowWithShadow
238+ width: root.shellOrientationAngle == 0 || root.shellOrientationAngle == 180 ? root.width : root.height
239+ height: root.shellOrientationAngle == 0 || root.shellOrientationAngle == 180 ? root.height : root.width
240+ rotation: normalizeAngle(-root.shellOrientationAngle)
241+ }
242+ PropertyChanges {
243+ target: appWindow
244+ surfaceOrientationAngle: orientationAngle
245+ }
246+ },
247+ State {
248+ name: "animatingRotation"
249+ }
250+ ]
251+
252+ x: (parent.width - width) / 2
253+ y: (parent.height - height) / 2
254+
255+ BorderImage {
256+ anchors {
257+ fill: appWindow
258+ margins: -units.gu(2)
259+ }
260+ source: "graphics/dropshadow2gu.sci"
261+ opacity: root.dropShadow ? .3 : 0
262+ Behavior on opacity { UbuntuNumberAnimation {} }
263+ }
264+
265+ ApplicationWindow {
266+ id: appWindow
267+ objectName: application ? "appWindow_" + application.appId : "appWindow_null"
268+ focus: true
269+ anchors {
270+ fill: parent
271+ topMargin: appWindow.fullscreen || application.rotatesWindowContents
272+ ? 0 : maximizedAppTopMargin
273+ }
274+
74− interactive: root.interactive
275+ interactive: root.interactive
75− orientation: root.orientation
276+ }
277+ }
278+ }
279+
280+ Item {
281+ // mimics appWindowWithShadow. Do the positioning of screenshots of non-fullscreen
282+ // app windows
283+ id: appWindowScreenshotWithShadow
284+ visible: false
285+
286+ property real transformRotationAngle: 0
287+ property real transformOriginX
288+ property real transformOriginY
289+
290+ transform: Rotation {
291+ origin.x: appWindowScreenshotWithShadow.transformOriginX
292+ origin.y: appWindowScreenshotWithShadow.transformOriginY
293+ axis { x: 0; y: 0; z: 1 }
294+ angle: appWindowScreenshotWithShadow.transformRotationAngle
295+ }
296+
297+ property var window: appWindowScreenshot
298+
299+ function take() {
300+ // Format: "image://application/$APP_ID/$CURRENT_TIME_MS"
301+ // eg: "image://application/calculator-app/123456"
302+ var timeMs = new Date().getTime();
303+ appWindowScreenshot.source = "image://application/" + root.application.appId + "/" + timeMs;
304+ }
305+ function discard() {
306+ appWindowScreenshot.source = "";
307+ }
308+
309+ Image {
310+ id: appWindowScreenshot
311+ source: ""
312+
313+ anchors.fill: parent
314+
315+ sourceSize.width: width
316+ sourceSize.height: height
76 }
317 }
77 }
318 }
78
319
qml/Stages/SurfaceContainer.qml+2−2
@@ -24,8 +24,8 @@ FocusScope {
24 objectName: "surfaceContainer"
24 objectName: "surfaceContainer"
25 property Item surface: null
25 property Item surface: null
26 property bool hadSurface: false
26 property bool hadSurface: false
27− property int orientation
28 property bool interactive
27 property bool interactive
28+ property int surfaceOrientationAngle: 0
29
29
30 onSurfaceChanged: {
30 onSurfaceChanged: {
31 if (surface) {
31 if (surface) {
@@ -39,10 +39,10 @@ FocusScope {
39 }
39 }
40 }
40 }
41 Binding { target: surface; property: "anchors.fill"; value: root }
41 Binding { target: surface; property: "anchors.fill"; value: root }
42− Binding { target: surface; property: "orientation"; value: root.orientation }
43 Binding { target: surface; property: "z"; value: 1 }
42 Binding { target: surface; property: "z"; value: 1 }
44 Binding { target: surface; property: "enabled"; value: root.interactive; when: surface }
43 Binding { target: surface; property: "enabled"; value: root.interactive; when: surface }
45 Binding { target: surface; property: "antialiasing"; value: !root.interactive; when: surface }
44 Binding { target: surface; property: "antialiasing"; value: !root.interactive; when: surface }
45+ Binding { target: surface; property: "orientationAngle"; value: root.surfaceOrientationAngle; when: surface }
46
46
47 InputWatcher {
47 InputWatcher {
48 target: root.surface
48 target: root.surface
qml/Stages/TabletStage.qml+177−50
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -28,13 +28,79 @@ Rectangle {
28 color: "#111111"
28 color: "#111111"
29
29
30 // Controls to be set from outside
30 // Controls to be set from outside
31− property bool shown: false
32− property bool moving: false
33 property int dragAreaWidth
31 property int dragAreaWidth
34 property real maximizedAppTopMargin
32 property real maximizedAppTopMargin
35 property bool interactive
33 property bool interactive
34+ property alias beingResized: spreadView.beingResized
35+
36+ property bool spreadEnabled: true // If false, animations and right edge will be disabled
37+
36 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
38 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
37− property int orientation: Qt.PortraitOrientation
39+ property int shellOrientationAngle: 0
40+ property int shellOrientation
41+ property int shellPrimaryOrientation
42+ property int nativeOrientation
43+ property real nativeWidth
44+ property real nativeHeight
45+ function updateFocusedAppOrientation() {
46+ var mainStageAppIndex = priv.indexOf(priv.mainStageAppId);
47+ if (mainStageAppIndex >= 0 && mainStageAppIndex < spreadRepeater.count) {
48+ spreadRepeater.itemAt(mainStageAppIndex).matchShellOrientation();
49+ }
50+
51+ for (var i = 0; i < spreadRepeater.count; ++i) {
52+
53+ if (i === mainStageAppIndex) {
54+ continue;
55+ }
56+
57+ var spreadDelegate = spreadRepeater.itemAt(i);
58+
59+ var delta = spreadDelegate.appWindowOrientationAngle - root.shellOrientationAngle;
60+ if (delta < 0) { delta += 360; }
61+ delta = delta % 360;
62+
63+ var supportedOrientations = spreadDelegate.application.supportedOrientations;
64+ if (supportedOrientations === Qt.PrimaryOrientation) {
65+ supportedOrientations = spreadDelegate.shellPrimaryOrientation;
66+ }
67+
68+ if (delta === 180 && (supportedOrientations & spreadDelegate.shellOrientation)) {
69+ spreadDelegate.matchShellOrientation();
70+ }
71+ }
72+ }
73+ function updateFocusedAppOrientationAnimated() {
74+ var mainStageAppIndex = priv.indexOf(priv.mainStageAppId);
75+ if (mainStageAppIndex >= 0 && mainStageAppIndex < spreadRepeater.count) {
76+ spreadRepeater.itemAt(mainStageAppIndex).animateToShellOrientation();
77+ }
78+
79+ if (priv.sideStageAppId) {
80+ var sideStageAppIndex = priv.indexOf(priv.sideStageAppId);
81+ if (sideStageAppIndex >= 0 && sideStageAppIndex < spreadRepeater.count) {
82+ spreadRepeater.itemAt(sideStageAppIndex).matchShellOrientation();
83+ }
84+ }
85+ }
86+
87+ // To be read from outside
88+ property var mainApp: null
89+ property int mainAppWindowOrientationAngle: 0
90+ readonly property bool orientationChangesEnabled: priv.mainAppOrientationChangesEnabled
91+
92+ onWidthChanged: {
93+ spreadView.selectedIndex = -1;
94+ spreadView.phase = 0;
95+ spreadView.contentX = -spreadView.shift;
96+ }
97+
98+ onShellOrientationChanged: {
99+ if (shellOrientation == Qt.PortraitOrientation || shellOrientation == Qt.InvertedPortraitOrientation) {
100+ ApplicationManager.focusApplication(priv.mainStageAppId);
101+ priv.sideStageAppId = "";
102+ }
103+ }
38
104
39 onInverseProgressChanged: {
105 onInverseProgressChanged: {
40 // This can't be a simple binding because that would be triggered after this handler
106 // This can't be a simple binding because that would be triggered after this handler
@@ -53,7 +119,19 @@ Rectangle {
53 id: priv
119 id: priv
54
120
55 property string focusedAppId: ApplicationManager.focusedApplicationId
121 property string focusedAppId: ApplicationManager.focusedApplicationId
122+ readonly property var focusedAppDelegate: {
123+ var index = indexOf(focusedAppId);
124+ return index >= 0 && index < spreadRepeater.count ? spreadRepeater.itemAt(index) : null
125+ }
126+
56 property string oldFocusedAppId: ""
127 property string oldFocusedAppId: ""
128+ property bool mainAppOrientationChangesEnabled: false
129+
130+ property real landscapeHeight: root.nativeOrientation == Qt.LandscapeOrientation ?
131+ root.nativeHeight : root.nativeWidth
132+
133+ property bool shellIsLandscape: root.shellOrientation === Qt.LandscapeOrientation
134+ || root.shellOrientation === Qt.InvertedLandscapeOrientation
57
135
58 property string mainStageAppId
136 property string mainStageAppId
59 property string sideStageAppId
137 property string sideStageAppId
@@ -71,24 +149,17 @@ Rectangle {
71 priv.sideStageAppId = focusedAppId;
149 priv.sideStageAppId = focusedAppId;
72 } else {
150 } else {
73 priv.mainStageAppId = focusedAppId;
151 priv.mainStageAppId = focusedAppId;
152+ root.mainApp = focusedApp;
74 }
153 }
75 }
154 }
76
155
77 appId0 = ApplicationManager.count >= 1 ? ApplicationManager.get(0).appId : "";
156 appId0 = ApplicationManager.count >= 1 ? ApplicationManager.get(0).appId : "";
78 appId1 = ApplicationManager.count > 1 ? ApplicationManager.get(1).appId : "";
157 appId1 = ApplicationManager.count > 1 ? ApplicationManager.get(1).appId : "";
79−
80− // Update the QML focus accordingly
81− updateSpreadDelegateFocus();
82 }
158 }
83
159
84− function updateSpreadDelegateFocus() {
160+ onFocusedAppDelegateChanged: {
85− if (priv.focusedAppId) {
161+ if (focusedAppDelegate) {
86− var focusedAppIndex = priv.indexOf(priv.focusedAppId);
162+ focusedAppDelegate.focus = true;
87− if (focusedAppIndex !== -1) {
88− spreadRepeater.itemAt(focusedAppIndex).focus = true;
89− } else {
90− console.warn("TabletStage: Failed to find the SpreadDelegate for appID=" + priv.focusedAppId);
91− }
92 }
163 }
93 }
164 }
94
165
@@ -169,9 +240,9 @@ Rectangle {
169
240
170 Flickable {
241 Flickable {
171 id: spreadView
242 id: spreadView
243+ objectName: "spreadView"
172 anchors.fill: parent
244 anchors.fill: parent
173− interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
174− && draggedDelegateCount === 0
245+ interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
175 contentWidth: spreadRow.width - shift
246 contentWidth: spreadRow.width - shift
176 contentX: -shift
247 contentX: -shift
177
248
@@ -218,7 +289,25 @@ Rectangle {
218 property int draggedDelegateCount: 0
289 property int draggedDelegateCount: 0
219 property int closingIndex: -1
290 property int closingIndex: -1
220
291
292+ // FIXME: Workaround Flickable's not keepping its contentX still when resized
293+ onContentXChanged: { forceItToRemainStillIfBeingResized(); }
294+ onShiftChanged: { forceItToRemainStillIfBeingResized(); }
295+ function forceItToRemainStillIfBeingResized() {
296+ if (root.beingResized && contentX != -shift) {
297+ contentX = -shift;
298+ }
299+ }
300+
221 property bool animateX: true
301 property bool animateX: true
302+ property bool beingResized: false
303+ onBeingResizedChanged: {
304+ if (beingResized) {
305+ // Brace yourselves for impact!
306+ selectedIndex = -1;
307+ phase = 0;
308+ contentX = -shift;
309+ }
310+ }
222
311
223 property bool sideStageDragging: sideStageDragHandle.dragging
312 property bool sideStageDragging: sideStageDragHandle.dragging
224 property real sideStageDragProgress: sideStageDragHandle.progress
313 property real sideStageDragProgress: sideStageDragHandle.progress
@@ -257,7 +346,6 @@ Rectangle {
257 case "overlay":
346 case "overlay":
258 return 1;
347 return 1;
259 }
348 }
260− print("Unhandled nextInStack case! This shouldn't happen any more when the Dash is an app!");
261 return -1;
349 return -1;
262 }
350 }
263 property int nextZInStack: indexToZIndex(nextInStack)
351 property int nextZInStack: indexToZIndex(nextInStack)
@@ -293,6 +381,10 @@ Rectangle {
293 }
381 }
294
382
295 onShiftedContentXChanged: {
383 onShiftedContentXChanged: {
384+ if (root.beingResized) {
385+ // Flickabe.contentX wiggles during resizes. Don't react to it.
386+ return;
387+ }
296 if (spreadView.phase == 0 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker2) {
388 if (spreadView.phase == 0 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker2) {
297 spreadView.phase = 1;
389 spreadView.phase = 1;
298 } else if (spreadView.phase == 1 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker4) {
390 } else if (spreadView.phase == 1 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker4) {
@@ -402,8 +494,8 @@ Rectangle {
402 MouseArea {
494 MouseArea {
403 id: spreadRow
495 id: spreadRow
404 x: spreadView.contentX
496 x: spreadView.contentX
405− height: root.height
406 width: spreadView.width + Math.max(spreadView.width, ApplicationManager.count * spreadView.tileDistance)
497 width: spreadView.width + Math.max(spreadView.width, ApplicationManager.count * spreadView.tileDistance)
498+ height: root.height
407
499
408 onClicked: {
500 onClicked: {
409 spreadView.snapTo(0);
501 spreadView.snapTo(0);
@@ -412,8 +504,9 @@ Rectangle {
412 Rectangle {
504 Rectangle {
413 id: sideStageBackground
505 id: sideStageBackground
414 color: "black"
506 color: "black"
415− anchors.fill: parent
416− anchors.leftMargin: spreadView.width - (1 - sideStageDragHandle.progress) * spreadView.sideStageWidth
507+ width: spreadView.sideStageWidth * (1 - sideStageDragHandle.progress)
508+ height: priv.landscapeHeight
509+ x: spreadView.width - width
417 z: spreadView.indexToZIndex(priv.indexOf(priv.sideStageAppId))
510 z: spreadView.indexToZIndex(priv.indexOf(priv.sideStageAppId))
418 opacity: spreadView.phase == 0 ? 1 : 0
511 opacity: spreadView.phase == 0 ? 1 : 0
419 Behavior on opacity { UbuntuNumberAnimation {} }
512 Behavior on opacity { UbuntuNumberAnimation {} }
@@ -421,8 +514,10 @@ Rectangle {
421
514
422 Item {
515 Item {
423 id: sideStageDragHandle
516 id: sideStageDragHandle
424− anchors { top: parent.top; bottom: parent.bottom; left: parent.left; leftMargin: spreadView.width - spreadView.sideStageWidth - width }
517+ anchors.right: sideStageBackground.left
518+ anchors.top: sideStageBackground.top
425 width: units.gu(2)
519 width: units.gu(2)
520+ height: priv.landscapeHeight
426 z: sideStageBackground.z
521 z: sideStageBackground.z
427 opacity: spreadView.phase <= 0 && spreadView.sideStageVisible ? 1 : 0
522 opacity: spreadView.phase <= 0 && spreadView.sideStageVisible ? 1 : 0
428 property real progress: 0
523 property real progress: 0
@@ -441,7 +536,6 @@ Rectangle {
441
536
442 Image {
537 Image {
443 anchors.centerIn: parent
538 anchors.centerIn: parent
444− anchors.horizontalCenterOffset: parent.progress * spreadView.sideStageWidth - (width - parent.width) / 2
445 width: sideStageDragHandleMouseArea.pressed ? parent.width * 2 : parent.width
539 width: sideStageDragHandleMouseArea.pressed ? parent.width * 2 : parent.width
446 height: parent.height
540 height: parent.height
447 source: "graphics/sidestage_handle@20.png"
541 source: "graphics/sidestage_handle@20.png"
@@ -454,16 +548,19 @@ Rectangle {
454 enabled: spreadView.shiftedContentX == 0
548 enabled: spreadView.shiftedContentX == 0
455 property int startX
549 property int startX
456 property var gesturePoints: new Array()
550 property var gesturePoints: new Array()
551+ property real totalDiff
457
552
458 onPressed: {
553 onPressed: {
459 gesturePoints = [];
554 gesturePoints = [];
460 startX = mouseX;
555 startX = mouseX;
556+ totalDiff = 0.0;
461 sideStageDragHandle.progress = 0;
557 sideStageDragHandle.progress = 0;
462 sideStageDragHandle.dragging = true;
558 sideStageDragHandle.dragging = true;
463 }
559 }
464 onMouseXChanged: {
560 onMouseXChanged: {
561+ totalDiff += mouseX - startX;
465 if (priv.mainStageAppId) {
562 if (priv.mainStageAppId) {
466− sideStageDragHandle.progress = Math.max(0, (-startX + mouseX) / spreadView.sideStageWidth);
563+ sideStageDragHandle.progress = Math.max(0, totalDiff / spreadView.sideStageWidth);
467 }
564 }
468 gesturePoints.push(mouseX);
565 gesturePoints.push(mouseX);
469 }
566 }
@@ -492,12 +589,27 @@ Rectangle {
492
589
493 Repeater {
590 Repeater {
494 id: spreadRepeater
591 id: spreadRepeater
592+ objectName: "spreadRepeater"
495 model: ApplicationManager
593 model: ApplicationManager
496
594
497 delegate: TransformedTabletSpreadDelegate {
595 delegate: TransformedTabletSpreadDelegate {
498 id: spreadTile
596 id: spreadTile
499− height: spreadView.height
597+ objectName: model.appId ? "tabletSpreadDelegate_" + model.appId
598+ : "tabletSpreadDelegate_null";
599+ width: {
600+ if (wantsMainStage) {
601+ return spreadView.width;
602+ } else {
500− width: model.stage == ApplicationInfoInterface.MainStage ? spreadView.width : spreadView.sideStageWidth
603+ return spreadView.sideStageWidth;
604+ }
605+ }
606+ height: {
607+ if (wantsMainStage) {
608+ return spreadView.height;
609+ } else {
610+ return priv.landscapeHeight;
611+ }
612+ }
501 active: model.appId == priv.mainStageAppId || model.appId == priv.sideStageAppId
613 active: model.appId == priv.mainStageAppId || model.appId == priv.sideStageAppId
502 zIndex: spreadView.indexToZIndex(index)
614 zIndex: spreadView.indexToZIndex(index)
503 selected: spreadView.selectedIndex == index
615 selected: spreadView.selectedIndex == index
@@ -510,6 +622,8 @@ Rectangle {
510 application: ApplicationManager.get(index)
622 application: ApplicationManager.get(index)
511 closeable: !isDash
623 closeable: !isDash
512
624
625+ readonly property bool wantsMainStage: model.stage == ApplicationInfoInterface.MainStage
626+
513 readonly property bool isDash: model.appId == "unity8-dash"
627 readonly property bool isDash: model.appId == "unity8-dash"
514
628
515 // FIXME: A regular binding doesn't update any more after closing an app.
629 // FIXME: A regular binding doesn't update any more after closing an app.
@@ -561,12 +675,11 @@ Rectangle {
561 return progress;
675 return progress;
562 }
676 }
563
677
564− Binding {
678+ shellOrientationAngle: wantsMainStage ? root.shellOrientationAngle : 0
565− target: spreadTile
679+ shellOrientation: wantsMainStage ? root.shellOrientation : Qt.PortraitOrientation
566− property: "orientation"
680+ shellPrimaryOrientation: wantsMainStage ? root.shellPrimaryOrientation : Qt.PortraitOrientation
567− when: spreadTile.interactive
681+ nativeOrientation: wantsMainStage ? root.nativeOrientation : Qt.PortraitOrientation
568− value: root.orientation
682+
569− }
570
683
571 onClicked: {
684 onClicked: {
572 if (spreadView.phase == 2) {
685 if (spreadView.phase == 2) {
@@ -587,6 +700,19 @@ Rectangle {
587 ApplicationManager.stopApplication(ApplicationManager.get(index).appId);
700 ApplicationManager.stopApplication(ApplicationManager.get(index).appId);
588 }
701 }
589
702
703+ Binding {
704+ target: root
705+ when: model.appId == priv.mainStageAppId
706+ property: "mainAppWindowOrientationAngle"
707+ value: appWindowOrientationAngle
708+ }
709+ Binding {
710+ target: priv
711+ when: model.appId == priv.mainStageAppId
712+ property: "mainAppOrientationChangesEnabled"
713+ value: orientationChangesEnabled
714+ }
715+
590 EasingCurve {
716 EasingCurve {
591 id: snappingCurve
717 id: snappingCurve
592 type: EasingCurve.Linear
718 type: EasingCurve.Linear
@@ -598,11 +724,13 @@ Rectangle {
598 }
724 }
599 }
725 }
600
726
601− EdgeDragArea {
727+ DirectionalDragArea {
602 id: spreadDragArea
728 id: spreadDragArea
729+ objectName: "spreadDragArea"
603 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
730 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
604 width: root.dragAreaWidth
731 width: root.dragAreaWidth
605 direction: Direction.Leftwards
732 direction: Direction.Leftwards
733+ enabled: (spreadView.phase != 2 && root.spreadEnabled) || dragging
606
734
607 property var gesturePoints: new Array()
735 property var gesturePoints: new Array()
608
736
@@ -624,26 +752,25 @@ Rectangle {
624 if (dragging) {
752 if (dragging) {
625 // Gesture recognized. Start recording this gesture
753 // Gesture recognized. Start recording this gesture
626 gesturePoints = [];
754 gesturePoints = [];
627− return;
755+ } else {
628− }
629−
630− // Ok. The user released. Find out if it was a one-way movement.
756+ // Ok. The user released. Find out if it was a one-way movement.
631− var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
757+ var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
632− gesturePoints = [];
758+ gesturePoints = [];
633
759
634− if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
760+ if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
635− // If it was a short one-way movement, do the Alt+Tab switch
761+ // If it was a short one-way movement, do the Alt+Tab switch
636− // no matter if we didn't cross positionMarker1 yet.
762+ // no matter if we didn't cross positionMarker1 yet.
637− spreadView.snapTo(spreadView.nextInStack);
638− } else if (!dragging) {
639− if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
640− spreadView.snap();
641− } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
642 spreadView.snapTo(spreadView.nextInStack);
763 spreadView.snapTo(spreadView.nextInStack);
643 } else {
764 } else {
765+ if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
766+ spreadView.snap();
767+ } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
768+ spreadView.snapTo(spreadView.nextInStack);
769+ } else {
644− // otherwise snap to the closest snap position we can find
770+ // otherwise snap to the closest snap position we can find
645− // (might be back to start, to app 1 or to spread)
771+ // (might be back to start, to app 1 or to spread)
646− spreadView.snap();
772+ spreadView.snap();
773+ }
647 }
774 }
648 }
775 }
649 }
776 }
qml/Stages/TransformedSpreadDelegate.qml+50−6
@@ -298,7 +298,6 @@ SpreadDelegate {
298
298
299 return easingCurve.value;
299 return easingCurve.value;
300 }
300 }
301−
302 }
301 }
303
302
304 transform: [
303 transform: [
@@ -307,16 +306,61 @@ SpreadDelegate {
307 axis { x: 0; y: 1; z: 0 }
306 axis { x: 0; y: 1; z: 0 }
308 angle: priv.angle
307 angle: priv.angle
309 },
308 },
309+
310+ // The next two transformations are to ensure that fullscreen and rotated
311+ // windows all have the same size and position as an unrotated (0 degrees)
312+ // non-fullscreen window when they're stacked on top of each other on the
313+ // far left of the spread.
314+ Translate {
315+ y: !fullscreen && appWindowRotation === 180
316+ ? priv.topMarginProgress * maximizedAppTopMargin
317+ : 0
318+ },
319+ Scale {
320+ origin {
321+ x: appWindowRotation === 270 ? spreadView.width : 0
322+ y: spreadView.height
323+ }
324+
325+ xScale: {
326+ switch (appWindowRotation) {
327+ case 90:
328+ case 270:
329+ if (fullscreen) {
330+ return 1;
331+ } else {
332+ return 1 + priv.topMarginProgress * maximizedAppTopMargin / spreadView.width;
333+ }
334+ break;
335+ default:
336+ return 1;
337+ }
338+ }
339+
340+ yScale: {
341+ switch (appWindowRotation) {
342+ case 0:
343+ if (fullscreen) {
344+ return 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height;
345+ } else {
346+ return 1;
347+ }
348+ break;
349+ case 90:
350+ case 270:
351+ return 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height;
352+ break;
353+ default:
354+ return 1;
355+ }
356+ }
357+ },
358+
310 Scale {
359 Scale {
311 origin { x: 0; y: spreadView.height / 2 }
360 origin { x: 0; y: spreadView.height / 2 }
312 xScale: priv.scale
361 xScale: priv.scale
313 yScale: xScale
362 yScale: xScale
314 },
363 },
315− Scale {
316− origin { x: 0; y: (spreadView.height * priv.scale) + maximizedAppTopMargin * 3 }
317− xScale: 1
318− yScale: fullscreen ? 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height : 1
319− },
320 Translate {
364 Translate {
321 x: priv.xTranslate
365 x: priv.xTranslate
322 }
366 }
qml/Stages/TransformedTabletSpreadDelegate.qml+5−2
@@ -50,7 +50,9 @@ SpreadDelegate {
50 property int dragOffset: 0
50 property int dragOffset: 0
51
51
52 dropShadow: spreadView.active ||
52 dropShadow: spreadView.active ||
53+ (active
53− (active && model.stage == ApplicationInfoInterface.MainStage && priv.xTranslate != 0)
54+ && (model.stage == ApplicationInfoInterface.MainStage || !priv.shellIsLandscape)
55+ && priv.xTranslate != 0)
54
56
55 onSelectedChanged: {
57 onSelectedChanged: {
56 if (selected) {
58 if (selected) {
@@ -139,7 +141,8 @@ SpreadDelegate {
139 !snapAnimation.running &&
141 !snapAnimation.running &&
140 model.appId !== "unity8-dash" &&
142 model.appId !== "unity8-dash" &&
141 !spreadView.sideStageDragging &&
143 !spreadView.sideStageDragging &&
142− spreadView.animateX
144+ spreadView.animateX &&
145+ !spreadView.beingResized
143 UbuntuNumberAnimation {
146 UbuntuNumberAnimation {
144 duration: UbuntuAnimation.FastDuration
147 duration: UbuntuAnimation.FastDuration
145 }
148 }
qml/Tutorial/TutorialBottom.qml+1−1
@@ -73,7 +73,7 @@ TutorialPage {
73 ]
73 ]
74 }
74 }
75
75
76− EdgeDragArea {
76+ DirectionalDragArea {
77 id: dragArea
77 id: dragArea
78 direction: Direction.Upwards
78 direction: Direction.Upwards
79 anchors {
79 anchors {
run.sh+1−1
@@ -44,7 +44,7 @@ else
44 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/plugins/LightDM/liblightdm
44 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/plugins/LightDM/liblightdm
45fi
45fi
46
46
47−QML_PHONE_SHELL_ARGS=""
47+QML_PHONE_SHELL_ARGS="-windowgeometry=40gux68gu -devicename=mako"
48if $MOUSE_TOUCH; then
48if $MOUSE_TOUCH; then
49 QML_PHONE_SHELL_ARGS="$QML_PHONE_SHELL_ARGS -mousetouch"
49 QML_PHONE_SHELL_ARGS="$QML_PHONE_SHELL_ARGS -mousetouch"
50fi
50fi
runtests.sh+3
@@ -42,6 +42,9 @@ sleep 1
42cd -
42cd -
43
43
44make -k qmltests
44make -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
45
48
46cd -
49cd -
47
50
src/ApplicationArguments.cppnew file+22
@@ -0,0 +1,22 @@
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 "ApplicationArguments.h"
18+
19+ApplicationArguments::ApplicationArguments(QObject *parent)
20+ : QObject(parent)
21+{
22+}
src/ApplicationArguments.h+7−11
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -13,7 +13,6 @@
13 * You should have received a copy of the GNU General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
15 *
16− * Authored by: Nick Dedekind <nick.dedekind@canonical.com>
17 */
16 */
18
17
19
18
@@ -22,23 +21,20 @@
22
21
23#include <QObject>
22#include <QObject>
24#include <QSize>
23#include <QSize>
25−#include <QStringList>
24+#include <QString>
26
25
27class ApplicationArguments : public QObject
26class ApplicationArguments : public QObject
28{
27{
29 Q_OBJECT
28 Q_OBJECT
29+ Q_PROPERTY(QString deviceName READ deviceName CONSTANT)
30public:
30public:
31− // Not exposed to the app as setSize isn't invokable
31+ ApplicationArguments(QObject *parent = nullptr);
32− void setSize(const QSize &size) {
33− m_size = size;
34− }
35
32
36− Q_INVOKABLE bool hasGeometry() const { return m_size.isValid(); }
33+ void setDeviceName(QString deviceName) { m_deviceName = deviceName; }
37− Q_INVOKABLE int width() const { return m_size.width(); }
34+ QString deviceName() const { return m_deviceName; }
38− Q_INVOKABLE int height() const { return m_size.height(); }
39
35
40private:
36private:
41− QSize m_size;
37+ QString m_deviceName;
42};
38};
43
39
44#endif // APPLICATION_ARGUMENTS_H
40#endif // APPLICATION_ARGUMENTS_H
src/CMakeLists.txt+5−6
@@ -17,7 +17,7 @@ file(GLOB_RECURSE QML_FILES
17)
17)
18
18
19add_executable(${SHELL_APP}
19add_executable(${SHELL_APP}
20− ApplicationArguments.h
20+ ApplicationArguments.cpp
21 main.cpp
21 main.cpp
22 MouseTouchAdaptor.cpp
22 MouseTouchAdaptor.cpp
23 CachingNetworkManagerFactory.cpp
23 CachingNetworkManagerFactory.cpp
@@ -26,13 +26,12 @@ add_executable(${SHELL_APP}
26)
26)
27
27
28qt5_use_modules(${SHELL_APP} Gui Qml Quick Test)
28qt5_use_modules(${SHELL_APP} Gui Qml Quick Test)
29−pkg_check_modules(XCB REQUIRED xcb)
30
29
31−if (NOT "${XCB_INCLUDE_DIRS}" STREQUAL "")
30+pkg_check_modules(NEEDED_LIBS REQUIRED xcb libandroid-properties)
31+if (NOT "${NEEDED_LIBS_INCLUDE_DIRS}" STREQUAL "")
32− set_target_properties(${SHELL_APP} PROPERTIES INCLUDE_DIRECTORIES ${XCB_INCLUDE_DIRS})
32+ set_target_properties(${SHELL_APP} PROPERTIES INCLUDE_DIRECTORIES ${NEEDED_LIBS_INCLUDE_DIRS})
33−
34− target_link_libraries(${SHELL_APP} ${XCB_LDFLAGS})
35endif()
33endif()
34+target_link_libraries(${SHELL_APP} ${NEEDED_LIBS_LDFLAGS})
36
35
37target_link_libraries(${SHELL_APP} UbuntuGestures connectivity-qt1)
36target_link_libraries(${SHELL_APP} UbuntuGestures connectivity-qt1)
38
37
src/Dash/CMakeLists.txt+7−1
@@ -1,6 +1,6 @@
1set(DASH_SRCS
1set(DASH_SRCS
2 main.cpp
2 main.cpp
3− ../ApplicationArguments.h
3+ ../ApplicationArguments.cpp
4 ../MouseTouchAdaptor.cpp
4 ../MouseTouchAdaptor.cpp
5 ../CachingNetworkManagerFactory.cpp
5 ../CachingNetworkManagerFactory.cpp
6)
6)
@@ -9,6 +9,12 @@ add_executable(unity8-dash ${DASH_SRCS})
9
9
10qt5_use_modules(unity8-dash Gui Qml Quick Test)
10qt5_use_modules(unity8-dash Gui Qml Quick Test)
11
11
12+pkg_check_modules(NEEDED_LIBS REQUIRED xcb libandroid-properties)
13+if (NOT "${NEEDED_LIBS_INCLUDE_DIRS}" STREQUAL "")
14+ set_target_properties(unity8-dash PROPERTIES INCLUDE_DIRECTORIES ${NEEDED_LIBS_INCLUDE_DIRS})
15+endif()
16+target_link_libraries(unity8-dash ${NEEDED_LIBS_LDFLAGS})
17+
12# For it to find libUbuntuGestures.so, needed by Ubuntu.Gestures QML module.
18# For it to find libUbuntuGestures.so, needed by Ubuntu.Gestures QML module.
13set_target_properties(unity8-dash PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}")
19set_target_properties(unity8-dash PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}")
14
20
src/Dash/main.cpp+12−12
@@ -31,9 +31,6 @@
31#include "../ApplicationArguments.h"
31#include "../ApplicationArguments.h"
32#include "../CachingNetworkManagerFactory.h"
32#include "../CachingNetworkManagerFactory.h"
33
33
34−// Ubuntu Gestures
35−#include <TouchRegistry.h>
36−
37int main(int argc, const char *argv[])
34int main(int argc, const char *argv[])
38{
35{
39 QGuiApplication *application = new QGuiApplication(argc, (char**)argv);
36 QGuiApplication *application = new QGuiApplication(argc, (char**)argv);
@@ -61,12 +58,6 @@ int main(int argc, const char *argv[])
61 parser.process(*application);
58 parser.process(*application);
62
59
63 ApplicationArguments qmlArgs;
60 ApplicationArguments qmlArgs;
64− if (parser.isSet(windowGeometryOption) &&
65− parser.value(windowGeometryOption).split('x').size() == 2)
66− {
67− QStringList geom = parser.value(windowGeometryOption).split('x');
68− qmlArgs.setSize(QSize(geom.at(0).toInt(), geom.at(1).toInt()));
69− }
70
61
71 if (getenv("QT_LOAD_TESTABILITY")) {
62 if (getenv("QT_LOAD_TESTABILITY")) {
72 QLibrary testLib(QLatin1String("qttestability"));
63 QLibrary testLib(QLatin1String("qttestability"));
@@ -88,12 +79,21 @@ int main(int argc, const char *argv[])
88
79
89 QQuickView* view = new QQuickView();
80 QQuickView* view = new QQuickView();
90 view->setResizeMode(QQuickView::SizeRootObjectToView);
81 view->setResizeMode(QQuickView::SizeRootObjectToView);
82+
83+ if (parser.isSet(windowGeometryOption) &&
84+ parser.value(windowGeometryOption).split('x').size() == 2)
85+ {
86+ QStringList geom = parser.value(windowGeometryOption).split('x');
87+ QSize windowSize(geom.at(0).toInt(), geom.at(1).toInt());
88+ if (windowSize.isValid()) {
89+ view->setWidth(windowSize.width());
90+ view->setHeight(windowSize.height());
91+ }
92+ }
93+
91 view->setTitle("Scopes");
94 view->setTitle("Scopes");
92 view->rootContext()->setContextProperty("applicationArguments", &qmlArgs);
95 view->rootContext()->setContextProperty("applicationArguments", &qmlArgs);
93
96
94− TouchRegistry touchRegistry;
95− view->installEventFilter(&touchRegistry);
96−
97 // You will need this if you want to interact with touch-only components using a mouse
97 // You will need this if you want to interact with touch-only components using a mouse
98 // Needed only when manually testing on a desktop.
98 // Needed only when manually testing on a desktop.
99 MouseTouchAdaptor *mouseTouchAdaptor = 0;
99 MouseTouchAdaptor *mouseTouchAdaptor = 0;
src/UnityCommandLineParser.cpp+5
@@ -49,6 +49,10 @@ UnityCommandLineParser::UnityCommandLineParser(const QCoreApplication &app)
49 "DISCOURAGED: Please set QT_LOAD_TESTABILITY instead.\nLoad the testability driver");
49 "DISCOURAGED: Please set QT_LOAD_TESTABILITY instead.\nLoad the testability driver");
50 parser.addOption(testabilityOption);
50 parser.addOption(testabilityOption);
51
51
52+ QCommandLineOption devicenameOption(QStringList() << "devicename",
53+ "Specify the device name instead of letting Unity 8 find it out", "devicename", "");
54+ parser.addOption(devicenameOption);
55+
52 QCommandLineOption modeOption("mode",
56 QCommandLineOption modeOption("mode",
53 "Whether to run greeter and/or shell [full-greeter, full-shell, greeter, shell]",
57 "Whether to run greeter and/or shell [full-greeter, full-shell, greeter, shell]",
54 "mode", "full-greeter");
58 "mode", "full-greeter");
@@ -73,6 +77,7 @@ UnityCommandLineParser::UnityCommandLineParser(const QCoreApplication &app)
73 m_hasFrameless = parser.isSet(framelessOption);
77 m_hasFrameless = parser.isSet(framelessOption);
74 m_hasMouseToTouch = parser.isSet(mousetouchOption);
78 m_hasMouseToTouch = parser.isSet(mousetouchOption);
75 m_hasFullscreen = parser.isSet(fullscreenOption);
79 m_hasFullscreen = parser.isSet(fullscreenOption);
80+ m_deviceName = parser.value(devicenameOption);
76 resolveMode(parser, modeOption);
81 resolveMode(parser, modeOption);
77}
82}
78
83
src/UnityCommandLineParser.h+2
@@ -30,6 +30,7 @@ public:
30 bool hasFrameless() const { return m_hasFrameless; }
30 bool hasFrameless() const { return m_hasFrameless; }
31 bool hasMouseToTouch() const { return m_hasMouseToTouch; }
31 bool hasMouseToTouch() const { return m_hasMouseToTouch; }
32 bool hasFullscreen() const { return m_hasFullscreen; }
32 bool hasFullscreen() const { return m_hasFullscreen; }
33+ QString deviceName() const { return m_deviceName; }
33 QString mode() const { return m_mode; }
34 QString mode() const { return m_mode; }
34private:
35private:
35
36
@@ -44,6 +45,7 @@ private:
44 bool m_hasFrameless;
45 bool m_hasFrameless;
45 bool m_hasMouseToTouch;
46 bool m_hasMouseToTouch;
46 bool m_hasFullscreen;
47 bool m_hasFullscreen;
48+ QString m_deviceName;
47 QString m_mode;
49 QString m_mode;
48};
50};
49
51
src/main.cpp+18−13
@@ -25,6 +25,9 @@
25#include <csignal>
25#include <csignal>
26#include <libintl.h>
26#include <libintl.h>
27
27
28+// libandroid-properties
29+#include <hybris/properties/properties.h>
30+
28// local
31// local
29#include <paths.h>
32#include <paths.h>
30#include "MouseTouchAdaptor.h"
33#include "MouseTouchAdaptor.h"
@@ -32,9 +35,6 @@
32#include "CachingNetworkManagerFactory.h"
35#include "CachingNetworkManagerFactory.h"
33#include "UnityCommandLineParser.h"
36#include "UnityCommandLineParser.h"
34
37
35−// Ubuntu Gestures
36−#include <TouchRegistry.h>
37−
38int main(int argc, const char *argv[])
38int main(int argc, const char *argv[])
39{
39{
40 bool isMirServer = false;
40 bool isMirServer = false;
@@ -50,13 +50,15 @@ int main(int argc, const char *argv[])
50
50
51 UnityCommandLineParser parser(*application);
51 UnityCommandLineParser parser(*application);
52
52
53− QString indicatorProfile = qgetenv("UNITY_INDICATOR_PROFILE");
54− if (indicatorProfile.isEmpty()) {
55− indicatorProfile = "phone";
56− }
57−
58 ApplicationArguments qmlArgs;
53 ApplicationArguments qmlArgs;
59− qmlArgs.setSize(parser.windowGeometry());
54+
55+ if (!parser.deviceName().isEmpty()) {
56+ qmlArgs.setDeviceName(parser.deviceName());
57+ } else {
58+ char buffer[200];
59+ property_get("ro.product.device", buffer /* value */, "desktop" /* default_value*/);
60+ qmlArgs.setDeviceName(QString(buffer));
61+ }
60
62
61 // The testability driver is only loaded by QApplication but not by QGuiApplication.
63 // The testability driver is only loaded by QApplication but not by QGuiApplication.
62 // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
64 // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
@@ -82,15 +84,18 @@ int main(int argc, const char *argv[])
82 view->setResizeMode(QQuickView::SizeRootObjectToView);
84 view->setResizeMode(QQuickView::SizeRootObjectToView);
83 view->setColor("black");
85 view->setColor("black");
84 view->setTitle("Unity8 Shell");
86 view->setTitle("Unity8 Shell");
87+
88+ if (parser.windowGeometry().isValid()) {
89+ view->setWidth(parser.windowGeometry().width());
90+ view->setHeight(parser.windowGeometry().height());
91+ }
92+
85 view->engine()->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
93 view->engine()->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
86 view->rootContext()->setContextProperty("applicationArguments", &qmlArgs);
94 view->rootContext()->setContextProperty("applicationArguments", &qmlArgs);
87− view->rootContext()->setContextProperty("indicatorProfile", indicatorProfile);
88 view->rootContext()->setContextProperty("shellMode", parser.mode());
95 view->rootContext()->setContextProperty("shellMode", parser.mode());
89 if (parser.hasFrameless()) {
96 if (parser.hasFrameless()) {
90 view->setFlags(Qt::FramelessWindowHint);
97 view->setFlags(Qt::FramelessWindowHint);
91 }
98 }
92− TouchRegistry touchRegistry;
93− view->installEventFilter(&touchRegistry);
94
99
95 // You will need this if you want to interact with touch-only components using a mouse
100 // You will need this if you want to interact with touch-only components using a mouse
96 // Needed only when manually testing on a desktop.
101 // Needed only when manually testing on a desktop.
@@ -99,7 +104,7 @@ int main(int argc, const char *argv[])
99 mouseTouchAdaptor = MouseTouchAdaptor::instance();
104 mouseTouchAdaptor = MouseTouchAdaptor::instance();
100 }
105 }
101
106
102− QUrl source(::qmlDirectory()+"Shell.qml");
107+ QUrl source(::qmlDirectory()+"OrientedShell.qml");
103 prependImportPaths(view->engine(), ::overrideImportPaths());
108 prependImportPaths(view->engine(), ::overrideImportPaths());
104 if (!isMirServer) {
109 if (!isMirServer) {
105 prependImportPaths(view->engine(), ::nonMirImportPaths());
110 prependImportPaths(view->engine(), ::nonMirImportPaths());
tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+14−4
@@ -21,6 +21,7 @@
21
21
22import logging
22import logging
23import os
23import os
24+import threading
24
25
25from autopilot.platform import model
26from autopilot.platform import model
26
27
@@ -47,7 +48,7 @@ class ApplicationLifecycleTests(tests.ApplicationLifeCycleTestCase):
47 end_y = start_y
48 end_y = start_y
48
49
49 logger.info("Swiping screen from the right edge")
50 logger.info("Swiping screen from the right edge")
50− self.touch.drag(start_x, start_y, end_x, end_y)
51+ self.main_window.pointing_device.drag(start_x, start_y, end_x, end_y)
51
52
52 def launch_fake_app(self):
53 def launch_fake_app(self):
53 _, desktop_file_path = self.create_test_application()
54 _, desktop_file_path = self.create_test_application()
@@ -90,10 +91,19 @@ class ApplicationLifecycleTests(tests.ApplicationLifeCycleTestCase):
90 """Greeter should hide when an app is opened"""
91 """Greeter should hide when an app is opened"""
91 process_helpers.lock_unity()
92 process_helpers.lock_unity()
92
93
93− application_name = self.launch_fake_app()
94+ # FIXME - this is because the device greeter uses a password.
95+ # Need to be able to selectively enable mocks so that we can use the fake greeter.
96+ def unlock_thread_worker(greeter):
97+ greeter.wait_swiped_away()
98+ process_helpers.unlock_unity()
99+ greeter.created.wait_for(False)
100+
94 greeter = self.main_window.get_greeter()
101 greeter = self.main_window.get_greeter()
95− greeter.wait_swiped_away()
102+ unlock_thread = threading.Thread(target=unlock_thread_worker, args=(greeter,))
96− process_helpers.unlock_unity()
103+ unlock_thread.start()
104+ application_name = self.launch_fake_app()
105+ unlock_thread.join(10)
106+
97 self.assert_current_focused_application(application_name)
107 self.assert_current_focused_application(application_name)
98
108
99 def test_greeter_hides_on_app_focus(self):
109 def test_greeter_hides_on_app_focus(self):
tests/autopilot/unity8/fixture_setup.py+197−3
@@ -17,21 +17,215 @@
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18#
18#
19
19
20−import fixtures
20+import os
21import subprocess
21import subprocess
22+import threading
23+import fixtures
22import logging
24import logging
23
25
26+import ubuntuuitoolkit
24from autopilot import introspection
27from autopilot import introspection
28+from autopilot.matchers import Eventually
29+from testtools.matchers import Equals
30+from ubuntuuitoolkit import fixture_setup
25
31
26from unity8 import (
32from unity8 import (
27 get_binary_path,
33 get_binary_path,
34+ sensors,
28 process_helpers
35 process_helpers
29)
36)
37+
30from unity8.shell import emulators
38from unity8.shell import emulators
31
39
40+from unity8.shell.emulators import (
41+ main_window as main_window_emulator
42+)
43+
44+from unity8 import (
45+ get_mocks_library_path,
46+ get_default_extra_mock_libraries,
47+ get_data_dirs
48+)
49+
50+logger = logging.getLogger(__name__)
51+
52+
53+class LaunchUnityWithFakeSensors(fixtures.Fixture):
54+
55+ """Fixture to launch Unity8 with an injectable sensors backend.
56+
57+ :ivar unity_proxy: The Autopilot proxy object for the Unity shell.
58+
59+ """
60+
61+ unity_proxy = None
62+ main_win = None
63+
64+ def setUp(self):
65+ """Restart Unity8 with testability and create sensors."""
66+ super().setUp()
67+ self.useFixture(
68+ fixture_setup.InitctlEnvironmentVariable(
69+ UBUNTU_PLATFORM_API_TEST_OVERRIDE='sensors'))
70+
71+ self.addCleanup(process_helpers.stop_job, 'unity8')
72+ restart_thread = threading.Thread(
73+ target=self._restart_unity_with_testability)
74+ restart_thread.start()
75+
76+ self._create_sensors()
77+
78+ restart_thread.join()
79+ self.fake_sensors = sensors.FakePlatformSensors()
80+
81+ def _get_lightdm_mock_path(self):
82+ lib_path = get_mocks_library_path()
83+ lightdm_mock_path = os.path.abspath(
84+ os.path.join(lib_path, "LightDM", "liblightdm")
85+ )
86+
87+ if not os.path.exists(lightdm_mock_path):
88+ raise RuntimeError(
89+ "LightDM mock does not exist at path {}.".
90+ format(lightdm_mock_path)
91+ )
92+ return lightdm_mock_path
93+
94+ def _get_qml_import_path_with_mock(self):
95+ """Return the QML2_IMPORT_PATH value with the mock path prepended."""
96+ qml_import_path = [get_mocks_library_path()]
97+ if os.getenv('QML2_IMPORT_PATH') is not None:
98+ qml_import_path.append(os.getenv('QML2_IMPORT_PATH'))
99+
100+ qml_import_path = ':'.join(qml_import_path)
101+ return qml_import_path
102+
103+ def _restart_unity_with_testability(self):
104+ _environment = {}
105+
106+ data_dirs = get_data_dirs(True)
107+ if data_dirs is not None:
108+ _environment['XDG_DATA_DIRS'] = data_dirs
109+
110+ _environment['QML2_IMPORT_PATH'] = (
111+ self._get_qml_import_path_with_mock()
112+ )
113+
114+ new_ld_library_path = [
115+ get_default_extra_mock_libraries(),
116+ self._get_lightdm_mock_path()
117+ ]
118+ if os.getenv('LD_LIBRARY_PATH') is not None:
119+ new_ld_library_path.append(os.getenv('LD_LIBRARY_PATH'))
120+ new_ld_library_path = ':'.join(new_ld_library_path)
121+ _environment['LD_LIBRARY_PATH'] = new_ld_library_path
122+
123+ # FIXME: we shouldn't be doing this
124+ # $MIR_SOCKET, fallback to $XDG_RUNTIME_DIR/mir_socket and
125+ # /tmp/mir_socket as last resort
126+ try:
127+ os.unlink(
128+ os.getenv('MIR_SOCKET',
129+ os.path.join(os.getenv('XDG_RUNTIME_DIR', "/tmp"),
130+ "mir_socket")))
131+ except OSError:
132+ pass
133+ try:
134+ os.unlink("/tmp/mir_socket")
135+ except OSError:
136+ pass
137+
138+ binary_arg = "BINARY=%s" % get_binary_path()
139+ env_args = ["%s=%s" % (k, v) for k, v in _environment.items()]
140+ args = [binary_arg] + env_args
141+ self.unity_proxy = process_helpers.restart_unity_with_testability(
142+ *args)
143+ self.main_win = self.unity_proxy.select_single(
144+ main_window_emulator.QQuickView)
145+
146+ def _create_sensors(self):
147+ # Wait for unity to start running.
148+ Eventually(Equals(True)).match(
149+ lambda: process_helpers.is_job_running('unity8'))
150+
151+ # Wait for the sensors fifo file to be created.
152+ fifo_path = '/tmp/sensor-fifo-{0}'.format(
153+ process_helpers.get_unity_pid())
154+ Eventually(Equals(True)).match(
155+ lambda: os.path.exists(fifo_path))
156+
157+ with open(fifo_path, 'w') as fifo:
158+ fifo.write('create accel 0 1000 0.1\n')
159+ fifo.write('create light 0 10 1\n')
160+ fifo.write('create proximity\n')
161+
32logger = logging.getLogger(__name__)
162logger = logging.getLogger(__name__)
33
163
34
164
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+
35class LaunchDashApp(fixtures.Fixture):
229class LaunchDashApp(fixtures.Fixture):
36
230
37 """Fixture to launch the Dash app."""
231 """Fixture to launch the Dash app."""
@@ -65,7 +259,7 @@ class LaunchDashApp(fixtures.Fixture):
65 pid = process_helpers.start_job('unity8-dash', *all_args)
259 pid = process_helpers.start_job('unity8-dash', *all_args)
66 return introspection.get_proxy_object_for_existing_process(
260 return introspection.get_proxy_object_for_existing_process(
67 pid=pid,
261 pid=pid,
68− emulator_base=emulators.UnityEmulatorBase,
262+ emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase
69 )
263 )
70
264
71 def stop_application(self):
265 def stop_application(self):
@@ -122,7 +316,7 @@ class LaunchMockIndicatorService(fixtures.Fixture):
122 self.ensure_not_running = ensure_not_running
316 self.ensure_not_running = ensure_not_running
123
317
124 def setUp(self):
318 def setUp(self):
125− super(LaunchMockIndicatorService, self).setUp()
319+ super().setUp()
126 if self.ensure_not_running:
320 if self.ensure_not_running:
127 self.ensure_service_not_running()
321 self.ensure_service_not_running()
128 self.addCleanup(self.stop_service)
322 self.addCleanup(self.stop_service)
tests/autopilot/unity8/indicators/__init__.py+2−4
@@ -18,10 +18,8 @@
18import ubuntuuitoolkit
18import ubuntuuitoolkit
19from autopilot import introspection
19from autopilot import introspection
20
20
21−from unity8.shell import emulators
22
21
23−
22+class IndicatorPage(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
24−class IndicatorPage(emulators.UnityEmulatorBase):
25
23
26 """Autopilot helper for the IndicatorPage component."""
24 """Autopilot helper for the IndicatorPage component."""
27
25
@@ -166,7 +164,7 @@ class TestIndicatorPage(IndicatorPage):
166 return self.select_single(objectName='indicator.action.slider')
164 return self.select_single(objectName='indicator.action.slider')
167
165
168
166
169−class Slider(emulators.UnityEmulatorBase):
167+class Slider(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
170
168
171 """Autopilot helper for the Slider component."""
169 """Autopilot helper for the Slider component."""
172
170
tests/autopilot/unity8/indicators/tests/__init__.py−2
@@ -40,5 +40,3 @@ class DeviceIndicatorTestCase(IndicatorTestCase):
40 if platform.model() == 'Desktop':
40 if platform.model() == 'Desktop':
41 self.skipTest('Test cannot be run on the desktop.')
41 self.skipTest('Test cannot be run on the desktop.')
42 super().setUp()
42 super().setUp()
43− self.unity_proxy = self.launch_unity()
44− process_helpers.unlock_unity(self.unity_proxy)
tests/autopilot/unity8/indicators/tests/test_action_latency.py+4−2
@@ -70,10 +70,11 @@ class TestServerValueUpdate(TestIndicatorBaseTestCase):
70 switch_menu = self.indicator_page.get_switch_menu()
70 switch_menu = self.indicator_page.get_switch_menu()
71
71
72 switch.change_state()
72 switch.change_state()
73+ final_value = switch.checked
73
74
74 self.assertThat(
75 self.assertThat(
75 switch_menu.serverChecked,
76 switch_menu.serverChecked,
76− Eventually(Equals(switch.checked), timeout=20)
77+ Eventually(Equals(final_value), timeout=20)
77 )
78 )
78
79
79 def test_slider_reaches_server_value(self):
80 def test_slider_reaches_server_value(self):
@@ -82,6 +83,7 @@ class TestServerValueUpdate(TestIndicatorBaseTestCase):
82
83
83 old_value = slider.value
84 old_value = slider.value
84 slider.slide_left()
85 slider.slide_left()
86+ final_value = slider.value
85
87
86 self.assertThat(
88 self.assertThat(
87 slider_menu.serverValue,
89 slider_menu.serverValue,
@@ -90,7 +92,7 @@ class TestServerValueUpdate(TestIndicatorBaseTestCase):
90
92
91 self.assertThat(
93 self.assertThat(
92 slider_menu.serverValue,
94 slider_menu.serverValue,
93− Eventually(Equals(slider.value), timeout=20)
95+ Eventually(Equals(final_value), timeout=20)
94 )
96 )
95
97
96
98
tests/autopilot/unity8/indicators/tests/test_display_indicator.py+1−1
@@ -18,7 +18,7 @@
18
18
19from unity8 import (
19from unity8 import (
20 fixture_setup,
20 fixture_setup,
21− indicators,
21+ indicators
22)
22)
23from unity8.indicators import tests
23from unity8.indicators import tests
24
24
tests/autopilot/unity8/process_helpers.py+5−4
@@ -21,6 +21,7 @@ import logging
21import subprocess
21import subprocess
22import dbus
22import dbus
23
23
24+import ubuntuuitoolkit
24from autopilot.exceptions import ProcessSearchError
25from autopilot.exceptions import ProcessSearchError
25from autopilot.introspection import get_proxy_object_for_existing_process
26from autopilot.introspection import get_proxy_object_for_existing_process
26
27
@@ -57,7 +58,7 @@ def unlock_unity(unity_proxy_obj=None):
57 """
58 """
58 if unity_proxy_obj is None:
59 if unity_proxy_obj is None:
59 try:
60 try:
60− pid = _get_unity_pid()
61+ pid = get_unity_pid()
61 unity = _get_unity_proxy_object(pid)
62 unity = _get_unity_proxy_object(pid)
62 main_window = unity.select_single(main_window_emulator.QQuickView)
63 main_window = unity.select_single(main_window_emulator.QQuickView)
63 except ProcessSearchError as e:
64 except ProcessSearchError as e:
@@ -107,7 +108,7 @@ def lock_unity(unity_proxy_obj=None):
107 """
108 """
108 if unity_proxy_obj is None:
109 if unity_proxy_obj is None:
109 try:
110 try:
110− pid = _get_unity_pid()
111+ pid = get_unity_pid()
111 unity = _get_unity_proxy_object(pid)
112 unity = _get_unity_proxy_object(pid)
112 main_window = unity.select_single(main_window_emulator.QQuickView)
113 main_window = unity.select_single(main_window_emulator.QQuickView)
113 except ProcessSearchError as e:
114 except ProcessSearchError as e:
@@ -261,7 +262,7 @@ def _get_unity_status():
261 raise CannotAccessUnity(str(error))
262 raise CannotAccessUnity(str(error))
262
263
263
264
264−def _get_unity_pid():
265+def get_unity_pid():
265 try:
266 try:
266 return get_job_pid('unity8')
267 return get_job_pid('unity8')
267 except JobError as error:
268 except JobError as error:
@@ -271,5 +272,5 @@ def _get_unity_pid():
271def _get_unity_proxy_object(pid):
272def _get_unity_proxy_object(pid):
272 return get_proxy_object_for_existing_process(
273 return get_proxy_object_for_existing_process(
273 pid=pid,
274 pid=pid,
274− emulator_base=emulators.UnityEmulatorBase,
275+ emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase
275 )
276 )
tests/autopilot/unity8/sensors.pynew file+93
@@ -0,0 +1,93 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2015 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+"""Control module for event injection to the fake/test platform sensors."""
21+
22+from unity8 import process_helpers
23+
24+
25+class FakePlatformSensors:
26+
27+ def __init__(self, pid=None):
28+ self.pid = pid or process_helpers.get_unity_pid()
29+
30+ def set_orientation(self, action):
31+ if action == 'top_up':
32+ with open("/tmp/sensor-fifo-{0}".format(self.pid), "w") as fifo:
33+ fifo.write("70 accel -10.050858 -0.598550 0.756568\n")
34+ fifo.write("70 accel -9.797073 -0.555455 1.019930\n")
35+ fifo.write("70 accel -10.141838 -0.770933 0.632069\n")
36+ fifo.write("70 accel -12.057199 -1.259350 1.690306\n")
37+ fifo.write("70 accel -19.282900 -3.926491 3.098097\n")
38+ fifo.write("70 accel -14.480132 -14.269443 1.216254\n")
39+ fifo.write("70 accel 16.419436 4.242526 -7.714118\n")
40+ fifo.write("70 accel 5.583278 8.279149 -1.848324\n")
41+ fifo.write("70 accel 1.422156 8.547300 0.416591\n")
42+ fifo.write("70 accel 4.357447 9.988609 -0.110133\n")
43+ fifo.write("70 accel 0.699107 9.840169 0.756568\n")
44+ fifo.write("70 accel 1.364695 9.844957 -0.287304\n")
45+ fifo.flush()
46+ elif action == 'top_down':
47+ with open("/tmp/sensor-fifo-{0}".format(self.pid), "w") as fifo:
48+ fifo.write("70 accel -10.050858 -0.598550 0.756568\n")
49+ fifo.write("70 accel 9.538500 -0.603339 1.292869\n")
50+ fifo.write("70 accel 9.485827 -0.636858 1.422156\n")
51+ fifo.write("70 accel 9.677363 -0.402226 1.374272\n")
52+ fifo.write("70 accel 9.303867 -0.507571 1.283292\n")
53+ fifo.write("70 accel 8.604761 -1.015141 1.436521\n")
54+ fifo.write("70 accel 7.580042 -2.001553 0.521936\n")
55+ fifo.write("70 accel 7.503428 -4.247314 0.502782\n")
56+ fifo.write("70 accel 7.067683 -7.240066 0.842759\n")
57+ fifo.write("70 accel 6.488286 -9.873688 -0.541090\n")
58+ fifo.write("70 accel 6.229713 -9.241618 -1.048660\n")
59+ fifo.write("70 accel 4.046201 -9.198523 -0.057461\n")
60+ fifo.write("70 accel 2.398990 -9.629479 0.957681\n")
61+ fifo.write("70 accel 1.632846 -9.361329 -0.311246\n")
62+ fifo.write("70 accel -0.181959 -9.696517 -0.301669\n")
63+ fifo.flush()
64+ elif action == 'left_up':
65+ with open("/tmp/sensor-fifo-{0}".format(self.pid), "w") as fifo:
66+ fifo.write("70 accel -10.050858 -0.598550 0.756568\n")
67+ fifo.write("70 accel 0.196325 9.878476 0.948104\n")
68+ fifo.write("70 accel 0.258574 9.955091 1.091756\n")
69+ fifo.write("70 accel 0.287304 10.041282 1.134852\n")
70+ fifo.write("70 accel 1.537078 10.553641 1.561020\n")
71+ fifo.write("70 accel 8.130709 10.093954 2.561796\n")
72+ fifo.write("70 accel -0.229843 5.348647 1.723825\n")
73+ fifo.write("70 accel -9.916783 0.488417 -3.418920\n")
74+ fifo.write("70 accel -13.417107 -0.416591 -2.360683\n")
75+ fifo.write("70 accel -13.872005 -2.049437 -0.574608\n")
76+ fifo.flush()
77+ elif action == 'right_up':
78+ with open("/tmp/sensor-fifo-{0}".format(self.pid), "w") as fifo:
79+ fifo.write("70 accel -10.050858 -0.598550 0.756568\n")
80+ fifo.write("70 accel -4.550858 -0.598550 0.856568\n")
81+ fifo.write("70 accel -0.799663 9.988609 1.197101\n")
82+ fifo.write("70 accel -0.861913 9.864111 1.066701\n")
83+ fifo.write("70 accel -0.861913 9.864111 0.866701\n")
84+ fifo.write("70 accel -1.776498 9.830592 1.273715\n")
85+ fifo.write("70 accel -2.376498 9.830592 1.273715\n")
86+ fifo.write("70 accel -13.158532 2.217031 1.091756\n")
87+ fifo.write("70 accel 5.056554 1.067814 0.799663\n")
88+ fifo.write("70 accel 5.056554 1.767814 0.799663\n")
89+ fifo.write("70 accel 14.882358 2.896984 1.221043\n")
90+ fifo.write("70 accel 9.466674 -0.363919 -1.029507\n")
91+ fifo.write("70 accel 11.253524 -0.186748 -0.311246\n")
92+ fifo.write("70 accel 12.253524 -0.186748 -0.311246\n")
93+ fifo.flush()
tests/autopilot/unity8/shell/__init__.py−24
@@ -19,7 +19,6 @@
19
19
20"""unity shell autopilot tests and emulators - sub level package."""
20"""unity shell autopilot tests and emulators - sub level package."""
21
21
22−from time import sleep
23from functools import wraps
22from functools import wraps
24from gi.repository import Notify
23from gi.repository import Notify
25
24
@@ -38,29 +37,6 @@ def disable_qml_mocking(fn):
38 return wrapper
37 return wrapper
39
38
40
39
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−
64def create_ephemeral_notification(
40def create_ephemeral_notification(
65 summary='',
41 summary='',
66 body='',
42 body='',
tests/autopilot/unity8/shell/emulators/__init__.py−6
@@ -17,12 +17,6 @@
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18#
18#
19
19
20−import ubuntuuitoolkit
21−
22−
23−class UnityEmulatorBase(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
24− """A base class for all unity emulators."""
25−
26
20
27class UnityEmulatorException(Exception):
21class UnityEmulatorException(Exception):
28 """Exception raised when there is an error with the Unity emulators."""
22 """Exception raised when there is an error with the Unity emulators."""
tests/autopilot/unity8/shell/emulators/dash.py+23−16
@@ -18,13 +18,13 @@
18#
18#
19
19
20import logging
20import logging
21−import ubuntuuitoolkit
22−
23−from unity8.shell import emulators
24
21
22+import ubuntuuitoolkit
25from autopilot import logging as autopilot_logging
23from autopilot import logging as autopilot_logging
26from autopilot.introspection import dbus
24from autopilot.introspection import dbus
27
25
26+from unity8.shell import emulators
27+
28
28
29logger = logging.getLogger(__name__)
29logger = logging.getLogger(__name__)
30
30
@@ -40,7 +40,7 @@ class DashApp():
40 self.dash = self.main_view.select_single(Dash)
40 self.dash = self.main_view.select_single(Dash)
41
41
42
42
43−class Dash(emulators.UnityEmulatorBase):
43+class Dash(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
44 """An emulator that understands the Dash."""
44 """An emulator that understands the Dash."""
45
45
46 def __init__(self, *args):
46 def __init__(self, *args):
@@ -130,7 +130,7 @@ class Dash(emulators.UnityEmulatorBase):
130 x, y, width, height = dash_content.globalRect
130 x, y, width, height = dash_content.globalRect
131 # Make the drag range be a multiple of the drag "rate" value.
131 # Make the drag range be a multiple of the drag "rate" value.
132 # Workarounds https://bugs.launchpad.net/mir/+bug/1399690
132 # Workarounds https://bugs.launchpad.net/mir/+bug/1399690
133− rate = 10
133+ rate = 5
134 divisions = 5
134 divisions = 5
135 jump = (width / divisions) // rate * rate
135 jump = (width / divisions) // rate * rate
136 start_x = x + jump
136 start_x = x + jump
@@ -146,7 +146,7 @@ class Dash(emulators.UnityEmulatorBase):
146 x, y, width, height = dash_content.globalRect
146 x, y, width, height = dash_content.globalRect
147 # Make the drag range be a multiple of the drag "rate" value.
147 # Make the drag range be a multiple of the drag "rate" value.
148 # Workarounds https://bugs.launchpad.net/mir/+bug/1399690
148 # Workarounds https://bugs.launchpad.net/mir/+bug/1399690
149− rate = 10
149+ rate = 5
150 divisions = 5
150 divisions = 5
151 jump = (width / divisions) // rate * rate
151 jump = (width / divisions) // rate * rate
152 start_x = x + jump * (divisions - 1)
152 start_x = x + jump * (divisions - 1)
@@ -185,14 +185,15 @@ class Dash(emulators.UnityEmulatorBase):
185
185
186
186
187class ListViewWithPageHeader(ubuntuuitoolkit.QQuickFlickable):
187class ListViewWithPageHeader(ubuntuuitoolkit.QQuickFlickable):
188− pass
188+
189+ margin_to_swipe_from_bottom = ubuntuuitoolkit.units.gu(4)
189
190
190
191
191−class GenericScopeView(emulators.UnityEmulatorBase):
192+class GenericScopeView(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
192 """Autopilot emulator for generic scopes."""
193 """Autopilot emulator for generic scopes."""
193
194
194 @autopilot_logging.log_action(logger.info)
195 @autopilot_logging.log_action(logger.info)
195− def open_preview(self, category, app_name):
196+ def open_preview(self, category, app_name, press_duration=0.10):
196 """Open the preview of an application.
197 """Open the preview of an application.
197
198
198 :parameter category: The name of the category where the application is.
199 :parameter category: The name of the category where the application is.
@@ -203,16 +204,19 @@ class GenericScopeView(emulators.UnityEmulatorBase):
203 # FIXME some categories need a long press in order to see the preview.
204 # FIXME some categories need a long press in order to see the preview.
204 # Some categories do not show previews, like recent apps.
205 # Some categories do not show previews, like recent apps.
205 # --elopio - 2014-1-14
206 # --elopio - 2014-1-14
206− self.click_scope_item(category, app_name)
207+ self.click_scope_item(category, app_name, press_duration)
207 preview_list = self.wait_select_single(
208 preview_list = self.wait_select_single(
208 'QQuickLoader', objectName='subPageLoader')
209 'QQuickLoader', objectName='subPageLoader')
209 preview_list.subPageShown.wait_for(True)
210 preview_list.subPageShown.wait_for(True)
210 preview_list.x.wait_for(0)
211 preview_list.x.wait_for(0)
212+ self.get_root_instance().select_single(
213+ objectName='processingIndicator').visible.wait_for(False)
211 return preview_list.select_single(
214 return preview_list.select_single(
212− Preview, objectName='preview{}'.format(preview_list.currentIndex))
215+ Preview, objectName='preview{}'.format(
216+ preview_list.initialIndex))
213
217
214 @autopilot_logging.log_action(logger.debug)
218 @autopilot_logging.log_action(logger.debug)
215− def click_scope_item(self, category, title):
219+ def click_scope_item(self, category, title, press_duration=0.10):
216 """Click an item from the scope.
220 """Click an item from the scope.
217
221
218 :parameter category: The name of the category where the item is.
222 :parameter category: The name of the category where the item is.
@@ -220,9 +224,12 @@ class GenericScopeView(emulators.UnityEmulatorBase):
220
224
221 """
225 """
222 category_element = self._get_category_element(category)
226 category_element = self._get_category_element(category)
223− icon = category_element.wait_select_single('AbstractButton',
227+ icon = category_element.wait_select_single(
224− title=title)
228+ 'AbstractButton', title=title)
229+ list_view = self.select_single(
230+ ListViewWithPageHeader, objectName='categoryListView')
231+ list_view.swipe_child_into_view(icon)
225− self.pointing_device.click_object(icon)
232+ self.pointing_device.click_object(icon, press_duration=press_duration)
226
233
227 def _get_category_element(self, category):
234 def _get_category_element(self, category):
228 try:
235 try:
@@ -255,5 +262,5 @@ class GenericScopeView(emulators.UnityEmulatorBase):
255 return result
262 return result
256
263
257
264
258−class Preview(emulators.UnityEmulatorBase):
265+class Preview(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
259 """Autopilot custom proxy object for generic previews."""
266 """Autopilot custom proxy object for generic previews."""
tests/autopilot/unity8/shell/emulators/greeter.py+2−4
@@ -1,7 +1,7 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2#
2#
3# Unity Autopilot Test Suite
3# Unity Autopilot Test Suite
4−# Copyright (C) 2012, 2013, 2014 Canonical
4+# Copyright (C) 2012, 2013, 2014, 2015 Canonical
5#
5#
6# This program is free software: you can redistribute it and/or modify
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
7# it under the terms of the GNU General Public License as published by
@@ -18,12 +18,10 @@
18#
18#
19
19
20import ubuntuuitoolkit
20import ubuntuuitoolkit
21−
22from autopilot.utilities import sleep
21from autopilot.utilities import sleep
23−from unity8.shell.emulators import UnityEmulatorBase
24
22
25
23
26−class Greeter(UnityEmulatorBase):
24+class Greeter(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
27 """An emulator that understands the greeter screen."""
25 """An emulator that understands the greeter screen."""
28
26
29 def wait_swiped_away(self):
27 def wait_swiped_away(self):
tests/autopilot/unity8/shell/emulators/launcher.py+3−2
@@ -1,7 +1,7 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2#
2#
3# Unity Autopilot Test Suite
3# Unity Autopilot Test Suite
4−# Copyright (C) 2012, 2013, 2014 Canonical
4+# Copyright (C) 2012, 2013, 2014, 2015 Canonical
5#
5#
6# This program is free software: you can redistribute it and/or modify
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
7# it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
20import logging
20import logging
21
21
22import autopilot.logging
22import autopilot.logging
23+import ubuntuuitoolkit
23
24
24from unity8.shell import emulators
25from unity8.shell import emulators
25
26
@@ -27,7 +28,7 @@ from unity8.shell import emulators
27logger = logging.getLogger(__name__)
28logger = logging.getLogger(__name__)
28
29
29
30
30−class Launcher(emulators.UnityEmulatorBase):
31+class Launcher(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
31
32
32 """An emulator that understands the Launcher."""
33 """An emulator that understands the Launcher."""
33
34
tests/autopilot/unity8/shell/emulators/main_window.py+14−2
@@ -19,17 +19,17 @@
19
19
20import logging
20import logging
21
21
22+import ubuntuuitoolkit
22from autopilot import logging as autopilot_logging
23from autopilot import logging as autopilot_logging
23from autopilot import input
24from autopilot import input
24
25
25−from unity8.shell import emulators
26from unity8.shell.emulators.greeter import Greeter
26from unity8.shell.emulators.greeter import Greeter
27from unity8.shell.emulators.launcher import Launcher
27from unity8.shell.emulators.launcher import Launcher
28
28
29logger = logging.getLogger(__name__)
29logger = logging.getLogger(__name__)
30
30
31
31
32−class QQuickView(emulators.UnityEmulatorBase):
32+class QQuickView(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
33 """An emulator class that makes it easy to interact with the shell"""
33 """An emulator class that makes it easy to interact with the shell"""
34
34
35 def get_greeter(self):
35 def get_greeter(self):
@@ -169,6 +169,18 @@ class QQuickView(emulators.UnityEmulatorBase):
169 objectName='pinPadButton{}'.format(button_id)
169 objectName='pinPadButton{}'.format(button_id)
170 )
170 )
171
171
172+ def get_shell_orientation_angle(self):
173+ return self._get_shell().orientationAngle
174+
175+ def get_shell_orientation(self):
176+ return self._get_shell().orientation
177+
178+ def get_shell_primary_orientation(self):
179+ return self._get_shell().primaryOrientation
180+
181+ def get_shell_native_orientation(self):
182+ return self._get_shell().nativeOrientation
183+
172 @autopilot_logging.log_action(logger.info)
184 @autopilot_logging.log_action(logger.info)
173 def wait_for_notification(self):
185 def wait_for_notification(self):
174 """Wait for a notification dialog to appear.
186 """Wait for a notification dialog to appear.
tests/autopilot/unity8/shell/tests/__init__.py+32−111
@@ -24,17 +24,17 @@ try:
24except ImportError:
24except ImportError:
25 Gio = None
25 Gio = None
26
26
27+import logging
28+import os
29+
27from autopilot import introspection
30from autopilot import introspection
28from autopilot.platform import model
31from autopilot.platform import model
29from autopilot.testcase import AutopilotTestCase
32from autopilot.testcase import AutopilotTestCase
30from autopilot.matchers import Eventually
33from autopilot.matchers import Eventually
31−from autopilot.input import Touch
32from autopilot.display import Display
34from autopilot.display import Display
33−import logging
34−import os.path
35−import subprocess
36−import sys
37from testtools.matchers import Equals
35from testtools.matchers import Equals
36+
37+import ubuntuuitoolkit
38from ubuntuuitoolkit import (
38from ubuntuuitoolkit import (
39 fixture_setup as toolkit_fixtures,
39 fixture_setup as toolkit_fixtures,
40 ubuntu_scenarios
40 ubuntu_scenarios
@@ -51,7 +51,6 @@ from unity8 import (
51 fixture_setup,
51 fixture_setup,
52 process_helpers
52 process_helpers
53)
53)
54−from unity8.shell import emulators
55from unity8.shell.emulators import (
54from unity8.shell.emulators import (
56 dash as dash_helpers,
55 dash as dash_helpers,
57 main_window as main_window_emulator,
56 main_window as main_window_emulator,
@@ -93,36 +92,25 @@ class UnityTestCase(AutopilotTestCase):
93 @classmethod
92 @classmethod
94 def setUpClass(cls):
93 def setUpClass(cls):
95 try:
94 try:
96− output = subprocess.check_output(
95+ is_unity_running = process_helpers.is_job_running('unity8')
97− ["/sbin/initctl", "status", "unity8"],
96+ except process_helpers.JobError as e:
98− stderr=subprocess.STDOUT,
97+ xdg_config_home = os.getenv(
99− universal_newlines=True,
98+ 'XDG_CONFIG_HOME', os.path.join(os.getenv('HOME'), '.config'))
100− )
99+ upstart_config_path = os.path.join(xdg_config_home, 'upstart')
101− except subprocess.CalledProcessError as e:
100+ logger.error(
102− sys.stderr.write(
103− "Error: `initctl status unity8` failed, most probably the "
101+ '`initctl status unity8` failed, most probably the '
104− "unity8 session could not be found:\n\n"
102+ 'unity8 session could not be found:\n\n'
105− "{0}\n"
103+ '{0}\n'
106− "Please install unity8 or copy data/unity8.conf to "
104+ 'Please install unity8 or copy data/unity8.conf to '
107− "{1}\n".format(
105+ '{1}\n'.format(e.output, upstart_config_path)
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"
124 )
106 )
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')
126
114
127 def setUp(self):
115 def setUp(self):
128 super().setUp()
116 super().setUp()
@@ -134,13 +122,6 @@ class UnityTestCase(AutopilotTestCase):
134 self._data_dirs_mock_enabled = True
122 self._data_dirs_mock_enabled = True
135 self._environment = {}
123 self._environment = {}
136
124
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()
144 self._setup_display_details()
125 self._setup_display_details()
145
126
146 def _setup_display_details(self):
127 def _setup_display_details(self):
@@ -196,40 +177,6 @@ class UnityTestCase(AutopilotTestCase):
196 divisor = divisor * 2
177 divisor = divisor * 2
197 return divisor
178 return divisor
198
179
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−
233 def launch_unity(self, mode="full-greeter", *args):
180 def launch_unity(self, mode="full-greeter", *args):
234 """
181 """
235 Launch the unity shell, return a proxy object for it.
182 Launch the unity shell, return a proxy object for it.
@@ -257,21 +204,6 @@ class UnityTestCase(AutopilotTestCase):
257 if self._data_dirs_mock_enabled:
204 if self._data_dirs_mock_enabled:
258 self._patch_data_dirs()
205 self._patch_data_dirs()
259
206
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−
275 unity8_cli_args_list = ["--mode={}".format(mode)]
207 unity8_cli_args_list = ["--mode={}".format(mode)]
276 if len(args) != 0:
208 if len(args) != 0:
277 unity8_cli_args_list += args
209 unity8_cli_args_list += args
@@ -297,26 +229,15 @@ class UnityTestCase(AutopilotTestCase):
297
229
298 def _launch_unity_with_upstart(self, binary_path, args):
230 def _launch_unity_with_upstart(self, binary_path, args):
299 logger.info("Starting unity")
231 logger.info("Starting unity")
232+ self.useFixture(toolkit_fixtures.InitctlEnvironmentVariable(
300− self._patch_environment("QT_LOAD_TESTABILITY", 1)
233+ global_=True, 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
306
234
307− self.addCleanup(self._cleanup_launching_upstart_unity)
235+ variables = self._environment
308−
236+ variables['ARGS'] = " ".join(args)
309− return process_helpers.restart_unity_with_testability(*all_args)
237+ launch_unity_fixture = fixture_setup.RestartUnityWithTestability(
310−
238+ binary_path, variables)
311− def _cleanup_launching_upstart_unity(self):
239+ self.useFixture(launch_unity_fixture)
312− logger.info("Stopping unity")
240+ return launch_unity_fixture.unity_proxy
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!")
320
241
321 def _patch_data_dirs(self):
242 def _patch_data_dirs(self):
322 data_dirs = get_data_dirs(self._data_dirs_mock_enabled)
243 data_dirs = get_data_dirs(self._data_dirs_mock_enabled)
@@ -369,7 +290,7 @@ class UnityTestCase(AutopilotTestCase):
369 pid = process_helpers.get_job_pid('unity8-dash')
290 pid = process_helpers.get_job_pid('unity8-dash')
370 dash_proxy = introspection.get_proxy_object_for_existing_process(
291 dash_proxy = introspection.get_proxy_object_for_existing_process(
371 pid=pid,
292 pid=pid,
372− emulator_base=emulators.UnityEmulatorBase,
293+ emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase
373 )
294 )
374 dash_app = dash_helpers.DashApp(dash_proxy)
295 dash_app = dash_helpers.DashApp(dash_proxy)
375 return dash_app.dash
296 return dash_app.dash
tests/autopilot/unity8/shell/tests/test_emulators.py+6
@@ -120,6 +120,12 @@ class GenericScopeViewEmulatorTestCase(tests.DashBaseTestCase):
120 self.assertIsInstance(preview, dash_emulators.Preview)
120 self.assertIsInstance(preview, dash_emulators.Preview)
121 self.assertTrue(preview.isCurrent)
121 self.assertTrue(preview.isCurrent)
122
122
123+ def test_open_preview_of_non_visible_item(self):
124+ """Open an item that requires swiping to make it visible."""
125+ preview = self.generic_scope.open_preview('2', 'Title.2.0')
126+ self.assertIsInstance(preview, dash_emulators.Preview)
127+ self.assertTrue(preview.isCurrent)
128+
123
129
124class DashAppsEmulatorTestCase(tests.DashBaseTestCase):
130class DashAppsEmulatorTestCase(tests.DashBaseTestCase):
125
131
tests/autopilot/unity8/shell/tests/test_rotation.pynew file+113
@@ -0,0 +1,113 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2015 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+"""Tests for shell-rotation"""
21+
22+from autopilot.platform import model
23+from unity8 import (
24+ fixture_setup,
25+ process_helpers
26+)
27+from unity8.shell import tests
28+import logging
29+from testtools.matchers import Equals
30+from autopilot.matchers import Eventually
31+
32+logger = logging.getLogger(__name__)
33+
34+
35+class RotationBase(tests.UnityTestCase):
36+ """Base class for all shell-rotation tests that provides helper methods."""
37+
38+ def setUp(self):
39+ if model() == 'Desktop':
40+ self.skipTest('Test cannot be run on the desktop.')
41+ super().setUp()
42+ self._qml_mock_enabled = False
43+ self._data_dirs_mock_enabled = False
44+
45+ def _assert_change_of_orientation_and_angle(self):
46+ self.assertThat(self.shell_proxy.orientation,
47+ Eventually(Equals(self.orientation)))
48+ self.assertThat(self.shell_proxy.orientationAngle,
49+ Eventually(Equals(self.angle)))
50+
51+
52+class TestFakeSensor(RotationBase):
53+ scenarios = [('top up', {'action': 'top_up', 'orientation': 1}),
54+ ('right up', {'action': 'right_up', 'orientation': 8}),
55+ ('top down', {'action': 'top_down', 'orientation': 4}),
56+ ('left up', {'action': 'left_up', 'orientation': 2})]
57+
58+ def test_fake_sensor(self):
59+ unity_with_sensors = fixture_setup.LaunchUnityWithFakeSensors()
60+ self.useFixture(unity_with_sensors)
61+ process_helpers.unlock_unity(unity_with_sensors.unity_proxy)
62+ fake_sensors = unity_with_sensors.fake_sensors
63+ o_proxy = unity_with_sensors.main_win.select_single('OrientedShell')
64+
65+ fake_sensors.set_orientation(self.action)
66+ self.assertThat(o_proxy.physicalOrientation,
67+ Eventually(Equals(self.orientation), timeout=15))
68+
69+
70+class TestRotationWithApp(RotationBase):
71+ scenarios = [
72+ ('top up, angle 0',
73+ {'action': 'top_up', 'orientation': 1, 'angle': 0}),
74+ ('right up, angle 90',
75+ {'action': 'right_up', 'orientation': 8, 'angle': 90}),
76+ ('top down, angle 180',
77+ {'action': 'top_down', 'orientation': 4, 'angle': 180}),
78+ ('left up, angle 270',
79+ {'action': 'left_up', 'orientation': 2, 'angle': 270})]
80+
81+ def test_rotation_with_webbrowser_app(self):
82+ """Do an orientation-change and verify that an app and the shell
83+ adapted correctly"""
84+
85+ unity_with_sensors = fixture_setup.LaunchUnityWithFakeSensors()
86+ self.useFixture(unity_with_sensors)
87+ process_helpers.unlock_unity(unity_with_sensors.unity_proxy)
88+ fake_sensors = unity_with_sensors.fake_sensors
89+ o_proxy = unity_with_sensors.main_win.select_single('OrientedShell')
90+ self.shell_proxy = unity_with_sensors.main_win.select_single('Shell')
91+
92+ # launch an application
93+ self.launch_upstart_application('webbrowser-app')
94+ unity_with_sensors.main_win.show_dash_from_launcher()
95+ unity_with_sensors.main_win.launch_application('webbrowser-app')
96+
97+ # skip test early, if device doesn't support a certain orientation
98+ if not (self.shell_proxy.orientation & o_proxy.supportedOrientations):
99+ self.skipTest('unsupported orientation ' + self.action)
100+
101+ self.assertThat(
102+ unity_with_sensors.main_win.get_current_focused_app_id(),
103+ Eventually(Equals('webbrowser-app')))
104+
105+ # get default orientation and angle
106+ self.orientation = self.shell_proxy.orientation
107+ self.angle = self.shell_proxy.orientationAngle
108+
109+ # check if fake sensors affect orientation and angle
110+ fake_sensors.set_orientation(self.action)
111+ self.assertThat(o_proxy.physicalOrientation,
112+ Eventually(Equals(self.orientation), timeout=15))
113+ self._assert_change_of_orientation_and_angle()
tests/autopilot/unity8/shell/tests/test_upstart.py+5−4
@@ -1,7 +1,7 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2#
2#
3# Unity Autopilot Test Suite
3# Unity Autopilot Test Suite
4−# Copyright (C) 2013, 2014 Canonical
4+# Copyright (C) 2013, 2014, 2015 Canonical
5#
5#
6# This program is free software: you can redistribute it and/or modify
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
7# it under the terms of the GNU General Public License as published by
@@ -26,13 +26,13 @@ import subprocess
26import time
26import time
27
27
28import fixtures
28import fixtures
29−from testtools.matchers import Equals, MismatchError
29+import ubuntuuitoolkit
30from autopilot.matchers import Eventually
30from autopilot.matchers import Eventually
31from autopilot.introspection import get_proxy_object_for_existing_process
31from autopilot.introspection import get_proxy_object_for_existing_process
32+from testtools.matchers import Equals, MismatchError
32from ubuntuuitoolkit import ubuntu_scenarios
33from ubuntuuitoolkit import ubuntu_scenarios
33
34
34from unity8 import get_binary_path
35from unity8 import get_binary_path
35−from unity8.shell.emulators import UnityEmulatorBase
36from unity8.shell.tests import UnityTestCase
36from unity8.shell.tests import UnityTestCase
37
37
38import logging
38import logging
@@ -90,10 +90,11 @@ class UpstartIntegrationTests(UnityTestCase):
90 self.addCleanup(ensure_stopped)
90 self.addCleanup(ensure_stopped)
91
91
92 def _set_proxy(self):
92 def _set_proxy(self):
93+ proxy_base = ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase
93 super()._set_proxy(
94 super()._set_proxy(
94 get_proxy_object_for_existing_process(
95 get_proxy_object_for_existing_process(
95 pid=self.process.pid,
96 pid=self.process.pid,
96− emulator_base=UnityEmulatorBase,))
97+ emulator_base=proxy_base))
97
98
98 def test_no_sigstop(self):
99 def test_no_sigstop(self):
99 self.useFixture(
100 self.useFixture(
tests/libs/UbuntuGestures/tst_TouchRegistry.cpp+139−74
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -53,8 +53,8 @@ private Q_SLOTS:
53 void initTestCase() {} // will be called before the first test function is executed
53 void initTestCase() {} // will be called before the first test function is executed
54 void cleanupTestCase() {} // will be called after the last test function was executed.
54 void cleanupTestCase() {} // will be called after the last test function was executed.
55
55
56− void init() {} // called right before each and every test function is executed
56+ void init(); // called right before each and every test function is executed
57− void cleanup() {} // called right after each and every test function is executed
57+ void cleanup(); // called right after each and every test function is executed
58
58
59 void requestWithNoCandidates();
59 void requestWithNoCandidates();
60 void lateCandidateRequestGetsNothing();
60 void lateCandidateRequestGetsNothing();
@@ -66,11 +66,24 @@ private Q_SLOTS:
66 void candidatesAndWatchers_2();
66 void candidatesAndWatchers_2();
67 void rejectingTouchfterItsEnd();
67 void rejectingTouchfterItsEnd();
68 void removeOldUndecidedCandidates();
68 void removeOldUndecidedCandidates();
69+ void interimOwnerWontGetUnownedTouchEvents();
70+
71+private:
72+ TouchRegistry *touchRegistry;
69};
73};
70
74
75+void tst_TouchRegistry::init()
76+{
77+ touchRegistry = TouchRegistry::instance();
78+}
79+
80+void tst_TouchRegistry::cleanup()
81+{
82+ delete touchRegistry;
83+}
84+
71void tst_TouchRegistry::requestWithNoCandidates()
85void tst_TouchRegistry::requestWithNoCandidates()
72{
86{
73− TouchRegistry touchRegistry;
74 DummyCandidate candidate;
87 DummyCandidate candidate;
75
88
76 {
89 {
@@ -82,17 +95,16 @@ void tst_TouchRegistry::requestWithNoCandidates()
82 Qt::NoModifier,
95 Qt::NoModifier,
83 Qt::TouchPointPressed,
96 Qt::TouchPointPressed,
84 touchPoints);
97 touchPoints);
85− touchRegistry.update(&touchEvent);
98+ touchRegistry->update(&touchEvent);
86 }
99 }
87
100
88− touchRegistry.requestTouchOwnership(0, &candidate);
101+ touchRegistry->requestTouchOwnership(0, &candidate);
89
102
90 QVERIFY(candidate.ownedTouches.contains(0));
103 QVERIFY(candidate.ownedTouches.contains(0));
91}
104}
92
105
93void tst_TouchRegistry::lateCandidateRequestGetsNothing()
106void tst_TouchRegistry::lateCandidateRequestGetsNothing()
94{
107{
95− TouchRegistry touchRegistry;
96 DummyCandidate earlyCandidate;
108 DummyCandidate earlyCandidate;
97 earlyCandidate.setObjectName("early");
109 earlyCandidate.setObjectName("early");
98 DummyCandidate lateCandidate;
110 DummyCandidate lateCandidate;
@@ -107,21 +119,21 @@ void tst_TouchRegistry::lateCandidateRequestGetsNothing()
107 Qt::NoModifier,
119 Qt::NoModifier,
108 Qt::TouchPointPressed,
120 Qt::TouchPointPressed,
109 touchPoints);
121 touchPoints);
110− touchRegistry.update(&touchEvent);
122+ touchRegistry->update(&touchEvent);
111 }
123 }
112
124
113− touchRegistry.addCandidateOwnerForTouch(0, &earlyCandidate);
125+ touchRegistry->addCandidateOwnerForTouch(0, &earlyCandidate);
114− touchRegistry.addCandidateOwnerForTouch(0, &lateCandidate);
126+ touchRegistry->addCandidateOwnerForTouch(0, &lateCandidate);
115
127
116 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
128 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
117 QVERIFY(lateCandidate.ownedTouches.isEmpty());
129 QVERIFY(lateCandidate.ownedTouches.isEmpty());
118
130
119− touchRegistry.requestTouchOwnership(0, &lateCandidate);
131+ touchRegistry->requestTouchOwnership(0, &lateCandidate);
120
132
121 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
133 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
122 QVERIFY(lateCandidate.ownedTouches.isEmpty());
134 QVERIFY(lateCandidate.ownedTouches.isEmpty());
123
135
124− touchRegistry.requestTouchOwnership(0, &earlyCandidate);
136+ touchRegistry->requestTouchOwnership(0, &earlyCandidate);
125
137
126 QVERIFY(earlyCandidate.ownedTouches.contains(0));
138 QVERIFY(earlyCandidate.ownedTouches.contains(0));
127 QVERIFY(lateCandidate.ownedTouches.isEmpty());
139 QVERIFY(lateCandidate.ownedTouches.isEmpty());
@@ -129,7 +141,6 @@ void tst_TouchRegistry::lateCandidateRequestGetsNothing()
129
141
130void tst_TouchRegistry::lateCandidateGestOwnershipOnceEarlyCandidateQuits()
142void tst_TouchRegistry::lateCandidateGestOwnershipOnceEarlyCandidateQuits()
131{
143{
132− TouchRegistry touchRegistry;
133 DummyCandidate earlyCandidate;
144 DummyCandidate earlyCandidate;
134 DummyCandidate lateCandidate;
145 DummyCandidate lateCandidate;
135
146
@@ -142,21 +153,21 @@ void tst_TouchRegistry::lateCandidateGestOwnershipOnceEarlyCandidateQuits()
142 Qt::NoModifier,
153 Qt::NoModifier,
143 Qt::TouchPointPressed,
154 Qt::TouchPointPressed,
144 touchPoints);
155 touchPoints);
145− touchRegistry.update(&touchEvent);
156+ touchRegistry->update(&touchEvent);
146 }
157 }
147
158
148− touchRegistry.addCandidateOwnerForTouch(0, &earlyCandidate);
159+ touchRegistry->addCandidateOwnerForTouch(0, &earlyCandidate);
149− touchRegistry.addCandidateOwnerForTouch(0, &lateCandidate);
160+ touchRegistry->addCandidateOwnerForTouch(0, &lateCandidate);
150
161
151 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
162 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
152 QVERIFY(lateCandidate.ownedTouches.isEmpty());
163 QVERIFY(lateCandidate.ownedTouches.isEmpty());
153
164
154− touchRegistry.requestTouchOwnership(0, &lateCandidate);
165+ touchRegistry->requestTouchOwnership(0, &lateCandidate);
155
166
156 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
167 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
157 QVERIFY(lateCandidate.ownedTouches.isEmpty());
168 QVERIFY(lateCandidate.ownedTouches.isEmpty());
158
169
159− touchRegistry.removeCandidateOwnerForTouch(0, &earlyCandidate);
170+ touchRegistry->removeCandidateOwnerForTouch(0, &earlyCandidate);
160
171
161 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
172 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
162 QVERIFY(lateCandidate.ownedTouches.contains(0));
173 QVERIFY(lateCandidate.ownedTouches.contains(0));
@@ -164,7 +175,6 @@ void tst_TouchRegistry::lateCandidateGestOwnershipOnceEarlyCandidateQuits()
164
175
165void tst_TouchRegistry::dispatchesTouchEventsToCandidates()
176void tst_TouchRegistry::dispatchesTouchEventsToCandidates()
166{
177{
167− TouchRegistry touchRegistry;
168 QQuickItem rootItem;
178 QQuickItem rootItem;
169
179
170 DummyCandidate candidate0;
180 DummyCandidate candidate0;
@@ -189,10 +199,10 @@ void tst_TouchRegistry::dispatchesTouchEventsToCandidates()
189 Qt::NoModifier,
199 Qt::NoModifier,
190 Qt::TouchPointPressed,
200 Qt::TouchPointPressed,
191 touchPoints);
201 touchPoints);
192− touchRegistry.update(&touchEvent);
202+ touchRegistry->update(&touchEvent);
193 }
203 }
194
204
195− touchRegistry.addCandidateOwnerForTouch(0, &candidate0);
205+ touchRegistry->addCandidateOwnerForTouch(0, &candidate0);
196
206
197 {
207 {
198 QList<QTouchEvent::TouchPoint> touchPoints;
208 QList<QTouchEvent::TouchPoint> touchPoints;
@@ -207,7 +217,7 @@ void tst_TouchRegistry::dispatchesTouchEventsToCandidates()
207 Qt::NoModifier,
217 Qt::NoModifier,
208 Qt::TouchPointPressed | Qt::TouchPointMoved,
218 Qt::TouchPointPressed | Qt::TouchPointMoved,
209 touchPoints);
219 touchPoints);
210− touchRegistry.update(&touchEvent);
220+ touchRegistry->update(&touchEvent);
211 }
221 }
212
222
213 // candidate0 should have received an update on the touch he's interested on.
223 // candidate0 should have received an update on the touch he's interested on.
@@ -219,7 +229,7 @@ void tst_TouchRegistry::dispatchesTouchEventsToCandidates()
219 QCOMPARE(candidate0.unownedTouchEvents[0].touchPoints[0].rect().x(), 11 - candidate0.x());
229 QCOMPARE(candidate0.unownedTouchEvents[0].touchPoints[0].rect().x(), 11 - candidate0.x());
220 QCOMPARE(candidate0.unownedTouchEvents[0].touchPoints[0].rect().y(), 11 - candidate0.y());
230 QCOMPARE(candidate0.unownedTouchEvents[0].touchPoints[0].rect().y(), 11 - candidate0.y());
221
231
222− touchRegistry.addCandidateOwnerForTouch(1, &candidate1);
232+ touchRegistry->addCandidateOwnerForTouch(1, &candidate1);
223
233
224 {
234 {
225 QList<QTouchEvent::TouchPoint> touchPoints;
235 QList<QTouchEvent::TouchPoint> touchPoints;
@@ -234,7 +244,7 @@ void tst_TouchRegistry::dispatchesTouchEventsToCandidates()
234 Qt::NoModifier,
244 Qt::NoModifier,
235 Qt::TouchPointPressed | Qt::TouchPointMoved,
245 Qt::TouchPointPressed | Qt::TouchPointMoved,
236 touchPoints);
246 touchPoints);
237− touchRegistry.update(&touchEvent);
247+ touchRegistry->update(&touchEvent);
238 }
248 }
239
249
240 // candidate0 gets updates only for touch 0 and
250 // candidate0 gets updates only for touch 0 and
@@ -251,7 +261,6 @@ void tst_TouchRegistry::dispatchesTouchEventsToCandidates()
251
261
252void tst_TouchRegistry::dispatchesTouchEventsToWatchers()
262void tst_TouchRegistry::dispatchesTouchEventsToWatchers()
253{
263{
254− TouchRegistry touchRegistry;
255 QQuickItem rootItem;
264 QQuickItem rootItem;
256
265
257 DummyCandidate watcher;
266 DummyCandidate watcher;
@@ -270,10 +279,10 @@ void tst_TouchRegistry::dispatchesTouchEventsToWatchers()
270 Qt::NoModifier,
279 Qt::NoModifier,
271 Qt::TouchPointPressed,
280 Qt::TouchPointPressed,
272 touchPoints);
281 touchPoints);
273− touchRegistry.update(&touchEvent);
282+ touchRegistry->update(&touchEvent);
274 }
283 }
275
284
276− touchRegistry.addTouchWatcher(0, &watcher);
285+ touchRegistry->addTouchWatcher(0, &watcher);
277
286
278 {
287 {
279 QList<QTouchEvent::TouchPoint> touchPoints;
288 QList<QTouchEvent::TouchPoint> touchPoints;
@@ -288,7 +297,7 @@ void tst_TouchRegistry::dispatchesTouchEventsToWatchers()
288 Qt::NoModifier,
297 Qt::NoModifier,
289 Qt::TouchPointPressed | Qt::TouchPointMoved,
298 Qt::TouchPointPressed | Qt::TouchPointMoved,
290 touchPoints);
299 touchPoints);
291− touchRegistry.update(&touchEvent);
300+ touchRegistry->update(&touchEvent);
292 }
301 }
293
302
294 // watcher should have received an update on the touch he's interested on.
303 // watcher should have received an update on the touch he's interested on.
@@ -313,7 +322,7 @@ void tst_TouchRegistry::dispatchesTouchEventsToWatchers()
313 Qt::NoModifier,
322 Qt::NoModifier,
314 Qt::TouchPointPressed | Qt::TouchPointMoved,
323 Qt::TouchPointPressed | Qt::TouchPointMoved,
315 touchPoints);
324 touchPoints);
316− touchRegistry.update(&touchEvent);
325+ touchRegistry->update(&touchEvent);
317 }
326 }
318
327
319 // watcher gets updates only for touch 0
328 // watcher gets updates only for touch 0
@@ -325,7 +334,6 @@ void tst_TouchRegistry::dispatchesTouchEventsToWatchers()
325
334
326void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
335void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
327{
336{
328− TouchRegistry touchRegistry;
329 DummyCandidate item;
337 DummyCandidate item;
330
338
331 {
339 {
@@ -337,10 +345,10 @@ void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
337 Qt::NoModifier,
345 Qt::NoModifier,
338 Qt::TouchPointPressed,
346 Qt::TouchPointPressed,
339 touchPoints);
347 touchPoints);
340− touchRegistry.update(&touchEvent);
348+ touchRegistry->update(&touchEvent);
341 }
349 }
342
350
343− touchRegistry.addCandidateOwnerForTouch(0, &item);
351+ touchRegistry->addCandidateOwnerForTouch(0, &item);
344
352
345 {
353 {
346 QList<QTouchEvent::TouchPoint> touchPoints;
354 QList<QTouchEvent::TouchPoint> touchPoints;
@@ -353,7 +361,7 @@ void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
353 Qt::NoModifier,
361 Qt::NoModifier,
354 Qt::TouchPointPressed | Qt::TouchPointMoved,
362 Qt::TouchPointPressed | Qt::TouchPointMoved,
355 touchPoints);
363 touchPoints);
356− touchRegistry.update(&touchEvent);
364+ touchRegistry->update(&touchEvent);
357 }
365 }
358
366
359 QCOMPARE(item.unownedTouchEvents.count(), 1);
367 QCOMPARE(item.unownedTouchEvents.count(), 1);
@@ -361,7 +369,7 @@ void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
361 QCOMPARE(item.unownedTouchEvents[0].touchPoints[0].id(), 0);
369 QCOMPARE(item.unownedTouchEvents[0].touchPoints[0].id(), 0);
362 item.unownedTouchEvents.clear();
370 item.unownedTouchEvents.clear();
363
371
364− touchRegistry.addTouchWatcher(1, &item);
372+ touchRegistry->addTouchWatcher(1, &item);
365
373
366 {
374 {
367 QList<QTouchEvent::TouchPoint> touchPoints;
375 QList<QTouchEvent::TouchPoint> touchPoints;
@@ -374,7 +382,7 @@ void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
374 Qt::NoModifier,
382 Qt::NoModifier,
375 Qt::TouchPointMoved,
383 Qt::TouchPointMoved,
376 touchPoints);
384 touchPoints);
377− touchRegistry.update(&touchEvent);
385+ touchRegistry->update(&touchEvent);
378 }
386 }
379
387
380 QCOMPARE(item.unownedTouchEvents.count(), 1);
388 QCOMPARE(item.unownedTouchEvents.count(), 1);
@@ -394,7 +402,7 @@ void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
394 Qt::NoModifier,
402 Qt::NoModifier,
395 Qt::TouchPointReleased | Qt::TouchPointMoved,
403 Qt::TouchPointReleased | Qt::TouchPointMoved,
396 touchPoints);
404 touchPoints);
397− touchRegistry.update(&touchEvent);
405+ touchRegistry->update(&touchEvent);
398 }
406 }
399
407
400 QCOMPARE(item.unownedTouchEvents.count(), 1);
408 QCOMPARE(item.unownedTouchEvents.count(), 1);
@@ -403,7 +411,7 @@ void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
403 QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(1));
411 QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(1));
404 item.unownedTouchEvents.clear();
412 item.unownedTouchEvents.clear();
405
413
406− touchRegistry.removeCandidateOwnerForTouch(0, &item);
414+ touchRegistry->removeCandidateOwnerForTouch(0, &item);
407
415
408 {
416 {
409 QList<QTouchEvent::TouchPoint> touchPoints;
417 QList<QTouchEvent::TouchPoint> touchPoints;
@@ -414,7 +422,7 @@ void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
414 Qt::NoModifier,
422 Qt::NoModifier,
415 Qt::TouchPointReleased,
423 Qt::TouchPointReleased,
416 touchPoints);
424 touchPoints);
417− touchRegistry.update(&touchEvent);
425+ touchRegistry->update(&touchEvent);
418 }
426 }
419
427
420 QCOMPARE(item.unownedTouchEvents.count(), 1);
428 QCOMPARE(item.unownedTouchEvents.count(), 1);
@@ -422,7 +430,7 @@ void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
422 QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(1));
430 QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(1));
423 item.unownedTouchEvents.clear();
431 item.unownedTouchEvents.clear();
424
432
425− QVERIFY(touchRegistry.m_touchInfoPool.isEmpty());
433+ QVERIFY(touchRegistry->m_touchInfoPool.isEmpty());
426}
434}
427
435
428/*
436/*
@@ -431,7 +439,6 @@ void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
431 */
439 */
432void tst_TouchRegistry::candidatesAndWatchers_1()
440void tst_TouchRegistry::candidatesAndWatchers_1()
433{
441{
434− TouchRegistry touchRegistry;
435 DummyCandidate item;
442 DummyCandidate item;
436
443
437 {
444 {
@@ -443,10 +450,10 @@ void tst_TouchRegistry::candidatesAndWatchers_1()
443 Qt::NoModifier,
450 Qt::NoModifier,
444 Qt::TouchPointPressed,
451 Qt::TouchPointPressed,
445 touchPoints);
452 touchPoints);
446− touchRegistry.update(&touchEvent);
453+ touchRegistry->update(&touchEvent);
447 }
454 }
448
455
449− touchRegistry.addCandidateOwnerForTouch(0, &item);
456+ touchRegistry->addCandidateOwnerForTouch(0, &item);
450
457
451 {
458 {
452 QList<QTouchEvent::TouchPoint> touchPoints;
459 QList<QTouchEvent::TouchPoint> touchPoints;
@@ -459,14 +466,14 @@ void tst_TouchRegistry::candidatesAndWatchers_1()
459 Qt::NoModifier,
466 Qt::NoModifier,
460 Qt::TouchPointPressed | Qt::TouchPointMoved,
467 Qt::TouchPointPressed | Qt::TouchPointMoved,
461 touchPoints);
468 touchPoints);
462− touchRegistry.update(&touchEvent);
469+ touchRegistry->update(&touchEvent);
463 }
470 }
464
471
465− touchRegistry.addTouchWatcher(1, &item);
472+ touchRegistry->addTouchWatcher(1, &item);
466
473
467− touchRegistry.removeCandidateOwnerForTouch(0, &item);
474+ touchRegistry->removeCandidateOwnerForTouch(0, &item);
468
475
469− touchRegistry.addTouchWatcher(0, &item);
476+ touchRegistry->addTouchWatcher(0, &item);
470
477
471 {
478 {
472 QList<QTouchEvent::TouchPoint> touchPoints;
479 QList<QTouchEvent::TouchPoint> touchPoints;
@@ -479,7 +486,7 @@ void tst_TouchRegistry::candidatesAndWatchers_1()
479 Qt::NoModifier,
486 Qt::NoModifier,
480 Qt::TouchPointReleased | Qt::TouchPointMoved,
487 Qt::TouchPointReleased | Qt::TouchPointMoved,
481 touchPoints);
488 touchPoints);
482− touchRegistry.update(&touchEvent);
489+ touchRegistry->update(&touchEvent);
483 }
490 }
484
491
485 {
492 {
@@ -491,10 +498,10 @@ void tst_TouchRegistry::candidatesAndWatchers_1()
491 Qt::NoModifier,
498 Qt::NoModifier,
492 Qt::TouchPointReleased,
499 Qt::TouchPointReleased,
493 touchPoints);
500 touchPoints);
494− touchRegistry.update(&touchEvent);
501+ touchRegistry->update(&touchEvent);
495 }
502 }
496
503
497− QVERIFY(touchRegistry.m_touchInfoPool.isEmpty());
504+ QVERIFY(touchRegistry->m_touchInfoPool.isEmpty());
498
505
499 item.unownedTouchEvents.clear();
506 item.unownedTouchEvents.clear();
500
507
@@ -507,7 +514,7 @@ void tst_TouchRegistry::candidatesAndWatchers_1()
507 Qt::NoModifier,
514 Qt::NoModifier,
508 Qt::TouchPointPressed,
515 Qt::TouchPointPressed,
509 touchPoints);
516 touchPoints);
510− touchRegistry.update(&touchEvent);
517+ touchRegistry->update(&touchEvent);
511 }
518 }
512
519
513 // Haven't made any subscription for that new touch 0.
520 // Haven't made any subscription for that new touch 0.
@@ -520,8 +527,6 @@ void tst_TouchRegistry::candidatesAndWatchers_1()
520 */
527 */
521void tst_TouchRegistry::candidatesAndWatchers_2()
528void tst_TouchRegistry::candidatesAndWatchers_2()
522{
529{
523− TouchRegistry touchRegistry;
524−
525 DummyCandidate directionalDragArea;
530 DummyCandidate directionalDragArea;
526 directionalDragArea.setObjectName("DirectionalDragArea");
531 directionalDragArea.setObjectName("DirectionalDragArea");
527
532
@@ -538,14 +543,14 @@ void tst_TouchRegistry::candidatesAndWatchers_2()
538 Qt::NoModifier,
543 Qt::NoModifier,
539 Qt::TouchPointPressed,
544 Qt::TouchPointPressed,
540 touchPoints);
545 touchPoints);
541− touchRegistry.update(&touchEvent);
546+ touchRegistry->update(&touchEvent);
542 }
547 }
543
548
544 // [TouchRegistry] addCandidateOwnerForTouch id 0 candidate DirectionalDragArea
549 // [TouchRegistry] addCandidateOwnerForTouch id 0 candidate DirectionalDragArea
545− touchRegistry.addCandidateOwnerForTouch(0, &directionalDragArea);
550+ touchRegistry->addCandidateOwnerForTouch(0, &directionalDragArea);
546
551
547 // [TouchRegistry] requestTouchOwnership id 0 candidate TouchGate
552 // [TouchRegistry] requestTouchOwnership id 0 candidate TouchGate
548− touchRegistry.requestTouchOwnership(0, &touchGate);
553+ touchRegistry->requestTouchOwnership(0, &touchGate);
549
554
550 //[TouchRegistry] got TouchUpdate (id:0, state:moved, scenePos:(147,1023)) (id:1, state:pressed, scenePos:(327,792))
555 //[TouchRegistry] got TouchUpdate (id:0, state:moved, scenePos:(147,1023)) (id:1, state:pressed, scenePos:(327,792))
551 {
556 {
@@ -559,24 +564,24 @@ void tst_TouchRegistry::candidatesAndWatchers_2()
559 Qt::NoModifier,
564 Qt::NoModifier,
560 Qt::TouchPointPressed | Qt::TouchPointMoved,
565 Qt::TouchPointPressed | Qt::TouchPointMoved,
561 touchPoints);
566 touchPoints);
562− touchRegistry.update(&touchEvent);
567+ touchRegistry->update(&touchEvent);
563 }
568 }
564
569
565 // [TouchRegistry] addTouchWatcher id 1 watcher DirectionalDragArea
570 // [TouchRegistry] addTouchWatcher id 1 watcher DirectionalDragArea
566− touchRegistry.addTouchWatcher(1, &directionalDragArea);
571+ touchRegistry->addTouchWatcher(1, &directionalDragArea);
567
572
568 // [TouchRegistry] removeCandidateOwnerForTouch id 0 candidate DirectionalDragArea
573 // [TouchRegistry] removeCandidateOwnerForTouch id 0 candidate DirectionalDragArea
569− touchRegistry.removeCandidateOwnerForTouch(0, &directionalDragArea);
574+ touchRegistry->removeCandidateOwnerForTouch(0, &directionalDragArea);
570
575
571 //[TouchRegistry] sending TouchWonershipEvent(id = 0 gained) to candidate TouchGate
576 //[TouchRegistry] sending TouchWonershipEvent(id = 0 gained) to candidate TouchGate
572 QCOMPARE(touchGate.ownedTouches.count(), 1);
577 QCOMPARE(touchGate.ownedTouches.count(), 1);
573 QVERIFY(touchGate.ownedTouches.contains(0));
578 QVERIFY(touchGate.ownedTouches.contains(0));
574
579
575 // [TouchRegistry] addTouchWatcher id 0 watcher DirectionalDragArea
580 // [TouchRegistry] addTouchWatcher id 0 watcher DirectionalDragArea
576− touchRegistry.addTouchWatcher(0, &directionalDragArea);
581+ touchRegistry->addTouchWatcher(0, &directionalDragArea);
577
582
578 // [TouchRegistry] requestTouchOwnership id 1 candidate TouchGate
583 // [TouchRegistry] requestTouchOwnership id 1 candidate TouchGate
579− touchRegistry.requestTouchOwnership(1, &touchGate);
584+ touchRegistry->requestTouchOwnership(1, &touchGate);
580
585
581 //[TouchRegistry] sending TouchWonershipEvent(id = 1 gained) to candidate TouchGate
586 //[TouchRegistry] sending TouchWonershipEvent(id = 1 gained) to candidate TouchGate
582 QCOMPARE(touchGate.ownedTouches.count(), 2);
587 QCOMPARE(touchGate.ownedTouches.count(), 2);
@@ -597,7 +602,7 @@ void tst_TouchRegistry::candidatesAndWatchers_2()
597 Qt::NoModifier,
602 Qt::NoModifier,
598 Qt::TouchPointMoved,
603 Qt::TouchPointMoved,
599 touchPoints);
604 touchPoints);
600− touchRegistry.update(&touchEvent);
605+ touchRegistry->update(&touchEvent);
601 }
606 }
602
607
603 //vvvvv DDA Watchers are being ignored from now on vvvvvvv
608 //vvvvv DDA Watchers are being ignored from now on vvvvvvv
@@ -623,7 +628,7 @@ void tst_TouchRegistry::candidatesAndWatchers_2()
623 Qt::NoModifier,
628 Qt::NoModifier,
624 Qt::TouchPointMoved | Qt::TouchPointReleased,
629 Qt::TouchPointMoved | Qt::TouchPointReleased,
625 touchPoints);
630 touchPoints);
626− touchRegistry.update(&touchEvent);
631+ touchRegistry->update(&touchEvent);
627 }
632 }
628
633
629 QCOMPARE(directionalDragArea.unownedTouchEvents.count(), 1);
634 QCOMPARE(directionalDragArea.unownedTouchEvents.count(), 1);
@@ -645,7 +650,7 @@ void tst_TouchRegistry::candidatesAndWatchers_2()
645 Qt::NoModifier,
650 Qt::NoModifier,
646 Qt::TouchPointReleased,
651 Qt::TouchPointReleased,
647 touchPoints);
652 touchPoints);
648− touchRegistry.update(&touchEvent);
653+ touchRegistry->update(&touchEvent);
649 }
654 }
650
655
651 QCOMPARE(directionalDragArea.unownedTouchEvents.count(), 1);
656 QCOMPARE(directionalDragArea.unownedTouchEvents.count(), 1);
@@ -657,7 +662,6 @@ void tst_TouchRegistry::candidatesAndWatchers_2()
657
662
658void tst_TouchRegistry::rejectingTouchfterItsEnd()
663void tst_TouchRegistry::rejectingTouchfterItsEnd()
659{
664{
660− TouchRegistry touchRegistry;
661 DummyCandidate earlyCandidate;
665 DummyCandidate earlyCandidate;
662 DummyCandidate lateCandidate;
666 DummyCandidate lateCandidate;
663
667
@@ -670,16 +674,16 @@ void tst_TouchRegistry::rejectingTouchfterItsEnd()
670 Qt::NoModifier,
674 Qt::NoModifier,
671 Qt::TouchPointPressed,
675 Qt::TouchPointPressed,
672 touchPoints);
676 touchPoints);
673− touchRegistry.update(&touchEvent);
677+ touchRegistry->update(&touchEvent);
674 }
678 }
675
679
676− touchRegistry.addCandidateOwnerForTouch(0, &earlyCandidate);
680+ touchRegistry->addCandidateOwnerForTouch(0, &earlyCandidate);
677− touchRegistry.addCandidateOwnerForTouch(0, &lateCandidate);
681+ touchRegistry->addCandidateOwnerForTouch(0, &lateCandidate);
678
682
679 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
683 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
680 QVERIFY(lateCandidate.ownedTouches.isEmpty());
684 QVERIFY(lateCandidate.ownedTouches.isEmpty());
681
685
682− touchRegistry.requestTouchOwnership(0, &lateCandidate);
686+ touchRegistry->requestTouchOwnership(0, &lateCandidate);
683
687
684 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
688 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
685 QVERIFY(lateCandidate.ownedTouches.isEmpty());
689 QVERIFY(lateCandidate.ownedTouches.isEmpty());
@@ -693,25 +697,25 @@ void tst_TouchRegistry::rejectingTouchfterItsEnd()
693 Qt::NoModifier,
697 Qt::NoModifier,
694 Qt::TouchPointPressed,
698 Qt::TouchPointPressed,
695 touchPoints);
699 touchPoints);
696− touchRegistry.update(&touchEvent);
700+ touchRegistry->update(&touchEvent);
697 }
701 }
698
702
699 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
703 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
700 QVERIFY(lateCandidate.ownedTouches.isEmpty());
704 QVERIFY(lateCandidate.ownedTouches.isEmpty());
701
705
702− touchRegistry.removeCandidateOwnerForTouch(0, &earlyCandidate);
706+ touchRegistry->removeCandidateOwnerForTouch(0, &earlyCandidate);
703
707
704 QCOMPARE(lateCandidate.ownedTouches.count(), 1);
708 QCOMPARE(lateCandidate.ownedTouches.count(), 1);
705 QCOMPARE(lateCandidate.ownedTouches.contains(0), 1);
709 QCOMPARE(lateCandidate.ownedTouches.contains(0), 1);
706
710
707 // Check that there's no trace left of touch 0 as we no longer need to keep tabs on it.
711 // Check that there's no trace left of touch 0 as we no longer need to keep tabs on it.
708− QVERIFY(!touchRegistry.findTouchInfo(0));
712+ QVERIFY(!touchRegistry->findTouchInfo(0));
709}
713}
710
714
711void tst_TouchRegistry::removeOldUndecidedCandidates()
715void tst_TouchRegistry::removeOldUndecidedCandidates()
712{
716{
713 FakeTimerFactory *fakeTimerFactory = new FakeTimerFactory;
717 FakeTimerFactory *fakeTimerFactory = new FakeTimerFactory;
714− TouchRegistry *touchRegistry = new TouchRegistry(nullptr, fakeTimerFactory);
718+ touchRegistry->setTimerFactory(fakeTimerFactory);
715
719
716 DummyCandidate undecidedCandidate;
720 DummyCandidate undecidedCandidate;
717 undecidedCandidate.setObjectName("undecided");
721 undecidedCandidate.setObjectName("undecided");
@@ -742,14 +746,75 @@ void tst_TouchRegistry::removeOldUndecidedCandidates()
742
746
743 // Simulate that enough time has passed to cause the CandidateInactivityTimer to timeout,
747 // Simulate that enough time has passed to cause the CandidateInactivityTimer to timeout,
744 // making TouchRegistry consider that undecidedCantidate defaulted.
748 // making TouchRegistry consider that undecidedCantidate defaulted.
745− fakeTimerFactory->makeRunningTimersTimeout();
749+ fakeTimerFactory->updateTime(10000);
746
750
747 QVERIFY(undecidedCandidate.ownedTouches.isEmpty());
751 QVERIFY(undecidedCandidate.ownedTouches.isEmpty());
748 QVERIFY(undecidedCandidate.lostTouches.contains(0));
752 QVERIFY(undecidedCandidate.lostTouches.contains(0));
749 QVERIFY(candidateThatWantsTouch.ownedTouches.contains(0));
753 QVERIFY(candidateThatWantsTouch.ownedTouches.contains(0));
750 QVERIFY(candidateThatWantsTouch.lostTouches.isEmpty());
754 QVERIFY(candidateThatWantsTouch.lostTouches.isEmpty());
755+}
751
756
757+/*
758+ An item that calls requestTouchOwnership() without first having called addCandidateOwnerForTouch()
759+ is assumed to be the interim owner of the touch point, thus there's no point in sending
760+ UnownedTouchEvents to him as he already gets proper QTouchEvents from QQuickWindow because
761+ he didn't ignore the first QTouchEvent with that touch point.
762+ */
763+void tst_TouchRegistry::interimOwnerWontGetUnownedTouchEvents()
764+{
765+ FakeTimerFactory *fakeTimerFactory = new FakeTimerFactory;
752− delete touchRegistry;
766+ touchRegistry->setTimerFactory(fakeTimerFactory);
767+
768+ DummyCandidate undecidedCandidate;
769+ undecidedCandidate.setObjectName("undecided");
770+
771+ DummyCandidate interimOwner;
772+ interimOwner.setObjectName("interimOwner");
773+
774+ {
775+ QList<QTouchEvent::TouchPoint> touchPoints;
776+ touchPoints.append(QTouchEvent::TouchPoint(0));
777+ touchPoints[0].setState(Qt::TouchPointPressed);
778+ QTouchEvent touchEvent(QEvent::TouchBegin,
779+ 0 /* device */,
780+ Qt::NoModifier,
781+ Qt::TouchPointPressed,
782+ touchPoints);
783+ touchRegistry->update(&touchEvent);
784+ }
785+
786+ touchRegistry->addCandidateOwnerForTouch(0, &undecidedCandidate);
787+ touchRegistry->requestTouchOwnership(0, &interimOwner);
788+
789+ {
790+ QList<QTouchEvent::TouchPoint> touchPoints;
791+ touchPoints.append(QTouchEvent::TouchPoint(0));
792+ touchPoints[0].setState(Qt::TouchPointMoved);
793+ QTouchEvent touchEvent(QEvent::TouchUpdate,
794+ 0 /* device */,
795+ Qt::NoModifier,
796+ Qt::TouchPointMoved,
797+ touchPoints);
798+ touchRegistry->update(&touchEvent);
799+ }
800+
801+ QCOMPARE(undecidedCandidate.unownedTouchEvents.count(), 1);
802+ QCOMPARE(interimOwner.unownedTouchEvents.count(), 0);
803+
804+ {
805+ QList<QTouchEvent::TouchPoint> touchPoints;
806+ touchPoints.append(QTouchEvent::TouchPoint(0));
807+ touchPoints[0].setState(Qt::TouchPointMoved);
808+ QTouchEvent touchEvent(QEvent::TouchUpdate,
809+ 0 /* device */,
810+ Qt::NoModifier,
811+ Qt::TouchPointMoved,
812+ touchPoints);
813+ touchRegistry->update(&touchEvent);
814+ }
815+
816+ QCOMPARE(undecidedCandidate.unownedTouchEvents.count(), 2);
817+ QCOMPARE(interimOwner.unownedTouchEvents.count(), 0);
753}
818}
754
819
755////////////// TouchMemento //////////
820////////////// TouchMemento //////////
tests/mocks/AccountsService/plugin.h+1−1
@@ -29,7 +29,7 @@ class AccountsServicePlugin : public QQmlExtensionPlugin
29 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
30
30
31public:
31public:
32− void registerTypes(const char *uri);
32+ void registerTypes(const char *uri) override;
33};
33};
34
34
35#endif
35#endif
tests/mocks/CMakeLists.txt+1
@@ -41,6 +41,7 @@ add_subdirectory(Ubuntu)
41add_subdirectory(Unity)
41add_subdirectory(Unity)
42add_subdirectory(QtMultimedia)
42add_subdirectory(QtMultimedia)
43add_subdirectory(Wizard)
43add_subdirectory(Wizard)
44+add_subdirectory(Utils)
44
45
45install(
46install(
46 DIRECTORY data
47 DIRECTORY data
tests/mocks/GSettings.1.0/fake_gsettings.h+2−2
@@ -78,10 +78,10 @@ public:
78 ~GSettingsControllerQml();
78 ~GSettingsControllerQml();
79
79
80 QString pictureUri() const;
80 QString pictureUri() const;
81− void setPictureUri(const QString &str);
81+ Q_INVOKABLE void setPictureUri(const QString &str);
82
82
83 QString usageMode() const;
83 QString usageMode() const;
84− void setUsageMode(const QString &usageMode);
84+ Q_INVOKABLE void setUsageMode(const QString &usageMode);
85
85
86Q_SIGNALS:
86Q_SIGNALS:
87 void pictureUriChanged(const QString&);
87 void pictureUriChanged(const QString&);
tests/mocks/GSettings.1.0/plugin.cpp+6
@@ -19,8 +19,14 @@
19
19
20#include <QtQml/qqml.h>
20#include <QtQml/qqml.h>
21
21
22+static QObject* controllerProvider(QQmlEngine* /* engine */, QJSEngine* /* scriptEngine */)
23+{
24+ return GSettingsControllerQml::instance();
25+}
26+
22void FakeGSettingsQmlPlugin::registerTypes(const char *uri)
27void FakeGSettingsQmlPlugin::registerTypes(const char *uri)
23{
28{
29+ qmlRegisterSingletonType<GSettingsControllerQml>(uri, 1, 0, "GSettingsController", controllerProvider);
24 qmlRegisterType<GSettingsQml>(uri, 1, 0, "GSettings");
30 qmlRegisterType<GSettingsQml>(uri, 1, 0, "GSettings");
25 qmlRegisterUncreatableType<GSettingsSchemaQml>(uri, 1, 0, "GSettingsSchema",
31 qmlRegisterUncreatableType<GSettingsSchemaQml>(uri, 1, 0, "GSettingsSchema",
26 "GSettingsSchema can only be used inside of a GSettings component");
32 "GSettingsSchema can only be used inside of a GSettings component");
tests/mocks/GSettings.1.0/plugin.h+1−1
@@ -25,7 +25,7 @@ class FakeGSettingsQmlPlugin : public QQmlExtensionPlugin
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26
26
27public:
27public:
28− void registerTypes(const char *uri);
28+ void registerTypes(const char *uri) override;
29};
29};
30
30
31#endif // PLUGIN_H
31#endif // PLUGIN_H
tests/mocks/LightDM/liblightdm/UsersModel.h+2−2
@@ -53,8 +53,8 @@ public:
53 BackgroundPathRole
53 BackgroundPathRole
54 };
54 };
55
55
56− int rowCount(const QModelIndex &parent) const;
56+ int rowCount(const QModelIndex &parent) const override;
57− QVariant data(const QModelIndex &index, int role) const;
57+ QVariant data(const QModelIndex &index, int role) const override;
58
58
59 QString mockMode() const;
59 QString mockMode() const;
60 void setMockMode(QString mockMode);
60 void setMockMode(QString mockMode);
tests/mocks/LightDM/plugin.h+1−1
@@ -26,7 +26,7 @@ class LightDMPlugin : public QQmlExtensionPlugin
26 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27
27
28public:
28public:
29− void registerTypes(const char *uri);
29+ void registerTypes(const char *uri) override;
30};
30};
31
31
32#endif
32#endif
tests/mocks/Lights/plugin.h+1−1
@@ -26,7 +26,7 @@ class PowerdPlugin : public QQmlExtensionPlugin
26 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27
27
28public:
28public:
29− void registerTypes(const char *uri);
29+ void registerTypes(const char *uri) override;
30};
30};
31
31
32#endif
32#endif
tests/mocks/MeeGo/QOfono/plugin.h+1−1
@@ -25,7 +25,7 @@ class BackendPlugin : public QQmlExtensionPlugin
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26
26
27public:
27public:
28− void registerTypes(const char *uri);
28+ void registerTypes(const char *uri) override;
29};
29};
30
30
31#endif // MOCK_QOFONO_PLUGIN_H
31#endif // MOCK_QOFONO_PLUGIN_H
tests/mocks/Powerd/plugin.h+1−1
@@ -28,7 +28,7 @@ class PowerdPlugin : public QQmlExtensionPlugin
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29
29
30public:
30public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
32};
32};
33
33
34#endif
34#endif
tests/mocks/QMenuModel/plugin.h+1−1
@@ -28,7 +28,7 @@ class QMenuModelPlugin : public QQmlExtensionPlugin
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29
29
30public:
30public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
32};
32};
33
33
34#endif
34#endif
tests/mocks/QMenuModel/unitymenumodel.h+6−6
@@ -61,12 +61,12 @@ public:
61
61
62 QString nameOwner() const;
62 QString nameOwner() const;
63
63
64− int rowCount(const QModelIndex &parent = QModelIndex()) const;
64+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
65− int columnCount(const QModelIndex &parent = QModelIndex()) const;
65+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
66− QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
66+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
67− QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const;
67+ QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override;
68− QModelIndex parent(const QModelIndex &index) const;
68+ QModelIndex parent(const QModelIndex &index) const override;
69− QHash<int, QByteArray> roleNames() const;
69+ QHash<int, QByteArray> roleNames() const override;
70
70
71 Q_INVOKABLE QObject * submenu(int position, QQmlComponent* actionStateParser = nullptr);
71 Q_INVOKABLE QObject * submenu(int position, QQmlComponent* actionStateParser = nullptr);
72 Q_INVOKABLE bool loadExtendedAttributes(int position, const QVariantMap &schema);
72 Q_INVOKABLE bool loadExtendedAttributes(int position, const QVariantMap &schema);
tests/mocks/QtMultimedia/plugin.h+1−1
@@ -28,7 +28,7 @@ class MockQtMultimediaPlugin : public QQmlExtensionPlugin
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29
29
30public:
30public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
32};
32};
33
33
34#endif
34#endif
tests/mocks/Ubuntu/Connectivity/plugin.h+2−2
@@ -28,8 +28,8 @@ class QmlConnectivityNetworkingPlugin : public QQmlExtensionPlugin {
28 Q_INTERFACES(QQmlExtensionInterface)
28 Q_INTERFACES(QQmlExtensionInterface)
29
29
30public:
30public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
32− void initializeEngine(QQmlEngine *engine, const char *uri);
32+ void initializeEngine(QQmlEngine *engine, const char *uri) override;
33};
33};
34
34
35#endif // QML_CONNECTIVITY_PLUGIN_H
35#endif // QML_CONNECTIVITY_PLUGIN_H
tests/mocks/Ubuntu/DownloadDaemonListener/plugin.h+1−1
@@ -24,7 +24,7 @@ class FakeUbuntuDownloadDaemonListenerQmlPlugin : public QQmlExtensionPlugin
24 Q_OBJECT
24 Q_OBJECT
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26public:
26public:
27− void registerTypes(const char *uri);
27+ void registerTypes(const char *uri) override;
28};
28};
29
29
30#endif
30#endif
tests/mocks/Ubuntu/Payments/plugin.h+1−1
@@ -25,7 +25,7 @@ class BackendPlugin : public QQmlExtensionPlugin
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26
26
27public:
27public:
28− void registerTypes(const char *uri);
28+ void registerTypes(const char *uri) override;
29};
29};
30
30
31#endif // PAYMENTS_PLUGIN_H
31#endif // PAYMENTS_PLUGIN_H
tests/mocks/Ubuntu/SystemImage/plugin.h+1−1
@@ -25,7 +25,7 @@ class BackendPlugin : public QQmlExtensionPlugin
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26
26
27public:
27public:
28− void registerTypes(const char *uri);
28+ void registerTypes(const char *uri) override;
29};
29};
30
30
31#endif // MOCK_SYSTEMIMAGE_PLUGIN_H
31#endif // MOCK_SYSTEMIMAGE_PLUGIN_H
tests/mocks/Ubuntu/SystemSettings/SecurityPrivacy/plugin.h+1−1
@@ -25,7 +25,7 @@ class BackendPlugin : public QQmlExtensionPlugin
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26
26
27public:
27public:
28− void registerTypes(const char *uri);
28+ void registerTypes(const char *uri) override;
29};
29};
30
30
31#endif // MOCK_SECURITYPRIVACY_PLUGIN_H
31#endif // MOCK_SECURITYPRIVACY_PLUGIN_H
tests/mocks/Ubuntu/Telephony/MockCallEntry.h+1−1
@@ -53,7 +53,7 @@ Q_SIGNALS:
53 void elapsedTimerRunningChanged();
53 void elapsedTimerRunningChanged();
54
54
55protected:
55protected:
56− void timerEvent(QTimerEvent * event);
56+ void timerEvent(QTimerEvent * event) override;
57
57
58private:
58private:
59 QString m_phoneNumber;
59 QString m_phoneNumber;
tests/mocks/Ubuntu/Telephony/plugin.h+2−2
@@ -27,8 +27,8 @@ class FakeUbuntuTelephonyQmlPlugin : public QQmlExtensionPlugin
27 Q_OBJECT
27 Q_OBJECT
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29public:
29public:
30− void initializeEngine(QQmlEngine *engine, const char *uri);
30+ void initializeEngine(QQmlEngine *engine, const char *uri) override;
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
32
32
33private:
33private:
34 QQmlContext *mRootContext;
34 QQmlContext *mRootContext;
tests/mocks/Unity/Application/ApplicationInfo.cpp+43−2
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -34,6 +34,11 @@ ApplicationInfo::ApplicationInfo(const QString &appId, QObject *parent)
34 , m_focused(false)
34 , m_focused(false)
35 , m_fullscreen(false)
35 , m_fullscreen(false)
36 , m_session(0)
36 , m_session(0)
37+ , m_supportedOrientations(Qt::PortraitOrientation |
38+ Qt::LandscapeOrientation |
39+ Qt::InvertedPortraitOrientation |
40+ Qt::InvertedLandscapeOrientation)
41+ , m_rotatesWindowContents(false)
37 , m_manualSurfaceCreation(false)
42 , m_manualSurfaceCreation(false)
38{
43{
39}
44}
@@ -45,6 +50,11 @@ ApplicationInfo::ApplicationInfo(QObject *parent)
45 , m_focused(false)
50 , m_focused(false)
46 , m_fullscreen(false)
51 , m_fullscreen(false)
47 , m_session(0)
52 , m_session(0)
53+ , m_supportedOrientations(Qt::PortraitOrientation |
54+ Qt::LandscapeOrientation |
55+ Qt::InvertedPortraitOrientation |
56+ Qt::InvertedLandscapeOrientation)
57+ , m_rotatesWindowContents(false)
48 , m_manualSurfaceCreation(false)
58 , m_manualSurfaceCreation(false)
49{
59{
50}
60}
@@ -99,9 +109,17 @@ void ApplicationInfo::setIconId(const QString &iconId)
99
109
100void ApplicationInfo::setScreenshotId(const QString &screenshotId)
110void ApplicationInfo::setScreenshotId(const QString &screenshotId)
101{
111{
112+ QString screenshotFileName;
113+
114+ if (screenshotId.endsWith(".svg")) {
115+ screenshotFileName = QString("%1/Dash/graphics/phone/screenshots/%2")
116+ .arg(qmlDirectory())
117+ .arg(screenshotId);
118+ } else {
102− QString screenshotFileName = QString("%1/Dash/graphics/phone/screenshots/%2@12.png")
119+ screenshotFileName = QString("%1/Dash/graphics/phone/screenshots/%2@12.png")
103 .arg(qmlDirectory())
120 .arg(qmlDirectory())
104 .arg(screenshotId);
121 .arg(screenshotId);
122+ }
105
123
106 if (screenshotFileName != m_screenshotFileName) {
124 if (screenshotFileName != m_screenshotFileName) {
107 m_screenshotFileName = screenshotFileName;
125 m_screenshotFileName = screenshotFileName;
@@ -145,6 +163,9 @@ void ApplicationInfo::setState(State value)
145
163
146 if (!m_manualSurfaceCreation && m_state == ApplicationInfo::Running) {
164 if (!m_manualSurfaceCreation && m_state == ApplicationInfo::Running) {
147 QTimer::singleShot(500, this, SLOT(createSession()));
165 QTimer::singleShot(500, this, SLOT(createSession()));
166+ } else if (m_state == ApplicationInfo::Stopped) {
167+ delete m_session;
168+ m_session = nullptr;
148 }
169 }
149 }
170 }
150}
171}
@@ -172,3 +193,23 @@ void ApplicationInfo::setManualSurfaceCreation(bool value)
172 Q_EMIT manualSurfaceCreationChanged(value);
193 Q_EMIT manualSurfaceCreationChanged(value);
173 }
194 }
174}
195}
196+
197+Qt::ScreenOrientations ApplicationInfo::supportedOrientations() const
198+{
199+ return m_supportedOrientations;
200+}
201+
202+void ApplicationInfo::setSupportedOrientations(Qt::ScreenOrientations orientations)
203+{
204+ m_supportedOrientations = orientations;
205+}
206+
207+bool ApplicationInfo::rotatesWindowContents() const
208+{
209+ return m_rotatesWindowContents;
210+}
211+
212+void ApplicationInfo::setRotatesWindowContents(bool value)
213+{
214+ m_rotatesWindowContents = value;
215+}
tests/mocks/Unity/Application/ApplicationInfo.h+9−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -80,6 +80,12 @@ public:
80 void setFullscreen(bool value);
80 void setFullscreen(bool value);
81 bool fullscreen() const { return m_fullscreen; }
81 bool fullscreen() const { return m_fullscreen; }
82
82
83+ Qt::ScreenOrientations supportedOrientations() const override;
84+ void setSupportedOrientations(Qt::ScreenOrientations orientations);
85+
86+ bool rotatesWindowContents() const override;
87+ void setRotatesWindowContents(bool value);
88+
83 bool manualSurfaceCreation() const { return m_manualSurfaceCreation; }
89 bool manualSurfaceCreation() const { return m_manualSurfaceCreation; }
84 void setManualSurfaceCreation(bool value);
90 void setManualSurfaceCreation(bool value);
85
91
@@ -108,6 +114,8 @@ private:
108 bool m_focused;
114 bool m_focused;
109 bool m_fullscreen;
115 bool m_fullscreen;
110 Session* m_session;
116 Session* m_session;
117+ Qt::ScreenOrientations m_supportedOrientations;
118+ bool m_rotatesWindowContents;
111
119
112 bool m_manualSurfaceCreation;
120 bool m_manualSurfaceCreation;
113};
121};
tests/mocks/Unity/Application/ApplicationManager.cpp+40−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -143,7 +143,7 @@ QModelIndex ApplicationManager::findIndex(ApplicationInfo* application)
143}
143}
144
144
145void ApplicationManager::add(ApplicationInfo *application) {
145void ApplicationManager::add(ApplicationInfo *application) {
146− if (!application) {
146+ if (!application || m_runningApplications.contains(application)) {
147 return;
147 return;
148 }
148 }
149
149
@@ -348,6 +348,13 @@ void ApplicationManager::unfocusCurrentApplication()
348
348
349void ApplicationManager::buildListOfAvailableApplications()
349void ApplicationManager::buildListOfAvailableApplications()
350{
350{
351+ /*
352+ ATTENTION!
353+ Be careful when changing application properties here as some qmltests
354+ rely on them being the way it's specified here (e.g. that camera-app
355+ is fullscreen, that twitter-webapp can rotate in all directions, etc)
356+ */
357+
351 ApplicationInfo *application;
358 ApplicationInfo *application;
352
359
353 application = new ApplicationInfo(this);
360 application = new ApplicationInfo(this);
@@ -355,6 +362,7 @@ void ApplicationManager::buildListOfAvailableApplications()
355 application->setName("Unity 8 Mock Dash");
362 application->setName("Unity 8 Mock Dash");
356 application->setScreenshotId("unity8-dash");
363 application->setScreenshotId("unity8-dash");
357 application->setStage(ApplicationInfo::MainStage);
364 application->setStage(ApplicationInfo::MainStage);
365+ application->setSupportedOrientations(Qt::PrimaryOrientation);
358 m_availableApplications.append(application);
366 m_availableApplications.append(application);
359
367
360 application = new ApplicationInfo(this);
368 application = new ApplicationInfo(this);
@@ -363,6 +371,8 @@ void ApplicationManager::buildListOfAvailableApplications()
363 application->setScreenshotId("dialer");
371 application->setScreenshotId("dialer");
364 application->setIconId("dialer-app");
372 application->setIconId("dialer-app");
365 application->setStage(ApplicationInfo::SideStage);
373 application->setStage(ApplicationInfo::SideStage);
374+ application->setSupportedOrientations(Qt::PortraitOrientation
375+ | Qt::InvertedPortraitOrientation);
366 m_availableApplications.append(application);
376 m_availableApplications.append(application);
367
377
368 application = new ApplicationInfo(this);
378 application = new ApplicationInfo(this);
@@ -371,6 +381,11 @@ void ApplicationManager::buildListOfAvailableApplications()
371 application->setScreenshotId("camera");
381 application->setScreenshotId("camera");
372 application->setIconId("camera");
382 application->setIconId("camera");
373 application->setFullscreen(true);
383 application->setFullscreen(true);
384+ application->setSupportedOrientations(Qt::PortraitOrientation
385+ | Qt::LandscapeOrientation
386+ | Qt::InvertedPortraitOrientation
387+ | Qt::InvertedLandscapeOrientation);
388+ application->setRotatesWindowContents(true);
374 m_availableApplications.append(application);
389 m_availableApplications.append(application);
375
390
376 application = new ApplicationInfo(this);
391 application = new ApplicationInfo(this);
@@ -416,13 +431,35 @@ void ApplicationManager::buildListOfAvailableApplications()
416 application->setAppId("gmail-webapp");
431 application->setAppId("gmail-webapp");
417 application->setName("GMail");
432 application->setName("GMail");
418 application->setIconId("gmail");
433 application->setIconId("gmail");
434+ application->setScreenshotId("gmail-webapp.svg");
435+ application->setFullscreen(false);
436+ application->setStage(ApplicationInfo::MainStage);
437+ application->setSupportedOrientations(Qt::PortraitOrientation
438+ | Qt::LandscapeOrientation
439+ | Qt::InvertedPortraitOrientation
440+ | Qt::InvertedLandscapeOrientation);
441+ m_availableApplications.append(application);
442+
443+ application = new ApplicationInfo(this);
444+ application->setAppId("music-app");
445+ application->setName("Music");
446+ application->setIconId("soundcloud");
447+ application->setScreenshotId("music");
448+ application->setFullscreen(false);
449+ application->setStage(ApplicationInfo::MainStage);
450+ application->setSupportedOrientations(Qt::PortraitOrientation
451+ | Qt::LandscapeOrientation
452+ | Qt::InvertedPortraitOrientation
453+ | Qt::InvertedLandscapeOrientation);
419 m_availableApplications.append(application);
454 m_availableApplications.append(application);
420
455
421 application = new ApplicationInfo(this);
456 application = new ApplicationInfo(this);
422 application->setAppId("ubuntu-weather-app");
457 application->setAppId("ubuntu-weather-app");
423 application->setName("Weather");
458 application->setName("Weather");
424 application->setIconId("weather");
459 application->setIconId("weather");
425− application->setStage(ApplicationInfo::SideStage);
460+ application->setScreenshotId("ubuntu-weather-app.svg");
461+ application->setSupportedOrientations(Qt::LandscapeOrientation
462+ | Qt::InvertedLandscapeOrientation);
426 m_availableApplications.append(application);
463 m_availableApplications.append(application);
427
464
428 application = new ApplicationInfo(this);
465 application = new ApplicationInfo(this);
tests/mocks/Unity/Application/CMakeLists.txt+1−2
@@ -1,5 +1,3 @@
1−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
2−
3set(FakeUnityApplicationQml_SOURCES
1set(FakeUnityApplicationQml_SOURCES
4 plugin.cpp
2 plugin.cpp
5 ApplicationInfo.cpp
3 ApplicationInfo.cpp
@@ -14,6 +12,7 @@ set(FakeUnityApplicationQml_SOURCES
14 SurfaceManager.cpp
12 SurfaceManager.cpp
15 SessionModel.h
13 SessionModel.h
16 UbuntuKeyboardInfo.cpp
14 UbuntuKeyboardInfo.cpp
15+ VirtualKeyboard.cpp
17 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
16 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
18 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
17 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
19)
18)
tests/mocks/Unity/Application/MirSurfaceItem.cpp+10−27
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -39,14 +39,12 @@ MirSurfaceItem::MirSurfaceItem(const QString& name,
39 , m_type(type)
39 , m_type(type)
40 , m_state(state)
40 , m_state(state)
41 , m_live(true)
41 , m_live(true)
42− , m_orientation(Qt::PortraitOrientation)
42+ , m_orientationAngle(Angle0)
43− , m_touchPressCount(0)
44− , m_touchReleaseCount(0)
45 , m_qmlItem(nullptr)
43 , m_qmlItem(nullptr)
46 , m_screenshotUrl(screenshot)
44 , m_screenshotUrl(screenshot)
45+ , m_touchPressCount(0)
46+ , m_touchReleaseCount(0)
47{
47{
48− qDebug() << "MirSurfaceItem::MirSurfaceItem() " << this->name();
49−
50 setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton |
48 setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton |
51 Qt::ExtraButton1 | Qt::ExtraButton2 | Qt::ExtraButton3 | Qt::ExtraButton4 |
49 Qt::ExtraButton1 | Qt::ExtraButton2 | Qt::ExtraButton3 | Qt::ExtraButton4 |
52 Qt::ExtraButton5 | Qt::ExtraButton6 | Qt::ExtraButton7 | Qt::ExtraButton8 |
50 Qt::ExtraButton5 | Qt::ExtraButton6 | Qt::ExtraButton7 | Qt::ExtraButton8 |
@@ -55,9 +53,6 @@ MirSurfaceItem::MirSurfaceItem(const QString& name,
55
53
56 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
54 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
57
55
58− connect(this, &QQuickItem::focusChanged,
59− this, &MirSurfaceItem::onFocusChanged);
60−
61 // The assumptions I make here really should hold.
56 // The assumptions I make here really should hold.
62 QQuickView *quickView =
57 QQuickView *quickView =
63 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
58 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
@@ -117,17 +112,17 @@ void MirSurfaceItem::release()
117 }
112 }
118}
113}
119
114
120−void MirSurfaceItem::setOrientation(const Qt::ScreenOrientation orientation)
115+void MirSurfaceItem::setOrientationAngle(OrientationAngle angle)
121{
116{
122− if (m_orientation == orientation)
117+ if (m_orientationAngle == angle)
123 return;
118 return;
124
119
125− m_orientation = orientation;
120+ m_orientationAngle = angle;
126
121
127− QQmlProperty orientationProp(m_qmlItem, "orientation");
122+ QQmlProperty orientationProp(m_qmlItem, "orientationAngle");
128− orientationProp.write(QVariant::fromValue(orientation));
123+ orientationProp.write(QVariant::fromValue(m_orientationAngle));
129
124
130− Q_EMIT orientationChanged();
125+ Q_EMIT orientationAngleChanged(m_orientationAngle);
131}
126}
132
127
133void MirSurfaceItem::setSession(Session* session)
128void MirSurfaceItem::setSession(Session* session)
@@ -152,15 +147,6 @@ void MirSurfaceItem::setLive(bool live)
152 }
147 }
153}
148}
154
149
155−void MirSurfaceItem::onFocusChanged()
156−{
157− if (!hasFocus()) {
158− // Causes a crash in tst_Shell.qml, inside the mock Unity.Application itself.
159− // Didn't have time to debug yet.
160− //Q_EMIT inputMethodDismissed();
161− }
162−}
163−
164void MirSurfaceItem::setState(MirSurfaceItem::State newState)
150void MirSurfaceItem::setState(MirSurfaceItem::State newState)
165{
151{
166 if (newState != m_state) {
152 if (newState != m_state) {
@@ -197,9 +183,6 @@ void MirSurfaceItem::touchEvent(QTouchEvent * event)
197 if (event->touchPointStates() & Qt::TouchPointPressed) {
183 if (event->touchPointStates() & Qt::TouchPointPressed) {
198 ++m_touchPressCount;
184 ++m_touchPressCount;
199 Q_EMIT touchPressCountChanged(m_touchPressCount);
185 Q_EMIT touchPressCountChanged(m_touchPressCount);
200− // Causes a crash in tst_Shell.qml, inside the mock Unity.Application itself.
201− // Didn't have time to debug yet.
202− // Q_EMIT inputMethodRequested();
203 } else if (event->touchPointStates() & Qt::TouchPointReleased) {
186 } else if (event->touchPointStates() & Qt::TouchPointReleased) {
204 ++m_touchReleaseCount;
187 ++m_touchReleaseCount;
205 Q_EMIT touchReleaseCountChanged(m_touchReleaseCount);
188 Q_EMIT touchReleaseCountChanged(m_touchReleaseCount);
tests/mocks/Unity/Application/MirSurfaceItem.h+29−29
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -29,17 +29,18 @@ class MirSurfaceItem : public QQuickItem
29 Q_OBJECT
29 Q_OBJECT
30 Q_ENUMS(Type)
30 Q_ENUMS(Type)
31 Q_ENUMS(State)
31 Q_ENUMS(State)
32+ Q_ENUMS(OrientationAngle)
32
33
33 Q_PROPERTY(Type type READ type NOTIFY typeChanged)
34 Q_PROPERTY(Type type READ type NOTIFY typeChanged)
34 Q_PROPERTY(State state READ state NOTIFY stateChanged)
35 Q_PROPERTY(State state READ state NOTIFY stateChanged)
35 Q_PROPERTY(QString name READ name CONSTANT)
36 Q_PROPERTY(QString name READ name CONSTANT)
36 Q_PROPERTY(bool live READ live NOTIFY liveChanged)
37 Q_PROPERTY(bool live READ live NOTIFY liveChanged)
37− Q_PROPERTY(Qt::ScreenOrientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged DESIGNABLE false)
38+ Q_PROPERTY(OrientationAngle orientationAngle READ orientationAngle WRITE setOrientationAngle
38−
39+ NOTIFY orientationAngleChanged DESIGNABLE false)
39− Q_PROPERTY(int touchPressCount READ touchPressCount WRITE setTouchPressCount NOTIFY touchPressCountChanged
40+ Q_PROPERTY(int touchPressCount READ touchPressCount WRITE setTouchPressCount
40− DESIGNABLE false)
41+ NOTIFY touchPressCountChanged DESIGNABLE false)
41− Q_PROPERTY(int touchReleaseCount READ touchReleaseCount WRITE setTouchReleaseCount NOTIFY touchReleaseCountChanged
42+ Q_PROPERTY(int touchReleaseCount READ touchReleaseCount WRITE setTouchReleaseCount
42− DESIGNABLE false)
43+ NOTIFY touchReleaseCountChanged DESIGNABLE false)
43
44
44public:
45public:
45 enum Type {
46 enum Type {
@@ -62,6 +63,13 @@ public:
62 Fullscreen,
63 Fullscreen,
63 };
64 };
64
65
66+ enum OrientationAngle {
67+ Angle0 = 0,
68+ Angle90 = 90,
69+ Angle180 = 180,
70+ Angle270 = 270
71+ };
72+
65 ~MirSurfaceItem();
73 ~MirSurfaceItem();
66
74
67 //getters
75 //getters
@@ -70,9 +78,9 @@ public:
70 State state() const { return m_state; }
78 State state() const { return m_state; }
71 QString name() const { return m_name; }
79 QString name() const { return m_name; }
72 bool live() const { return m_live; }
80 bool live() const { return m_live; }
73− Qt::ScreenOrientation orientation() const { return m_orientation; }
81+ OrientationAngle orientationAngle() const { return m_orientationAngle; }
74
82
75− void setOrientation(const Qt::ScreenOrientation orientation);
83+ void setOrientationAngle(OrientationAngle angle);
76
84
77 void setSession(Session* item);
85 void setSession(Session* item);
78 void setScreenshot(const QUrl& screenshot);
86 void setScreenshot(const QUrl& screenshot);
@@ -91,29 +99,14 @@ Q_SIGNALS:
91 void typeChanged(Type);
99 void typeChanged(Type);
92 void stateChanged(State);
100 void stateChanged(State);
93 void liveChanged(bool live);
101 void liveChanged(bool live);
94− void orientationChanged();
102+ void orientationAngleChanged(OrientationAngle angle);
95 void touchPressCountChanged(int count);
103 void touchPressCountChanged(int count);
96 void touchReleaseCountChanged(int count);
104 void touchReleaseCountChanged(int count);
97
105
98− void inputMethodRequested();
99− void inputMethodDismissed();
100−
101 // internal mock use
106 // internal mock use
102 void deregister();
107 void deregister();
103
108
104protected:
109protected:
105− void touchEvent(QTouchEvent * event) override;
106−
107− // becaue the default implementation ignores the events
108− void mousePressEvent(QMouseEvent *) override {}
109− void mouseMoveEvent(QMouseEvent *) override {}
110− void mouseReleaseEvent(QMouseEvent *) override {}
111−
112−private Q_SLOTS:
113− void onFocusChanged();
114− void onComponentStatusChanged(QQmlComponent::Status status);
115−
116−private:
117 explicit MirSurfaceItem(const QString& name,
110 explicit MirSurfaceItem(const QString& name,
118 Type type,
111 Type type,
119 State state,
112 State state,
@@ -121,6 +114,12 @@ private:
121 const QString &qmlFilePath = QString(),
114 const QString &qmlFilePath = QString(),
122 QQuickItem *parent = 0);
115 QQuickItem *parent = 0);
123
116
117+ void touchEvent(QTouchEvent * event) override;
118+
119+private Q_SLOTS:
120+ void onComponentStatusChanged(QQmlComponent::Status status);
121+
122+private:
124 void createQmlContentItem();
123 void createQmlContentItem();
125 void printComponentErrors();
124 void printComponentErrors();
126
125
@@ -129,19 +128,20 @@ private:
129 const Type m_type;
128 const Type m_type;
130 State m_state;
129 State m_state;
131 bool m_live;
130 bool m_live;
132− Qt::ScreenOrientation m_orientation;
131+ OrientationAngle m_orientationAngle;
133− int m_touchPressCount;
134− int m_touchReleaseCount;
135
132
136 QQmlComponent *m_qmlContentComponent;
133 QQmlComponent *m_qmlContentComponent;
137 QQuickItem *m_qmlItem;
134 QQuickItem *m_qmlItem;
138 QUrl m_screenshotUrl;
135 QUrl m_screenshotUrl;
139
136
137+ int m_touchPressCount;
138+ int m_touchReleaseCount;
139+
140 friend class SurfaceManager;
140 friend class SurfaceManager;
141};
141};
142
142
143Q_DECLARE_METATYPE(MirSurfaceItem*)
143Q_DECLARE_METATYPE(MirSurfaceItem*)
144Q_DECLARE_METATYPE(QList<MirSurfaceItem*>)
144Q_DECLARE_METATYPE(QList<MirSurfaceItem*>)
145−Q_DECLARE_METATYPE(Qt::ScreenOrientation)
145+Q_DECLARE_METATYPE(MirSurfaceItem::OrientationAngle)
146
146
147#endif // MIRSURFACEITEM_H
147#endif // MIRSURFACEITEM_H
tests/mocks/Unity/Application/MirSurfaceItem.qml+12−14
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2014-2015 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
5 * it under the terms of the GNU Lesser General Public License as published by
@@ -23,19 +23,11 @@ Rectangle {
23 implicitWidth: units.gu(40)
23 implicitWidth: units.gu(40)
24 implicitHeight: units.gu(70)
24 implicitHeight: units.gu(70)
25
25
26− rotation: {
26+ width: parent ? parent.width : implicitWidth
27− if (orientation == Qt.PortraitOrientation) return 0;
28− else if (orientation == Qt.LandscapeOrientation) return 90;
29− else if (orientation == Qt.InvertedPortraitOrientation) return 180;
30− else return 270;
31− }
32− x: parent ? (parent.width - width) / 2 : 0
33− y: parent ? (parent.height - height) / 2 : 0
34− width: parent ? (rotation == 0 || rotation == 180 ? parent.width : parent.height) : implicitWidth
35− height: parent ? (rotation == 0 || rotation == 180 ? parent.height : parent.width) : implicitHeight
27+ height: parent ? parent.height : implicitHeight
36
28
37 property alias screenshotSource: screenshotImage.source
29 property alias screenshotSource: screenshotImage.source
38− property int orientation: Qt.PortraitOrientation
30+ property int orientationAngle
39
31
40 Image {
32 Image {
41 id: screenshotImage
33 id: screenshotImage
@@ -50,6 +42,7 @@ Rectangle {
50 fontSizeMode: Text.Fit
42 fontSizeMode: Text.Fit
51 minimumPixelSize: 10
43 minimumPixelSize: 10
52 verticalAlignment: Text.AlignVCenter
44 verticalAlignment: Text.AlignVCenter
45+ rotation: surfaceText.rotation
53 x: surfaceText.x
46 x: surfaceText.x
54 y: surfaceText.y
47 y: surfaceText.y
55 width: surfaceText.width
48 width: surfaceText.width
@@ -59,12 +52,17 @@ Rectangle {
59 }
52 }
60 Text {
53 Text {
61 id: surfaceText
54 id: surfaceText
62− anchors.fill: parent
63− text: "SURFACE"
55+ text: "SURFACE " + root.width + "," + root.height
64 color: root.parent && root.parent.activeFocus ? "yellow" : "blue"
56 color: root.parent && root.parent.activeFocus ? "yellow" : "blue"
65 font.bold: true
57 font.bold: true
66 fontSizeMode: Text.Fit
58 fontSizeMode: Text.Fit
67 minimumPixelSize: 10; font.pixelSize: 200
59 minimumPixelSize: 10; font.pixelSize: 200
68 verticalAlignment: Text.AlignVCenter
60 verticalAlignment: Text.AlignVCenter
61+
62+ rotation: root.orientationAngle
63+ x: (parent.width - width) / 2
64+ y: (parent.height - height) / 2
65+ width: (rotation == 0 || rotation == 180 ? parent.width : parent.height)
66+ height:(rotation == 0 || rotation == 180 ? parent.height : parent.width)
69 }
67 }
70}
68}
tests/mocks/Unity/Application/ObjectListModel.h+1−1
@@ -76,7 +76,7 @@ public:
76 return QVariant();
76 return QVariant();
77 }
77 }
78
78
79− QHash<int, QByteArray> roleNames() const
79+ QHash<int, QByteArray> roleNames() const override
80 {
80 {
81 QHash<int, QByteArray> roleNames;
81 QHash<int, QByteArray> roleNames;
82 roleNames.insert(RoleModelData, "modelData");
82 roleNames.insert(RoleModelData, "modelData");
tests/mocks/Unity/Application/SurfaceManager.cpp+4−34
@@ -16,8 +16,9 @@
16
16
17#include "SurfaceManager.h"
17#include "SurfaceManager.h"
18
18
19+#include "VirtualKeyboard.h"
20+
19#include <paths.h>
21#include <paths.h>
20−#include "MirSurfaceItem.h"
21
22
22SurfaceManager *SurfaceManager::the_surface_manager = nullptr;
23SurfaceManager *SurfaceManager::the_surface_manager = nullptr;
23
24
@@ -40,10 +41,7 @@ MirSurfaceItem *SurfaceManager::createSurface(const QString& name,
40 MirSurfaceItem::State state,
41 MirSurfaceItem::State state,
41 const QUrl& screenshot)
42 const QUrl& screenshot)
42{
43{
43− MirSurfaceItem* surface = new MirSurfaceItem(name,
44+ MirSurfaceItem* surface = new MirSurfaceItem(name, type, state, screenshot);
44− type,
45− state,
46− screenshot);
47 Q_EMIT surfaceCreated(surface);
45 Q_EMIT surfaceCreated(surface);
48 return surface;
46 return surface;
49}
47}
@@ -57,40 +55,12 @@ void SurfaceManager::registerSurface(MirSurfaceItem *surface)
57 surface->setLive(false);
55 surface->setLive(false);
58 Q_EMIT surfaceDestroyed(surface);
56 Q_EMIT surfaceDestroyed(surface);
59 });
57 });
60− connect(surface, &MirSurfaceItem::inputMethodRequested,
61− this, &SurfaceManager::showInputMethod);
62− connect(surface, &MirSurfaceItem::inputMethodDismissed,
63− this, &SurfaceManager::hideInputMethod);
64−}
65−
66−void SurfaceManager::showInputMethod()
67−{
68− inputMethodSurface()->setState(MirSurfaceItem::Restored);
69−}
70−
71−void SurfaceManager::hideInputMethod()
72−{
73− if (m_virtualKeyboard) {
74− m_virtualKeyboard->setState(MirSurfaceItem::Minimized);
75− }
76}
58}
77
59
78MirSurfaceItem *SurfaceManager::inputMethodSurface()
60MirSurfaceItem *SurfaceManager::inputMethodSurface()
79{
61{
80 if (!m_virtualKeyboard) {
62 if (!m_virtualKeyboard) {
81−
82− QString screenshotPath = QString("file://%1/Dash/graphics/phone/screenshots/vkb_portrait.png")
83− .arg(qmlDirectory());
84−
85− QString qmlFilePath = QString("%1/Unity/Application/VirtualKeyboard.qml")
86− .arg(mockPluginsDir());
87−
88− m_virtualKeyboard = new MirSurfaceItem(
63+ m_virtualKeyboard = new VirtualKeyboard;
89− "input-method",
90− MirSurfaceItem::InputMethod,
91− MirSurfaceItem::Minimized,
92− screenshotPath,
93− qmlFilePath);
94 Q_EMIT surfaceCreated(m_virtualKeyboard);
64 Q_EMIT surfaceCreated(m_virtualKeyboard);
95 }
65 }
96 return m_virtualKeyboard;
66 return m_virtualKeyboard;
tests/mocks/Unity/Application/SurfaceManager.h+2−5
@@ -20,6 +20,7 @@
20#include <QObject>
20#include <QObject>
21
21
22#include "MirSurfaceItem.h"
22#include "MirSurfaceItem.h"
23+#include "VirtualKeyboard.h"
23
24
24class SurfaceManager : public QObject
25class SurfaceManager : public QObject
25{
26{
@@ -45,13 +46,9 @@ Q_SIGNALS:
45 void surfaceCreated(MirSurfaceItem *surface);
46 void surfaceCreated(MirSurfaceItem *surface);
46 void surfaceDestroyed(MirSurfaceItem *surface);
47 void surfaceDestroyed(MirSurfaceItem *surface);
47
48
48−private Q_SLOTS:
49− void showInputMethod();
50− void hideInputMethod();
51−
52private:
49private:
53 static SurfaceManager *the_surface_manager;
50 static SurfaceManager *the_surface_manager;
54− MirSurfaceItem *m_virtualKeyboard;
51+ VirtualKeyboard *m_virtualKeyboard;
55};
52};
56
53
57#endif // SURFACEMANAGER_H
54#endif // SURFACEMANAGER_H
tests/mocks/Unity/Application/UbuntuKeyboardInfo.cpp+33−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software: you can redistribute it and/or modify it under
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
5 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -26,3 +26,35 @@ UbuntuKeyboardInfo::UbuntuKeyboardInfo(QObject *parent)
26 m_height(200)
26 m_height(200)
27{
27{
28}
28}
29+
30+void UbuntuKeyboardInfo::setX(qreal value)
31+{
32+ if (value != m_x) {
33+ m_x = value;
34+ Q_EMIT xChanged(m_x);
35+ }
36+}
37+
38+void UbuntuKeyboardInfo::setY(qreal value)
39+{
40+ if (value != m_y) {
41+ m_y = value;
42+ Q_EMIT yChanged(m_y);
43+ }
44+}
45+
46+void UbuntuKeyboardInfo::setWidth(qreal value)
47+{
48+ if (value != m_width) {
49+ m_width = value;
50+ Q_EMIT widthChanged(m_width);
51+ }
52+}
53+
54+void UbuntuKeyboardInfo::setHeight(qreal value)
55+{
56+ if (value != m_height) {
57+ m_height = value;
58+ Q_EMIT heightChanged(m_height);
59+ }
60+}
tests/mocks/Unity/Application/UbuntuKeyboardInfo.h+11−5
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software: you can redistribute it and/or modify it under
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
5 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -22,10 +22,10 @@
22
22
23class UbuntuKeyboardInfo : public QObject {
23class UbuntuKeyboardInfo : public QObject {
24 Q_OBJECT
24 Q_OBJECT
25− Q_PROPERTY(qreal x READ x NOTIFY xChanged)
25+ Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged)
26− Q_PROPERTY(qreal y READ y NOTIFY yChanged)
26+ Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged)
27− Q_PROPERTY(qreal width READ width NOTIFY widthChanged)
27+ Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged)
28− Q_PROPERTY(qreal height READ height NOTIFY heightChanged)
28+ Q_PROPERTY(qreal height READ height WRITE setHeight NOTIFY heightChanged)
29public:
29public:
30 UbuntuKeyboardInfo(QObject *parent = 0);
30 UbuntuKeyboardInfo(QObject *parent = 0);
31 virtual ~UbuntuKeyboardInfo() {}
31 virtual ~UbuntuKeyboardInfo() {}
@@ -34,6 +34,12 @@ public:
34 qreal width() const { return m_width; }
34 qreal width() const { return m_width; }
35 qreal height() const { return m_height; }
35 qreal height() const { return m_height; }
36
36
37+ // Just in the fake implementation
38+ void setX(qreal value);
39+ void setY(qreal value);
40+ void setWidth(qreal value);
41+ void setHeight(qreal value);
42+
37 static UbuntuKeyboardInfo *singleton() {
43 static UbuntuKeyboardInfo *singleton() {
38 if (!m_instance) {
44 if (!m_instance) {
39 m_instance = new UbuntuKeyboardInfo;
45 m_instance = new UbuntuKeyboardInfo;
tests/mocks/Unity/Application/VirtualKeyboard.cppnew file+44
@@ -0,0 +1,44 @@
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 "VirtualKeyboard.h"
18+
19+#include <paths.h>
20+
21+#include <QString>
22+
23+#include <QDebug>
24+
25+VirtualKeyboard::VirtualKeyboard(QQuickItem *parent)
26+ : MirSurfaceItem("input-method",
27+ MirSurfaceItem::InputMethod,
28+ MirSurfaceItem::Minimized,
29+ QString("file://%1/Dash/graphics/phone/screenshots/vkb_portrait.png")
30+ .arg(qmlDirectory()),
31+ QString("%1/Unity/Application/VirtualKeyboard.qml")
32+ .arg(mockPluginsDir()),
33+ parent)
34+{
35+}
36+
37+VirtualKeyboard::~VirtualKeyboard()
38+{
39+}
40+
41+void VirtualKeyboard::touchEvent(QTouchEvent *event)
42+{
43+ event->setAccepted(false);
44+}
tests/mocks/Unity/Application/VirtualKeyboard.hnew file+42
@@ -0,0 +1,42 @@
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 VIRTUALKEYBOARD_H
18+#define VIRTUALKEYBOARD_H
19+
20+#include "MirSurfaceItem.h"
21+#include "MirSurfaceItemModel.h"
22+
23+#include <QQuickItem>
24+#include <QUrl>
25+
26+class Session;
27+
28+class VirtualKeyboard : public MirSurfaceItem
29+{
30+ Q_OBJECT
31+public:
32+ explicit VirtualKeyboard(QQuickItem *parent = 0);
33+ ~VirtualKeyboard();
34+
35+protected:
36+ void touchEvent(QTouchEvent * event) override;
37+};
38+
39+Q_DECLARE_METATYPE(VirtualKeyboard*)
40+Q_DECLARE_METATYPE(QList<VirtualKeyboard*>)
41+
42+#endif // VIRTUALKEYBOARD_H
tests/mocks/Unity/Application/VirtualKeyboard.qml+7−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2014-2015 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
5 * it under the terms of the GNU Lesser General Public License as published by
@@ -15,6 +15,7 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18+import Unity.Application 0.1
18
19
19Item {
20Item {
20 implicitWidth: units.gu(40)
21 implicitWidth: units.gu(40)
@@ -37,4 +38,9 @@ Item {
37 anchors.fill: parent
38 anchors.fill: parent
38 }
39 }
39 }
40 }
41+
42+ Binding { target: UbuntuKeyboardInfo; property: "x"; value: screenshotImage.x}
43+ Binding { target: UbuntuKeyboardInfo; property: "y"; value: screenshotImage.y}
44+ Binding { target: UbuntuKeyboardInfo; property: "width"; value: screenshotImage.width}
45+ Binding { target: UbuntuKeyboardInfo; property: "height"; value: screenshotImage.height}
40}
46}
tests/mocks/Unity/Application/plugin.h+2−2
@@ -24,8 +24,8 @@ class FakeUnityApplicationQmlPlugin : public QQmlExtensionPlugin
24 Q_OBJECT
24 Q_OBJECT
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26public:
26public:
27− void registerTypes(const char *uri);
27+ void registerTypes(const char *uri) override;
28− void initializeEngine(QQmlEngine *engine, const char *uri);
28+ void initializeEngine(QQmlEngine *engine, const char *uri) override;
29};
29};
30
30
31#endif
31#endif
tests/mocks/Unity/Connectivity/plugin.h+1−1
@@ -25,7 +25,7 @@ class BackendPlugin : public QQmlExtensionPlugin
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26
26
27public:
27public:
28− void registerTypes(const char *uri);
28+ void registerTypes(const char *uri) override;
29};
29};
30
30
31#endif // MOCK_CONNECTIVITY_PLUGIN_H
31#endif // MOCK_CONNECTIVITY_PLUGIN_H
tests/mocks/Unity/DashCommunicator/plugin.h+1−1
@@ -28,7 +28,7 @@ class DashCommunicatorPlugin : public QQmlExtensionPlugin
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29
29
30public:
30public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
32};
32};
33
33
34#endif
34#endif
tests/mocks/Unity/Indicators/fakeindicatorsmodel.h+5−5
@@ -50,11 +50,11 @@ public:
50 QVariant modelData() const { return m_modelData; }
50 QVariant modelData() const { return m_modelData; }
51
51
52 /* QAbstractItemModel */
52 /* QAbstractItemModel */
53− QHash<int, QByteArray> roleNames() const;
53+ QHash<int, QByteArray> roleNames() const override;
54− int columnCount(const QModelIndex &parent = QModelIndex()) const;
54+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
55− QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
55+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
56− QModelIndex parent (const QModelIndex &index) const;
56+ QModelIndex parent (const QModelIndex &index) const override;
57− int rowCount(const QModelIndex &parent = QModelIndex()) const;
57+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
58
58
59Q_SIGNALS:
59Q_SIGNALS:
60 void countChanged();
60 void countChanged();
tests/mocks/Unity/Indicators/fakeplugin.h+1−1
@@ -26,7 +26,7 @@ class IndicatorsFakePlugin : public QQmlExtensionPlugin
26 Q_OBJECT
26 Q_OBJECT
27 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28public:
28public:
29− void registerTypes(const char *uri);
29+ void registerTypes(const char *uri) override;
30};
30};
31
31
32#endif // INDICATORS_FAKE_PLUGIN_H
32#endif // INDICATORS_FAKE_PLUGIN_H
tests/mocks/Unity/Launcher/MockLauncherItem.h+2−2
@@ -43,9 +43,9 @@ public:
43 int progress() const override;
43 int progress() const override;
44 int count() const override;
44 int count() const override;
45 bool countVisible() const override;
45 bool countVisible() const override;
46− bool focused() const;
46+ bool focused() const override;
47
47
48− unity::shell::launcher::QuickListModelInterface *quickList() const;
48+ unity::shell::launcher::QuickListModelInterface *quickList() const override;
49
49
50private:
50private:
51 void setPinned(bool pinned);
51 void setPinned(bool pinned);
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+3−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013,2015 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
5 * it under the terms of the GNU Lesser General Public License as published by
@@ -35,6 +35,8 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
35 item->setProgress(50);
35 item->setProgress(50);
36 item->setCountVisible(true);
36 item->setCountVisible(true);
37 m_list.append(item);
37 m_list.append(item);
38+ item = new MockLauncherItem("music-app", "/usr/share/applications/music-app.desktop", "Music", "soundcloud", this);
39+ m_list.append(item);
38 item = new MockLauncherItem("facebook-webapp", "/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook", this);
40 item = new MockLauncherItem("facebook-webapp", "/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook", this);
39 item->setProgress(150);
41 item->setProgress(150);
40 m_list.append(item);
42 m_list.append(item);
tests/mocks/Unity/Launcher/MockLauncherModel.h+10−10
@@ -34,20 +34,20 @@ public:
34 MockLauncherModel(QObject* parent = 0);
34 MockLauncherModel(QObject* parent = 0);
35 ~MockLauncherModel();
35 ~MockLauncherModel();
36
36
37− int rowCount(const QModelIndex& parent) const;
37+ int rowCount(const QModelIndex& parent) const override;
38
38
39− QVariant data(const QModelIndex& index, int role) const;
39+ QVariant data(const QModelIndex& index, int role) const override;
40
40
41− Q_INVOKABLE unity::shell::launcher::LauncherItemInterface *get(int index) const;
41+ Q_INVOKABLE unity::shell::launcher::LauncherItemInterface *get(int index) const override;
42− Q_INVOKABLE void move(int oldIndex, int newIndex);
42+ Q_INVOKABLE void move(int oldIndex, int newIndex) override;
43− Q_INVOKABLE void pin(const QString &appId, int index = -1);
43+ Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
44− Q_INVOKABLE void requestRemove(const QString &appId);
44+ Q_INVOKABLE void requestRemove(const QString &appId) override;
45− Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
45+ Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex) override;
46− Q_INVOKABLE void setUser(const QString &username);
46+ Q_INVOKABLE void setUser(const QString &username) override;
47 Q_INVOKABLE QString getUrlForAppId(const QString &appId) const;
47 Q_INVOKABLE QString getUrlForAppId(const QString &appId) const;
48
48
49− unity::shell::application::ApplicationManagerInterface *applicationManager() const;
49+ unity::shell::application::ApplicationManagerInterface *applicationManager() const override;
50− void setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager);
50+ void setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager) override;
51
51
52 bool onlyPinned() const override;
52 bool onlyPinned() const override;
53 void setOnlyPinned(bool onlyPinned) override;
53 void setOnlyPinned(bool onlyPinned) override;
tests/mocks/Unity/Launcher/MockQuickListModel.h+2−2
@@ -30,9 +30,9 @@ class MockQuickListModel: public QuickListModelInterface
30public:
30public:
31 MockQuickListModel(QObject *parent = 0);
31 MockQuickListModel(QObject *parent = 0);
32
32
33− QVariant data(const QModelIndex &index, int role) const;
33+ QVariant data(const QModelIndex &index, int role) const override;
34
34
35− int rowCount(const QModelIndex &parent = QModelIndex()) const;
35+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
36};
36};
37
37
38#endif // MOCKQUICKLISTMODEL_H
38#endif // MOCKQUICKLISTMODEL_H
tests/mocks/Unity/Launcher/plugin.h+1−1
@@ -29,7 +29,7 @@ class TestLauncherPlugin : public QQmlExtensionPlugin
29 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
30
30
31public:
31public:
32− void registerTypes(const char* uri);
32+ void registerTypes(const char* uri) override;
33};
33};
34
34
35#endif // TESTLAUNCHER_PLUGIN_H
35#endif // TESTLAUNCHER_PLUGIN_H
tests/mocks/Unity/Notifications/MockActionModel.h+3−3
@@ -32,9 +32,9 @@ public:
32 ActionModel(QObject *parent=nullptr);
32 ActionModel(QObject *parent=nullptr);
33 virtual ~ActionModel();
33 virtual ~ActionModel();
34
34
35− virtual int rowCount(const QModelIndex &index) const;
35+ int rowCount(const QModelIndex &index) const override;
36− virtual QVariant data(const QModelIndex &index, int role) const;
36+ QVariant data(const QModelIndex &index, int role) const override;
37− virtual QHash<int, QByteArray> roleNames() const;
37+ QHash<int, QByteArray> roleNames() const override;
38
38
39 Q_ENUMS(ActionsRoles)
39 Q_ENUMS(ActionsRoles)
40 enum ActionsRoles {
40 enum ActionsRoles {
tests/mocks/Unity/Notifications/MockNotificationModel.h+3−3
@@ -35,9 +35,9 @@ public:
35 MockNotificationModel(QObject *parent=nullptr);
35 MockNotificationModel(QObject *parent=nullptr);
36 virtual ~MockNotificationModel();
36 virtual ~MockNotificationModel();
37
37
38− virtual int rowCount(const QModelIndex &parent) const;
38+ int rowCount(const QModelIndex &parent) const override;
39− virtual QVariant data(const QModelIndex &index, int role) const;
39+ QVariant data(const QModelIndex &index, int role) const override;
40− virtual QHash<int, QByteArray> roleNames() const;
40+ QHash<int, QByteArray> roleNames() const override;
41
41
42 Q_INVOKABLE void append(MockNotification* n);
42 Q_INVOKABLE void append(MockNotification* n);
43 MockNotification* getNotification(int id) const;
43 MockNotification* getNotification(int id) const;
tests/mocks/Unity/Notifications/plugin.h+1−1
@@ -28,7 +28,7 @@ class TestNotificationPlugin : public QQmlExtensionPlugin
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29
29
30public:
30public:
31− void registerTypes(const char* uri);
31+ void registerTypes(const char* uri) override;
32};
32};
33
33
34#endif // TESTNOTIFICATION_PLUGIN_H
34#endif // TESTNOTIFICATION_PLUGIN_H
tests/mocks/Unity/fake_scopes.h+2−2
@@ -50,8 +50,8 @@ public:
50 Q_INVOKABLE void clearFavorites();
50 Q_INVOKABLE void clearFavorites();
51 Q_INVOKABLE void load();
51 Q_INVOKABLE void load();
52
52
53− QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
53+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
54− QModelIndex parent ( const QModelIndex & index ) const;
54+ QModelIndex parent ( const QModelIndex & index ) const override;
55
55
56 bool loaded() const override;
56 bool loaded() const override;
57 int count() const override;
57 int count() const override;
tests/mocks/Unity/fake_unity_plugin.h+1−1
@@ -28,7 +28,7 @@ class FakeUnityPlugin : public QQmlExtensionPlugin
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29
29
30public:
30public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
32};
32};
33
33
34#endif
34#endif
tests/mocks/Utils/CMakeLists.txtnew file+40
@@ -0,0 +1,40 @@
1+pkg_search_module(GIO REQUIRED gio-2.0)
2+
3+include_directories(
4+ ${CMAKE_CURRENT_SOURCE_DIR}
5+ ${CMAKE_CURRENT_BINARY_DIR}
6+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
7+ ${GIO_INCLUDE_DIRS}
8+ ${CMAKE_SOURCE_DIR}/plugins/Utils
9+)
10+
11+set(QMLPLUGIN_SRC
12+ ${CMAKE_SOURCE_DIR}/plugins/Utils/inputwatcher.cpp
13+ ${CMAKE_SOURCE_DIR}/plugins/Utils/qlimitproxymodelqml.cpp
14+ ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
15+ ${CMAKE_SOURCE_DIR}/plugins/Utils/relativetimeformatter.cpp
16+ ${CMAKE_SOURCE_DIR}/plugins/Utils/timeformatter.cpp
17+ ${CMAKE_SOURCE_DIR}/plugins/Utils/unitymenumodelpaths.cpp
18+ ${CMAKE_SOURCE_DIR}/plugins/Utils/windowkeysfilter.cpp
19+ ${CMAKE_SOURCE_DIR}/plugins/Utils/windowscreenshotprovider.cpp
20+ ${CMAKE_SOURCE_DIR}/plugins/Utils/easingcurve.cpp
21+ constants.cpp
22+ plugin.cpp
23+ windowstatestorage.cpp
24+ )
25+
26+add_library(FakeUtils-qml SHARED
27+ ${QMLPLUGIN_SRC}
28+ )
29+
30+target_link_libraries(FakeUtils-qml ${GIO_LDFLAGS})
31+
32+# Because this is an internal support library, we want
33+# to expose all symbols in it. Consider changing this
34+# either to a static library or just using the
35+# files directly in targets.
36+set_target_properties(FakeUtils-qml PROPERTIES COMPILE_FLAGS -fvisibility=default)
37+
38+qt5_use_modules(FakeUtils-qml Qml Quick DBus Network Gui)
39+
40+add_unity8_mock(Utils 0.1 Utils TARGETS FakeUtils-qml)
tests/mocks/Utils/HomeKeyWatcher.qmlnew file+21
@@ -0,0 +1,21 @@
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+
19+QtObject {
20+ signal activated()
21+}
tests/mocks/Utils/Style.jsnew file+37
@@ -0,0 +1,37 @@
1+/*
2+ * Copyright (C) 2014 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+.pragma library
18+
19+/*! \brief Calculate average luminance of the passed colors
20+
21+ \note If not fully opaque, luminance is dependant on blending.
22+ */
23+function luminance() {
24+ var sum = 0;
25+ // TODO this was originally
26+ // for (var k in arguments) {
27+ // but for some unkown reason was causing crashes in testDash/testDashContent
28+ // investigate when we have some time
29+ for (var k = 0; k < arguments.length; ++k) {
30+ // only way to convert string to color
31+ var c = Qt.lighter(arguments[k], 1.0);
32+
33+ sum += 0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b;
34+ }
35+
36+ return sum / arguments.length;
37+}
tests/mocks/Utils/Utils.qmltypesnew file+175
@@ -0,0 +1,175 @@
1+import QtQuick.tooling 1.1
2+
3+// This file describes the plugin-supplied types contained in the library.
4+// It is used for QML tooling purposes only.
5+//
6+// This file was auto-generated by:
7+// 'qmlplugindump -notrelocatable Utils 0.1 plugins'
8+
9+Module {
10+ Component {
11+ name: "EasingCurve"
12+ prototype: "QObject"
13+ exports: ["Utils/EasingCurve 0.1"]
14+ exportMetaObjectRevisions: [0]
15+ Property { name: "type"; type: "QEasingCurve::Type" }
16+ Property { name: "period"; type: "double" }
17+ Property { name: "progress"; type: "double" }
18+ Property { name: "value"; type: "double"; isReadonly: true }
19+ }
20+ Component {
21+ name: "QAbstractProxyModel"
22+ prototype: "QAbstractItemModel"
23+ Property { name: "sourceModel"; type: "QAbstractItemModel"; isPointer: true }
24+ }
25+ Component { name: "QIdentityProxyModel"; prototype: "QAbstractProxyModel" }
26+ Component {
27+ name: "QLimitProxyModelQML"
28+ prototype: "QIdentityProxyModel"
29+ exports: ["Utils/LimitProxyModel 0.1"]
30+ exportMetaObjectRevisions: [0]
31+ Property { name: "model"; type: "QAbstractItemModel"; isPointer: true }
32+ Property { name: "limit"; type: "int" }
33+ Property { name: "count"; type: "int"; isReadonly: true }
34+ Signal { name: "totalCountChanged" }
35+ }
36+ Component {
37+ name: "QSortFilterProxyModel"
38+ prototype: "QAbstractProxyModel"
39+ Property { name: "filterRegExp"; type: "QRegExp" }
40+ Property { name: "filterKeyColumn"; type: "int" }
41+ Property { name: "dynamicSortFilter"; type: "bool" }
42+ Property { name: "filterCaseSensitivity"; type: "Qt::CaseSensitivity" }
43+ Property { name: "sortCaseSensitivity"; type: "Qt::CaseSensitivity" }
44+ Property { name: "isSortLocaleAware"; type: "bool" }
45+ Property { name: "sortRole"; type: "int" }
46+ Property { name: "filterRole"; type: "int" }
47+ Method {
48+ name: "setFilterRegExp"
49+ Parameter { name: "pattern"; type: "string" }
50+ }
51+ Method {
52+ name: "setFilterWildcard"
53+ Parameter { name: "pattern"; type: "string" }
54+ }
55+ Method {
56+ name: "setFilterFixedString"
57+ Parameter { name: "pattern"; type: "string" }
58+ }
59+ Method { name: "clear" }
60+ Method { name: "invalidate" }
61+ }
62+ Component {
63+ name: "RelativeTimeFormatter"
64+ prototype: "TimeFormatter"
65+ exports: ["Utils/RelativeTimeFormatter 0.1"]
66+ exportMetaObjectRevisions: [0]
67+ }
68+ Component {
69+ name: "TimeFormatter"
70+ prototype: "QObject"
71+ exports: ["Utils/GDateTimeFormatter 0.1", "Utils/TimeFormatter 0.1"]
72+ exportMetaObjectRevisions: [0, 0]
73+ Property { name: "format"; type: "string" }
74+ Property { name: "timeString"; type: "string"; isReadonly: true }
75+ Property { name: "time"; type: "qlonglong" }
76+ Signal {
77+ name: "formatChanged"
78+ Parameter { name: "format"; type: "string" }
79+ }
80+ Signal {
81+ name: "timeStringChanged"
82+ Parameter { name: "timeString"; type: "string" }
83+ }
84+ Signal {
85+ name: "timeChanged"
86+ Parameter { name: "time"; type: "qlonglong" }
87+ }
88+ }
89+ Component {
90+ name: "UnityMenuModelPaths"
91+ prototype: "QObject"
92+ exports: ["Utils/UnityMenuModelPaths 0.1"]
93+ exportMetaObjectRevisions: [0]
94+ Property { name: "source"; type: "QVariant" }
95+ Property { name: "busName"; type: "QByteArray"; isReadonly: true }
96+ Property { name: "actions"; type: "QVariantMap"; isReadonly: true }
97+ Property { name: "menuObjectPath"; type: "QByteArray"; isReadonly: true }
98+ Property { name: "busNameHint"; type: "QByteArray" }
99+ Property { name: "actionsHint"; type: "QByteArray" }
100+ Property { name: "menuObjectPathHint"; type: "QByteArray" }
101+ }
102+ Component {
103+ name: "UnitySortFilterProxyModelQML"
104+ prototype: "QSortFilterProxyModel"
105+ exports: ["Utils/UnitySortFilterProxyModel 0.1"]
106+ exportMetaObjectRevisions: [0]
107+ Property { name: "model"; type: "QAbstractItemModel"; isPointer: true }
108+ Property { name: "totalCount"; type: "int"; isReadonly: true }
109+ Property { name: "count"; type: "int"; isReadonly: true }
110+ Property { name: "invertMatch"; type: "bool" }
111+ Signal {
112+ name: "invertMatchChanged"
113+ Parameter { type: "bool" }
114+ }
115+ Method {
116+ name: "get"
117+ type: "QVariantMap"
118+ Parameter { name: "row"; type: "int" }
119+ }
120+ Method {
121+ name: "data"
122+ type: "QVariant"
123+ Parameter { name: "row"; type: "int" }
124+ Parameter { name: "role"; type: "int" }
125+ }
126+ Method { name: "count"; type: "int" }
127+ Method {
128+ name: "findFirst"
129+ type: "int"
130+ Parameter { name: "role"; type: "int" }
131+ Parameter { name: "value"; type: "QVariant" }
132+ }
133+ Method {
134+ name: "mapRowToSource"
135+ type: "int"
136+ Parameter { name: "row"; type: "int" }
137+ }
138+ Method {
139+ name: "mapFromSource"
140+ type: "int"
141+ Parameter { name: "row"; type: "int" }
142+ }
143+ Method {
144+ name: "mapToSource"
145+ type: "int"
146+ Parameter { name: "row"; type: "int" }
147+ }
148+ }
149+ Component {
150+ name: "WindowKeysFilter"
151+ defaultProperty: "data"
152+ prototype: "QQuickItem"
153+ exports: ["Utils/WindowKeysFilter 0.1"]
154+ exportMetaObjectRevisions: [0]
155+ }
156+ Component {
157+ name: "WindowStateStorage"
158+ prototype: "QObject"
159+ exports: ["Utils/WindowStateStorage 0.1"]
160+ isCreatable: false
161+ isSingleton: true
162+ exportMetaObjectRevisions: [0]
163+ Method {
164+ name: "saveGeometry"
165+ Parameter { name: "windowId"; type: "string" }
166+ Parameter { name: "rect"; type: "QRect" }
167+ }
168+ Method {
169+ name: "getGeometry"
170+ type: "QRect"
171+ Parameter { name: "windowId"; type: "string" }
172+ Parameter { name: "defaultValue"; type: "QRect" }
173+ }
174+ }
175+}
tests/mocks/Utils/constants.cppnew file+23
@@ -0,0 +1,23 @@
1+/*
2+ * Copyright 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 Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+*/
16+
17+#include "constants.h"
18+
19+Constants::Constants(QObject *parent)
20+ : QObject(parent)
21+{
22+ m_indicatorValueTimeout = 5000;
23+}
tests/mocks/Utils/constants.hnew file+43
@@ -0,0 +1,43 @@
1+/*
2+ * Copyright 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 Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+*/
16+
17+#ifndef CONSTANTS_H
18+#define CONSTANTS_H
19+
20+#include <QObject>
21+
22+/**
23+ * @brief The Constants class
24+ *
25+ * This singleton class exposes contants to Qml
26+ *
27+ */
28+
29+class Constants: public QObject
30+{
31+ Q_OBJECT
32+ Q_PROPERTY(int indicatorValueTimeout READ indicatorValueTimeout CONSTANT)
33+
34+public:
35+ Constants(QObject *parent = 0);
36+
37+ int indicatorValueTimeout() const { return m_indicatorValueTimeout; }
38+
39+private:
40+ int m_indicatorValueTimeout;
41+};
42+
43+#endif
tests/mocks/Utils/plugin.cppnew file+76
@@ -0,0 +1,76 @@
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+// Qt
18+#include <QtQml/qqml.h>
19+#include <QDBusConnection>
20+#include <QQmlContext>
21+#include <QtQuick/QQuickWindow>
22+#include <QDebug>
23+
24+// local
25+#include "plugin.h"
26+#include "windowstatestorage.h"
27+#include "constants.h"
28+
29+// plugin
30+#include <inputwatcher.h>
31+#include <qlimitproxymodelqml.h>
32+#include <unitysortfilterproxymodelqml.h>
33+#include <relativetimeformatter.h>
34+#include <timeformatter.h>
35+#include <unitymenumodelpaths.h>
36+#include <windowkeysfilter.h>
37+#include <windowscreenshotprovider.h>
38+#include <easingcurve.h>
39+
40+static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
41+{
42+ Q_UNUSED(engine)
43+ Q_UNUSED(scriptEngine)
44+ return new WindowStateStorage();
45+}
46+
47+static QObject *createConstants(QQmlEngine *engine, QJSEngine *scriptEngine)
48+{
49+ Q_UNUSED(engine)
50+ Q_UNUSED(scriptEngine)
51+ return new Constants();
52+}
53+
54+void FakeUtilsPlugin::registerTypes(const char *uri)
55+{
56+ Q_ASSERT(uri == QLatin1String("Utils"));;
57+ qmlRegisterType<QAbstractItemModel>();
58+ qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
59+ qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
60+ qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
61+ qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");
62+ qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
63+ qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
64+ qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
65+ qmlRegisterType<RelativeTimeFormatter>(uri, 0, 1, "RelativeTimeFormatter");
66+ qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
67+ qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
68+ qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
69+}
70+
71+void FakeUtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
72+{
73+ QQmlExtensionPlugin::initializeEngine(engine, uri);
74+
75+ engine->addImageProvider(QLatin1String("window"), new WindowScreenshotProvider);
76+}
tests/mocks/Utils/plugin.hnew file+33
@@ -0,0 +1,33 @@
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 FAKE_UTILS_PLUGIN_H
18+#define FAKE_UTILS_PLUGIN_H
19+
20+#include <QtQml/QQmlEngine>
21+#include <QtQml/QQmlExtensionPlugin>
22+
23+class FakeUtilsPlugin : public QQmlExtensionPlugin
24+{
25+ Q_OBJECT
26+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27+
28+public:
29+ void registerTypes(const char *uri);
30+ void initializeEngine(QQmlEngine *engine, const char *uri);
31+};
32+
33+#endif
tests/mocks/Utils/qmldirnew file+5
@@ -0,0 +1,5 @@
1+module Utils
2+plugin FakeUtils-qml
3+typeinfo Utils.qmltypes
4+Style 0.1 Style.js
5+HomeKeyWatcher 0.1 HomeKeyWatcher.qml
tests/mocks/Utils/windowstatestorage.cppnew file+48
@@ -0,0 +1,48 @@
1+/*
2+ * Copyright 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 Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "windowstatestorage.h"
18+
19+#include <QRect>
20+
21+WindowStateStorage::WindowStateStorage(QObject *parent):
22+ QObject(parent)
23+{
24+}
25+
26+void WindowStateStorage::setGeometry(const QVariantMap& geometry)
27+{
28+ if (geometry != m_geometry) {
29+ m_geometry = geometry;
30+ Q_EMIT geometryChanged(m_geometry);
31+ }
32+}
33+
34+QVariantMap WindowStateStorage::geometry() const
35+{
36+ return m_geometry;
37+}
38+
39+void WindowStateStorage::saveGeometry(const QString &windowId, const QRect &rect)
40+{
41+ m_geometry[windowId] = rect;
42+}
43+
44+QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue)
45+{
46+ if (!m_geometry.contains(windowId)) return defaultValue;
47+ return m_geometry.value(windowId).toRect();
48+}
tests/mocks/Utils/windowstatestorage.hnew file+38
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright 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 Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <QObject>
18+#include <QVariantMap>
19+
20+class WindowStateStorage: public QObject
21+{
22+ Q_OBJECT
23+ Q_PROPERTY(QVariantMap geometry READ geometry WRITE setGeometry NOTIFY geometryChanged)
24+public:
25+ WindowStateStorage(QObject *parent = 0);
26+
27+ Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
28+ Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue);
29+
30+Q_SIGNALS:
31+ void geometryChanged(const QVariantMap& geometry);
32+
33+private:
34+ void setGeometry(const QVariantMap& geometry);
35+ QVariantMap geometry() const;
36+
37+ QVariantMap m_geometry;
38+};
tests/mocks/Wizard/mockplugin.h+1−1
@@ -26,7 +26,7 @@ class MockWizardPlugin : public QQmlExtensionPlugin
26 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27
27
28public:
28public:
29− void registerTypes(const char *uri);
29+ void registerTypes(const char *uri) override;
30};
30};
31
31
32#endif
32#endif
tests/mocks/indicator-service/mock-indicator-service.c+76−12
@@ -27,8 +27,15 @@ typedef struct
27 guint actions_export_id;
27 guint actions_export_id;
28 guint menu_export_id;
28 guint menu_export_id;
29 int action_delay;
29 int action_delay;
30+ int change_interval;
30} IndicatorTestService;
31} IndicatorTestService;
31
32
33+typedef struct
34+{
35+ GSimpleAction *action;
36+ GVariant* value;
37+} Action;
38+
32static void
39static void
33bus_acquired (GDBusConnection *connection,
40bus_acquired (GDBusConnection *connection,
34 const gchar *name,
41 const gchar *name,
@@ -96,10 +103,10 @@ actual_switch (gpointer user_data)
96 gboolean state = g_variant_get_boolean (v);
103 gboolean state = g_variant_get_boolean (v);
97 GVariant *new_state = g_variant_new_boolean (state == TRUE ? FALSE : TRUE);
104 GVariant *new_state = g_variant_new_boolean (state == TRUE ? FALSE : TRUE);
98
105
99− g_simple_action_set_state(G_SIMPLE_ACTION(action), new_state);
106+ g_simple_action_set_state(action, new_state);
100
107
101 g_variant_unref (v);
108 g_variant_unref (v);
102− g_message ("switching");
109+ g_message ("switching %d", state);
103 return FALSE;
110 return FALSE;
104}
111}
105
112
@@ -120,13 +127,12 @@ activate_switch (GSimpleAction *action,
120static gboolean
127static gboolean
121actual_slide (gpointer user_data)
128actual_slide (gpointer user_data)
122{
129{
123− GSimpleAction *action = user_data;
130+ Action *slide_action = user_data;
124−
125− GVariant* new_state = g_object_get_qdata (G_OBJECT (action), slider_value_quark ());
126
131
127− g_simple_action_set_state(G_SIMPLE_ACTION(action), new_state);
132+ g_simple_action_set_state(G_SIMPLE_ACTION(slide_action->action), slide_action->value);
133+ g_message ("sliding %f", g_variant_get_double(slide_action->value));
134+ free(slide_action);
128
135
129− g_message ("switching");
130 return FALSE;
136 return FALSE;
131}
137}
132
138
@@ -136,10 +142,40 @@ void change_slider (GSimpleAction *action,
136{
142{
137 IndicatorTestService *indicator = user_data;
143 IndicatorTestService *indicator = user_data;
138
144
139− g_object_set_qdata (G_OBJECT (action), slider_value_quark (), g_variant_ref(value));
145+ Action* slide_action = malloc(sizeof(Action));
146+ slide_action->action = action;
147+ slide_action->value = g_variant_ref(value);
140
148
141− g_timeout_add(indicator->action_delay, actual_slide, action);
149+ g_timeout_add(indicator->action_delay, actual_slide, slide_action);
142− g_message ("slide delay %.03f", g_variant_get_double(value));
150+ g_message ("slide delay %f", g_variant_get_double(value));
151+}
152+
153+static gboolean
154+change_interval (gpointer user_data)
155+{
156+ g_message ("change interval");
157+
158+ IndicatorTestService *indicator = user_data;
159+
160+ GAction* action_switch = g_action_map_lookup_action(G_ACTION_MAP(indicator->actions), "action.switch");
161+ actual_switch(G_SIMPLE_ACTION(action_switch));
162+
163+ GAction* action_checkbox = g_action_map_lookup_action(G_ACTION_MAP(indicator->actions), "action.checkbox");
164+ actual_switch(G_SIMPLE_ACTION(action_checkbox));
165+
166+ GAction* action_accessPoint = g_action_map_lookup_action(G_ACTION_MAP(indicator->actions), "action.accessPoint");
167+ actual_switch(G_SIMPLE_ACTION(action_accessPoint));
168+
169+ GAction* action_slider = g_action_map_lookup_action(G_ACTION_MAP(indicator->actions), "action.slider");
170+ static double old_value = 0.25;
171+ double new_value = old_value == 0.25 ? 0.75 : 0.25;
172+ old_value = new_value;
173+ Action* slide_action = malloc(sizeof(Action));
174+ slide_action->action = G_SIMPLE_ACTION(action_slider);
175+ slide_action->value = g_variant_new_double(new_value);
176+ actual_slide(slide_action);
177+
178+ return TRUE;
143}
179}
144
180
145int
181int
@@ -147,6 +183,7 @@ main (int argc, char **argv)
147{
183{
148 IndicatorTestService indicator = { 0 };
184 IndicatorTestService indicator = { 0 };
149 indicator.action_delay = -1;
185 indicator.action_delay = -1;
186+ indicator.change_interval = -1;
150 GMenuItem *item;
187 GMenuItem *item;
151 GMenu *submenu;
188 GMenu *submenu;
152 GActionEntry entries[] = {
189 GActionEntry entries[] = {
@@ -191,6 +228,27 @@ main (int argc, char **argv)
191 }
228 }
192 break;
229 break;
193 }
230 }
231+
232+ case 'c':
233+ {
234+ arg += 2;
235+ if (!arg[0] && i < argc-1) {
236+ i++;
237+ int interval = -1;
238+
239+ if (sscanf(argv[i], "%d", &interval) == 1) {
240+ indicator.change_interval = interval;
241+ } else {
242+ printf("Invalid change interval value: %s\n", argv[i]);
243+ help = 1;
244+ }
245+ } else {
246+ printf("Invalid change interval value: %s\n", argv[i]);
247+ help = 1;
248+ }
249+ break;
250+ }
251+
194 case 'h':
252 case 'h':
195 help = 1;
253 help = 1;
196 break;
254 break;
@@ -201,8 +259,9 @@ main (int argc, char **argv)
201
259
202 if (help) {
260 if (help) {
203 printf("Usage: %s [<options>]\n"
261 printf("Usage: %s [<options>]\n"
204− " -t DELAY Action activation delay\n"
262+ " -t DELAY Action activation delay\n"
263+ " -c CHANGE_INTERVAL Interval to change action values\n"
205− " -h Show this help text\n"
264+ " -h Show this help text\n"
206 , argv[0]);
265 , argv[0]);
207 return 0;
266 return 0;
208 }
267 }
@@ -249,6 +308,11 @@ main (int argc, char **argv)
249 NULL);
308 NULL);
250
309
251 loop = g_main_loop_new (NULL, FALSE);
310 loop = g_main_loop_new (NULL, FALSE);
311+
312+ if (indicator.change_interval != -1) {
313+ g_timeout_add(indicator.change_interval, change_interval, &indicator);
314+ }
315+
252 g_main_loop_run (loop);
316 g_main_loop_run (loop);
253
317
254 g_object_unref (submenu);
318 g_object_unref (submenu);
tests/plugins/Dash/cardcreator/1.res+24−10
@@ -1,6 +1,5 @@
1AbstractButton {
1AbstractButton {
2 id: root;
2 id: root;
3− property var template;
4 property var components;
3 property var components;
5 property var cardData;
4 property var cardData;
6 property var artShapeBorderSource: undefined;
5 property var artShapeBorderSource: undefined;
@@ -13,7 +12,7 @@ AbstractButton {
13 property bool asynchronous: true;
12 property bool asynchronous: true;
14 property bool showHeader: true;
13 property bool showHeader: true;
15 implicitWidth: childrenRect.width;
14 implicitWidth: childrenRect.width;
16− enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
15+ enabled: true;
17
16
18onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
17onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
19readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
18readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
@@ -28,12 +27,29 @@ Item {
28 active: cardData && cardData["art"] || false;
27 active: cardData && cardData["art"] || false;
29 asynchronous: root.asynchronous;
28 asynchronous: root.asynchronous;
30 visible: status == Loader.Ready;
29 visible: status == Loader.Ready;
31− sourceComponent: UbuntuShape {
30+ sourceComponent: Item {
32− id: artShape;
31+ id: artShape;
33− objectName: "artShape";
32+ objectName: "artShape";
34− radius: "medium";
33+ property bool doShapeItem: components["art"]["conciergeMode"] !== true;
35− visible: image.status == Image.Ready;
34+ visible: image.status == Image.Ready;
35+ readonly property alias image: artImage.image;
36+ property alias borderSource: artShapeShape.borderSource;
37+ ShaderEffectSource {
38+ id: artShapeSource;
39+ sourceItem: artImage;
40+ anchors.centerIn: parent;
41+ width: 1;
42+ height: 1;
43+ hideSource: doShapeItem;
44+ }
45+ Shape {
46+ id: artShapeShape;
47+ image: artShapeSource;
48+ anchors.fill: parent;
49+ visible: doShapeItem;
50+ radius: "medium";
51+ }
36− readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
52+ readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
37 readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
53 readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
38 Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
54 Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
39 Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
55 Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
@@ -51,11 +67,9 @@ Item {
51 objectName: "artImage";
67 objectName: "artImage";
52 source: cardData && cardData["art"] || "";
68 source: cardData && cardData["art"] || "";
53 asynchronous: root.asynchronous;
69 asynchronous: root.asynchronous;
54− visible: false;
55 width: root.width;
70 width: root.width;
56 height: width / artShape.aspect;
71 height: width / artShape.aspect;
57 }
72 }
58− image: artImage.image;
59 }
73 }
60 }
74 }
61 }
75 }
tests/plugins/Dash/cardcreator/2.res+3−8
@@ -1,6 +1,5 @@
1AbstractButton {
1AbstractButton {
2 id: root;
2 id: root;
3− property var template;
4 property var components;
3 property var components;
5 property var cardData;
4 property var cardData;
6 property var artShapeBorderSource: undefined;
5 property var artShapeBorderSource: undefined;
@@ -13,7 +12,7 @@ AbstractButton {
13 property bool asynchronous: true;
12 property bool asynchronous: true;
14 property bool showHeader: true;
13 property bool showHeader: true;
15 implicitWidth: childrenRect.width;
14 implicitWidth: childrenRect.width;
16− enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
15+ enabled: true;
17
16
18Loader {
17Loader {
19 id: backgroundLoader;
18 id: backgroundLoader;
@@ -33,18 +32,14 @@ Loader {
33 objectName: "backgroundImage";
32 objectName: "backgroundImage";
34 source: {
33 source: {
35 if (cardData && typeof cardData["background"] === "string") return cardData["background"];
34 if (cardData && typeof cardData["background"] === "string") return cardData["background"];
36− else if (template && typeof template["card-background"] === "string") return template["card-background"];
35+ else return "";
37− else return "";
38 }
36 }
39 }
37 }
40 function getColor(index) {
38 function getColor(index) {
41 if (cardData && typeof cardData["background"] === "object"
39 if (cardData && typeof cardData["background"] === "object"
42 && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
40 && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
43 return cardData["background"]["elements"][index];
41 return cardData["background"]["elements"][index];
44− } else if (template && typeof template["card-background"] === "object"
42+ } else return index === 0 ? "#E9E9E9" : undefined;
45− && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
46− return template["card-background"]["elements"][index];
47− } else return undefined;
48 }
43 }
49 }
44 }
50 }
45 }
tests/plugins/Dash/cardcreator/3.res+34−20
@@ -1,6 +1,5 @@
1AbstractButton {
1AbstractButton {
2 id: root;
2 id: root;
3− property var template;
4 property var components;
3 property var components;
5 property var cardData;
4 property var cardData;
6 property var artShapeBorderSource: undefined;
5 property var artShapeBorderSource: undefined;
@@ -13,7 +12,7 @@ AbstractButton {
13 property bool asynchronous: true;
12 property bool asynchronous: true;
14 property bool showHeader: true;
13 property bool showHeader: true;
15 implicitWidth: childrenRect.width;
14 implicitWidth: childrenRect.width;
16− enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
15+ enabled: true;
17
16
18onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
17onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
19readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
18readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
@@ -28,34 +27,49 @@ Item {
28 active: cardData && cardData["art"] || false;
27 active: cardData && cardData["art"] || false;
29 asynchronous: root.asynchronous;
28 asynchronous: root.asynchronous;
30 visible: status == Loader.Ready;
29 visible: status == Loader.Ready;
31− sourceComponent: UbuntuShape {
30+ sourceComponent: Item {
32− id: artShape;
31+ id: artShape;
33− objectName: "artShape";
32+ objectName: "artShape";
34− radius: "medium";
33+ property bool doShapeItem: components["art"]["conciergeMode"] !== true;
35− visible: image.status == Image.Ready;
34+ visible: image.status == Image.Ready;
35+ readonly property alias image: artImage.image;
36+ property alias borderSource: artShapeShape.borderSource;
37+ ShaderEffectSource {
38+ id: artShapeSource;
39+ sourceItem: artImage;
40+ anchors.centerIn: parent;
41+ width: 1;
42+ height: 1;
43+ hideSource: doShapeItem;
44+ }
45+ Shape {
46+ id: artShapeShape;
47+ image: artShapeSource;
48+ anchors.fill: parent;
49+ visible: doShapeItem;
50+ radius: "medium";
51+ }
36− readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
52+ readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
37− readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
53+ readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
38− Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
54+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
39− Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
55+ Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
40− function updateWidthHeightBindings() {
56+ function updateWidthHeightBindings() {
41− if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
57+ if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
42− width = root.fixedArtShapeSize.width;
58+ width = root.fixedArtShapeSize.width;
43− height = root.fixedArtShapeSize.height;
59+ height = root.fixedArtShapeSize.height;
44− } else {
60+ } else {
45 width = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.width });
61 width = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.width });
46 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
62 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
47 }
63 }
48− }
64+ }
49 CroppedImageMinimumSourceSize {
65 CroppedImageMinimumSourceSize {
50 id: artImage;
66 id: artImage;
51 objectName: "artImage";
67 objectName: "artImage";
52− source: cardData && cardData["art"] || "";
68+ source: cardData && cardData["art"] || "";
53 asynchronous: root.asynchronous;
69 asynchronous: root.asynchronous;
54− visible: false;
55 width: root.width;
70 width: root.width;
56 height: width / artShape.aspect;
71 height: width / artShape.aspect;
57 }
72 }
58− image: artImage.image;
59 }
73 }
60 }
74 }
61 }
75 }
tests/plugins/Dash/cardcreator/4.res+1−2
@@ -1,6 +1,5 @@
1AbstractButton {
1AbstractButton {
2 id: root;
2 id: root;
3− property var template;
4 property var components;
3 property var components;
5 property var cardData;
4 property var cardData;
6 property var artShapeBorderSource: undefined;
5 property var artShapeBorderSource: undefined;
@@ -13,7 +12,7 @@ AbstractButton {
13 property bool asynchronous: true;
12 property bool asynchronous: true;
14 property bool showHeader: true;
13 property bool showHeader: true;
15 implicitWidth: childrenRect.width;
14 implicitWidth: childrenRect.width;
16− enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
15+ enabled: true;
17
16
18readonly property size artShapeSize: Qt.size(-1, -1);
17readonly property size artShapeSize: Qt.size(-1, -1);
19readonly property int headerHeight: row.height;
18readonly property int headerHeight: row.height;
tests/plugins/Dash/cardcreator/5.res+35−21
@@ -1,6 +1,5 @@
1AbstractButton {
1AbstractButton {
2 id: root;
2 id: root;
3− property var template;
4 property var components;
3 property var components;
5 property var cardData;
4 property var cardData;
6 property var artShapeBorderSource: undefined;
5 property var artShapeBorderSource: undefined;
@@ -13,7 +12,7 @@ AbstractButton {
13 property bool asynchronous: true;
12 property bool asynchronous: true;
14 property bool showHeader: true;
13 property bool showHeader: true;
15 implicitWidth: childrenRect.width;
14 implicitWidth: childrenRect.width;
16− enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
15+ enabled: false;
17
16
18onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
17onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
19readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
18readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
@@ -28,34 +27,49 @@ Item {
28 active: cardData && cardData["art"] || false;
27 active: cardData && cardData["art"] || false;
29 asynchronous: root.asynchronous;
28 asynchronous: root.asynchronous;
30 visible: status == Loader.Ready;
29 visible: status == Loader.Ready;
31− sourceComponent: UbuntuShape {
30+ sourceComponent: Item {
32− id: artShape;
31+ id: artShape;
33− objectName: "artShape";
32+ objectName: "artShape";
34− radius: "medium";
33+ property bool doShapeItem: components["art"]["conciergeMode"] !== true;
35− visible: image.status == Image.Ready;
34+ visible: image.status == Image.Ready;
35+ readonly property alias image: artImage.image;
36+ property alias borderSource: artShapeShape.borderSource;
37+ ShaderEffectSource {
38+ id: artShapeSource;
39+ sourceItem: artImage;
40+ anchors.centerIn: parent;
41+ width: 1;
42+ height: 1;
43+ hideSource: doShapeItem;
44+ }
45+ Shape {
46+ id: artShapeShape;
47+ image: artShapeSource;
48+ anchors.fill: parent;
49+ visible: doShapeItem;
50+ radius: "medium";
51+ }
36− readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
52+ readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
37− readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
53+ readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
38− Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
54+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
39− Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
55+ Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
40− function updateWidthHeightBindings() {
56+ function updateWidthHeightBindings() {
41− if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
57+ if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
42− width = root.fixedArtShapeSize.width;
58+ width = root.fixedArtShapeSize.width;
43− height = root.fixedArtShapeSize.height;
59+ height = root.fixedArtShapeSize.height;
44− } else {
60+ } else {
45 width = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.width });
61 width = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.width });
46 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
62 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
47 }
63 }
48− }
64+ }
49 CroppedImageMinimumSourceSize {
65 CroppedImageMinimumSourceSize {
50 id: artImage;
66 id: artImage;
51 objectName: "artImage";
67 objectName: "artImage";
52− source: cardData && cardData["art"] || "";
68+ source: cardData && cardData["art"] || "";
53 asynchronous: root.asynchronous;
69 asynchronous: root.asynchronous;
54− visible: false;
55 width: root.width;
70 width: root.width;
56 height: width / artShape.aspect;
71 height: width / artShape.aspect;
57 }
72 }
58− image: artImage.image;
59 }
73 }
60 }
74 }
61 }
75 }
@@ -150,5 +164,5 @@ UbuntuShape {
150 radius: "medium";
164 radius: "medium";
151 borderSource: "radius_pressed.sci"
165 borderSource: "radius_pressed.sci"
152}
166}
153−implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);
167+implicitHeight: artShapeHolder.height;
154}
168}
tests/plugins/Dash/cardcreator/5.tst+1−1
@@ -1,3 +1,3 @@
1−template: {"card-layout":"vertical","card-size":"medium","category-layout":"carousel","collapsed-rows":2,"overlay":true}
1+template: {"non-interactive":true,"card-layout":"vertical","card-size":"medium","category-layout":"carousel","collapsed-rows":2,"overlay":true}
2components: {"art":{"aspect-ratio":1,"field":"art"},"subtitle":{"field":"artist"},"title":{"field":"title"},"attributes":{}}
2components: {"art":{"aspect-ratio":1,"field":"art"},"subtitle":{"field":"artist"},"title":{"field":"title"},"attributes":{}}
3result: 5.res
3result: 5.res
tests/plugins/Dash/cardcreator/6.res+3−8
@@ -1,6 +1,5 @@
1AbstractButton {
1AbstractButton {
2 id: root;
2 id: root;
3− property var template;
4 property var components;
3 property var components;
5 property var cardData;
4 property var cardData;
6 property var artShapeBorderSource: undefined;
5 property var artShapeBorderSource: undefined;
@@ -13,7 +12,7 @@ AbstractButton {
13 property bool asynchronous: true;
12 property bool asynchronous: true;
14 property bool showHeader: true;
13 property bool showHeader: true;
15 implicitWidth: childrenRect.width;
14 implicitWidth: childrenRect.width;
16− enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
15+ enabled: true;
17
16
18Loader {
17Loader {
19 id: backgroundLoader;
18 id: backgroundLoader;
@@ -33,18 +32,14 @@ Loader {
33 objectName: "backgroundImage";
32 objectName: "backgroundImage";
34 source: {
33 source: {
35 if (cardData && typeof cardData["background"] === "string") return cardData["background"];
34 if (cardData && typeof cardData["background"] === "string") return cardData["background"];
36− else if (template && typeof template["card-background"] === "string") return template["card-background"];
35+ else return "http://assets.ubuntu.com/sites/ubuntu/latest/u/img/logos/logo-ubuntu-grey.png";
37− else return "";
38 }
36 }
39 }
37 }
40 function getColor(index) {
38 function getColor(index) {
41 if (cardData && typeof cardData["background"] === "object"
39 if (cardData && typeof cardData["background"] === "object"
42 && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
40 && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
43 return cardData["background"]["elements"][index];
41 return cardData["background"]["elements"][index];
44− } else if (template && typeof template["card-background"] === "object"
42+ } else return index === 0 ? undefined : undefined;
45− && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
46− return template["card-background"]["elements"][index];
47− } else return undefined;
48 }
43 }
49 }
44 }
50 }
45 }
tests/plugins/Dash/cardcreator/6.tst+1−1
@@ -1,3 +1,3 @@
1−template: {"card-background":{"elements":["#E9E9E9"],"type":"color"},"card-layout":"vertical","card-size":"medium","category-layout":"grid","collapsed-rows":2}
1+template: {"card-background":"http://assets.ubuntu.com/sites/ubuntu/latest/u/img/logos/logo-ubuntu-grey.png","card-layout":"vertical","card-size":"medium","category-layout":"grid","collapsed-rows":2}
2components: {"art":{"aspect-ratio":1},"background":{"field":"background"},"subtitle":{"field":"author"},"title":{"field":"title"},"emblem":{"field":"source"},"attributes":{}}
2components: {"art":{"aspect-ratio":1},"background":{"field":"background"},"subtitle":{"field":"author"},"title":{"field":"title"},"emblem":{"field":"source"},"attributes":{}}
3result: 6.res
3result: 6.res
tests/plugins/Dash/cardcreator/7.res+3−8
@@ -1,6 +1,5 @@
1AbstractButton {
1AbstractButton {
2 id: root;
2 id: root;
3− property var template;
4 property var components;
3 property var components;
5 property var cardData;
4 property var cardData;
6 property var artShapeBorderSource: undefined;
5 property var artShapeBorderSource: undefined;
@@ -13,7 +12,7 @@ AbstractButton {
13 property bool asynchronous: true;
12 property bool asynchronous: true;
14 property bool showHeader: true;
13 property bool showHeader: true;
15 implicitWidth: childrenRect.width;
14 implicitWidth: childrenRect.width;
16− enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
15+ enabled: true;
17
16
18Loader {
17Loader {
19 id: backgroundLoader;
18 id: backgroundLoader;
@@ -33,18 +32,14 @@ Loader {
33 objectName: "backgroundImage";
32 objectName: "backgroundImage";
34 source: {
33 source: {
35 if (cardData && typeof cardData["background"] === "string") return cardData["background"];
34 if (cardData && typeof cardData["background"] === "string") return cardData["background"];
36− else if (template && typeof template["card-background"] === "string") return template["card-background"];
35+ else return "";
37− else return "";
38 }
36 }
39 }
37 }
40 function getColor(index) {
38 function getColor(index) {
41 if (cardData && typeof cardData["background"] === "object"
39 if (cardData && typeof cardData["background"] === "object"
42 && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
40 && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
43 return cardData["background"]["elements"][index];
41 return cardData["background"]["elements"][index];
44− } else if (template && typeof template["card-background"] === "object"
42+ } else return index === 0 ? "#E9E9E9" : "#E9AAE9";
45− && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
46− return template["card-background"]["elements"][index];
47− } else return undefined;
48 }
43 }
49 }
44 }
50 }
45 }
tests/plugins/Dash/cardcreator/7.tst+1−1
@@ -1,3 +1,3 @@
1−template: {"card-background":{"elements":["#E9E9E9"],"type":"color"},"card-layout":"vertical","card-size":"medium","category-layout":"grid","collapsed-rows":2}
1+template: {"card-background":{"elements":["#E9E9E9","#E9AAE9"],"type":"color"},"card-layout":"vertical","card-size":"medium","category-layout":"grid","collapsed-rows":2}
2components: {"art":{"aspect-ratio":1},"background":{"field":"background"},"mascot":{"field":"icon"},"subtitle":{"field":"author"},"title":{"field":"title"},"attributes":{"field":"attributes","max-count":2}}
2components: {"art":{"aspect-ratio":1},"background":{"field":"background"},"mascot":{"field":"icon"},"subtitle":{"field":"author"},"title":{"field":"title"},"attributes":{"field":"attributes","max-count":2}}
3result: 7.res
3result: 7.res
tests/plugins/Dash/horizontaljournaltest.cpp+3−3
@@ -32,19 +32,19 @@
32
32
33class WidthModel : public QAbstractListModel {
33class WidthModel : public QAbstractListModel {
34public:
34public:
35− QHash<int, QByteArray> roleNames() const
35+ QHash<int, QByteArray> roleNames() const override
36 {
36 {
37 QHash<int, QByteArray> roles;
37 QHash<int, QByteArray> roles;
38 roles.insert(Qt::DisplayRole, "modelWidth");
38 roles.insert(Qt::DisplayRole, "modelWidth");
39 return roles;
39 return roles;
40 }
40 }
41
41
42− int rowCount(const QModelIndex & /*parent*/) const
42+ int rowCount(const QModelIndex & /*parent*/) const override
43 {
43 {
44 return m_list.count();
44 return m_list.count();
45 }
45 }
46
46
47− QVariant data(const QModelIndex &index, int /*role*/) const
47+ QVariant data(const QModelIndex &index, int /*role*/) const override
48 {
48 {
49 return m_list[index.row()];
49 return m_list[index.row()];
50 }
50 }
tests/plugins/Dash/horizontaljournaltry.cpp+1−1
@@ -29,7 +29,7 @@
29class QWidthModela : public QStringListModel {
29class QWidthModela : public QStringListModel {
30 Q_OBJECT
30 Q_OBJECT
31public:
31public:
32− QHash<int, QByteArray> roleNames() const
32+ QHash<int, QByteArray> roleNames() const override
33 {
33 {
34 QHash<int, QByteArray> roles;
34 QHash<int, QByteArray> roles;
35 roles.insert(Qt::DisplayRole, "modelWidth");
35 roles.insert(Qt::DisplayRole, "modelWidth");
tests/plugins/Dash/listviewwithpageheadersectionexternalmodeltest.cpp+3−3
@@ -37,7 +37,7 @@ public:
37 list << "A" << "B" << "C" << "D" << "E" << "F" << "G" << "H";
37 list << "A" << "B" << "C" << "D" << "E" << "F" << "G" << "H";
38 }
38 }
39
39
40− int rowCount(const QModelIndex &parent) const
40+ int rowCount(const QModelIndex &parent) const override
41 {
41 {
42 if (parent.isValid())
42 if (parent.isValid())
43 return 0;
43 return 0;
@@ -45,14 +45,14 @@ public:
45 return list.count();
45 return list.count();
46 }
46 }
47
47
48− QVariant data(const QModelIndex &index, int role) const
48+ QVariant data(const QModelIndex &index, int role) const override
49 {
49 {
50 if (role == Qt::SizeHintRole) return 50;
50 if (role == Qt::SizeHintRole) return 50;
51 else if (role == Qt::DisplayRole) return list[index.row()];
51 else if (role == Qt::DisplayRole) return list[index.row()];
52 return QVariant();
52 return QVariant();
53 }
53 }
54
54
55− QHash<int, QByteArray> roleNames() const
55+ QHash<int, QByteArray> roleNames() const override
56 {
56 {
57 QHash<int, QByteArray> hash;
57 QHash<int, QByteArray> hash;
58 hash.insert(Qt::DisplayRole, "type");
58 hash.insert(Qt::DisplayRole, "type");
tests/plugins/Dash/listviewwithpageheadertest.cpp+8−2
@@ -973,9 +973,15 @@ private Q_SLOTS:
973 QCOMPARE(lvwph->contentY(), 1258.);
973 QCOMPARE(lvwph->contentY(), 1258.);
974 QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
974 QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
975
975
976− changeContentY(-1700);
976+ changeContentY(-800);
977−
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);
978 QTRY_COMPARE(lvwph->m_visibleItems.count(), 12);
983 QTRY_COMPARE(lvwph->m_visibleItems.count(), 12);
984+
979 QCOMPARE(lvwph->m_firstVisibleIndex, 0);
985 QCOMPARE(lvwph->m_firstVisibleIndex, 0);
980 verifyItem(0, -308., 75., true);
986 verifyItem(0, -308., 75., true);
981 verifyItem(1, -233., 75., true);
987 verifyItem(1, -233., 75., true);
tests/plugins/Dash/organicgridtest.cpp+2−2
@@ -34,12 +34,12 @@ class DummyModel : public QAbstractListModel {
34public:
34public:
35 DummyModel() : m_count(0) {}
35 DummyModel() : m_count(0) {}
36
36
37− int rowCount(const QModelIndex & /*parent*/) const
37+ int rowCount(const QModelIndex & /*parent*/) const override
38 {
38 {
39 return m_count;
39 return m_count;
40 }
40 }
41
41
42− QVariant data(const QModelIndex & /*index*/, int /*role*/) const
42+ QVariant data(const QModelIndex & /*index*/, int /*role*/) const override
43 {
43 {
44 return QVariant();
44 return QVariant();
45 }
45 }
tests/plugins/Dash/organicgridtry.cpp+2−2
@@ -31,12 +31,12 @@ class DummyModel : public QAbstractListModel {
31public:
31public:
32 DummyModel() : m_count(0) {}
32 DummyModel() : m_count(0) {}
33
33
34− int rowCount(const QModelIndex & /*parent*/) const
34+ int rowCount(const QModelIndex & /*parent*/) const override
35 {
35 {
36 return m_count;
36 return m_count;
37 }
37 }
38
38
39− QVariant data(const QModelIndex & /*index*/, int /*role*/) const
39+ QVariant data(const QModelIndex & /*index*/, int /*role*/) const override
40 {
40 {
41 return QVariant();
41 return QVariant();
42 }
42 }
tests/plugins/Dash/verticaljournaltest.cpp+3−3
@@ -32,19 +32,19 @@
32
32
33class HeightModel : public QAbstractListModel {
33class HeightModel : public QAbstractListModel {
34public:
34public:
35− QHash<int, QByteArray> roleNames() const
35+ QHash<int, QByteArray> roleNames() const override
36 {
36 {
37 QHash<int, QByteArray> roles;
37 QHash<int, QByteArray> roles;
38 roles.insert(Qt::DisplayRole, "modelHeight");
38 roles.insert(Qt::DisplayRole, "modelHeight");
39 return roles;
39 return roles;
40 }
40 }
41
41
42− int rowCount(const QModelIndex & /*parent*/) const
42+ int rowCount(const QModelIndex & /*parent*/) const override
43 {
43 {
44 return m_list.count();
44 return m_list.count();
45 }
45 }
46
46
47− QVariant data(const QModelIndex &index, int /*role*/) const
47+ QVariant data(const QModelIndex &index, int /*role*/) const override
48 {
48 {
49 return m_list[index.row()];
49 return m_list[index.row()];
50 }
50 }
tests/plugins/Dash/verticaljournaltry.cpp+1−1
@@ -29,7 +29,7 @@
29class QHeightModel : public QStringListModel {
29class QHeightModel : public QStringListModel {
30 Q_OBJECT
30 Q_OBJECT
31public:
31public:
32− QHash<int, QByteArray> roleNames() const
32+ QHash<int, QByteArray> roleNames() const override
33 {
33 {
34 QHash<int, QByteArray> roles;
34 QHash<int, QByteArray> roles;
35 roles.insert(Qt::DisplayRole, "modelHeight");
35 roles.insert(Qt::DisplayRole, "modelHeight");
tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,5 +1,5 @@
1pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
2−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
2+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
3
3
4include_directories(
4include_directories(
5 ${CMAKE_CURRENT_SOURCE_DIR}
5 ${CMAKE_CURRENT_SOURCE_DIR}
tests/plugins/LightDM/CMakeLists.txt+35−16
@@ -1,8 +1,38 @@
1+add_definitions(
2+ -DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}"
3+ )
4+include_directories(
5+ ${CMAKE_CURRENT_BINARY_DIR}
6+ )
7+
1add_executable(GreeterDBusTestExec
8add_executable(GreeterDBusTestExec
2 dbus.cpp
9 dbus.cpp
3 ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
10 ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
4 )
11 )
5qt5_use_modules(GreeterDBusTestExec Core DBus Quick Test)
12qt5_use_modules(GreeterDBusTestExec Core DBus Quick Test)
13+target_link_libraries(GreeterDBusTestExec
14+ -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm
15+ -llightdm-qt5-2
16+ )
17+target_include_directories(GreeterDBusTestExec PUBLIC
18+ ${CMAKE_SOURCE_DIR}/plugins/LightDM
19+ ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM
20+ )
21+add_binary_qml_test(GreeterDBus "${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm" MockLightDM "QML2_IMPORT_PATH=${CMAKE_BINARY_DIR}/tests/mocks")
22+
23+add_executable(GreeterPamTestExec
24+ pam.cpp
25+ ${CMAKE_SOURCE_DIR}/plugins/LightDM/liblightdm/GreeterPrivate.cpp
26+ )
27+qt5_use_modules(GreeterPamTestExec Concurrent Core Test)
28+target_link_libraries(GreeterPamTestExec
29+ integratedLightDM
30+ )
31+target_include_directories(GreeterPamTestExec PUBLIC
32+ ${CMAKE_SOURCE_DIR}/plugins/LightDM/liblightdm
33+ )
34+add_qmltest_target(testGreeterPam "${CMAKE_CURRENT_BINARY_DIR}/GreeterPamTestExec" TRUE FALSE)
35+add_dependencies(testGreeterPam GreeterPamTestExec)
6
36
7add_executable(GreeterUsersModelTestExec
37add_executable(GreeterUsersModelTestExec
8 usersmodel.cpp
38 usersmodel.cpp
@@ -10,24 +40,13 @@ add_executable(GreeterUsersModelTestExec
10 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
40 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
11 )
41 )
12qt5_use_modules(GreeterUsersModelTestExec Core Test)
42qt5_use_modules(GreeterUsersModelTestExec Core Test)
13−
43+target_link_libraries(GreeterUsersModelTestExec
14−include_directories(
44+ -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm
15− ${CMAKE_CURRENT_BINARY_DIR}
45+ -llightdm-qt5-2
46+ )
47+target_include_directories(GreeterUsersModelTestExec PUBLIC
16 ${CMAKE_SOURCE_DIR}/plugins/LightDM
48 ${CMAKE_SOURCE_DIR}/plugins/LightDM
17 ${CMAKE_SOURCE_DIR}/plugins/Utils
49 ${CMAKE_SOURCE_DIR}/plugins/Utils
18 ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM
50 ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM
19 )
51 )
20−
21−target_link_libraries(GreeterDBusTestExec
22− MockLightDM
23− )
24−
25−target_link_libraries(GreeterUsersModelTestExec
26− MockLightDM
27− )
28−
29−add_definitions(-DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
30−
31−add_binary_qml_test(GreeterDBus "${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm" MockLightDM "QML2_IMPORT_PATH=${CMAKE_BINARY_DIR}/tests/mocks")
32−
33add_binary_qml_test(GreeterUsersModel "${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm" MockLightDM "LIBLIGHTDM_MOCK_MODE=full")
52add_binary_qml_test(GreeterUsersModel "${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm" MockLightDM "LIBLIGHTDM_MOCK_MODE=full")
tests/plugins/LightDM/pam.cppnew file+52
@@ -0,0 +1,52 @@
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 "GreeterPrivate.h"
18+
19+#include <QtTest>
20+
21+class GreeterPamTest : public QObject
22+{
23+ Q_OBJECT
24+
25+private Q_SLOTS:
26+
27+ void init()
28+ {
29+ m_greeterpriv = new QLightDM::GreeterPrivate();
30+ }
31+
32+ void cleanup()
33+ {
34+ delete m_greeterpriv;
35+ QTRY_COMPARE(QThreadPool::globalInstance()->activeThreadCount(), 0);
36+ }
37+
38+ void testRapidFireAuthentication()
39+ {
40+ m_greeterpriv->authenticationUser = qgetenv("USER");
41+ for (int i = 0; i < 100; i++) {
42+ m_greeterpriv->handleAuthenticate();
43+ }
44+ }
45+
46+private:
47+ QLightDM::GreeterPrivate *m_greeterpriv;
48+};
49+
50+QTEST_GUILESS_MAIN(GreeterPamTest)
51+
52+#include "pam.moc"
tests/plugins/Ubuntu/Gestures/CMakeLists.txt+5−2
@@ -19,14 +19,16 @@ add_custom_target(UbuntuGesturesTestQmlFiles ALL
19)
19)
20
20
21add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
21add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
22+add_definitions(-DTESTS_UTILS_MODULES_DIR="${CMAKE_BINARY_DIR}/tests/utils/modules")
22
23
23macro(add_gesture_ui_test CLASSNAME)
24macro(add_gesture_ui_test CLASSNAME)
24− add_executable(${CLASSNAME}TestExec tst_${CLASSNAME}.cpp GestureTest.cpp)
25+ add_executable(${CLASSNAME}TestExec tst_${CLASSNAME}.cpp GestureTest.cpp TestItem.cpp)
25 qt5_use_modules(${CLASSNAME}TestExec Test Core Qml Gui Quick)
26 qt5_use_modules(${CLASSNAME}TestExec Test Core Qml Gui Quick)
26 target_link_libraries(${CLASSNAME}TestExec UbuntuGesturesQml UbuntuGestures)
27 target_link_libraries(${CLASSNAME}TestExec UbuntuGesturesQml UbuntuGestures)
27
28
28 add_binary_qml_test(${CLASSNAME} ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures UbuntuGesturesTestQmlFiles "")
29 add_binary_qml_test(${CLASSNAME} ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures UbuntuGesturesTestQmlFiles "")
29− add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
30+ add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins
31+ ${CMAKE_BINARY_DIR}/tests/utils/modules)
30endmacro(add_gesture_ui_test)
32endmacro(add_gesture_ui_test)
31
33
32macro(add_gesture_test CLASSNAME)
34macro(add_gesture_test CLASSNAME)
@@ -44,6 +46,7 @@ macro(add_gesture_test CLASSNAME)
44endmacro(add_gesture_test)
46endmacro(add_gesture_test)
45
47
46add_gesture_ui_test(DirectionalDragArea)
48add_gesture_ui_test(DirectionalDragArea)
49+add_gesture_ui_test(FloatingFlickable)
47add_gesture_ui_test(PressedOutsideNotifier)
50add_gesture_ui_test(PressedOutsideNotifier)
48add_gesture_ui_test(TouchDispatcher)
51add_gesture_ui_test(TouchDispatcher)
49add_gesture_ui_test(TouchGate)
52add_gesture_ui_test(TouchGate)
tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml+5−19
@@ -37,8 +37,8 @@ Item {
37
37
38 Rectangle {
38 Rectangle {
39 id: dragAreaRect
39 id: dragAreaRect
40− color: "yellow"
41− opacity: 0.0
40+ opacity: dragArea.dragging ? 0.5 : 0.0
41+ color: "green"
42 anchors.fill: dragArea
42 anchors.fill: dragArea
43 }
43 }
44
44
@@ -49,24 +49,10 @@ Item {
49 height: units.gu(5)
49 height: units.gu(5)
50
50
51 direction: Direction.Downwards
51 direction: Direction.Downwards
52− maxDeviation: units.gu(2)
53− wideningAngle: 10
54− distanceThreshold: units.gu(4)
55
52
56− onStatusChanged: {
53+ onDraggingChanged: {
57− switch (status) {
54+ if (dragging) {
58− case DirectionalDragArea.WaitingForTouch:
59− dragAreaRect.opacity = 0.0
60− break;
61− case DirectionalDragArea.Undecided:
62− dragAreaRect.color = "yellow"
63− dragAreaRect.opacity = 0.3
64− launcher.y = Qt.binding(launcher.followDragArea)
55+ launcher.y = Qt.binding(launcher.followDragArea)
65− break;
66− default: // DirectionalDragArea.Recognized:
67− dragAreaRect.color = "green"
68− dragAreaRect.opacity = 0.5
69− break;
70 }
56 }
71 }
57 }
72
58
tests/plugins/Ubuntu/Gestures/GestureTest.cpp+14−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -21,8 +21,11 @@
21#include <QQuickView>
21#include <QQuickView>
22#include <QtTest>
22#include <QtTest>
23
23
24+#include <Timer.h>
24#include <TouchRegistry.h>
25#include <TouchRegistry.h>
25
26
27+using namespace UbuntuGestures;
28+
26GestureTest::GestureTest(const QString &qmlFilename)
29GestureTest::GestureTest(const QString &qmlFilename)
27 : QObject(), m_device(nullptr), m_view(nullptr), m_qmlFilename(qmlFilename)
30 : QObject(), m_device(nullptr), m_view(nullptr), m_qmlFilename(qmlFilename)
28{
31{
@@ -40,14 +43,18 @@ void GestureTest::initTestCase()
40void GestureTest::init()
43void GestureTest::init()
41{
44{
42 m_view = new QQuickView;
45 m_view = new QQuickView;
43− m_view->setResizeMode(QQuickView::SizeViewToRootObject);
46+ m_view->setResizeMode(QQuickView::SizeRootObjectToView);
44 m_view->engine()->addImportPath(QStringLiteral(UBUNTU_GESTURES_PLUGIN_DIR));
47 m_view->engine()->addImportPath(QStringLiteral(UBUNTU_GESTURES_PLUGIN_DIR));
48+ m_view->engine()->addImportPath(QStringLiteral(TESTS_UTILS_MODULES_DIR));
45 m_view->setSource(QUrl::fromLocalFile(m_qmlFilename));
49 m_view->setSource(QUrl::fromLocalFile(m_qmlFilename));
46 m_view->show();
50 m_view->show();
47 QVERIFY(QTest::qWaitForWindowExposed(m_view));
51 QVERIFY(QTest::qWaitForWindowExposed(m_view));
48 QVERIFY(m_view->rootObject() != 0);
52 QVERIFY(m_view->rootObject() != 0);
49
53
50− m_touchRegistry = new TouchRegistry;
54+ m_fakeTimerFactory = new FakeTimerFactory;
55+
56+ m_touchRegistry = TouchRegistry::instance();
57+ m_touchRegistry->setTimerFactory(m_fakeTimerFactory);
51 m_view->installEventFilter(m_touchRegistry);
58 m_view->installEventFilter(m_touchRegistry);
52
59
53 qApp->processEvents();
60 qApp->processEvents();
@@ -59,6 +66,10 @@ void GestureTest::cleanup()
59 delete m_touchRegistry;
66 delete m_touchRegistry;
60 m_touchRegistry = nullptr;
67 m_touchRegistry = nullptr;
61
68
69+ // TouchRegistry will take down the timer factory along with him
70+ // delete m_fakeTimerFactory;
71+ m_fakeTimerFactory = nullptr;
72+
62 delete m_view;
73 delete m_view;
63 m_view = nullptr;
74 m_view = nullptr;
64}
75}
tests/plugins/Ubuntu/Gestures/GestureTest.h+4
@@ -23,6 +23,9 @@
23class QQuickView;
23class QQuickView;
24class QTouchDevice;
24class QTouchDevice;
25
25
26+namespace UbuntuGestures {
27+ class FakeTimerFactory;
28+}
26class TouchRegistry;
29class TouchRegistry;
27
30
28// C++ std lib
31// C++ std lib
@@ -81,6 +84,7 @@ protected:
81 QTouchDevice *m_device;
84 QTouchDevice *m_device;
82 QQuickView *m_view;
85 QQuickView *m_view;
83 TouchRegistry *m_touchRegistry;
86 TouchRegistry *m_touchRegistry;
87+ UbuntuGestures::FakeTimerFactory *m_fakeTimerFactory;
84 QString m_qmlFilename;
88 QString m_qmlFilename;
85};
89};
86
90
tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml+6−19
@@ -21,7 +21,7 @@ import Ubuntu.Components 0.1
21Item {
21Item {
22 id: root
22 id: root
23
23
24− function reset() { launcher.x = root.width }
24+ function reset() { launcher.x = Qt.binding(function(){return root.width;}); }
25
25
26 Rectangle {
26 Rectangle {
27 id: launcher
27 id: launcher
@@ -41,7 +41,8 @@ Item {
41
41
42 Rectangle {
42 Rectangle {
43 id: dragAreaRect
43 id: dragAreaRect
44− opacity: 0.0
44+ opacity: dragArea.dragging ? 0.5 : 0.0
45+ color: "green"
45 anchors.fill: dragArea
46 anchors.fill: dragArea
46 }
47 }
47
48
@@ -52,24 +53,10 @@ Item {
52 width: units.gu(5)
53 width: units.gu(5)
53
54
54 direction: Direction.Leftwards
55 direction: Direction.Leftwards
55− maxDeviation: units.gu(2)
56− wideningAngle: 10
57− distanceThreshold: units.gu(4)
58
56
59− onStatusChanged: {
57+ onDraggingChanged: {
60− switch (status) {
58+ if (dragging) {
61− case DirectionalDragArea.WaitingForTouch:
62− dragAreaRect.opacity = 0.0
63− break;
64− case DirectionalDragArea.Undecided:
65− dragAreaRect.color = "yellow"
66− dragAreaRect.opacity = 0.3
67− launcher.x = Qt.binding(launcher.followDragArea)
59+ launcher.x = Qt.binding(launcher.followDragArea)
68− break;
69− default: //case DirectionalDragArea.Recognized:
70− dragAreaRect.color = "green"
71− dragAreaRect.opacity = 0.5
72− break;
73 }
60 }
74 }
61 }
75
62
tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml+5−19
@@ -38,7 +38,8 @@ Item {
38
38
39 Rectangle {
39 Rectangle {
40 id: dragAreaRect
40 id: dragAreaRect
41− opacity: 0.0
41+ opacity: dragArea.dragging ? 0.5 : 0.0
42+ color: "green"
42 anchors.fill: dragArea
43 anchors.fill: dragArea
43 }
44 }
44
45
@@ -52,25 +53,10 @@ Item {
52 width: units.gu(5)
53 width: units.gu(5)
53
54
54 direction: Direction.Rightwards
55 direction: Direction.Rightwards
55− maxDeviation: units.gu(2)
56− wideningAngle: 10
57− distanceThreshold: units.gu(4)
58− minSpeed: 50
59
56
60− onStatusChanged: {
57+ onDraggingChanged: {
61− switch (status) {
58+ if (dragging) {
62− case DirectionalDragArea.WaitingForTouch:
63− dragAreaRect.opacity = 0.0
64− break;
65− case DirectionalDragArea.Undecided:
66− dragAreaRect.color = "yellow"
67− dragAreaRect.opacity = 0.3
68− launcher.x = Qt.binding(launcher.followDragArea)
59+ launcher.x = Qt.binding(launcher.followDragArea)
69− break;
70− default: //case DirectionalDragArea.Recognized:
71− dragAreaRect.color = "green"
72− dragAreaRect.opacity = 0.5
73− break;
74 }
60 }
75 }
61 }
76
62
tests/plugins/Ubuntu/Gestures/TestItem.cppnew file+30
@@ -0,0 +1,30 @@
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 "TestItem.h"
18+
19+#include <QTouchEvent>
20+
21+void TestItem::touchEvent(QTouchEvent *event)
22+{
23+ QTouchEvent *clonedEvent = new QTouchEvent(event->type(),
24+ event->device(),
25+ event->modifiers(),
26+ event->touchPointStates(),
27+ event->touchPoints());
28+
29+ touchEventsReceived.append(QSharedPointer<QTouchEvent>(clonedEvent));
30+}
tests/plugins/Ubuntu/Gestures/TestItem.hnew file+33
@@ -0,0 +1,33 @@
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 UBUNTUGESTURES_TEST_ITEM_H
18+#define UBUNTUGESTURES_TEST_ITEM_H
19+
20+#include <QQuickItem>
21+
22+class TestItem : public QQuickItem
23+{
24+ Q_OBJECT
25+
26+public:
27+ QList<QSharedPointer<QTouchEvent>> touchEventsReceived;
28+
29+protected:
30+ void touchEvent(QTouchEvent *event) override;
31+};
32+
33+#endif // UBUNTUGESTURES_TEST_ITEM_H
tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml+6−19
@@ -21,7 +21,7 @@ import Ubuntu.Components 0.1
21Item {
21Item {
22 id: root
22 id: root
23
23
24− function reset() { launcher.y = root.height }
24+ function reset() { launcher.y = Qt.binding(function(){return root.height;}); }
25
25
26 Rectangle {
26 Rectangle {
27 id: launcher
27 id: launcher
@@ -41,7 +41,8 @@ Item {
41
41
42 Rectangle {
42 Rectangle {
43 id: dragAreaRect
43 id: dragAreaRect
44− opacity: 0.0
44+ opacity: dragArea.dragging ? 0.5 : 0.0
45+ color: "green"
45 anchors.fill: dragArea
46 anchors.fill: dragArea
46 }
47 }
47
48
@@ -52,24 +53,10 @@ Item {
52 height: units.gu(5)
53 height: units.gu(5)
53
54
54 direction: Direction.Upwards
55 direction: Direction.Upwards
55− maxDeviation: units.gu(2)
56− wideningAngle: 10
57− distanceThreshold: units.gu(4)
58
56
59− onStatusChanged: {
57+ onDraggingChanged: {
60− switch (status) {
58+ if (dragging) {
61− case DirectionalDragArea.WaitingForTouch:
62− dragAreaRect.opacity = 0.0
63− break;
64− case DirectionalDragArea.Undecided:
65− dragAreaRect.color = "yellow"
66− dragAreaRect.opacity = 0.3
67− launcher.y = Qt.binding(launcher.followDragArea)
59+ launcher.y = Qt.binding(launcher.followDragArea)
68− break;
69− default: //case DirectionalDragArea.Recognized:
70− dragAreaRect.color = "green"
71− dragAreaRect.opacity = 0.5
72− break;
73 }
60 }
74 }
61 }
75
62
tests/plugins/Ubuntu/Gestures/tst_AxisVelocityCalculator.cpp+1−1
@@ -24,7 +24,7 @@ class FakeTimeSource : public UbuntuGestures::TimeSource {
24public:
24public:
25 FakeTimeSource() : m_value(0) {}
25 FakeTimeSource() : m_value(0) {}
26
26
27− virtual qint64 msecsSinceReference() {
27+ qint64 msecsSinceReference() override {
28 return m_value;
28 return m_value;
29 }
29 }
30
30
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp+481−446
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -20,51 +20,38 @@
20#include <QtQml/QQmlEngine>
20#include <QtQml/QQmlEngine>
21#include <QPointer>
21#include <QPointer>
22#include <private/qquickmousearea_p.h>
22#include <private/qquickmousearea_p.h>
23−#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
24− #include <private/qquickwindow_p.h>
23+#include <private/qquickwindow_p.h>
25−#endif
26
24
27
25
28#include <DirectionalDragArea.h>
26#include <DirectionalDragArea.h>
27+#include <DirectionalDragArea_p.h>
29#include <TouchRegistry.h>
28#include <TouchRegistry.h>
30
29
31#include "GestureTest.h"
30#include "GestureTest.h"
32
31
33using namespace UbuntuGestures;
32using namespace UbuntuGestures;
34
33
35−
34+// Because QSignalSpy(directionalDragArea, SIGNAL(DirectionalDragArea::Status)) simply
36−class ComplexFakeTimer : public FakeTimer
35+// doesn't work
37−{
36+class StatusSpy : public QObject {
38 Q_OBJECT
37 Q_OBJECT
39public:
38public:
40− ComplexFakeTimer(const SharedTimeSource &timeSource, QObject *parent = 0)
39+ StatusSpy(DirectionalDragArea *edgeDragArea) {
41− : FakeTimer(parent),
40+ m_recognized = false;
42− m_timeSource(timeSource)
41+ connect(edgeDragArea->d, &DirectionalDragAreaPrivate::statusChanged,
43− {}
42+ this, &StatusSpy::onStatusChanged);
44−
45− void start() override {
46− AbstractTimer::start();
47− m_nextTimeoutTime = m_timeSource->msecsSinceReference() + (qint64)interval();
48 }
43 }
49−
44+ bool recognized() {
50− void emitTimeout() {
45+ return m_recognized;
51− m_nextTimeoutTime += interval();
52− Q_EMIT timeout();
53 }
46 }
54
47
55− qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
48+private Q_SLOTS:
49+ void onStatusChanged(DirectionalDragAreaPrivate::Status status) {
50+ m_recognized |= status == DirectionalDragAreaPrivate::Recognized;
51+ }
56
52
57private:
53private:
58− SharedTimeSource m_timeSource;
54+ bool m_recognized;
59− qint64 m_nextTimeoutTime;
60−};
61−
62−class FakeTimeSource : public UbuntuGestures::TimeSource
63−{
64−public:
65− FakeTimeSource() { m_msecsSinceReference = 0; }
66− virtual qint64 msecsSinceReference() {return m_msecsSinceReference;}
67− qint64 m_msecsSinceReference;
68};
55};
69
56
70/*
57/*
@@ -97,16 +84,10 @@ class tst_DirectionalDragArea: public GestureTest
97public:
84public:
98 tst_DirectionalDragArea();
85 tst_DirectionalDragArea();
99private Q_SLOTS:
86private Q_SLOTS:
100− void init(); // called right before each and every test function is executed
87+ void init() override; // called right before each and every test function is executed
101− void cleanup(); // called right after each and every test function is executed
102
88
103− void edgeDrag();
104− void edgeDrag_data();
105 void dragWithShortDirectionChange();
89 void dragWithShortDirectionChange();
106− void minSpeed();
107− void minSpeed_data();
108 void recognitionTimerUsage();
90 void recognitionTimerUsage();
109− void maxSilenceTime();
110 void sceneXAndX();
91 void sceneXAndX();
111 void sceneYAndY();
92 void sceneYAndY();
112 void twoFingerTap();
93 void twoFingerTap();
@@ -122,14 +103,24 @@ private Q_SLOTS:
122 void immediateRecognitionWhenConstraintsDisabled();
103 void immediateRecognitionWhenConstraintsDisabled();
123 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition();
104 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition();
124 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition_data();
105 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition_data();
125− void tappedSignal();
126− void tappedSignal_data();
127 void gettingTouchOwnershipMakesMouseAreaBehindGetCanceled();
106 void gettingTouchOwnershipMakesMouseAreaBehindGetCanceled();
107+ void interleavedTouches();
108+ void makoRightEdgeDrag();
109+ void makoRightEdgeDrag_verticalDownwards();
110+ void makoLeftEdgeDrag_slowStart();
111+ void makoLeftEdgeDrag_movesSlightlyBackwardsOnStart();
128
112
129private:
113private:
114+ // QTest::touchEvent takes QPoint instead of QPointF and I don't want to
115+ // lose precision due to rounding.
116+ // Besides, those helper functions lead to more compact code.
117+ void sendTouchPress(qint64 timestamp, int id, QPointF pos);
118+ void sendTouchUpdate(qint64 timestamp, int id, QPointF pos);
119+ void sendTouchRelease(qint64 timestamp, int id, QPointF pos);
120+ void sendTouch(qint64 timestamp, int id, QPointF pos,
121+ Qt::TouchPointState pointState, QEvent::Type eventType);
122+
130 void passTime(qint64 timeSpanMs);
123 void passTime(qint64 timeSpanMs);
131− ComplexFakeTimer *fakeTimer;
132− QSharedPointer<FakeTimeSource> fakeTimeSource;
133};
124};
134
125
135tst_DirectionalDragArea::tst_DirectionalDragArea()
126tst_DirectionalDragArea::tst_DirectionalDragArea()
@@ -148,185 +139,57 @@ void tst_DirectionalDragArea::init()
148 m_view->resize(m_view->rootObject()->width(), m_view->rootObject()->height());
139 m_view->resize(m_view->rootObject()->width(), m_view->rootObject()->height());
149 QTRY_COMPARE(m_view->width(), (int)m_view->rootObject()->width());
140 QTRY_COMPARE(m_view->width(), (int)m_view->rootObject()->width());
150 QTRY_COMPARE(m_view->height(), (int)m_view->rootObject()->height());
141 QTRY_COMPARE(m_view->height(), (int)m_view->rootObject()->height());
151−
152− fakeTimeSource.reset(new FakeTimeSource);
153− fakeTimer = new ComplexFakeTimer(fakeTimeSource);
154−}
155−
156−void tst_DirectionalDragArea::cleanup()
157−{
158− delete fakeTimer;
159− fakeTimer = 0;
160−
161− fakeTimeSource.reset();
162−
163− GestureTest::cleanup();
164−}
165−
166−void tst_DirectionalDragArea::passTime(qint64 timeSpanMs)
167−{
168− qint64 finalTime = fakeTimeSource->m_msecsSinceReference + timeSpanMs;
169−
170− if (fakeTimer->isRunning() && finalTime >= fakeTimer->nextTimeoutTime()) {
171− fakeTimeSource->m_msecsSinceReference = fakeTimer->nextTimeoutTime();
172− fakeTimer->emitTimeout();
173−
174− qint64 timeSpanRemainder = finalTime - fakeTimeSource->m_msecsSinceReference;
175− if (timeSpanRemainder > 0) {
176− passTime(timeSpanRemainder);
177− }
178− } else {
179− fakeTimeSource->m_msecsSinceReference = finalTime;
180− }
181}
142}
182
143
183−namespace {
144+void tst_DirectionalDragArea::sendTouchPress(qint64 timestamp, int id, QPointF pos)
184−QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea)
185{
145{
186− QPointF localCenter(edgeDragArea->width() / 2., edgeDragArea->height() / 2.);
146+ sendTouch(timestamp, id, pos, Qt::TouchPointPressed, QEvent::TouchBegin);
187− return edgeDragArea->mapToScene(localCenter);
188}
147}
189
148
190−QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea,
149+void tst_DirectionalDragArea::sendTouchUpdate(qint64 timestamp, int id, QPointF pos)
191− qreal wideningAngleMultiplier)
192{
150{
193− qreal angleRadians = edgeDragArea->wideningAngle() * wideningAngleMultiplier
151+ sendTouch(timestamp, id, pos, Qt::TouchPointMoved, QEvent::TouchUpdate);
194− * M_PI / 180.0;
195−
196− qreal angleCos = qCos(angleRadians);
197− qreal angleSin = qSin(angleRadians);
198−
199− switch (edgeDragArea->direction()) {
200− case Direction::Upwards:
201− return QPointF(angleSin, -angleCos);
202− case Direction::Downwards:
203− return QPointF(angleSin, angleCos);
204− case Direction::Leftwards:
205− return QPointF(-angleCos, angleSin);
206− default: // Direction::Rightwards:
207− return QPointF(angleCos, angleSin);
208− }
209}
152}
210
153
211−QPointF createTouchDeviation(DirectionalDragArea *edgeDragArea)
154+void tst_DirectionalDragArea::sendTouchRelease(qint64 timestamp, int id, QPointF pos)
212{
155{
213− qreal deviation = edgeDragArea->maxDeviation() * 0.8;
156+ sendTouch(timestamp, id, pos, Qt::TouchPointReleased, QEvent::TouchEnd);
214−
215− if (Direction::isHorizontal(edgeDragArea->direction())) {
216− return QPointF(0, deviation);
217− } else {
218− return QPointF(deviation, 0);
219− }
220−}
221}
157}
222
158
223−void tst_DirectionalDragArea::edgeDrag()
159+void tst_DirectionalDragArea::sendTouch(qint64 timestamp, int id, QPointF pos,
160+ Qt::TouchPointState pointState, QEvent::Type eventType)
224{
161{
225− QFETCH(QString, dragAreaObjectName);
162+ m_fakeTimerFactory->updateTime(timestamp);
226− QFETCH(qreal, wideningAngleMultiplier);
227− QFETCH(qreal, dragDistanceFactor);
228− QFETCH(bool, expectGestureRecognition);
229−
230− DirectionalDragArea *edgeDragArea =
231− m_view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
232− QVERIFY(edgeDragArea != 0);
233− edgeDragArea->setRecognitionTimer(fakeTimer);
234− edgeDragArea->setTimeSource(fakeTimeSource);
235−
236− QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
237−
238− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
239− QPointF touchPoint = initialTouchPos;
240−
241− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
242− QPointF dragDirectionVector = calculateDirectionVector(edgeDragArea,
243− wideningAngleMultiplier);
244− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
245− QPointF touchMovement = dragDirectionVector * movementStepDistance;
246− int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
247− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
248−
249− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
250−
251− QCOMPARE(draggingSpy.count(), 1);
252− QCOMPARE(edgeDragArea->dragging(), true);
253−
254− if (wideningAngleMultiplier > 0) {
255− // go close to the border of the valid area for this touch point
256− // in order to make it easier to leave it by dragging at an angle
257− // slightly bigger than the widening angle
258− touchPoint += createTouchDeviation(edgeDragArea);
259− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
260− }
261
163
262− for (int i = 0; i < totalMovementSteps; ++i) {
164+ QTouchEvent::TouchPoint point;
263− touchPoint += touchMovement;
264− passTime(movementTimeStepMs);
265− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
266− }
267
165
268− if (expectGestureRecognition)
166+ point.setState(pointState);
269− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
167+ point.setId(id);
168+ point.setScenePos(pos);
169+ point.setPos(pos);
270
170
271− if (edgeDragArea->status() == DirectionalDragArea::WaitingForTouch) {
171+ QList<QTouchEvent::TouchPoint> points;
272− QCOMPARE(edgeDragArea->dragging(), false);
172+ points << point;
273− QCOMPARE(draggingSpy.count(), 2);
274− }
275
173
276− QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
174+ QTouchEvent touchEvent(eventType, m_device, Qt::NoModifier, Qt::TouchPointPressed, points);
175+ QCoreApplication::sendEvent(m_view, &touchEvent);
277
176
278− QCOMPARE(draggingSpy.count(), 2);
177+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
279− QCOMPARE(edgeDragArea->dragging(), false);
178+ windowPrivate->flushDelayedTouchEvent();
280}
179}
281
180
282−void tst_DirectionalDragArea::edgeDrag_data()
181+void tst_DirectionalDragArea::passTime(qint64 timeSpanMs)
283{
182{
284− QTest::addColumn<QString>("dragAreaObjectName");
183+ qint64 finalTime = m_fakeTimerFactory->timeSource()->msecsSinceReference() + timeSpanMs;
285− QTest::addColumn<qreal>("wideningAngleMultiplier");
184+ m_fakeTimerFactory->updateTime(finalTime);
286− QTest::addColumn<qreal>("dragDistanceFactor");
185+}
287− QTest::addColumn<bool>("expectGestureRecognition");
288−
289− QTest::newRow("rightwards, tiny drag")
290− << "hpDragArea" << 0.0 << 0.2 << false;
291−
292− QTest::newRow("rightwards, straight drag")
293− << "hpDragArea" << 0.0 << 3.0 << true;
294−
295− QTest::newRow("rightwards, diagonal drag")
296− << "hpDragArea" << 0.9 << 3.0 << true;
297−
298− QTest::newRow("rightwards, overly diagonal drag")
299− << "hpDragArea" << 2.0 << 3.0 << false;
300−
301− QTest::newRow("leftwards, tiny drag")
302− << "hnDragArea" << 0.0 << 0.2 << false;
303−
304− QTest::newRow("leftwards, straight drag")
305− << "hnDragArea" << 0.0 << 3.0 << true;
306−
307− QTest::newRow("leftwards, diagonal drag")
308− << "hnDragArea" << 0.9 << 3.0 << true;
309−
310− QTest::newRow("downwards, tiny drag")
311− << "vpDragArea" << 0.0 << 0.2 << false;
312−
313− QTest::newRow("downwards, straight drag")
314− << "vpDragArea" << 0.0 << 3.0 << true;
315−
316− QTest::newRow("downwards, diagonal drag")
317− << "vpDragArea" << 0.9 << 3.0 << true;
318−
319− QTest::newRow("upwards, tiny drag")
320− << "vnDragArea" << 0.0 << 0.2 << false;
321−
322− QTest::newRow("upwards, straight drag")
323− << "vnDragArea" << 0.0 << 3.0 << true;
324−
325− QTest::newRow("upwards, diagonal drag")
326− << "vnDragArea" << 0.9 << 3.0 << true;
327
186
328− QTest::newRow("upwards, overly diagonal drag")
187+namespace {
329− << "vnDragArea" << 2.0 << 3.0 << false;
188+QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea)
189+{
190+ QPointF localCenter(edgeDragArea->width() / 2., edgeDragArea->height() / 2.);
191+ return edgeDragArea->mapToScene(localCenter);
192+}
330}
193}
331
194
332/*
195/*
@@ -339,17 +202,17 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
339 DirectionalDragArea *edgeDragArea =
202 DirectionalDragArea *edgeDragArea =
340 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
203 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
341 QVERIFY(edgeDragArea != 0);
204 QVERIFY(edgeDragArea != 0);
342− edgeDragArea->setRecognitionTimer(fakeTimer);
205+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
343− edgeDragArea->setTimeSource(fakeTimeSource);
206+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
344
207
345 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
208 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
346 QPointF touchPoint = initialTouchPos;
209 QPointF touchPoint = initialTouchPos;
347
210
348− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;
211+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0;
349 QPointF dragDirectionVector(1.0, 0.0);
212 QPointF dragDirectionVector(1.0, 0.0);
350− qreal touchStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
213+ qreal touchStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
351− // make sure we are above minimum speed
214+ // make sure we are above maximum time
352− int touchStepTimeMs = (touchStepDistance / (edgeDragArea->minSpeed() * 5.0f)) * 1000.0f;
215+ int touchStepTimeMs = edgeDragArea->d->maxTime / 20. ;
353 QPointF touchMovement = dragDirectionVector * touchStepDistance;
216 QPointF touchMovement = dragDirectionVector * touchStepDistance;
354
217
355 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
218 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
@@ -373,65 +236,13 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
373 passTime(touchStepTimeMs);
236 passTime(touchStepTimeMs);
374 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
237 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
375 } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance
238 } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance
376− || fakeTimeSource->m_msecsSinceReference < (edgeDragArea->compositionTime() * 1.5f));
239+ || m_fakeTimerFactory->timeSource()->msecsSinceReference() < (edgeDragArea->d->compositionTime * 1.5f));
377
240
378− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
241+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
379
242
380 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
243 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
381}
244}
382
245
383−/*
384− Checks that a gesture will be rejected if it's slower than minSpeed while
385− status is Undecided.
386− */
387−void tst_DirectionalDragArea::minSpeed()
388−{
389− QFETCH(qreal, minSpeed);
390− QFETCH(qreal, speed);
391− QFETCH(int, expectedStatusAfterSpeedCheck);
392−
393− DirectionalDragArea *edgeDragArea =
394− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
395− QVERIFY(edgeDragArea != 0);
396− edgeDragArea->setRecognitionTimer(fakeTimer);
397− edgeDragArea->setTimeSource(fakeTimeSource);
398−
399− // A really long, unattainable, number. We don't want it getting recognized before
400− // the speed checks we want have been performed
401− edgeDragArea->setDistanceThreshold(500000);
402−
403− edgeDragArea->setMinSpeed(minSpeed);
404−
405− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
406− QPointF touchPoint = initialTouchPos;
407−
408− QPointF dragDirectionVector(1.0, 0.0);
409− qint64 timeStepMsecs = 10;
410− qreal distanceStep = (speed / 1000.0f) * timeStepMsecs;
411− QPointF touchMovement = dragDirectionVector * distanceStep;
412−
413− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
414−
415− // Move for a while to ensure our speed check is performed a couple of times
416− for (int i=0; i < 20; ++i) {
417− touchPoint += touchMovement;
418− passTime(timeStepMsecs);
419− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
420− }
421−
422− QCOMPARE((int)edgeDragArea->status(), expectedStatusAfterSpeedCheck);
423−}
424−
425−void tst_DirectionalDragArea::minSpeed_data()
426−{
427− QTest::addColumn<qreal>("minSpeed");
428− QTest::addColumn<qreal>("speed");
429− QTest::addColumn<int>("expectedStatusAfterSpeedCheck");
430−
431− QTest::newRow("slower than minSpeed") << 100.0 << 50.0 << (int)DirectionalDragArea::WaitingForTouch;
432− QTest::newRow("faster than minSpeed") << 100.0 << 150.0 << (int)DirectionalDragArea::Undecided;
433−}
434−
435/*
246/*
436 Checks that the recognition timer is started and stopped appropriately.
247 Checks that the recognition timer is started and stopped appropriately.
437 I.e., check that it's running only while gesture recognition is taking place
248 I.e., check that it's running only while gesture recognition is taking place
@@ -442,11 +253,9 @@ void tst_DirectionalDragArea::recognitionTimerUsage()
442 DirectionalDragArea *edgeDragArea =
253 DirectionalDragArea *edgeDragArea =
443 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
254 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
444 QVERIFY(edgeDragArea != 0);
255 QVERIFY(edgeDragArea != 0);
256+ AbstractTimer *fakeTimer = m_fakeTimerFactory->createTimer();
445− edgeDragArea->setRecognitionTimer(fakeTimer);
257+ edgeDragArea->d->setRecognitionTimer(fakeTimer);
446− edgeDragArea->setTimeSource(fakeTimeSource);
258+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
447−
448− // don't let it interfere with our test
449− edgeDragArea->setMinSpeed(0.0);
450
259
451 int timeStepMs = 5; // some arbitrary small value.
260 int timeStepMs = 5; // some arbitrary small value.
452
261
@@ -454,62 +263,31 @@ void tst_DirectionalDragArea::recognitionTimerUsage()
454 QPointF touchPoint = initialTouchPos;
263 QPointF touchPoint = initialTouchPos;
455
264
456 QPointF dragDirectionVector(1.0, 0.0);
265 QPointF dragDirectionVector(1.0, 0.0);
457− QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.2f);
266+ QPointF touchMovement = dragDirectionVector * (edgeDragArea->d->distanceThreshold * 0.2f);
458
267
459− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
268+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
460 QVERIFY(!fakeTimer->isRunning());
269 QVERIFY(!fakeTimer->isRunning());
461
270
462 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
271 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
463
272
464− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
273+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
465 QVERIFY(fakeTimer->isRunning());
274 QVERIFY(fakeTimer->isRunning());
466
275
467 // Move beyond distance threshold and composition time to ensure recognition
276 // Move beyond distance threshold and composition time to ensure recognition
468− while (fakeTimeSource->m_msecsSinceReference <= edgeDragArea->compositionTime() ||
277+ while (m_fakeTimerFactory->timeSource()->msecsSinceReference() <= edgeDragArea->d->compositionTime ||
469− (touchPoint - initialTouchPos).manhattanLength() <= edgeDragArea->distanceThreshold()) {
278+ (touchPoint - initialTouchPos).manhattanLength() <= edgeDragArea->d->distanceThreshold) {
470
279
471− QCOMPARE(edgeDragArea->status() == DirectionalDragArea::Undecided, fakeTimer->isRunning());
280+ QCOMPARE(edgeDragArea->d->status == DirectionalDragAreaPrivate::Undecided, fakeTimer->isRunning());
472
281
473 touchPoint += touchMovement;
282 touchPoint += touchMovement;
474 passTime(timeStepMs);
283 passTime(timeStepMs);
475 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
284 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
476 }
285 }
477
286
478− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
287+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
479 QVERIFY(!fakeTimer->isRunning());
288 QVERIFY(!fakeTimer->isRunning());
480}
289}
481
290
482−/*
483− A gesture should be rejected if too much time has passed without any new input
484− events from it.
485− */
486−void tst_DirectionalDragArea::maxSilenceTime()
487−{
488− DirectionalDragArea *edgeDragArea =
489− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
490− QVERIFY(edgeDragArea != 0);
491− edgeDragArea->setRecognitionTimer(fakeTimer);
492− edgeDragArea->setTimeSource(fakeTimeSource);
493−
494− // Make sure this property is not disabled
495− edgeDragArea->setMaxSilenceTime(100);
496−
497− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
498− QPointF touchPoint = initialTouchPos;
499−
500− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
501−
502− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
503− QVERIFY(fakeTimer->isRunning());
504−
505− // Force timer to timeout until after maxSilenceTime has been reached
506− while (fakeTimeSource->m_msecsSinceReference < edgeDragArea->maxSilenceTime()) {
507− passTime(fakeTimer->interval());
508− }
509−
510− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
511−}
512−
513/*
291/*
514 Checks that it informs the X coordinate of the touch point in local and scene coordinates
292 Checks that it informs the X coordinate of the touch point in local and scene coordinates
515 correctly.
293 correctly.
@@ -519,18 +297,19 @@ void tst_DirectionalDragArea::sceneXAndX()
519 DirectionalDragArea *edgeDragArea =
297 DirectionalDragArea *edgeDragArea =
520 m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
298 m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
521 QVERIFY(edgeDragArea != 0);
299 QVERIFY(edgeDragArea != 0);
522− edgeDragArea->setRecognitionTimer(fakeTimer);
300+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
523− edgeDragArea->setTimeSource(fakeTimeSource);
301+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
302+ edgeDragArea->setImmediateRecognition(true);
524
303
525 QPointF touchScenePos(m_view->width() - (edgeDragArea->width()/2.0f), m_view->height()/2.0f);
304 QPointF touchScenePos(m_view->width() - (edgeDragArea->width()/2.0f), m_view->height()/2.0f);
526
305
527− QTest::touchEvent(m_view, m_device).press(0, touchScenePos.toPoint());
306+ sendTouchPress(0 /* timestamp */, 0 /* id */, touchScenePos);
528
307
529 QSignalSpy touchXSpy(edgeDragArea, SIGNAL(touchXChanged(qreal)));
308 QSignalSpy touchXSpy(edgeDragArea, SIGNAL(touchXChanged(qreal)));
530 QSignalSpy touchSceneXSpy(edgeDragArea, SIGNAL(touchSceneXChanged(qreal)));
309 QSignalSpy touchSceneXSpy(edgeDragArea, SIGNAL(touchSceneXChanged(qreal)));
531
310
532 touchScenePos.rx() = m_view->width() / 2;
311 touchScenePos.rx() = m_view->width() / 2;
533− QTest::touchEvent(m_view, m_device).move(0, touchScenePos.toPoint());
312+ sendTouchUpdate(50 /* timestamp */, 0 /* id */, touchScenePos);
534
313
535 QCOMPARE(touchXSpy.count(), 1);
314 QCOMPARE(touchXSpy.count(), 1);
536 QCOMPARE(touchSceneXSpy.count(), 1);
315 QCOMPARE(touchSceneXSpy.count(), 1);
@@ -547,18 +326,19 @@ void tst_DirectionalDragArea::sceneYAndY()
547 DirectionalDragArea *edgeDragArea =
326 DirectionalDragArea *edgeDragArea =
548 m_view->rootObject()->findChild<DirectionalDragArea*>("vnDragArea");
327 m_view->rootObject()->findChild<DirectionalDragArea*>("vnDragArea");
549 QVERIFY(edgeDragArea != 0);
328 QVERIFY(edgeDragArea != 0);
550− edgeDragArea->setRecognitionTimer(fakeTimer);
329+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
551− edgeDragArea->setTimeSource(fakeTimeSource);
330+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
331+ edgeDragArea->setImmediateRecognition(true);
552
332
553 QPointF touchScenePos(m_view->width()/2.0f, m_view->height() - (edgeDragArea->height()/2.0f));
333 QPointF touchScenePos(m_view->width()/2.0f, m_view->height() - (edgeDragArea->height()/2.0f));
554
334
555− QTest::touchEvent(m_view, m_device).press(0, touchScenePos.toPoint());
335+ sendTouchPress(0 /* timestamp */, 0 /* id */, touchScenePos);
556
336
557 QSignalSpy touchYSpy(edgeDragArea, SIGNAL(touchYChanged(qreal)));
337 QSignalSpy touchYSpy(edgeDragArea, SIGNAL(touchYChanged(qreal)));
558 QSignalSpy touchSceneYSpy(edgeDragArea, SIGNAL(touchSceneYChanged(qreal)));
338 QSignalSpy touchSceneYSpy(edgeDragArea, SIGNAL(touchSceneYChanged(qreal)));
559
339
560 touchScenePos.ry() = m_view->height() / 2;
340 touchScenePos.ry() = m_view->height() / 2;
561− QTest::touchEvent(m_view, m_device).move(0, touchScenePos.toPoint());
341+ sendTouchUpdate(50 /* timestamp */, 0 /* id */, touchScenePos);
562
342
563 QCOMPARE(touchYSpy.count(), 1);
343 QCOMPARE(touchYSpy.count(), 1);
564 QCOMPARE(touchSceneYSpy.count(), 1);
344 QCOMPARE(touchSceneYSpy.count(), 1);
@@ -577,8 +357,8 @@ void tst_DirectionalDragArea::twoFingerTap()
577 DirectionalDragArea *edgeDragArea =
357 DirectionalDragArea *edgeDragArea =
578 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
358 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
579 QVERIFY(edgeDragArea != 0);
359 QVERIFY(edgeDragArea != 0);
580− edgeDragArea->setRecognitionTimer(fakeTimer);
360+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
581− edgeDragArea->setTimeSource(fakeTimeSource);
361+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
582
362
583 // Make touches evenly spaced along the edgeDragArea
363 // Make touches evenly spaced along the edgeDragArea
584 QPoint touchAPos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
364 QPoint touchAPos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
@@ -592,7 +372,7 @@ void tst_DirectionalDragArea::twoFingerTap()
592 QTest::touchEvent(m_view, m_device)
372 QTest::touchEvent(m_view, m_device)
593 .press(0, touchAPos);
373 .press(0, touchAPos);
594
374
595− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
375+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
596
376
597 passTime(timeStepMsecs);
377 passTime(timeStepMsecs);
598 QTest::touchEvent(m_view, m_device)
378 QTest::touchEvent(m_view, m_device)
@@ -601,20 +381,20 @@ void tst_DirectionalDragArea::twoFingerTap()
601
381
602 // A second touch point appeared during recognition, reject immediately as this
382 // A second touch point appeared during recognition, reject immediately as this
603 // can't be a single-touch gesture anymore.
383 // can't be a single-touch gesture anymore.
604− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
384+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
605
385
606 passTime(timeStepMsecs);
386 passTime(timeStepMsecs);
607 QTest::touchEvent(m_view, m_device)
387 QTest::touchEvent(m_view, m_device)
608 .release(0, touchAPos)
388 .release(0, touchAPos)
609 .move(1, touchBPos);
389 .move(1, touchBPos);
610
390
611− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
391+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
612
392
613 passTime(timeStepMsecs);
393 passTime(timeStepMsecs);
614 QTest::touchEvent(m_view, m_device)
394 QTest::touchEvent(m_view, m_device)
615 .release(1, touchBPos);
395 .release(1, touchBPos);
616
396
617− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
397+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
618
398
619 // Perform the second two-finger tap
399 // Perform the second two-finger tap
620
400
@@ -622,27 +402,27 @@ void tst_DirectionalDragArea::twoFingerTap()
622 QTest::touchEvent(m_view, m_device)
402 QTest::touchEvent(m_view, m_device)
623 .press(0, touchAPos);
403 .press(0, touchAPos);
624
404
625− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
405+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
626
406
627 passTime(timeStepMsecs);
407 passTime(timeStepMsecs);
628 QTest::touchEvent(m_view, m_device)
408 QTest::touchEvent(m_view, m_device)
629 .move(0, touchAPos)
409 .move(0, touchAPos)
630 .press(1, touchBPos);
410 .press(1, touchBPos);
631
411
632− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
412+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
633
413
634 passTime(timeStepMsecs);
414 passTime(timeStepMsecs);
635 QTest::touchEvent(m_view, m_device)
415 QTest::touchEvent(m_view, m_device)
636 .release(0, touchAPos)
416 .release(0, touchAPos)
637 .move(1, touchBPos);
417 .move(1, touchBPos);
638
418
639− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
419+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
640
420
641 passTime(timeStepMsecs);
421 passTime(timeStepMsecs);
642 QTest::touchEvent(m_view, m_device)
422 QTest::touchEvent(m_view, m_device)
643 .release(1, touchBPos);
423 .release(1, touchBPos);
644
424
645− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
425+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
646}
426}
647
427
648/*
428/*
@@ -659,25 +439,25 @@ void tst_DirectionalDragArea::movingDDA()
659 DirectionalDragArea *edgeDragArea =
439 DirectionalDragArea *edgeDragArea =
660 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
440 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
661 Q_ASSERT(edgeDragArea != 0);
441 Q_ASSERT(edgeDragArea != 0);
662− edgeDragArea->setRecognitionTimer(fakeTimer);
442+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
663− edgeDragArea->setTimeSource(fakeTimeSource);
443+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
664
444
665 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
445 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
666 QPointF touchPoint = initialTouchPos;
446 QPointF touchPoint = initialTouchPos;
667
447
668− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
448+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
669 QPointF dragDirectionVector(1.0f, 0.0f);
449 QPointF dragDirectionVector(1.0f, 0.0f);
670
450
671− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
451+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
672 QPointF touchMovement = dragDirectionVector * movementStepDistance;
452 QPointF touchMovement = dragDirectionVector * movementStepDistance;
673 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
453 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
674− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
454+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
675
455
676 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
456 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
677
457
678 // Move it far ahead along the direction of the gesture
458 // Move it far ahead along the direction of the gesture
679 // rightwardsLauncher is a parent of our DirectionalDragArea. So moving it will move our DDA
459 // rightwardsLauncher is a parent of our DirectionalDragArea. So moving it will move our DDA
680− rightwardsLauncher->setX(rightwardsLauncher->x() + edgeDragArea->distanceThreshold() * 5.0f);
460+ rightwardsLauncher->setX(rightwardsLauncher->x() + edgeDragArea->d->distanceThreshold * 5.0f);
681
461
682 for (int i = 0; i < totalMovementSteps; ++i) {
462 for (int i = 0; i < totalMovementSteps; ++i) {
683 touchPoint += touchMovement;
463 touchPoint += touchMovement;
@@ -685,7 +465,7 @@ void tst_DirectionalDragArea::movingDDA()
685 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
465 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
686 }
466 }
687
467
688− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
468+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
689
469
690 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
470 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
691}
471}
@@ -699,8 +479,8 @@ void tst_DirectionalDragArea::ignoreOldFinger()
699 DirectionalDragArea *edgeDragArea =
479 DirectionalDragArea *edgeDragArea =
700 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
480 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
701 Q_ASSERT(edgeDragArea != 0);
481 Q_ASSERT(edgeDragArea != 0);
702− edgeDragArea->setRecognitionTimer(fakeTimer);
482+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
703− edgeDragArea->setTimeSource(fakeTimeSource);
483+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
704
484
705 // Make touches evenly spaced along the edgeDragArea
485 // Make touches evenly spaced along the edgeDragArea
706 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
486 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
@@ -708,26 +488,26 @@ void tst_DirectionalDragArea::ignoreOldFinger()
708
488
709 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
489 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
710
490
711− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
491+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
712
492
713 // leave it lying around for some time
493 // leave it lying around for some time
714− passTime(qMax(edgeDragArea->maxSilenceTime(), edgeDragArea->compositionTime()) * 10);
494+ passTime(edgeDragArea->d->maxTime * 10);
715
495
716− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
496+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
717
497
718− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
498+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
719 QPointF dragDirectionVector(1.0f, 0.0f);
499 QPointF dragDirectionVector(1.0f, 0.0f);
720
500
721− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
501+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
722 QPointF touchMovement = dragDirectionVector * movementStepDistance;
502 QPointF touchMovement = dragDirectionVector * movementStepDistance;
723 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
503 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
724− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
504+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
725
505
726 QTest::touchEvent(m_view, m_device)
506 QTest::touchEvent(m_view, m_device)
727 .move(0, touch0Pos)
507 .move(0, touch0Pos)
728 .press(1, touch1Pos);
508 .press(1, touch1Pos);
729
509
730− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
510+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
731
511
732 for (int i = 0; i < totalMovementSteps; ++i) {
512 for (int i = 0; i < totalMovementSteps; ++i) {
733 touch1Pos += touchMovement.toPoint();
513 touch1Pos += touchMovement.toPoint();
@@ -737,13 +517,13 @@ void tst_DirectionalDragArea::ignoreOldFinger()
737 .move(1, touch1Pos);
517 .move(1, touch1Pos);
738 }
518 }
739
519
740− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
520+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
741
521
742 QTest::touchEvent(m_view, m_device)
522 QTest::touchEvent(m_view, m_device)
743 .move(0, touch0Pos)
523 .move(0, touch0Pos)
744 .release(1, touch1Pos);
524 .release(1, touch1Pos);
745
525
746− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
526+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
747}
527}
748
528
749/*
529/*
@@ -762,19 +542,19 @@ void tst_DirectionalDragArea::rotated()
762 DirectionalDragArea *edgeDragArea =
542 DirectionalDragArea *edgeDragArea =
763 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
543 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
764 Q_ASSERT(edgeDragArea != 0);
544 Q_ASSERT(edgeDragArea != 0);
765− edgeDragArea->setRecognitionTimer(fakeTimer);
545+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
766− edgeDragArea->setTimeSource(fakeTimeSource);
546+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
767
547
768 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
548 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
769 QPointF touchPoint = initialTouchPos;
549 QPointF touchPoint = initialTouchPos;
770
550
771− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
551+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
772 QPointF dragDirectionVector(0.0f, 1.0f);
552 QPointF dragDirectionVector(0.0f, 1.0f);
773
553
774− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
554+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
775 QPointF touchMovement = dragDirectionVector * movementStepDistance;
555 QPointF touchMovement = dragDirectionVector * movementStepDistance;
776 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
556 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
777− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
557+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
778
558
779 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
559 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
780
560
@@ -784,7 +564,7 @@ void tst_DirectionalDragArea::rotated()
784 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
564 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
785 }
565 }
786
566
787− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
567+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
788
568
789 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
569 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
790}
570}
@@ -802,31 +582,30 @@ void tst_DirectionalDragArea::sceneDistance()
802 DirectionalDragArea *edgeDragArea =
582 DirectionalDragArea *edgeDragArea =
803 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
583 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
804 Q_ASSERT(edgeDragArea != 0);
584 Q_ASSERT(edgeDragArea != 0);
805− edgeDragArea->setRecognitionTimer(fakeTimer);
585+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
806− edgeDragArea->setTimeSource(fakeTimeSource);
586+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
587+
588+ // to disable the position smoothing so that we can more easily check sceneDistance values
589+ edgeDragArea->setImmediateRecognition(true);
807
590
808 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
591 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
809 QPointF touchPoint = initialTouchPos;
592 QPointF touchPoint = initialTouchPos;
810
593
811− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
594+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
812
595
813− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
596+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
814 QPointF touchMovement = dragDirectionVector * movementStepDistance;
597 QPointF touchMovement = dragDirectionVector * movementStepDistance;
815 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
598 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
816− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
599+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
817
600
818− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
601+ qint64 timestamp = 0;
602+
603+ sendTouchPress(timestamp, 0, touchPoint);
819
604
820−#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
821− QQuickWindowPrivate *wp = QQuickWindowPrivate::get(m_view);
822−#endif
823 for (int i = 0; i < totalMovementSteps; ++i) {
605 for (int i = 0; i < totalMovementSteps; ++i) {
824 touchPoint += touchMovement;
606 touchPoint += touchMovement;
825− passTime(movementTimeStepMs);
607+ timestamp += movementTimeStepMs;
826− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
608+ sendTouchUpdate(timestamp, 0, touchPoint);
827−#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
828− wp->flushDelayedTouchEvent();
829−#endif
830 }
609 }
831
610
832 qreal actualDragDistance = ((qreal)totalMovementSteps) * movementStepDistance;
611 qreal actualDragDistance = ((qreal)totalMovementSteps) * movementStepDistance;
@@ -836,7 +615,8 @@ void tst_DirectionalDragArea::sceneDistance()
836 // NB: qFuzzyCompare(), used internally by QCOMPARE(), is broken.
615 // NB: qFuzzyCompare(), used internally by QCOMPARE(), is broken.
837 QVERIFY(qAbs(edgeDragArea->sceneDistance() - actualDragDistance) < 0.001);
616 QVERIFY(qAbs(edgeDragArea->sceneDistance() - actualDragDistance) < 0.001);
838
617
839− QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
618+ timestamp += movementTimeStepMs;
619+ sendTouchRelease(timestamp, 0, touchPoint);
840}
620}
841
621
842void tst_DirectionalDragArea::sceneDistance_data()
622void tst_DirectionalDragArea::sceneDistance_data()
@@ -860,18 +640,18 @@ void tst_DirectionalDragArea::disabledWhileDragging()
860 DirectionalDragArea *edgeDragArea =
640 DirectionalDragArea *edgeDragArea =
861 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
641 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
862 Q_ASSERT(edgeDragArea != 0);
642 Q_ASSERT(edgeDragArea != 0);
863− edgeDragArea->setRecognitionTimer(fakeTimer);
643+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
864− edgeDragArea->setTimeSource(fakeTimeSource);
644+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
865
645
866 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
646 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
867
647
868− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
648+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
869 QPointF dragDirectionVector(1., 0.); // horizontal positive
649 QPointF dragDirectionVector(1., 0.); // horizontal positive
870
650
871− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
651+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
872 QPointF touchMovement = dragDirectionVector * movementStepDistance;
652 QPointF touchMovement = dragDirectionVector * movementStepDistance;
873 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
653 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
874− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
654+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
875
655
876 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
656 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
877
657
@@ -881,13 +661,13 @@ void tst_DirectionalDragArea::disabledWhileDragging()
881 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
661 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
882 }
662 }
883
663
884− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
664+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
885 QCOMPARE(edgeDragArea->dragging(), true);
665 QCOMPARE(edgeDragArea->dragging(), true);
886
666
887 // disable the dragArea while it's being dragged.
667 // disable the dragArea while it's being dragged.
888 edgeDragArea->setEnabled(false);
668 edgeDragArea->setEnabled(false);
889
669
890− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
670+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
891 QCOMPARE(edgeDragArea->dragging(), false);
671 QCOMPARE(edgeDragArea->dragging(), false);
892
672
893 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
673 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
@@ -898,15 +678,14 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
898 DirectionalDragArea *edgeDragArea =
678 DirectionalDragArea *edgeDragArea =
899 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
679 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
900 Q_ASSERT(edgeDragArea != 0);
680 Q_ASSERT(edgeDragArea != 0);
901− edgeDragArea->setRecognitionTimer(fakeTimer);
681+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
902− edgeDragArea->setTimeSource(fakeTimeSource);
682+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
903
683
904 // Disable some constraints we're not interested in
684 // Disable some constraints we're not interested in
905− edgeDragArea->setMaxSilenceTime(60 * 1000);
685+ edgeDragArea->d->setMaxTime(60 * 1000);
906− edgeDragArea->setMinSpeed(0);
907
686
908 // And ensure others have the values we want
687 // And ensure others have the values we want
909− edgeDragArea->setCompositionTime(60);
688+ edgeDragArea->d->compositionTime = 60;
910
689
911 // Put an item right behind edgeDragArea to receive the touches ignored by it
690 // Put an item right behind edgeDragArea to receive the touches ignored by it
912 DummyItem *dummyItem = new DummyItem;
691 DummyItem *dummyItem = new DummyItem;
@@ -923,10 +702,10 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
923
702
924 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
703 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
925
704
926− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
705+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
927
706
928 // We are now going to be way beyond compositionTime
707 // We are now going to be way beyond compositionTime
929− passTime(edgeDragArea->compositionTime()*3);
708+ passTime(edgeDragArea->d->compositionTime*3);
930
709
931 QTest::touchEvent(m_view, m_device)
710 QTest::touchEvent(m_view, m_device)
932 .move(0, touch0Pos)
711 .move(0, touch0Pos)
@@ -948,7 +727,7 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
948 .move(0, touch0Pos)
727 .move(0, touch0Pos)
949 .move(1, touch1Pos);
728 .move(1, touch1Pos);
950
729
951− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
730+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
952
731
953 passTime(5);
732 passTime(5);
954
733
@@ -956,7 +735,7 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
956 .release(0, touch0Pos)
735 .release(0, touch0Pos)
957 .move(1, touch1Pos);
736 .move(1, touch1Pos);
958
737
959− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
738+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
960
739
961 passTime(5);
740 passTime(5);
962
741
@@ -964,7 +743,7 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
964 .release(1, touch1Pos);
743 .release(1, touch1Pos);
965
744
966 // Shouldn't be keepping info about touches that no longer exist or interest us
745 // Shouldn't be keepping info about touches that no longer exist or interest us
967− QVERIFY(edgeDragArea->m_activeTouches.isEmpty());
746+ QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
968
747
969 delete dummyItem;
748 delete dummyItem;
970}
749}
@@ -974,12 +753,11 @@ void tst_DirectionalDragArea::givesUpWhenLosesTouch()
974 DirectionalDragArea *edgeDragArea =
753 DirectionalDragArea *edgeDragArea =
975 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
754 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
976 Q_ASSERT(edgeDragArea != 0);
755 Q_ASSERT(edgeDragArea != 0);
977− edgeDragArea->setRecognitionTimer(fakeTimer);
756+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
978− edgeDragArea->setTimeSource(fakeTimeSource);
757+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
979
758
980 // Disable some constraints we're not interested in
759 // Disable some constraints we're not interested in
981− edgeDragArea->setMaxSilenceTime(60 * 1000);
760+ edgeDragArea->d->setMaxTime(60 * 1000);
982− edgeDragArea->setMinSpeed(0);
983
761
984 // Put an item right in front of edgeDragArea
762 // Put an item right in front of edgeDragArea
985 DummyItem *dummyItem = new DummyItem(edgeDragArea->parentItem());
763 DummyItem *dummyItem = new DummyItem(edgeDragArea->parentItem());
@@ -998,11 +776,11 @@ void tst_DirectionalDragArea::givesUpWhenLosesTouch()
998
776
999 QTest::touchEvent(m_view, m_device).press(0, touchPos);
777 QTest::touchEvent(m_view, m_device).press(0, touchPos);
1000
778
1001− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
779+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1002
780
1003 m_touchRegistry->requestTouchOwnership(0, dummyItem);
781 m_touchRegistry->requestTouchOwnership(0, dummyItem);
1004
782
1005− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
783+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1006
784
1007 dummyItem->grabTouchPoints({0});
785 dummyItem->grabTouchPoints({0});
1008 dummyItem->touchEventHandler = [&](QTouchEvent *event) { event->accept(); };
786 dummyItem->touchEventHandler = [&](QTouchEvent *event) { event->accept(); };
@@ -1010,9 +788,9 @@ void tst_DirectionalDragArea::givesUpWhenLosesTouch()
1010 passTime(5);
788 passTime(5);
1011 QTest::touchEvent(m_view, m_device).release(0, touchPos);
789 QTest::touchEvent(m_view, m_device).release(0, touchPos);
1012
790
1013− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
791+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1014
792
1015− QVERIFY(edgeDragArea->m_activeTouches.isEmpty());
793+ QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
1016}
794}
1017
795
1018void tst_DirectionalDragArea::threeFingerDrag()
796void tst_DirectionalDragArea::threeFingerDrag()
@@ -1020,15 +798,14 @@ void tst_DirectionalDragArea::threeFingerDrag()
1020 DirectionalDragArea *edgeDragArea =
798 DirectionalDragArea *edgeDragArea =
1021 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
799 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1022 Q_ASSERT(edgeDragArea != 0);
800 Q_ASSERT(edgeDragArea != 0);
1023− edgeDragArea->setRecognitionTimer(fakeTimer);
801+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
1024− edgeDragArea->setTimeSource(fakeTimeSource);
802+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1025
803
1026 // Disable some constraints we're not interested in
804 // Disable some constraints we're not interested in
1027− edgeDragArea->setMaxSilenceTime(60 * 1000);
805+ edgeDragArea->d->setMaxTime(60 * 1000);
1028− edgeDragArea->setMinSpeed(0);
1029
806
1030 // And ensure others have the values we want
807 // And ensure others have the values we want
1031− edgeDragArea->setCompositionTime(60);
808+ edgeDragArea->d->compositionTime = 60;
1032
809
1033 // Make touches evenly spaced along the edgeDragArea
810 // Make touches evenly spaced along the edgeDragArea
1034 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.25f);
811 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.25f);
@@ -1038,14 +815,14 @@ void tst_DirectionalDragArea::threeFingerDrag()
1038 QTest::touchEvent(m_view, m_device)
815 QTest::touchEvent(m_view, m_device)
1039 .press(0, touch0Pos);
816 .press(0, touch0Pos);
1040
817
1041− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
818+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1042
819
1043 passTime(5);
820 passTime(5);
1044 QTest::touchEvent(m_view, m_device)
821 QTest::touchEvent(m_view, m_device)
1045 .move(0, touch0Pos)
822 .move(0, touch0Pos)
1046 .press(1, touch1Pos);
823 .press(1, touch1Pos);
1047
824
1048− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
825+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1049
826
1050 passTime(5);
827 passTime(5);
1051 QTest::touchEvent(m_view, m_device)
828 QTest::touchEvent(m_view, m_device)
@@ -1053,7 +830,7 @@ void tst_DirectionalDragArea::threeFingerDrag()
1053 .move(1, touch1Pos)
830 .move(1, touch1Pos)
1054 .press(2, touch2Pos);
831 .press(2, touch2Pos);
1055
832
1056− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
833+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1057
834
1058 passTime(10);
835 passTime(10);
1059 QTest::touchEvent(m_view, m_device)
836 QTest::touchEvent(m_view, m_device)
@@ -1077,7 +854,7 @@ void tst_DirectionalDragArea::threeFingerDrag()
1077 .release(0, touch0Pos);
854 .release(0, touch0Pos);
1078
855
1079 // Shouldn't be keepping info about touches that no longer exist or interest us
856 // Shouldn't be keepping info about touches that no longer exist or interest us
1080− QVERIFY(edgeDragArea->m_activeTouches.isEmpty());
857+ QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
1081}
858}
1082
859
1083/*
860/*
@@ -1090,12 +867,12 @@ void tst_DirectionalDragArea::immediateRecognitionWhenConstraintsDisabled()
1090 DirectionalDragArea *edgeDragArea =
867 DirectionalDragArea *edgeDragArea =
1091 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
868 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1092 Q_ASSERT(edgeDragArea != 0);
869 Q_ASSERT(edgeDragArea != 0);
1093− edgeDragArea->setRecognitionTimer(fakeTimer);
870+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
1094− edgeDragArea->setTimeSource(fakeTimeSource);
871+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1095
872
1096 // Disable the minimum amount of constraints to ensure immediate recognition
873 // Disable the minimum amount of constraints to ensure immediate recognition
1097− edgeDragArea->setDistanceThreshold(0);
874+ edgeDragArea->d->setDistanceThreshold(0);
1098− edgeDragArea->setCompositionTime(0);
875+ edgeDragArea->d->compositionTime = 0;
1099
876
1100 // Put an item right behind edgeDragArea to receive the touches ignored by it
877 // Put an item right behind edgeDragArea to receive the touches ignored by it
1101 DummyItem *dummyItem = new DummyItem;
878 DummyItem *dummyItem = new DummyItem;
@@ -1111,7 +888,7 @@ void tst_DirectionalDragArea::immediateRecognitionWhenConstraintsDisabled()
1111 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
888 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
1112
889
1113 // check for immediate recognition
890 // check for immediate recognition
1114− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
891+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1115
892
1116 // and therefore it should have immediately grabbed the touch point,
893 // and therefore it should have immediately grabbed the touch point,
1117 // not letting it leak to items behind him.
894 // not letting it leak to items behind him.
@@ -1123,19 +900,19 @@ void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRec
1123 DirectionalDragArea *edgeDragArea =
900 DirectionalDragArea *edgeDragArea =
1124 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
901 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1125 Q_ASSERT(edgeDragArea != 0);
902 Q_ASSERT(edgeDragArea != 0);
1126− edgeDragArea->setRecognitionTimer(fakeTimer);
903+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
1127− edgeDragArea->setTimeSource(fakeTimeSource);
904+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1128
905
1129 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
906 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
1130
907
1131 // Move less than the minimum needed for the drag gesture recognition
908 // Move less than the minimum needed for the drag gesture recognition
1132− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*0.5f;
909+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 0.5f;
1133 QPointF dragDirectionVector(1., 0.); // horizontal positive
910 QPointF dragDirectionVector(1., 0.); // horizontal positive
1134
911
1135− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
912+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
1136 QPointF touchMovement = dragDirectionVector * movementStepDistance;
913 QPointF touchMovement = dragDirectionVector * movementStepDistance;
1137 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
914 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
1138− int movementTimeStepMs = (edgeDragArea->compositionTime() * 0.8f) / totalMovementSteps;
915+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 0.8f) / totalMovementSteps;
1139
916
1140 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
917 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
1141
918
@@ -1145,14 +922,14 @@ void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRec
1145 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
922 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
1146 }
923 }
1147
924
1148− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
925+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1149
926
1150 // edgeDragArea should be an undecided candidate
927 // edgeDragArea should be an undecided candidate
1151 {
928 {
1152 auto touchInfo = m_touchRegistry->findTouchInfo(0);
929 auto touchInfo = m_touchRegistry->findTouchInfo(0);
1153 QCOMPARE(touchInfo->candidates.size(), 1);
930 QCOMPARE(touchInfo->candidates.size(), 1);
1154 QCOMPARE(touchInfo->candidates.at(0).item.data(), edgeDragArea);
931 QCOMPARE(touchInfo->candidates.at(0).item.data(), edgeDragArea);
1155− QCOMPARE(touchInfo->candidates.at(0).undecided, true);
932+ QCOMPARE(touchInfo->candidates.at(0).state, TouchRegistry::CandidateInfo::Undecided);
1156 }
933 }
1157
934
1158 // disable the dragArea while it's still recognizing a possible drag gesture.
935 // disable the dragArea while it's still recognizing a possible drag gesture.
@@ -1169,7 +946,7 @@ void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRec
1169 QCOMPARE(touchInfo->candidates.size(), 0);
946 QCOMPARE(touchInfo->candidates.size(), 0);
1170 }
947 }
1171
948
1172− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
949+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1173
950
1174 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
951 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
1175}
952}
@@ -1182,47 +959,13 @@ void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRec
1182 QTest::newRow("invisible") << false;
959 QTest::newRow("invisible") << false;
1183}
960}
1184
961
1185−void tst_DirectionalDragArea::tappedSignal()
1186−{
1187− DirectionalDragArea *edgeDragArea =
1188− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1189− QVERIFY(edgeDragArea != 0);
1190− edgeDragArea->setRecognitionTimer(fakeTimer);
1191− edgeDragArea->setTimeSource(fakeTimeSource);
1192−
1193− QFETCH(bool, immediateGestureRecognition);
1194− if (immediateGestureRecognition) {
1195− // Disable the minimum amount of constraints to ensure immediate recognition
1196− edgeDragArea->setDistanceThreshold(0);
1197− edgeDragArea->setCompositionTime(0);
1198− }
1199−
1200− QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()/2.0f);
1201−
1202− QSignalSpy tappedSpy(edgeDragArea, SIGNAL(tapped()));
1203−
1204− QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
1205− passTime(edgeDragArea->maxTapDuration() / 2);
1206− QTest::touchEvent(m_view, m_device).release(0, touch0Pos);
1207−
1208− QCOMPARE(tappedSpy.count(), 1);
1209−}
1210−
1211−void tst_DirectionalDragArea::tappedSignal_data()
1212−{
1213− QTest::addColumn<bool>("immediateGestureRecognition");
1214−
1215− QTest::newRow("immediate gesture recognition") << true;
1216− QTest::newRow("default gesture recognition") << false;
1217−}
1218−
1219void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCanceled()
962void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCanceled()
1220{
963{
1221 DirectionalDragArea *edgeDragArea =
964 DirectionalDragArea *edgeDragArea =
1222 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
965 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1223 QVERIFY(edgeDragArea != nullptr);
966 QVERIFY(edgeDragArea != nullptr);
1224− edgeDragArea->setRecognitionTimer(fakeTimer);
967+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
1225− edgeDragArea->setTimeSource(fakeTimeSource);
968+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1226
969
1227 QQuickMouseArea *mouseArea =
970 QQuickMouseArea *mouseArea =
1228 m_view->rootObject()->findChild<QQuickMouseArea*>("mouseArea");
971 m_view->rootObject()->findChild<QQuickMouseArea*>("mouseArea");
@@ -1233,12 +976,12 @@ void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCancel
1233 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
976 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
1234 QPointF touchPoint = initialTouchPos;
977 QPointF touchPoint = initialTouchPos;
1235
978
1236− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2;
979+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2;
1237 QPointF dragDirectionVector(1.0f, 0.0f); // rightwards
980 QPointF dragDirectionVector(1.0f, 0.0f); // rightwards
1238− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
981+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
1239 QPointF touchMovement = dragDirectionVector * movementStepDistance;
982 QPointF touchMovement = dragDirectionVector * movementStepDistance;
1240 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
983 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
1241− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
984+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
1242
985
1243 QCOMPARE(mouseArea->pressed(), false);
986 QCOMPARE(mouseArea->pressed(), false);
1244
987
@@ -1259,13 +1002,305 @@ void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCancel
1259 // As the DirectionalDragArea recognizes the gesture, it grabs the touch from the MouseArea,
1002 // As the DirectionalDragArea recognizes the gesture, it grabs the touch from the MouseArea,
1260 // which should make the MouseArea get a cancelation event, which will then cause it to
1003 // which should make the MouseArea get a cancelation event, which will then cause it to
1261 // reset its state (going back to "unpressed"/"released").
1004 // reset its state (going back to "unpressed"/"released").
1262− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
1005+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1263 QCOMPARE(mouseArea->pressed(), false);
1006 QCOMPARE(mouseArea->pressed(), false);
1264 QCOMPARE(mouseAreaSpy.canceledCount, 1);
1007 QCOMPARE(mouseAreaSpy.canceledCount, 1);
1265
1008
1266 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
1009 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
1267}
1010}
1268
1011
1012+void tst_DirectionalDragArea::interleavedTouches()
1013+{
1014+ DirectionalDragArea *edgeDragArea =
1015+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1016+ QVERIFY(edgeDragArea != 0);
1017+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
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(edgeDragArea));
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(edgeDragArea));
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(edgeDragArea));
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(edgeDragArea));
1266+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1267+
1268+ edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1269+
1270+ StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1271+
1272+ sendTouchPress(41097, 24, QPointF(13.9909,827.177));
1273+ sendTouchUpdate(41120, 24, QPointF(19.2375,825.677));
1274+ sendTouchUpdate(41138, 24, QPointF(18.4057,826.177));
1275+ sendTouchUpdate(41161, 24, QPointF(20.1067,825.867));
1276+ sendTouchUpdate(41177, 24, QPointF(21.8869,824.977));
1277+ sendTouchUpdate(41193, 24, QPointF(24.7603,823.494));
1278+ sendTouchUpdate(41211, 24, QPointF(28.3889,821.725));
1279+ sendTouchUpdate(41229, 24, QPointF(32.2909,819.955));
1280+ sendTouchUpdate(41247, 24, QPointF(38.2251,817.431));
1281+ sendTouchUpdate(41266, 24, QPointF(52.4182,814.223));
1282+ sendTouchUpdate(41284, 24, QPointF(85.8465,809.483));
1283+ sendTouchUpdate(41302, 24, QPointF(126.091,802.741));
1284+ sendTouchUpdate(41320, 24, QPointF(153.171,797.977));
1285+ sendTouchUpdate(41338, 24, QPointF(170.565,795.077));
1286+ sendTouchUpdate(41356, 24, QPointF(178.685,794.101));
1287+ sendTouchUpdate(41375, 24, QPointF(183.706,793.225));
1288+ sendTouchUpdate(41393, 24, QPointF(186.112,793.19));
1289+ sendTouchUpdate(41411, 24, QPointF(187.634,793.19));
1290+ sendTouchUpdate(41429, 24, QPointF(188.505,793.19));
1291+ sendTouchUpdate(41532, 24, QPointF(187.816,793.19));
1292+ sendTouchUpdate(41538, 24, QPointF(186.902,793.19));
1293+ sendTouchUpdate(41557, 24, QPointF(186.01,793.19));
1294+ sendTouchUpdate(41575, 24, QPointF(185.125,793.444));
1295+ sendTouchUpdate(41593, 24, QPointF(184.229,793.69));
1296+ sendTouchUpdate(41605, 24, QPointF(183.88,793.69));
1297+ sendTouchRelease(41607, 24, QPointF(183.88,793.69));
1298+
1299+ QCOMPARE(statusSpy->recognized(), true);
1300+
1301+ delete statusSpy;
1302+}
1303+
1269QTEST_MAIN(tst_DirectionalDragArea)
1304QTEST_MAIN(tst_DirectionalDragArea)
1270
1305
1271#include "tst_DirectionalDragArea.moc"
1306#include "tst_DirectionalDragArea.moc"
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml+8−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -16,12 +16,19 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19+import Unity.Test 0.1
19
20
20Rectangle {
21Rectangle {
21 width: units.gu(60)
22 width: units.gu(60)
22 height: units.gu(60)
23 height: units.gu(60)
23 color: "white"
24 color: "white"
24
25
26+ Binding {
27+ target: MouseTouchAdaptor
28+ property: "enabled"
29+ value: true
30+ }
31+
25 MouseArea {
32 MouseArea {
26 id: mouseArea
33 id: mouseArea
27 objectName: "mouseArea"
34 objectName: "mouseArea"
tests/plugins/Ubuntu/Gestures/tst_FloatingFlickable.cppnew file+122
@@ -0,0 +1,122 @@
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 <QtTest>
18+#include <QQuickView>
19+
20+// Ubuntu.Gestures plugin
21+#include <DirectionalDragArea.h>
22+#include <FloatingFlickable.h>
23+
24+#include "GestureTest.h"
25+#include "TestItem.h"
26+
27+using namespace UbuntuGestures;
28+
29+class tst_FloatingFlickable: public GestureTest
30+{
31+ Q_OBJECT
32+public:
33+ tst_FloatingFlickable();
34+private Q_SLOTS:
35+ void tapGoesThrough();
36+ void flickChangesContentX();
37+ void flickChangesContentY();
38+};
39+
40+tst_FloatingFlickable::tst_FloatingFlickable()
41+ : GestureTest(QStringLiteral("tst_FloatingFlickable.qml"))
42+{
43+}
44+
45+void tst_FloatingFlickable::tapGoesThrough()
46+{
47+ FloatingFlickable *floatingFlickable =
48+ m_view->rootObject()->findChild<FloatingFlickable*>("floatingFlickable");
49+ QVERIFY(floatingFlickable != nullptr);
50+
51+ TestItem *testItem = new TestItem;
52+ testItem->setWidth(floatingFlickable->width());
53+ testItem->setHeight(floatingFlickable->height());
54+ testItem->setParentItem(m_view->rootObject());
55+ testItem->setZ(1.0);
56+
57+ floatingFlickable->setZ(2.0);
58+
59+ QTest::touchEvent(m_view, m_device)
60+ .press(0, QPoint(floatingFlickable->width()/2, floatingFlickable->height()/2));
61+
62+ QCOMPARE(testItem->touchEventsReceived.count(), 1);
63+}
64+
65+void tst_FloatingFlickable::flickChangesContentX()
66+{
67+ FloatingFlickable *floatingFlickable =
68+ m_view->rootObject()->findChild<FloatingFlickable*>("floatingFlickable");
69+ QVERIFY(floatingFlickable != nullptr);
70+
71+ floatingFlickable->m_dragArea->removeTimeConstraints();
72+
73+ qreal startContentX = floatingFlickable->contentX();
74+
75+ QPoint startPos(floatingFlickable->width() - 5, 10);
76+ int stepCount = 20;
77+ int step = (startPos.x() - 5) / stepCount;
78+
79+ QTest::touchEvent(m_view, m_device).press(0, startPos);
80+ for (int i = 0; i < stepCount; ++i) {
81+ QTest::qWait(10);
82+ QPoint touchPos = startPos - QPoint(step*i, 0);
83+ QTest::touchEvent(m_view, m_device).move(0, touchPos);
84+ }
85+
86+ QTest::qWait(10);
87+ QTest::touchEvent(m_view, m_device).release(0, QPoint(5, 10));
88+
89+ QVERIFY(floatingFlickable->contentX() > (startContentX + floatingFlickable->width()/2));
90+}
91+
92+void tst_FloatingFlickable::flickChangesContentY()
93+{
94+ FloatingFlickable *floatingFlickable =
95+ m_view->rootObject()->findChild<FloatingFlickable*>("floatingFlickable");
96+ QVERIFY(floatingFlickable != nullptr);
97+
98+ floatingFlickable->setDirection(Direction::Vertical);
99+ floatingFlickable->m_dragArea->removeTimeConstraints();
100+
101+ qreal startContentY = floatingFlickable->contentY();
102+
103+ QPoint startPos(10, floatingFlickable->height() - 5);
104+ int stepCount = 20;
105+ int step = (startPos.y() - 5) / stepCount;
106+
107+ QTest::touchEvent(m_view, m_device).press(0, startPos);
108+ for (int i = 0; i < stepCount; ++i) {
109+ QTest::qWait(10);
110+ QPoint touchPos = startPos - QPoint(0, step*i);
111+ QTest::touchEvent(m_view, m_device).move(0, touchPos);
112+ }
113+
114+ QTest::qWait(10);
115+ QTest::touchEvent(m_view, m_device).release(0, QPoint(10, 5));
116+
117+ QVERIFY(floatingFlickable->contentY() > (startContentY + floatingFlickable->height()/2));
118+}
119+
120+QTEST_MAIN(tst_FloatingFlickable)
121+
122+#include "tst_FloatingFlickable.moc"
tests/plugins/Ubuntu/Gestures/tst_FloatingFlickable.qmlnew file+80
@@ -0,0 +1,80 @@
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 Ubuntu.Components 0.1
19+import Ubuntu.Gestures 0.1
20+import Unity.Test 0.1
21+
22+Rectangle {
23+ id: root
24+ width: units.gu(60)
25+ height: units.gu(60)
26+ color: "white"
27+
28+ Binding {
29+ target: MouseTouchAdaptor
30+ property: "enabled"
31+ value: true
32+ }
33+
34+ Grid {
35+ id: colorGrid
36+ readonly property bool horizontal: floatingFlickable.direction === Direction.Horizontal
37+ x: horizontal ? -floatingFlickable.contentX : 0
38+ y: horizontal ? 0 : -floatingFlickable.contentY
39+ rows: horizontal ? 1 : 100
40+ columns: horizontal ? 100 : 1
41+ Repeater {
42+ model: 100
43+ Rectangle {
44+ width: colorGrid.horizontal ? units.gu(12) : root.width
45+ height: colorGrid.horizontal ? root.height : units.gu(12)
46+ color: mouseArea.pressed ? "red"
47+ : Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
48+ MouseArea {
49+ id: mouseArea
50+ anchors.fill: parent
51+ }
52+ }
53+ }
54+ }
55+
56+ FloatingFlickable {
57+ id: floatingFlickable
58+ objectName: "floatingFlickable"
59+ anchors.fill: parent
60+ contentWidth: colorGrid.width
61+ contentHeight: colorGrid.height
62+ }
63+
64+ Button {
65+ anchors.bottom: parent.bottom
66+ anchors.right: parent.right
67+ anchors.margins: units.gu(1)
68+
69+ text: floatingFlickable.direction === Direction.Horizontal ? "Horizontal" : "Vertical"
70+ activeFocusOnPress: false
71+
72+ onClicked: {
73+ if (floatingFlickable.direction === Direction.Horizontal) {
74+ floatingFlickable.direction = Direction.Vertical;
75+ } else {
76+ floatingFlickable.direction = Direction.Horizontal;
77+ }
78+ }
79+ }
80+}
tests/plugins/Ubuntu/Gestures/tst_PressedOutsideNotifier.cpp+1−1
@@ -28,7 +28,7 @@ class tst_PressedOutsideNotifier: public GestureTest
28public:
28public:
29 tst_PressedOutsideNotifier();
29 tst_PressedOutsideNotifier();
30private Q_SLOTS:
30private Q_SLOTS:
31− void init(); // called right before each and every test function is executed
31+ void init() override; // called right before each and every test function is executed
32
32
33 void touchOutsideAreaTriggersSignal();
33 void touchOutsideAreaTriggersSignal();
34 void touchInsideAreaHasNoEffect();
34 void touchInsideAreaHasNoEffect();
tests/plugins/Ubuntu/Gestures/tst_TouchDispatcher.cpp+5−6
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -81,8 +81,7 @@ void tst_TouchDispatcher::sendMouseEventIfTouchIgnored()
81 event->accept();
81 event->accept();
82 };
82 };
83
83
84− touchDispatcher.dispatch(QEvent::TouchBegin, m_device, Qt::NoModifier, touchPoints, m_view,
84+ touchDispatcher.dispatch(m_device, Qt::NoModifier, touchPoints, m_view, 12345 /* timestamp */);
85− 12345 /* timestamp */);
86
85
87 QVERIFY(gotTouchEvent);
86 QVERIFY(gotTouchEvent);
88 QCOMPARE(gotMousePressEvent, shouldGetMousePress);
87 QCOMPARE(gotMousePressEvent, shouldGetMousePress);
@@ -128,18 +127,18 @@ void tst_TouchDispatcher::mouseDoubleClick()
128 }
127 }
129 ulong timestamp = 12345;
128 ulong timestamp = 12345;
130
129
131− touchDispatcher.dispatch(QEvent::TouchBegin, m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
130+ touchDispatcher.dispatch(m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
132
131
133 touchPoints[0].setState(Qt::TouchPointReleased);
132 touchPoints[0].setState(Qt::TouchPointReleased);
134 timestamp += doubleClickInterval / 10;
133 timestamp += doubleClickInterval / 10;
135
134
136− touchDispatcher.dispatch(QEvent::TouchEnd, m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
135+ touchDispatcher.dispatch(m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
137
136
138 touchPoints[0].setId(1);
137 touchPoints[0].setId(1);
139 touchPoints[0].setState(Qt::TouchPointPressed);
138 touchPoints[0].setState(Qt::TouchPointPressed);
140 timestamp += timeBetweenClicks;
139 timestamp += timeBetweenClicks;
141
140
142− touchDispatcher.dispatch(QEvent::TouchBegin, m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
141+ touchDispatcher.dispatch(m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
143
142
144 QCOMPARE(gotDoubleClickEvent, shouldSendDoubleClick);
143 QCOMPARE(gotDoubleClickEvent, shouldSendDoubleClick);
145}
144}
tests/plugins/Ubuntu/Gestures/tst_TouchGate.cpp+54−22
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -29,16 +29,7 @@
29#include <TouchGate.h>
29#include <TouchGate.h>
30#include <TouchRegistry.h>
30#include <TouchRegistry.h>
31
31
32−class TestItem : public QQuickItem
32+#include "TestItem.h"
33−{
34− Q_OBJECT
35−
36−public:
37− QList<QSharedPointer<QTouchEvent>> touchEventsReceived;
38−
39−protected:
40− void touchEvent(QTouchEvent *event) override;
41−};
42
33
43class CandidateItem : public QQuickItem
34class CandidateItem : public QQuickItem
44{
35{
@@ -63,6 +54,7 @@ private Q_SLOTS:
63 void init(); // called right before each and every test function is executed
54 void init(); // called right before each and every test function is executed
64 void cleanup(); // called right after each and every test function is executed
55 void cleanup(); // called right after each and every test function is executed
65
56
57+private:
66 void onlyCandidate_passThrough();
58 void onlyCandidate_passThrough();
67 void holdsEventsUntilGainsOwnership();
59 void holdsEventsUntilGainsOwnership();
68 void holdsEventsUntilGainsOwnership_data();
60 void holdsEventsUntilGainsOwnership_data();
@@ -70,6 +62,9 @@ private Q_SLOTS:
70 void dontDispacthToDisabledOrInvisibleTarget();
62 void dontDispacthToDisabledOrInvisibleTarget();
71 void dontDispacthToDisabledOrInvisibleTarget_data();
63 void dontDispacthToDisabledOrInvisibleTarget_data();
72
64
65+private Q_SLOTS:
66+ void disabledWhileHoldingTouch();
67+
73private:
68private:
74 QQuickView *createView();
69 QQuickView *createView();
75 TouchRegistry *touchRegistry;
70 TouchRegistry *touchRegistry;
@@ -100,10 +95,9 @@ void tst_TouchGate::cleanupTestCase()
100
95
101void tst_TouchGate::init()
96void tst_TouchGate::init()
102{
97{
103− touchRegistry = new TouchRegistry;
98+ touchRegistry = TouchRegistry::instance();
104
99
105 view = createView();
100 view = createView();
106− view->installEventFilter(touchRegistry);
107 view->setSource(QUrl::fromLocalFile("touchGateExample.qml"));
101 view->setSource(QUrl::fromLocalFile("touchGateExample.qml"));
108 view->show();
102 view->show();
109 QVERIFY(QTest::qWaitForWindowExposed(view));
103 QVERIFY(QTest::qWaitForWindowExposed(view));
@@ -326,17 +320,55 @@ void tst_TouchGate::dontDispacthToDisabledOrInvisibleTarget_data()
326 QTest::newRow("disabled invisible") << false << false;
320 QTest::newRow("disabled invisible") << false << false;
327}
321}
328
322
329−///////////// TestItem /////////////////////////////////////////////////////////////
323+void tst_TouchGate::disabledWhileHoldingTouch()
330−
331−void TestItem::touchEvent(QTouchEvent *event)
332{
324{
333− QTouchEvent *clonedEvent = new QTouchEvent(event->type(),
325+ TouchGate *touchGate = view->rootObject()->findChild<TouchGate*>("touchGate");
334− event->device(),
326+ Q_ASSERT(touchGate);
335− event->modifiers(),
327+
336− event->touchPointStates(),
328+ TestItem *testItem = new TestItem;
337− event->touchPoints());
329+ testItem->setWidth(touchGate->width());
330+ testItem->setHeight(touchGate->height());
331+ testItem->setParentItem(view->rootObject());
332+ testItem->setZ(0.0);
333+
334+ touchGate->setZ(1.0);
335+ touchGate->setTargetItem(testItem);
338
336
337+ QTest::touchEvent(view, device)
338+ .press(0, QPoint(100,100));
339+
340+ QCOMPARE(testItem->touchEventsReceived.count(), 1);
341+ testItem->touchEventsReceived.clear();
342+
343+ touchGate->setEnabled(false);
344+
345+ QTest::touchEvent(view, device)
346+ .move(0, QPoint(101,101));
347+
348+ // Nothing new came as TouchGate didn't even get it because it's disabled
349+ QCOMPARE(testItem->touchEventsReceived.count(), 0);
350+
351+ touchGate->setEnabled(true);
352+
353+ QTest::touchEvent(view, device)
354+ .move(0, QPoint(102,102));
355+
356+ QTest::touchEvent(view, device)
357+ .release(0, QPoint(102,102));
358+
359+ // Nothing new came because TouchGate has already discarded touch 0
360+ QCOMPARE(testItem->touchEventsReceived.count(), 0);
361+
362+ QTest::touchEvent(view, device)
363+ .press(1, QPoint(200,200));
364+
365+ QCOMPARE(testItem->touchEventsReceived.count(), 1);
366+ {
367+ // it got only the new touch point.
339− touchEventsReceived.append(QSharedPointer<QTouchEvent>(clonedEvent));
368+ QSharedPointer<QTouchEvent> touchEvent = testItem->touchEventsReceived[0];
369+ QCOMPARE(touchEvent->touchPoints().count(), 1);
370+ QCOMPARE(touchEvent->touchPoints()[0].id(), 1);
371+ }
340}
372}
341
373
342///////////// CandidateItem /////////////////////////////////////////////////////////////
374///////////// CandidateItem /////////////////////////////////////////////////////////////
tests/plugins/Unity/Indicators/menucontentactivatortest.cpp+2−2
@@ -30,8 +30,8 @@ public:
30 : UnityIndicators::AbstractTimer(parent)
30 : UnityIndicators::AbstractTimer(parent)
31 {}
31 {}
32
32
33− virtual int interval() const { return m_duration; }
33+ int interval() const override { return m_duration; }
34− virtual void setInterval(int msecs) { m_duration = msecs; }
34+ void setInterval(int msecs) override { m_duration = msecs; }
35
35
36 void emitTimeout() {
36 void emitTimeout() {
37 if (isRunning()) {
37 if (isRunning()) {
tests/plugins/Unity/Launcher/CMakeLists.txt−1
@@ -1,6 +1,5 @@
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
2pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
3−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
4
3
5include_directories(
4include_directories(
6 ${CMAKE_CURRENT_SOURCE_DIR}
5 ${CMAKE_CURRENT_SOURCE_DIR}
tests/plugins/Unity/Launcher/launchermodeltest.cpp+24−22

This diff is too big to show here.

tests/plugins/Utils/CMakeLists.txt+1

This diff is too big to show here.

tests/plugins/Utils/homekeywatchertest.cppnew file+323

This diff is too big to show here.

tests/plugins/Utils/qlimitproxymodeltest.cpp+5−5

This diff is too big to show here.

tests/plugins/Utils/unitysortfilterproxymodeltest.cpp+5−5

This diff is too big to show here.

tests/qmltests/CMakeLists.txt+3−1

This diff is too big to show here.

tests/qmltests/Components/CMakeLists.txt+19−3

This diff is too big to show here.

tests/qmltests/Components/tst_DragHandle.cpp+15−21

This diff is too big to show here.

tests/qmltests/Components/tst_DragHandle.qml+7

This diff is too big to show here.

tests/qmltests/Components/tst_DragHandle/BidirectionalShowable.qml+2−2

This diff is too big to show here.

tests/qmltests/Components/tst_DragHandle/HorizontalShowable.qml+4−4

This diff is too big to show here.

tests/qmltests/Components/tst_DragHandle/VerticalShowable.qml+4−4

This diff is too big to show here.

tests/qmltests/Components/tst_EdgeDragEvaluator.cppnew file+165

This diff is too big to show here.

tests/qmltests/Components/tst_EdgeDragEvaluator.qmlnew file+27

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewRatingEdit.qmlnew file+80

This diff is too big to show here.

tests/qmltests/Dash/tst_Card.qml−1

This diff is too big to show here.

tests/qmltests/Dash/tst_DashContent.qml+7−2

This diff is too big to show here.

tests/qmltests/Dash/tst_GenericScopeView.qml+30−7

This diff is too big to show here.

tests/qmltests/Panel/tst_IndicatorsMenu.qml−2

This diff is too big to show here.

tests/qmltests/Panel/tst_Panel.qml−2

This diff is too big to show here.

tests/qmltests/Stages/ApplicationCheckBox.qml+49−20

This diff is too big to show here.

tests/qmltests/Stages/tst_ApplicationWindow.qml+14−23

This diff is too big to show here.

tests/qmltests/Stages/tst_DesktopStage.qml+77−67

This diff is too big to show here.

tests/qmltests/Stages/tst_PhoneStage.qml+13−66

This diff is too big to show here.

tests/qmltests/Stages/tst_SessionContainer.qml+1−64

This diff is too big to show here.

tests/qmltests/Stages/tst_SpreadDelegate.qml+172−37

This diff is too big to show here.

tests/qmltests/Stages/tst_TabletStage.qml+51

This diff is too big to show here.

tests/qmltests/Tutorial/tst_Tutorial.qml+6−1

This diff is too big to show here.

tests/qmltests/tst_OrientedShell.qmlnew file+1151

This diff is too big to show here.

tests/qmltests/tst_Shell.qml+166−117

This diff is too big to show here.

tests/qmltests/tst_ShellWithPin.qml+9−11

This diff is too big to show here.

tests/uqmlscene/main.cpp−10

This diff is too big to show here.

tests/utils/modules/Unity/Test/MouseTouchEmulationCheckbox.qml+1

This diff is too big to show here.

tests/utils/modules/Unity/Test/UnityTestCase.qml+58−11

This diff is too big to show here.

tests/utils/modules/Unity/Test/plugin.h+1−1

This diff is too big to show here.

tests/utils/modules/Unity/Test/testutil.cpp+8−20

This diff is too big to show here.

tests/utils/modules/Unity/Test/testutil.h+1−1

This diff is too big to show here.