on xenial help sign in
Merged with trunk.
d1b74f7Richard Huddie
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
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
debian/changelog+79
@@ -1,3 +1,82 @@
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+
1unity8 (8.02+15.10.20150518.1-0ubuntu1) wily; urgency=medium
80unity8 (8.02+15.10.20150518.1-0ubuntu1) wily; urgency=medium
2
81
3 [ Albert Astals Cid ]
82 [ Albert Astals Cid ]
debian/control+5−4
@@ -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,
@@ -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
plugins/Dash/CardCreator.js+24−10
@@ -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';
@@ -70,6 +69,7 @@ var kArtShapeHolderCode = 'Item { \n\
70 property bool doShapeItem: components["art"]["conciergeMode"] !== true; \n\
69 property bool doShapeItem: components["art"]["conciergeMode"] !== true; \n\
71 visible: image.status == Image.Ready; \n\
70 visible: image.status == Image.Ready; \n\
72 readonly property alias image: artImage.image; \n\
71 readonly property alias image: artImage.image; \n\
72+ property alias borderSource: artShapeShape.borderSource; \n\
73 ShaderEffectSource { \n\
73 ShaderEffectSource { \n\
74 id: artShapeSource; \n\
74 id: artShapeSource; \n\
75 sourceItem: artImage; \n\
75 sourceItem: artImage; \n\
@@ -79,6 +79,7 @@ var kArtShapeHolderCode = 'Item { \n\
79 hideSource: doShapeItem; \n\
79 hideSource: doShapeItem; \n\
80 } \n\
80 } \n\
81 Shape { \n\
81 Shape { \n\
82+ id: artShapeShape; \n\
82 image: artShapeSource; \n\
83 image: artShapeSource; \n\
83 anchors.fill: parent; \n\
84 anchors.fill: parent; \n\
84 visible: doShapeItem; \n\
85 visible: doShapeItem; \n\
@@ -315,9 +316,11 @@ var kSummaryLabelCode = 'Label { \n\
315
316
316function cardString(template, components) {
317function cardString(template, components) {
317 var code;
318 var code;
319+
320+ var templateInteractive = (template == null ? true : (template["non-interactive"] !== undefined ? !template["non-interactive"] : true)) ? "true" : "false";
321+
318 code = 'AbstractButton { \n\
322 code = 'AbstractButton { \n\
319 id: root; \n\
323 id: root; \n\
320− property var template; \n\
321 property var components; \n\
324 property var components; \n\
322 property var cardData; \n\
325 property var cardData; \n\
323 property var artShapeBorderSource: undefined; \n\
326 property var artShapeBorderSource: undefined; \n\
@@ -330,8 +333,8 @@ function cardString(template, components) {
330 property bool asynchronous: true; \n\
333 property bool asynchronous: true; \n\
331 property bool showHeader: true; \n\
334 property bool showHeader: true; \n\
332 implicitWidth: childrenRect.width; \n\
335 implicitWidth: childrenRect.width; \n\
333− enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true); \n\
336+ enabled: %1; \n\
334− \n';
337+ \n'.arg(templateInteractive);
335
338
336 var hasArt = components["art"] && components["art"]["field"] || false;
339 var hasArt = components["art"] && components["art"]["field"] || false;
337 var hasSummary = components["summary"] || false;
340 var hasSummary = components["summary"] || false;
@@ -348,7 +351,18 @@ function cardString(template, components) {
348 var hasAttributes = hasTitle && components["attributes"]["field"] || false;
351 var hasAttributes = hasTitle && components["attributes"]["field"] || false;
349
352
350 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+ }
351− code += kBackgroundLoaderCode;
365+ code += kBackgroundLoaderCode.arg(templateCardBackground).arg(backgroundElements0).arg(backgroundElements1);
352 }
366 }
353
367
354 if (hasArt) {
368 if (hasArt) {
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/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/Utils/CMakeLists.txt+1
@@ -18,6 +18,7 @@ set(QMLPLUGIN_SRC
18 Timer.cpp
18 Timer.cpp
19 unitymenumodelpaths.cpp
19 unitymenumodelpaths.cpp
20 windowkeysfilter.cpp
20 windowkeysfilter.cpp
21+ windowscreenshotprovider.cpp
21 easingcurve.cpp
22 easingcurve.cpp
22 windowstatestorage.cpp
23 windowstatestorage.cpp
23 plugin.cpp
24 plugin.cpp
plugins/Utils/plugin.cpp+4−1
@@ -23,6 +23,7 @@
23#include "plugin.h"
23#include "plugin.h"
24
24
25// local
25// local
26+#include "easingcurve.h"
26#include "HomeKeyWatcher.h"
27#include "HomeKeyWatcher.h"
27#include "inputwatcher.h"
28#include "inputwatcher.h"
28#include "qlimitproxymodelqml.h"
29#include "qlimitproxymodelqml.h"
@@ -31,7 +32,7 @@
31#include "timeformatter.h"
32#include "timeformatter.h"
32#include "unitymenumodelpaths.h"
33#include "unitymenumodelpaths.h"
33#include "windowkeysfilter.h"
34#include "windowkeysfilter.h"
34−#include "easingcurve.h"
35+#include "windowscreenshotprovider.h"
35#include "windowstatestorage.h"
36#include "windowstatestorage.h"
36#include "constants.h"
37#include "constants.h"
37
38
@@ -70,4 +71,6 @@ void UtilsPlugin::registerTypes(const char *uri)
70void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
71void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
71{
72{
72 QQmlExtensionPlugin::initializeEngine(engine, uri);
73 QQmlExtensionPlugin::initializeEngine(engine, uri);
74+
75+ engine->addImageProvider(QLatin1String("window"), new WindowScreenshotProvider);
73}
76}
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_
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/de.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: 2015-05-14 15:49+0000\n"
11+"PO-Revision-Date: 2015-06-17 14:05+0000\n"
12−"Last-Translator: Tobias Bannert <Unknown>\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-05-15 05:31+0000\n"
18+"X-Launchpad-Export-Date: 2015-06-19 05:01+0000\n"
19−"X-Generator: Launchpad (build 17493)\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 "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 "Leistung"
120+msgstr "Ausschalten"
121
121
122#: qml/Components/Dialogs.qml:153
122#: qml/Components/Dialogs.qml:153
123msgid ""
123msgid ""
@@ -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"
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/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/hu.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-05-04 18:02+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-05-05 05:47+0000\n"
18+"X-Launchpad-Export-Date: 2015-06-20 05:33+0000\n"
19−"X-Generator: Launchpad (build 17474)\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 ""
po/ia.po+94−83
@@ -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-27 10:07+0000\n"
11+"PO-Revision-Date: 2015-06-08 18:58+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12+"Last-Translator: karm <melo@carmu.com>\n"
13"Language-Team: Interlingua <ia@li.org>\n"
13"Language-Team: Interlingua <ia@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-28 05:33+0000\n"
18+"X-Launchpad-Export-Date: 2015-06-09 05:42+0000\n"
19−"X-Generator: Launchpad (build 17453)\n"
19+"X-Generator: Launchpad (build 17549)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -29,7 +29,7 @@ msgstr "Via-breve a pin"
29
29
30#: plugins/Unity/Launcher/launcheritem.cpp:100
30#: plugins/Unity/Launcher/launcheritem.cpp:100
31msgid "Unpin shortcut"
31msgid "Unpin shortcut"
32−msgstr "Via-breve levar pin"
32+msgstr "Via-breve a levar pin"
33
33
34#: qml/Components/DelayedLockscreen.qml:49
34#: qml/Components/DelayedLockscreen.qml:49
35msgid "Device Locked"
35msgid "Device Locked"
@@ -49,13 +49,13 @@ msgstr ""
49#, qt-format
49#, qt-format
50msgid "Please wait %1 minute and then try again…"
50msgid "Please wait %1 minute and then try again…"
51msgid_plural "Please wait %1 minutes and then try again…"
51msgid_plural "Please wait %1 minutes and then try again…"
52−msgstr[0] "Per favor attende %1 minuta e postea tentar de nove"
52+msgstr[0] "Per favor attende %1 minuta e postea tenta de nove"
53msgstr[1] "Per favor attende %1 minutas e postea tentar de nove"
53msgstr[1] "Per favor attende %1 minutas e postea tentar de nove"
54
54
55#: qml/Components/Dialogs.qml:69
55#: qml/Components/Dialogs.qml:69
56msgctxt "Title: Lock/Log out dialog"
56msgctxt "Title: Lock/Log out dialog"
57msgid "Log out"
57msgid "Log out"
58−msgstr ""
58+msgstr "Clauder de session"
59
59
60#: qml/Components/Dialogs.qml:70
60#: qml/Components/Dialogs.qml:70
61msgid "Are you sure you want to log out?"
61msgid "Are you sure you want to log out?"
@@ -64,12 +64,12 @@ msgstr "Desira tu vermente Clauder le Session"
64#: qml/Components/Dialogs.qml:72
64#: qml/Components/Dialogs.qml:72
65msgctxt "Button: Lock the system"
65msgctxt "Button: Lock the system"
66msgid "Lock"
66msgid "Lock"
67−msgstr ""
67+msgstr "Blocar"
68
68
69#: qml/Components/Dialogs.qml:79
69#: qml/Components/Dialogs.qml:79
70msgctxt "Button: Log out from the system"
70msgctxt "Button: Log out from the system"
71msgid "Log Out"
71msgid "Log Out"
72−msgstr ""
72+msgstr "Claude session"
73
73
74#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
74#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
75#: qml/Components/Dialogs.qml:173
75#: qml/Components/Dialogs.qml:173
@@ -79,7 +79,7 @@ msgstr "Cancellar"
79#: qml/Components/Dialogs.qml:98
79#: qml/Components/Dialogs.qml:98
80msgctxt "Title: Reboot/Shut down dialog"
80msgctxt "Title: Reboot/Shut down dialog"
81msgid "Shut down"
81msgid "Shut down"
82−msgstr ""
82+msgstr "Stoppar"
83
83
84#: qml/Components/Dialogs.qml:99
84#: qml/Components/Dialogs.qml:99
85msgid "Are you sure you want to shut down?"
85msgid "Are you sure you want to shut down?"
@@ -88,21 +88,21 @@ msgstr "Desira tu vermente Stoppar"
88#: qml/Components/Dialogs.qml:101
88#: qml/Components/Dialogs.qml:101
89msgctxt "Button: Reboot the system"
89msgctxt "Button: Reboot the system"
90msgid "Reboot"
90msgid "Reboot"
91−msgstr ""
91+msgstr "Reinitiar"
92
92
93#: qml/Components/Dialogs.qml:109
93#: qml/Components/Dialogs.qml:109
94msgctxt "Button: Shut down the system"
94msgctxt "Button: Shut down the system"
95msgid "Shut down"
95msgid "Shut down"
96−msgstr ""
96+msgstr "Stoppar"
97
97
98#: qml/Components/Dialogs.qml:129
98#: qml/Components/Dialogs.qml:129
99msgctxt "Title: Reboot dialog"
99msgctxt "Title: Reboot dialog"
100msgid "Reboot"
100msgid "Reboot"
101−msgstr ""
101+msgstr "Reinitiar"
102
102
103#: qml/Components/Dialogs.qml:130
103#: qml/Components/Dialogs.qml:130
104msgid "Are you sure you want to reboot?"
104msgid "Are you sure you want to reboot?"
105−msgstr "Desira tu vermente Reinitiar"
105+msgstr "Desira tu vermente reinitiar"
106
106
107#: qml/Components/Dialogs.qml:132
107#: qml/Components/Dialogs.qml:132
108msgid "No"
108msgid "No"
@@ -115,7 +115,7 @@ msgstr "Si"
115#: qml/Components/Dialogs.qml:152
115#: qml/Components/Dialogs.qml:152
116msgctxt "Title: Power off/Restart dialog"
116msgctxt "Title: Power off/Restart dialog"
117msgid "Power"
117msgid "Power"
118−msgstr ""
118+msgstr "Energia"
119
119
120#: qml/Components/Dialogs.qml:153
120#: qml/Components/Dialogs.qml:153
121msgid ""
121msgid ""
@@ -123,17 +123,17 @@ msgid ""
123"to power off?"
123"to power off?"
124msgstr ""
124msgstr ""
125"Desira tu vermente \n"
125"Desira tu vermente \n"
126−"Stoppar le energia?"
126+"stoppar le energia?"
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"
130msgid "Power off"
130msgid "Power off"
131−msgstr ""
131+msgstr "Stoppar le energia"
132
132
133#: qml/Components/Dialogs.qml:164
133#: qml/Components/Dialogs.qml:164
134msgctxt "Button: Restart the system"
134msgctxt "Button: Restart the system"
135msgid "Restart"
135msgid "Restart"
136−msgstr ""
136+msgstr "Reinitiar"
137
137
138#: qml/Components/Lockscreen.qml:245
138#: qml/Components/Lockscreen.qml:245
139msgid "Return to Call"
139msgid "Return to Call"
@@ -158,22 +158,22 @@ msgstr "Vider toto"
158#: qml/Dash/GenericScopeView.qml:594
158#: qml/Dash/GenericScopeView.qml:594
159msgctxt "Label: Hint for dash search line edit"
159msgctxt "Label: Hint for dash search line edit"
160msgid "Search"
160msgid "Search"
161−msgstr ""
161+msgstr "Cerca"
162
162
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 "Salva"
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 "Cerca"
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 "Configurationes"
177
177
178#: qml/Dash/PageHeader.qml:293
178#: qml/Dash/PageHeader.qml:293
179msgid "Remove from Favorites"
179msgid "Remove from Favorites"
@@ -213,7 +213,7 @@ msgstr "Relaxar pro renovar…"
213
213
214#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
214#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
215msgid "Enable location data"
215msgid "Enable location data"
216−msgstr ""
216+msgstr "Habilita le datos del location"
217
217
218#: qml/Dash/ScopesList.qml:66
218#: qml/Dash/ScopesList.qml:66
219msgid "Manage"
219msgid "Manage"
@@ -233,11 +233,11 @@ msgstr "Liberar"
233
233
234#: qml/Greeter/LoginList.qml:236
234#: qml/Greeter/LoginList.qml:236
235msgid "Retry"
235msgid "Retry"
236−msgstr ""
236+msgstr "Re-provar"
237
237
238#: qml/Greeter/LoginList.qml:237
238#: qml/Greeter/LoginList.qml:237
239msgid "Tap to unlock"
239msgid "Tap to unlock"
240−msgstr ""
240+msgstr "Pulsa pro liberar"
241
241
242#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
242#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
243msgid "Sorry, incorrect passphrase."
243msgid "Sorry, incorrect passphrase."
@@ -299,7 +299,7 @@ msgstr "Displacente, codice-contrasigno incorrecte"
299
299
300#: qml/Notifications/NotificationMenuItemFactory.qml:128
300#: qml/Notifications/NotificationMenuItemFactory.qml:128
301msgid "Show password"
301msgid "Show password"
302−msgstr "Monstra le contrasigno"
302+msgstr "Monstrar le contrasigno"
303
303
304#: qml/Panel/ActiveCallHint.qml:79
304#: qml/Panel/ActiveCallHint.qml:79
305msgid "Tap to return to call..."
305msgid "Tap to return to call..."
@@ -340,7 +340,7 @@ msgstr "Fallite, colpar pro re-provar"
340#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
340#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
341msgctxt "Label: Hint in message indicator line edit"
341msgctxt "Label: Hint in message indicator line edit"
342msgid "Reply"
342msgid "Reply"
343−msgstr ""
343+msgstr "Responde"
344
344
345#: qml/Panel/Indicators/ModemInfoItem.qml:105
345#: qml/Panel/Indicators/ModemInfoItem.qml:105
346msgid "Unlock SIM"
346msgid "Unlock SIM"
@@ -352,162 +352,165 @@ msgstr "Roaming"
352
352
353#: qml/Tutorial/TutorialBottomFinish.qml:24
353#: qml/Tutorial/TutorialBottomFinish.qml:24
354msgid "This action does different things for different apps"
354msgid "This action does different things for different apps"
355−msgstr ""
355+msgstr "Iste action ha effectos differente pro applicationes differente"
356
356
357#: qml/Tutorial/TutorialBottomFinish.qml:25
357#: qml/Tutorial/TutorialBottomFinish.qml:25
358msgid "Tap here to finish."
358msgid "Tap here to finish."
359−msgstr ""
359+msgstr "Pulsa ci pro finir"
360
360
361#: qml/Tutorial/TutorialBottom.qml:28
361#: qml/Tutorial/TutorialBottom.qml:28
362msgid "Open special menus"
362msgid "Open special menus"
363−msgstr ""
363+msgstr "Aperi le menus special"
364
364
365#: qml/Tutorial/TutorialBottom.qml:29
365#: qml/Tutorial/TutorialBottom.qml:29
366msgid "Swipe up from the bottom edge."
366msgid "Swipe up from the bottom edge."
367−msgstr ""
367+msgstr "Stria in alto ex le bordo basse."
368
368
369#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
369#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
370#: qml/Tutorial/TutorialRight.qml:144
370#: qml/Tutorial/TutorialRight.qml:144
371msgid "Tap here to continue."
371msgid "Tap here to continue."
372−msgstr ""
372+msgstr "Pulsa ci pro 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 "Clicca ci pro finir"
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"
380−msgstr ""
380+msgstr "Iste es le vias breve al applicationes favorite"
381
381
382#: qml/Tutorial/TutorialLeft.qml:26
382#: qml/Tutorial/TutorialLeft.qml:26
383msgid "Open the launcher"
383msgid "Open the launcher"
384−msgstr ""
384+msgstr "Aperi le lanceator"
385
385
386#: qml/Tutorial/TutorialLeft.qml:27
386#: qml/Tutorial/TutorialLeft.qml:27
387msgid "Short swipe from the left edge."
387msgid "Short swipe from the left edge."
388−msgstr ""
388+msgstr "Stria curte ex le bordo sinistre."
389
389
390#: qml/Tutorial/TutorialPage.qml:178
390#: qml/Tutorial/TutorialPage.qml:178
391msgid "You almost got it!"
391msgid "You almost got it!"
392−msgstr ""
392+msgstr "Tu ha quasi obtenite lo!"
393
393
394#: qml/Tutorial/TutorialPage.qml:195
394#: qml/Tutorial/TutorialPage.qml:195
395msgid "Try again."
395msgid "Try again."
396−msgstr ""
396+msgstr "Prova de novo"
397
397
398#: qml/Tutorial/TutorialRight.qml:31
398#: qml/Tutorial/TutorialRight.qml:31
399msgid "To view open apps"
399msgid "To view open apps"
400−msgstr ""
400+msgstr "Aperi le applicationes pro vider"
401
401
402#: qml/Tutorial/TutorialRight.qml:32
402#: qml/Tutorial/TutorialRight.qml:32
403msgid "Long swipe from the right edge."
403msgid "Long swipe from the right edge."
404−msgstr ""
404+msgstr "Stria longemente ex le bordo dextre."
405
405
406#: qml/Tutorial/TutorialRight.qml:122
406#: qml/Tutorial/TutorialRight.qml:122
407msgid "View all your running tasks."
407msgid "View all your running tasks."
408−msgstr ""
408+msgstr "Vide tote tu tasks fluente"
409
409
410#: qml/Wizard/Page.qml:89
410#: qml/Wizard/Page.qml:89
411msgctxt "Button: Go back one page in the Wizard"
411msgctxt "Button: Go back one page in the Wizard"
412msgid "Back"
412msgid "Back"
413−msgstr ""
413+msgstr "Retro"
414
414
415#: qml/Wizard/Pages/10-welcome.qml:27
415#: qml/Wizard/Pages/10-welcome.qml:27
416msgid "Hi!"
416msgid "Hi!"
417−msgstr ""
417+msgstr "Salute!"
418
418
419#: qml/Wizard/Pages/10-welcome.qml:44
419#: qml/Wizard/Pages/10-welcome.qml:44
420msgid "Welcome to your Ubuntu phone."
420msgid "Welcome to your Ubuntu phone."
421−msgstr ""
421+msgstr "Benvenite a tu telephono Ubuntu."
422
422
423#: qml/Wizard/Pages/10-welcome.qml:52
423#: qml/Wizard/Pages/10-welcome.qml:52
424msgid "Let’s get started."
424msgid "Let’s get started."
425−msgstr ""
425+msgstr "Que nos initia."
426
426
427#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
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
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
429#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
430#: qml/Wizard/Pages/passwd-set.qml:90
430#: qml/Wizard/Pages/passwd-set.qml:90
431msgid "Continue"
431msgid "Continue"
432−msgstr ""
432+msgstr "Continua"
433
433
434#: qml/Wizard/Pages/20-sim.qml:25
434#: qml/Wizard/Pages/20-sim.qml:25
435msgid "Add a SIM card and restart your device"
435msgid "Add a SIM card and restart your device"
436−msgstr ""
436+msgstr "Adde un carta SIM e reinitia tu dispositivo"
437
437
438#: qml/Wizard/Pages/20-sim.qml:59
438#: qml/Wizard/Pages/20-sim.qml:59
439msgid "Without it, you won’t be able to make calls or use text messaging."
439msgid "Without it, you won’t be able to make calls or use text messaging."
440msgstr ""
440msgstr ""
441+"Sin illo, tu non es habile a facer appellos o usar le messages de texto."
441
442
442#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
443#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
443msgid "Skip"
444msgid "Skip"
444−msgstr ""
445+msgstr "Salta"
445
446
446#: qml/Wizard/Pages/30-passwd-type.qml:39
447#: qml/Wizard/Pages/30-passwd-type.qml:39
447msgid "Lock security"
448msgid "Lock security"
448−msgstr ""
449+msgstr "Bloco de securitate"
449
450
450#: qml/Wizard/Pages/30-passwd-type.qml:74
451#: qml/Wizard/Pages/30-passwd-type.qml:74
451msgid "Please select how you’d like to unlock your phone."
452msgid "Please select how you’d like to unlock your phone."
452−msgstr ""
453+msgstr "Per favor selige in qual maniera disblocar tu telephono."
453
454
454#: qml/Wizard/Pages/30-passwd-type.qml:97
455#: qml/Wizard/Pages/30-passwd-type.qml:97
455msgctxt "Label: Type of security method"
456msgctxt "Label: Type of security method"
456msgid "Swipe"
457msgid "Swipe"
457−msgstr ""
458+msgstr "Striar"
458
459
459#: qml/Wizard/Pages/30-passwd-type.qml:98
460#: qml/Wizard/Pages/30-passwd-type.qml:98
460msgctxt "Label: Description of security method"
461msgctxt "Label: Description of security method"
461msgid "No security"
462msgid "No security"
462−msgstr ""
463+msgstr "Nulle securitate"
463
464
464#: qml/Wizard/Pages/30-passwd-type.qml:100
465#: qml/Wizard/Pages/30-passwd-type.qml:100
465msgctxt "Label: Type of security method"
466msgctxt "Label: Type of security method"
466msgid "Passcode"
467msgid "Passcode"
467−msgstr ""
468+msgstr "Codice-contrasigno"
468
469
469#: qml/Wizard/Pages/30-passwd-type.qml:101
470#: qml/Wizard/Pages/30-passwd-type.qml:101
470msgctxt "Label: Description of security method"
471msgctxt "Label: Description of security method"
471msgid "4 digits only"
472msgid "4 digits only"
472−msgstr ""
473+msgstr "4 cifras sol"
473
474
474#: qml/Wizard/Pages/30-passwd-type.qml:103
475#: qml/Wizard/Pages/30-passwd-type.qml:103
475msgctxt "Label: Type of security method"
476msgctxt "Label: Type of security method"
476msgid "Passphrase"
477msgid "Passphrase"
477−msgstr ""
478+msgstr "Phrase-contrasigno"
478
479
479#: qml/Wizard/Pages/30-passwd-type.qml:104
480#: qml/Wizard/Pages/30-passwd-type.qml:104
480msgctxt "Label: Description of security method"
481msgctxt "Label: Description of security method"
481msgid "Numbers and letters"
482msgid "Numbers and letters"
482−msgstr ""
483+msgstr "Numeros e litteras"
483
484
484#: qml/Wizard/Pages/40-wifi.qml:29
485#: qml/Wizard/Pages/40-wifi.qml:29
485msgid "Connect to Wi‑Fi"
486msgid "Connect to Wi‑Fi"
486−msgstr ""
487+msgstr "Connecter al Wi‑Fi"
487
488
488#: qml/Wizard/Pages/40-wifi.qml:168
489#: qml/Wizard/Pages/40-wifi.qml:168
489msgid "Available networks…"
490msgid "Available networks…"
490−msgstr ""
491+msgstr "Retes disponibile..."
491
492
492#: qml/Wizard/Pages/40-wifi.qml:169
493#: qml/Wizard/Pages/40-wifi.qml:169
493msgid "No available networks."
494msgid "No available networks."
494−msgstr ""
495+msgstr "Nulle rete disponibile."
495
496
496#: qml/Wizard/Pages/50-location.qml:27
497#: qml/Wizard/Pages/50-location.qml:27
497msgid "Location"
498msgid "Location"
498−msgstr ""
499+msgstr "Location"
499
500
500#: qml/Wizard/Pages/50-location.qml:62
501#: qml/Wizard/Pages/50-location.qml:62
501msgid "Let the phone detect your location:"
502msgid "Let the phone detect your location:"
502−msgstr ""
503+msgstr "Lassar le telephono disvelar tu position:"
503
504
504#: qml/Wizard/Pages/50-location.qml:69
505#: qml/Wizard/Pages/50-location.qml:69
505msgid "Using GPS only (less accurate)"
506msgid "Using GPS only (less accurate)"
506−msgstr ""
507+msgstr "Usar sol le GPS (minus accurate)"
507
508
508#: qml/Wizard/Pages/50-location.qml:86
509#: qml/Wizard/Pages/50-location.qml:86
509msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
510msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
510msgstr ""
511msgstr ""
512+"Usar le GPS, le Wi-Fi in modo anonyme e le informationes del rete cellular "
513+"(recommendate)"
511
514
512#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
515#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
513#: qml/Wizard/Pages/50-location.qml:103
516#: qml/Wizard/Pages/50-location.qml:103
@@ -515,81 +518,89 @@ msgid ""
515"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
518"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
516"conditions</a>."
519"conditions</a>."
517msgstr ""
520msgstr ""
521+"Per le selection de iste option tu esser de accordo con le Nokia HERE <a "
522+"href='#'>terminos e conditiones</a>."
518
523
519#: qml/Wizard/Pages/50-location.qml:112
524#: qml/Wizard/Pages/50-location.qml:112
520msgid "Not at all"
525msgid "Not at all"
521−msgstr ""
526+msgstr "No, del toto"
522
527
523#: qml/Wizard/Pages/50-location.qml:124
528#: qml/Wizard/Pages/50-location.qml:124
524msgid "You can change your mind later in <b>System Settings</b>."
529msgid "You can change your mind later in <b>System Settings</b>."
525msgstr ""
530msgstr ""
531+"Tu pote cambiar tu desiderata plus tarde in <b>Configurationes del "
532+"systema</b>."
526
533
527#: qml/Wizard/Pages/60-reporting.qml:24
534#: qml/Wizard/Pages/60-reporting.qml:24
528msgid "Improving your experience"
535msgid "Improving your experience"
529−msgstr ""
536+msgstr "Affinamento de tu experientia"
530
537
531#: qml/Wizard/Pages/60-reporting.qml:36
538#: qml/Wizard/Pages/60-reporting.qml:36
532msgid ""
539msgid ""
533"Your phone is set up to automatically report errors to Canonical and its "
540"Your phone is set up to automatically report errors to Canonical and its "
534"partners, the makers of the operating system."
541"partners, the makers of the operating system."
535msgstr ""
542msgstr ""
543+"Tu telephono es configurate a reportar automaticamente le errores a "
544+"Canonical e su socios, le fabricantes del systema operative."
536
545
537#: qml/Wizard/Pages/60-reporting.qml:43
546#: qml/Wizard/Pages/60-reporting.qml:43
538msgid ""
547msgid ""
539"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
548"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
540"Privacy</b>"
549"Privacy</b>"
541msgstr ""
550msgstr ""
551+"Isto pote esser impedite in <b>Preparationes del systema</b> sub "
552+"<b>Securitate &amp; Confidentialitate</b>"
542
553
543#: qml/Wizard/Pages/80-finished.qml:24
554#: qml/Wizard/Pages/80-finished.qml:24
544msgid "All done"
555msgid "All done"
545−msgstr ""
556+msgstr "Toto preste"
546
557
547#: qml/Wizard/Pages/80-finished.qml:39
558#: qml/Wizard/Pages/80-finished.qml:39
548msgid "Nice work!"
559msgid "Nice work!"
549−msgstr ""
560+msgstr "Belle obra!"
550
561
551#: qml/Wizard/Pages/80-finished.qml:46
562#: qml/Wizard/Pages/80-finished.qml:46
552msgid "Your phone is now ready to use."
563msgid "Your phone is now ready to use."
553−msgstr ""
564+msgstr "Tu telephono es ora preste al uso."
554
565
555#: qml/Wizard/Pages/80-finished.qml:53
566#: qml/Wizard/Pages/80-finished.qml:53
556msgid "Finish"
567msgid "Finish"
557−msgstr ""
568+msgstr "Fin"
558
569
559#: qml/Wizard/Pages/here-terms.qml:27
570#: qml/Wizard/Pages/here-terms.qml:27
560msgid "Terms & Conditions"
571msgid "Terms & Conditions"
561−msgstr ""
572+msgstr "Terminos & Conditiones"
562
573
563#: qml/Wizard/Pages/passwd-confirm.qml:49
574#: qml/Wizard/Pages/passwd-confirm.qml:49
564msgid "Confirm passphrase"
575msgid "Confirm passphrase"
565−msgstr ""
576+msgstr "Confirma le phrase-contrasigno"
566
577
567#: qml/Wizard/Pages/passwd-confirm.qml:50
578#: qml/Wizard/Pages/passwd-confirm.qml:50
568msgid "Confirm passcode"
579msgid "Confirm passcode"
569−msgstr ""
580+msgstr "Confirma le codice-contrasigno"
570
581
571#: qml/Wizard/Pages/passwd-confirm.qml:53
582#: qml/Wizard/Pages/passwd-confirm.qml:53
572#: qml/Wizard/Pages/passwd-confirm.qml:54
583#: qml/Wizard/Pages/passwd-confirm.qml:54
573msgid "Please try again."
584msgid "Please try again."
574−msgstr ""
585+msgstr "Reproba per favor."
575
586
576#: qml/Wizard/Pages/passwd-set.qml:61
587#: qml/Wizard/Pages/passwd-set.qml:61
577msgid "Choose your passcode"
588msgid "Choose your passcode"
578−msgstr ""
589+msgstr "Selige tu codice-contrasigno"
579
590
580#: qml/Wizard/Pages/passwd-set.qml:68
591#: qml/Wizard/Pages/passwd-set.qml:68
581msgid "Passphrase must be 4 characters long"
592msgid "Passphrase must be 4 characters long"
582−msgstr ""
593+msgstr "Le phrase-contrasigno debe esser longe 4 characteres"
583
594
584#: qml/Wizard/StackButton.qml:39
595#: qml/Wizard/StackButton.qml:39
585#, qt-format
596#, qt-format
586msgid "〈 %1"
597msgid "〈 %1"
587−msgstr ""
598+msgstr "〈 %1"
588
599
589#: qml/Wizard/StackButton.qml:42
600#: qml/Wizard/StackButton.qml:42
590#, qt-format
601#, qt-format
591msgid "%1 〉"
602msgid "%1 〉"
592−msgstr ""
603+msgstr "%1 〉"
593
604
594#~ msgid "Log out"
605#~ msgid "Log out"
595#~ msgstr "Clauder le Session"
606#~ msgstr "Clauder le Session"
@@ -641,13 +652,6 @@ msgstr ""
641#~ msgid "Well done"
652#~ msgid "Well done"
642#~ msgstr "Ben!"
653#~ msgstr "Ben!"
643
654
644−#~ msgid ""
645−#~ "You have now mastered the edge gestures and can start using the "
646−#~ "phone<br><br>Tap on the screen to start"
647−#~ msgstr ""
648−#~ "Tu ha ora maestrate le gestos del bordo e pote initiar usar le "
649−#~ "telephono<br><br>colpar sur le schermo pro initiar"
650−
651#~ msgid "Search"
655#~ msgid "Search"
652#~ msgstr "Cercar"
656#~ msgstr "Cercar"
653
657
@@ -671,3 +675,10 @@ msgstr ""
671
675
672#~ msgid "Speaking..."
676#~ msgid "Speaking..."
673#~ msgstr "Parlante..."
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/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"
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/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/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+1−1
@@ -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/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/Greeter.qml+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()
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/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/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+157−81
@@ -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,47 +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
110
62− property bool sideStageEnabled: shell.width >= units.gu(100)
63− readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
64−
65− property int orientation
66− readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
67− onDeviceOrientationAngleChanged: {
68− if (!OrientationLock.enabled) {
69− orientation = Screen.orientation;
70− }
71− }
72− readonly property bool orientationLockEnabled: OrientationLock.enabled
73− onOrientationLockEnabledChanged: {
74− if (orientationLockEnabled) {
75− OrientationLock.savedOrientation = Screen.orientation;
76− } else {
77− orientation = Screen.orientation;
78− }
79− }
80−
81 // This is _only_ used to expose the property to autopilot tests
111 // This is _only_ used to expose the property to autopilot tests
82 readonly property string testShellMode: shellMode
112 readonly property string testShellMode: shellMode
83
113
114+ readonly property alias greeter: greeterLoader.item
115+
84 function activateApplication(appId) {
116 function activateApplication(appId) {
85 if (ApplicationManager.findApplication(appId)) {
117 if (ApplicationManager.findApplication(appId)) {
86 ApplicationManager.requestFocusApplication(appId);
118 ApplicationManager.requestFocusApplication(appId);
87 } else {
119 } else {
88− var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
120+ var execFlags = shell.usageScenario === "phone" ? ApplicationManager.ForceMainStage
121+ : ApplicationManager.NoFlag;
89 ApplicationManager.startApplication(appId, execFlags);
122 ApplicationManager.startApplication(appId, execFlags);
90 }
123 }
91 }
124 }
@@ -122,11 +155,6 @@ Item {
122 sourceSize.width: 0
155 sourceSize.width: 0
123 }
156 }
124
157
125− GSettings {
126− id: usageModeSettings
127− schema.id: "com.canonical.Unity8"
128− }
129−
130 Binding {
158 Binding {
131 target: LauncherModel
159 target: LauncherModel
132 property: "applicationManager"
160 property: "applicationManager"
@@ -138,9 +166,7 @@ Item {
138 if (ApplicationManager.count > 0) {
166 if (ApplicationManager.count > 0) {
139 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
167 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
140 }
168 }
141− if (orientationLockEnabled) {
169+ finishStartUpTimer.start();
142− orientation = OrientationLock.savedOrientation;
143− }
144 }
170 }
145
171
146 VolumeControl {
172 VolumeControl {
@@ -233,9 +259,21 @@ Item {
233 // theoretical attack where user enters lockedApp mode, then makes
259 // theoretical attack where user enters lockedApp mode, then makes
234 // the screen larger (maybe connects to monitor) and tries to enter
260 // the screen larger (maybe connects to monitor) and tries to enter
235 // tablet mode.
261 // tablet mode.
262+
236− 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 {
237− source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
274+ return "Stages/DesktopStage.qml";
238− : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
275+ }
276+ }
239
277
240 property bool interactive: tutorial.spreadEnabled
278 property bool interactive: tutorial.spreadEnabled
241 && !greeter.shown
279 && !greeter.shown
@@ -278,7 +316,12 @@ Item {
278 }
316 }
279 Binding {
317 Binding {
280 target: applicationsDisplayLoader.item
318 target: applicationsDisplayLoader.item
281− property: "orientation"
319+ property: "shellOrientationAngle"
320+ value: shell.orientationAngle
321+ }
322+ Binding {
323+ target: applicationsDisplayLoader.item
324+ property: "shellOrientation"
282 value: shell.orientation
325 value: shell.orientation
283 }
326 }
284 Binding {
327 Binding {
@@ -286,6 +329,31 @@ Item {
286 property: "background"
329 property: "background"
287 value: shell.background
330 value: shell.background
288 }
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+ }
289 }
357 }
290
358
291 Tutorial {
359 Tutorial {
@@ -350,50 +418,59 @@ Item {
350 }
418 }
351 }
419 }
352
420
353− Greeter {
421+ Loader {
354− id: greeter
422+ id: greeterLoader
355− objectName: "greeter"
356−
357− hides: [launcher, panel.indicators]
358− tabletMode: shell.sideStageEnabled
359− launcherOffset: launcher.progress
360− forcedUnlock: tutorial.running
361− background: shell.background
362−
363 anchors.fill: parent
423 anchors.fill: parent
364 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+ }
365
431
366− // avoid overlapping with Launcher's edge drag area
432+ Component {
367− // FIXME: Fix TouchRegistry & friends and remove this workaround
433+ id: integratedGreeter
368− // Issue involves launcher's DDA getting disabled on a long
434+ Greeter {
369− // left-edge drag
370− dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
371
435
372− onSessionStarted: {
436+ hides: [launcher, panel.indicators]
373− launcher.hide();
437+ tabletMode: shell.sideStageEnabled
374− }
438+ launcherOffset: launcher.progress
439+ forcedUnlock: tutorial.running
440+ background: shell.background
375
441
376− onTease: {
442+ // avoid overlapping with Launcher's edge drag area
377− 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: {
378− launcher.tease();
449+ launcher.hide();
379 }
450 }
380− }
381
451
382− onEmergencyCall: startLockedApp("dialer-app")
452+ onTease: {
453+ if (!tutorial.running) {
454+ launcher.tease();
455+ }
456+ }
383
457
384− Timer {
458+ onEmergencyCall: startLockedApp("dialer-app")
385− // See powerConnection for why this is useful
459+
386− id: showGreeterDelayed
460+ Binding {
387− interval: 1
461+ target: ApplicationManager
388− onTriggered: {
462+ property: "suspended"
389− greeter.forceShow();
463+ value: greeter.shown
390 }
464 }
391 }
465 }
466+ }
392
467
393− Binding {
468+ Timer {
394− target: ApplicationManager
469+ // See powerConnection for why this is useful
395− property: "suspended"
470+ id: showGreeterDelayed
471+ interval: 1
472+ onTriggered: {
396− value: greeter.shown
473+ greeter.forceShow();
397 }
474 }
398 }
475 }
399
476
@@ -482,20 +559,18 @@ Item {
482 expandedPanelHeight: units.gu(7)
559 expandedPanelHeight: units.gu(7)
483
560
484 indicatorsModel: Indicators.IndicatorsModel {
561 indicatorsModel: Indicators.IndicatorsModel {
485− // TODO: This should be sourced by device type (e.g. "desktop", "tablet", "phone"...)
562+ // tablet and phone both use the same profile
486− profile: indicatorProfile
563+ profile: shell.usageScenario === "desktop" ? "desktop" : "phone"
487− Component.onCompleted: load()
564+ Component.onCompleted: load();
488 }
565 }
489 }
566 }
567+
490 callHint {
568 callHint {
491 greeterShown: greeter.shown
569 greeterShown: greeter.shown
492 }
570 }
493
571
494− property bool topmostApplicationIsFullscreen:
572+ property bool mainAppIsFullscreen: shell.mainApp && shell.mainApp.fullscreen
495− ApplicationManager.focusedApplicationId &&
496− ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
497−
498− fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
573+ fullscreenMode: (mainAppIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
499 || greeter.hasLockedApp
574 || greeter.hasLockedApp
500 }
575 }
501
576
@@ -513,7 +588,7 @@ Item {
513 available: tutorial.launcherEnabled
588 available: tutorial.launcherEnabled
514 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
589 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
515 && !greeter.hasLockedApp
590 && !greeter.hasLockedApp
516− inverted: usageModeSettings.usageMode === "Staged"
591+ inverted: shell.usageScenario !== "desktop"
517 shadeBackground: !tutorial.running
592 shadeBackground: !tutorial.running
518
593
519 onShowDashHome: showHome()
594 onShowDashHome: showHome()
@@ -555,7 +630,7 @@ Item {
555 Rectangle {
630 Rectangle {
556 id: modalNotificationBackground
631 id: modalNotificationBackground
557
632
558− visible: notifications.useModal && (notifications.state == "narrow")
633+ visible: notifications.useModal
559 color: "#000000"
634 color: "#000000"
560 anchors.fill: parent
635 anchors.fill: parent
561 opacity: 0.9
636 opacity: 0.9
@@ -600,6 +675,7 @@ Item {
600
675
601 Dialogs {
676 Dialogs {
602 id: dialogs
677 id: dialogs
678+ objectName: "dialogs"
603 anchors.fill: parent
679 anchors.fill: parent
604 z: overlay.z + 10
680 z: overlay.z + 10
605 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
@@ -24,10 +24,11 @@ FocusScope {
24 // to be read from outside
24 // to be read from outside
25 readonly property bool fullscreen: application ? application.fullscreen : false
25 readonly property bool fullscreen: application ? application.fullscreen : false
26 property alias interactive: sessionContainer.interactive
26 property alias interactive: sessionContainer.interactive
27+ property bool orientationChangesEnabled: d.supportsSurfaceResize ? d.surfaceOldEnoughToBeResized : true
27
28
28 // to be set from outside
29 // to be set from outside
29 property QtObject application
30 property QtObject application
30− property int orientation
31+ property int surfaceOrientationAngle
31
32
32 QtObject {
33 QtObject {
33 id: d
34 id: d
@@ -62,6 +63,15 @@ FocusScope {
62 // Remove this when possible
63 // Remove this when possible
63 property bool surfaceInitialized: false
64 property bool surfaceInitialized: false
64
65
66+ property bool supportsSurfaceResize:
67+ application &&
68+ ((application.supportedOrientations & Qt.PortraitOrientation)
69+ || (application.supportedOrientations & Qt.InvertedPortraitOrientation))
70+ &&
71+ ((application.supportedOrientations & Qt.LandscapeOrientation)
72+ || (application.supportedOrientations & Qt.InvertedLandscapeOrientation))
73+
74+ property bool surfaceOldEnoughToBeResized: false
65 }
75 }
66
76
67 Timer {
77 Timer {
@@ -70,6 +80,12 @@ FocusScope {
70 onTriggered: { if (sessionContainer.surface) {d.surfaceInitialized = true;} }
80 onTriggered: { if (sessionContainer.surface) {d.surfaceInitialized = true;} }
71 }
81 }
72
82
83+ Timer {
84+ id: surfaceIsOldTimer
85+ interval: 1000
86+ onTriggered: { if (stateGroup.state === "surface") { d.surfaceOldEnoughToBeResized = true; } }
87+ }
88+
73 Image {
89 Image {
74 id: screenshotImage
90 id: screenshotImage
75 objectName: "screenshotImage"
91 objectName: "screenshotImage"
@@ -113,7 +129,8 @@ FocusScope {
113 // A fake application might not even have a session property.
129 // A fake application might not even have a session property.
114 session: application && application.session ? application.session : null
130 session: application && application.session ? application.session : null
115 anchors.fill: parent
131 anchors.fill: parent
116− orientation: root.orientation
132+
133+ surfaceOrientationAngle: application && application.rotatesWindowContents ? root.surfaceOrientationAngle : 0
117
134
118 onSurfaceChanged: {
135 onSurfaceChanged: {
119 if (sessionContainer.surface) {
136 if (sessionContainer.surface) {
@@ -179,15 +196,21 @@ FocusScope {
179 UbuntuNumberAnimation { target: sessionContainer.surfaceContainer; property: "opacity";
196 UbuntuNumberAnimation { target: sessionContainer.surfaceContainer; property: "opacity";
180 from: 0.0; to: 1.0
197 from: 0.0; to: 1.0
181 duration: UbuntuAnimation.BriskDuration }
198 duration: UbuntuAnimation.BriskDuration }
199+ ScriptAction { script: {
182− PropertyAction { target: splashLoader; property: "active"; value: false }
200+ splashLoader.active = false;
201+ surfaceIsOldTimer.start();
202+ } }
183 }
203 }
184 },
204 },
185 Transition {
205 Transition {
186 from: "surface"; to: "splashScreen"
206 from: "surface"; to: "splashScreen"
187 SequentialAnimation {
207 SequentialAnimation {
208+ ScriptAction { script: {
209+ surfaceIsOldTimer.stop();
210+ d.surfaceOldEnoughToBeResized = false;
188− PropertyAction { target: splashLoader; property: "active"; value: true }
211+ splashLoader.active = true;
189− PropertyAction { target: sessionContainer.surfaceContainer
212+ sessionContainer.surfaceContainer.visible = true;
190− property: "visible"; value: true }
213+ } }
191 UbuntuNumberAnimation { target: splashLoader; property: "opacity";
214 UbuntuNumberAnimation { target: splashLoader; property: "opacity";
192 from: 0.0; to: 1.0
215 from: 0.0; to: 1.0
193 duration: UbuntuAnimation.BriskDuration }
216 duration: UbuntuAnimation.BriskDuration }
@@ -198,14 +221,18 @@ FocusScope {
198 Transition {
221 Transition {
199 from: "surface"; to: "screenshot"
222 from: "surface"; to: "screenshot"
200 SequentialAnimation {
223 SequentialAnimation {
224+ ScriptAction { script: {
225+ surfaceIsOldTimer.stop();
226+ d.surfaceOldEnoughToBeResized = false;
201− PropertyAction { target: screenshotImage
227+ screenshotImage.visible = true;
202− property: "visible"; value: true }
228+ } }
203 UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
229 UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
204 from: 0.0; to: 1.0
230 from: 0.0; to: 1.0
205 duration: UbuntuAnimation.BriskDuration }
231 duration: UbuntuAnimation.BriskDuration }
232+ ScriptAction { script: {
206− PropertyAction { target: sessionContainer.surfaceContainer
233+ sessionContainer.surfaceContainer.visible = false;
207− property: "visible"; value: false }
208− ScriptAction { script: { if (sessionContainer.session) { sessionContainer.session.release(); } } }
234+ if (sessionContainer.session) { sessionContainer.session.release(); }
235+ } }
209 }
236 }
210 },
237 },
211 Transition {
238 Transition {
@@ -216,8 +243,11 @@ FocusScope {
216 UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
243 UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
217 from: 1.0; to: 0.0
244 from: 1.0; to: 0.0
218 duration: UbuntuAnimation.BriskDuration }
245 duration: UbuntuAnimation.BriskDuration }
246+ ScriptAction { script: {
219− PropertyAction { target: screenshotImage; property: "visible"; value: false }
247+ screenshotImage.visible = false;
220− PropertyAction { target: screenshotImage; property: "source"; value: "" }
248+ screenshotImage.source = "";
249+ surfaceIsOldTimer.start();
250+ } }
221 }
251 }
222 },
252 },
223 Transition {
253 Transition {
@@ -233,10 +263,12 @@ FocusScope {
233 },
263 },
234 Transition {
264 Transition {
235 from: "surface"; to: "void"
265 from: "surface"; to: "void"
236− SequentialAnimation {
266+ ScriptAction { script: {
237− PropertyAction { target: sessionContainer.surfaceContainer; property: "visible"; value: false }
267+ surfaceIsOldTimer.stop();
268+ d.surfaceOldEnoughToBeResized = false;
269+ sessionContainer.surfaceContainer.visible = false;
238− ScriptAction { script: { if (sessionContainer.session) { sessionContainer.session.release(); } } }
270+ if (sessionContainer.session) { sessionContainer.session.release(); }
239− }
271+ } }
240 },
272 },
241 Transition {
273 Transition {
242 from: "void"; to: "surface"
274 from: "void"; to: "surface"
@@ -246,6 +278,9 @@ FocusScope {
246 UbuntuNumberAnimation { target: sessionContainer.surfaceContainer; property: "opacity";
278 UbuntuNumberAnimation { target: sessionContainer.surfaceContainer; property: "opacity";
247 from: 0.0; to: 1.0
279 from: 0.0; to: 1.0
248 duration: UbuntuAnimation.BriskDuration }
280 duration: UbuntuAnimation.BriskDuration }
281+ ScriptAction { script: {
282+ surfaceIsOldTimer.start();
283+ } }
249 }
284 }
250 }
285 }
251 ]
286 ]
qml/Stages/DesktopStage.qml+23
@@ -26,6 +26,29 @@ Rectangle {
26
26
27 anchors.fill: parent
27 anchors.fill: parent
28
28
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
51+
29 property alias background: wallpaper.source
52 property alias background: wallpaper.source
30
53
31 CrossFadeImage {
54 CrossFadeImage {
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+136−37
@@ -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,8 +175,9 @@ 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 readonly property var focusedAppDelegate: {
181 readonly property var focusedAppDelegate: {
121 var index = indexOf(focusedAppId);
182 var index = indexOf(focusedAppId);
122 return index >= 0 && index < spreadRepeater.count ? spreadRepeater.itemAt(index) : null
183 return index >= 0 && index < spreadRepeater.count ? spreadRepeater.itemAt(index) : null
@@ -131,15 +192,27 @@ Rectangle {
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 {
@@ -187,9 +260,25 @@ Rectangle {
187 property int draggedDelegateCount: 0
260 property int draggedDelegateCount: 0
188 property int closingIndex: -1
261 property int closingIndex: -1
189
262
190− 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+ }
191
275
192 onShiftedContentXChanged: {
276 onShiftedContentXChanged: {
277+ if (root.beingResized) {
278+ // Flickabe.contentX wiggles during resizes. Don't react to it.
279+ return;
280+ }
281+
193 switch (phase) {
282 switch (phase) {
194 case 0:
283 case 0:
195 if (shiftedContentX > width * positionMarker2) {
284 if (shiftedContentX > width * positionMarker2) {
@@ -204,6 +293,7 @@ Rectangle {
204 }
293 }
205 break;
294 break;
206 }
295 }
296+ fullyShowingFocusedAppUpdateTimer.restart();
207 }
297 }
208
298
209 function snap() {
299 function snap() {
@@ -228,7 +318,7 @@ Rectangle {
228 snapAnimation.start();
318 snapAnimation.start();
229 return;
319 return;
230 }
320 }
231− if (applicationManager.count <= index) {
321+ if (root.applicationManager.count <= index) {
232 // 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
233 index = 0;
323 index = 0;
234 }
324 }
@@ -265,7 +355,7 @@ Rectangle {
265 ScriptAction {
355 ScriptAction {
266 script: {
356 script: {
267 if (spreadView.selectedIndex >= 0) {
357 if (spreadView.selectedIndex >= 0) {
268− applicationManager.focusApplication(applicationManager.get(spreadView.selectedIndex).appId);
358+ root.applicationManager.focusApplication(root.applicationManager.get(spreadView.selectedIndex).appId);
269
359
270 spreadView.selectedIndex = -1;
360 spreadView.selectedIndex = -1;
271 spreadView.phase = 0;
361 spreadView.phase = 0;
@@ -280,7 +370,7 @@ Rectangle {
280 // 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:
281 // 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)
282 // + 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
283− 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
284 height: parent.height
374 height: parent.height
285 Behavior on width {
375 Behavior on width {
286 enabled: spreadView.closingIndex >= 0
376 enabled: spreadView.closingIndex >= 0
@@ -303,7 +393,7 @@ Rectangle {
303 Repeater {
393 Repeater {
304 id: spreadRepeater
394 id: spreadRepeater
305 objectName: "spreadRepeater"
395 objectName: "spreadRepeater"
306− model: applicationManager
396+ model: root.applicationManager
307 delegate: TransformedSpreadDelegate {
397 delegate: TransformedSpreadDelegate {
308 id: appDelegate
398 id: appDelegate
309 objectName: "appDelegate" + index
399 objectName: "appDelegate" + index
@@ -318,11 +408,10 @@ Rectangle {
318 selected: spreadView.selectedIndex == index
408 selected: spreadView.selectedIndex == index
319 otherSelected: spreadView.selectedIndex >= 0 && !selected
409 otherSelected: spreadView.selectedIndex >= 0 && !selected
320 interactive: !spreadView.interactive && spreadView.phase === 0
410 interactive: !spreadView.interactive && spreadView.phase === 0
321− && spreadView.shiftedContentX === 0 && root.interactive && isFocused
411+ && priv.fullyShowingFocusedApp && root.interactive && isFocused
322 swipeToCloseEnabled: spreadView.interactive && root.interactive && !snapAnimation.running
412 swipeToCloseEnabled: spreadView.interactive && root.interactive && !snapAnimation.running
323 maximizedAppTopMargin: root.maximizedAppTopMargin
413 maximizedAppTopMargin: root.maximizedAppTopMargin
324− dropShadow: spreadView.active ||
414+ dropShadow: spreadView.active || priv.focusedAppDelegateIsDislocated
325− (priv.focusedAppDelegate && priv.focusedAppDelegate.x !== 0)
326 focusFirstApp: root.focusFirstApp
415 focusFirstApp: root.focusFirstApp
327
416
328 readonly property bool isDash: model.appId == "unity8-dash"
417 readonly property bool isDash: model.appId == "unity8-dash"
@@ -345,7 +434,7 @@ Rectangle {
345 return spreadView.width + spreadIndex * spreadView.tileDistance;
434 return spreadView.width + spreadIndex * spreadView.tileDistance;
346 }
435 }
347
436
348− application: applicationManager.get(index)
437+ application: root.applicationManager.get(index)
349 closeable: !isDash
438 closeable: !isDash
350
439
351 property real behavioredIndex: index
440 property real behavioredIndex: index
@@ -361,18 +450,16 @@ Rectangle {
361 }
450 }
362 }
451 }
363
452
453+ property var xBehavior: xBehavior
364 Behavior on x {
454 Behavior on x {
455+ id: xBehavior
365 enabled: root.spreadEnabled &&
456 enabled: root.spreadEnabled &&
366 !spreadView.active &&
457 !spreadView.active &&
367 !snapAnimation.running &&
458 !snapAnimation.running &&
368− priv.animateX
459+ priv.animateX &&
460+ !root.beingResized
369 UbuntuNumberAnimation {
461 UbuntuNumberAnimation {
370 duration: UbuntuAnimation.BriskDuration
462 duration: UbuntuAnimation.BriskDuration
371− onRunningChanged: {
372− if (!running && root.inverseProgress == 0) {
373− spreadView.focusChanging = false;
374− }
375− }
376 }
463 }
377 }
464 }
378
465
@@ -412,22 +499,21 @@ Rectangle {
412 }
499 }
413
500
414 // Hiding tiles when their progress is negative or reached the maximum
501 // Hiding tiles when their progress is negative or reached the maximum
415− visible: (progress >= 0 && progress < 1.7) ||
502+ visible: (progress >= 0 && progress < 1.7)
416− (isDash && priv.focusedAppDelegate.x !== 0)
503+ || (isDash && priv.focusedAppDelegateIsDislocated)
417
504
418− Binding {
505+
419− target: appDelegate
506+ shellOrientationAngle: root.shellOrientationAngle
420− property: "orientation"
507+ shellOrientation: root.shellOrientation
421− when: appDelegate.interactive
508+ shellPrimaryOrientation: root.shellPrimaryOrientation
422− value: root.orientation
509+ nativeOrientation: root.nativeOrientation
423− }
424
510
425 onClicked: {
511 onClicked: {
426 if (root.altTabEnabled && spreadView.phase == 2) {
512 if (root.altTabEnabled && spreadView.phase == 2) {
427− if (applicationManager.focusedApplicationId == applicationManager.get(index).appId) {
513+ if (root.applicationManager.focusedApplicationId == root.applicationManager.get(index).appId) {
428 spreadView.snapTo(index);
514 spreadView.snapTo(index);
429 } else {
515 } else {
430− applicationManager.requestFocusApplication(applicationManager.get(index).appId);
516+ root.applicationManager.requestFocusApplication(root.applicationManager.get(index).appId);
431 }
517 }
432 }
518 }
433 }
519 }
@@ -442,7 +528,20 @@ Rectangle {
442
528
443 onClosed: {
529 onClosed: {
444 spreadView.closingIndex = index;
530 spreadView.closingIndex = index;
445− 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
446 }
545 }
447 }
546 }
448 }
547 }
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+149−19
@@ -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
@@ -59,6 +125,13 @@ Rectangle {
59 }
125 }
60
126
61 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
62
135
63 property string mainStageAppId
136 property string mainStageAppId
64 property string sideStageAppId
137 property string sideStageAppId
@@ -76,6 +149,7 @@ Rectangle {
76 priv.sideStageAppId = focusedAppId;
149 priv.sideStageAppId = focusedAppId;
77 } else {
150 } else {
78 priv.mainStageAppId = focusedAppId;
151 priv.mainStageAppId = focusedAppId;
152+ root.mainApp = focusedApp;
79 }
153 }
80 }
154 }
81
155
@@ -166,6 +240,7 @@ Rectangle {
166
240
167 Flickable {
241 Flickable {
168 id: spreadView
242 id: spreadView
243+ objectName: "spreadView"
169 anchors.fill: parent
244 anchors.fill: parent
170 interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
245 interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
171 contentWidth: spreadRow.width - shift
246 contentWidth: spreadRow.width - shift
@@ -214,7 +289,25 @@ Rectangle {
214 property int draggedDelegateCount: 0
289 property int draggedDelegateCount: 0
215 property int closingIndex: -1
290 property int closingIndex: -1
216
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+
217 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+ }
218
311
219 property bool sideStageDragging: sideStageDragHandle.dragging
312 property bool sideStageDragging: sideStageDragHandle.dragging
220 property real sideStageDragProgress: sideStageDragHandle.progress
313 property real sideStageDragProgress: sideStageDragHandle.progress
@@ -253,7 +346,6 @@ Rectangle {
253 case "overlay":
346 case "overlay":
254 return 1;
347 return 1;
255 }
348 }
256− print("Unhandled nextInStack case! This shouldn't happen any more when the Dash is an app!");
257 return -1;
349 return -1;
258 }
350 }
259 property int nextZInStack: indexToZIndex(nextInStack)
351 property int nextZInStack: indexToZIndex(nextInStack)
@@ -289,6 +381,10 @@ Rectangle {
289 }
381 }
290
382
291 onShiftedContentXChanged: {
383 onShiftedContentXChanged: {
384+ if (root.beingResized) {
385+ // Flickabe.contentX wiggles during resizes. Don't react to it.
386+ return;
387+ }
292 if (spreadView.phase == 0 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker2) {
388 if (spreadView.phase == 0 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker2) {
293 spreadView.phase = 1;
389 spreadView.phase = 1;
294 } else if (spreadView.phase == 1 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker4) {
390 } else if (spreadView.phase == 1 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker4) {
@@ -398,8 +494,8 @@ Rectangle {
398 MouseArea {
494 MouseArea {
399 id: spreadRow
495 id: spreadRow
400 x: spreadView.contentX
496 x: spreadView.contentX
401− height: root.height
402 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
403
499
404 onClicked: {
500 onClicked: {
405 spreadView.snapTo(0);
501 spreadView.snapTo(0);
@@ -408,8 +504,9 @@ Rectangle {
408 Rectangle {
504 Rectangle {
409 id: sideStageBackground
505 id: sideStageBackground
410 color: "black"
506 color: "black"
411− anchors.fill: parent
412− 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
413 z: spreadView.indexToZIndex(priv.indexOf(priv.sideStageAppId))
510 z: spreadView.indexToZIndex(priv.indexOf(priv.sideStageAppId))
414 opacity: spreadView.phase == 0 ? 1 : 0
511 opacity: spreadView.phase == 0 ? 1 : 0
415 Behavior on opacity { UbuntuNumberAnimation {} }
512 Behavior on opacity { UbuntuNumberAnimation {} }
@@ -417,8 +514,10 @@ Rectangle {
417
514
418 Item {
515 Item {
419 id: sideStageDragHandle
516 id: sideStageDragHandle
420− 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
421 width: units.gu(2)
519 width: units.gu(2)
520+ height: priv.landscapeHeight
422 z: sideStageBackground.z
521 z: sideStageBackground.z
423 opacity: spreadView.phase <= 0 && spreadView.sideStageVisible ? 1 : 0
522 opacity: spreadView.phase <= 0 && spreadView.sideStageVisible ? 1 : 0
424 property real progress: 0
523 property real progress: 0
@@ -437,7 +536,6 @@ Rectangle {
437
536
438 Image {
537 Image {
439 anchors.centerIn: parent
538 anchors.centerIn: parent
440− anchors.horizontalCenterOffset: parent.progress * spreadView.sideStageWidth - (width - parent.width) / 2
441 width: sideStageDragHandleMouseArea.pressed ? parent.width * 2 : parent.width
539 width: sideStageDragHandleMouseArea.pressed ? parent.width * 2 : parent.width
442 height: parent.height
540 height: parent.height
443 source: "graphics/sidestage_handle@20.png"
541 source: "graphics/sidestage_handle@20.png"
@@ -450,16 +548,19 @@ Rectangle {
450 enabled: spreadView.shiftedContentX == 0
548 enabled: spreadView.shiftedContentX == 0
451 property int startX
549 property int startX
452 property var gesturePoints: new Array()
550 property var gesturePoints: new Array()
551+ property real totalDiff
453
552
454 onPressed: {
553 onPressed: {
455 gesturePoints = [];
554 gesturePoints = [];
456 startX = mouseX;
555 startX = mouseX;
556+ totalDiff = 0.0;
457 sideStageDragHandle.progress = 0;
557 sideStageDragHandle.progress = 0;
458 sideStageDragHandle.dragging = true;
558 sideStageDragHandle.dragging = true;
459 }
559 }
460 onMouseXChanged: {
560 onMouseXChanged: {
561+ totalDiff += mouseX - startX;
461 if (priv.mainStageAppId) {
562 if (priv.mainStageAppId) {
462− sideStageDragHandle.progress = Math.max(0, (-startX + mouseX) / spreadView.sideStageWidth);
563+ sideStageDragHandle.progress = Math.max(0, totalDiff / spreadView.sideStageWidth);
463 }
564 }
464 gesturePoints.push(mouseX);
565 gesturePoints.push(mouseX);
465 }
566 }
@@ -488,14 +589,27 @@ Rectangle {
488
589
489 Repeater {
590 Repeater {
490 id: spreadRepeater
591 id: spreadRepeater
592+ objectName: "spreadRepeater"
491 model: ApplicationManager
593 model: ApplicationManager
492
594
493 delegate: TransformedTabletSpreadDelegate {
595 delegate: TransformedTabletSpreadDelegate {
494 id: spreadTile
596 id: spreadTile
495 objectName: model.appId ? "tabletSpreadDelegate_" + model.appId
597 objectName: model.appId ? "tabletSpreadDelegate_" + model.appId
496 : "tabletSpreadDelegate_null";
598 : "tabletSpreadDelegate_null";
497− height: spreadView.height
599+ width: {
600+ if (wantsMainStage) {
601+ return spreadView.width;
602+ } else {
498− 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+ }
499 active: model.appId == priv.mainStageAppId || model.appId == priv.sideStageAppId
613 active: model.appId == priv.mainStageAppId || model.appId == priv.sideStageAppId
500 zIndex: spreadView.indexToZIndex(index)
614 zIndex: spreadView.indexToZIndex(index)
501 selected: spreadView.selectedIndex == index
615 selected: spreadView.selectedIndex == index
@@ -508,6 +622,8 @@ Rectangle {
508 application: ApplicationManager.get(index)
622 application: ApplicationManager.get(index)
509 closeable: !isDash
623 closeable: !isDash
510
624
625+ readonly property bool wantsMainStage: model.stage == ApplicationInfoInterface.MainStage
626+
511 readonly property bool isDash: model.appId == "unity8-dash"
627 readonly property bool isDash: model.appId == "unity8-dash"
512
628
513 // 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.
@@ -559,12 +675,11 @@ Rectangle {
559 return progress;
675 return progress;
560 }
676 }
561
677
562− Binding {
678+ shellOrientationAngle: wantsMainStage ? root.shellOrientationAngle : 0
563− target: spreadTile
679+ shellOrientation: wantsMainStage ? root.shellOrientation : Qt.PortraitOrientation
564− property: "orientation"
680+ shellPrimaryOrientation: wantsMainStage ? root.shellPrimaryOrientation : Qt.PortraitOrientation
565− when: spreadTile.interactive
681+ nativeOrientation: wantsMainStage ? root.nativeOrientation : Qt.PortraitOrientation
566− value: root.orientation
682+
567− }
568
683
569 onClicked: {
684 onClicked: {
570 if (spreadView.phase == 2) {
685 if (spreadView.phase == 2) {
@@ -585,6 +700,19 @@ Rectangle {
585 ApplicationManager.stopApplication(ApplicationManager.get(index).appId);
700 ApplicationManager.stopApplication(ApplicationManager.get(index).appId);
586 }
701 }
587
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+
588 EasingCurve {
716 EasingCurve {
589 id: snappingCurve
717 id: snappingCurve
590 type: EasingCurve.Linear
718 type: EasingCurve.Linear
@@ -598,9 +726,11 @@ Rectangle {
598
726
599 DirectionalDragArea {
727 DirectionalDragArea {
600 id: spreadDragArea
728 id: spreadDragArea
729+ objectName: "spreadDragArea"
601 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
730 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
602 width: root.dragAreaWidth
731 width: root.dragAreaWidth
603 direction: Direction.Leftwards
732 direction: Direction.Leftwards
733+ enabled: (spreadView.phase != 2 && root.spreadEnabled) || dragging
604
734
605 property var gesturePoints: new Array()
735 property var gesturePoints: new Array()
606
736
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 }
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
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−6
@@ -58,12 +58,6 @@ int main(int argc, const char *argv[])
58 parser.process(*application);
58 parser.process(*application);
59
59
60 ApplicationArguments qmlArgs;
60 ApplicationArguments qmlArgs;
61− if (parser.isSet(windowGeometryOption) &&
62− parser.value(windowGeometryOption).split('x').size() == 2)
63− {
64− QStringList geom = parser.value(windowGeometryOption).split('x');
65− qmlArgs.setSize(QSize(geom.at(0).toInt(), geom.at(1).toInt()));
66− }
67
61
68 if (getenv("QT_LOAD_TESTABILITY")) {
62 if (getenv("QT_LOAD_TESTABILITY")) {
69 QLibrary testLib(QLatin1String("qttestability"));
63 QLibrary testLib(QLatin1String("qttestability"));
@@ -85,6 +79,18 @@ int main(int argc, const char *argv[])
85
79
86 QQuickView* view = new QQuickView();
80 QQuickView* view = new QQuickView();
87 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+
88 view->setTitle("Scopes");
94 view->setTitle("Scopes");
89 view->rootContext()->setContextProperty("applicationArguments", &qmlArgs);
95 view->rootContext()->setContextProperty("applicationArguments", &qmlArgs);
90
96
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−8
@@ -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"
@@ -47,13 +50,15 @@ int main(int argc, const char *argv[])
47
50
48 UnityCommandLineParser parser(*application);
51 UnityCommandLineParser parser(*application);
49
52
50− QString indicatorProfile = qgetenv("UNITY_INDICATOR_PROFILE");
51− if (indicatorProfile.isEmpty()) {
52− indicatorProfile = "phone";
53− }
54−
55 ApplicationArguments qmlArgs;
53 ApplicationArguments qmlArgs;
56− 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+ }
57
62
58 // 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.
59 // 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.
@@ -79,9 +84,14 @@ int main(int argc, const char *argv[])
79 view->setResizeMode(QQuickView::SizeRootObjectToView);
84 view->setResizeMode(QQuickView::SizeRootObjectToView);
80 view->setColor("black");
85 view->setColor("black");
81 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+
82 view->engine()->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
93 view->engine()->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
83 view->rootContext()->setContextProperty("applicationArguments", &qmlArgs);
94 view->rootContext()->setContextProperty("applicationArguments", &qmlArgs);
84− view->rootContext()->setContextProperty("indicatorProfile", indicatorProfile);
85 view->rootContext()->setContextProperty("shellMode", parser.mode());
95 view->rootContext()->setContextProperty("shellMode", parser.mode());
86 if (parser.hasFrameless()) {
96 if (parser.hasFrameless()) {
87 view->setFlags(Qt::FramelessWindowHint);
97 view->setFlags(Qt::FramelessWindowHint);
@@ -94,7 +104,7 @@ int main(int argc, const char *argv[])
94 mouseTouchAdaptor = MouseTouchAdaptor::instance();
104 mouseTouchAdaptor = MouseTouchAdaptor::instance();
95 }
105 }
96
106
97− QUrl source(::qmlDirectory()+"Shell.qml");
107+ QUrl source(::qmlDirectory()+"OrientedShell.qml");
98 prependImportPaths(view->engine(), ::overrideImportPaths());
108 prependImportPaths(view->engine(), ::overrideImportPaths());
99 if (!isMirServer) {
109 if (!isMirServer) {
100 prependImportPaths(view->engine(), ::nonMirImportPaths());
110 prependImportPaths(view->engine(), ::nonMirImportPaths());
tests/autopilot/unity8/dash.py+2−2
@@ -130,7 +130,7 @@ class Dash(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
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(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
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)
tests/autopilot/unity8/fixture_setup.py+131−3
@@ -17,19 +17,147 @@
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
21−import logging
22import os
20import os
23import subprocess
21import subprocess
22+import threading
23+import fixtures
24+import logging
24
25
25import ubuntuuitoolkit
26import ubuntuuitoolkit
26from 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
27
31
28from unity8 import (
32from unity8 import (
29 get_binary_path,
33 get_binary_path,
34+ sensors,
30 process_helpers
35 process_helpers
31)
36)
32
37
38+from unity8.shell import emulators
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')
33
161
34logger = logging.getLogger(__name__)
162logger = logging.getLogger(__name__)
35
163
@@ -188,7 +316,7 @@ class LaunchMockIndicatorService(fixtures.Fixture):
188 self.ensure_not_running = ensure_not_running
316 self.ensure_not_running = ensure_not_running
189
317
190 def setUp(self):
318 def setUp(self):
191− super(LaunchMockIndicatorService, self).setUp()
319+ super().setUp()
192 if self.ensure_not_running:
320 if self.ensure_not_running:
193 self.ensure_service_not_running()
321 self.ensure_service_not_running()
194 self.addCleanup(self.stop_service)
322 self.addCleanup(self.stop_service)
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/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+12
@@ -233,6 +233,18 @@ class QQuickView(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
233 objectName='pinPadButton{}'.format(button_id)
233 objectName='pinPadButton{}'.format(button_id)
234 )
234 )
235
235
236+ def get_shell_orientation_angle(self):
237+ return self._get_shell().orientationAngle
238+
239+ def get_shell_orientation(self):
240+ return self._get_shell().orientation
241+
242+ def get_shell_primary_orientation(self):
243+ return self._get_shell().primaryOrientation
244+
245+ def get_shell_native_orientation(self):
246+ return self._get_shell().nativeOrientation
247+
236 @autopilot_logging.log_action(logger.info)
248 @autopilot_logging.log_action(logger.info)
237 def wait_for_notification(self):
249 def wait_for_notification(self):
238 """Wait for a notification dialog to appear.
250 """Wait for a notification dialog to appear.
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/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+39−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
@@ -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/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/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/Utils/CMakeLists.txt+1
@@ -16,6 +16,7 @@ set(QMLPLUGIN_SRC
16 ${CMAKE_SOURCE_DIR}/plugins/Utils/timeformatter.cpp
16 ${CMAKE_SOURCE_DIR}/plugins/Utils/timeformatter.cpp
17 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitymenumodelpaths.cpp
17 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitymenumodelpaths.cpp
18 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowkeysfilter.cpp
18 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowkeysfilter.cpp
19+ ${CMAKE_SOURCE_DIR}/plugins/Utils/windowscreenshotprovider.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/Utils/easingcurve.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/Utils/easingcurve.cpp
20 constants.cpp
21 constants.cpp
21 plugin.cpp
22 plugin.cpp
tests/mocks/Utils/plugin.cpp+3
@@ -34,6 +34,7 @@
34#include <timeformatter.h>
34#include <timeformatter.h>
35#include <unitymenumodelpaths.h>
35#include <unitymenumodelpaths.h>
36#include <windowkeysfilter.h>
36#include <windowkeysfilter.h>
37+#include <windowscreenshotprovider.h>
37#include <easingcurve.h>
38#include <easingcurve.h>
38
39
39static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
40static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
@@ -70,4 +71,6 @@ void FakeUtilsPlugin::registerTypes(const char *uri)
70void FakeUtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
71void FakeUtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
71{
72{
72 QQmlExtensionPlugin::initializeEngine(engine, uri);
73 QQmlExtensionPlugin::initializeEngine(engine, uri);
74+
75+ engine->addImageProvider(QLatin1String("window"), new WindowScreenshotProvider);
73}
76}
tests/plugins/Dash/cardcreator/1.res+3−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
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);
@@ -34,6 +33,7 @@ Item {
34 property bool doShapeItem: components["art"]["conciergeMode"] !== true;
33 property bool doShapeItem: components["art"]["conciergeMode"] !== true;
35 visible: image.status == Image.Ready;
34 visible: image.status == Image.Ready;
36 readonly property alias image: artImage.image;
35 readonly property alias image: artImage.image;
36+ property alias borderSource: artShapeShape.borderSource;
37 ShaderEffectSource {
37 ShaderEffectSource {
38 id: artShapeSource;
38 id: artShapeSource;
39 sourceItem: artImage;
39 sourceItem: artImage;
@@ -43,6 +43,7 @@ Item {
43 hideSource: doShapeItem;
43 hideSource: doShapeItem;
44 }
44 }
45 Shape {
45 Shape {
46+ id: artShapeShape;
46 image: artShapeSource;
47 image: artShapeSource;
47 anchors.fill: parent;
48 anchors.fill: parent;
48 visible: doShapeItem;
49 visible: doShapeItem;
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+3−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
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);
@@ -34,6 +33,7 @@ Item {
34 property bool doShapeItem: components["art"]["conciergeMode"] !== true;
33 property bool doShapeItem: components["art"]["conciergeMode"] !== true;
35 visible: image.status == Image.Ready;
34 visible: image.status == Image.Ready;
36 readonly property alias image: artImage.image;
35 readonly property alias image: artImage.image;
36+ property alias borderSource: artShapeShape.borderSource;
37 ShaderEffectSource {
37 ShaderEffectSource {
38 id: artShapeSource;
38 id: artShapeSource;
39 sourceItem: artImage;
39 sourceItem: artImage;
@@ -43,6 +43,7 @@ Item {
43 hideSource: doShapeItem;
43 hideSource: doShapeItem;
44 }
44 }
45 Shape {
45 Shape {
46+ id: artShapeShape;
46 image: artShapeSource;
47 image: artShapeSource;
47 anchors.fill: parent;
48 anchors.fill: parent;
48 visible: doShapeItem;
49 visible: doShapeItem;
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+3−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: 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);
@@ -34,6 +33,7 @@ Item {
34 property bool doShapeItem: components["art"]["conciergeMode"] !== true;
33 property bool doShapeItem: components["art"]["conciergeMode"] !== true;
35 visible: image.status == Image.Ready;
34 visible: image.status == Image.Ready;
36 readonly property alias image: artImage.image;
35 readonly property alias image: artImage.image;
36+ property alias borderSource: artShapeShape.borderSource;
37 ShaderEffectSource {
37 ShaderEffectSource {
38 id: artShapeSource;
38 id: artShapeSource;
39 sourceItem: artImage;
39 sourceItem: artImage;
@@ -43,6 +43,7 @@ Item {
43 hideSource: doShapeItem;
43 hideSource: doShapeItem;
44 }
44 }
45 Shape {
45 Shape {
46+ id: artShapeShape;
46 image: artShapeSource;
47 image: artShapeSource;
47 anchors.fill: parent;
48 anchors.fill: parent;
48 visible: doShapeItem;
49 visible: doShapeItem;
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/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/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+3−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013-2014 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
@@ -52,6 +52,8 @@ public:
52 QColor splashColor() const override { return QColor(0,0,0,0); }
52 QColor splashColor() const override { return QColor(0,0,0,0); }
53 QColor splashColorHeader() const override { return QColor(0,0,0,0); }
53 QColor splashColorHeader() const override { return QColor(0,0,0,0); }
54 QColor splashColorFooter() const override { return QColor(0,0,0,0); }
54 QColor splashColorFooter() const override { return QColor(0,0,0,0); }
55+ Qt::ScreenOrientations supportedOrientations() const override { return Qt::PortraitOrientation; }
56+ bool rotatesWindowContents() const override { return false; }
55
57
56 // Methods used for mocking (not in the interface)
58 // Methods used for mocking (not in the interface)
57 void setFocused(bool focused) { m_focused = focused; Q_EMIT focusedChanged(focused); }
59 void setFocused(bool focused) { m_focused = focused; Q_EMIT focusedChanged(focused); }
tests/qmltests/CMakeLists.txt+2−1
@@ -19,6 +19,7 @@ set(qmltest_DEFAULT_TARGETS qmluitests)
19set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
19set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
20set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm:${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
20set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm:${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
21
21
22+add_qml_test(. OrientedShell)
22add_qml_test(. Shell)
23add_qml_test(. Shell)
23add_qml_test(. ShellWithPin)
24add_qml_test(. ShellWithPin)
24add_qml_test(Components Background)
25add_qml_test(Components Background)
@@ -93,4 +94,4 @@ add_qml_test(Stages TabletStage)
93add_qml_test(Stages WindowMoveResizeArea)
94add_qml_test(Stages WindowMoveResizeArea)
94add_qml_test(Stages Splash)
95add_qml_test(Stages Splash)
95add_qml_test(Tutorial Tutorial)
96add_qml_test(Tutorial Tutorial)
96−add_qml_test(Wizard Wizard)
97+add_qml_test(Wizard Wizard ENVIRONMENT "OXIDE_NO_SANDBOX=1")
tests/qmltests/Dash/tst_Card.qml−1
@@ -124,7 +124,6 @@ Rectangle {
124 sourceComponent: cardTool.cardComponent
124 sourceComponent: cardTool.cardComponent
125 clip: true
125 clip: true
126 onLoaded: {
126 onLoaded: {
127− item.template = Qt.binding(function() { return cardTool.template; });
128 item.components = Qt.binding(function() { return cardTool.components; });
127 item.components = Qt.binding(function() { return cardTool.components; });
129 item.cardData = Qt.binding(function() { return Helpers.mapData(dataArea.text, cardTool.components, dataError); });
128 item.cardData = Qt.binding(function() { return Helpers.mapData(dataArea.text, cardTool.components, dataError); });
130 item.width = Qt.binding(function() { return cardTool.cardWidth || item.implicitWidth; });
129 item.width = Qt.binding(function() { return cardTool.cardWidth || item.implicitWidth; });
tests/qmltests/Dash/tst_GenericScopeView.qml+30−7
@@ -302,6 +302,7 @@ Item {
302 }
302 }
303
303
304 function closePreview() {
304 function closePreview() {
305+ tryCompare(testCase.subPageLoader, "x", 0);
305 var closePreviewMouseArea = findChild(subPageLoader.item, "pageHeader");
306 var closePreviewMouseArea = findChild(subPageLoader.item, "pageHeader");
306 mouseClick(closePreviewMouseArea, units.gu(2), units.gu(2));
307 mouseClick(closePreviewMouseArea, units.gu(2), units.gu(2));
307
308
@@ -616,27 +617,49 @@ Item {
616 waitForRendering(category0);
617 waitForRendering(category0);
617
618
618 var cardTool = findChild(category0, "cardTool");
619 var cardTool = findChild(category0, "cardTool");
619− var cardGrid = category0.item;
620−
621 cardTool.template["non-interactive"] = true;
620 cardTool.template["non-interactive"] = true;
621+ cardTool.templateChanged();
622+
623+ var category0 = findChild(categoryListView, "dashCategory0");
624+ waitForRendering(category0);
625+
626+ var cardGrid = category0.item;
622 compare(cardGrid.cardTool.template["non-interactive"], true);
627 compare(cardGrid.cardTool.template["non-interactive"], true);
623
628
624 var item0 = findChild(cardGrid, "delegate0");
629 var item0 = findChild(cardGrid, "delegate0");
625 waitForRendering(item0);
630 waitForRendering(item0);
626− item0.template = cardTool.template;
627− compare(item0.template["non-interactive"], true);
628 compare(item0.enabled, false);
631 compare(item0.enabled, false);
629 var touchdown = findChild(item0, "touchdown");
632 var touchdown = findChild(item0, "touchdown");
630
633
631 compare(touchdown.visible, false);
634 compare(touchdown.visible, false);
632− mouseClick(item0);
635+ mousePress(item0);
633 compare(touchdown.visible, false);
636 compare(touchdown.visible, false);
637+ mouseRelease(item0);
634
638
635 cardTool.template["non-interactive"] = false;
639 cardTool.template["non-interactive"] = false;
640+ cardTool.templateChanged();
636 compare(cardGrid.cardTool.template["non-interactive"], false);
641 compare(cardGrid.cardTool.template["non-interactive"], false);
637− item0.template = cardTool.template;
642+
638− compare(item0.template["non-interactive"], false);
643+ waitForRendering(category0);
644+ item0 = findChild(cardGrid, "delegate0");
639 compare(item0.enabled, true);
645 compare(item0.enabled, true);
646+ var touchdown = findChild(item0, "touchdown");
647+
648+ compare(touchdown.visible, false);
649+ mousePress(item0);
650+ compare(touchdown.visible, true);
651+ mouseRelease(item0);
652+ compare(touchdown.visible, false);
653+ closePreview();
654+ }
655+
656+ function test_carousel_borderSource() {
657+ var category = scrollToCategory("dashCategory1");
658+ var tile = findChild(category, "carouselDelegate0");
659+ tryCompareFunction(function() { return findChild(tile, "artShapeLoader") !== null; }, true);
660+ var artShapeLoader = findChild(tile, "artShapeLoader");
661+ var shape = findChildsByType(artShapeLoader, "UCUbuntuShape");
662+ compare(shape.borderSource, undefined);
640 }
663 }
641 }
664 }
642 }
665 }
tests/qmltests/Panel/tst_IndicatorsMenu.qml−2
@@ -28,8 +28,6 @@ IndicatorTest {
28 height: units.gu(71)
28 height: units.gu(71)
29 color: "white"
29 color: "white"
30
30
31− property string indicatorProfile: "phone"
32−
33 RowLayout {
31 RowLayout {
34 anchors.fill: parent
32 anchors.fill: parent
35 anchors.margins: units.gu(1)
33 anchors.margins: units.gu(1)
tests/qmltests/Panel/tst_Panel.qml−2
@@ -29,8 +29,6 @@ IndicatorTest {
29 height: units.gu(71)
29 height: units.gu(71)
30 color: "white"
30 color: "white"
31
31
32− property string indicatorProfile: "phone"
33−
34 RowLayout {
32 RowLayout {
35 anchors.fill: parent
33 anchors.fill: parent
36 anchors.margins: units.gu(1)
34 anchors.margins: units.gu(1)
tests/qmltests/Stages/tst_ApplicationWindow.qml+14−23
@@ -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 General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -50,7 +50,7 @@ Rectangle {
50 ApplicationWindow {
50 ApplicationWindow {
51 anchors.fill: parent
51 anchors.fill: parent
52 application: fakeApplication
52 application: fakeApplication
53− orientation: Qt.PortraitOrientation
53+ surfaceOrientationAngle: 0
54 interactive: true
54 interactive: true
55 focus: true
55 focus: true
56 }
56 }
@@ -210,15 +210,6 @@ Rectangle {
210 stateGroup = null;
210 stateGroup = null;
211 }
211 }
212
212
213− // wait until any transition animation has finished
214− function waitUntilTransitionsEnd() {
215− var transitions = stateGroup.transitions;
216− for (var i = 0; i < transitions.length; ++i) {
217− var transition = transitions[i];
218− tryCompare(transition, "running", false, 2000);
219− }
220− }
221−
222 function init() {
213 function init() {
223 findInterestingApplicationWindowChildren();
214 findInterestingApplicationWindowChildren();
224 }
215 }
@@ -280,12 +271,12 @@ Rectangle {
280
271
281 tryCompare(stateGroup, "state", "surface");
272 tryCompare(stateGroup, "state", "surface");
282
273
283− waitUntilTransitionsEnd();
274+ waitUntilTransitionsEnd(stateGroup);
284
275
285 setApplicationState(appSuspended);
276 setApplicationState(appSuspended);
286
277
287 verify(stateGroup.state === "surface");
278 verify(stateGroup.state === "surface");
288− waitUntilTransitionsEnd();
279+ waitUntilTransitionsEnd(stateGroup);
289 }
280 }
290
281
291 function test_killedAppShowsScreenshot() {
282 function test_killedAppShowsScreenshot() {
@@ -311,7 +302,7 @@ Rectangle {
311 initSession();
302 initSession();
312 setApplicationState(appRunning);
303 setApplicationState(appRunning);
313 tryCompare(stateGroup, "state", "surface");
304 tryCompare(stateGroup, "state", "surface");
314− waitUntilTransitionsEnd();
305+ waitUntilTransitionsEnd(stateGroup);
315
306
316 setApplicationState(appSuspended);
307 setApplicationState(appSuspended);
317
308
@@ -319,19 +310,19 @@ Rectangle {
319 setApplicationState(appStopped);
310 setApplicationState(appStopped);
320
311
321 tryCompare(stateGroup, "state", "screenshot");
312 tryCompare(stateGroup, "state", "screenshot");
322− waitUntilTransitionsEnd();
313+ waitUntilTransitionsEnd(stateGroup);
323 tryCompare(fakeApplication, "session", null);
314 tryCompare(fakeApplication, "session", null);
324
315
325 // and restart it
316 // and restart it
326 setApplicationState(appStarting);
317 setApplicationState(appStarting);
327
318
328− waitUntilTransitionsEnd();
319+ waitUntilTransitionsEnd(stateGroup);
329 verify(stateGroup.state === "screenshot");
320 verify(stateGroup.state === "screenshot");
330 verify(fakeSession === null);
321 verify(fakeSession === null);
331
322
332 setApplicationState(appRunning);
323 setApplicationState(appRunning);
333
324
334− waitUntilTransitionsEnd();
325+ waitUntilTransitionsEnd(stateGroup);
335 verify(stateGroup.state === "screenshot");
326 verify(stateGroup.state === "screenshot");
336
327
337 initSession();
328 initSession();
@@ -344,7 +335,7 @@ Rectangle {
344 initSession();
335 initSession();
345 setApplicationState(appRunning);
336 setApplicationState(appRunning);
346 tryCompare(stateGroup, "state", "surface");
337 tryCompare(stateGroup, "state", "surface");
347− waitUntilTransitionsEnd();
338+ waitUntilTransitionsEnd(stateGroup);
348
339
349 // oh, it crashed...
340 // oh, it crashed...
350 setApplicationState(appStopped);
341 setApplicationState(appStopped);
@@ -357,18 +348,18 @@ Rectangle {
357 initSession();
348 initSession();
358 setApplicationState(appRunning);
349 setApplicationState(appRunning);
359 tryCompare(stateGroup, "state", "surface");
350 tryCompare(stateGroup, "state", "surface");
360− waitUntilTransitionsEnd();
351+ waitUntilTransitionsEnd(stateGroup);
361 verify(fakeSession.surface !== null);
352 verify(fakeSession.surface !== null);
362
353
363 applicationWindowLoader.item.visible = false;
354 applicationWindowLoader.item.visible = false;
364
355
365− waitUntilTransitionsEnd();
356+ waitUntilTransitionsEnd(stateGroup);
366 verify(stateGroup.state === "surface");
357 verify(stateGroup.state === "surface");
367 verify(fakeSession.surface !== null);
358 verify(fakeSession.surface !== null);
368
359
369 applicationWindowLoader.item.visible = true;
360 applicationWindowLoader.item.visible = true;
370
361
371− waitUntilTransitionsEnd();
362+ waitUntilTransitionsEnd(stateGroup);
372 verify(stateGroup.state === "surface");
363 verify(stateGroup.state === "surface");
373 verify(fakeSession.surface !== null);
364 verify(fakeSession.surface !== null);
374 }
365 }
@@ -379,7 +370,7 @@ Rectangle {
379
370
380 tryCompare(stateGroup, "state", "surface");
371 tryCompare(stateGroup, "state", "surface");
381
372
382− waitUntilTransitionsEnd();
373+ waitUntilTransitionsEnd(stateGroup);
383
374
384 var fakeSurface = fakeSession.surface;
375 var fakeSurface = fakeSession.surface;
385 fakeSurface.touchPressCount = 0;
376 fakeSurface.touchPressCount = 0;
@@ -396,7 +387,7 @@ Rectangle {
396 initSession();
387 initSession();
397 setApplicationState(appRunning);
388 setApplicationState(appRunning);
398 tryCompare(stateGroup, "state", "surface");
389 tryCompare(stateGroup, "state", "surface");
399− waitUntilTransitionsEnd();
390+ waitUntilTransitionsEnd(stateGroup);
400
391
401 cleanupSession();
392 cleanupSession();
402
393
tests/qmltests/Stages/tst_PhoneStage.qml+5−58
@@ -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 General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -33,7 +33,9 @@ Item {
33 dragAreaWidth: units.gu(2)
33 dragAreaWidth: units.gu(2)
34 maximizedAppTopMargin: units.gu(3) + units.dp(2)
34 maximizedAppTopMargin: units.gu(3) + units.dp(2)
35 interactive: true
35 interactive: true
36+ shellOrientation: Qt.PortraitOrientation
37+ shellPrimaryOrientation: Qt.PortraitOrientation
36− orientation: Qt.PortraitOrientation
38+ nativeOrientation: Qt.PortraitOrientation
37 }
39 }
38
40
39 Binding {
41 Binding {
@@ -73,21 +75,6 @@ Item {
73 ApplicationManager.get(appList.selectedAppIndex).setState(ApplicationInfoInterface.Stopped);
75 ApplicationManager.get(appList.selectedAppIndex).setState(ApplicationInfoInterface.Stopped);
74 }
76 }
75 }
77 }
76− Button {
77− anchors { left: parent.left; right: parent.right }
78− text: "Rotate device \u27F3"
79− onClicked: {
80− if (phoneStage.orientation == Qt.PortraitOrientation) {
81− phoneStage.orientation = Qt.LandscapeOrientation;
82− } else if (phoneStage.orientation == Qt.LandscapeOrientation) {
83− phoneStage.orientation = Qt.InvertedPortraitOrientation;
84− } else if (phoneStage.orientation == Qt.InvertedPortraitOrientation) {
85− phoneStage.orientation = Qt.InvertedLandscapeOrientation;
86− } else {
87− phoneStage.orientation = Qt.PortraitOrientation;
88− }
89− }
90− }
91 }
78 }
92 ListView {
79 ListView {
93 id: appList
80 id: appList
@@ -283,46 +270,6 @@ Item {
283 compare(ApplicationManager.focusedApplicationId, selectedApp.appId);
270 compare(ApplicationManager.focusedApplicationId, selectedApp.appId);
284 }
271 }
285
272
286− function test_orientationChangeSentToFocusedApp() {
287− phoneStage.orientation = Qt.PortraitOrientation;
288− addApps(1);
289−
290− var spreadView = findChild(phoneStage, "spreadView");
291− var app = findChild(spreadView, "appDelegate0");
292− tryCompare(app, "orientation", Qt.PortraitOrientation);
293−
294− phoneStage.orientation = Qt.LandscapeOrientation;
295− tryCompare(app, "orientation", Qt.LandscapeOrientation);
296− }
297−
298− function test_orientationChangeNotSentToAppsWhileSpreadOpen() {
299− phoneStage.orientation = Qt.PortraitOrientation;
300− addApps(1);
301−
302− var spreadView = findChild(phoneStage, "spreadView");
303− var app = findChild(spreadView, "appDelegate0");
304− tryCompare(app, "orientation", Qt.PortraitOrientation);
305−
306− goToSpread();
307− phoneStage.orientation = Qt.LandscapeOrientation;
308− tryCompare(app, "orientation", Qt.PortraitOrientation);
309− }
310−
311− function test_orientationChangeNotSentToUnfocusedAppUntilItFocused() {
312− phoneStage.orientation = Qt.PortraitOrientation;
313− addApps(1);
314−
315− var spreadView = findChild(phoneStage, "spreadView");
316− var app = findChild(spreadView, "appDelegate0");
317−
318− goToSpread();
319− phoneStage.orientation = Qt.LandscapeOrientation;
320− tryCompare(app, "orientation", Qt.PortraitOrientation);
321−
322− phoneStage.select(app.application.appId);
323− tryCompare(app, "orientation", Qt.LandscapeOrientation);
324− }
325−
326 function test_backgroundClickCancelsSpread() {
273 function test_backgroundClickCancelsSpread() {
327 addApps(3);
274 addApps(3);
328
275
@@ -347,7 +294,7 @@ Item {
347 ApplicationManager.stopApplication(ApplicationManager.get(closingIndex).appId)
294 ApplicationManager.stopApplication(ApplicationManager.get(closingIndex).appId)
348 tryCompare(ApplicationManager, "count", oldCount - 1)
295 tryCompare(ApplicationManager, "count", oldCount - 1)
349 }
296 }
350− phoneStage.orientation = Qt.PortraitOrientation;
297+ phoneStage.shellOrientationAngle = 0;
351 }
298 }
352
299
353 function test_focusNewTopMostAppAfterFocusedOneClosesItself() {
300 function test_focusNewTopMostAppAfterFocusedOneClosesItself() {
tests/qmltests/Stages/tst_SessionContainer.qml+1−64
@@ -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 General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -41,7 +41,6 @@ Rectangle {
41 SessionContainer {
41 SessionContainer {
42 id: sessionContainer
42 id: sessionContainer
43 anchors.fill: parent
43 anchors.fill: parent
44− orientation: Qt.PortraitOrientation
45 focus: true
44 focus: true
46 interactive: true
45 interactive: true
47 }
46 }
@@ -117,24 +116,6 @@ Rectangle {
117 session: sessionContainerLoader.item ? sessionContainerLoader.item.session : null
116 session: sessionContainerLoader.item ? sessionContainerLoader.item.session : null
118 }
117 }
119 }
118 }
120−
121− Button {
122− anchors { left: parent.left; right: parent.right }
123− text: "Rotate device \u27F3"
124− onClicked: {
125− var orientation = sessionContainerLoader.item.orientation
126− if (orientation == Qt.PortraitOrientation) {
127− orientation = Qt.LandscapeOrientation;
128− } else if (orientation == Qt.LandscapeOrientation) {
129− orientation = Qt.InvertedPortraitOrientation;
130− } else if (orientation == Qt.InvertedPortraitOrientation) {
131− orientation = Qt.InvertedLandscapeOrientation;
132− } else {
133− orientation = Qt.PortraitOrientation;
134− }
135− sessionContainerLoader.item.orientation = orientation;
136− }
137− }
138 }
119 }
139 }
120 }
140
121
@@ -324,49 +305,5 @@ Rectangle {
324 // wait for animation to end
305 // wait for animation to end
325 tryCompareFunction(function() { return isContainerAnimating(childContainer); }, false);
306 tryCompareFunction(function() { return isContainerAnimating(childContainer); }, false);
326 }
307 }
327−
328− function test_orientationPropagatedToChildren_data() {
329− return [ { tag: "count=1", count: 1 },
330− { tag: "count=4", count: 4 } ];
331− }
332−
333− /* Test orientation changes are propagated to all children immediately */
334− function test_orientationPropagatedToChildren(data) {
335− sessionCheckbox.checked = true;
336− var rootSessionContainer = sessionContainerLoader.item;
337− compare(rootSessionContainer.childSessions.count(), 0);
338−
339− var i;
340− var sessions = [];
341− for (i = 0; i < data.count; i++) {
342− var session = ApplicationTest.addChildSession(rootSessionContainer.session,
343− "gallery");
344− session.createSurface();
345− rootSessionContainer.session.addChildSession(session);
346−
347− // Check child SessionContainer has orientation matching the parent
348− var delegate = findChild(rootSessionContainer, "childDelegate" + i);
349− var childSessionContainer = findChild(delegate, "sessionContainer");
350−
351− tryCompare(rootSessionContainer, "orientation", childSessionContainer.orientation);
352−
353− sessions.push(session);
354− }
355−
356− // Change orientation and verify all children updated
357− rootSessionContainer.orientation = Qt.LandscapeOrientation;
358−
359− for (i = 0; i < data.count; i++) {
360− var delegate = findChild(rootSessionContainer, "childDelegate" + i);
361− var childSessionContainer = findChild(delegate, "sessionContainer");
362−
363− tryCompare(rootSessionContainer, "orientation", childSessionContainer.orientation);
364− }
365−
366− // Clean up
367− for (i = data.count-1; i >= 0; i--) {
368− ApplicationTest.removeSession(sessions[i]);
369− }
370− }
371 }
308 }
372}
309}
tests/qmltests/Stages/tst_SpreadDelegate.qml+172−37
@@ -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 General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -26,46 +26,89 @@ import Unity.Application 0.1
26Rectangle {
26Rectangle {
27 color: "red"
27 color: "red"
28 id: root
28 id: root
29− width: units.gu(70)
29+ width: fakeShell.shortestDimension + controls.width
30− height: units.gu(70)
30+ height: fakeShell.longestDimension
31
31
32− Component.onCompleted: {
33− root.fakeApplication = ApplicationManager.startApplication("gallery-app");
34− }
35 property QtObject fakeApplication: null
32 property QtObject fakeApplication: null
36
33
37− Component {
34+ Item {
38− id: spreadDelegateComponent
35+ id: fakeShell
36+
37+ readonly property real shortestDimension: units.gu(40)
38+ readonly property real longestDimension: units.gu(70)
39+
40+ width: landscape ? longestDimension : shortestDimension
41+ height: landscape ? shortestDimension : longestDimension
42+
43+ x: landscape ? (height - width) / 2 : 0
44+ y: landscape ? (width - height) / 2 : 0
45+
46+ property bool landscape: orientationAngle == 90 || orientationAngle == 270
47+ property int orientationAngle: shellOrientationAngleSelector.value
48+
49+ rotation: orientationAngle
50+
51+ Loader {
52+ id: spreadDelegateLoader
53+ width: parent.width
54+ height: parent.height
55+
56+ active: false
57+ property bool itemDestroyed: false
39− SpreadDelegate {
58+ sourceComponent: SpreadDelegate {
40− anchors.fill: parent
59+ anchors.fill: parent
41− swipeToCloseEnabled: swipeToCloseCheckbox.checked
60+ swipeToCloseEnabled: swipeToCloseCheckbox.checked
42− closeable: closeableCheckbox.checked
61+ closeable: closeableCheckbox.checked
43− application: fakeApplication
62+ application: fakeApplication
44− }
63+ shellOrientationAngle: shellOrientationAngleSelector.value
45− }
64+ shellOrientation: {
46− Loader {
65+ switch (shellOrientationAngleSelector.value) {
47− id: spreadDelegateLoader
66+ case 0:
48− anchors {
67+ return Qt.PortraitOrientation;
49− top: parent.top
68+ case 90:
50− bottom: parent.bottom
69+ return Qt.InvertedLandscapeOrientation;
51− left: parent.left
70+ case 180:
71+ return Qt.InvertedPortraitOrientation;
72+ default: // 270
73+ return Qt.LandscapeOrientation;
74+ }
75+ }
76+ shellPrimaryOrientation: Qt.PortraitOrientation
77+ nativeOrientation: Qt.PortraitOrientation
78+ maximizedAppTopMargin: units.gu(3)
79+ Component.onDestruction: {
80+ spreadDelegateLoader.itemDestroyed = true;
81+ }
82+ Component.onCompleted: {
83+ spreadDelegateLoader.itemDestroyed = false;
84+ }
85+ }
52 }
86 }
53− width: units.gu(40)
54− sourceComponent: spreadDelegateComponent
55 }
87 }
56
88
57 Rectangle {
89 Rectangle {
90+ id: controls
58 color: "white"
91 color: "white"
92+ x: fakeShell.shortestDimension
93+ width: units.gu(30)
59 anchors {
94 anchors {
60 top: parent.top
95 top: parent.top
61 bottom: parent.bottom
96 bottom: parent.bottom
62− left: spreadDelegateLoader.right
63− right: parent.right
64 }
97 }
65
98
66 Column {
99 Column {
67 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
100 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
68 spacing: units.gu(1)
101 spacing: units.gu(1)
102+ Button {
103+ id: loadWithWeatherApp
104+ text: "Load with ubuntu-weather-app"
105+ onClicked: { testCase.restartWithApp("ubuntu-weather-app"); }
106+ }
107+ Button {
108+ id: loadWithGalleryApp
109+ text: "Load with gallery-app"
110+ onClicked: { testCase.restartWithApp("gallery-app"); }
111+ }
69 Row {
112 Row {
70 anchors { left: parent.left; right: parent.right }
113 anchors { left: parent.left; right: parent.right }
71 CheckBox { id: swipeToCloseCheckbox; checked: false; }
114 CheckBox { id: swipeToCloseCheckbox; checked: false; }
@@ -76,6 +119,21 @@ Rectangle {
76 CheckBox { id: closeableCheckbox; checked: false }
119 CheckBox { id: closeableCheckbox; checked: false }
77 Label { text: "closeable" }
120 Label { text: "closeable" }
78 }
121 }
122+ ListItem.ItemSelector {
123+ id: shellOrientationAngleSelector
124+ anchors { left: parent.left; right: parent.right }
125+ text: "shellOrientationAngle"
126+ model: ["0", "90", "180", "270"]
127+ property int value: selectedIndex * 90
128+ }
129+ Button {
130+ text: "matchShellOrientation()"
131+ onClicked: { spreadDelegateLoader.item.matchShellOrientation(); }
132+ }
133+ Button {
134+ text: "animateToShellOrientation()"
135+ onClicked: { spreadDelegateLoader.item.animateToShellOrientation(); }
136+ }
79 }
137 }
80 }
138 }
81
139
@@ -91,18 +149,47 @@ Rectangle {
91 }
149 }
92
150
93 property var dragArea
151 property var dragArea
152+ property Item spreadDelegate: spreadDelegateLoader.item
94
153
95 function init() {
154 function init() {
96− dragArea = findInvisibleChild(spreadDelegateLoader.item, "dragArea");
97− dragArea.__dateTime = fakeDateTime;
98 }
155 }
99
156
100 function cleanup() {
157 function cleanup() {
101− // reload our test subject to get it in a fresh state once again
158+ unloadSpreadDelegate();
159+ spyClosedSignal.clear();
160+ shellOrientationAngleSelector.selectedIndex = 0;
161+ ApplicationManager.stopApplication(root.fakeApplication.appId);
162+ root.fakeApplication = null;
163+ }
164+
165+ function restartWithApp(appId) {
102− spreadDelegateLoader.active = false;
166+ if (spreadDelegateLoader.active) {
167+ unloadSpreadDelegate();
168+ }
169+ if (root.fakeApplication) {
170+ ApplicationManager.stopApplication(root.fakeApplication.appId);
171+ }
172+
173+ root.fakeApplication = ApplicationManager.startApplication(appId);
103 spreadDelegateLoader.active = true;
174 spreadDelegateLoader.active = true;
175+ tryCompare(spreadDelegateLoader, "status", Loader.Ready);
176+
177+ dragArea = findInvisibleChild(spreadDelegate, "dragArea");
178+ dragArea.__dateTime = fakeDateTime;
179+ }
180+
181+ function unloadSpreadDelegate() {
182+ spreadDelegateLoader.active = false;
183+ tryCompare(spreadDelegateLoader, "status", Loader.Null);
184+ tryCompare(spreadDelegateLoader, "item", null);
185+ // Loader.status might be Loader.Null and Loader.item might be null but the Loader
186+ // item might still be alive. So if we set Loader.active back to true
187+ // again right now we will get the very same Shell instance back. So no reload
188+ // actually took place. Likely because Loader waits until the next event loop
189+ // iteration to do its work. So to ensure the reload, we will wait until the
190+ // Shell instance gets destroyed.
191+ tryCompare(spreadDelegateLoader, "itemDestroyed", true);
104
192
105− spyClosedSignal.clear();
106 }
193 }
107
194
108 function test_swipeToClose_data() {
195 function test_swipeToClose_data() {
@@ -119,9 +206,10 @@ Rectangle {
119 }
206 }
120
207
121 function test_swipeToClose(data) {
208 function test_swipeToClose(data) {
209+ loadWithGalleryApp.clicked();
122− var appWindowWithShadow = findChild(spreadDelegateLoader.item, "appWindowWithShadow");
210+ var displacedAppWindowWithShadow = findChild(spreadDelegateLoader.item, "displacedAppWindowWithShadow");
123
211
124− verify(appWindowWithShadow.y === 0);
212+ verify(displacedAppWindowWithShadow.y === 0);
125
213
126 swipeToCloseCheckbox.checked = data.swipeToClose;
214 swipeToCloseCheckbox.checked = data.swipeToClose;
127 closeableCheckbox.checked = data.closeable;
215 closeableCheckbox.checked = data.closeable;
@@ -135,14 +223,14 @@ Rectangle {
135 true /* beginTouch */, false /* endTouch */, dragArea.minSpeedToClose * 1.1 /* speed */);
223 true /* beginTouch */, false /* endTouch */, dragArea.minSpeedToClose * 1.1 /* speed */);
136
224
137 if (data.swipeToClose) {
225 if (data.swipeToClose) {
138− verify(appWindowWithShadow.y < 0);
226+ verify(displacedAppWindowWithShadow.y < 0);
139 var threshold = findChild(spreadDelegateLoader.item, "dragArea").threshold
227 var threshold = findChild(spreadDelegateLoader.item, "dragArea").threshold
140 if (data.closeable) {
228 if (data.closeable) {
141 // Verify that the delegate started moving exactly "threshold" after the finger movement
229 // Verify that the delegate started moving exactly "threshold" after the finger movement
142 // and did not jump up to the finger, but lags the threshold behind
230 // and did not jump up to the finger, but lags the threshold behind
143− compare(Math.abs(Math.abs(appWindowWithShadow.y) - dragDistance), threshold);
231+ compare(Math.abs(Math.abs(displacedAppWindowWithShadow.y) - dragDistance), threshold);
144 } else {
232 } else {
145− verify(Math.abs(Math.abs(appWindowWithShadow.y) - dragDistance) > threshold);
233+ verify(Math.abs(Math.abs(displacedAppWindowWithShadow.y) - dragDistance) > threshold);
146 }
234 }
147
235
148 touchRelease(spreadDelegateLoader.item, touchX, toY - units.gu(1));
236 touchRelease(spreadDelegateLoader.item, touchX, toY - units.gu(1));
@@ -153,16 +241,63 @@ Rectangle {
153 verify(spyClosedSignal.count === 1);
241 verify(spyClosedSignal.count === 1);
154 } else {
242 } else {
155 verify(spyClosedSignal.count === 0);
243 verify(spyClosedSignal.count === 0);
156− tryCompare(appWindowWithShadow, "y", 0);
244+ tryCompare(displacedAppWindowWithShadow, "y", 0);
157 }
245 }
158
246
159 } else {
247 } else {
160− verify(appWindowWithShadow.y === 0);
248+ verify(displacedAppWindowWithShadow.y === 0);
161
249
162 touchRelease(spreadDelegateLoader.item, touchX, toY);
250 touchRelease(spreadDelegateLoader.item, touchX, toY);
163 }
251 }
164 }
252 }
165
253
254+ function test_loadingLandscapeOnlyAppWhenShellInPortrait() {
255+ loadWithWeatherApp.clicked();
256+
257+ var appWindow = findChild(spreadDelegate, "appWindow_ubuntu-weather-app");
258+ verify(appWindow);
259+
260+ // It must have landscape dimensions as it does not support portrait
261+ tryCompare(appWindow, "width", fakeShell.height);
262+ tryCompare(appWindow, "height", fakeShell.width - spreadDelegate.maximizedAppTopMargin);
263+ }
264+
265+ function test_keepsSceneTransformationWhenShellRotates_data() {
266+ return [
267+ {tag: "0", selectedIndex: 0},
268+ {tag: "90", selectedIndex: 1},
269+ {tag: "180", selectedIndex: 2},
270+ {tag: "270", selectedIndex: 3}
271+ ];
272+ }
273+ function test_keepsSceneTransformationWhenShellRotates(data) {
274+ loadWithGalleryApp.clicked();
275+
276+ var appWindowWithShadow = findChild(spreadDelegate, "appWindowWithShadow");
277+ verify(appWindowWithShadow);
278+
279+ // Wait until it reaches the state we are interested on.
280+ // It begins with "startingUp"
281+ tryCompare(appWindowWithShadow, "state", "keepSceneRotation");
282+
283+ shellOrientationAngleSelector.selectedIndex = data.selectedIndex;
284+
285+ // must keep same aspect ratio
286+ compare(appWindowWithShadow.width, fakeShell.shortestDimension);
287+ compare(appWindowWithShadow.height, fakeShell.longestDimension);
288+
289+
290+ // and scene transformation must be the identity (ie, no rotation or translation)
291+ var pointInDelegateCoords = appWindowWithShadow.mapToItem(root, 0, 0);
292+ compare(pointInDelegateCoords.x, 0);
293+ compare(pointInDelegateCoords.y, 0);
294+
295+ pointInDelegateCoords = appWindowWithShadow.mapToItem(root,
296+ fakeShell.shortestDimension, fakeShell.longestDimension);
297+ compare(pointInDelegateCoords.x, fakeShell.shortestDimension);
298+ compare(pointInDelegateCoords.y, fakeShell.longestDimension);
299+ }
300+
166 function waitForCloseAnimationToFinish() {
301 function waitForCloseAnimationToFinish() {
167 var closeAnimation = findInvisibleChild(spreadDelegateLoader.item, "closeAnimation");
302 var closeAnimation = findInvisibleChild(spreadDelegateLoader.item, "closeAnimation");
168 wait(closeAnimation.duration * 1.5);
303 wait(closeAnimation.duration * 1.5);
tests/qmltests/Stages/tst_TabletStage.qml+51
@@ -49,6 +49,11 @@ Rectangle {
49 dragAreaWidth: units.gu(2)
49 dragAreaWidth: units.gu(2)
50 maximizedAppTopMargin: units.gu(3) + units.dp(2)
50 maximizedAppTopMargin: units.gu(3) + units.dp(2)
51 interactive: true
51 interactive: true
52+ shellOrientation: Qt.LandscapeOrientation
53+ shellPrimaryOrientation: Qt.LandscapeOrientation
54+ nativeOrientation: Qt.LandscapeOrientation
55+ nativeWidth: width
56+ nativeHeight: height
52 focus: true
57 focus: true
53 }
58 }
54 }
59 }
@@ -149,6 +154,52 @@ Rectangle {
149 tryCompare(dialerApp.session.surface, "activeFocus", true);
154 tryCompare(dialerApp.session.surface, "activeFocus", true);
150 tryCompare(webbrowserApp.session.surface, "activeFocus", false);
155 tryCompare(webbrowserApp.session.surface, "activeFocus", false);
151 }
156 }
157+
158+
159+ function test_closeAppInSideStage() {
160+ dialerCheckBox.checked = true;
161+ waitUntilAppSurfaceShowsUp(dialerCheckBox.appId);
162+
163+ performEdgeSwipeToShowAppSpread();
164+
165+ var appDelegate = findChild(tabletStage, "tabletSpreadDelegate_" + dialerCheckBox.appId);
166+ verify(appDelegate);
167+ tryCompare(appDelegate, "swipeToCloseEnabled", true);
168+
169+ swipeAppUpwards(dialerCheckBox.appId);
170+
171+ // Check that dialer-app has been closed
172+
173+ tryCompareFunction(function() {
174+ return findChild(tabletStage, "appWindow_" + dialerCheckBox.appId);
175+ }, null);
176+
177+ tryCompareFunction(function() {
178+ return ApplicationManager.findApplication(dialerCheckBox.appId);
179+ }, null);
180+ }
181+
182+ function performEdgeSwipeToShowAppSpread() {
183+ var touchStartY = tabletStage.height / 2;
184+ touchFlick(tabletStage,
185+ tabletStage.width - 1, touchStartY,
186+ 0, touchStartY);
187+
188+ var spreadView = findChild(tabletStage, "spreadView");
189+ verify(spreadView);
190+ tryCompare(spreadView, "phase", 2);
191+ tryCompare(spreadView, "flicking", false);
192+ tryCompare(spreadView, "moving", false);
193+ }
194+
195+ function swipeAppUpwards(appId) {
196+ var appWindow = findChild(tabletStage, "appWindow_" + appId);
197+ verify(appWindow);
198+
199+ touchFlick(appWindow,
200+ appWindow.width / 2, appWindow.height / 2,
201+ appWindow.width / 2, -appWindow.height / 2);
202+ }
152 }
203 }
153
204
154}
205}
tests/qmltests/Tutorial/tst_Tutorial.qml+1
@@ -73,6 +73,7 @@ Item {
73 sourceComponent: Component {
73 sourceComponent: Component {
74 Shell {
74 Shell {
75 property string indicatorProfile: "phone"
75 property string indicatorProfile: "phone"
76+ property string shellMode: "full-greeter" /* default */
76
77
77 Component.onDestruction: {
78 Component.onDestruction: {
78 shellLoader.itemDestroyed = true;
79 shellLoader.itemDestroyed = true;
tests/qmltests/tst_OrientedShell.qmlnew file+1151
@@ -0,0 +1,1151 @@
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.Layouts 1.1
19+import QtTest 1.0
20+import Ubuntu.Components 1.1
21+import Ubuntu.Components.ListItems 1.0 as ListItem
22+import Unity.Application 0.1
23+import Unity.Test 0.1
24+import LightDM 0.1 as LightDM
25+import Powerd 0.1
26+
27+import "../../qml"
28+
29+Rectangle {
30+ id: root
31+ color: "grey"
32+ width: units.gu(160) + controls.width
33+ height: units.gu(100)
34+
35+ QtObject {
36+ id: applicationArguments
37+
38+ function hasGeometry() {
39+ return false;
40+ }
41+
42+ function width() {
43+ return 0;
44+ }
45+
46+ function height() {
47+ return 0;
48+ }
49+ property string deviceName: "mako"
50+ }
51+
52+ QtObject {
53+ id: mockOrientationLock
54+ property int savedOrientation
55+ }
56+
57+ QtObject {
58+ id: mockUsageModeSettings
59+ property string usageMode: usageModeSelector.model[usageModeSelector.selectedIndex]
60+ }
61+
62+ property int physicalOrientation0
63+ property int physicalOrientation90
64+ property int physicalOrientation180
65+ property int physicalOrientation270
66+ property real primaryOrientationAngle
67+
68+ state: applicationArguments.deviceName
69+ states: [
70+ State {
71+ name: "mako"
72+ PropertyChanges {
73+ target: orientedShellLoader
74+ width: units.gu(40)
75+ height: units.gu(71)
76+ }
77+ PropertyChanges {
78+ target: root
79+ physicalOrientation0: Qt.PortraitOrientation
80+ physicalOrientation90: Qt.InvertedLandscapeOrientation
81+ physicalOrientation180: Qt.InvertedPortraitOrientation
82+ physicalOrientation270: Qt.LandscapeOrientation
83+ primaryOrientationAngle: 0
84+ }
85+ },
86+ State {
87+ name: "manta"
88+ PropertyChanges {
89+ target: orientedShellLoader
90+ width: units.gu(160)
91+ height: units.gu(100)
92+ }
93+ PropertyChanges {
94+ target: root
95+ physicalOrientation90: Qt.PortraitOrientation
96+ physicalOrientation180: Qt.InvertedLandscapeOrientation
97+ physicalOrientation270: Qt.InvertedPortraitOrientation
98+ physicalOrientation0: Qt.LandscapeOrientation
99+ primaryOrientationAngle: 0
100+ }
101+ },
102+ State {
103+ name: "flo"
104+ PropertyChanges {
105+ target: orientedShellLoader
106+ width: units.gu(62)
107+ height: units.gu(100)
108+ }
109+ PropertyChanges {
110+ target: root
111+ physicalOrientation270: Qt.PortraitOrientation
112+ physicalOrientation0: Qt.InvertedLandscapeOrientation
113+ physicalOrientation90: Qt.InvertedPortraitOrientation
114+ physicalOrientation180: Qt.LandscapeOrientation
115+ primaryOrientationAngle: 90
116+ }
117+ }
118+ ]
119+
120+ Loader {
121+ id: orientedShellLoader
122+
123+ x: ((root.width - controls.width) - width) / 2
124+ y: (root.height - height) / 2
125+
126+ focus: true
127+
128+ property bool itemDestroyed: false
129+ sourceComponent: Component {
130+ OrientedShell {
131+ anchors.fill: parent
132+ usageModeSettings: mockUsageModeSettings
133+ physicalOrientation: root.physicalOrientation0
134+ orientationLocked: orientationLockedCheckBox.checked
135+ orientationLock: mockOrientationLock
136+ Component.onDestruction: {
137+ orientedShellLoader.itemDestroyed = true;
138+ }
139+ }
140+ }
141+ }
142+
143+ function orientationsToStr(orientations) {
144+ if (orientations === Qt.PrimaryOrientation) {
145+ return "Primary";
146+ } else {
147+ var str = "";
148+ if (orientations & Qt.PortraitOrientation) {
149+ str += " Portrait";
150+ }
151+ if (orientations & Qt.InvertedPortraitOrientation) {
152+ str += " InvertedPortrait";
153+ }
154+ if (orientations & Qt.LandscapeOrientation) {
155+ str += " Landscape";
156+ }
157+ if (orientations & Qt.InvertedLandscapeOrientation) {
158+ str += " InvertedLandscape";
159+ }
160+ return str;
161+ }
162+ }
163+
164+ Rectangle {
165+ width: controls.width
166+ anchors {
167+ top: parent.top
168+ bottom: parent.bottom
169+ right: parent.right
170+ }
171+ color: "darkgrey"
172+ }
173+ Flickable {
174+ id: controls
175+ width: units.gu(30)
176+ anchors {
177+ top: parent.top
178+ bottom: parent.bottom
179+ right: parent.right
180+ }
181+
182+ boundsBehavior: Flickable.StopAtBounds
183+ contentHeight: controlsColumn.height
184+
185+ Column {
186+ id: controlsColumn
187+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
188+ spacing: units.gu(1)
189+ Button {
190+ text: "Show Greeter"
191+ activeFocusOnPress: false
192+ onClicked: {
193+ if (orientedShellLoader.status !== Loader.Ready)
194+ return;
195+
196+ LightDM.Greeter.showGreeter();
197+ }
198+ }
199+ Label {
200+ text: "Physical Orientation:"
201+ }
202+ Button {
203+ id: rotate0Button
204+ text: root.orientationsToStr(root.physicalOrientation0) + " (0)"
205+ activeFocusOnPress: false
206+ onClicked: {
207+ orientedShellLoader.item.physicalOrientation = root.physicalOrientation0;
208+ }
209+ }
210+ Button {
211+ id: rotate90Button
212+ text: root.orientationsToStr(root.physicalOrientation90) + " (90)"
213+ activeFocusOnPress: false
214+ onClicked: {
215+ orientedShellLoader.item.physicalOrientation = root.physicalOrientation90;
216+ }
217+ }
218+ Button {
219+ id: rotate180Button
220+ text: root.orientationsToStr(root.physicalOrientation180) + " (180)"
221+ activeFocusOnPress: false
222+ onClicked: {
223+ orientedShellLoader.item.physicalOrientation = root.physicalOrientation180;
224+ }
225+ }
226+ Button {
227+ id: rotate270Button
228+ text: root.orientationsToStr(root.physicalOrientation270) + " (270)"
229+ activeFocusOnPress: false
230+ onClicked: {
231+ orientedShellLoader.item.physicalOrientation = root.physicalOrientation270;
232+ }
233+ }
234+ RowLayout {
235+ Layout.fillWidth: true
236+ CheckBox {
237+ id: orientationLockedCheckBox
238+ checked: false
239+ activeFocusOnPress: false
240+ }
241+ Label {
242+ text: "Orientation Locked"
243+ anchors.verticalCenter: parent.verticalCenter
244+ }
245+ }
246+ Button {
247+ text: "Power dialog"
248+ activeFocusOnPress: false
249+ onClicked: { testCase.showPowerDialog(); }
250+ }
251+ ListItem.ItemSelector {
252+ id: deviceNameSelector
253+ anchors { left: parent.left; right: parent.right }
254+ activeFocusOnPress: false
255+ text: "Device Name"
256+ model: ["mako", "manta", "flo"]
257+ onSelectedIndexChanged: {
258+ testCase.tearDown();
259+ applicationArguments.deviceName = model[selectedIndex];
260+ orientedShellLoader.active = true;
261+ }
262+ }
263+ ListItem.ItemSelector {
264+ id: usageModeSelector
265+ anchors { left: parent.left; right: parent.right }
266+ activeFocusOnPress: false
267+ text: "Usage Mode"
268+ model: ["Staged", "Windowed", "Automatic"]
269+ }
270+ Button {
271+ text: "Switch fullscreen"
272+ activeFocusOnPress: false
273+ onClicked: {
274+ var app = ApplicationManager.findApplication(ApplicationManager.focusedApplicationId);
275+ app.fullscreen = !app.fullscreen;
276+ }
277+ }
278+ RowLayout {
279+ Layout.fillWidth: true
280+ CheckBox {
281+ checked: false
282+ activeFocusOnPress: false
283+ onCheckedChanged: {
284+ var surface = SurfaceManager.inputMethodSurface();
285+ if (checked) {
286+ surface.setState(MirSurfaceItem.Restored);
287+ } else {
288+ surface.setState(MirSurfaceItem.Minimized);
289+ }
290+ }
291+ }
292+ Label {
293+ text: "Input Method"
294+ anchors.verticalCenter: parent.verticalCenter
295+ }
296+ }
297+ Button {
298+ text: Powerd.status === Powerd.On ? "Display ON" : "Display OFF"
299+ activeFocusOnPress: false
300+ onClicked: {
301+ if (Powerd.status === Powerd.On) {
302+ Powerd.status = Powerd.Off;
303+ } else {
304+ Powerd.status = Powerd.On;
305+ }
306+ }
307+ }
308+ }
309+ }
310+
311+ UnityTestCase {
312+ id: testCase
313+ name: "OrientedShell"
314+ when: windowShown
315+
316+ property Item orientedShell: orientedShellLoader.status === Loader.Ready ? orientedShellLoader.item : null
317+ property Item shell
318+
319+ SignalSpy { id: signalSpy }
320+ SignalSpy { id: signalSpy2 }
321+
322+ Connections {
323+ id: spreadRepeaterConnections
324+ ignoreUnknownSignals : true
325+ property var itemAddedCallback: null
326+ onItemAdded: {
327+ if (itemAddedCallback) {
328+ itemAddedCallback(item);
329+ }
330+ }
331+ }
332+
333+ function init() {
334+ if (orientedShellLoader.active) {
335+ // happens for the very first test function as shell
336+ // is loaded by default
337+ tearDown();
338+ }
339+ }
340+
341+ function tearDown() {
342+ orientedShellLoader.itemDestroyed = false;
343+ orientedShellLoader.active = false;
344+
345+ tryCompare(orientedShellLoader, "status", Loader.Null);
346+ tryCompare(orientedShellLoader, "item", null);
347+ // Loader.status might be Loader.Null and Loader.item might be null but the Loader
348+ // actually took place. Likely because Loader waits until the next event loop
349+ // iteration to do its work. So to ensure the reload, we will wait until the
350+ // Shell instance gets destroyed.
351+ tryCompare(orientedShellLoader, "itemDestroyed", true);
352+
353+ // kill all (fake) running apps
354+ killApps();
355+
356+ spreadRepeaterConnections.target = null;
357+ spreadRepeaterConnections.itemAddedCallback = null;
358+ signalSpy.target = null;
359+ signalSpy.signalName = "";
360+
361+ LightDM.Greeter.authenticate(""); // reset greeter
362+ }
363+
364+ function cleanup() {
365+ tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
366+ shell = null;
367+
368+ tearDown();
369+ }
370+
371+ function test_appSupportingOnlyPrimaryOrientationMakesShellStayPut_data() {
372+ return [
373+ {tag: "mako", deviceName: "mako"},
374+ {tag: "manta", deviceName: "manta"},
375+ {tag: "flo", deviceName: "flo"}
376+ ];
377+ }
378+ function test_appSupportingOnlyPrimaryOrientationMakesShellStayPut(data) {
379+ loadShell(data.deviceName);
380+
381+ // unity8-dash supports only primary orientation and should be already running
382+ var dashAppWindow = findChild(shell, "appWindow_unity8-dash");
383+ verify(dashAppWindow);
384+ compare(ApplicationManager.focusedApplicationId, "unity8-dash");
385+ var dashApp = dashAppWindow.application
386+ verify(dashApp);
387+ compare(dashApp.rotatesWindowContents, false);
388+ compare(dashApp.supportedOrientations, Qt.PrimaryOrientation);
389+ compare(dashApp.stage, ApplicationInfoInterface.MainStage);
390+
391+ tryCompareFunction(function(){return dashApp.session.surface != null;}, true);
392+ verify(checkAppSurfaceOrientation(dashApp, root.primaryOrientationAngle));
393+
394+ compare(shell.transformRotationAngle, root.primaryOrientationAngle);
395+ rotateTo(90);
396+
397+ verify(checkAppSurfaceOrientation(dashApp, root.primaryOrientationAngle));
398+ compare(shell.transformRotationAngle, root.primaryOrientationAngle);
399+
400+ rotateTo(180);
401+
402+ verify(checkAppSurfaceOrientation(dashApp, root.primaryOrientationAngle));
403+ compare(shell.transformRotationAngle, root.primaryOrientationAngle);
404+
405+ rotateTo(270);
406+
407+ verify(checkAppSurfaceOrientation(dashApp, root.primaryOrientationAngle));
408+ compare(shell.transformRotationAngle, root.primaryOrientationAngle);
409+ }
410+
411+ function test_greeterRemainsInPrimaryOrientation_data() {
412+ return [
413+ {tag: "mako", deviceName: "mako"},
414+ {tag: "manta", deviceName: "manta"},
415+ {tag: "flo", deviceName: "flo"}
416+ ];
417+ }
418+ function test_greeterRemainsInPrimaryOrientation(data) {
419+ loadShell(data.deviceName);
420+
421+ var gmailApp = ApplicationManager.startApplication("gmail-webapp");
422+ verify(gmailApp);
423+
424+ // ensure the mock gmail-webapp is as we expect
425+ compare(gmailApp.rotatesWindowContents, false);
426+ compare(gmailApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
427+ | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
428+ compare(gmailApp.stage, ApplicationInfoInterface.MainStage);
429+
430+ // wait until it's able to rotate
431+ tryCompare(shell, "orientationChangesEnabled", true);
432+
433+ compare(shell.transformRotationAngle, root.primaryOrientationAngle);
434+ rotateTo(90);
435+ tryCompare(shell, "transformRotationAngle", root.primaryOrientationAngle + 90);
436+
437+ showGreeter();
438+
439+ tryCompare(shell, "transformRotationAngle", root.primaryOrientationAngle);
440+ rotateTo(180);
441+ compare(shell.transformRotationAngle, root.primaryOrientationAngle);
442+ rotateTo(270);
443+ compare(shell.transformRotationAngle, root.primaryOrientationAngle);
444+ rotateTo(0);
445+ compare(shell.transformRotationAngle, root.primaryOrientationAngle);
446+ }
447+
448+ function test_appRotatesWindowContents_data() {
449+ return [
450+ {tag: "mako", deviceName: "mako", orientationAngleAfterRotation: 90},
451+ {tag: "manta", deviceName: "manta", orientationAngleAfterRotation: 90},
452+ {tag: "flo", deviceName: "flo", orientationAngleAfterRotation: 180}
453+ ];
454+ }
455+ function test_appRotatesWindowContents(data) {
456+ loadShell(data.deviceName);
457+ var cameraApp = ApplicationManager.startApplication("camera-app");
458+ verify(cameraApp);
459+
460+ // ensure the mock camera-app is as we expect
461+ compare(cameraApp.fullscreen, true);
462+ compare(cameraApp.rotatesWindowContents, true);
463+ compare(cameraApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
464+ | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
465+
466+ waitUntilAppSurfaceShowsUp("camera-app")
467+
468+ var cameraSurface = cameraApp.session.surface;
469+ verify(cameraSurface);
470+
471+ var focusChangedSpy = signalSpy;
472+ focusChangedSpy.clear();
473+ focusChangedSpy.target = cameraSurface;
474+ focusChangedSpy.signalName = "activeFocusChanged";
475+ verify(focusChangedSpy.valid);
476+
477+ verify(cameraSurface.activeFocus);
478+
479+ tryCompare(shell, "orientationChangesEnabled", true);
480+
481+ var rotationStates = findInvisibleChild(orientedShell, "rotationStates");
482+ verify(rotationStates);
483+ var immediateTransition = null
484+ for (var i = 0; i < rotationStates.transitions.length && !immediateTransition; ++i) {
485+ var transition = rotationStates.transitions[i];
486+ if (transition.objectName == "immediateTransition") {
487+ immediateTransition = transition;
488+ }
489+ }
490+ verify(immediateTransition);
491+ var transitionSpy = signalSpy2;
492+ transitionSpy.clear();
493+ transitionSpy.target = immediateTransition;
494+ transitionSpy.signalName = "runningChanged";
495+ verify(transitionSpy.valid);
496+
497+ rotateTo(90);
498+
499+ tryCompare(cameraSurface, "orientationAngle", data.orientationAngleAfterRotation);
500+
501+ // the rotation should have been immediate
502+ // false -> true -> false
503+ compare(transitionSpy.count, 2);
504+
505+ // It should retain native dimensions regardless of its rotation/orientation
506+ compare(cameraSurface.width, orientedShell.width);
507+ compare(cameraSurface.height, orientedShell.height);
508+
509+ // Surface focus shouldn't have been touched because of the rotation
510+ compare(focusChangedSpy.count, 0);
511+ }
512+
513+ /*
514+ Preconditions:
515+ Shell orientation angle matches the screen one.
516+
517+ Use case:
518+ User switches to an app that has an orientation angle different from the
519+ shell one but that also happens to support the current shell orientation
520+ angle.
521+
522+ Expected outcome:
523+ The app should get rotated to match shell's orientation angle
524+ */
525+ function test_switchingToAppWithDifferentRotation_data() {
526+ return [
527+ {tag: "mako", deviceName: "mako", shellAngleAfterRotation: 90},
528+ {tag: "manta", deviceName: "manta", shellAngleAfterRotation: 90},
529+ {tag: "flo", deviceName: "flo", shellAngleAfterRotation: 180}
530+ ];
531+ }
532+ function test_switchingToAppWithDifferentRotation(data) {
533+ loadShell(data.deviceName);
534+ var gmailApp = ApplicationManager.startApplication("gmail-webapp");
535+ verify(gmailApp);
536+
537+ // ensure the mock gmail-webapp is as we expect
538+ compare(gmailApp.rotatesWindowContents, false);
539+ compare(gmailApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
540+ | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
541+ compare(gmailApp.stage, ApplicationInfoInterface.MainStage);
542+
543+ waitUntilAppSurfaceShowsUp("gmail-webapp")
544+
545+ var musicApp = ApplicationManager.startApplication("music-app");
546+ verify(musicApp);
547+
548+ // ensure the mock music-app is as we expect
549+ compare(musicApp.rotatesWindowContents, false);
550+ compare(musicApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
551+ | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
552+ compare(musicApp.stage, ApplicationInfoInterface.MainStage);
553+
554+ waitUntilAppSurfaceShowsUp("music-app")
555+ tryCompare(shell, "orientationChangesEnabled", true);
556+
557+ rotateTo(90);
558+ tryCompare(shell, "transformRotationAngle", data.shellAngleAfterRotation);
559+
560+ performEdgeSwipeToSwitchToPreviousApp();
561+
562+ tryCompare(shell, "mainAppWindowOrientationAngle", data.shellAngleAfterRotation);
563+ compare(shell.transformRotationAngle, data.shellAngleAfterRotation);
564+ }
565+
566+ /*
567+ Preconditions:
568+ - Device supports portrait, landscape and inverted-landscape
569+
570+ Steps:
571+ 1 - Launch app that supports all orientations
572+ 2 - Rotate device to inverted-landscape
573+ 3 - See that shell gets rotated to inverted-landscape accordingly
574+ 4 - Rotate device to inverted-portrait
575+
576+ Expected outcome:
577+ Shell stays at inverted-landscape
578+
579+ Actual outcome:
580+ Shell rotates to landscape
581+
582+ Comments:
583+ Rationale being that shell should be rotated to the closest supported orientation.
584+ In that case, landscape and inverted-landscape are both 90 degrees away from the physical
585+ orientation (Screen.orientation), so they are both equally good alternatives
586+ */
587+ function test_rotateToUnsupportedDeviceOrientation(data) {
588+ loadShell("mako");
589+ var twitterApp = ApplicationManager.startApplication("twitter-webapp");
590+ verify(twitterApp);
591+
592+ // ensure the mock twitter-webapp is as we expect
593+ compare(twitterApp.rotatesWindowContents, false);
594+ compare(twitterApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
595+ | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
596+
597+ waitUntilAppSurfaceShowsUp("twitter-webapp")
598+
599+ rotateTo(data.rotationAngle);
600+ tryCompare(shell, "transformRotationAngle", data.rotationAngle);
601+
602+ rotateTo(180);
603+ tryCompare(shell, "transformRotationAngle", data.rotationAngle);
604+ }
605+ function test_rotateToUnsupportedDeviceOrientation_data() {
606+ return [
607+ {tag: "90", rotationAngle: 90},
608+ {tag: "270", rotationAngle: 270}
609+ ];
610+ }
611+
612+ function test_launchLandscapeOnlyAppFromPortrait() {
613+ loadShell("mako");
614+ var weatherApp = ApplicationManager.startApplication("ubuntu-weather-app");
615+ verify(weatherApp);
616+
617+ // ensure the mock app is as we expect
618+ compare(weatherApp.supportedOrientations, Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation);
619+
620+ waitUntilAppSurfaceShowsUp("ubuntu-weather-app");
621+
622+ var rotationStates = findInvisibleChild(orientedShell, "rotationStates");
623+ waitUntilTransitionsEnd(rotationStates);
624+
625+ tryCompareFunction(function (){return shell.transformRotationAngle === 90
626+ || shell.transformRotationAngle === 270;}, true);
627+ }
628+
629+ /*
630+ - launch an app that supports all orientations, such as twitter-webapp
631+ - wait a bit until that app is considered to have finished initializing and is thus
632+ ready to get resized/rotated
633+ - switch back to dash
634+ - rotate device to 90 degrees
635+ - Physical orientation is 90 but Shell orientation is kept at 0 because unity8-dash
636+ doesn't support such orientation
637+ - do a long right-edge drag to show the apps spread
638+ - tap on twitter-webapp
639+
640+ Shell will rotate to match the physical orientation.
641+
642+ This is a kind of tricky case as there are a few things happening at the same time:
643+ 1 - Stage switching from apps spread (phase 2) to showing the focused app (phase 0)
644+ 2 - orientation and aspect ratio (ie size) changes
645+
646+ This may trigger some corner case bugs. such as one were
647+ the greeter is not kept completely outside the shell, causing the black rect in Shell.qml
648+ have an opacity > 0.
649+ */
650+ function test_greeterStaysAwayAfterRotation() {
651+ loadShell("mako");
652+ var twitterApp = ApplicationManager.startApplication("twitter-webapp");
653+ verify(twitterApp);
654+
655+ // ensure the mock twitter-webapp is as we expect
656+ compare(twitterApp.rotatesWindowContents, false);
657+ compare(twitterApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
658+ | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
659+
660+ waitUntilAppSurfaceShowsUp("twitter-webapp");
661+ waitUntilAppWindowCanRotate("twitter-webapp");
662+
663+ // go back to unity8-dash
664+ performEdgeSwipeToSwitchToPreviousApp();
665+
666+ rotateTo(90);
667+ wait(1); // spin the event loop to let all bindings do their thing
668+ // should not rotat as unity8-dash doesn't support it
669+ tryCompare(shell, "transformRotationAngle", 0);
670+
671+ performEdgeSwipeToShowAppSpread();
672+
673+ // wait until things have settled
674+ var greeter = findChild(shell, "greeter");
675+ tryCompare(greeter, "animating", false);
676+
677+ var twitterDelegate = findChild(shell, "appDelegate1");
678+ compare(twitterDelegate.application.appId, "twitter-webapp");
679+ twitterDelegate.clicked();
680+
681+ // now it should finally follow the physical orientation
682+ tryCompare(shell, "transformRotationAngle", 90);
683+
684+ // greeter should remaing completely hidden
685+ tryCompare(greeter, "shown", false);
686+ }
687+
688+ function test_appInSideStageDoesntRotateOnStartUp_data() {
689+ return [
690+ {tag: "manta", deviceName: "manta"},
691+ {tag: "flo", deviceName: "flo"}
692+ ];
693+ }
694+ function test_appInSideStageDoesntRotateOnStartUp(data) {
695+ loadShell(data.deviceName);
696+
697+ var twitterDelegate = null;
698+
699+ verify(spreadRepeaterConnections.target);
700+ spreadRepeaterConnections.itemAddedCallback = function(item) {
701+ verify(item.application.appId, "twitter-webapp");
702+ twitterDelegate = item;
703+ signalSpy.target = findInvisibleChild(item, "orientationChangeAnimation");
704+ verify(signalSpy.valid);
705+ }
706+
707+ signalSpy.clear();
708+ signalSpy.target = null;
709+ signalSpy.signalName = "runningChanged";
710+
711+ var twitterApp = ApplicationManager.startApplication("twitter-webapp");
712+ verify(twitterApp);
713+
714+ // ensure the mock twitter-webapp is as we expect
715+ compare(twitterApp.rotatesWindowContents, false);
716+ compare(twitterApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
717+ | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
718+ compare(twitterApp.stage, ApplicationInfoInterface.SideStage);
719+
720+ // Wait until spreadRepeaterConnections hs caught the new SpreadDelegate and
721+ // set up the signalSpy target accordingly.
722+ tryCompareFunction(function(){ return signalSpy.target != null && signalSpy.valid; }, true);
723+
724+ tryCompare(twitterDelegate, "orientationChangesEnabled", true);
725+
726+ var appWindowWithShadow = findChild(twitterDelegate, "appWindowWithShadow");
727+ verify(appWindowWithShadow);
728+ tryCompare(appWindowWithShadow, "state", "keepSceneRotation");
729+
730+ // no reason for any rotation animation to have taken place
731+ compare(signalSpy.count, 0);
732+ }
733+
734+ function test_portraitOnlyAppInSideStage_data() {
735+ return [
736+ {tag: "manta", deviceName: "manta"},
737+ {tag: "flo", deviceName: "flo"}
738+ ];
739+ }
740+ function test_portraitOnlyAppInSideStage(data) {
741+ loadShell(data.deviceName);
742+
743+ var dialerDelegate = null;
744+ verify(spreadRepeaterConnections.target);
745+ spreadRepeaterConnections.itemAddedCallback = function(item) {
746+ dialerDelegate = item;
747+ verify(item.application.appId, "dialer-app");
748+ }
749+
750+ var dialerApp = ApplicationManager.startApplication("dialer-app");
751+ verify(dialerApp);
752+
753+ // ensure the mock dialer-app is as we expect
754+ compare(dialerApp.rotatesWindowContents, false);
755+ compare(dialerApp.supportedOrientations, Qt.PortraitOrientation | Qt.InvertedPortraitOrientation);
756+ compare(dialerApp.stage, ApplicationInfoInterface.SideStage);
757+
758+ tryCompareFunction(function(){ return dialerDelegate != null; }, true);
759+ tryCompare(dialerDelegate, "orientationChangesEnabled", true);
760+
761+ var appWindowWithShadow = findChild(dialerDelegate, "appWindowWithShadow");
762+ verify(appWindowWithShadow);
763+ tryCompare(appWindowWithShadow, "state", "keepSceneRotation");
764+
765+ // app must have portrait aspect ratio
766+ verify(appWindowWithShadow.width < appWindowWithShadow.height);
767+
768+ compare(appWindowWithShadow.rotation, 0);
769+
770+ verifyAppWindowWithinSpreadDelegateBoundaries(dialerDelegate);
771+
772+ // shell should remain in its primery orientation as the app in the main stage
773+ // is the one that dictates its orientation. In this case it's unity8-dash
774+ // which supports only primary orientation
775+ compare(shell.orientation, orientedShell.primaryOrientation);
776+ }
777+
778+ function test_sideStageAppsRemainPortraitInSpread_data() {
779+ return [
780+ {tag: "manta", deviceName: "manta"},
781+ {tag: "flo", deviceName: "flo"}
782+ ];
783+ }
784+ function test_sideStageAppsRemainPortraitInSpread(data) {
785+ loadShell(data.deviceName);
786+
787+
788+ ////
789+ // Launch dialer
790+ var dialerDelegate = null;
791+ verify(spreadRepeaterConnections.target);
792+ spreadRepeaterConnections.itemAddedCallback = function(item) {
793+ dialerDelegate = item;
794+ verify(item.application.appId, "dialer-app");
795+ }
796+
797+ var dialerApp = ApplicationManager.startApplication("dialer-app");
798+ verify(dialerApp);
799+
800+ // ensure the mock dialer-app is as we expect
801+ compare(dialerApp.rotatesWindowContents, false);
802+ compare(dialerApp.supportedOrientations, Qt.PortraitOrientation | Qt.InvertedPortraitOrientation);
803+ compare(dialerApp.stage, ApplicationInfoInterface.SideStage);
804+
805+ tryCompareFunction(function(){ return dialerDelegate != null; }, true);
806+ waitUntilAppDelegateIsFullyInit(dialerDelegate);
807+
808+
809+ ////
810+ // Launch twitter
811+ var twitterDelegate = null;
812+ spreadRepeaterConnections.itemAddedCallback = function(item) {
813+ twitterDelegate = item;
814+ verify(item.application.appId, "twitter-webapp");
815+ }
816+
817+ var twitterApp = ApplicationManager.startApplication("twitter-webapp");
818+ verify(twitterApp);
819+
820+ // ensure the mock twitter-webapp is as we expect
821+ compare(twitterApp.rotatesWindowContents, false);
822+ compare(twitterApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
823+ | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
824+ compare(twitterApp.stage, ApplicationInfoInterface.SideStage);
825+
826+ tryCompareFunction(function(){ return twitterDelegate != null; }, true);
827+ waitUntilAppDelegateIsFullyInit(twitterDelegate);
828+
829+ ////
830+ // Edge swipe to show spread and check orientations
831+
832+ performEdgeSwipeToShowAppSpread();
833+
834+ {
835+ var appWindowWithShadow = findChild(dialerDelegate, "appWindowWithShadow");
836+ verify(appWindowWithShadow);
837+ tryCompare(appWindowWithShadow, "state", "keepSceneRotation");
838+ compare(appWindowWithShadow.width, dialerDelegate.width);
839+ compare(appWindowWithShadow.height, dialerDelegate.height);
840+ compare(appWindowWithShadow.rotation, 0);
841+ }
842+ {
843+ var appWindowWithShadow = findChild(twitterDelegate, "appWindowWithShadow");
844+ verify(appWindowWithShadow);
845+ tryCompare(appWindowWithShadow, "state", "keepSceneRotation");
846+ compare(appWindowWithShadow.width, twitterDelegate.width);
847+ compare(appWindowWithShadow.height, twitterDelegate.height);
848+ compare(appWindowWithShadow.rotation, 0);
849+ }
850+
851+ verifyAppWindowWithinSpreadDelegateBoundaries(dialerDelegate);
852+ verifyAppWindowWithinSpreadDelegateBoundaries(twitterDelegate);
853+ }
854+
855+ function test_launchedAppHasActiveFocus_data() {
856+ return [
857+ {tag: "mako", deviceName: "mako"},
858+ {tag: "manta", deviceName: "manta"},
859+ {tag: "flo", deviceName: "flo"}
860+ ];
861+ }
862+ function test_launchedAppHasActiveFocus(data) {
863+ loadShell(data.deviceName);
864+
865+ var gmailApp = ApplicationManager.startApplication("gmail-webapp");
866+ verify(gmailApp);
867+ waitUntilAppSurfaceShowsUp("gmail-webapp")
868+
869+ verify(gmailApp.session.surface);
870+
871+ tryCompare(gmailApp.session.surface, "activeFocus", true);
872+ }
873+
874+ function test_launchLandscapeOnlyAppOverPortraitOnlyDashThenSwitchToDash() {
875+ loadShell("mako");
876+
877+ // starts as portrait, as unity8-dash is portrait only
878+ tryCompare(shell, "transformRotationAngle", 0);
879+
880+ var weatherApp = ApplicationManager.startApplication("ubuntu-weather-app");
881+ verify(weatherApp);
882+
883+ // ensure the mock app is as we expect
884+ compare(weatherApp.supportedOrientations, Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation);
885+
886+ waitUntilAppSurfaceShowsUp("ubuntu-weather-app");
887+
888+ // should have rotated to landscape
889+ tryCompareFunction(function () { return shell.transformRotationAngle == 270
890+ || shell.transformRotationAngle == 90; }, true);
891+
892+ var rotationStates = findInvisibleChild(orientedShell, "rotationStates");
893+ waitUntilTransitionsEnd(rotationStates);
894+
895+ performLeftEdgeSwipeToSwitchToDash();
896+
897+ // Should be back to portrait
898+ tryCompare(shell, "transformRotationAngle", 0);
899+ }
900+
901+ // angle - rotation angle in degrees clockwise, relative to the primary orientation.
902+ function rotateTo(angle) {
903+ switch (angle) {
904+ case 0:
905+ rotate0Button.clicked();
906+ break;
907+ case 90:
908+ rotate90Button.clicked();
909+ break;
910+ case 180:
911+ rotate180Button.clicked();
912+ break;
913+ case 270:
914+ rotate270Button.clicked();
915+ break;
916+ default:
917+ verify(false);
918+ }
919+
920+ var rotationStates = findInvisibleChild(orientedShell, "rotationStates");
921+ waitUntilTransitionsEnd(rotationStates);
922+ }
923+
924+ function waitUntilAppDelegateIsFullyInit(spreadDelegate) {
925+ tryCompare(spreadDelegate, "orientationChangesEnabled", true);
926+
927+ var appWindowWithShadow = findChild(spreadDelegate, "appWindowWithShadow");
928+ verify(appWindowWithShadow);
929+ tryCompare(appWindowWithShadow, "state", "keepSceneRotation");
930+
931+ var appWindowStates = findInvisibleChild(appWindowWithShadow, "applicationWindowStateGroup");
932+ verify(appWindowStates);
933+ tryCompare(appWindowStates, "state", "surface");
934+ }
935+
936+ function waitUntilAppSurfaceShowsUp(appId) {
937+ var appWindow = findChild(shell, "appWindow_" + appId);
938+ verify(appWindow);
939+ var appWindowStates = findInvisibleChild(appWindow, "applicationWindowStateGroup");
940+ verify(appWindowStates);
941+ tryCompare(appWindowStates, "state", "surface");
942+ }
943+
944+ function waitUntilAppWindowCanRotate(appId) {
945+ var appWindow = findChild(shell, "appWindow_" + appId);
946+ verify(appWindow);
947+ tryCompare(appWindow, "orientationChangesEnabled", true);
948+ }
949+
950+ function waitUntilShellIsInOrientation(orientation) {
951+ tryCompare(shell, "orientation", orientation);
952+ var rotationStates = findInvisibleChild(orientedShell, "rotationStates");
953+ waitUntilTransitionsEnd(rotationStates);
954+ }
955+
956+ function performLeftEdgeSwipeToSwitchToDash() {
957+ var spreadView = findChild(shell, "spreadView");
958+ var swipeLength = spreadView.width * 0.7;
959+
960+ var touchStartX = 1;
961+ var touchStartY = shell.height / 2;
962+ touchFlick(shell,
963+ touchStartX, touchStartY,
964+ touchStartX + swipeLength, touchStartY);
965+
966+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
967+ }
968+
969+ function performEdgeSwipeToSwitchToPreviousApp() {
970+ // swipe just enough to ensure an app switch action.
971+ // If we swipe too much we will trigger the spread mode
972+ // and we don't want that.
973+ var spreadView = findChild(shell, "spreadView");
974+ verify(spreadView);
975+
976+ verify(ApplicationManager.count >= 2);
977+ var previousApp = ApplicationManager.get(1);
978+
979+ var touchStartX = shell.width - 1;
980+ var touchStartY = shell.height / 2;
981+
982+ var condition;
983+ if (applicationArguments.deviceName === "phone") {
984+ condition = function() {
985+ return spreadView.shiftedContentX > units.gu(2) &&
986+ spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width;
987+ };
988+ } else {
989+ condition = function() {
990+ return spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker1
991+ && spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2;
992+ };
993+ }
994+
995+ touchDragUntil(shell,
996+ touchStartX, touchStartY,
997+ -units.gu(1), 0,
998+ condition);
999+
1000+ // ensure the app switch animation has ended
1001+ tryCompare(spreadView, "shiftedContentX", 0);
1002+
1003+ compare(ApplicationManager.get(0).appId, previousApp.appId);
1004+ }
1005+
1006+ function performEdgeSwipeToShowAppSpread() {
1007+ var touchStartY = shell.height / 2;
1008+ touchFlick(shell,
1009+ shell.width - 1, touchStartY,
1010+ 0, touchStartY);
1011+
1012+ var spreadView = findChild(shell, "spreadView");
1013+ verify(spreadView);
1014+ tryCompare(spreadView, "phase", 2);
1015+ tryCompare(spreadView, "flicking", false);
1016+ tryCompare(spreadView, "moving", false);
1017+ }
1018+
1019+ function showPowerDialog() {
1020+ var dialogs = findChild(orientedShell, "dialogs");
1021+ var dialogsPrivate = findInvisibleChild(dialogs, "dialogsPrivate");
1022+ dialogsPrivate.showPowerDialog();
1023+ }
1024+
1025+ function verifyAppWindowWithinSpreadDelegateBoundaries(spreadDelegate) {
1026+ var appWindowWithShadow = findChild(spreadDelegate, "appWindowWithShadow");
1027+ verify(appWindowWithShadow);
1028+
1029+ var windowInDelegateCoords = appWindowWithShadow.mapToItem(spreadDelegate, 0, 0,
1030+ appWindowWithShadow.width, appWindowWithShadow.height);
1031+
1032+ compare(windowInDelegateCoords.x, 0);
1033+ compare(windowInDelegateCoords.y, 0);
1034+ compare(windowInDelegateCoords.width, spreadDelegate.width);
1035+ compare(windowInDelegateCoords.height, spreadDelegate.height);
1036+ }
1037+
1038+ function swipeAwayGreeter() {
1039+ var greeter = findChild(shell, "greeter");
1040+ tryCompare(greeter, "fullyShown", true);
1041+
1042+ var touchX = shell.width - (shell.edgeSize / 2);
1043+ var touchY = shell.height / 2;
1044+ touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
1045+
1046+ // wait until the animation has finished
1047+ tryCompare(greeter, "shown", false);
1048+ waitForRendering(greeter);
1049+ }
1050+
1051+ function showGreeter() {
1052+ LightDM.Greeter.showGreeter();
1053+ // wait until the animation has finished
1054+ var greeter = findChild(shell, "greeter");
1055+ tryCompare(greeter, "fullyShown", true);
1056+ }
1057+
1058+ function killApps() {
1059+ while (ApplicationManager.count > 1) {
1060+ var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
1061+ ApplicationManager.stopApplication(ApplicationManager.get(appIndex).appId);
1062+ }
1063+ compare(ApplicationManager.count, 1)
1064+ }
1065+
1066+ function loadShell(deviceName) {
1067+ applicationArguments.deviceName = deviceName;
1068+
1069+ // reload our test subject to get it in a fresh state once again
1070+ orientedShellLoader.active = true;
1071+
1072+ tryCompare(orientedShellLoader, "status", Loader.Ready);
1073+ removeTimeConstraintsFromDirectionalDragAreas(orientedShellLoader.item);
1074+
1075+ shell = findChild(orientedShell, "shell");
1076+
1077+ tryCompare(shell, "enabled", true); // enabled by greeter when ready
1078+
1079+ waitUntilShellIsInOrientation(root.physicalOrientation0);
1080+
1081+ waitForGreeterToStabilize();
1082+
1083+ swipeAwayGreeter();
1084+
1085+ var spreadRepeater = findChild(shell, "spreadRepeater");
1086+ if (spreadRepeater) {
1087+ spreadRepeaterConnections.target = spreadRepeater;
1088+ }
1089+ }
1090+
1091+ function waitForGreeterToStabilize() {
1092+ var greeter = findChild(shell, "greeter");
1093+ verify(greeter);
1094+
1095+ var loginList = findChild(greeter, "loginList");
1096+ // Only present in WideView
1097+ if (loginList) {
1098+ var userList = findChild(loginList, "userList");
1099+ verify(userList);
1100+ tryCompare(userList, "movingInternally", false);
1101+ }
1102+ }
1103+
1104+ // expectedAngle is in orientedShell's coordinate system
1105+ function checkAppSurfaceOrientation(app, expectedAngle) {
1106+ var surface = app.session.surface;
1107+ if (!surface) {
1108+ console.warn("no surface");
1109+ return false;
1110+ }
1111+
1112+ if (!itemIsChildOfOrientedShell(surface)) {
1113+ console.warn("surface not a child of OrientedShell");
1114+ return false;
1115+ }
1116+
1117+ var topMargin = 0.;
1118+ if (!app.fullscreen) {
1119+ var appsDisplayLoader = findChild(shell, "applicationsDisplayLoader");
1120+ verify(appsDisplayLoader);
1121+ verify(appsDisplayLoader.item);
1122+ verify(appsDisplayLoader.item.maximizedAppTopMargin !== undefined);
1123+ topMargin = appsDisplayLoader.item.maximizedAppTopMargin;
1124+ }
1125+
1126+ var point = surface.mapToItem(orientedShell, 0, 0);
1127+
1128+ switch (expectedAngle) {
1129+ case 0:
1130+ return point.x === 0. && point.y === topMargin;
1131+ case 90:
1132+ return point.x === orientedShell.width - topMargin && point.y === 0;
1133+ case 180:
1134+ return point.x === orientedShell.width && point.y === orientedShell.height - topMargin;
1135+ default: // 270
1136+ return point.x === topMargin && point.y === orientedShell.height;
1137+ }
1138+ }
1139+
1140+ function itemIsChildOfOrientedShell(item) {
1141+ var parent = item.parent;
1142+ var found = false;
1143+ while (parent && !found) {
1144+ found = parent === orientedShell;
1145+ parent = parent.parent;
1146+ }
1147+ return found;
1148+ }
1149+ }
1150+
1151+}
tests/qmltests/tst_Shell.qml+16−68
@@ -1,9 +1,6 @@
1/*
1/*
2 * Copyright (C) 2013-2015 Canonical, Ltd.
2 * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
3 *
4− * Authors:
5− * Daniel d'Andrada <daniel.dandrada@canonical.com>
6− *
7 * 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
8 * 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
9 * the Free Software Foundation; version 3.
6 * the Free Software Foundation; version 3.
@@ -49,22 +46,6 @@ Rectangle {
49 shellLoader.active = true;
46 shellLoader.active = true;
50 }
47 }
51
48
52− QtObject {
53− id: applicationArguments
54−
55− function hasGeometry() {
56− return false;
57− }
58−
59− function width() {
60− return 0;
61− }
62−
63− function height() {
64− return 0;
65− }
66− }
67−
68 Item {
49 Item {
69 anchors.left: root.left
50 anchors.left: root.left
70 anchors.right: controls.left
51 anchors.right: controls.left
@@ -100,8 +81,12 @@ Rectangle {
100 property bool itemDestroyed: false
81 property bool itemDestroyed: false
101 sourceComponent: Component {
82 sourceComponent: Component {
102 Shell {
83 Shell {
103− property string indicatorProfile: "phone"
84+ property string shellMode: "full-greeter" /* default */
104
85
86+ usageScenario: "phone"
87+ orientation: Qt.PortraitOrientation
88+ primaryOrientation: Qt.PortraitOrientation
89+ nativeOrientation: Qt.PortraitOrientation
105 Component.onDestruction: {
90 Component.onDestruction: {
106 shellLoader.itemDestroyed = true;
91 shellLoader.itemDestroyed = true;
107 }
92 }
@@ -664,15 +649,6 @@ Rectangle {
664 tryCompare(item, "visible", false);
649 tryCompare(item, "visible", false);
665 }
650 }
666
651
667− // wait until any transition animation has finished
668− function waitUntilTransitionsEnd(stateGroup) {
669− var transitions = stateGroup.transitions;
670− for (var i = 0; i < transitions.length; ++i) {
671− var transition = transitions[i];
672− tryCompare(transition, "running", false, 2000);
673− }
674− }
675−
676 // Wait until the ApplicationWindow for the given Application object is fully loaded
652 // Wait until the ApplicationWindow for the given Application object is fully loaded
677 // (ie, the real surface has replaced the splash screen)
653 // (ie, the real surface has replaced the splash screen)
678 function waitUntilAppWindowIsFullyLoaded(app) {
654 function waitUntilAppWindowIsFullyLoaded(app) {
@@ -865,37 +841,6 @@ Rectangle {
865 removeTimeConstraintsFromDirectionalDragAreas(greeter);
841 removeTimeConstraintsFromDirectionalDragAreas(greeter);
866 }
842 }
867
843
868− function test_greeterDoesNotChangeIndicatorProfile() {
869− loadShell("phone");
870− swipeAwayGreeter();
871− var panel = findChild(shell, "panel");
872− tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
873−
874− showGreeter();
875− tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
876−
877− LightDM.Greeter.hideGreeter();
878− tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
879− }
880−
881− function test_shellProfileChangesReachIndicators() {
882− loadShell("phone");
883− swipeAwayGreeter();
884− var panel = findChild(shell, "panel");
885−
886− shell.indicatorProfile = "test1";
887− for (var i = 0; i < panel.indicators.indicatorsModel.count; ++i) {
888− var properties = panel.indicators.indicatorsModel.data(i, IndicatorsModelRole.IndicatorProperties);
889− verify(properties["menuObjectPath"].substr(-5), "test1");
890− }
891−
892− shell.indicatorProfile = "test2";
893− for (var i = 0; i < panel.indicators.indicatorsModel.count; ++i) {
894− var properties = panel.indicators.indicatorsModel.data(i, IndicatorsModelRole.IndicatorProperties);
895− verify(properties["menuObjectPath"].substr(-5), "test2");
896− }
897− }
898−
899 function test_focusRequestedHidesGreeter() {
844 function test_focusRequestedHidesGreeter() {
900 loadShell("phone");
845 loadShell("phone");
901 swipeAwayGreeter();
846 swipeAwayGreeter();
@@ -1070,9 +1015,14 @@ Rectangle {
1070 loadShell("phone");
1015 loadShell("phone");
1071 swipeAwayGreeter();
1016 swipeAwayGreeter();
1072 var topmostSpreadDelegate = findChild(shell, "appDelegate0");
1017 var topmostSpreadDelegate = findChild(shell, "appDelegate0");
1018+ verify(topmostSpreadDelegate);
1019+
1020+ waitUntilFocusedApplicationIsShowingItsSurface();
1021+
1073 var topmostSurface = findChild(topmostSpreadDelegate, "surfaceContainer").surface;
1022 var topmostSurface = findChild(topmostSpreadDelegate, "surfaceContainer").surface;
1074− var rightEdgeDragArea = findChild(shell, "spreadDragArea");
1023+ verify(topmostSurface);
1075
1024
1025+ var rightEdgeDragArea = findChild(shell, "spreadDragArea");
1076 topmostSurface.touchPressCount = 0;
1026 topmostSurface.touchPressCount = 0;
1077 topmostSurface.touchReleaseCount = 0;
1027 topmostSurface.touchReleaseCount = 0;
1078
1028
@@ -1159,13 +1109,11 @@ Rectangle {
1159 // left edge drag area.
1109 // left edge drag area.
1160 {
1110 {
1161 var buttonShowDashHome = findChild(launcher, "buttonShowDashHome");
1111 var buttonShowDashHome = findChild(launcher, "buttonShowDashHome");
1162− var startPos = buttonShowDashHome.mapToItem(shell,
1112+ touchFlick(buttonShowDashHome,
1163− buttonShowDashHome.width * 0.2,
1113+ buttonShowDashHome.width * 0.2, /* startPos.x */
1164− buttonShowDashHome.height * 0.8);
1114+ buttonShowDashHome.height * 0.8, /* startPos.y */
1165− var endPos = buttonShowDashHome.mapToItem(shell,
1115+ buttonShowDashHome.width * 0.8, /* endPos.x */
1166− buttonShowDashHome.width * 0.8,
1116+ buttonShowDashHome.height * 0.2 /* endPos.y */);
1167− buttonShowDashHome.height * 0.2);
1168− touchFlick(shell, startPos.x, startPos.y, endPos.x, endPos.y);
1169 }
1117 }
1170
1118
1171 compare(launcherShowDashHomeSpy.count, 1);
1119 compare(launcherShowDashHomeSpy.count, 1);
tests/qmltests/tst_ShellWithPin.qml+7−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
@@ -69,7 +69,7 @@ Item {
69 property bool itemDestroyed: false
69 property bool itemDestroyed: false
70 sourceComponent: Component {
70 sourceComponent: Component {
71 Shell {
71 Shell {
72− property string indicatorProfile: "phone"
72+ property string shellMode: "full-greeter" /* default */
73
73
74 Component.onDestruction: {
74 Component.onDestruction: {
75 shellLoader.itemDestroyed = true
75 shellLoader.itemDestroyed = true
@@ -397,24 +397,22 @@ Item {
397 var applicationsDisplayLoader = findChild(shell, "applicationsDisplayLoader")
397 var applicationsDisplayLoader = findChild(shell, "applicationsDisplayLoader")
398
398
399 // We start in phone mode
399 // We start in phone mode
400− tryCompare(shell, "sideStageEnabled", false)
400+ compare(shell.usageScenario, "phone");
401− tryCompare(applicationsDisplayLoader, "tabletMode", false)
401+ compare(applicationsDisplayLoader.usageScenario, "phone");
402
402
403 var lockscreen = findChild(shell, "lockscreen")
403 var lockscreen = findChild(shell, "lockscreen")
404 lockscreen.emergencyCall()
404 lockscreen.emergencyCall()
405 confirmLockedApp("dialer-app")
405 confirmLockedApp("dialer-app")
406
406
407 // OK, we're in. Now try (but fail) to switch to tablet mode
407 // OK, we're in. Now try (but fail) to switch to tablet mode
408− shell.tablet = true
408+ shell.usageScenario = "tablet";
409− tryCompare(shell, "sideStageEnabled", true)
410− tryCompare(applicationsDisplayLoader, "tabletMode", false)
409+ compare(applicationsDisplayLoader.usageScenario, "phone");
411
410
412 // And when we kill the app, we go back to locked tablet mode
411 // And when we kill the app, we go back to locked tablet mode
413 killApps()
412 killApps()
414 var greeter = findChild(shell, "greeter")
413 var greeter = findChild(shell, "greeter")
415 tryCompare(greeter, "fullyShown", true)
414 tryCompare(greeter, "fullyShown", true)
416− tryCompare(shell, "sideStageEnabled", true)
417− tryCompare(applicationsDisplayLoader, "tabletMode", true)
415+ compare(applicationsDisplayLoader.usageScenario, "tablet");
418 }
416 }
419
417
420 function test_emergencyDialerIncoming() {
418 function test_emergencyDialerIncoming() {
tests/utils/modules/Unity/Test/UnityTestCase.qml+55−2
@@ -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 General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@ TestCase {
26 // This is needed for waitForRendering calls to return
26 // This is needed for waitForRendering calls to return
27 // if the watched element already got rendered
27 // if the watched element already got rendered
28 Rectangle {
28 Rectangle {
29+ id: rotatingRectangle
29 width: units.gu(1)
30 width: units.gu(1)
30 height: width
31 height: width
31 parent: testCase.parent
32 parent: testCase.parent
@@ -35,7 +36,7 @@ TestCase {
35 visible: testCase.running
36 visible: testCase.running
36
37
37 RotationAnimation on rotation {
38 RotationAnimation on rotation {
38− running: parent.visible
39+ running: rotatingRectangle.visible
39 from: 0
40 from: 0
40 to: 360
41 to: 360
41 loops: Animation.Infinite
42 loops: Animation.Infinite
@@ -308,6 +309,48 @@ TestCase {
308 }
309 }
309 }
310 }
310
311
312+ // perform a drag in the given direction until the given condition is true
313+ // The condition is a function to be evaluated after every step
314+ function touchDragUntil(item, startX, startY, stepX, stepY, condition) {
315+
316+ var root = fetchRootItem(item);
317+ var pos = item.mapToItem(root, startX, startY);
318+
319+ // convert step to scene coords
320+ {
321+ var stepStart = item.mapToItem(root, 0, 0);
322+ var stepEnd = item.mapToItem(root, stepX, stepY);
323+ }
324+ stepX = stepEnd.x - stepStart.x;
325+ stepY = stepEnd.y - stepStart.y;
326+
327+ var event = touchEvent(item)
328+ event.press(0 /* touchId */, pos.x, pos.y)
329+ event.commit()
330+
331+ // we have to stop at some point
332+ var maxSteps = 100;
333+ var stepsDone = 0;
334+
335+ while (!condition() && stepsDone < maxSteps) {
336+ wait(25);
337+ fakeDateTime.currentTimeMs += 25;
338+
339+ pos.x += stepX;
340+ pos.y += stepY;
341+
342+ event = touchEvent(item);
343+ event.move(0 /* touchId */, pos.x, pos.y);
344+ event.commit();
345+
346+ stepsDone += 1;
347+ }
348+
349+ event = touchEvent(item)
350+ event.release(0 /* touchId */, pos.x, pos.y)
351+ event.commit()
352+ }
353+
311 function touchPinch(item, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End) {
354 function touchPinch(item, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End) {
312 // Make sure the item is rendered
355 // Make sure the item is rendered
313 waitForRendering(item);
356 waitForRendering(item);
@@ -419,4 +462,14 @@ TestCase {
419 return qtest_results.waitForRendering(item, timeout);
462 return qtest_results.waitForRendering(item, timeout);
420 }
463 }
421
464
465+ /*
466+ Wait until any transition animation has finished for the given StateGroup or Item
467+ */
468+ function waitUntilTransitionsEnd(stateGroup) {
469+ var transitions = stateGroup.transitions;
470+ for (var i = 0; i < transitions.length; ++i) {
471+ var transition = transitions[i];
472+ tryCompare(transition, "running", false, 2000);
473+ }
474+ }
422}
475}