on xenial help sign in
Merge trunk.
5bb641cMichał Sawicz
CMakeLists.txt+1
@@ -54,6 +54,7 @@ find_package(Qt5Quick 5.2 REQUIRED)
54find_package(Qt5Gui 5.2 REQUIRED)
54find_package(Qt5Gui 5.2 REQUIRED)
55find_package(Qt5DBus 5.2 REQUIRED)
55find_package(Qt5DBus 5.2 REQUIRED)
56find_package(Qt5Concurrent 5.2 REQUIRED)
56find_package(Qt5Concurrent 5.2 REQUIRED)
57+find_package(Qt5Sql 5.2 REQUIRED)
57
58
58# Standard install paths
59# Standard install paths
59include(GNUInstallDirs)
60include(GNUInstallDirs)
debian/changelog+64
@@ -1,3 +1,67 @@
1+unity8 (8.02+15.04.20150216.1-0ubuntu1) vivid; urgency=medium
2+
3+ [ Andrea Cimitan ]
4+ * Fix temp scopes opening temp scopes, correctly close previously
5+ opened temp scope with its preview (LP: #1410337)
6+ * Set sourcesize for scopes images in manage dash
7+
8+ [ CI Train Bot ]
9+ * Resync trunk
10+
11+ [ Albert Astals Cid ]
12+ * Test for bug #1316660 (LP: #1316660)
13+ * Hardcode tranlsation for internal.location field (LP: #1393438)
14+ * Make sure the height it's the height it will have
15+
16+ [ Michael Zanetti ]
17+ * Cleanup cmake warning about missing Qt5Sql module
18+
19+ -- CI Train Bot <ci-train-bot@canonical.com> Mon, 16 Feb 2015 13:14:26 +0000
20+
21+unity8 (8.02+15.04.20150211-0ubuntu2) vivid; urgency=medium
22+
23+ * No-change rebuild against Qt 5.4.0.
24+
25+ -- Timo Jyrinki <timo-jyrinki@ubuntu.com> Fri, 13 Feb 2015 15:17:18 +0200
26+
27+unity8 (8.02+15.04.20150211-0ubuntu1) vivid; urgency=medium
28+
29+ [ Michael Terry ]
30+ * Redesign tutorial to match latest spec (just removing obsolete pages
31+ and redesigning look, no new pages yet)
32+ * Add new right-edge and bottom-edge screens to the first-boot edge
33+ tutorial (LP: #1383297)
34+ * Support switching the indicator profile on the fly, as the greeter
35+ appears and disappears. (But don't use it yet, not until the
36+ settings panel to control this is in place.)
37+ * Clip the infographic to its bounding box, instead of letting it
38+ bleed past the welcome page (visible when dragging the welcome page
39+ to the side).
40+
41+ [ Michael Zanetti ]
42+ * Add an AccountService based launcher model to be used with split
43+ greeter
44+ * Launcher fixes for windowed mode
45+ * save and restore window size and position
46+ * Fix resizing windows when making it smaller than minSize and then
47+ larger again.
48+
49+ [ handsome_feng<445865575@qq.com> ]
50+ * makes the header fully reveal when tapping the search icon. (LP:
51+ #1379327)
52+
53+ [ Mirco Müller ]
54+ * Allow swipe-to-dismiss for contracted snap-decision notifications,
55+ interactive notifications and ephemeral notifications. (LP:
56+ #1355422, #1334855)
57+
58+ [ Daniel d'Andrada ]
59+ * Make indicators bar eat all events
60+ * Improve Launcher tests, making them more reliable
61+ * Tapping on a surface gives it active focus
62+
63+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 11 Feb 2015 17:13:16 +0000
64+
1unity8 (8.02+15.04.20150123.3-0ubuntu1) vivid; urgency=low
65unity8 (8.02+15.04.20150123.3-0ubuntu1) vivid; urgency=low
2
66
3 [ Sebastien Bacher ]
67 [ Sebastien Bacher ]
debian/control+4−2
@@ -27,7 +27,7 @@ Build-Depends: cmake,
27 libqmenumodel-dev (>= 0.2.9),
27 libqmenumodel-dev (>= 0.2.9),
28 libqt5xmlpatterns5-dev,
28 libqt5xmlpatterns5-dev,
29 libsystemsettings-dev,
29 libsystemsettings-dev,
30− libunity-api-dev (>= 7.94),
30+ libunity-api-dev (>= 7.96),
31 libusermetricsoutput1-dev,
31 libusermetricsoutput1-dev,
32 libxcb1-dev,
32 libxcb1-dev,
33 pkg-config,
33 pkg-config,
@@ -39,6 +39,7 @@ Build-Depends: cmake,
39 qml-module-qtquick-layouts,
39 qml-module-qtquick-layouts,
40 qml-module-qtquick-xmllistmodel,
40 qml-module-qtquick-xmllistmodel,
41 qml-module-qtquick2,
41 qml-module-qtquick2,
42+ qml-module-qtsysteminfo,
42 qml-module-qttest,
43 qml-module-qttest,
43 qt5-default,
44 qt5-default,
44 qtbase5-dev (>= 5.2.1),
45 qtbase5-dev (>= 5.2.1),
@@ -90,6 +91,7 @@ Depends: gsettings-desktop-schemas,
90 qmenumodel-qml (>= 0.2.9),
91 qmenumodel-qml (>= 0.2.9),
91 qml-module-qt-labs-folderlistmodel,
92 qml-module-qt-labs-folderlistmodel,
92 qml-module-qtquick-xmllistmodel,
93 qml-module-qtquick-xmllistmodel,
94+ qml-module-qtsysteminfo,
93 qtdeclarative5-gsettings1.0,
95 qtdeclarative5-gsettings1.0,
94 qtdeclarative5-qtmir-plugin (>= 0.4.4),
96 qtdeclarative5-qtmir-plugin (>= 0.4.4),
95 qtdeclarative5-ubuntu-telephony0.1,
97 qtdeclarative5-ubuntu-telephony0.1,
@@ -124,7 +126,7 @@ Depends: qml-module-qtquick-layouts,
124 unity-application-impl-4,
126 unity-application-impl-4,
125 unity-notifications-impl-3,
127 unity-notifications-impl-3,
126 unity-plugin-scopes | unity-scopes-impl,
128 unity-plugin-scopes | unity-scopes-impl,
127− unity-scopes-impl-4,
129+ unity-scopes-impl-6,
128 unity8-fake-env | unity-application-impl,
130 unity8-fake-env | unity-application-impl,
129 ${misc:Depends},
131 ${misc:Depends},
130Breaks: unity8 (<< 7.86),
132Breaks: unity8 (<< 7.86),
debian/unity8-private.install+1
@@ -1,6 +1,7 @@
1usr/lib/*/libunity8-private.*
1usr/lib/*/libunity8-private.*
2usr/lib/*/unity8/qml/AccountsService
2usr/lib/*/unity8/qml/AccountsService
3usr/lib/*/unity8/qml/Dash
3usr/lib/*/unity8/qml/Dash
4+usr/lib/*/unity8/qml/Greeter
4usr/lib/*/unity8/qml/LightDM
5usr/lib/*/unity8/qml/LightDM
5usr/lib/*/unity8/qml/Lights
6usr/lib/*/unity8/qml/Lights
6usr/lib/*/unity8/qml/Powerd
7usr/lib/*/unity8/qml/Powerd
debian/unity8.install+1
@@ -10,5 +10,6 @@ usr/share/unity8/Launcher
10usr/share/unity8/Panel
10usr/share/unity8/Panel
11usr/share/unity8/Shell.qml
11usr/share/unity8/Shell.qml
12usr/share/unity8/Stages
12usr/share/unity8/Stages
13+usr/share/unity8/Tutorial
13usr/share/unity8/Wizard
14usr/share/unity8/Wizard
14usr/share/url-dispatcher/urls/unity8-dash.url-dispatcher
15usr/share/url-dispatcher/urls/unity8-dash.url-dispatcher
plugins/AccountsService/AccountsServiceDBusAdaptor.cpp+9
@@ -57,6 +57,15 @@ void AccountsServiceDBusAdaptor::setUserProperty(const QString &user, const QStr
57 }
57 }
58}
58}
59
59
60+void AccountsServiceDBusAdaptor::setUserPropertyAsync(const QString &user, const QString &interface, const QString &property, const QVariant &value)
61+{
62+ QDBusInterface *iface = getUserInterface(user);
63+ if (iface != nullptr && iface->isValid()) {
64+ // The value needs to be carefully wrapped
65+ iface->asyncCall("Set", interface, property, QVariant::fromValue(QDBusVariant(value)));
66+ }
67+}
68+
60void AccountsServiceDBusAdaptor::propertiesChangedSlot(const QString &interface, const QVariantMap &changed, const QStringList &invalid)
69void AccountsServiceDBusAdaptor::propertiesChangedSlot(const QString &interface, const QVariantMap &changed, const QStringList &invalid)
61{
70{
62 // Merge changed and invalidated together
71 // Merge changed and invalidated together
plugins/AccountsService/AccountsServiceDBusAdaptor.h+12
@@ -19,6 +19,7 @@
19#ifndef UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
19#ifndef UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
20#define UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
20#define UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
21
21
22+#include <QDBusArgument>
22#include <QDBusContext>
23#include <QDBusContext>
23#include <QDBusInterface>
24#include <QDBusInterface>
24#include <QMap>
25#include <QMap>
@@ -33,7 +34,18 @@ public:
33 explicit AccountsServiceDBusAdaptor(QObject *parent = 0);
34 explicit AccountsServiceDBusAdaptor(QObject *parent = 0);
34
35
35 Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &interface, const QString &property);
36 Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &interface, const QString &property);
37+
38+ template <typename T>
39+ inline T getUserProperty(const QString &user, const QString &interface, const QString &property) {
40+ QVariant variant = getUserProperty(user, interface, property);
41+ if (variant.isValid() && variant.canConvert<QDBusArgument>()) {
42+ return qdbus_cast<T>(variant.value<QDBusArgument>());
43+ }
44+ return T();
45+ }
46+
36 Q_INVOKABLE void setUserProperty(const QString &user, const QString &interface, const QString &property, const QVariant &value);
47 Q_INVOKABLE void setUserProperty(const QString &user, const QString &interface, const QString &property, const QVariant &value);
48+ Q_INVOKABLE void setUserPropertyAsync(const QString &user, const QString &interface, const QString &property, const QVariant &value);
37
49
38Q_SIGNALS:
50Q_SIGNALS:
39 void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
51 void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
plugins/AccountsService/com.canonical.unity.AccountsService.xml+1−2
@@ -14,8 +14,7 @@
14 <annotation name="org.freedesktop.Accounts.DefaultValue" value="true"/>
14 <annotation name="org.freedesktop.Accounts.DefaultValue" value="true"/>
15 </property>
15 </property>
16
16
17− <!-- Should have been named LauncherItems, sorry folks. -mterry -->
18− <property name="launcher-items" type="aa{sv}" access="readwrite">
17+ <property name="LauncherItems" type="aa{sv}" access="readwrite">
19 <annotation name="org.freedesktop.Accounts.DefaultValue" value="[{'defaults': <true>}]"/>
18 <annotation name="org.freedesktop.Accounts.DefaultValue" value="[{'defaults': <true>}]"/>
20 </property>
19 </property>
21
20
plugins/CMakeLists.txt+1
@@ -12,6 +12,7 @@ macro(add_unity8_plugin PLUGIN VERSION PATH)
12endmacro()
12endmacro()
13
13
14add_subdirectory(AccountsService)
14add_subdirectory(AccountsService)
15+add_subdirectory(Greeter)
15add_subdirectory(LightDM)
16add_subdirectory(LightDM)
16add_subdirectory(Lights)
17add_subdirectory(Lights)
17add_subdirectory(Dash)
18add_subdirectory(Dash)
plugins/Dash/CardCreator.js+2−1
@@ -320,7 +320,8 @@ function cardString(template, components) {
320 var hasSummary = components["summary"] || false;
320 var hasSummary = components["summary"] || false;
321 var artAndSummary = hasArt && hasSummary;
321 var artAndSummary = hasArt && hasSummary;
322 var isHorizontal = template["card-layout"] === "horizontal";
322 var isHorizontal = template["card-layout"] === "horizontal";
323− var hasBackground = (hasSummary || !isHorizontal) && (template["card-background"] || components["background"] || artAndSummary);
323+ var hasBackground = (!isHorizontal && (template["card-background"] || components["background"] || artAndSummary)) ||
324+ (hasSummary && (template["card-background"] || components["background"]));
324 var hasTitle = components["title"] || false;
325 var hasTitle = components["title"] || false;
325 var hasMascot = components["mascot"] || false;
326 var hasMascot = components["mascot"] || false;
326 var hasEmblem = components["emblem"] && !(hasMascot && template["card-size"] === "small") || false;
327 var hasEmblem = components["emblem"] && !(hasMascot && template["card-size"] === "small") || false;
plugins/Greeter/CMakeLists.txtnew file+6
@@ -0,0 +1,6 @@
1+macro(add_unity8_greeter_plugin PLUGIN VERSION PATH)
2+ export_qmlfiles(${PLUGIN} ${PATH} DESTINATION ${SHELL_INSTALL_QML}/Greeter TARGET_PREFIX Greeter ${ARGN})
3+ export_qmlplugin(${PLUGIN} ${VERSION} ${PATH} DESTINATION ${SHELL_INSTALL_QML}/Greeter TARGET_PREFIX Greeter ${ARGN})
4+endmacro()
5+
6+add_subdirectory(Unity)
plugins/Greeter/Unity/CMakeLists.txtnew file+1
@@ -0,0 +1 @@
1+add_subdirectory(Launcher)
plugins/Greeter/Unity/Launcher/CMakeLists.txtnew file+38
@@ -0,0 +1,38 @@
1+include(FindPkgConfig)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
3+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
4+pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
5+
6+add_definitions(-DSM_BUSNAME=systemBus)
7+
8+include_directories(
9+ ${CMAKE_CURRENT_SOURCE_DIR}
10+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService
11+ ${GSETTINGS_QT_INCLUDE_DIRS}
12+ ${libunity8-private_SOURCE_DIR}
13+)
14+
15+set(QMLLAUNCHERPLUGINAS_SRC
16+ plugin.cpp
17+ launchermodelas.cpp
18+ launcheritem.cpp
19+ quicklistmodel.cpp
20+ quicklistentry.cpp
21+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
22+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
23+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
24+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
25+ )
26+
27+add_library(UnityLauncherAS-qml MODULE
28+ ${QMLLAUNCHERPLUGINAS_SRC}
29+ )
30+
31+target_link_libraries(UnityLauncherAS-qml
32+ unity8-private
33+ ${GSETTINGS_QT_LDFLAGS}
34+ )
35+
36+qt5_use_modules(UnityLauncherAS-qml DBus Qml Gui)
37+
38+add_unity8_greeter_plugin(Unity.Launcher 0.1 Unity/Launcher TARGETS UnityLauncherAS-qml)
plugins/Greeter/Unity/Launcher/Launcher.qmltypesnew file+179
@@ -0,0 +1,179 @@
1+import QtQuick.tooling 1.1
2+
3+// This file describes the plugin-supplied types contained in the library.
4+// It is used for QML tooling purposes only.
5+//
6+// This file was auto-generated by:
7+// 'qmlplugindump -notrelocatable Unity.Launcher 0.1 plugins'
8+
9+Module {
10+ Component {
11+ name: "LauncherItem"
12+ prototype: "unity::shell::launcher::LauncherItemInterface"
13+ exports: ["Unity.Launcher/LauncherItem 0.1"]
14+ isCreatable: false
15+ exportMetaObjectRevisions: [0]
16+ }
17+ Component {
18+ name: "LauncherModel"
19+ prototype: "unity::shell::launcher::LauncherModelInterface"
20+ exports: ["Unity.Launcher/LauncherModel 0.1"]
21+ isCreatable: false
22+ isSingleton: true
23+ exportMetaObjectRevisions: [0]
24+ Method {
25+ name: "requestRemove"
26+ Parameter { name: "appId"; type: "string" }
27+ }
28+ Method {
29+ name: "get"
30+ type: "unity::shell::launcher::LauncherItemInterface*"
31+ Parameter { name: "index"; type: "int" }
32+ }
33+ Method {
34+ name: "move"
35+ Parameter { name: "oldIndex"; type: "int" }
36+ Parameter { name: "newIndex"; type: "int" }
37+ }
38+ Method {
39+ name: "pin"
40+ Parameter { name: "appId"; type: "string" }
41+ Parameter { name: "index"; type: "int" }
42+ }
43+ Method {
44+ name: "pin"
45+ Parameter { name: "appId"; type: "string" }
46+ }
47+ Method {
48+ name: "quickListActionInvoked"
49+ Parameter { name: "appId"; type: "string" }
50+ Parameter { name: "actionIndex"; type: "int" }
51+ }
52+ Method {
53+ name: "setUser"
54+ Parameter { name: "username"; type: "string" }
55+ }
56+ Method {
57+ name: "getUrlForAppId"
58+ type: "string"
59+ Parameter { name: "appId"; type: "string" }
60+ }
61+ }
62+ Component {
63+ name: "QuickListModel"
64+ prototype: "unity::shell::launcher::QuickListModelInterface"
65+ exports: ["Unity.Launcher/QuickListModel 0.1"]
66+ isCreatable: false
67+ exportMetaObjectRevisions: [0]
68+ }
69+ Component {
70+ name: "unity::shell::launcher::LauncherItemInterface"
71+ prototype: "QObject"
72+ exports: ["Unity.Launcher/LauncherItemInterface 0.1"]
73+ isCreatable: false
74+ exportMetaObjectRevisions: [0]
75+ Property { name: "appId"; type: "string"; isReadonly: true }
76+ Property { name: "name"; type: "string"; isReadonly: true }
77+ Property { name: "icon"; type: "string"; isReadonly: true }
78+ Property { name: "pinned"; type: "bool"; isReadonly: true }
79+ Property { name: "running"; type: "bool"; isReadonly: true }
80+ Property { name: "recent"; type: "bool"; isReadonly: true }
81+ Property { name: "progress"; type: "int"; isReadonly: true }
82+ Property { name: "count"; type: "int"; isReadonly: true }
83+ Property { name: "countVisible"; type: "bool"; isReadonly: true }
84+ Property { name: "focused"; type: "bool"; isReadonly: true }
85+ Property {
86+ name: "quickList"
87+ type: "unity::shell::launcher::QuickListModelInterface"
88+ isReadonly: true
89+ isPointer: true
90+ }
91+ Signal {
92+ name: "nameChanged"
93+ Parameter { name: "name"; type: "string" }
94+ }
95+ Signal {
96+ name: "iconChanged"
97+ Parameter { name: "icon"; type: "string" }
98+ }
99+ Signal {
100+ name: "pinnedChanged"
101+ Parameter { name: "pinned"; type: "bool" }
102+ }
103+ Signal {
104+ name: "runningChanged"
105+ Parameter { name: "running"; type: "bool" }
106+ }
107+ Signal {
108+ name: "recentChanged"
109+ Parameter { name: "running"; type: "bool" }
110+ }
111+ Signal {
112+ name: "progressChanged"
113+ Parameter { name: "progress"; type: "int" }
114+ }
115+ Signal {
116+ name: "countChanged"
117+ Parameter { name: "count"; type: "int" }
118+ }
119+ Signal {
120+ name: "countVisibleChanged"
121+ Parameter { name: "countVisible"; type: "bool" }
122+ }
123+ Signal {
124+ name: "focusedChanged"
125+ Parameter { name: "focused"; type: "bool" }
126+ }
127+ }
128+ Component {
129+ name: "unity::shell::launcher::LauncherModelInterface"
130+ prototype: "QAbstractListModel"
131+ exports: ["Unity.Launcher/LauncherModelInterface 0.1"]
132+ isCreatable: false
133+ exportMetaObjectRevisions: [0]
134+ Property {
135+ name: "applicationManager"
136+ type: "unity::shell::application::ApplicationManagerInterface"
137+ isPointer: true
138+ }
139+ Method {
140+ name: "move"
141+ Parameter { name: "oldIndex"; type: "int" }
142+ Parameter { name: "newIndex"; type: "int" }
143+ }
144+ Method {
145+ name: "get"
146+ type: "unity::shell::launcher::LauncherItemInterface*"
147+ Parameter { name: "index"; type: "int" }
148+ }
149+ Method {
150+ name: "pin"
151+ Parameter { name: "appId"; type: "string" }
152+ Parameter { name: "index"; type: "int" }
153+ }
154+ Method {
155+ name: "pin"
156+ Parameter { name: "appId"; type: "string" }
157+ }
158+ Method {
159+ name: "requestRemove"
160+ Parameter { name: "appId"; type: "string" }
161+ }
162+ Method {
163+ name: "quickListActionInvoked"
164+ Parameter { name: "appId"; type: "string" }
165+ Parameter { name: "actionIndex"; type: "int" }
166+ }
167+ Method {
168+ name: "setUser"
169+ Parameter { name: "username"; type: "string" }
170+ }
171+ }
172+ Component {
173+ name: "unity::shell::launcher::QuickListModelInterface"
174+ prototype: "QAbstractListModel"
175+ exports: ["Unity.Launcher/QuickListInterface 0.1"]
176+ isCreatable: false
177+ exportMetaObjectRevisions: [0]
178+ }
179+}
plugins/Greeter/Unity/Launcher/launcheritem.cppnew file+171
@@ -0,0 +1,171 @@
1+/*
2+ * Copyright 2014-2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "launcheritem.h"
18+#include "quicklistmodel.h"
19+
20+#include <libintl.h>
21+
22+LauncherItem::LauncherItem(const QString &appId, const QString &name, const QString &icon, QObject *parent) :
23+ LauncherItemInterface(parent),
24+ m_appId(appId),
25+ m_name(name),
26+ m_icon(icon),
27+ m_pinned(false),
28+ m_running(false),
29+ m_recent(false),
30+ m_progress(-1),
31+ m_count(0),
32+ m_countVisible(false),
33+ m_focused(false),
34+ m_quickList(new QuickListModel(this))
35+{
36+ QuickListEntry nameAction;
37+ nameAction.setActionId("launch_item");
38+ nameAction.setText(m_name);
39+ m_quickList->appendAction(nameAction);
40+}
41+
42+QString LauncherItem::appId() const
43+{
44+ return m_appId;
45+}
46+
47+QString LauncherItem::name() const
48+{
49+ return m_name;
50+}
51+
52+void LauncherItem::setName(const QString &name)
53+{
54+ if (m_name != name) {
55+ m_name = name;
56+ QuickListEntry entry;
57+ entry.setActionId("launch_item");
58+ entry.setText(m_name);
59+ m_quickList->updateAction(entry);
60+ Q_EMIT nameChanged(name);
61+ }
62+}
63+
64+QString LauncherItem::icon() const
65+{
66+ return m_icon;
67+}
68+
69+void LauncherItem::setIcon(const QString &icon)
70+{
71+ if (m_icon != icon) {
72+ m_icon = icon;
73+ Q_EMIT iconChanged(icon);
74+ }
75+}
76+
77+bool LauncherItem::pinned() const
78+{
79+ return m_pinned;
80+}
81+
82+void LauncherItem::setPinned(bool pinned)
83+{
84+ if (m_pinned != pinned) {
85+ m_pinned = pinned;
86+ Q_EMIT pinnedChanged(pinned);
87+ }
88+}
89+
90+bool LauncherItem::running() const
91+{
92+ return m_running;
93+}
94+
95+void LauncherItem::setRunning(bool running)
96+{
97+ if (m_running != running) {
98+ m_running = running;
99+ Q_EMIT runningChanged(running);
100+ }
101+}
102+
103+bool LauncherItem::recent() const
104+{
105+ return m_recent;
106+}
107+
108+void LauncherItem::setRecent(bool recent)
109+{
110+ if (m_recent != recent) {
111+ m_recent = recent;
112+ Q_EMIT recentChanged(recent);
113+ }
114+}
115+
116+int LauncherItem::progress() const
117+{
118+ return m_progress;
119+}
120+
121+void LauncherItem::setProgress(int progress)
122+{
123+ if (m_progress != progress) {
124+ m_progress = progress;
125+ Q_EMIT progressChanged(progress);
126+ }
127+}
128+
129+int LauncherItem::count() const
130+{
131+ return m_count;
132+}
133+
134+void LauncherItem::setCount(int count)
135+{
136+ if (m_count != count) {
137+ m_count = count;
138+ Q_EMIT countChanged(count);
139+ }
140+}
141+
142+bool LauncherItem::countVisible() const
143+{
144+ return m_countVisible;
145+}
146+
147+void LauncherItem::setCountVisible(bool countVisible)
148+{
149+ if (m_countVisible != countVisible) {
150+ m_countVisible = countVisible;
151+ Q_EMIT countVisibleChanged(countVisible);
152+ }
153+}
154+
155+bool LauncherItem::focused() const
156+{
157+ return m_focused;
158+}
159+
160+void LauncherItem::setFocused(bool focused)
161+{
162+ if (m_focused != focused) {
163+ m_focused = focused;
164+ Q_EMIT focusedChanged(focused);
165+ }
166+}
167+
168+unity::shell::launcher::QuickListModelInterface *LauncherItem::quickList() const
169+{
170+ return m_quickList;
171+}
plugins/Greeter/Unity/Launcher/launcheritem.hnew file+74
@@ -0,0 +1,74 @@
1+/*
2+ * Copyright 2014-2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef LAUNCHERITEM_H
18+#define LAUNCHERITEM_H
19+
20+#include "quicklistmodel.h"
21+
22+#include <unity/shell/launcher/LauncherItemInterface.h>
23+
24+class QuickListModel;
25+
26+using namespace unity::shell::launcher;
27+
28+class LauncherItem: public LauncherItemInterface
29+{
30+ Q_OBJECT
31+public:
32+ LauncherItem(const QString &appId, const QString &name, const QString &icon, QObject *parent = 0);
33+
34+ QString appId() const override;
35+ QString name() const override;
36+ QString icon() const override;
37+ bool pinned() const override;
38+ bool running() const override;
39+ bool recent() const override;
40+ int progress() const override;
41+ int count() const override;
42+ bool countVisible() const override;
43+ bool focused() const override;
44+
45+ unity::shell::launcher::QuickListModelInterface *quickList() const override;
46+
47+private:
48+ void setName(const QString &name);
49+ void setIcon(const QString &icon);
50+ void setPinned(bool pinned);
51+ void setRunning(bool running);
52+ void setRecent(bool recent);
53+ void setProgress(int progress);
54+ void setCount(int count);
55+ void setCountVisible(bool countVisible);
56+ void setFocused(bool focused);
57+
58+private:
59+ QString m_appId;
60+ QString m_name;
61+ QString m_icon;
62+ bool m_pinned;
63+ bool m_running;
64+ bool m_recent;
65+ int m_progress;
66+ int m_count;
67+ bool m_countVisible;
68+ bool m_focused;
69+ QuickListModel *m_quickList;
70+
71+ friend class LauncherModel;
72+};
73+
74+#endif // LAUNCHERITEM_H
plugins/Greeter/Unity/Launcher/launchermodelas.cppnew file+277
@@ -0,0 +1,277 @@
1+/*
2+ * Copyright 2014-2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "launchermodelas.h"
18+#include "launcheritem.h"
19+#include "AccountsServiceDBusAdaptor.h"
20+#include <unity/shell/application/ApplicationInfoInterface.h>
21+
22+#include <QDesktopServices>
23+#include <QDebug>
24+#include <QDBusArgument>
25+
26+using namespace unity::shell::application;
27+
28+LauncherModel::LauncherModel(QObject *parent):
29+ LauncherModelInterface(parent),
30+ m_accounts(new AccountsServiceDBusAdaptor(this)),
31+ m_onlyPinned(true)
32+{
33+ connect(m_accounts, &AccountsServiceDBusAdaptor::propertiesChanged, this, &LauncherModel::propertiesChanged);
34+ refresh();
35+}
36+
37+LauncherModel::~LauncherModel()
38+{
39+ while (!m_list.empty()) {
40+ m_list.takeFirst()->deleteLater();
41+ }
42+}
43+
44+int LauncherModel::rowCount(const QModelIndex &parent) const
45+{
46+ Q_UNUSED(parent)
47+ return m_list.count();
48+}
49+
50+QVariant LauncherModel::data(const QModelIndex &index, int role) const
51+{
52+ LauncherItem *item = m_list.at(index.row());
53+ switch(role) {
54+ case RoleAppId:
55+ return item->appId();
56+ case RoleName:
57+ return item->name();
58+ case RoleIcon:
59+ return item->icon();
60+ case RolePinned:
61+ return item->pinned();
62+ case RoleCount:
63+ return item->count();
64+ case RoleCountVisible:
65+ return item->countVisible();
66+ case RoleProgress:
67+ return item->progress();
68+ case RoleFocused:
69+ return item->focused();
70+ }
71+
72+ return QVariant();
73+}
74+
75+unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) const
76+{
77+ if (index < 0 || index >= m_list.count()) {
78+ return 0;
79+ }
80+ return m_list.at(index);
81+}
82+
83+void LauncherModel::move(int oldIndex, int newIndex)
84+{
85+ Q_UNUSED(oldIndex)
86+ Q_UNUSED(newIndex)
87+ qWarning() << "This is a read only implementation. Cannot move items.";
88+}
89+
90+void LauncherModel::pin(const QString &appId, int index)
91+{
92+ Q_UNUSED(appId)
93+ Q_UNUSED(index)
94+ qWarning() << "This is a read only implementation. Cannot pin items";
95+}
96+
97+void LauncherModel::requestRemove(const QString &appId)
98+{
99+ Q_UNUSED(appId)
100+ qWarning() << "This is a read only implementation. Cannot remove items";
101+}
102+
103+void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex)
104+{
105+ int index = findApplication(appId);
106+ if (index < 0) {
107+ return;
108+ }
109+
110+ LauncherItem *item = m_list.at(index);
111+ QuickListModel *model = qobject_cast<QuickListModel*>(item->quickList());
112+ if (model) {
113+ QString actionId = model->get(actionIndex).actionId();
114+
115+ // Check if this is one of the launcher actions we handle ourselves
116+ if (actionId == "launch_item") {
117+ QDesktopServices::openUrl(getUrlForAppId(appId));
118+
119+ // Nope, we don't know this action, let the backend forward it to the application
120+ } else {
121+ // TODO: forward quicklist action to app, possibly via m_dbusIface
122+ }
123+ }
124+}
125+
126+void LauncherModel::setUser(const QString &username)
127+{
128+ if (m_user != username) {
129+ m_user = username;
130+ refresh();
131+ }
132+}
133+
134+QString LauncherModel::getUrlForAppId(const QString &appId) const
135+{
136+ // appId is either an appId or a legacy app name. Let's find out which
137+ if (appId.isEmpty()) {
138+ return QString();
139+ }
140+
141+ if (!appId.contains("_")) {
142+ return "application:///" + appId + ".desktop";
143+ }
144+
145+ QStringList parts = appId.split('_');
146+ QString package = parts.value(0);
147+ QString app = parts.value(1, "first-listed-app");
148+ return "appid://" + package + "/" + app + "/current-user-version";
149+}
150+
151+ApplicationManagerInterface *LauncherModel::applicationManager() const
152+{
153+ return nullptr;
154+}
155+
156+void LauncherModel::setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager)
157+{
158+ Q_UNUSED(appManager)
159+ qWarning() << "This plugin syncs all its states from AccountsService. Not using ApplicationManager.";
160+ return;
161+}
162+
163+bool LauncherModel::onlyPinned() const
164+{
165+ return m_onlyPinned;
166+}
167+
168+void LauncherModel::setOnlyPinned(bool onlyPinned)
169+{
170+ if (m_onlyPinned != onlyPinned) {
171+ m_onlyPinned = onlyPinned;
172+ Q_EMIT onlyPinnedChanged();
173+ refresh();
174+ }
175+}
176+
177+int LauncherModel::findApplication(const QString &appId)
178+{
179+ for (int i = 0; i < m_list.count(); ++i) {
180+ LauncherItem *item = m_list.at(i);
181+ if (item->appId() == appId) {
182+ return i;
183+ }
184+ }
185+ return -1;
186+}
187+
188+typedef QList<QVariantMap> VariantMapList;
189+void LauncherModel::refresh()
190+{
191+ QList<QVariantMap> items;
192+
193+ if (m_accounts && !m_user.isEmpty()) {
194+ items = m_accounts->getUserProperty<VariantMapList>(m_user, "com.canonical.unity.AccountsService", "LauncherItems");
195+ }
196+
197+ // First walk through all the existing items and see if we need to remove something
198+ QList<LauncherItem*> toBeRemoved;
199+
200+ Q_FOREACH (LauncherItem* item, m_list) {
201+ bool found = false;
202+ Q_FOREACH(const QVariant &asItem, items) {
203+ if (asItem.toMap().value("id").toString() == item->appId()) {
204+ // Only keep and update it if we either show unpinned or it is pinned
205+ if (!m_onlyPinned || asItem.toMap().value("pinned").toBool()) {
206+ found = true;
207+ item->setName(asItem.toMap().value("name").toString());
208+ item->setIcon(asItem.toMap().value("icon").toString());
209+ item->setCount(asItem.toMap().value("count").toInt());
210+ item->setCountVisible(asItem.toMap().value("countVisible").toBool());
211+ int idx = m_list.indexOf(item);
212+ Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleName << RoleIcon);
213+ }
214+ break;
215+ }
216+ }
217+ if (!found) {
218+ toBeRemoved.append(item);
219+ }
220+ }
221+
222+ Q_FOREACH (LauncherItem* item, toBeRemoved) {
223+ int idx = m_list.indexOf(item);
224+ beginRemoveRows(QModelIndex(), idx, idx);
225+ m_list.takeAt(idx)->deleteLater();
226+ endRemoveRows();
227+ }
228+
229+ // Now walk through list and see if we need to add something
230+ int skipped = 0;
231+ for (int asIndex = 0; asIndex < items.count(); ++asIndex) {
232+ QVariant entry = items.at(asIndex);
233+
234+ if (m_onlyPinned && !entry.toMap().value("pinned").toBool()) {
235+ // Skipping it as we only show pinned and it is not
236+ skipped++;
237+ continue;
238+ }
239+ int newPosition = asIndex - skipped;
240+
241+ int itemIndex = -1;
242+ for (int i = 0; i < m_list.count(); ++i) {
243+ if (m_list.at(i)->appId() == entry.toMap().value("id").toString()) {
244+ itemIndex = i;
245+ break;
246+ }
247+ }
248+
249+ if (itemIndex == -1) {
250+ // Need to add it. Just add it into the addedIndex to keep same ordering as the list in AS.
251+ LauncherItem *item = new LauncherItem(entry.toMap().value("id").toString(),
252+ entry.toMap().value("name").toString(),
253+ entry.toMap().value("icon").toString(),
254+ this);
255+ item->setPinned(true);
256+ item->setCount(entry.toMap().value("count").toInt());
257+ item->setCountVisible(entry.toMap().value("countVisible").toBool());
258+ beginInsertRows(QModelIndex(), newPosition, newPosition);
259+ m_list.insert(newPosition, item);
260+ endInsertRows();
261+ } else if (itemIndex != newPosition) {
262+ // The item is already there, but it is in a different place than in the settings.
263+ // Move it to the addedIndex
264+ beginMoveRows(QModelIndex(), itemIndex, itemIndex, QModelIndex(), newPosition);
265+ m_list.move(itemIndex, newPosition);
266+ endMoveRows();
267+ }
268+ }
269+}
270+
271+void LauncherModel::propertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
272+{
273+ if (user != m_user || interface != "com.canonical.unity.AccountsService" || !changed.contains("LauncherItems")) {
274+ return;
275+ }
276+ refresh();
277+}
plugins/Greeter/Unity/Launcher/launchermodelas.hnew file+75
@@ -0,0 +1,75 @@
1+/*
2+ * Copyright 2014-2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef LAUNCHERMODEL_H
18+#define LAUNCHERMODEL_H
19+
20+#include <unity/shell/launcher/LauncherModelInterface.h>
21+#include <unity/shell/application/ApplicationManagerInterface.h>
22+
23+#include <QAbstractListModel>
24+
25+class LauncherItem;
26+class GSettings;
27+class AccountsServiceDBusAdaptor;
28+
29+using namespace unity::shell::launcher;
30+using namespace unity::shell::application;
31+
32+class LauncherModel: public LauncherModelInterface
33+{
34+ Q_OBJECT
35+
36+public:
37+ LauncherModel(QObject *parent = 0);
38+ ~LauncherModel();
39+
40+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
41+
42+ QVariant data(const QModelIndex &index, int role) const;
43+
44+ Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const;
45+ Q_INVOKABLE void move(int oldIndex, int newIndex);
46+ Q_INVOKABLE void pin(const QString &appId, int index = -1);
47+ Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
48+ Q_INVOKABLE void setUser(const QString &username);
49+ Q_INVOKABLE QString getUrlForAppId(const QString &appId) const;
50+
51+ unity::shell::application::ApplicationManagerInterface* applicationManager() const;
52+ void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
53+
54+ bool onlyPinned() const override;
55+ void setOnlyPinned(bool onlyPinned) override;
56+
57+ int findApplication(const QString &appId);
58+
59+public Q_SLOTS:
60+ void requestRemove(const QString &appId) override;
61+ Q_INVOKABLE void refresh();
62+
63+private Q_SLOTS:
64+ void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
65+
66+private:
67+ QString m_user;
68+ QList<LauncherItem*> m_list;
69+ AccountsServiceDBusAdaptor *m_accounts;
70+ bool m_onlyPinned;
71+
72+ friend class LauncherModelASTest;
73+};
74+
75+#endif // LAUNCHERMODEL_H
plugins/Greeter/Unity/Launcher/plugin.cppnew file+46
@@ -0,0 +1,46 @@
1+/*
2+ * Copyright 2014-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+// Qt
18+#include <QtQml/qqml.h>
19+
20+// self
21+#include "plugin.h"
22+
23+// local
24+#include "launchermodelas.h"
25+#include "launcheritem.h"
26+
27+
28+using namespace unity::shell::launcher;
29+
30+static QObject* modelProvider(QQmlEngine* /* engine */, QJSEngine* /* scriptEngine */)
31+{
32+ return new LauncherModel();
33+}
34+
35+void UnityLauncherPlugin::registerTypes(const char *uri)
36+{
37+ Q_ASSERT(uri == QLatin1String("Unity.Launcher"));
38+
39+ qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
40+ qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
41+ qmlRegisterUncreatableType<QuickListModelInterface>(uri, 0, 1, "QuickListInterface", "Abstract Interface. Cannot be instantiated.");
42+
43+ qmlRegisterSingletonType<LauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
44+ qmlRegisterUncreatableType<LauncherItem>(uri, 0, 1, "LauncherItem", "Can't create new Launcher Items in QML. Get them from the LauncherModel.");
45+ qmlRegisterUncreatableType<QuickListModel>(uri, 0, 1, "QuickListModel", "Can't create a QuickListModel in QML. Get them from the LauncherItems.");
46+}
tests/mocks/LightDM/single-passphrase/UsersModelPrivate.cpp → plugins/Greeter/Unity/Launcher/plugin.hrenamed+13−14
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 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
@@ -12,22 +12,21 @@
12 *
12 *
13 * You should have received a copy of the GNU General Public License
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
17 */
15 */
18
16
19−#include "../UsersModelPrivate.h"
17+#ifndef UNITY_LAUNCHER_PLUGIN_H
18+#define UNITY_LAUNCHER_PLUGIN_H
20
19
21−namespace QLightDM
20+#include <QtQml/QQmlEngine>
22−{
21+#include <QtQml/QQmlExtensionPlugin>
23
22
24−UsersModelPrivate::UsersModelPrivate(UsersModel* parent)
23+class UnityLauncherPlugin : public QQmlExtensionPlugin
25− : q_ptr(parent)
26{
24{
27− entries =
25+ Q_OBJECT
28− {
26+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29− { "single", "Has Passphrase", 0, 0, false, false, 0, 0 },
27+
28+public:
29+ void registerTypes(const char *uri);
30− };
30+};
31−}
32
31
33−}
32+#endif
plugins/Greeter/Unity/Launcher/qmldirnew file+3
@@ -0,0 +1,3 @@
1+module Unity.Launcher
2+plugin UnityLauncherAS-qml
3+typeinfo Launcher.qmltypes
tests/mocks/LightDM/single-pin/GreeterPrivate.cpp → plugins/Greeter/Unity/Launcher/quicklistentry.cpprenamed+28−18
@@ -1,5 +1,4 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
1+/* Copyright (C) 2014-2015 Canonical, Ltd.
3 *
2 *
4 * This program is free software; you can redistribute it and/or modify
3 * 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
4 * it under the terms of the GNU General Public License as published by
@@ -12,35 +11,46 @@
12 *
11 *
13 * You should have received a copy of the GNU General Public License
12 * 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/>.
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
17 */
14 */
18
15
19−#include "../Greeter.h"
16+#include "quicklistentry.h"
20−#include "../GreeterPrivate.h"
17+
18+QuickListEntry::QuickListEntry()
19+{
20+
21+}
22+
23+QString QuickListEntry::actionId() const
24+{
25+ return m_actionId;
26+}
21
27
22−namespace QLightDM
28+void QuickListEntry::setActionId(const QString &actionId)
23{
29{
30+ m_actionId = actionId;
31+}
24
32
25−GreeterPrivate::GreeterPrivate(Greeter* parent)
33+QString QuickListEntry::text() const
26− : authenticated(false),
27− authenticationUser(),
28− q_ptr(parent)
29{
34{
35+ return m_text;
30}
36}
31
37
32−void GreeterPrivate::handleAuthenticate()
38+void QuickListEntry::setText(const QString &text)
33{
39{
34− Q_Q(Greeter);
40+ m_text = text;
35− Q_EMIT q->showPrompt("Password: ", Greeter::PromptTypeSecret);
36}
41}
37
42
38−void GreeterPrivate::handleRespond(const QString &response)
43+QString QuickListEntry::icon() const
39{
44{
40− Q_Q(Greeter);
45+ return m_icon;
46+}
41
47
42− authenticated = (response == "1234");
48+void QuickListEntry::setIcon(const QString &icon)
43− q->sendAuthenticationComplete();
49+{
50+ m_icon = icon;
44}
51}
45
52
53+bool QuickListEntry::clickable() const
54+{
55+ return !m_actionId.isEmpty();
46}
56}
plugins/Greeter/Unity/Launcher/quicklistentry.hnew file+43
@@ -0,0 +1,43 @@
1+/* Copyright (C) 2014-2015 Canonical, Ltd.
2+ *
3+ * This program is free software; you can redistribute it and/or modify
4+ * it under the terms of the GNU General Public License as published by
5+ * the Free Software Foundation; version 3.
6+ *
7+ * This program is distributed in the hope that it will be useful,
8+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
9+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+ * GNU General Public License for more details.
11+ *
12+ * You should have received a copy of the GNU General Public License
13+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
14+ */
15+
16+#ifndef QUICKLISTENTRY_H
17+#define QUICKLISTENTRY_H
18+
19+#include <QString>
20+
21+class QuickListEntry
22+{
23+public:
24+ QuickListEntry();
25+
26+ QString actionId() const;
27+ void setActionId(const QString &actionId);
28+
29+ QString text() const;
30+ void setText(const QString &text);
31+
32+ QString icon() const;
33+ void setIcon(const QString &icon);
34+
35+ bool clickable() const;
36+
37+private:
38+ QString m_actionId;
39+ QString m_text;
40+ QString m_icon;
41+};
42+
43+#endif // QUICKLISTENTRY
plugins/Greeter/Unity/Launcher/quicklistmodel.cppnew file+70
@@ -0,0 +1,70 @@
1+/*
2+ * Copyright 2014-2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "quicklistmodel.h"
18+
19+QuickListModel::QuickListModel(QObject *parent) :
20+ QuickListModelInterface(parent)
21+{
22+
23+}
24+
25+QuickListModel::~QuickListModel()
26+{
27+
28+}
29+
30+void QuickListModel::appendAction(const QuickListEntry &entry)
31+{
32+ beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
33+ m_list.append(entry);
34+ endInsertRows();
35+}
36+
37+void QuickListModel::updateAction(const QuickListEntry &entry)
38+{
39+ for (int i = 0; i < m_list.count(); ++i) {
40+ if (m_list.at(i).actionId() == entry.actionId()) {
41+ m_list.replace(i, entry);
42+ Q_EMIT dataChanged(index(i), index(i));
43+ return;
44+ }
45+ }
46+}
47+
48+QuickListEntry QuickListModel::get(int index) const
49+{
50+ return m_list.at(index);
51+}
52+
53+int QuickListModel::rowCount(const QModelIndex &index) const
54+{
55+ Q_UNUSED(index)
56+ return m_list.count();
57+}
58+
59+QVariant QuickListModel::data(const QModelIndex &index, int role) const
60+{
61+ switch (role) {
62+ case RoleLabel:
63+ return m_list.at(index.row()).text();
64+ case RoleIcon:
65+ return m_list.at(index.row()).icon();
66+ case RoleClickable:
67+ return m_list.at(index.row()).clickable();
68+ }
69+ return QVariant();
70+}
plugins/Greeter/Unity/Launcher/quicklistmodel.hnew file+55
@@ -0,0 +1,55 @@
1+/*
2+ * Copyright 2014-2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef QUICKLISTMODEL_H
18+#define QUICKLISTMODEL_H
19+
20+#include "quicklistentry.h"
21+
22+#include <unity/shell/launcher/QuickListModelInterface.h>
23+
24+using namespace unity::shell::launcher;
25+
26+class QuickListModel: public QuickListModelInterface
27+{
28+ Q_OBJECT
29+
30+public:
31+ explicit QuickListModel(QObject *parent = 0);
32+
33+ ~QuickListModel();
34+
35+ void appendAction(const QuickListEntry &entry);
36+
37+ /**
38+ * @brief Update an existing action
39+ * @param entry The new, updated entry
40+ *
41+ * This will only do something if entry.actionId is found in the model.
42+ * To add a new entry, use appendAction().
43+ */
44+ void updateAction(const QuickListEntry &entry);
45+
46+ QuickListEntry get(int index) const;
47+
48+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
49+ QVariant data(const QModelIndex &index, int role) const;
50+
51+private:
52+ QList<QuickListEntry> m_list;
53+};
54+
55+#endif // QUICKLISTMODEL_H
plugins/LightDM/CMakeLists.txt+3−3
@@ -1,9 +1,9 @@
1−# Dependencies
2−include(FindPkgConfig)
3pkg_check_modules(LIBUSERMETRICSOUTPUT REQUIRED libusermetricsoutput-1)
1pkg_check_modules(LIBUSERMETRICSOUTPUT REQUIRED libusermetricsoutput-1)
4# TODO: Once we split out a separate greeter process, uncomment these lines
2# TODO: Once we split out a separate greeter process, uncomment these lines
5#pkg_check_modules(LIBLIGHTDM REQUIRED liblightdm-qt5-2)
3#pkg_check_modules(LIBLIGHTDM REQUIRED liblightdm-qt5-2)
6
4
5+add_subdirectory(liblightdm)
6+
7include_directories(
7include_directories(
8 ${CMAKE_CURRENT_SOURCE_DIR}
8 ${CMAKE_CURRENT_SOURCE_DIR}
9 ${CMAKE_CURRENT_BINARY_DIR}
9 ${CMAKE_CURRENT_BINARY_DIR}
@@ -28,7 +28,7 @@ add_library(LightDM-qml MODULE
28 )
28 )
29
29
30target_link_libraries(LightDM-qml
30target_link_libraries(LightDM-qml
31− MockLightDM-demo
31+ integratedLightDM
32 unity8-private
32 unity8-private
33# TODO: Once we split out a separate greeter process, uncomment these lines
33# TODO: Once we split out a separate greeter process, uncomment these lines
34# ${LIBLIGHTDM_LDFLAGS}
34# ${LIBLIGHTDM_LDFLAGS}
plugins/LightDM/Greeter.cpp+1−19
@@ -17,26 +17,8 @@
17 */
17 */
18
18
19#include "Greeter.h"
19#include "Greeter.h"
20+#include "GreeterPrivate.h"
20#include <libintl.h>
21#include <libintl.h>
21−#include <QLightDM/Greeter>
22−
23−class GreeterPrivate
24−{
25−public:
26− explicit GreeterPrivate(Greeter *parent);
27−
28− QLightDM::Greeter *m_greeter;
29− bool m_active;
30− bool wasPrompted;
31− bool promptless;
32−
33−protected:
34− Greeter * const q_ptr;
35−
36−private:
37− Q_DECLARE_PUBLIC(Greeter)
38−};
39−
40
22
41GreeterPrivate::GreeterPrivate(Greeter* parent)
23GreeterPrivate::GreeterPrivate(Greeter* parent)
42 : m_greeter(new QLightDM::Greeter(parent)),
24 : m_greeter(new QLightDM::Greeter(parent)),
plugins/LightDM/Greeter.h+1−1
@@ -68,7 +68,7 @@ Q_SIGNALS:
68 // should switch to this user if possible.
68 // should switch to this user if possible.
69 void requestAuthenticationUser(const QString &user);
69 void requestAuthenticationUser(const QString &user);
70
70
71−private:
71+protected:
72 GreeterPrivate * const d_ptr;
72 GreeterPrivate * const d_ptr;
73
73
74 Q_DECLARE_PRIVATE(Greeter)
74 Q_DECLARE_PRIVATE(Greeter)
plugins/LightDM/GreeterPrivate.hnew file+39
@@ -0,0 +1,39 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef UNITY_GREETER_PRIVATE_H
18+#define UNITY_GREETER_PRIVATE_H
19+
20+#include <QLightDM/Greeter>
21+
22+class GreeterPrivate
23+{
24+public:
25+ explicit GreeterPrivate(Greeter *parent);
26+
27+ QLightDM::Greeter *m_greeter;
28+ bool m_active;
29+ bool wasPrompted;
30+ bool promptless;
31+
32+protected:
33+ Greeter * const q_ptr;
34+
35+private:
36+ Q_DECLARE_PUBLIC(Greeter)
37+};
38+
39+#endif // UNITY_GREETER_PRIVATE_H
tests/mocks/LightDM/demo/CMakeLists.txt → plugins/LightDM/liblightdm/CMakeLists.txtrenamed+6−6
@@ -1,14 +1,14 @@
1pkg_check_modules(LIBUSERMETRICSOUTPUT REQUIRED libusermetricsoutput-1)
1pkg_check_modules(LIBUSERMETRICSOUTPUT REQUIRED libusermetricsoutput-1)
2
2
3set(LibLightDM_SOURCES
3set(LibLightDM_SOURCES
4− ../Greeter.cpp
4+ Greeter.cpp
5− ../UsersModel.cpp
5+ UsersModel.cpp
6 GreeterPrivate.cpp
6 GreeterPrivate.cpp
7 UsersModelPrivate.cpp
7 UsersModelPrivate.cpp
8 ${CMAKE_SOURCE_DIR}/plugins/Utils/qvariantlistmodel.cpp
8 ${CMAKE_SOURCE_DIR}/plugins/Utils/qvariantlistmodel.cpp
9 )
9 )
10
10
11−add_library(MockLightDM-demo STATIC ${LibLightDM_SOURCES})
11+add_library(integratedLightDM STATIC ${LibLightDM_SOURCES})
12add_library(MockLightDM-demo-shared SHARED ${LibLightDM_SOURCES})
12add_library(MockLightDM-demo-shared SHARED ${LibLightDM_SOURCES})
13
13
14include_directories(
14include_directories(
@@ -16,7 +16,7 @@ include_directories(
16 ${LIBUSERMETRICSOUTPUT_INCLUDE_DIRS}
16 ${LIBUSERMETRICSOUTPUT_INCLUDE_DIRS}
17)
17)
18
18
19−target_link_libraries(MockLightDM-demo
19+target_link_libraries(integratedLightDM
20 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
20 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
21 -lpam
21 -lpam
22)
22)
@@ -25,10 +25,10 @@ target_link_libraries(MockLightDM-demo-shared
25 -lpam
25 -lpam
26)
26)
27
27
28−qt5_use_modules(MockLightDM-demo Concurrent Gui)
28+qt5_use_modules(integratedLightDM Concurrent Gui)
29qt5_use_modules(MockLightDM-demo-shared Concurrent Gui)
29qt5_use_modules(MockLightDM-demo-shared Concurrent Gui)
30
30
31−set_target_properties(MockLightDM-demo PROPERTIES COMPILE_FLAGS -fPIC)
31+set_target_properties(integratedLightDM PROPERTIES COMPILE_FLAGS -fPIC)
32set_target_properties(MockLightDM-demo-shared PROPERTIES
32set_target_properties(MockLightDM-demo-shared PROPERTIES
33 OUTPUT_NAME lightdm-qt5-2)
33 OUTPUT_NAME lightdm-qt5-2)
34
34
tests/mocks/LightDM/Greeter.cpp → plugins/LightDM/liblightdm/Greeter.cpprenamed+7−7
@@ -16,6 +16,13 @@
16 * Author: Michael Terry <michael.terry@canonical.com>
16 * Author: Michael Terry <michael.terry@canonical.com>
17 */
17 */
18
18
19+
20+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
21+ * CHANGES MADE HERE MUST BE REFLECTED ON THE MOCK LIB
22+ * COUNTERPART IN tests/mocks/Lightdm/liblightdm
23+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
24+
25+
19#include "Greeter.h"
26#include "Greeter.h"
20#include "GreeterPrivate.h"
27#include "GreeterPrivate.h"
21#include <QtCore/QCoreApplication>
28#include <QtCore/QCoreApplication>
@@ -128,13 +135,6 @@ void Greeter::authenticate(const QString &username)
128
135
129 d->authenticated = false;
136 d->authenticated = false;
130 d->authenticationUser = username;
137 d->authenticationUser = username;
131− d->twoFactorDone = false;
132− QTimer::singleShot(0, this, SLOT(delayedAuthentication()));
133−}
134−
135−void Greeter::delayedAuthentication()
136−{
137− Q_D(Greeter);
138 d->handleAuthenticate();
138 d->handleAuthenticate();
139}
139}
140
140
tests/mocks/LightDM/Greeter.h → plugins/LightDM/liblightdm/Greeter.hrenamed+4−3
@@ -24,6 +24,10 @@
24#include <QtCore/QObject>
24#include <QtCore/QObject>
25#include <QtCore/QVariant>
25#include <QtCore/QVariant>
26
26
27+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
28+ * CHANGES MADE HERE MUST BE REFLECTED ON THE MOCK LIB
29+ * COUNTERPART IN tests/mocks/Lightdm/liblightdm
30+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
27
31
28namespace QLightDM
32namespace QLightDM
29{
33{
@@ -99,9 +103,6 @@ Q_SIGNALS:
99protected:
103protected:
100 void sendAuthenticationComplete();
104 void sendAuthenticationComplete();
101
105
102−private Q_SLOTS:
103− void delayedAuthentication();
104−
105private:
106private:
106 GreeterPrivate *d_ptr;
107 GreeterPrivate *d_ptr;
107 Q_DECLARE_PRIVATE(Greeter)
108 Q_DECLARE_PRIVATE(Greeter)
tests/mocks/LightDM/demo/GreeterPrivate.cpp → plugins/LightDM/liblightdm/GreeterPrivate.cpprenamed+2−2
@@ -16,8 +16,8 @@
16 * Author: Michael Terry <michael.terry@canonical.com>
16 * Author: Michael Terry <michael.terry@canonical.com>
17 */
17 */
18
18
19−#include "../Greeter.h"
19+#include "Greeter.h"
20−#include "../GreeterPrivate.h"
20+#include "GreeterPrivate.h"
21#include <QFuture>
21#include <QFuture>
22#include <QFutureInterface>
22#include <QFutureInterface>
23#include <QFutureWatcher>
23#include <QFutureWatcher>
tests/mocks/LightDM/GreeterPrivate.h → plugins/LightDM/liblightdm/GreeterPrivate.hrenamed−1
@@ -35,7 +35,6 @@ public:
35 // These variables may not be used by all subclasses, that's no problem
35 // These variables may not be used by all subclasses, that's no problem
36 bool authenticated;
36 bool authenticated;
37 QString authenticationUser;
37 QString authenticationUser;
38− bool twoFactorDone;
39
38
40 void handleAuthenticate();
39 void handleAuthenticate();
41 void handleRespond(const QString &response);
40 void handleRespond(const QString &response);
tests/mocks/LightDM/UsersModel.cpp → plugins/LightDM/liblightdm/UsersModel.cpprenamed+6
@@ -16,6 +16,12 @@
16 * Author: Michael Terry <michael.terry@canonical.com>
16 * Author: Michael Terry <michael.terry@canonical.com>
17 */
17 */
18
18
19+
20+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
21+ * CHANGES MADE HERE MUST BE REFLECTED ON THE MOCK LIB
22+ * COUNTERPART IN tests/mocks/Lightdm/liblightdm
23+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
24+
19// LightDM currently is Qt4 compatible, and so doesn't define setRoleNames.
25// LightDM currently is Qt4 compatible, and so doesn't define setRoleNames.
20// To use the same method of setting role name that it does, we
26// To use the same method of setting role name that it does, we
21// set our compatibility to Qt4 here too.
27// set our compatibility to Qt4 here too.
tests/mocks/LightDM/UsersModel.h → plugins/LightDM/liblightdm/UsersModel.hrenamed+4
@@ -24,6 +24,10 @@
24#include <QtCore/QSharedDataPointer>
24#include <QtCore/QSharedDataPointer>
25#include <QAbstractListModel>
25#include <QAbstractListModel>
26
26
27+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
28+ * CHANGES MADE HERE MUST BE REFLECTED ON THE MOCK LIB
29+ * COUNTERPART IN tests/mocks/Lightdm/liblightdm
30+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
27
31
28namespace QLightDM
32namespace QLightDM
29{
33{
tests/mocks/LightDM/demo/UsersModelPrivate.cpp → plugins/LightDM/liblightdm/UsersModelPrivate.cpprenamed+1−1
@@ -16,7 +16,7 @@
16 * Author: Michael Terry <michael.terry@canonical.com>
16 * Author: Michael Terry <michael.terry@canonical.com>
17 */
17 */
18
18
19−#include "../UsersModelPrivate.h"
19+#include "UsersModelPrivate.h"
20
20
21#include <QDir>
21#include <QDir>
22#include <QSettings>
22#include <QSettings>
tests/mocks/LightDM/UsersModelPrivate.h → plugins/LightDM/liblightdm/UsersModelPrivate.hrenamed

The content is the same.

plugins/Lights/CMakeLists.txt−2
@@ -1,5 +1,3 @@
1−include(FindPkgConfig)
2−
3pkg_check_modules(ANDROID_HEADERS REQUIRED android-headers)
1pkg_check_modules(ANDROID_HEADERS REQUIRED android-headers)
4pkg_check_modules(ANDROID_HARDWARE REQUIRED libhardware)
2pkg_check_modules(ANDROID_HARDWARE REQUIRED libhardware)
5
3
plugins/Powerd/CMakeLists.txt−2
@@ -1,5 +1,3 @@
1−include(FindPkgConfig)
2−
3pkg_search_module(GIO REQUIRED gio-2.0)
1pkg_search_module(GIO REQUIRED gio-2.0)
4
2
5include_directories(
3include_directories(
plugins/Ubuntu/Gestures/TouchGate.cpp+2
@@ -53,6 +53,8 @@ void TouchGate::touchEvent(QTouchEvent *event)
53 m_touchInfoMap[touchPoint.id()].ownership = OwnershipRequested;
53 m_touchInfoMap[touchPoint.id()].ownership = OwnershipRequested;
54 m_touchInfoMap[touchPoint.id()].ended = false;
54 m_touchInfoMap[touchPoint.id()].ended = false;
55 TouchRegistry::instance()->requestTouchOwnership(touchPoint.id(), this);
55 TouchRegistry::instance()->requestTouchOwnership(touchPoint.id(), this);
56+
57+ Q_EMIT pressed();
56 }
58 }
57
59
58 goodToGo &= m_touchInfoMap.contains(touchPoint.id())
60 goodToGo &= m_touchInfoMap.contains(touchPoint.id())
plugins/Ubuntu/Gestures/TouchGate.h+2
@@ -53,6 +53,8 @@ public:
53Q_SIGNALS:
53Q_SIGNALS:
54 void targetItemChanged(QQuickItem *item);
54 void targetItemChanged(QQuickItem *item);
55
55
56+ void pressed();
57+
56protected:
58protected:
57 void touchEvent(QTouchEvent *event) override;
59 void touchEvent(QTouchEvent *event) override;
58private:
60private:
plugins/Unity/Indicators/indicator.cpp+23−8
@@ -31,24 +31,26 @@ Indicator::~Indicator()
31{
31{
32}
32}
33
33
34−void Indicator::init(const QString &profile, const QString& busName, const QSettings& settings)
34+void Indicator::init(const QString& busName, const QSettings& settings)
35{
35{
36− setId(settings.value("Indicator Service/Name").toString());
36+ // Save all keys we care about from the QSettings object. It's annoying
37+ // that we can't just copy the object.
38+ m_settings.clear();
39+ Q_FOREACH(const QString& key, settings.allKeys()) {
40+ if (key.endsWith("/Position") || key.endsWith("/ObjectPath")) {
41+ m_settings.insert(key, settings.value(key));
42+ }
43+ }
37
44
38− QVariant pos = settings.value(profile + "/Position");
39− if (!pos.isValid())
40− pos = settings.value("Indicator Service/Position", QVariant::fromValue(0));
45+ setId(settings.value("Indicator Service/Name").toString());
41− setPosition(pos.toInt());
42
46
43 QString actionObjectPath = settings.value("Indicator Service/ObjectPath").toString();
47 QString actionObjectPath = settings.value("Indicator Service/ObjectPath").toString();
44− QString menuObjectPath = settings.value(profile + "/ObjectPath").toString();
45
48
46 QVariantMap properties;
49 QVariantMap properties;
47 properties.clear();
50 properties.clear();
48 properties.insert("busType", 1);
51 properties.insert("busType", 1);
49 properties.insert("busName", busName);
52 properties.insert("busName", busName);
50 properties.insert("actionsObjectPath", actionObjectPath);
53 properties.insert("actionsObjectPath", actionObjectPath);
51− properties.insert("menuObjectPath", menuObjectPath);
52 setIndicatorProperties(properties);
54 setIndicatorProperties(properties);
53}
55}
54
56
@@ -78,6 +80,19 @@ void Indicator::setPosition(int position)
78 }
80 }
79}
81}
80
82
83+void Indicator::setProfile(const QString& profile)
84+{
85+ QVariant pos = m_settings.value(profile + "/Position");
86+ if (!pos.isValid())
87+ pos = m_settings.value("Indicator Service/Position", QVariant::fromValue(0));
88+ setPosition(pos.toInt());
89+
90+ QString menuObjectPath = m_settings.value(profile + "/ObjectPath").toString();
91+ QVariantMap map = m_properties.toMap();
92+ map.insert("menuObjectPath", menuObjectPath);
93+ setIndicatorProperties(map);
94+}
95+
81QVariant Indicator::indicatorProperties() const
96QVariant Indicator::indicatorProperties() const
82{
97{
83 return m_properties;
98 return m_properties;
plugins/Unity/Indicators/indicator.h+5−1
@@ -35,12 +35,15 @@ public:
35 Indicator(QObject *parent = 0);
35 Indicator(QObject *parent = 0);
36 virtual ~Indicator();
36 virtual ~Indicator();
37
37
38− void init(const QString &profile, const QString& busName, const QSettings& settings);
38+ void init(const QString& busName, const QSettings& settings);
39
39
40 QString identifier() const;
40 QString identifier() const;
41 int position() const;
41 int position() const;
42 QVariant indicatorProperties() const;
42 QVariant indicatorProperties() const;
43
43
44+public Q_SLOTS:
45+ void setProfile(const QString& profile);
46+
44Q_SIGNALS:
47Q_SIGNALS:
45 void identifierChanged(const QString &identifier);
48 void identifierChanged(const QString &identifier);
46 void positionChanged(int position);
49 void positionChanged(int position);
@@ -55,6 +58,7 @@ private:
55 QString m_identifier;
58 QString m_identifier;
56 int m_position;
59 int m_position;
57 QVariant m_properties;
60 QVariant m_properties;
61+ QVariantMap m_settings;
58};
62};
59
63
60#endif // INDICATOR_H
64#endif // INDICATOR_H
plugins/Unity/Indicators/indicatorsmanager.cpp+17−3
@@ -54,10 +54,9 @@ IndicatorsManager::~IndicatorsManager()
54
54
55}
55}
56
56
57−void IndicatorsManager::load(const QString& profile)
57+void IndicatorsManager::load()
58{
58{
59 unload();
59 unload();
60− m_profile = profile;
61
60
62 QStringList xdgLocations = shellDataDirs();//QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
61 QStringList xdgLocations = shellDataDirs();//QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
63
62
@@ -216,6 +215,19 @@ void IndicatorsManager::setLoaded(bool loaded)
216 }
215 }
217}
216}
218
217
218+QString IndicatorsManager::profile() const
219+{
220+ return m_profile;
221+}
222+
223+void IndicatorsManager::setProfile(const QString& profile)
224+{
225+ if (m_profile != profile) {
226+ m_profile = profile;
227+ Q_EMIT profileChanged(m_profile);
228+ }
229+}
230+
219void IndicatorsManager::startVerify(const QString& path)
231void IndicatorsManager::startVerify(const QString& path)
220{
232{
221 QHashIterator<QString, IndicatorData*> iter(m_indicatorsData);
233 QHashIterator<QString, IndicatorData*> iter(m_indicatorsData);
@@ -268,7 +280,9 @@ Indicator::Ptr IndicatorsManager::indicator(const QString& indicator_name)
268 Indicator::Ptr new_indicator(new Indicator(this));
280 Indicator::Ptr new_indicator(new Indicator(this));
269 data->m_indicator = new_indicator;
281 data->m_indicator = new_indicator;
270 QSettings settings(data->m_fileInfo.absoluteFilePath(), QSettings::IniFormat, this);
282 QSettings settings(data->m_fileInfo.absoluteFilePath(), QSettings::IniFormat, this);
271− new_indicator->init(m_profile, data->m_fileInfo.fileName(), settings);
283+ new_indicator->init(data->m_fileInfo.fileName(), settings);
284+ new_indicator->setProfile(m_profile);
285+ QObject::connect(this, SIGNAL(profileChanged(const QString&)), new_indicator.data(), SLOT(setProfile(const QString&)));
272 return new_indicator;
286 return new_indicator;
273}
287}
274
288
plugins/Unity/Indicators/indicatorsmanager.h+6−1
@@ -32,13 +32,17 @@ class UNITYINDICATORS_EXPORT IndicatorsManager : public QObject
32{
32{
33 Q_OBJECT
33 Q_OBJECT
34 Q_PROPERTY(bool loaded READ isLoaded NOTIFY loadedChanged)
34 Q_PROPERTY(bool loaded READ isLoaded NOTIFY loadedChanged)
35+ Q_PROPERTY(QString profile READ profile WRITE setProfile NOTIFY profileChanged)
35public:
36public:
36 explicit IndicatorsManager(QObject* parent = 0);
37 explicit IndicatorsManager(QObject* parent = 0);
37 ~IndicatorsManager();
38 ~IndicatorsManager();
38
39
39− Q_INVOKABLE void load(const QString& profile);
40+ Q_INVOKABLE void load();
40 Q_INVOKABLE void unload();
41 Q_INVOKABLE void unload();
41
42
43+ QString profile() const;
44+ void setProfile(const QString& profile);
45+
42 Indicator::Ptr indicator(const QString& indicator_name);
46 Indicator::Ptr indicator(const QString& indicator_name);
43
47
44 QList<Indicator::Ptr> indicators();
48 QList<Indicator::Ptr> indicators();
@@ -47,6 +51,7 @@ public:
47
51
48Q_SIGNALS:
52Q_SIGNALS:
49 void loadedChanged(bool);
53 void loadedChanged(bool);
54+ void profileChanged(const QString&);
50
55
51 void indicatorLoaded(const QString& indicator_name);
56 void indicatorLoaded(const QString& indicator_name);
52 void indicatorAboutToBeUnloaded(const QString& indicator_name);
57 void indicatorAboutToBeUnloaded(const QString& indicator_name);
plugins/Unity/Indicators/indicatorsmodel.cpp+25−2
@@ -56,6 +56,7 @@ IndicatorsModel::IndicatorsModel(QObject *parent)
56 m_manager = new IndicatorsManager(this);
56 m_manager = new IndicatorsManager(this);
57 QObject::connect(m_manager, SIGNAL(indicatorLoaded(const QString&)), this, SLOT(onIndicatorLoaded(const QString&)));
57 QObject::connect(m_manager, SIGNAL(indicatorLoaded(const QString&)), this, SLOT(onIndicatorLoaded(const QString&)));
58 QObject::connect(m_manager, SIGNAL(indicatorAboutToBeUnloaded(const QString&)), this, SLOT(onIndicatorAboutToBeUnloaded(const QString&)));
58 QObject::connect(m_manager, SIGNAL(indicatorAboutToBeUnloaded(const QString&)), this, SLOT(onIndicatorAboutToBeUnloaded(const QString&)));
59+ QObject::connect(m_manager, SIGNAL(profileChanged(const QString&)), this, SIGNAL(profileChanged()));
59
60
60 QObject::connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
61 QObject::connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
61 QObject::connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
62 QObject::connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
@@ -80,15 +81,37 @@ int IndicatorsModel::count() const
80 return rowCount();
81 return rowCount();
81}
82}
82
83
84+/*!
85+ \qmlproperty IndicatorsModel::profile
86+ The indicator profile to use for the model.
87+
88+ \b Note: methods should only be called after the Component has completed.
89+*/
90+QString IndicatorsModel::profile() const
91+{
92+ return m_manager->profile();
93+}
94+
95+/*!
96+ \qmlproperty IndicatorsModel::setProfile
97+ Set the indicator profile to use for the model.
98+
99+ \b Note: methods should only be called after the Component has completed.
100+*/
101+void IndicatorsModel::setProfile(const QString &profile)
102+{
103+ m_manager->setProfile(profile);
104+}
105+
83/*!
106/*!
84 \qmlmethod IndicatorsModel::unload()
107 \qmlmethod IndicatorsModel::unload()
85
108
86 Load all indicators.
109 Load all indicators.
87*/
110*/
88−void IndicatorsModel::load(const QString& profile)
111+void IndicatorsModel::load()
89{
112{
90 m_indicators.clear();
113 m_indicators.clear();
91− m_manager->load(profile);
114+ m_manager->load();
92}
115}
93
116
94/*!
117/*!
plugins/Unity/Indicators/indicatorsmodel.h+6−1
@@ -34,17 +34,21 @@ class UNITYINDICATORS_EXPORT IndicatorsModel : public QAbstractListModel
34 Q_OBJECT
34 Q_OBJECT
35 Q_ENUMS(Roles)
35 Q_ENUMS(Roles)
36 Q_PROPERTY(int count READ count NOTIFY countChanged)
36 Q_PROPERTY(int count READ count NOTIFY countChanged)
37+ Q_PROPERTY(QString profile READ profile WRITE setProfile NOTIFY profileChanged)
37
38
38public:
39public:
39
40
40 IndicatorsModel(QObject *parent=0);
41 IndicatorsModel(QObject *parent=0);
41 ~IndicatorsModel();
42 ~IndicatorsModel();
42
43
43− Q_INVOKABLE void load(const QString& profile);
44+ Q_INVOKABLE void load();
44 Q_INVOKABLE void unload();
45 Q_INVOKABLE void unload();
45
46
46 Q_INVOKABLE QVariant data(int row, int role) const;
47 Q_INVOKABLE QVariant data(int row, int role) const;
47
48
49+ QString profile() const;
50+ void setProfile(const QString& profile);
51+
48 /* QAbstractItemModel */
52 /* QAbstractItemModel */
49 QHash<int, QByteArray> roleNames() const;
53 QHash<int, QByteArray> roleNames() const;
50 int columnCount(const QModelIndex &parent = QModelIndex()) const;
54 int columnCount(const QModelIndex &parent = QModelIndex()) const;
@@ -55,6 +59,7 @@ public:
55
59
56Q_SIGNALS:
60Q_SIGNALS:
57 void countChanged();
61 void countChanged();
62+ void profileChanged();
58 void indicatorDataChanged(const QVariant& data);
63 void indicatorDataChanged(const QVariant& data);
59
64
60private Q_SLOTS:
65private Q_SLOTS:
plugins/Unity/Indicators/modelactionrootstate.cpp+4−2
@@ -71,7 +71,7 @@ void ModelActionRootState::setMenu(UnityMenuModel* menu)
71
71
72bool ModelActionRootState::valid() const
72bool ModelActionRootState::valid() const
73{
73{
74− return m_menu && m_menu->rowCount() > 0;
74+ return !currentState().empty();
75}
75}
76
76
77void ModelActionRootState::onModelRowsAdded(const QModelIndex& parent, int start, int end)
77void ModelActionRootState::onModelRowsAdded(const QModelIndex& parent, int start, int end)
@@ -121,7 +121,9 @@ void ModelActionRootState::updateActionState()
121 m_menu->setActionStateParser(oldParser);
121 m_menu->setActionStateParser(oldParser);
122
122
123 setCurrentState(state);
123 setCurrentState(state);
124− } else {
124+ } else if (!m_menu) {
125 setCurrentState(QVariantMap());
125 setCurrentState(QVariantMap());
126 }
126 }
127+ // else if m_menu->rowCount() == 0, let's leave existing cache in place
128+ // until the new menu comes in, to avoid flashing the UI empty for a moment
127}
129}
plugins/Unity/Indicators/unitymenumodelcache.cpp+8−11
@@ -46,17 +46,14 @@ QSharedPointer<UnityMenuModel> UnityMenuModelCache::model(const QByteArray& path
46 QQmlEngine::setObjectOwnership(model, QQmlEngine::CppOwnership);
46 QQmlEngine::setObjectOwnership(model, QQmlEngine::CppOwnership);
47
47
48 QSharedPointer<UnityMenuModel> menuModel(model);
48 QSharedPointer<UnityMenuModel> menuModel(model);
49− connect(model, &QObject::destroyed, this, [this] {
49+
50− QMutableHashIterator<QByteArray, QWeakPointer<UnityMenuModel>> iter(m_registry);
50+ // Keep a shared pointer (rather than weak pointer which would cause the
51− while(iter.hasNext()) {
51+ // model to be deleted when all shared pointers we give out are deleted).
52− auto keyVal = iter.next();
52+ // We want to keep all models cached because when we switch indicator
53− if (keyVal.value().isNull()) {
53+ // profiles, we will be switching paths often. And we want to keep the
54− iter.remove();
54+ // old model around, ready to be used. Otherwise the UI might momentarily
55− break;
55+ // wait as we populate the model from DBus yet again.
56− }
57− }
58− });
59− m_registry[path] = menuModel.toWeakRef();
56+ m_registry[path] = menuModel;
60
57
61 menuModel->setMenuObjectPath(path);
58 menuModel->setMenuObjectPath(path);
62 return menuModel;
59 return menuModel;
plugins/Unity/Indicators/unitymenumodelcache.h+2−2
@@ -25,7 +25,7 @@
25#include <QObject>
25#include <QObject>
26#include <QHash>
26#include <QHash>
27#include <QPointer>
27#include <QPointer>
28−#include <QWeakPointer>
28+#include <QSharedPointer>
29
29
30class UnityMenuModel;
30class UnityMenuModel;
31
31
@@ -43,7 +43,7 @@ public:
43 Q_INVOKABLE virtual bool contains(const QByteArray& path);
43 Q_INVOKABLE virtual bool contains(const QByteArray& path);
44
44
45protected:
45protected:
46− QHash<QByteArray, QWeakPointer<UnityMenuModel>> m_registry;
46+ QHash<QByteArray, QSharedPointer<UnityMenuModel>> m_registry;
47 static QPointer<UnityMenuModelCache> theCache;
47 static QPointer<UnityMenuModelCache> theCache;
48};
48};
49
49
plugins/Unity/Launcher/CMakeLists.txt+2−2
@@ -1,5 +1,4 @@
1−include(FindPkgConfig)
2−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=5)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
3pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
2pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
4pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
3pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
5
4
@@ -21,6 +20,7 @@ set(QMLLAUNCHERPLUGIN_SRC
21 dbusinterface.cpp
20 dbusinterface.cpp
22 gsettings.cpp
21 gsettings.cpp
23 desktopfilehandler.cpp
22 desktopfilehandler.cpp
23+ asadapter.cpp
24 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
24 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
25 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
25 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
26 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
26 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
plugins/Unity/Launcher/asadapter.cppnew file+60
@@ -0,0 +1,60 @@
1+/*
2+ * Copyright 2014-2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "asadapter.h"
18+#include "launcheritem.h"
19+#include "AccountsServiceDBusAdaptor.h"
20+
21+#include <QDebug>
22+
23+ASAdapter::ASAdapter()
24+{
25+ m_accounts = new AccountsServiceDBusAdaptor();
26+ m_user = qgetenv("USER");
27+ if (m_user.isEmpty()) {
28+ qWarning() << "$USER not valid. Account Service integration will not work.";
29+ }
30+}
31+
32+ASAdapter::~ASAdapter()
33+{
34+ m_accounts->deleteLater();
35+}
36+
37+void ASAdapter::syncItems(QList<LauncherItem *> m_list)
38+{
39+ if (m_accounts && !m_user.isEmpty()) {
40+ QList<QVariantMap> items;
41+
42+ Q_FOREACH(LauncherItem *item, m_list) {
43+ items << itemToVariant(item);
44+ }
45+
46+ m_accounts->setUserPropertyAsync(m_user, "com.canonical.unity.AccountsService", "LauncherItems", QVariant::fromValue(items));
47+ }
48+}
49+
50+QVariantMap ASAdapter::itemToVariant(LauncherItem *item) const
51+{
52+ QVariantMap details;
53+ details.insert("id", item->appId());
54+ details.insert("name", item->name());
55+ details.insert("icon", item->icon());
56+ details.insert("count", item->count());
57+ details.insert("countVisible", item->countVisible());
58+ details.insert("pinned", item->pinned());
59+ return details;
60+}
tests/mocks/Unity/Notifications/MockNotificationTypes.h → plugins/Unity/Launcher/asadapter.hrenamed+24−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
@@ -12,25 +12,35 @@
12 *
12 *
13 * You should have received a copy of the GNU Lesser General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors:
17− * Mirco Mueller <mirco.mueller@canonical.com>
18 */
15 */
19
16
20−#ifndef MOCK_NOTIFICATION_TYPES_H
17+#ifndef ASADAPTER_H
21−#define MOCK_NOTIFICATION_TYPES_H
18+#define ASADAPTER_H
22
19
23−#include <QObject>
20+#include <QVariantMap>
24
21
25−class MockNotification : public QObject {
22+class LauncherItem;
26− Q_OBJECT
23+class AccountsServiceDBusAdaptor;
27− Q_ENUMS(Type)
24+class QDBusInterface;
28
25
26+class ASAdapter
27+{
29public:
28public:
30− MockNotification(QObject *parent=nullptr);
29+ ASAdapter();
31− virtual ~MockNotification();
30+ ~ASAdapter();
31+
32+ void syncItems(QList<LauncherItem*> m_list);
33+
34+private:
35+ QVariantMap itemToVariant(LauncherItem *item) const;
36+
37+private:
38+ AccountsServiceDBusAdaptor *m_accounts;
39+ QString m_user;
40+
41+ QDBusInterface *m_userInterface;
32
42
33− enum Type { PlaceHolder, Confirmation, Ephemeral, Interactive, SnapDecision };
43+ friend class LauncherModelTest;
34};
44};
35
45
36−#endif // MOCK_NOTIFICATION_TYPES_H
46+#endif
plugins/Unity/Launcher/launchermodel.cpp+20
@@ -22,6 +22,7 @@
22#include "gsettings.h"
22#include "gsettings.h"
23#include "desktopfilehandler.h"
23#include "desktopfilehandler.h"
24#include "dbusinterface.h"
24#include "dbusinterface.h"
25+#include "asadapter.h"
25
26
26#include <unity/shell/application/ApplicationInfoInterface.h>
27#include <unity/shell/application/ApplicationInfoInterface.h>
27
28
@@ -34,6 +35,7 @@ LauncherModel::LauncherModel(QObject *parent):
34 LauncherModelInterface(parent),
35 LauncherModelInterface(parent),
35 m_settings(new GSettings(this)),
36 m_settings(new GSettings(this)),
36 m_dbusIface(new DBusInterface(this)),
37 m_dbusIface(new DBusInterface(this)),
38+ m_asAdapter(new ASAdapter()),
37 m_appManager(0)
39 m_appManager(0)
38{
40{
39 connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged);
41 connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged);
@@ -50,6 +52,8 @@ LauncherModel::~LauncherModel()
50 while (!m_list.empty()) {
52 while (!m_list.empty()) {
51 m_list.takeFirst()->deleteLater();
53 m_list.takeFirst()->deleteLater();
52 }
54 }
55+
56+ delete m_asAdapter;
53}
57}
54
58
55int LauncherModel::rowCount(const QModelIndex &parent) const
59int LauncherModel::rowCount(const QModelIndex &parent) const
@@ -261,6 +265,15 @@ void LauncherModel::setApplicationManager(unity::shell::application::Application
261 }
265 }
262}
266}
263
267
268+bool LauncherModel::onlyPinned() const
269+{
270+ return false;
271+}
272+
273+void LauncherModel::setOnlyPinned(bool onlyPinned) {
274+ Q_UNUSED(onlyPinned);
275+ qWarning() << "This launcher implementation does not support showing only pinned apps";
276+}
264
277
265void LauncherModel::storeAppList()
278void LauncherModel::storeAppList()
266{
279{
@@ -271,6 +284,7 @@ void LauncherModel::storeAppList()
271 }
284 }
272 }
285 }
273 m_settings->setStoredApplications(appIds);
286 m_settings->setStoredApplications(appIds);
287+ m_asAdapter->syncItems(m_list);
274}
288}
275
289
276void LauncherModel::unpin(const QString &appId)
290void LauncherModel::unpin(const QString &appId)
@@ -321,6 +335,7 @@ void LauncherModel::countChanged(const QString &appId, int count)
321 LauncherItem *item = m_list.at(idx);
335 LauncherItem *item = m_list.at(idx);
322 item->setCount(count);
336 item->setCount(count);
323 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
337 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
338+ m_asAdapter->syncItems(m_list);
324 }
339 }
325}
340}
326
341
@@ -352,6 +367,7 @@ void LauncherModel::countVisibleChanged(const QString &appId, int countVisible)
352 Q_EMIT hint();
367 Q_EMIT hint();
353 }
368 }
354 }
369 }
370+ m_asAdapter->syncItems(m_list);
355}
371}
356
372
357void LauncherModel::refresh()
373void LauncherModel::refresh()
@@ -433,6 +449,8 @@ void LauncherModel::refresh()
433 if (changed) {
449 if (changed) {
434 Q_EMIT hint();
450 Q_EMIT hint();
435 }
451 }
452+
453+ m_asAdapter->syncItems(m_list);
436}
454}
437
455
438void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
456void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
@@ -467,6 +485,7 @@ void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
467 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
485 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
468 m_list.append(item);
486 m_list.append(item);
469 endInsertRows();
487 endInsertRows();
488+ m_asAdapter->syncItems(m_list);
470 }
489 }
471}
490}
472
491
@@ -486,6 +505,7 @@ void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
486 beginRemoveRows(QModelIndex(), appIndex, appIndex);
505 beginRemoveRows(QModelIndex(), appIndex, appIndex);
487 m_list.takeAt(appIndex)->deleteLater();
506 m_list.takeAt(appIndex)->deleteLater();
488 endRemoveRows();
507 endRemoveRows();
508+ m_asAdapter->syncItems(m_list);
489 }
509 }
490}
510}
491
511
plugins/Unity/Launcher/launchermodel.h+5
@@ -28,6 +28,7 @@
28class LauncherItem;
28class LauncherItem;
29class GSettings;
29class GSettings;
30class DBusInterface;
30class DBusInterface;
31+class ASAdapter;
31
32
32using namespace unity::shell::launcher;
33using namespace unity::shell::launcher;
33using namespace unity::shell::application;
34using namespace unity::shell::application;
@@ -54,6 +55,9 @@ public:
54 unity::shell::application::ApplicationManagerInterface* applicationManager() const;
55 unity::shell::application::ApplicationManagerInterface* applicationManager() const;
55 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
56 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
56
57
58+ bool onlyPinned() const override;
59+ void setOnlyPinned(bool onlyPinned) override;
60+
57 int findApplication(const QString &appId);
61 int findApplication(const QString &appId);
58
62
59public Q_SLOTS:
63public Q_SLOTS:
@@ -79,6 +83,7 @@ private:
79
83
80 GSettings *m_settings;
84 GSettings *m_settings;
81 DBusInterface *m_dbusIface;
85 DBusInterface *m_dbusIface;
86+ ASAdapter *m_asAdapter;
82
87
83 ApplicationManagerInterface *m_appManager;
88 ApplicationManagerInterface *m_appManager;
84
89
plugins/Unity/Session/CMakeLists.txt−2
@@ -1,5 +1,3 @@
1−include(FindPkgConfig)
2−
3pkg_search_module(GIO REQUIRED gio-2.0)
1pkg_search_module(GIO REQUIRED gio-2.0)
4
2
5include_directories(
3include_directories(
plugins/Utils/CMakeLists.txt+2−4
@@ -1,6 +1,3 @@
1−# Dependencies
2−include(FindPkgConfig)
3−
4pkg_search_module(GIO REQUIRED gio-2.0)
1pkg_search_module(GIO REQUIRED gio-2.0)
5
2
6include_directories(
3include_directories(
@@ -18,6 +15,7 @@ set(QMLPLUGIN_SRC
18 unitymenumodelpaths.cpp
15 unitymenumodelpaths.cpp
19 windowkeysfilter.cpp
16 windowkeysfilter.cpp
20 easingcurve.cpp
17 easingcurve.cpp
18+ windowstatestorage.cpp
21 plugin.cpp
19 plugin.cpp
22 )
20 )
23
21
@@ -33,6 +31,6 @@ target_link_libraries(Utils-qml ${GIO_LDFLAGS})
33# files directly in targets.
31# files directly in targets.
34set_target_properties(Utils-qml PROPERTIES COMPILE_FLAGS -fvisibility=default)
32set_target_properties(Utils-qml PROPERTIES COMPILE_FLAGS -fvisibility=default)
35
33
36−qt5_use_modules(Utils-qml Qml Quick DBus Network Gui)
34+qt5_use_modules(Utils-qml Qml Quick DBus Network Gui Sql Concurrent)
37
35
38add_unity8_plugin(Utils 0.1 Utils TARGETS Utils-qml)
36add_unity8_plugin(Utils 0.1 Utils TARGETS Utils-qml)
plugins/Utils/plugin.cpp+9
@@ -33,6 +33,14 @@
33#include "unitymenumodelpaths.h"
33#include "unitymenumodelpaths.h"
34#include "windowkeysfilter.h"
34#include "windowkeysfilter.h"
35#include "easingcurve.h"
35#include "easingcurve.h"
36+#include "windowstatestorage.h"
37+
38+static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
39+{
40+ Q_UNUSED(engine)
41+ Q_UNUSED(scriptEngine)
42+ return new WindowStateStorage();
43+}
36
44
37void UtilsPlugin::registerTypes(const char *uri)
45void UtilsPlugin::registerTypes(const char *uri)
38{
46{
@@ -46,6 +54,7 @@ void UtilsPlugin::registerTypes(const char *uri)
46 qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
54 qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
47 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
55 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
48 qmlRegisterType<RelativeTimeFormatter>(uri, 0, 1, "RelativeTimeFormatter");
56 qmlRegisterType<RelativeTimeFormatter>(uri, 0, 1, "RelativeTimeFormatter");
57+ qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
49}
58}
50
59
51void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
60void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
plugins/Utils/windowstatestorage.cppnew file+96
@@ -0,0 +1,96 @@
1+/*
2+ * Copyright 2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "windowstatestorage.h"
18+
19+#include <QtConcurrent>
20+#include <QDebug>
21+#include <QSqlQuery>
22+#include <QSqlError>
23+#include <QSqlResult>
24+#include <QRect>
25+
26+QMutex WindowStateStorage::s_mutex;
27+
28+WindowStateStorage::WindowStateStorage(QObject *parent):
29+ QObject(parent)
30+{
31+ QString dbPath = QDir::homePath() + "/.cache/unity8/";
32+ m_db = QSqlDatabase::addDatabase("QSQLITE");
33+ QDir dir;
34+ dir.mkpath(dbPath);
35+ m_db.setDatabaseName(dbPath + "windowstatestorage.sqlite");
36+ initdb();
37+}
38+
39+void WindowStateStorage::saveGeometry(const QString &windowId, const QRect &rect)
40+{
41+ QString queryString = QString("INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values ('%1', '%2', '%3', '%4', '%5');")
42+ .arg(windowId)
43+ .arg(rect.x())
44+ .arg(rect.y())
45+ .arg(rect.width())
46+ .arg(rect.height());
47+
48+ QtConcurrent::run(executeAsyncQuery, queryString);
49+}
50+
51+void WindowStateStorage::executeAsyncQuery(const QString &queryString)
52+{
53+ QMutexLocker l(&s_mutex);
54+ QSqlQuery query;
55+
56+ bool ok = query.exec(queryString);
57+ if (!ok) {
58+ qWarning() << "Error executing query" << queryString
59+ << "Driver error:" << query.lastError().driverText()
60+ << "Database error:" << query.lastError().databaseText();
61+ }
62+}
63+
64+QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue)
65+{
66+ QMutexLocker l(&s_mutex);
67+ QString queryString = QString("SELECT * FROM geometry WHERE windowId = '%1';")
68+ .arg(windowId);
69+ QSqlQuery query;
70+
71+ bool ok = query.exec(queryString);
72+ if (!ok) {
73+ qWarning() << "Error retrieving window state for" << windowId
74+ << "Driver error:" << query.lastError().driverText()
75+ << "Database error:" << query.lastError().databaseText();
76+ return defaultValue;
77+ }
78+ if (!query.first()) {
79+ return defaultValue;
80+ }
81+ return QRect(query.value("x").toInt(), query.value("y").toInt(), query.value("width").toInt(), query.value("height").toInt());
82+}
83+
84+void WindowStateStorage::initdb()
85+{
86+ m_db.open();
87+ if (!m_db.open()) {
88+ qWarning() << "Error opening state database:" << m_db.lastError().driverText() << m_db.lastError().databaseText();
89+ return;
90+ }
91+
92+ if (!m_db.tables().contains("geometry")) {
93+ QSqlQuery query;
94+ query.exec("CREATE TABLE geometry(windowId TEXT UNIQUE, x INTEGER, y INTEGER, width INTEGER, height INTEGER);");
95+ }
96+}
plugins/Utils/windowstatestorage.hnew file+38
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright 2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <QObject>
18+#include <QSqlDatabase>
19+#include <QMutex>
20+
21+class WindowStateStorage: public QObject
22+{
23+ Q_OBJECT
24+public:
25+ WindowStateStorage(QObject *parent = 0);
26+
27+ Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
28+ Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue);
29+
30+private:
31+ void initdb();
32+
33+ static void executeAsyncQuery(const QString &queryString);
34+ static QMutex s_mutex;
35+
36+ // NB: This is accessed from threads. Make sure to mutex it.
37+ QSqlDatabase m_db;
38+};
po/am.po+1−1
@@ -15,7 +15,7 @@ msgstr ""
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-01-29 06:00+0000\n"
18+"X-Launchpad-Export-Date: 2015-01-30 07:08+0000\n"
19"X-Generator: Launchpad (build 17306)\n"
19"X-Generator: Launchpad (build 17306)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:130
21#: plugins/LightDM/Greeter.cpp:130
po/ast.po+51−44
@@ -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-01-22 11:09+0100\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2014-11-08 18:24+0000\n"
11+"PO-Revision-Date: 2015-02-13 17:17+0000\n"
12"Last-Translator: ivarela <ivarela@ubuntu.com>\n"
12"Last-Translator: ivarela <ivarela@ubuntu.com>\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-01-28 06:29+0000\n"
18+"X-Launchpad-Export-Date: 2015-02-14 06:34+0000\n"
19−"X-Generator: Launchpad (build 17306)\n"
19+"X-Generator: Launchpad (build 17341)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:130
21#: plugins/LightDM/Greeter.cpp:130
22msgid "Password: "
22msgid "Password: "
@@ -230,15 +230,15 @@ msgstr "Soltar p'anovar..."
230
230
231#: qml/Dash/ScopesList.qml:66
231#: qml/Dash/ScopesList.qml:66
232msgid "Manage"
232msgid "Manage"
233−msgstr ""
233+msgstr "Alministrar"
234
234
235#: qml/Dash/ScopesList.qml:112
235#: qml/Dash/ScopesList.qml:112
236msgid "Home"
236msgid "Home"
237−msgstr ""
237+msgstr "Aniciu"
238
238
239#: qml/Dash/ScopesList.qml:113
239#: qml/Dash/ScopesList.qml:113
240msgid "Also installed"
240msgid "Also installed"
241−msgstr ""
241+msgstr "Tamién instalaos"
242
242
243#: qml/Greeter/Greeter.qml:133
243#: qml/Greeter/Greeter.qml:133
244msgid "Unlock"
244msgid "Unlock"
@@ -286,7 +286,7 @@ msgstr "Falló, toca pa reintentar"
286
286
287#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
287#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
288msgid "Reply"
288msgid "Reply"
289−msgstr ""
289+msgstr "Responder"
290
290
291#: qml/Panel/Indicators/ModemInfoItem.qml:105
291#: qml/Panel/Indicators/ModemInfoItem.qml:105
292msgid "Unlock SIM"
292msgid "Unlock SIM"
@@ -356,98 +356,99 @@ msgstr ""
356
356
357#: qml/Wizard/Page.qml:89
357#: qml/Wizard/Page.qml:89
358msgid "Back"
358msgid "Back"
359−msgstr ""
359+msgstr "Atrás"
360
360
361#: qml/Wizard/Pages/10-welcome.qml:27
361#: qml/Wizard/Pages/10-welcome.qml:27
362msgid "Hi!"
362msgid "Hi!"
363−msgstr ""
363+msgstr "Hola!"
364
364
365#: qml/Wizard/Pages/10-welcome.qml:44
365#: qml/Wizard/Pages/10-welcome.qml:44
366msgid "Welcome to your Ubuntu phone."
366msgid "Welcome to your Ubuntu phone."
367−msgstr ""
367+msgstr "Dámoste l'acoyida a Ubuntu Phone"
368
368
369#: qml/Wizard/Pages/10-welcome.qml:52
369#: qml/Wizard/Pages/10-welcome.qml:52
370msgid "Let’s get started."
370msgid "Let’s get started."
371−msgstr ""
371+msgstr "Entamamos."
372
372
373#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
373#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
374#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
374#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
375#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
375#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
376#: qml/Wizard/Pages/passwd-set.qml:90
376#: qml/Wizard/Pages/passwd-set.qml:90
377msgid "Continue"
377msgid "Continue"
378−msgstr ""
378+msgstr "Siguir"
379
379
380#: qml/Wizard/Pages/20-sim.qml:25
380#: qml/Wizard/Pages/20-sim.qml:25
381msgid "Add a SIM card and restart your device"
381msgid "Add a SIM card and restart your device"
382−msgstr ""
382+msgstr "Amiesta una tarxeta SIM y reanicia'l preséu"
383
383
384#: qml/Wizard/Pages/20-sim.qml:55
384#: qml/Wizard/Pages/20-sim.qml:55
385msgid "Without it, you won’t be able to make calls or use text messaging."
385msgid "Without it, you won’t be able to make calls or use text messaging."
386−msgstr ""
386+msgstr "Ensin ella, nun vas poder facer llamaes nin unviar mensaxes."
387
387
388#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
388#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
389msgid "Skip"
389msgid "Skip"
390−msgstr ""
390+msgstr "Omitir"
391
391
392#: qml/Wizard/Pages/30-passwd-type.qml:39
392#: qml/Wizard/Pages/30-passwd-type.qml:39
393msgid "Lock security"
393msgid "Lock security"
394−msgstr ""
394+msgstr "Seguridá de bloquéu"
395
395
396#: qml/Wizard/Pages/30-passwd-type.qml:74
396#: qml/Wizard/Pages/30-passwd-type.qml:74
397msgid "Please select how you’d like to unlock your phone."
397msgid "Please select how you’d like to unlock your phone."
398−msgstr ""
398+msgstr "Seleiciona cómo te prestaría desbloquiar el teléfonu."
399
399
400#: qml/Wizard/Pages/30-passwd-type.qml:97
400#: qml/Wizard/Pages/30-passwd-type.qml:97
401msgid "Swipe"
401msgid "Swipe"
402−msgstr ""
402+msgstr "Eslizar"
403
403
404#: qml/Wizard/Pages/30-passwd-type.qml:98
404#: qml/Wizard/Pages/30-passwd-type.qml:98
405msgid "No security"
405msgid "No security"
406−msgstr ""
406+msgstr "Ensin seguridá"
407
407
408#: qml/Wizard/Pages/30-passwd-type.qml:100
408#: qml/Wizard/Pages/30-passwd-type.qml:100
409msgid "Passcode"
409msgid "Passcode"
410−msgstr ""
410+msgstr "Códigu de pasu"
411
411
412#: qml/Wizard/Pages/30-passwd-type.qml:101
412#: qml/Wizard/Pages/30-passwd-type.qml:101
413msgid "4 digits only"
413msgid "4 digits only"
414−msgstr ""
414+msgstr "namái 4 díxitos"
415
415
416#: qml/Wizard/Pages/30-passwd-type.qml:103
416#: qml/Wizard/Pages/30-passwd-type.qml:103
417msgid "Passphrase"
417msgid "Passphrase"
418−msgstr ""
418+msgstr "Fras de pasu"
419
419
420#: qml/Wizard/Pages/30-passwd-type.qml:104
420#: qml/Wizard/Pages/30-passwd-type.qml:104
421msgid "Numbers and letters"
421msgid "Numbers and letters"
422−msgstr ""
422+msgstr "Númberos y lletres"
423
423
424#: qml/Wizard/Pages/40-wifi.qml:28
424#: qml/Wizard/Pages/40-wifi.qml:28
425msgid "Connect to Wi‑Fi"
425msgid "Connect to Wi‑Fi"
426−msgstr ""
426+msgstr "Coneutar a WiFi"
427
427
428#: qml/Wizard/Pages/40-wifi.qml:147
428#: qml/Wizard/Pages/40-wifi.qml:147
429msgid "Available networks…"
429msgid "Available networks…"
430−msgstr ""
430+msgstr "Redes disponibles..."
431
431
432#: qml/Wizard/Pages/40-wifi.qml:148
432#: qml/Wizard/Pages/40-wifi.qml:148
433msgid "No available networks."
433msgid "No available networks."
434−msgstr ""
434+msgstr "Nun hai redes disponibles"
435
435
436#: qml/Wizard/Pages/50-location.qml:27
436#: qml/Wizard/Pages/50-location.qml:27
437msgid "Location"
437msgid "Location"
438−msgstr ""
438+msgstr "Llocalización"
439
439
440#: qml/Wizard/Pages/50-location.qml:62
440#: qml/Wizard/Pages/50-location.qml:62
441msgid "Let the phone detect your location:"
441msgid "Let the phone detect your location:"
442−msgstr ""
442+msgstr "Permitir al teléfonu deteutar la llocalización:"
443
443
444#: qml/Wizard/Pages/50-location.qml:69
444#: qml/Wizard/Pages/50-location.qml:69
445msgid "Using GPS only (less accurate)"
445msgid "Using GPS only (less accurate)"
446−msgstr ""
446+msgstr "Namái usando GPS (menos precisión)"
447
447
448#: qml/Wizard/Pages/50-location.qml:86
448#: qml/Wizard/Pages/50-location.qml:86
449msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
449msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
450msgstr ""
450msgstr ""
451+"Usando GPS, Wifi anónimes ya información de redes móviles (recomiéndase)"
451
452
452#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
453#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
453#: qml/Wizard/Pages/50-location.qml:103
454#: qml/Wizard/Pages/50-location.qml:103
@@ -455,81 +456,87 @@ msgid ""
455"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
456"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
456"conditions</a>."
457"conditions</a>."
457msgstr ""
458msgstr ""
459+"Seleicionando esta opción, aceutes los <a href='#'>términos y "
460+"condiciones</a> de Nokia HERE."
458
461
459#: qml/Wizard/Pages/50-location.qml:112
462#: qml/Wizard/Pages/50-location.qml:112
460msgid "Not at all"
463msgid "Not at all"
461−msgstr ""
464+msgstr "De nengún mou"
462
465
463#: qml/Wizard/Pages/50-location.qml:124
466#: qml/Wizard/Pages/50-location.qml:124
464msgid "You can change your mind later in <b>System Settings</b>."
467msgid "You can change your mind later in <b>System Settings</b>."
465−msgstr ""
468+msgstr "Pues cambialo dempués n'<b>Axustes del sistema</b>."
466
469
467#: qml/Wizard/Pages/60-reporting.qml:24
470#: qml/Wizard/Pages/60-reporting.qml:24
468msgid "Improving your experience"
471msgid "Improving your experience"
469−msgstr ""
472+msgstr "Ameyorar la esperiencia"
470
473
471#: qml/Wizard/Pages/60-reporting.qml:36
474#: qml/Wizard/Pages/60-reporting.qml:36
472msgid ""
475msgid ""
473"Your phone is set up to automatically report errors to Canonical and its "
476"Your phone is set up to automatically report errors to Canonical and its "
474"partners, the makers of the operating system."
477"partners, the makers of the operating system."
475msgstr ""
478msgstr ""
479+"El teléfonu ta configuráu pa informar automáticamente de los fallos a "
480+"Canonical y asociaos, los fabricantes del sistema operativu."
476
481
477#: qml/Wizard/Pages/60-reporting.qml:43
482#: qml/Wizard/Pages/60-reporting.qml:43
478msgid ""
483msgid ""
479"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
484"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
480"Privacy</b>"
485"Privacy</b>"
481msgstr ""
486msgstr ""
487+"Esto pue desactivase nos <b>Axustes del sistema</b> en <b>Seguranza y "
488+"privacidá</b>"
482
489
483#: qml/Wizard/Pages/80-finished.qml:24
490#: qml/Wizard/Pages/80-finished.qml:24
484msgid "All done"
491msgid "All done"
485−msgstr ""
492+msgstr "Too fecho"
486
493
487#: qml/Wizard/Pages/80-finished.qml:39
494#: qml/Wizard/Pages/80-finished.qml:39
488msgid "Nice work!"
495msgid "Nice work!"
489−msgstr ""
496+msgstr "Bon trabayu!"
490
497
491#: qml/Wizard/Pages/80-finished.qml:46
498#: qml/Wizard/Pages/80-finished.qml:46
492msgid "Your phone is now ready to use."
499msgid "Your phone is now ready to use."
493−msgstr ""
500+msgstr "El teléfonu yá ta preparáu pa usar."
494
501
495#: qml/Wizard/Pages/80-finished.qml:53
502#: qml/Wizard/Pages/80-finished.qml:53
496msgid "Finish"
503msgid "Finish"
497−msgstr ""
504+msgstr "Finar"
498
505
499#: qml/Wizard/Pages/here-terms.qml:27
506#: qml/Wizard/Pages/here-terms.qml:27
500msgid "Terms & Conditions"
507msgid "Terms & Conditions"
501−msgstr ""
508+msgstr "Términos y condiciones"
502
509
503#: qml/Wizard/Pages/passwd-confirm.qml:49
510#: qml/Wizard/Pages/passwd-confirm.qml:49
504msgid "Confirm passphrase"
511msgid "Confirm passphrase"
505−msgstr ""
512+msgstr "Confirmar fras secreta"
506
513
507#: qml/Wizard/Pages/passwd-confirm.qml:50
514#: qml/Wizard/Pages/passwd-confirm.qml:50
508msgid "Confirm passcode"
515msgid "Confirm passcode"
509−msgstr ""
516+msgstr "Confirmar el códigu de pasu"
510
517
511#: qml/Wizard/Pages/passwd-confirm.qml:53
518#: qml/Wizard/Pages/passwd-confirm.qml:53
512#: qml/Wizard/Pages/passwd-confirm.qml:54
519#: qml/Wizard/Pages/passwd-confirm.qml:54
513msgid "Please try again."
520msgid "Please try again."
514−msgstr ""
521+msgstr "Vuelvi a intentalo."
515
522
516#: qml/Wizard/Pages/passwd-set.qml:61
523#: qml/Wizard/Pages/passwd-set.qml:61
517msgid "Choose your passcode"
524msgid "Choose your passcode"
518−msgstr ""
525+msgstr "Escueyi'l códigu de pasu"
519
526
520#: qml/Wizard/Pages/passwd-set.qml:68
527#: qml/Wizard/Pages/passwd-set.qml:68
521msgid "Passphrase must be 4 characters long"
528msgid "Passphrase must be 4 characters long"
522−msgstr ""
529+msgstr "La fras de contraseña tien de ser de 4 caráuteres"
523
530
524#: qml/Wizard/StackButton.qml:39
531#: qml/Wizard/StackButton.qml:39
525#, qt-format
532#, qt-format
526msgid "〈 %1"
533msgid "〈 %1"
527−msgstr ""
534+msgstr "〈 %1"
528
535
529#: qml/Wizard/StackButton.qml:42
536#: qml/Wizard/StackButton.qml:42
530#, qt-format
537#, qt-format
531msgid "%1 〉"
538msgid "%1 〉"
532−msgstr ""
539+msgstr "%1 〉"
533
540
534#~ msgid "Confirm"
541#~ msgid "Confirm"
535#~ msgstr "Confirmar"
542#~ msgstr "Confirmar"
po/be.po+52−45
@@ -8,16 +8,16 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2014-09-24 17:12+0000\n"
11+"PO-Revision-Date: 2015-02-07 19:04+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12+"Last-Translator: Mikhail_SaTuRn <mikhail_saturn@tut.by>\n"
13"Language-Team: Belarusian <be@li.org>\n"
13"Language-Team: Belarusian <be@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
17"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
19−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
19+"X-Launchpad-Export-Date: 2015-02-08 06:11+0000\n"
20−"X-Generator: Launchpad (build 17306)\n"
20+"X-Generator: Launchpad (build 17331)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:130
22#: plugins/LightDM/Greeter.cpp:130
23msgid "Password: "
23msgid "Password: "
@@ -87,7 +87,7 @@ msgstr "Перазагрузіць"
87
87
88#: qml/Components/Dialogs.qml:132
88#: qml/Components/Dialogs.qml:132
89msgid "Are you sure you want to reboot?"
89msgid "Are you sure you want to reboot?"
90−msgstr "Вы ўпэўненыя, што жадаеце перазагрузіць?"
90+msgstr "Вы ўпэўнены, што жадаеце перазагрузіць?"
91
91
92#: qml/Components/Dialogs.qml:154
92#: qml/Components/Dialogs.qml:154
93msgid "Power"
93msgid "Power"
@@ -166,11 +166,11 @@ msgstr ""
166
166
167#: qml/Components/Lockscreen.qml:238
167#: qml/Components/Lockscreen.qml:238
168msgid "Return to Call"
168msgid "Return to Call"
169−msgstr "Вярнуцца да Выкліку"
169+msgstr "Вярнуцца да выкліку"
170
170
171#: qml/Components/Lockscreen.qml:238
171#: qml/Components/Lockscreen.qml:238
172msgid "Emergency Call"
172msgid "Emergency Call"
173−msgstr "Терміновы Выклік"
173+msgstr "Терміновы выклік"
174
174
175#: qml/Components/Lockscreen.qml:270
175#: qml/Components/Lockscreen.qml:270
176msgid "OK"
176msgid "OK"
@@ -291,7 +291,7 @@ msgstr "Памылка, націсніце для паўтору"
291
291
292#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
292#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
293msgid "Reply"
293msgid "Reply"
294−msgstr ""
294+msgstr "Адказаць"
295
295
296#: qml/Panel/Indicators/ModemInfoItem.qml:105
296#: qml/Panel/Indicators/ModemInfoItem.qml:105
297msgid "Unlock SIM"
297msgid "Unlock SIM"
@@ -361,98 +361,99 @@ msgstr ""
361
361
362#: qml/Wizard/Page.qml:89
362#: qml/Wizard/Page.qml:89
363msgid "Back"
363msgid "Back"
364−msgstr ""
364+msgstr "Назад"
365
365
366#: qml/Wizard/Pages/10-welcome.qml:27
366#: qml/Wizard/Pages/10-welcome.qml:27
367msgid "Hi!"
367msgid "Hi!"
368−msgstr ""
368+msgstr "Вітай!"
369
369
370#: qml/Wizard/Pages/10-welcome.qml:44
370#: qml/Wizard/Pages/10-welcome.qml:44
371msgid "Welcome to your Ubuntu phone."
371msgid "Welcome to your Ubuntu phone."
372−msgstr ""
372+msgstr "Вітаем Вас у сістэме тэлефона Ubuntu."
373
373
374#: qml/Wizard/Pages/10-welcome.qml:52
374#: qml/Wizard/Pages/10-welcome.qml:52
375msgid "Let’s get started."
375msgid "Let’s get started."
376−msgstr ""
376+msgstr "Пачнем."
377
377
378#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
378#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
379#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
379#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
380#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
380#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
381#: qml/Wizard/Pages/passwd-set.qml:90
381#: qml/Wizard/Pages/passwd-set.qml:90
382msgid "Continue"
382msgid "Continue"
383−msgstr ""
383+msgstr "Працягнуць"
384
384
385#: qml/Wizard/Pages/20-sim.qml:25
385#: qml/Wizard/Pages/20-sim.qml:25
386msgid "Add a SIM card and restart your device"
386msgid "Add a SIM card and restart your device"
387−msgstr ""
387+msgstr "Дадайце SIM-карту і перазагрузіце Вашую прыладу"
388
388
389#: qml/Wizard/Pages/20-sim.qml:55
389#: qml/Wizard/Pages/20-sim.qml:55
390msgid "Without it, you won’t be able to make calls or use text messaging."
390msgid "Without it, you won’t be able to make calls or use text messaging."
391−msgstr ""
391+msgstr "Без яе вы не зможаце тэлефанаваць ці адпраўляць паведамленні."
392
392
393#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
393#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
394msgid "Skip"
394msgid "Skip"
395−msgstr ""
395+msgstr "Прапусціць"
396
396
397#: qml/Wizard/Pages/30-passwd-type.qml:39
397#: qml/Wizard/Pages/30-passwd-type.qml:39
398msgid "Lock security"
398msgid "Lock security"
399−msgstr ""
399+msgstr "Блакаванне бяспекі"
400
400
401#: qml/Wizard/Pages/30-passwd-type.qml:74
401#: qml/Wizard/Pages/30-passwd-type.qml:74
402msgid "Please select how you’d like to unlock your phone."
402msgid "Please select how you’d like to unlock your phone."
403−msgstr ""
403+msgstr "Калі ласка, абярыце спосаб разблакавання тэлефона."
404
404
405#: qml/Wizard/Pages/30-passwd-type.qml:97
405#: qml/Wizard/Pages/30-passwd-type.qml:97
406msgid "Swipe"
406msgid "Swipe"
407−msgstr ""
407+msgstr "Свайп(Рух пальцам)"
408
408
409#: qml/Wizard/Pages/30-passwd-type.qml:98
409#: qml/Wizard/Pages/30-passwd-type.qml:98
410msgid "No security"
410msgid "No security"
411−msgstr ""
411+msgstr "Без абароны"
412
412
413#: qml/Wizard/Pages/30-passwd-type.qml:100
413#: qml/Wizard/Pages/30-passwd-type.qml:100
414msgid "Passcode"
414msgid "Passcode"
415−msgstr ""
415+msgstr "Код-пароль"
416
416
417#: qml/Wizard/Pages/30-passwd-type.qml:101
417#: qml/Wizard/Pages/30-passwd-type.qml:101
418msgid "4 digits only"
418msgid "4 digits only"
419−msgstr ""
419+msgstr "Толькі 4 лічбы"
420
420
421#: qml/Wizard/Pages/30-passwd-type.qml:103
421#: qml/Wizard/Pages/30-passwd-type.qml:103
422msgid "Passphrase"
422msgid "Passphrase"
423−msgstr ""
423+msgstr "Пароль"
424
424
425#: qml/Wizard/Pages/30-passwd-type.qml:104
425#: qml/Wizard/Pages/30-passwd-type.qml:104
426msgid "Numbers and letters"
426msgid "Numbers and letters"
427−msgstr ""
427+msgstr "Лічбы і літары"
428
428
429#: qml/Wizard/Pages/40-wifi.qml:28
429#: qml/Wizard/Pages/40-wifi.qml:28
430msgid "Connect to Wi‑Fi"
430msgid "Connect to Wi‑Fi"
431−msgstr ""
431+msgstr "Злучыцца з Wi-Fi"
432
432
433#: qml/Wizard/Pages/40-wifi.qml:147
433#: qml/Wizard/Pages/40-wifi.qml:147
434msgid "Available networks…"
434msgid "Available networks…"
435−msgstr ""
435+msgstr "Даступныя сеткі..."
436
436
437#: qml/Wizard/Pages/40-wifi.qml:148
437#: qml/Wizard/Pages/40-wifi.qml:148
438msgid "No available networks."
438msgid "No available networks."
439−msgstr ""
439+msgstr "Няма даступных сетак."
440
440
441#: qml/Wizard/Pages/50-location.qml:27
441#: qml/Wizard/Pages/50-location.qml:27
442msgid "Location"
442msgid "Location"
443−msgstr ""
443+msgstr "Месца"
444
444
445#: qml/Wizard/Pages/50-location.qml:62
445#: qml/Wizard/Pages/50-location.qml:62
446msgid "Let the phone detect your location:"
446msgid "Let the phone detect your location:"
447−msgstr ""
447+msgstr "Дазволіць тэлефону вызначаць месцазнаходжанне:"
448
448
449#: qml/Wizard/Pages/50-location.qml:69
449#: qml/Wizard/Pages/50-location.qml:69
450msgid "Using GPS only (less accurate)"
450msgid "Using GPS only (less accurate)"
451−msgstr ""
451+msgstr "З дапамогай толькі GPS (найменш дакладна)"
452
452
453#: qml/Wizard/Pages/50-location.qml:86
453#: qml/Wizard/Pages/50-location.qml:86
454msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
454msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
455msgstr ""
455msgstr ""
456+"На аснове GPS, ананімізаванага Wi-Fi і дадзеных сотавай сеткі (рэкамендуецца)"
456
457
457#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
458#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
458#: qml/Wizard/Pages/50-location.qml:103
459#: qml/Wizard/Pages/50-location.qml:103
@@ -460,81 +461,87 @@ msgid ""
460"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
461"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
461"conditions</a>."
462"conditions</a>."
462msgstr ""
463msgstr ""
464+"Выбарам гэтага варыянту дзеянняў вы згаджаецеся з <a href='#'> умовамі "
465+"прадстаўлення паслуг Nokia HERE</a>."
463
466
464#: qml/Wizard/Pages/50-location.qml:112
467#: qml/Wizard/Pages/50-location.qml:112
465msgid "Not at all"
468msgid "Not at all"
466−msgstr ""
469+msgstr "Не дазваляць"
467
470
468#: qml/Wizard/Pages/50-location.qml:124
471#: qml/Wizard/Pages/50-location.qml:124
469msgid "You can change your mind later in <b>System Settings</b>."
472msgid "You can change your mind later in <b>System Settings</b>."
470−msgstr ""
473+msgstr "Пасля змяніць налады можна з дапамогай <b>Сістэмных параметраў</b>."
471
474
472#: qml/Wizard/Pages/60-reporting.qml:24
475#: qml/Wizard/Pages/60-reporting.qml:24
473msgid "Improving your experience"
476msgid "Improving your experience"
474−msgstr ""
477+msgstr "Спрашчэнне вашай працы"
475
478
476#: qml/Wizard/Pages/60-reporting.qml:36
479#: qml/Wizard/Pages/60-reporting.qml:36
477msgid ""
480msgid ""
478"Your phone is set up to automatically report errors to Canonical and its "
481"Your phone is set up to automatically report errors to Canonical and its "
479"partners, the makers of the operating system."
482"partners, the makers of the operating system."
480msgstr ""
483msgstr ""
484+"Ваш тэлефон настроены на аўтаматычную адпраўку справаздач пра памылкі ў "
485+"Canonical і партнёрскія установы, вытворцам аперацыйнай сістэмы."
481
486
482#: qml/Wizard/Pages/60-reporting.qml:43
487#: qml/Wizard/Pages/60-reporting.qml:43
483msgid ""
488msgid ""
484"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
489"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
485"Privacy</b>"
490"Privacy</b>"
486msgstr ""
491msgstr ""
492+"Гэтае можна адключыць у <b>Сістэмных параметрах</b>, <b>Бяспека &amp; "
493+"канфедэнцыйнасць</b>"
487
494
488#: qml/Wizard/Pages/80-finished.qml:24
495#: qml/Wizard/Pages/80-finished.qml:24
489msgid "All done"
496msgid "All done"
490−msgstr ""
497+msgstr "Зроблена"
491
498
492#: qml/Wizard/Pages/80-finished.qml:39
499#: qml/Wizard/Pages/80-finished.qml:39
493msgid "Nice work!"
500msgid "Nice work!"
494−msgstr ""
501+msgstr "Выдатная праца!"
495
502
496#: qml/Wizard/Pages/80-finished.qml:46
503#: qml/Wizard/Pages/80-finished.qml:46
497msgid "Your phone is now ready to use."
504msgid "Your phone is now ready to use."
498−msgstr ""
505+msgstr "Цяпер Вашым тэлефонам можна карыстацца."
499
506
500#: qml/Wizard/Pages/80-finished.qml:53
507#: qml/Wizard/Pages/80-finished.qml:53
501msgid "Finish"
508msgid "Finish"
502−msgstr ""
509+msgstr "Скончыць"
503
510
504#: qml/Wizard/Pages/here-terms.qml:27
511#: qml/Wizard/Pages/here-terms.qml:27
505msgid "Terms & Conditions"
512msgid "Terms & Conditions"
506−msgstr ""
513+msgstr "Умовы карыстання"
507
514
508#: qml/Wizard/Pages/passwd-confirm.qml:49
515#: qml/Wizard/Pages/passwd-confirm.qml:49
509msgid "Confirm passphrase"
516msgid "Confirm passphrase"
510−msgstr ""
517+msgstr "Пацвердзіце пароль"
511
518
512#: qml/Wizard/Pages/passwd-confirm.qml:50
519#: qml/Wizard/Pages/passwd-confirm.qml:50
513msgid "Confirm passcode"
520msgid "Confirm passcode"
514−msgstr ""
521+msgstr "Пацвердзіце код"
515
522
516#: qml/Wizard/Pages/passwd-confirm.qml:53
523#: qml/Wizard/Pages/passwd-confirm.qml:53
517#: qml/Wizard/Pages/passwd-confirm.qml:54
524#: qml/Wizard/Pages/passwd-confirm.qml:54
518msgid "Please try again."
525msgid "Please try again."
519−msgstr ""
526+msgstr "Калі ласка, паспрабуйце яшчэ раз."
520
527
521#: qml/Wizard/Pages/passwd-set.qml:61
528#: qml/Wizard/Pages/passwd-set.qml:61
522msgid "Choose your passcode"
529msgid "Choose your passcode"
523−msgstr ""
530+msgstr "Выберыце ваш код-пароль"
524
531
525#: qml/Wizard/Pages/passwd-set.qml:68
532#: qml/Wizard/Pages/passwd-set.qml:68
526msgid "Passphrase must be 4 characters long"
533msgid "Passphrase must be 4 characters long"
527−msgstr ""
534+msgstr "Пароль мусіць складацца мінімум з 4 сімвалаў"
528
535
529#: qml/Wizard/StackButton.qml:39
536#: qml/Wizard/StackButton.qml:39
530#, qt-format
537#, qt-format
531msgid "〈 %1"
538msgid "〈 %1"
532−msgstr ""
539+msgstr "〈 %1"
533
540
534#: qml/Wizard/StackButton.qml:42
541#: qml/Wizard/StackButton.qml:42
535#, qt-format
542#, qt-format
536msgid "%1 〉"
543msgid "%1 〉"
537−msgstr ""
544+msgstr "%1 〉"
538
545
539#~ msgid "Confirm"
546#~ msgid "Confirm"
540#~ msgstr "Пацвердзіць"
547#~ msgstr "Пацвердзіць"
po/ce.ponew file+521
@@ -0,0 +1,521 @@
1+# Chechen translation for unity8
2+# Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11+"PO-Revision-Date: 2015-02-15 04:01+0000\n"
12+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13+"Language-Team: Chechen <ce@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2015-02-16 06:52+0000\n"
19+"X-Generator: Launchpad (build 17341)\n"
20+
21+#: plugins/LightDM/Greeter.cpp:130
22+msgid "Password: "
23+msgstr ""
24+
25+#: plugins/Unity/Launcher/launcheritem.cpp:45
26+#: plugins/Unity/Launcher/launcheritem.cpp:100
27+msgid "Pin shortcut"
28+msgstr ""
29+
30+#: plugins/Unity/Launcher/launcheritem.cpp:100
31+msgid "Unpin shortcut"
32+msgstr ""
33+
34+#: qml/Components/DelayedLockscreen.qml:47
35+msgid "Device Locked"
36+msgstr ""
37+
38+#: qml/Components/DelayedLockscreen.qml:62
39+msgid "You have been locked out due to too many failed passphrase attempts."
40+msgstr ""
41+
42+#: qml/Components/DelayedLockscreen.qml:63
43+msgid "You have been locked out due to too many failed passcode attempts."
44+msgstr ""
45+
46+#: qml/Components/DelayedLockscreen.qml:72
47+#, qt-format
48+msgid "Please wait %1 minute and then try again…"
49+msgid_plural "Please wait %1 minutes and then try again…"
50+msgstr[0] ""
51+msgstr[1] ""
52+
53+#: qml/Components/Dialogs.qml:86
54+msgid "Log out"
55+msgstr ""
56+
57+#: qml/Components/Dialogs.qml:87
58+msgid "Are you sure you want to log out?"
59+msgstr ""
60+
61+#: qml/Components/Dialogs.qml:89 qml/Components/Dialogs.qml:111
62+#: qml/Components/Dialogs.qml:134
63+msgid "No"
64+msgstr ""
65+
66+#: qml/Components/Dialogs.qml:95 qml/Components/Dialogs.qml:117
67+#: qml/Components/Dialogs.qml:140
68+msgid "Yes"
69+msgstr ""
70+
71+#: qml/Components/Dialogs.qml:108
72+msgid "Shut down"
73+msgstr ""
74+
75+#: qml/Components/Dialogs.qml:109
76+msgid "Are you sure you want to shut down?"
77+msgstr ""
78+
79+#: qml/Components/Dialogs.qml:131
80+msgid "Reboot"
81+msgstr ""
82+
83+#: qml/Components/Dialogs.qml:132
84+msgid "Are you sure you want to reboot?"
85+msgstr ""
86+
87+#: qml/Components/Dialogs.qml:154
88+msgid "Power"
89+msgstr ""
90+
91+#: qml/Components/Dialogs.qml:155
92+msgid ""
93+"Are you sure you would like\n"
94+"to power off?"
95+msgstr ""
96+
97+#: qml/Components/Dialogs.qml:157
98+msgid "Power off"
99+msgstr ""
100+
101+#: qml/Components/Dialogs.qml:166
102+msgid "Restart"
103+msgstr ""
104+
105+#: qml/Components/Dialogs.qml:175
106+msgid "Cancel"
107+msgstr ""
108+
109+#: qml/Components/EdgeDemoOverlay.qml:151
110+msgid "Skip intro"
111+msgstr ""
112+
113+#: qml/Components/EdgeDemo.qml:125
114+msgid "Right edge"
115+msgstr ""
116+
117+#: qml/Components/EdgeDemo.qml:126
118+msgid "Try swiping from the right edge to unlock the phone"
119+msgstr ""
120+
121+#: qml/Components/EdgeDemo.qml:157
122+msgid "Top edge"
123+msgstr ""
124+
125+#: qml/Components/EdgeDemo.qml:158
126+msgid "Try swiping from the top edge to access the indicators"
127+msgstr ""
128+
129+#: qml/Components/EdgeDemo.qml:183
130+msgid "Close"
131+msgstr ""
132+
133+#: qml/Components/EdgeDemo.qml:184
134+msgid "Swipe up again to close the settings screen"
135+msgstr ""
136+
137+#: qml/Components/EdgeDemo.qml:214
138+msgid "Left edge"
139+msgstr ""
140+
141+#: qml/Components/EdgeDemo.qml:215
142+msgid "Swipe from the left to reveal the launcher for quick access to apps"
143+msgstr ""
144+
145+#: qml/Components/EdgeDemo.qml:242
146+msgid "Well done"
147+msgstr ""
148+
149+#: qml/Components/EdgeDemo.qml:243
150+msgid ""
151+"You have now mastered the edge gestures and can start using the "
152+"phone<br><br>Tap on the screen to start"
153+msgstr ""
154+
155+#: qml/Components/Lockscreen.qml:238
156+msgid "Return to Call"
157+msgstr ""
158+
159+#: qml/Components/Lockscreen.qml:238
160+msgid "Emergency Call"
161+msgstr ""
162+
163+#: qml/Components/Lockscreen.qml:270
164+msgid "OK"
165+msgstr ""
166+
167+#: qml/Dash/GenericScopeView.qml:485 qml/Dash/GenericScopeView.qml:642
168+msgid "See less"
169+msgstr ""
170+
171+#: qml/Dash/GenericScopeView.qml:485
172+msgid "See all"
173+msgstr ""
174+
175+#: qml/Dash/GenericScopeView.qml:547 qml/Dash/PageHeader.qml:274
176+#: qml/Panel/SearchIndicator.qml:27
177+msgid "Search"
178+msgstr ""
179+
180+#: qml/Dash/PageHeader.qml:267
181+msgid "Store"
182+msgstr ""
183+
184+#: qml/Dash/PageHeader.qml:284
185+msgid "Settings"
186+msgstr ""
187+
188+#: qml/Dash/PageHeader.qml:291
189+msgid "Remove from Favorites"
190+msgstr ""
191+
192+#: qml/Dash/PageHeader.qml:291
193+msgid "Add to Favorites"
194+msgstr ""
195+
196+#: qml/Dash/Previews/PreviewActionCombo.qml:35
197+msgid "More..."
198+msgstr ""
199+
200+#: qml/Dash/Previews/PreviewActionCombo.qml:35
201+msgid "Less..."
202+msgstr ""
203+
204+#: qml/Dash/Previews/PreviewRatingInput.qml:81
205+msgid "Rate this"
206+msgstr ""
207+
208+#: qml/Dash/Previews/PreviewRatingInput.qml:126
209+msgid "Add a review"
210+msgstr ""
211+
212+#: qml/Dash/Previews/PreviewRatingInput.qml:166
213+msgid "Send"
214+msgstr ""
215+
216+#: qml/Dash/PullToRefreshScopeStyle.qml:55
217+msgid "Pull to refresh…"
218+msgstr ""
219+
220+#: qml/Dash/PullToRefreshScopeStyle.qml:60
221+msgid "Release to refresh…"
222+msgstr ""
223+
224+#: qml/Dash/ScopesList.qml:66
225+msgid "Manage"
226+msgstr ""
227+
228+#: qml/Dash/ScopesList.qml:112
229+msgid "Home"
230+msgstr ""
231+
232+#: qml/Dash/ScopesList.qml:113
233+msgid "Also installed"
234+msgstr ""
235+
236+#: qml/Greeter/Greeter.qml:133
237+msgid "Unlock"
238+msgstr ""
239+
240+#: qml/Notifications/NotificationMenuItemFactory.qml:128
241+msgid "Show password"
242+msgstr ""
243+
244+#: qml/Panel/ActiveCallHint.qml:79
245+msgid "Tap to return to call..."
246+msgstr ""
247+
248+#: qml/Panel/ActiveCallHint.qml:92
249+msgid "Conference"
250+msgstr ""
251+
252+#: qml/Panel/Indicators/MenuItemFactory.qml:643
253+msgid "Nothing is playing"
254+msgstr ""
255+
256+#: qml/Panel/Indicators/MenuItemFactory.qml:791
257+msgid "In queue…"
258+msgstr ""
259+
260+#: qml/Panel/Indicators/MenuItemFactory.qml:795
261+msgid "Downloading"
262+msgstr ""
263+
264+#: qml/Panel/Indicators/MenuItemFactory.qml:797
265+msgid "Paused, tap to resume"
266+msgstr ""
267+
268+#: qml/Panel/Indicators/MenuItemFactory.qml:799
269+msgid "Canceled"
270+msgstr ""
271+
272+#: qml/Panel/Indicators/MenuItemFactory.qml:801
273+msgid "Finished"
274+msgstr ""
275+
276+#: qml/Panel/Indicators/MenuItemFactory.qml:803
277+msgid "Failed, tap to retry"
278+msgstr ""
279+
280+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
281+msgid "Reply"
282+msgstr ""
283+
284+#: qml/Panel/Indicators/ModemInfoItem.qml:105
285+msgid "Unlock SIM"
286+msgstr ""
287+
288+#: qml/Panel/Indicators/RoamingIndication.qml:27
289+msgid "Roaming"
290+msgstr ""
291+
292+#: qml/Shell.qml:390
293+#, qt-format
294+msgid "Enter %1"
295+msgstr ""
296+
297+#: qml/Shell.qml:391 qml/Wizard/Pages/passwd-set.qml:60
298+msgid "Enter passphrase"
299+msgstr ""
300+
301+#: qml/Shell.qml:392
302+msgid "Enter passcode"
303+msgstr ""
304+
305+#: qml/Shell.qml:393
306+#, qt-format
307+msgid "Sorry, incorrect %1"
308+msgstr ""
309+
310+#: qml/Shell.qml:394
311+msgid "Sorry, incorrect passphrase"
312+msgstr ""
313+
314+#: qml/Shell.qml:395
315+msgid "Please re-enter"
316+msgstr ""
317+
318+#: qml/Shell.qml:396
319+msgid "Sorry, incorrect passcode"
320+msgstr ""
321+
322+#: qml/Shell.qml:490 qml/Wizard/Pages/passwd-confirm.qml:53
323+msgid "Sorry, incorrect passphrase."
324+msgstr ""
325+
326+#: qml/Shell.qml:491 qml/Wizard/Pages/passwd-confirm.qml:54
327+msgid "Sorry, incorrect passcode."
328+msgstr ""
329+
330+#: qml/Shell.qml:492
331+msgid "This will be your last attempt."
332+msgstr ""
333+
334+#: qml/Shell.qml:494
335+msgid ""
336+"If passphrase is entered incorrectly, your phone will conduct a factory "
337+"reset and all personal data will be deleted."
338+msgstr ""
339+
340+#: qml/Shell.qml:495
341+msgid ""
342+"If passcode is entered incorrectly, your phone will conduct a factory reset "
343+"and all personal data will be deleted."
344+msgstr ""
345+
346+#: qml/Wizard/Page.qml:89
347+msgid "Back"
348+msgstr ""
349+
350+#: qml/Wizard/Pages/10-welcome.qml:27
351+msgid "Hi!"
352+msgstr ""
353+
354+#: qml/Wizard/Pages/10-welcome.qml:44
355+msgid "Welcome to your Ubuntu phone."
356+msgstr ""
357+
358+#: qml/Wizard/Pages/10-welcome.qml:52
359+msgid "Let’s get started."
360+msgstr ""
361+
362+#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
363+#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
364+#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
365+#: qml/Wizard/Pages/passwd-set.qml:90
366+msgid "Continue"
367+msgstr ""
368+
369+#: qml/Wizard/Pages/20-sim.qml:25
370+msgid "Add a SIM card and restart your device"
371+msgstr ""
372+
373+#: qml/Wizard/Pages/20-sim.qml:55
374+msgid "Without it, you won’t be able to make calls or use text messaging."
375+msgstr ""
376+
377+#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
378+msgid "Skip"
379+msgstr ""
380+
381+#: qml/Wizard/Pages/30-passwd-type.qml:39
382+msgid "Lock security"
383+msgstr ""
384+
385+#: qml/Wizard/Pages/30-passwd-type.qml:74
386+msgid "Please select how you’d like to unlock your phone."
387+msgstr ""
388+
389+#: qml/Wizard/Pages/30-passwd-type.qml:97
390+msgid "Swipe"
391+msgstr ""
392+
393+#: qml/Wizard/Pages/30-passwd-type.qml:98
394+msgid "No security"
395+msgstr ""
396+
397+#: qml/Wizard/Pages/30-passwd-type.qml:100
398+msgid "Passcode"
399+msgstr ""
400+
401+#: qml/Wizard/Pages/30-passwd-type.qml:101
402+msgid "4 digits only"
403+msgstr ""
404+
405+#: qml/Wizard/Pages/30-passwd-type.qml:103
406+msgid "Passphrase"
407+msgstr ""
408+
409+#: qml/Wizard/Pages/30-passwd-type.qml:104
410+msgid "Numbers and letters"
411+msgstr ""
412+
413+#: qml/Wizard/Pages/40-wifi.qml:28
414+msgid "Connect to Wi‑Fi"
415+msgstr ""
416+
417+#: qml/Wizard/Pages/40-wifi.qml:147
418+msgid "Available networks…"
419+msgstr ""
420+
421+#: qml/Wizard/Pages/40-wifi.qml:148
422+msgid "No available networks."
423+msgstr ""
424+
425+#: qml/Wizard/Pages/50-location.qml:27
426+msgid "Location"
427+msgstr ""
428+
429+#: qml/Wizard/Pages/50-location.qml:62
430+msgid "Let the phone detect your location:"
431+msgstr ""
432+
433+#: qml/Wizard/Pages/50-location.qml:69
434+msgid "Using GPS only (less accurate)"
435+msgstr ""
436+
437+#: qml/Wizard/Pages/50-location.qml:86
438+msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
439+msgstr ""
440+
441+#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
442+#: qml/Wizard/Pages/50-location.qml:103
443+msgid ""
444+"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
445+"conditions</a>."
446+msgstr ""
447+
448+#: qml/Wizard/Pages/50-location.qml:112
449+msgid "Not at all"
450+msgstr ""
451+
452+#: qml/Wizard/Pages/50-location.qml:124
453+msgid "You can change your mind later in <b>System Settings</b>."
454+msgstr ""
455+
456+#: qml/Wizard/Pages/60-reporting.qml:24
457+msgid "Improving your experience"
458+msgstr ""
459+
460+#: qml/Wizard/Pages/60-reporting.qml:36
461+msgid ""
462+"Your phone is set up to automatically report errors to Canonical and its "
463+"partners, the makers of the operating system."
464+msgstr ""
465+
466+#: qml/Wizard/Pages/60-reporting.qml:43
467+msgid ""
468+"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
469+"Privacy</b>"
470+msgstr ""
471+
472+#: qml/Wizard/Pages/80-finished.qml:24
473+msgid "All done"
474+msgstr ""
475+
476+#: qml/Wizard/Pages/80-finished.qml:39
477+msgid "Nice work!"
478+msgstr ""
479+
480+#: qml/Wizard/Pages/80-finished.qml:46
481+msgid "Your phone is now ready to use."
482+msgstr ""
483+
484+#: qml/Wizard/Pages/80-finished.qml:53
485+msgid "Finish"
486+msgstr ""
487+
488+#: qml/Wizard/Pages/here-terms.qml:27
489+msgid "Terms & Conditions"
490+msgstr ""
491+
492+#: qml/Wizard/Pages/passwd-confirm.qml:49
493+msgid "Confirm passphrase"
494+msgstr ""
495+
496+#: qml/Wizard/Pages/passwd-confirm.qml:50
497+msgid "Confirm passcode"
498+msgstr ""
499+
500+#: qml/Wizard/Pages/passwd-confirm.qml:53
501+#: qml/Wizard/Pages/passwd-confirm.qml:54
502+msgid "Please try again."
503+msgstr ""
504+
505+#: qml/Wizard/Pages/passwd-set.qml:61
506+msgid "Choose your passcode"
507+msgstr ""
508+
509+#: qml/Wizard/Pages/passwd-set.qml:68
510+msgid "Passphrase must be 4 characters long"
511+msgstr ""
512+
513+#: qml/Wizard/StackButton.qml:39
514+#, qt-format
515+msgid "〈 %1"
516+msgstr ""
517+
518+#: qml/Wizard/StackButton.qml:42
519+#, qt-format
520+msgid "%1 〉"
521+msgstr ""
po/de.po+3−3
@@ -8,14 +8,14 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2014-12-12 01:42+0000\n"
11+"PO-Revision-Date: 2015-01-29 21:52+0000\n"
12"Last-Translator: Niklas Wenzel <nikwen.developer@gmail.com>\n"
12"Last-Translator: Niklas Wenzel <nikwen.developer@gmail.com>\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-01-28 06:30+0000\n"
18+"X-Launchpad-Export-Date: 2015-01-30 07:08+0000\n"
19"X-Generator: Launchpad (build 17306)\n"
19"X-Generator: Launchpad (build 17306)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:130
21#: plugins/LightDM/Greeter.cpp:130
@@ -290,7 +290,7 @@ msgstr "Fehlgeschlagen, tippen Sie zum Wiederholen"
290
290
291#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
291#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
292msgid "Reply"
292msgid "Reply"
293−msgstr ""
293+msgstr "Antworten"
294
294
295#: qml/Panel/Indicators/ModemInfoItem.qml:105
295#: qml/Panel/Indicators/ModemInfoItem.qml:105
296msgid "Unlock SIM"
296msgid "Unlock SIM"
po/el.po+68−50
@@ -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-01-22 11:09+0100\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2014-11-08 18:38+0000\n"
11+"PO-Revision-Date: 2015-02-15 20:45+0000\n"
12−"Last-Translator: Efstathios Iosifidis <Unknown>\n"
12+"Last-Translator: Filippos Kolyvas <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-01-28 06:30+0000\n"
18+"X-Launchpad-Export-Date: 2015-02-16 06:52+0000\n"
19−"X-Generator: Launchpad (build 17306)\n"
19+"X-Generator: Launchpad (build 17341)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:130
21#: plugins/LightDM/Greeter.cpp:130
22msgid "Password: "
22msgid "Password: "
@@ -90,7 +90,7 @@ msgstr "Επιθυμείτε σίγουρα επανεκκίνηση;"
90
90
91#: qml/Components/Dialogs.qml:154
91#: qml/Components/Dialogs.qml:154
92msgid "Power"
92msgid "Power"
93−msgstr ""
93+msgstr "Ενέργεια"
94
94
95#: qml/Components/Dialogs.qml:155
95#: qml/Components/Dialogs.qml:155
96msgid ""
96msgid ""
@@ -225,23 +225,23 @@ msgstr "Αποστολή"
225
225
226#: qml/Dash/PullToRefreshScopeStyle.qml:55
226#: qml/Dash/PullToRefreshScopeStyle.qml:55
227msgid "Pull to refresh…"
227msgid "Pull to refresh…"
228−msgstr ""
228+msgstr "Σύρτε για ανανέωση…"
229
229
230#: qml/Dash/PullToRefreshScopeStyle.qml:60
230#: qml/Dash/PullToRefreshScopeStyle.qml:60
231msgid "Release to refresh…"
231msgid "Release to refresh…"
232−msgstr ""
232+msgstr "Αφήστε για ανανέωση…"
233
233
234#: qml/Dash/ScopesList.qml:66
234#: qml/Dash/ScopesList.qml:66
235msgid "Manage"
235msgid "Manage"
236−msgstr ""
236+msgstr "Διαχείριση"
237
237
238#: qml/Dash/ScopesList.qml:112
238#: qml/Dash/ScopesList.qml:112
239msgid "Home"
239msgid "Home"
240−msgstr ""
240+msgstr "Αρχή"
241
241
242#: qml/Dash/ScopesList.qml:113
242#: qml/Dash/ScopesList.qml:113
243msgid "Also installed"
243msgid "Also installed"
244−msgstr ""
244+msgstr "Επίσης εγκατεστημένα"
245
245
246#: qml/Greeter/Greeter.qml:133
246#: qml/Greeter/Greeter.qml:133
247msgid "Unlock"
247msgid "Unlock"
@@ -289,7 +289,7 @@ msgstr "Αποτυχία, πατήστε για να ξαναπροσπαθήσ
289
289
290#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
290#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
291msgid "Reply"
291msgid "Reply"
292−msgstr ""
292+msgstr "Απάντηση"
293
293
294#: qml/Panel/Indicators/ModemInfoItem.qml:105
294#: qml/Panel/Indicators/ModemInfoItem.qml:105
295msgid "Unlock SIM"
295msgid "Unlock SIM"
@@ -327,126 +327,136 @@ msgstr "Παρακαλώ πληκτρολογήστε ξανά"
327
327
328#: qml/Shell.qml:396
328#: qml/Shell.qml:396
329msgid "Sorry, incorrect passcode"
329msgid "Sorry, incorrect passcode"
330−msgstr ""
330+msgstr "Συγνώμη, μη έγκυρος κωδικός πρόσβασης"
331
331
332#: qml/Shell.qml:490 qml/Wizard/Pages/passwd-confirm.qml:53
332#: qml/Shell.qml:490 qml/Wizard/Pages/passwd-confirm.qml:53
333msgid "Sorry, incorrect passphrase."
333msgid "Sorry, incorrect passphrase."
334−msgstr ""
334+msgstr "Συγνώμη, μη έγκυρη συνθηματική φράση."
335
335
336#: qml/Shell.qml:491 qml/Wizard/Pages/passwd-confirm.qml:54
336#: qml/Shell.qml:491 qml/Wizard/Pages/passwd-confirm.qml:54
337msgid "Sorry, incorrect passcode."
337msgid "Sorry, incorrect passcode."
338−msgstr ""
338+msgstr "Συγνώμη, μη έγκυρος κωδικός πρόσβασης."
339
339
340#: qml/Shell.qml:492
340#: qml/Shell.qml:492
341msgid "This will be your last attempt."
341msgid "This will be your last attempt."
342−msgstr ""
342+msgstr "Αυτή θα είναι η τελευταία σας προσπάθεια."
343
343
344#: qml/Shell.qml:494
344#: qml/Shell.qml:494
345msgid ""
345msgid ""
346"If passphrase is entered incorrectly, your phone will conduct a factory "
346"If passphrase is entered incorrectly, your phone will conduct a factory "
347"reset and all personal data will be deleted."
347"reset and all personal data will be deleted."
348msgstr ""
348msgstr ""
349+"Εάν η συνθηματική φράση που θα εισάγετε είναι μη έγκυρη, το τηλέφωνο σας θα "
350+"κάνει επαναφορά των εργοστασιακών ρυθμίσεων και όλα τα δεδομένα σας θα "
351+"διαγραφούν."
349
352
350#: qml/Shell.qml:495
353#: qml/Shell.qml:495
351msgid ""
354msgid ""
352"If passcode is entered incorrectly, your phone will conduct a factory reset "
355"If passcode is entered incorrectly, your phone will conduct a factory reset "
353"and all personal data will be deleted."
356"and all personal data will be deleted."
354msgstr ""
357msgstr ""
358+"Εάν ο κωδικός πρόσβασης που θα εισάγετε είναι μη έγκυρος, το τηλέφωνο σας θα "
359+"κάνει επαναφορά των εργοστασιακών ρυθμίσεων και όλα τα δεδομένα σας θα "
360+"διαγραφούν."
355
361
356#: qml/Wizard/Page.qml:89
362#: qml/Wizard/Page.qml:89
357msgid "Back"
363msgid "Back"
358−msgstr ""
364+msgstr "Πίσω"
359
365
360#: qml/Wizard/Pages/10-welcome.qml:27
366#: qml/Wizard/Pages/10-welcome.qml:27
361msgid "Hi!"
367msgid "Hi!"
362−msgstr ""
368+msgstr "Γεια!"
363
369
364#: qml/Wizard/Pages/10-welcome.qml:44
370#: qml/Wizard/Pages/10-welcome.qml:44
365msgid "Welcome to your Ubuntu phone."
371msgid "Welcome to your Ubuntu phone."
366−msgstr ""
372+msgstr "Καλώς ήρθατε στο Ubuntu phone σας."
367
373
368#: qml/Wizard/Pages/10-welcome.qml:52
374#: qml/Wizard/Pages/10-welcome.qml:52
369msgid "Let’s get started."
375msgid "Let’s get started."
370−msgstr ""
376+msgstr "Ας ξεκινήσουμε."
371
377
372#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
378#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
373#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
379#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
374#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
380#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
375#: qml/Wizard/Pages/passwd-set.qml:90
381#: qml/Wizard/Pages/passwd-set.qml:90
376msgid "Continue"
382msgid "Continue"
377−msgstr ""
383+msgstr "Συνέχιση"
378
384
379#: qml/Wizard/Pages/20-sim.qml:25
385#: qml/Wizard/Pages/20-sim.qml:25
380msgid "Add a SIM card and restart your device"
386msgid "Add a SIM card and restart your device"
381−msgstr ""
387+msgstr "Εισάγετε μια κάρτα SIM και επανεκκινήστε τη συσκευή σας"
382
388
383#: qml/Wizard/Pages/20-sim.qml:55
389#: qml/Wizard/Pages/20-sim.qml:55
384msgid "Without it, you won’t be able to make calls or use text messaging."
390msgid "Without it, you won’t be able to make calls or use text messaging."
385msgstr ""
391msgstr ""
392+"Χωρίς αυτήν, δεν θα μπορείτε να κάνετε κλήσεις ή να χρησιμοποιήσετε μηνύματα "
393+"κειμένου."
386
394
387#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
395#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
388msgid "Skip"
396msgid "Skip"
389−msgstr ""
397+msgstr "Παράβλεψη"
390
398
391#: qml/Wizard/Pages/30-passwd-type.qml:39
399#: qml/Wizard/Pages/30-passwd-type.qml:39
392msgid "Lock security"
400msgid "Lock security"
393−msgstr ""
401+msgstr "Κλείδωμα ασφαλείας"
394
402
395#: qml/Wizard/Pages/30-passwd-type.qml:74
403#: qml/Wizard/Pages/30-passwd-type.qml:74
396msgid "Please select how you’d like to unlock your phone."
404msgid "Please select how you’d like to unlock your phone."
397−msgstr ""
405+msgstr "Παρακαλούμε επιλέξτε πως θα θέλατε να ξεκλειδώνετε το τηλέφωνό σας."
398
406
399#: qml/Wizard/Pages/30-passwd-type.qml:97
407#: qml/Wizard/Pages/30-passwd-type.qml:97
400msgid "Swipe"
408msgid "Swipe"
401−msgstr ""
409+msgstr "Χειρονομία"
402
410
403#: qml/Wizard/Pages/30-passwd-type.qml:98
411#: qml/Wizard/Pages/30-passwd-type.qml:98
404msgid "No security"
412msgid "No security"
405−msgstr ""
413+msgstr "Χωρίς ασφάλεια"
406
414
407#: qml/Wizard/Pages/30-passwd-type.qml:100
415#: qml/Wizard/Pages/30-passwd-type.qml:100
408msgid "Passcode"
416msgid "Passcode"
409−msgstr ""
417+msgstr "Κωδικός πρόσβασης"
410
418
411#: qml/Wizard/Pages/30-passwd-type.qml:101
419#: qml/Wizard/Pages/30-passwd-type.qml:101
412msgid "4 digits only"
420msgid "4 digits only"
413−msgstr ""
421+msgstr "Μόνο με 4 ψηφία"
414
422
415#: qml/Wizard/Pages/30-passwd-type.qml:103
423#: qml/Wizard/Pages/30-passwd-type.qml:103
416msgid "Passphrase"
424msgid "Passphrase"
417−msgstr ""
425+msgstr "Συνθηματική φράση"
418
426
419#: qml/Wizard/Pages/30-passwd-type.qml:104
427#: qml/Wizard/Pages/30-passwd-type.qml:104
420msgid "Numbers and letters"
428msgid "Numbers and letters"
421−msgstr ""
429+msgstr "Αριθμοί και γράμματα"
422
430
423#: qml/Wizard/Pages/40-wifi.qml:28
431#: qml/Wizard/Pages/40-wifi.qml:28
424msgid "Connect to Wi‑Fi"
432msgid "Connect to Wi‑Fi"
425−msgstr ""
433+msgstr "Σύνδεση σε δίκτυο Wi-Fi"
426
434
427#: qml/Wizard/Pages/40-wifi.qml:147
435#: qml/Wizard/Pages/40-wifi.qml:147
428msgid "Available networks…"
436msgid "Available networks…"
429−msgstr ""
437+msgstr "Διαθέσιμα δίκτυα..."
430
438
431#: qml/Wizard/Pages/40-wifi.qml:148
439#: qml/Wizard/Pages/40-wifi.qml:148
432msgid "No available networks."
440msgid "No available networks."
433−msgstr ""
441+msgstr "Δεν υπάρχουν διαθέσιμα δίκτυα."
434
442
435#: qml/Wizard/Pages/50-location.qml:27
443#: qml/Wizard/Pages/50-location.qml:27
436msgid "Location"
444msgid "Location"
437−msgstr ""
445+msgstr "Τοποθεσία"
438
446
439#: qml/Wizard/Pages/50-location.qml:62
447#: qml/Wizard/Pages/50-location.qml:62
440msgid "Let the phone detect your location:"
448msgid "Let the phone detect your location:"
441−msgstr ""
449+msgstr "Επιτρέψετε το τηλέφωνο να εντοπίσει την τοποθεσία σας:"
442
450
443#: qml/Wizard/Pages/50-location.qml:69
451#: qml/Wizard/Pages/50-location.qml:69
444msgid "Using GPS only (less accurate)"
452msgid "Using GPS only (less accurate)"
445−msgstr ""
453+msgstr "Χρήση μόνο GPS (λιγότερο ακριβή)"
446
454
447#: qml/Wizard/Pages/50-location.qml:86
455#: qml/Wizard/Pages/50-location.qml:86
448msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
456msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
449msgstr ""
457msgstr ""
458+"Χρήση GPS, ανωνυνοποιημένα δίκτυα Wi-Fi και πληροφορίες δικτύων κινητής "
459+"τηλεφωνίας (συνιστάται)"
450
460
451#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
461#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
452#: qml/Wizard/Pages/50-location.qml:103
462#: qml/Wizard/Pages/50-location.qml:103
@@ -454,81 +464,89 @@ msgid ""
454"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
464"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
455"conditions</a>."
465"conditions</a>."
456msgstr ""
466msgstr ""
467+"Ενεργοποιώντας αυτή την επιλογή, αποδέχεστε τους <a href='#'>όρους και "
468+"προϋποθέσεις</a> της Nokia HERE."
457
469
458#: qml/Wizard/Pages/50-location.qml:112
470#: qml/Wizard/Pages/50-location.qml:112
459msgid "Not at all"
471msgid "Not at all"
460−msgstr ""
472+msgstr "Καθόλου"
461
473
462#: qml/Wizard/Pages/50-location.qml:124
474#: qml/Wizard/Pages/50-location.qml:124
463msgid "You can change your mind later in <b>System Settings</b>."
475msgid "You can change your mind later in <b>System Settings</b>."
464msgstr ""
476msgstr ""
477+"Μπορείτε να αλλάξετε τη γνώμη σας αργότερα στις <b>Ρυθμίσεις Συστήματος</b>."
465
478
466#: qml/Wizard/Pages/60-reporting.qml:24
479#: qml/Wizard/Pages/60-reporting.qml:24
467msgid "Improving your experience"
480msgid "Improving your experience"
468−msgstr ""
481+msgstr "Βελτιώστε την εμπειρία σας"
469
482
470#: qml/Wizard/Pages/60-reporting.qml:36
483#: qml/Wizard/Pages/60-reporting.qml:36
471msgid ""
484msgid ""
472"Your phone is set up to automatically report errors to Canonical and its "
485"Your phone is set up to automatically report errors to Canonical and its "
473"partners, the makers of the operating system."
486"partners, the makers of the operating system."
474msgstr ""
487msgstr ""
488+"Το τηλέφωνό σας είναι ρυθμισμένο να στέλνει αυτόματα αναφορές σφαλμάτων στην "
489+"Canonical και τους συνεργάτες της, τους δημιουργούς τους λειτουργικού "
490+"συστήματος."
475
491
476#: qml/Wizard/Pages/60-reporting.qml:43
492#: qml/Wizard/Pages/60-reporting.qml:43
477msgid ""
493msgid ""
478"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
494"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
479"Privacy</b>"
495"Privacy</b>"
480msgstr ""
496msgstr ""
497+"Αυτό μπορεί να απενεργοποιηθεί στις <b>Ρυθμίσεις Συστήματος</b> στο πεδίο "
498+"<b>Ασφάλεια &amp; Ιδιωτικότητα</b>"
481
499
482#: qml/Wizard/Pages/80-finished.qml:24
500#: qml/Wizard/Pages/80-finished.qml:24
483msgid "All done"
501msgid "All done"
484−msgstr ""
502+msgstr "Όλα έτοιμα"
485
503
486#: qml/Wizard/Pages/80-finished.qml:39
504#: qml/Wizard/Pages/80-finished.qml:39
487msgid "Nice work!"
505msgid "Nice work!"
488−msgstr ""
506+msgstr "Ωραία δουλειά!"
489
507
490#: qml/Wizard/Pages/80-finished.qml:46
508#: qml/Wizard/Pages/80-finished.qml:46
491msgid "Your phone is now ready to use."
509msgid "Your phone is now ready to use."
492−msgstr ""
510+msgstr "Το τηλέφωνό σας είναι πλέον έτοιμο για χρήση."
493
511
494#: qml/Wizard/Pages/80-finished.qml:53
512#: qml/Wizard/Pages/80-finished.qml:53
495msgid "Finish"
513msgid "Finish"
496−msgstr ""
514+msgstr "Ολοκλήρωση"
497
515
498#: qml/Wizard/Pages/here-terms.qml:27
516#: qml/Wizard/Pages/here-terms.qml:27
499msgid "Terms & Conditions"
517msgid "Terms & Conditions"
500−msgstr ""
518+msgstr "Όροι και προϋποθέσεις"
501
519
502#: qml/Wizard/Pages/passwd-confirm.qml:49
520#: qml/Wizard/Pages/passwd-confirm.qml:49
503msgid "Confirm passphrase"
521msgid "Confirm passphrase"
504−msgstr ""
522+msgstr "Επιβεβαίωση φράσης πρόσβασης"
505
523
506#: qml/Wizard/Pages/passwd-confirm.qml:50
524#: qml/Wizard/Pages/passwd-confirm.qml:50
507msgid "Confirm passcode"
525msgid "Confirm passcode"
508−msgstr ""
526+msgstr "Επιβεβαιώστε τον κωδικό πρόσβασης"
509
527
510#: qml/Wizard/Pages/passwd-confirm.qml:53
528#: qml/Wizard/Pages/passwd-confirm.qml:53
511#: qml/Wizard/Pages/passwd-confirm.qml:54
529#: qml/Wizard/Pages/passwd-confirm.qml:54
512msgid "Please try again."
530msgid "Please try again."
513−msgstr ""
531+msgstr "Παρακαλώ προσπαθήστε ξανά."
514
532
515#: qml/Wizard/Pages/passwd-set.qml:61
533#: qml/Wizard/Pages/passwd-set.qml:61
516msgid "Choose your passcode"
534msgid "Choose your passcode"
517−msgstr ""
535+msgstr "Επιλογή κωδικού πρόσβασης"
518
536
519#: qml/Wizard/Pages/passwd-set.qml:68
537#: qml/Wizard/Pages/passwd-set.qml:68
520msgid "Passphrase must be 4 characters long"
538msgid "Passphrase must be 4 characters long"
521−msgstr ""
539+msgstr "Η φράση πρόσβασης πρέπει να έχει 4 χαρακτήρες"
522
540
523#: qml/Wizard/StackButton.qml:39
541#: qml/Wizard/StackButton.qml:39
524#, qt-format
542#, qt-format
525msgid "〈 %1"
543msgid "〈 %1"
526−msgstr ""
544+msgstr "〈 %1"
527
545
528#: qml/Wizard/StackButton.qml:42
546#: qml/Wizard/StackButton.qml:42
529#, qt-format
547#, qt-format
530msgid "%1 〉"
548msgid "%1 〉"
531−msgstr ""
549+msgstr "%1 〉"
532
550
533#~ msgid "Confirm"
551#~ msgid "Confirm"
534#~ msgstr "Επιβεβαίωση"
552#~ msgstr "Επιβεβαίωση"
po/en_AU.po+1−1
@@ -15,7 +15,7 @@ msgstr ""
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-01-29 06:00+0000\n"
18+"X-Launchpad-Export-Date: 2015-01-30 07:08+0000\n"
19"X-Generator: Launchpad (build 17306)\n"
19"X-Generator: Launchpad (build 17306)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:130
21#: plugins/LightDM/Greeter.cpp:130
po/es.po+3−3
@@ -8,14 +8,14 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2015-01-16 03:09+0000\n"
11+"PO-Revision-Date: 2015-02-01 03:20+0000\n"
12"Last-Translator: Adolfo Jayme <fitoschido@gmail.com>\n"
12"Last-Translator: Adolfo Jayme <fitoschido@gmail.com>\n"
13"Language-Team: Spanish <es@li.org>\n"
13"Language-Team: Spanish <es@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
18+"X-Launchpad-Export-Date: 2015-02-02 06:54+0000\n"
19"X-Generator: Launchpad (build 17306)\n"
19"X-Generator: Launchpad (build 17306)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:130
21#: plugins/LightDM/Greeter.cpp:130
@@ -290,7 +290,7 @@ msgstr "Falló, toque para reintentar"
290
290
291#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
291#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
292msgid "Reply"
292msgid "Reply"
293−msgstr ""
293+msgstr "Responder"
294
294
295#: qml/Panel/Indicators/ModemInfoItem.qml:105
295#: qml/Panel/Indicators/ModemInfoItem.qml:105
296msgid "Unlock SIM"
296msgid "Unlock SIM"
po/fa.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2015-01-09 15:59+0000\n"
11+"PO-Revision-Date: 2015-02-04 01:20+0000\n"
12"Last-Translator: Danial Behzadi <dani.behzi@gmail.com>\n"
12"Last-Translator: Danial Behzadi <dani.behzi@gmail.com>\n"
13"Language-Team: Persian <fa@li.org>\n"
13"Language-Team: Persian <fa@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
18+"X-Launchpad-Export-Date: 2015-02-05 07:41+0000\n"
19−"X-Generator: Launchpad (build 17306)\n"
19+"X-Generator: Launchpad (build 17331)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:130
21#: plugins/LightDM/Greeter.cpp:130
22msgid "Password: "
22msgid "Password: "
@@ -283,7 +283,7 @@ msgstr "شکست خورد، برای تلاش دوباره ضربه بزنید"
283
283
284#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
284#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
285msgid "Reply"
285msgid "Reply"
286−msgstr ""
286+msgstr "پاسخ"
287
287
288#: qml/Panel/Indicators/ModemInfoItem.qml:105
288#: qml/Panel/Indicators/ModemInfoItem.qml:105
289msgid "Unlock SIM"
289msgid "Unlock SIM"
po/fi.po+3−3
@@ -8,14 +8,14 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2014-12-14 15:37+0000\n"
11+"PO-Revision-Date: 2015-01-29 09:21+0000\n"
12"Last-Translator: Jiri Grönroos <Unknown>\n"
12"Last-Translator: Jiri Grönroos <Unknown>\n"
13"Language-Team: Finnish <fi@li.org>\n"
13"Language-Team: Finnish <fi@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
18+"X-Launchpad-Export-Date: 2015-01-30 07:08+0000\n"
19"X-Generator: Launchpad (build 17306)\n"
19"X-Generator: Launchpad (build 17306)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:130
21#: plugins/LightDM/Greeter.cpp:130
@@ -285,7 +285,7 @@ msgstr "Epäonnistunut, napauta yrittääksesi uudelleen"
285
285
286#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
286#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
287msgid "Reply"
287msgid "Reply"
288−msgstr ""
288+msgstr "Vastaa"
289
289
290#: qml/Panel/Indicators/ModemInfoItem.qml:105
290#: qml/Panel/Indicators/ModemInfoItem.qml:105
291msgid "Unlock SIM"
291msgid "Unlock SIM"
po/fr.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2014-12-10 22:01+0000\n"
11+"PO-Revision-Date: 2015-02-03 20:11+0000\n"
12"Last-Translator: Jean Marc <Unknown>\n"
12"Last-Translator: Jean Marc <Unknown>\n"
13"Language-Team: French <fr@li.org>\n"
13"Language-Team: French <fr@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
18+"X-Launchpad-Export-Date: 2015-02-04 07:13+0000\n"
19−"X-Generator: Launchpad (build 17306)\n"
19+"X-Generator: Launchpad (build 17331)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:130
21#: plugins/LightDM/Greeter.cpp:130
22msgid "Password: "
22msgid "Password: "
@@ -294,7 +294,7 @@ msgstr "Échec, appuyez pour réessayer"
294
294
295#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
295#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
296msgid "Reply"
296msgid "Reply"
297−msgstr ""
297+msgstr "Répondre"
298
298
299#: qml/Panel/Indicators/ModemInfoItem.qml:105
299#: qml/Panel/Indicators/ModemInfoItem.qml:105
300msgid "Unlock SIM"
300msgid "Unlock SIM"
po/gd.po+45−40
@@ -8,7 +8,7 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2014-11-09 08:18+0000\n"
11+"PO-Revision-Date: 2015-02-11 15:01+0000\n"
12"Last-Translator: GunChleoc <Unknown>\n"
12"Last-Translator: GunChleoc <Unknown>\n"
13"Language-Team: Fòram na Gàidhlig\n"
13"Language-Team: Fòram na Gàidhlig\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : "
17"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : "
18"(n > 2 && n < 20) ? 2 : 3;\n"
18"(n > 2 && n < 20) ? 2 : 3;\n"
19−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
19+"X-Launchpad-Export-Date: 2015-02-13 06:54+0000\n"
20−"X-Generator: Launchpad (build 17306)\n"
20+"X-Generator: Launchpad (build 17336)\n"
21"Language: gd\n"
21"Language: gd\n"
22
22
23#: plugins/LightDM/Greeter.cpp:130
23#: plugins/LightDM/Greeter.cpp:130
@@ -241,11 +241,11 @@ msgstr "Leig às airson ath-nuadhachadh…"
241
241
242#: qml/Dash/ScopesList.qml:66
242#: qml/Dash/ScopesList.qml:66
243msgid "Manage"
243msgid "Manage"
244−msgstr ""
244+msgstr "Stiùirich"
245
245
246#: qml/Dash/ScopesList.qml:112
246#: qml/Dash/ScopesList.qml:112
247msgid "Home"
247msgid "Home"
248−msgstr ""
248+msgstr "Dhachaigh"
249
249
250#: qml/Dash/ScopesList.qml:113
250#: qml/Dash/ScopesList.qml:113
251msgid "Also installed"
251msgid "Also installed"
@@ -297,7 +297,7 @@ msgstr "Dh'fhàillig leis, thoir gnogag gus feuchainn a-rithist"
297
297
298#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
298#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
299msgid "Reply"
299msgid "Reply"
300−msgstr ""
300+msgstr "Freagair"
301
301
302#: qml/Panel/Indicators/ModemInfoItem.qml:105
302#: qml/Panel/Indicators/ModemInfoItem.qml:105
303msgid "Unlock SIM"
303msgid "Unlock SIM"
@@ -369,58 +369,58 @@ msgstr ""
369
369
370#: qml/Wizard/Page.qml:89
370#: qml/Wizard/Page.qml:89
371msgid "Back"
371msgid "Back"
372−msgstr ""
372+msgstr "Air ais"
373
373
374#: qml/Wizard/Pages/10-welcome.qml:27
374#: qml/Wizard/Pages/10-welcome.qml:27
375msgid "Hi!"
375msgid "Hi!"
376−msgstr ""
376+msgstr "Shin thu!"
377
377
378#: qml/Wizard/Pages/10-welcome.qml:44
378#: qml/Wizard/Pages/10-welcome.qml:44
379msgid "Welcome to your Ubuntu phone."
379msgid "Welcome to your Ubuntu phone."
380−msgstr ""
380+msgstr "Fàilte dhan fhòn Ubuntu agad."
381
381
382#: qml/Wizard/Pages/10-welcome.qml:52
382#: qml/Wizard/Pages/10-welcome.qml:52
383msgid "Let’s get started."
383msgid "Let’s get started."
384−msgstr ""
384+msgstr "Tòisicheamaid."
385
385
386#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
386#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
387#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
387#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
388#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
388#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
389#: qml/Wizard/Pages/passwd-set.qml:90
389#: qml/Wizard/Pages/passwd-set.qml:90
390msgid "Continue"
390msgid "Continue"
391−msgstr ""
391+msgstr "Lean air adhart"
392
392
393#: qml/Wizard/Pages/20-sim.qml:25
393#: qml/Wizard/Pages/20-sim.qml:25
394msgid "Add a SIM card and restart your device"
394msgid "Add a SIM card and restart your device"
395−msgstr ""
395+msgstr "Cuir cairt SIM ris 's tòisich an uidheam agad às ùr"
396
396
397#: qml/Wizard/Pages/20-sim.qml:55
397#: qml/Wizard/Pages/20-sim.qml:55
398msgid "Without it, you won’t be able to make calls or use text messaging."
398msgid "Without it, you won’t be able to make calls or use text messaging."
399−msgstr ""
399+msgstr "Chan urrainn dhut gairm no teachdaireachd a chur às a h-aonais."
400
400
401#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
401#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
402msgid "Skip"
402msgid "Skip"
403−msgstr ""
403+msgstr "Leum thairis air"
404
404
405#: qml/Wizard/Pages/30-passwd-type.qml:39
405#: qml/Wizard/Pages/30-passwd-type.qml:39
406msgid "Lock security"
406msgid "Lock security"
407−msgstr ""
407+msgstr "Glas tèarainteachd"
408
408
409#: qml/Wizard/Pages/30-passwd-type.qml:74
409#: qml/Wizard/Pages/30-passwd-type.qml:74
410msgid "Please select how you’d like to unlock your phone."
410msgid "Please select how you’d like to unlock your phone."
411−msgstr ""
411+msgstr "Tagh an dòigh sa tha thu airson a' ghlas a thoirt far an fhòn agad."
412
412
413#: qml/Wizard/Pages/30-passwd-type.qml:97
413#: qml/Wizard/Pages/30-passwd-type.qml:97
414msgid "Swipe"
414msgid "Swipe"
415−msgstr ""
415+msgstr "Grad-shlaighd"
416
416
417#: qml/Wizard/Pages/30-passwd-type.qml:98
417#: qml/Wizard/Pages/30-passwd-type.qml:98
418msgid "No security"
418msgid "No security"
419−msgstr ""
419+msgstr "Gun tèarainteachd"
420
420
421#: qml/Wizard/Pages/30-passwd-type.qml:100
421#: qml/Wizard/Pages/30-passwd-type.qml:100
422msgid "Passcode"
422msgid "Passcode"
423−msgstr ""
423+msgstr "Còd-faire"
424
424
425#: qml/Wizard/Pages/30-passwd-type.qml:101
425#: qml/Wizard/Pages/30-passwd-type.qml:101
426msgid "4 digits only"
426msgid "4 digits only"
@@ -428,31 +428,31 @@ msgstr ""
428
428
429#: qml/Wizard/Pages/30-passwd-type.qml:103
429#: qml/Wizard/Pages/30-passwd-type.qml:103
430msgid "Passphrase"
430msgid "Passphrase"
431−msgstr ""
431+msgstr "Abairt-fhaire"
432
432
433#: qml/Wizard/Pages/30-passwd-type.qml:104
433#: qml/Wizard/Pages/30-passwd-type.qml:104
434msgid "Numbers and letters"
434msgid "Numbers and letters"
435−msgstr ""
435+msgstr "Àireamhan 's litrichean"
436
436
437#: qml/Wizard/Pages/40-wifi.qml:28
437#: qml/Wizard/Pages/40-wifi.qml:28
438msgid "Connect to Wi‑Fi"
438msgid "Connect to Wi‑Fi"
439−msgstr ""
439+msgstr "Ceangail ri Wi-Fi"
440
440
441#: qml/Wizard/Pages/40-wifi.qml:147
441#: qml/Wizard/Pages/40-wifi.qml:147
442msgid "Available networks…"
442msgid "Available networks…"
443−msgstr ""
443+msgstr "Lìonraidhean ri làimh…"
444
444
445#: qml/Wizard/Pages/40-wifi.qml:148
445#: qml/Wizard/Pages/40-wifi.qml:148
446msgid "No available networks."
446msgid "No available networks."
447−msgstr ""
447+msgstr "Chan eil lìonra ri làimh."
448
448
449#: qml/Wizard/Pages/50-location.qml:27
449#: qml/Wizard/Pages/50-location.qml:27
450msgid "Location"
450msgid "Location"
451−msgstr ""
451+msgstr "Àite"
452
452
453#: qml/Wizard/Pages/50-location.qml:62
453#: qml/Wizard/Pages/50-location.qml:62
454msgid "Let the phone detect your location:"
454msgid "Let the phone detect your location:"
455−msgstr ""
455+msgstr "Faodaidh am fòn aithneachadh far a bheil thu:"
456
456
457#: qml/Wizard/Pages/50-location.qml:69
457#: qml/Wizard/Pages/50-location.qml:69
458msgid "Using GPS only (less accurate)"
458msgid "Using GPS only (less accurate)"
@@ -471,7 +471,7 @@ msgstr ""
471
471
472#: qml/Wizard/Pages/50-location.qml:112
472#: qml/Wizard/Pages/50-location.qml:112
473msgid "Not at all"
473msgid "Not at all"
474−msgstr ""
474+msgstr "Chan fhaod idir"
475
475
476#: qml/Wizard/Pages/50-location.qml:124
476#: qml/Wizard/Pages/50-location.qml:124
477msgid "You can change your mind later in <b>System Settings</b>."
477msgid "You can change your mind later in <b>System Settings</b>."
@@ -479,70 +479,75 @@ msgstr ""
479
479
480#: qml/Wizard/Pages/60-reporting.qml:24
480#: qml/Wizard/Pages/60-reporting.qml:24
481msgid "Improving your experience"
481msgid "Improving your experience"
482−msgstr ""
482+msgstr "A' cur piseach air a' ghnàth-eòlas agad"
483
483
484#: qml/Wizard/Pages/60-reporting.qml:36
484#: qml/Wizard/Pages/60-reporting.qml:36
485msgid ""
485msgid ""
486"Your phone is set up to automatically report errors to Canonical and its "
486"Your phone is set up to automatically report errors to Canonical and its "
487"partners, the makers of the operating system."
487"partners, the makers of the operating system."
488msgstr ""
488msgstr ""
489+"Chaidh am fòn agad a shuidheachadh gus innse mu mhearachdan gu fèin-"
490+"obrachail do Chanonical 's a chom-pàirtichean a rinn an siostam-obrachaidh "
491+"seo."
489
492
490#: qml/Wizard/Pages/60-reporting.qml:43
493#: qml/Wizard/Pages/60-reporting.qml:43
491msgid ""
494msgid ""
492"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
495"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
493"Privacy</b>"
496"Privacy</b>"
494msgstr ""
497msgstr ""
498+"Gabhaidh seo cur à comas ann an <b>Roghainnean an t-siostaim</b> fo "
499+"<b>Thèarainteachd ⁊ prìobhaideachd</b>"
495
500
496#: qml/Wizard/Pages/80-finished.qml:24
501#: qml/Wizard/Pages/80-finished.qml:24
497msgid "All done"
502msgid "All done"
498−msgstr ""
503+msgstr "Coileanta"
499
504
500#: qml/Wizard/Pages/80-finished.qml:39
505#: qml/Wizard/Pages/80-finished.qml:39
501msgid "Nice work!"
506msgid "Nice work!"
502−msgstr ""
507+msgstr "Shin thu!"
503
508
504#: qml/Wizard/Pages/80-finished.qml:46
509#: qml/Wizard/Pages/80-finished.qml:46
505msgid "Your phone is now ready to use."
510msgid "Your phone is now ready to use."
506−msgstr ""
511+msgstr "Gabhaidh am fòn agad cleachdadh a-nis."
507
512
508#: qml/Wizard/Pages/80-finished.qml:53
513#: qml/Wizard/Pages/80-finished.qml:53
509msgid "Finish"
514msgid "Finish"
510−msgstr ""
515+msgstr "Crìochnaich"
511
516
512#: qml/Wizard/Pages/here-terms.qml:27
517#: qml/Wizard/Pages/here-terms.qml:27
513msgid "Terms & Conditions"
518msgid "Terms & Conditions"
514−msgstr ""
519+msgstr "Teirmichean ⁊ cumhaichean"
515
520
516#: qml/Wizard/Pages/passwd-confirm.qml:49
521#: qml/Wizard/Pages/passwd-confirm.qml:49
517msgid "Confirm passphrase"
522msgid "Confirm passphrase"
518−msgstr ""
523+msgstr "Dearbhaich an abairt-fhaire"
519
524
520#: qml/Wizard/Pages/passwd-confirm.qml:50
525#: qml/Wizard/Pages/passwd-confirm.qml:50
521msgid "Confirm passcode"
526msgid "Confirm passcode"
522−msgstr ""
527+msgstr "Dearbhaich an còd-faire"
523
528
524#: qml/Wizard/Pages/passwd-confirm.qml:53
529#: qml/Wizard/Pages/passwd-confirm.qml:53
525#: qml/Wizard/Pages/passwd-confirm.qml:54
530#: qml/Wizard/Pages/passwd-confirm.qml:54
526msgid "Please try again."
531msgid "Please try again."
527−msgstr ""
532+msgstr "Feuch ris a-rithist."
528
533
529#: qml/Wizard/Pages/passwd-set.qml:61
534#: qml/Wizard/Pages/passwd-set.qml:61
530msgid "Choose your passcode"
535msgid "Choose your passcode"
531−msgstr ""
536+msgstr "Tagh an còd-faire agad"
532
537
533#: qml/Wizard/Pages/passwd-set.qml:68
538#: qml/Wizard/Pages/passwd-set.qml:68
534msgid "Passphrase must be 4 characters long"
539msgid "Passphrase must be 4 characters long"
535−msgstr ""
540+msgstr "Feumaidh an abairt-fhaire a bhith 4 caractaran a dh'fhaid"
536
541
537#: qml/Wizard/StackButton.qml:39
542#: qml/Wizard/StackButton.qml:39
538#, qt-format
543#, qt-format
539msgid "〈 %1"
544msgid "〈 %1"
540−msgstr ""
545+msgstr "〈 %1"
541
546
542#: qml/Wizard/StackButton.qml:42
547#: qml/Wizard/StackButton.qml:42
543#, qt-format
548#, qt-format
544msgid "%1 〉"
549msgid "%1 〉"
545−msgstr ""
550+msgstr "%1 〉"
546
551
547#~ msgid "Confirm"
552#~ msgid "Confirm"
548#~ msgstr "Dearbhaich"
553#~ msgstr "Dearbhaich"
po/gl.po+7−7
@@ -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-01-22 11:09+0100\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2015-01-28 10:25+0000\n"
11+"PO-Revision-Date: 2015-02-11 10:36+0000\n"
12"Last-Translator: Marcos Lans <Unknown>\n"
12"Last-Translator: Marcos Lans <Unknown>\n"
13"Language-Team: Galician <gl@li.org>\n"
13"Language-Team: Galician <gl@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-01-29 06:00+0000\n"
18+"X-Launchpad-Export-Date: 2015-02-13 06:54+0000\n"
19−"X-Generator: Launchpad (build 17306)\n"
19+"X-Generator: Launchpad (build 17336)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:130
21#: plugins/LightDM/Greeter.cpp:130
22msgid "Password: "
22msgid "Password: "
@@ -56,7 +56,7 @@ msgstr "Saír da sesión"
56
56
57#: qml/Components/Dialogs.qml:87
57#: qml/Components/Dialogs.qml:87
58msgid "Are you sure you want to log out?"
58msgid "Are you sure you want to log out?"
59−msgstr "Está seguro de querer pechar a sesión?"
59+msgstr "Confirma que desexa pechar a sesión?"
60
60
61#: qml/Components/Dialogs.qml:89 qml/Components/Dialogs.qml:111
61#: qml/Components/Dialogs.qml:89 qml/Components/Dialogs.qml:111
62#: qml/Components/Dialogs.qml:134
62#: qml/Components/Dialogs.qml:134
@@ -74,7 +74,7 @@ msgstr "Apagar"
74
74
75#: qml/Components/Dialogs.qml:109
75#: qml/Components/Dialogs.qml:109
76msgid "Are you sure you want to shut down?"
76msgid "Are you sure you want to shut down?"
77−msgstr "Está seguro de querer apagar?"
77+msgstr "Confirma que desexa apagar?"
78
78
79#: qml/Components/Dialogs.qml:131
79#: qml/Components/Dialogs.qml:131
80msgid "Reboot"
80msgid "Reboot"
@@ -82,7 +82,7 @@ msgstr "Reiniciar"
82
82
83#: qml/Components/Dialogs.qml:132
83#: qml/Components/Dialogs.qml:132
84msgid "Are you sure you want to reboot?"
84msgid "Are you sure you want to reboot?"
85−msgstr "Está seguro de querer reiniciar?"
85+msgstr "Confirma que desexa reiniciar?"
86
86
87#: qml/Components/Dialogs.qml:154
87#: qml/Components/Dialogs.qml:154
88msgid "Power"
88msgid "Power"
@@ -92,7 +92,7 @@ msgstr "Enerxía"
92msgid ""
92msgid ""
93"Are you sure you would like\n"
93"Are you sure you would like\n"
94"to power off?"
94"to power off?"
95−msgstr "Está seguro de querer apagar?"
95+msgstr "Confirma que desexa apagar?"
96
96
97#: qml/Components/Dialogs.qml:157
97#: qml/Components/Dialogs.qml:157
98msgid "Power off"
98msgid "Power off"
po/hu.po+3−3
@@ -8,14 +8,14 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2015-01-17 10:51+0000\n"
11+"PO-Revision-Date: 2015-01-29 09:19+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-01-28 06:30+0000\n"
18+"X-Launchpad-Export-Date: 2015-01-30 07:08+0000\n"
19"X-Generator: Launchpad (build 17306)\n"
19"X-Generator: Launchpad (build 17306)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:130
21#: plugins/LightDM/Greeter.cpp:130
@@ -285,7 +285,7 @@ msgstr "Sikertelen, koppintson az újrapróbáláshoz"
285
285
286#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
286#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
287msgid "Reply"
287msgid "Reply"
288−msgstr ""
288+msgstr "Válasz"
289
289
290#: qml/Panel/Indicators/ModemInfoItem.qml:105
290#: qml/Panel/Indicators/ModemInfoItem.qml:105
291msgid "Unlock SIM"
291msgid "Unlock SIM"
po/nl.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-01-22 11:09+0100\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2015-01-17 11:13+0000\n"
11+"PO-Revision-Date: 2015-02-12 20:12+0000\n"
12"Last-Translator: rob <linuxned@gmail.com>\n"
12"Last-Translator: rob <linuxned@gmail.com>\n"
13"Language-Team: Dutch <nl@li.org>\n"
13"Language-Team: Dutch <nl@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
18+"X-Launchpad-Export-Date: 2015-02-13 06:54+0000\n"
19−"X-Generator: Launchpad (build 17306)\n"
19+"X-Generator: Launchpad (build 17336)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:130
21#: plugins/LightDM/Greeter.cpp:130
22msgid "Password: "
22msgid "Password: "
@@ -287,7 +287,7 @@ msgstr "Mislukt, tik om het opnieuw te proberen"
287
287
288#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
288#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
289msgid "Reply"
289msgid "Reply"
290−msgstr ""
290+msgstr "Beantwoorden"
291
291
292#: qml/Panel/Indicators/ModemInfoItem.qml:105
292#: qml/Panel/Indicators/ModemInfoItem.qml:105
293msgid "Unlock SIM"
293msgid "Unlock SIM"
po/oc.ponew file+521
@@ -0,0 +1,521 @@
1+# Occitan (post 1500) translation for unity8
2+# Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11+"PO-Revision-Date: 2015-02-05 20:35+0000\n"
12+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13+"Language-Team: Occitan (post 1500) <oc@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n > 1;\n"
18+"X-Launchpad-Export-Date: 2015-02-07 07:31+0000\n"
19+"X-Generator: Launchpad (build 17331)\n"
20+
21+#: plugins/LightDM/Greeter.cpp:130
22+msgid "Password: "
23+msgstr "Senhal : "
24+
25+#: plugins/Unity/Launcher/launcheritem.cpp:45
26+#: plugins/Unity/Launcher/launcheritem.cpp:100
27+msgid "Pin shortcut"
28+msgstr ""
29+
30+#: plugins/Unity/Launcher/launcheritem.cpp:100
31+msgid "Unpin shortcut"
32+msgstr ""
33+
34+#: qml/Components/DelayedLockscreen.qml:47
35+msgid "Device Locked"
36+msgstr ""
37+
38+#: qml/Components/DelayedLockscreen.qml:62
39+msgid "You have been locked out due to too many failed passphrase attempts."
40+msgstr ""
41+
42+#: qml/Components/DelayedLockscreen.qml:63
43+msgid "You have been locked out due to too many failed passcode attempts."
44+msgstr ""
45+
46+#: qml/Components/DelayedLockscreen.qml:72
47+#, qt-format
48+msgid "Please wait %1 minute and then try again…"
49+msgid_plural "Please wait %1 minutes and then try again…"
50+msgstr[0] ""
51+msgstr[1] ""
52+
53+#: qml/Components/Dialogs.qml:86
54+msgid "Log out"
55+msgstr ""
56+
57+#: qml/Components/Dialogs.qml:87
58+msgid "Are you sure you want to log out?"
59+msgstr ""
60+
61+#: qml/Components/Dialogs.qml:89 qml/Components/Dialogs.qml:111
62+#: qml/Components/Dialogs.qml:134
63+msgid "No"
64+msgstr "Non"
65+
66+#: qml/Components/Dialogs.qml:95 qml/Components/Dialogs.qml:117
67+#: qml/Components/Dialogs.qml:140
68+msgid "Yes"
69+msgstr ""
70+
71+#: qml/Components/Dialogs.qml:108
72+msgid "Shut down"
73+msgstr ""
74+
75+#: qml/Components/Dialogs.qml:109
76+msgid "Are you sure you want to shut down?"
77+msgstr ""
78+
79+#: qml/Components/Dialogs.qml:131
80+msgid "Reboot"
81+msgstr ""
82+
83+#: qml/Components/Dialogs.qml:132
84+msgid "Are you sure you want to reboot?"
85+msgstr ""
86+
87+#: qml/Components/Dialogs.qml:154
88+msgid "Power"
89+msgstr ""
90+
91+#: qml/Components/Dialogs.qml:155
92+msgid ""
93+"Are you sure you would like\n"
94+"to power off?"
95+msgstr ""
96+
97+#: qml/Components/Dialogs.qml:157
98+msgid "Power off"
99+msgstr ""
100+
101+#: qml/Components/Dialogs.qml:166
102+msgid "Restart"
103+msgstr ""
104+
105+#: qml/Components/Dialogs.qml:175
106+msgid "Cancel"
107+msgstr ""
108+
109+#: qml/Components/EdgeDemoOverlay.qml:151
110+msgid "Skip intro"
111+msgstr ""
112+
113+#: qml/Components/EdgeDemo.qml:125
114+msgid "Right edge"
115+msgstr ""
116+
117+#: qml/Components/EdgeDemo.qml:126
118+msgid "Try swiping from the right edge to unlock the phone"
119+msgstr ""
120+
121+#: qml/Components/EdgeDemo.qml:157
122+msgid "Top edge"
123+msgstr ""
124+
125+#: qml/Components/EdgeDemo.qml:158
126+msgid "Try swiping from the top edge to access the indicators"
127+msgstr ""
128+
129+#: qml/Components/EdgeDemo.qml:183
130+msgid "Close"
131+msgstr ""
132+
133+#: qml/Components/EdgeDemo.qml:184
134+msgid "Swipe up again to close the settings screen"
135+msgstr ""
136+
137+#: qml/Components/EdgeDemo.qml:214
138+msgid "Left edge"
139+msgstr ""
140+
141+#: qml/Components/EdgeDemo.qml:215
142+msgid "Swipe from the left to reveal the launcher for quick access to apps"
143+msgstr ""
144+
145+#: qml/Components/EdgeDemo.qml:242
146+msgid "Well done"
147+msgstr ""
148+
149+#: qml/Components/EdgeDemo.qml:243
150+msgid ""
151+"You have now mastered the edge gestures and can start using the "
152+"phone<br><br>Tap on the screen to start"
153+msgstr ""
154+
155+#: qml/Components/Lockscreen.qml:238
156+msgid "Return to Call"
157+msgstr ""
158+
159+#: qml/Components/Lockscreen.qml:238
160+msgid "Emergency Call"
161+msgstr ""
162+
163+#: qml/Components/Lockscreen.qml:270
164+msgid "OK"
165+msgstr ""
166+
167+#: qml/Dash/GenericScopeView.qml:485 qml/Dash/GenericScopeView.qml:642
168+msgid "See less"
169+msgstr ""
170+
171+#: qml/Dash/GenericScopeView.qml:485
172+msgid "See all"
173+msgstr ""
174+
175+#: qml/Dash/GenericScopeView.qml:547 qml/Dash/PageHeader.qml:274
176+#: qml/Panel/SearchIndicator.qml:27
177+msgid "Search"
178+msgstr ""
179+
180+#: qml/Dash/PageHeader.qml:267
181+msgid "Store"
182+msgstr ""
183+
184+#: qml/Dash/PageHeader.qml:284
185+msgid "Settings"
186+msgstr ""
187+
188+#: qml/Dash/PageHeader.qml:291
189+msgid "Remove from Favorites"
190+msgstr ""
191+
192+#: qml/Dash/PageHeader.qml:291
193+msgid "Add to Favorites"
194+msgstr ""
195+
196+#: qml/Dash/Previews/PreviewActionCombo.qml:35
197+msgid "More..."
198+msgstr ""
199+
200+#: qml/Dash/Previews/PreviewActionCombo.qml:35
201+msgid "Less..."
202+msgstr ""
203+
204+#: qml/Dash/Previews/PreviewRatingInput.qml:81
205+msgid "Rate this"
206+msgstr ""
207+
208+#: qml/Dash/Previews/PreviewRatingInput.qml:126
209+msgid "Add a review"
210+msgstr ""
211+
212+#: qml/Dash/Previews/PreviewRatingInput.qml:166
213+msgid "Send"
214+msgstr ""
215+
216+#: qml/Dash/PullToRefreshScopeStyle.qml:55
217+msgid "Pull to refresh…"
218+msgstr ""
219+
220+#: qml/Dash/PullToRefreshScopeStyle.qml:60
221+msgid "Release to refresh…"
222+msgstr ""
223+
224+#: qml/Dash/ScopesList.qml:66
225+msgid "Manage"
226+msgstr ""
227+
228+#: qml/Dash/ScopesList.qml:112
229+msgid "Home"
230+msgstr ""
231+
232+#: qml/Dash/ScopesList.qml:113
233+msgid "Also installed"
234+msgstr ""
235+
236+#: qml/Greeter/Greeter.qml:133
237+msgid "Unlock"
238+msgstr ""
239+
240+#: qml/Notifications/NotificationMenuItemFactory.qml:128
241+msgid "Show password"
242+msgstr ""
243+
244+#: qml/Panel/ActiveCallHint.qml:79
245+msgid "Tap to return to call..."
246+msgstr ""
247+
248+#: qml/Panel/ActiveCallHint.qml:92
249+msgid "Conference"
250+msgstr ""
251+
252+#: qml/Panel/Indicators/MenuItemFactory.qml:643
253+msgid "Nothing is playing"
254+msgstr ""
255+
256+#: qml/Panel/Indicators/MenuItemFactory.qml:791
257+msgid "In queue…"
258+msgstr ""
259+
260+#: qml/Panel/Indicators/MenuItemFactory.qml:795
261+msgid "Downloading"
262+msgstr ""
263+
264+#: qml/Panel/Indicators/MenuItemFactory.qml:797
265+msgid "Paused, tap to resume"
266+msgstr ""
267+
268+#: qml/Panel/Indicators/MenuItemFactory.qml:799
269+msgid "Canceled"
270+msgstr ""
271+
272+#: qml/Panel/Indicators/MenuItemFactory.qml:801
273+msgid "Finished"
274+msgstr ""
275+
276+#: qml/Panel/Indicators/MenuItemFactory.qml:803
277+msgid "Failed, tap to retry"
278+msgstr ""
279+
280+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
281+msgid "Reply"
282+msgstr ""
283+
284+#: qml/Panel/Indicators/ModemInfoItem.qml:105
285+msgid "Unlock SIM"
286+msgstr ""
287+
288+#: qml/Panel/Indicators/RoamingIndication.qml:27
289+msgid "Roaming"
290+msgstr ""
291+
292+#: qml/Shell.qml:390
293+#, qt-format
294+msgid "Enter %1"
295+msgstr ""
296+
297+#: qml/Shell.qml:391 qml/Wizard/Pages/passwd-set.qml:60
298+msgid "Enter passphrase"
299+msgstr ""
300+
301+#: qml/Shell.qml:392
302+msgid "Enter passcode"
303+msgstr ""
304+
305+#: qml/Shell.qml:393
306+#, qt-format
307+msgid "Sorry, incorrect %1"
308+msgstr ""
309+
310+#: qml/Shell.qml:394
311+msgid "Sorry, incorrect passphrase"
312+msgstr ""
313+
314+#: qml/Shell.qml:395
315+msgid "Please re-enter"
316+msgstr ""
317+
318+#: qml/Shell.qml:396
319+msgid "Sorry, incorrect passcode"
320+msgstr ""
321+
322+#: qml/Shell.qml:490 qml/Wizard/Pages/passwd-confirm.qml:53
323+msgid "Sorry, incorrect passphrase."
324+msgstr ""
325+
326+#: qml/Shell.qml:491 qml/Wizard/Pages/passwd-confirm.qml:54
327+msgid "Sorry, incorrect passcode."
328+msgstr ""
329+
330+#: qml/Shell.qml:492
331+msgid "This will be your last attempt."
332+msgstr ""
333+
334+#: qml/Shell.qml:494
335+msgid ""
336+"If passphrase is entered incorrectly, your phone will conduct a factory "
337+"reset and all personal data will be deleted."
338+msgstr ""
339+
340+#: qml/Shell.qml:495
341+msgid ""
342+"If passcode is entered incorrectly, your phone will conduct a factory reset "
343+"and all personal data will be deleted."
344+msgstr ""
345+
346+#: qml/Wizard/Page.qml:89
347+msgid "Back"
348+msgstr ""
349+
350+#: qml/Wizard/Pages/10-welcome.qml:27
351+msgid "Hi!"
352+msgstr ""
353+
354+#: qml/Wizard/Pages/10-welcome.qml:44
355+msgid "Welcome to your Ubuntu phone."
356+msgstr ""
357+
358+#: qml/Wizard/Pages/10-welcome.qml:52
359+msgid "Let’s get started."
360+msgstr ""
361+
362+#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
363+#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
364+#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
365+#: qml/Wizard/Pages/passwd-set.qml:90
366+msgid "Continue"
367+msgstr ""
368+
369+#: qml/Wizard/Pages/20-sim.qml:25
370+msgid "Add a SIM card and restart your device"
371+msgstr ""
372+
373+#: qml/Wizard/Pages/20-sim.qml:55
374+msgid "Without it, you won’t be able to make calls or use text messaging."
375+msgstr ""
376+
377+#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
378+msgid "Skip"
379+msgstr ""
380+
381+#: qml/Wizard/Pages/30-passwd-type.qml:39
382+msgid "Lock security"
383+msgstr ""
384+
385+#: qml/Wizard/Pages/30-passwd-type.qml:74
386+msgid "Please select how you’d like to unlock your phone."
387+msgstr ""
388+
389+#: qml/Wizard/Pages/30-passwd-type.qml:97
390+msgid "Swipe"
391+msgstr ""
392+
393+#: qml/Wizard/Pages/30-passwd-type.qml:98
394+msgid "No security"
395+msgstr ""
396+
397+#: qml/Wizard/Pages/30-passwd-type.qml:100
398+msgid "Passcode"
399+msgstr ""
400+
401+#: qml/Wizard/Pages/30-passwd-type.qml:101
402+msgid "4 digits only"
403+msgstr ""
404+
405+#: qml/Wizard/Pages/30-passwd-type.qml:103
406+msgid "Passphrase"
407+msgstr ""
408+
409+#: qml/Wizard/Pages/30-passwd-type.qml:104
410+msgid "Numbers and letters"
411+msgstr ""
412+
413+#: qml/Wizard/Pages/40-wifi.qml:28
414+msgid "Connect to Wi‑Fi"
415+msgstr ""
416+
417+#: qml/Wizard/Pages/40-wifi.qml:147
418+msgid "Available networks…"
419+msgstr ""
420+
421+#: qml/Wizard/Pages/40-wifi.qml:148
422+msgid "No available networks."
423+msgstr ""
424+
425+#: qml/Wizard/Pages/50-location.qml:27
426+msgid "Location"
427+msgstr ""
428+
429+#: qml/Wizard/Pages/50-location.qml:62
430+msgid "Let the phone detect your location:"
431+msgstr ""
432+
433+#: qml/Wizard/Pages/50-location.qml:69
434+msgid "Using GPS only (less accurate)"
435+msgstr ""
436+
437+#: qml/Wizard/Pages/50-location.qml:86
438+msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
439+msgstr ""
440+
441+#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
442+#: qml/Wizard/Pages/50-location.qml:103
443+msgid ""
444+"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
445+"conditions</a>."
446+msgstr ""
447+
448+#: qml/Wizard/Pages/50-location.qml:112
449+msgid "Not at all"
450+msgstr ""
451+
452+#: qml/Wizard/Pages/50-location.qml:124
453+msgid "You can change your mind later in <b>System Settings</b>."
454+msgstr ""
455+
456+#: qml/Wizard/Pages/60-reporting.qml:24
457+msgid "Improving your experience"
458+msgstr ""
459+
460+#: qml/Wizard/Pages/60-reporting.qml:36
461+msgid ""
462+"Your phone is set up to automatically report errors to Canonical and its "
463+"partners, the makers of the operating system."
464+msgstr ""
465+
466+#: qml/Wizard/Pages/60-reporting.qml:43
467+msgid ""
468+"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
469+"Privacy</b>"
470+msgstr ""
471+
472+#: qml/Wizard/Pages/80-finished.qml:24
473+msgid "All done"
474+msgstr ""
475+
476+#: qml/Wizard/Pages/80-finished.qml:39
477+msgid "Nice work!"
478+msgstr ""
479+
480+#: qml/Wizard/Pages/80-finished.qml:46
481+msgid "Your phone is now ready to use."
482+msgstr ""
483+
484+#: qml/Wizard/Pages/80-finished.qml:53
485+msgid "Finish"
486+msgstr ""
487+
488+#: qml/Wizard/Pages/here-terms.qml:27
489+msgid "Terms & Conditions"
490+msgstr ""
491+
492+#: qml/Wizard/Pages/passwd-confirm.qml:49
493+msgid "Confirm passphrase"
494+msgstr ""
495+
496+#: qml/Wizard/Pages/passwd-confirm.qml:50
497+msgid "Confirm passcode"
498+msgstr ""
499+
500+#: qml/Wizard/Pages/passwd-confirm.qml:53
501+#: qml/Wizard/Pages/passwd-confirm.qml:54
502+msgid "Please try again."
503+msgstr ""
504+
505+#: qml/Wizard/Pages/passwd-set.qml:61
506+msgid "Choose your passcode"
507+msgstr ""
508+
509+#: qml/Wizard/Pages/passwd-set.qml:68
510+msgid "Passphrase must be 4 characters long"
511+msgstr ""
512+
513+#: qml/Wizard/StackButton.qml:39
514+#, qt-format
515+msgid "〈 %1"
516+msgstr ""
517+
518+#: qml/Wizard/StackButton.qml:42
519+#, qt-format
520+msgid "%1 〉"
521+msgstr ""
po/pl.po+95−80
@@ -8,20 +8,20 @@ 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-01-22 11:09+0100\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2014-08-18 20:25+0000\n"
11+"PO-Revision-Date: 2015-02-14 23:41+0000\n"
12−"Last-Translator: Paweł Stroka <pawstr@gmail.com>\n"
12+"Last-Translator: GTriderXC <gtriderxc@yahoo.com>\n"
13"Language-Team: Polish <pl@li.org>\n"
13"Language-Team: Polish <pl@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
17"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
18"|| n%100>=20) ? 1 : 2;\n"
18"|| n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
19+"X-Launchpad-Export-Date: 2015-02-15 06:35+0000\n"
20−"X-Generator: Launchpad (build 17306)\n"
20+"X-Generator: Launchpad (build 17341)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:130
22#: plugins/LightDM/Greeter.cpp:130
23msgid "Password: "
23msgid "Password: "
24−msgstr ""
24+msgstr "Hasło: "
25
25
26#: plugins/Unity/Launcher/launcheritem.cpp:45
26#: plugins/Unity/Launcher/launcheritem.cpp:45
27#: plugins/Unity/Launcher/launcheritem.cpp:100
27#: plugins/Unity/Launcher/launcheritem.cpp:100
@@ -34,78 +34,82 @@ msgstr "Odepnij skrót"
34
34
35#: qml/Components/DelayedLockscreen.qml:47
35#: qml/Components/DelayedLockscreen.qml:47
36msgid "Device Locked"
36msgid "Device Locked"
37−msgstr ""
37+msgstr "Urządzenie zablokowane"
38
38
39#: qml/Components/DelayedLockscreen.qml:62
39#: qml/Components/DelayedLockscreen.qml:62
40msgid "You have been locked out due to too many failed passphrase attempts."
40msgid "You have been locked out due to too many failed passphrase attempts."
41msgstr ""
41msgstr ""
42+"Zostałeś zablokowany z powodu zbyt wielu błędnych prób podania hasła."
42
43
43#: qml/Components/DelayedLockscreen.qml:63
44#: qml/Components/DelayedLockscreen.qml:63
44msgid "You have been locked out due to too many failed passcode attempts."
45msgid "You have been locked out due to too many failed passcode attempts."
45−msgstr ""
46+msgstr "Zablokowano z powodu zbyt wielu prób podania niepoprawnego hasła"
46
47
47#: qml/Components/DelayedLockscreen.qml:72
48#: qml/Components/DelayedLockscreen.qml:72
48#, qt-format
49#, qt-format
49msgid "Please wait %1 minute and then try again…"
50msgid "Please wait %1 minute and then try again…"
50msgid_plural "Please wait %1 minutes and then try again…"
51msgid_plural "Please wait %1 minutes and then try again…"
51−msgstr[0] ""
52+msgstr[0] "Poczekaj %1 minutę i spróbuj ponownie…"
52−msgstr[1] ""
53+msgstr[1] "Poczekaj %1 minuty i spróbuj ponownie…"
54+msgstr[2] "Poczekaj %1 minut i spróbuj ponownie…"
53
55
54#: qml/Components/Dialogs.qml:86
56#: qml/Components/Dialogs.qml:86
55msgid "Log out"
57msgid "Log out"
56−msgstr ""
58+msgstr "Wyloguj"
57
59
58#: qml/Components/Dialogs.qml:87
60#: qml/Components/Dialogs.qml:87
59msgid "Are you sure you want to log out?"
61msgid "Are you sure you want to log out?"
60−msgstr ""
62+msgstr "Czy na pewno wylogować?"
61
63
62#: qml/Components/Dialogs.qml:89 qml/Components/Dialogs.qml:111
64#: qml/Components/Dialogs.qml:89 qml/Components/Dialogs.qml:111
63#: qml/Components/Dialogs.qml:134
65#: qml/Components/Dialogs.qml:134
64msgid "No"
66msgid "No"
65−msgstr ""
67+msgstr "Nie"
66
68
67#: qml/Components/Dialogs.qml:95 qml/Components/Dialogs.qml:117
69#: qml/Components/Dialogs.qml:95 qml/Components/Dialogs.qml:117
68#: qml/Components/Dialogs.qml:140
70#: qml/Components/Dialogs.qml:140
69msgid "Yes"
71msgid "Yes"
70−msgstr ""
72+msgstr "Tak"
71
73
72#: qml/Components/Dialogs.qml:108
74#: qml/Components/Dialogs.qml:108
73msgid "Shut down"
75msgid "Shut down"
74−msgstr ""
76+msgstr "Wyłącz"
75
77
76#: qml/Components/Dialogs.qml:109
78#: qml/Components/Dialogs.qml:109
77msgid "Are you sure you want to shut down?"
79msgid "Are you sure you want to shut down?"
78−msgstr ""
80+msgstr "Czy na pewno wyłączyć?"
79
81
80#: qml/Components/Dialogs.qml:131
82#: qml/Components/Dialogs.qml:131
81msgid "Reboot"
83msgid "Reboot"
82−msgstr ""
84+msgstr "Uruchom ponownie"
83
85
84#: qml/Components/Dialogs.qml:132
86#: qml/Components/Dialogs.qml:132
85msgid "Are you sure you want to reboot?"
87msgid "Are you sure you want to reboot?"
86−msgstr ""
88+msgstr "Czy na pewno uruchomić ponownie?"
87
89
88#: qml/Components/Dialogs.qml:154
90#: qml/Components/Dialogs.qml:154
89msgid "Power"
91msgid "Power"
90−msgstr ""
92+msgstr "Zasilanie"
91
93
92#: qml/Components/Dialogs.qml:155
94#: qml/Components/Dialogs.qml:155
93msgid ""
95msgid ""
94"Are you sure you would like\n"
96"Are you sure you would like\n"
95"to power off?"
97"to power off?"
96msgstr ""
98msgstr ""
99+"Jesteś pewny że chcesz\n"
100+"wyłączyć?"
97
101
98#: qml/Components/Dialogs.qml:157
102#: qml/Components/Dialogs.qml:157
99msgid "Power off"
103msgid "Power off"
100−msgstr ""
104+msgstr "Wyłącz"
101
105
102#: qml/Components/Dialogs.qml:166
106#: qml/Components/Dialogs.qml:166
103msgid "Restart"
107msgid "Restart"
104−msgstr ""
108+msgstr "Uruchom ponownie"
105
109
106#: qml/Components/Dialogs.qml:175
110#: qml/Components/Dialogs.qml:175
107msgid "Cancel"
111msgid "Cancel"
108−msgstr ""
112+msgstr "Anuluj"
109
113
110#: qml/Components/EdgeDemoOverlay.qml:151
114#: qml/Components/EdgeDemoOverlay.qml:151
111msgid "Skip intro"
115msgid "Skip intro"
@@ -159,7 +163,7 @@ msgstr ""
159
163
160#: qml/Components/Lockscreen.qml:238
164#: qml/Components/Lockscreen.qml:238
161msgid "Return to Call"
165msgid "Return to Call"
162−msgstr ""
166+msgstr "Powróć do rozmowy"
163
167
164#: qml/Components/Lockscreen.qml:238
168#: qml/Components/Lockscreen.qml:238
165msgid "Emergency Call"
169msgid "Emergency Call"
@@ -184,19 +188,19 @@ msgstr "Szukaj"
184
188
185#: qml/Dash/PageHeader.qml:267
189#: qml/Dash/PageHeader.qml:267
186msgid "Store"
190msgid "Store"
187−msgstr ""
191+msgstr "Zachowaj"
188
192
189#: qml/Dash/PageHeader.qml:284
193#: qml/Dash/PageHeader.qml:284
190msgid "Settings"
194msgid "Settings"
191−msgstr ""
195+msgstr "Ustawienia"
192
196
193#: qml/Dash/PageHeader.qml:291
197#: qml/Dash/PageHeader.qml:291
194msgid "Remove from Favorites"
198msgid "Remove from Favorites"
195−msgstr ""
199+msgstr "Usuń z ulubionych"
196
200
197#: qml/Dash/PageHeader.qml:291
201#: qml/Dash/PageHeader.qml:291
198msgid "Add to Favorites"
202msgid "Add to Favorites"
199−msgstr ""
203+msgstr "Dodaj do ulubionych"
200
204
201#: qml/Dash/Previews/PreviewActionCombo.qml:35
205#: qml/Dash/Previews/PreviewActionCombo.qml:35
202msgid "More..."
206msgid "More..."
@@ -220,15 +224,15 @@ msgstr "Wyślij"
220
224
221#: qml/Dash/PullToRefreshScopeStyle.qml:55
225#: qml/Dash/PullToRefreshScopeStyle.qml:55
222msgid "Pull to refresh…"
226msgid "Pull to refresh…"
223−msgstr ""
227+msgstr "Przeciągnij by odświeżyć"
224
228
225#: qml/Dash/PullToRefreshScopeStyle.qml:60
229#: qml/Dash/PullToRefreshScopeStyle.qml:60
226msgid "Release to refresh…"
230msgid "Release to refresh…"
227−msgstr ""
231+msgstr "Zwolnij by odświeżyć"
228
232
229#: qml/Dash/ScopesList.qml:66
233#: qml/Dash/ScopesList.qml:66
230msgid "Manage"
234msgid "Manage"
231−msgstr ""
235+msgstr "Zarządzaj"
232
236
233#: qml/Dash/ScopesList.qml:112
237#: qml/Dash/ScopesList.qml:112
234msgid "Home"
238msgid "Home"
@@ -236,11 +240,11 @@ msgstr ""
236
240
237#: qml/Dash/ScopesList.qml:113
241#: qml/Dash/ScopesList.qml:113
238msgid "Also installed"
242msgid "Also installed"
239−msgstr ""
243+msgstr "Również zainstalowane"
240
244
241#: qml/Greeter/Greeter.qml:133
245#: qml/Greeter/Greeter.qml:133
242msgid "Unlock"
246msgid "Unlock"
243−msgstr ""
247+msgstr "Odblokuj"
244
248
245#: qml/Notifications/NotificationMenuItemFactory.qml:128
249#: qml/Notifications/NotificationMenuItemFactory.qml:128
246msgid "Show password"
250msgid "Show password"
@@ -256,7 +260,7 @@ msgstr "Konferencja"
256
260
257#: qml/Panel/Indicators/MenuItemFactory.qml:643
261#: qml/Panel/Indicators/MenuItemFactory.qml:643
258msgid "Nothing is playing"
262msgid "Nothing is playing"
259−msgstr ""
263+msgstr "Nic nie jest odtwarzane"
260
264
261#: qml/Panel/Indicators/MenuItemFactory.qml:791
265#: qml/Panel/Indicators/MenuItemFactory.qml:791
262msgid "In queue…"
266msgid "In queue…"
@@ -284,45 +288,45 @@ msgstr "Błąd, kliknij aby powtórzyć"
284
288
285#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
289#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
286msgid "Reply"
290msgid "Reply"
287−msgstr ""
291+msgstr "Odpowiedz"
288
292
289#: qml/Panel/Indicators/ModemInfoItem.qml:105
293#: qml/Panel/Indicators/ModemInfoItem.qml:105
290msgid "Unlock SIM"
294msgid "Unlock SIM"
291−msgstr ""
295+msgstr "Odblokuj kartę SIM"
292
296
293#: qml/Panel/Indicators/RoamingIndication.qml:27
297#: qml/Panel/Indicators/RoamingIndication.qml:27
294msgid "Roaming"
298msgid "Roaming"
295−msgstr ""
299+msgstr "Roaming"
296
300
297#: qml/Shell.qml:390
301#: qml/Shell.qml:390
298#, qt-format
302#, qt-format
299msgid "Enter %1"
303msgid "Enter %1"
300−msgstr ""
304+msgstr "Wprowadź %1"
301
305
302#: qml/Shell.qml:391 qml/Wizard/Pages/passwd-set.qml:60
306#: qml/Shell.qml:391 qml/Wizard/Pages/passwd-set.qml:60
303msgid "Enter passphrase"
307msgid "Enter passphrase"
304−msgstr ""
308+msgstr "Wprowadź hasło"
305
309
306#: qml/Shell.qml:392
310#: qml/Shell.qml:392
307msgid "Enter passcode"
311msgid "Enter passcode"
308−msgstr ""
312+msgstr "Podaj kod"
309
313
310#: qml/Shell.qml:393
314#: qml/Shell.qml:393
311#, qt-format
315#, qt-format
312msgid "Sorry, incorrect %1"
316msgid "Sorry, incorrect %1"
313−msgstr ""
317+msgstr "Niestety niepoprawne %1"
314
318
315#: qml/Shell.qml:394
319#: qml/Shell.qml:394
316msgid "Sorry, incorrect passphrase"
320msgid "Sorry, incorrect passphrase"
317−msgstr ""
321+msgstr "Niestety, błędne hasło"
318
322
319#: qml/Shell.qml:395
323#: qml/Shell.qml:395
320msgid "Please re-enter"
324msgid "Please re-enter"
321−msgstr ""
325+msgstr "Proszę wprowadź ponownie"
322
326
323#: qml/Shell.qml:396
327#: qml/Shell.qml:396
324msgid "Sorry, incorrect passcode"
328msgid "Sorry, incorrect passcode"
325−msgstr ""
329+msgstr "Niestety ten kod nie jest poprawny"
326
330
327#: qml/Shell.qml:490 qml/Wizard/Pages/passwd-confirm.qml:53
331#: qml/Shell.qml:490 qml/Wizard/Pages/passwd-confirm.qml:53
328msgid "Sorry, incorrect passphrase."
332msgid "Sorry, incorrect passphrase."
@@ -334,114 +338,119 @@ msgstr "Niestety, błędny kod"
334
338
335#: qml/Shell.qml:492
339#: qml/Shell.qml:492
336msgid "This will be your last attempt."
340msgid "This will be your last attempt."
337−msgstr ""
341+msgstr "To już jest ostatnia próba."
338
342
339#: qml/Shell.qml:494
343#: qml/Shell.qml:494
340msgid ""
344msgid ""
341"If passphrase is entered incorrectly, your phone will conduct a factory "
345"If passphrase is entered incorrectly, your phone will conduct a factory "
342"reset and all personal data will be deleted."
346"reset and all personal data will be deleted."
343msgstr ""
347msgstr ""
348+"Jeśli zostanie wprowadzone błędne hasło, telefon przywróci ustawienia "
349+"fabryczne i wszystkie dane osobiste zostaną usunięte."
344
350
345#: qml/Shell.qml:495
351#: qml/Shell.qml:495
346msgid ""
352msgid ""
347"If passcode is entered incorrectly, your phone will conduct a factory reset "
353"If passcode is entered incorrectly, your phone will conduct a factory reset "
348"and all personal data will be deleted."
354"and all personal data will be deleted."
349msgstr ""
355msgstr ""
356+"Jeśli zostanie wprowadzony błędny kod, telefon przywróci ustawienia "
357+"fabryczne i wszystkie dane osobiste zostaną usunięte."
350
358
351#: qml/Wizard/Page.qml:89
359#: qml/Wizard/Page.qml:89
352msgid "Back"
360msgid "Back"
353−msgstr ""
361+msgstr "Cofnij"
354
362
355#: qml/Wizard/Pages/10-welcome.qml:27
363#: qml/Wizard/Pages/10-welcome.qml:27
356msgid "Hi!"
364msgid "Hi!"
357−msgstr ""
365+msgstr "Witaj!"
358
366
359#: qml/Wizard/Pages/10-welcome.qml:44
367#: qml/Wizard/Pages/10-welcome.qml:44
360msgid "Welcome to your Ubuntu phone."
368msgid "Welcome to your Ubuntu phone."
361−msgstr ""
369+msgstr "Witamy w telefonie z systemem Ubuntu."
362
370
363#: qml/Wizard/Pages/10-welcome.qml:52
371#: qml/Wizard/Pages/10-welcome.qml:52
364msgid "Let’s get started."
372msgid "Let’s get started."
365−msgstr ""
373+msgstr "Zaczynajmy."
366
374
367#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
375#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
368#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
376#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
369#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
377#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
370#: qml/Wizard/Pages/passwd-set.qml:90
378#: qml/Wizard/Pages/passwd-set.qml:90
371msgid "Continue"
379msgid "Continue"
372−msgstr ""
380+msgstr "Kontynuuj"
373
381
374#: qml/Wizard/Pages/20-sim.qml:25
382#: qml/Wizard/Pages/20-sim.qml:25
375msgid "Add a SIM card and restart your device"
383msgid "Add a SIM card and restart your device"
376−msgstr ""
384+msgstr "Włóż kartę SIM i uruchom urządzenie ponownie"
377
385
378#: qml/Wizard/Pages/20-sim.qml:55
386#: qml/Wizard/Pages/20-sim.qml:55
379msgid "Without it, you won’t be able to make calls or use text messaging."
387msgid "Without it, you won’t be able to make calls or use text messaging."
380msgstr ""
388msgstr ""
389+"Inaczej wykonywanie połączeń i wysyłanie wiadomości nie będzie możliwe."
381
390
382#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
391#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
383msgid "Skip"
392msgid "Skip"
384−msgstr ""
393+msgstr "Pomiń"
385
394
386#: qml/Wizard/Pages/30-passwd-type.qml:39
395#: qml/Wizard/Pages/30-passwd-type.qml:39
387msgid "Lock security"
396msgid "Lock security"
388−msgstr ""
397+msgstr "Blokada"
389
398
390#: qml/Wizard/Pages/30-passwd-type.qml:74
399#: qml/Wizard/Pages/30-passwd-type.qml:74
391msgid "Please select how you’d like to unlock your phone."
400msgid "Please select how you’d like to unlock your phone."
392−msgstr ""
401+msgstr "Ustaw proszę jak ma być odblokowywane to urządzenie"
393
402
394#: qml/Wizard/Pages/30-passwd-type.qml:97
403#: qml/Wizard/Pages/30-passwd-type.qml:97
395msgid "Swipe"
404msgid "Swipe"
396−msgstr ""
405+msgstr "Przeciągnięcie"
397
406
398#: qml/Wizard/Pages/30-passwd-type.qml:98
407#: qml/Wizard/Pages/30-passwd-type.qml:98
399msgid "No security"
408msgid "No security"
400−msgstr ""
409+msgstr "Brak zabezpieczenia"
401
410
402#: qml/Wizard/Pages/30-passwd-type.qml:100
411#: qml/Wizard/Pages/30-passwd-type.qml:100
403msgid "Passcode"
412msgid "Passcode"
404−msgstr ""
413+msgstr "Kod"
405
414
406#: qml/Wizard/Pages/30-passwd-type.qml:101
415#: qml/Wizard/Pages/30-passwd-type.qml:101
407msgid "4 digits only"
416msgid "4 digits only"
408−msgstr ""
417+msgstr "Tylko 4 cyfry"
409
418
410#: qml/Wizard/Pages/30-passwd-type.qml:103
419#: qml/Wizard/Pages/30-passwd-type.qml:103
411msgid "Passphrase"
420msgid "Passphrase"
412−msgstr ""
421+msgstr "Hasło"
413
422
414#: qml/Wizard/Pages/30-passwd-type.qml:104
423#: qml/Wizard/Pages/30-passwd-type.qml:104
415msgid "Numbers and letters"
424msgid "Numbers and letters"
416−msgstr ""
425+msgstr "Cyfry i litery"
417
426
418#: qml/Wizard/Pages/40-wifi.qml:28
427#: qml/Wizard/Pages/40-wifi.qml:28
419msgid "Connect to Wi‑Fi"
428msgid "Connect to Wi‑Fi"
420−msgstr ""
429+msgstr "Połącz z Wi-Fi"
421
430
422#: qml/Wizard/Pages/40-wifi.qml:147
431#: qml/Wizard/Pages/40-wifi.qml:147
423msgid "Available networks…"
432msgid "Available networks…"
424−msgstr ""
433+msgstr "Dostępne sieci"
425
434
426#: qml/Wizard/Pages/40-wifi.qml:148
435#: qml/Wizard/Pages/40-wifi.qml:148
427msgid "No available networks."
436msgid "No available networks."
428−msgstr ""
437+msgstr "Brak dostępnych sieci"
429
438
430#: qml/Wizard/Pages/50-location.qml:27
439#: qml/Wizard/Pages/50-location.qml:27
431msgid "Location"
440msgid "Location"
432−msgstr ""
441+msgstr "Lokalizacja"
433
442
434#: qml/Wizard/Pages/50-location.qml:62
443#: qml/Wizard/Pages/50-location.qml:62
435msgid "Let the phone detect your location:"
444msgid "Let the phone detect your location:"
436−msgstr ""
445+msgstr "Pozwól temu urządzeniu na wykrywanie lokalizacji:"
437
446
438#: qml/Wizard/Pages/50-location.qml:69
447#: qml/Wizard/Pages/50-location.qml:69
439msgid "Using GPS only (less accurate)"
448msgid "Using GPS only (less accurate)"
440−msgstr ""
449+msgstr "Używając tylko GPS (mniej dokładne)"
441
450
442#: qml/Wizard/Pages/50-location.qml:86
451#: qml/Wizard/Pages/50-location.qml:86
443msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
452msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
444−msgstr ""
453+msgstr "Używając GPS, anonimowych Wi-Fi oraz sieci komórkowej (polecane)"
445
454
446#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
455#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
447#: qml/Wizard/Pages/50-location.qml:103
456#: qml/Wizard/Pages/50-location.qml:103
@@ -449,81 +458,87 @@ msgid ""
449"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
458"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
450"conditions</a>."
459"conditions</a>."
451msgstr ""
460msgstr ""
461+"Zaznaczając tę opcję akceptujesz <a href='#'>warunki i zasady regulaminu</a> "
462+"Nokia HERE."
452
463
453#: qml/Wizard/Pages/50-location.qml:112
464#: qml/Wizard/Pages/50-location.qml:112
454msgid "Not at all"
465msgid "Not at all"
455−msgstr ""
466+msgstr "Brak zgody"
456
467
457#: qml/Wizard/Pages/50-location.qml:124
468#: qml/Wizard/Pages/50-location.qml:124
458msgid "You can change your mind later in <b>System Settings</b>."
469msgid "You can change your mind later in <b>System Settings</b>."
459−msgstr ""
470+msgstr "Jeśli zmienisz zdanie, zajrzyj do <b>ustawień systemu</b>."
460
471
461#: qml/Wizard/Pages/60-reporting.qml:24
472#: qml/Wizard/Pages/60-reporting.qml:24
462msgid "Improving your experience"
473msgid "Improving your experience"
463−msgstr ""
474+msgstr "Rozwój dostarczanych usług"
464
475
465#: qml/Wizard/Pages/60-reporting.qml:36
476#: qml/Wizard/Pages/60-reporting.qml:36
466msgid ""
477msgid ""
467"Your phone is set up to automatically report errors to Canonical and its "
478"Your phone is set up to automatically report errors to Canonical and its "
468"partners, the makers of the operating system."
479"partners, the makers of the operating system."
469msgstr ""
480msgstr ""
481+"To urządzenie jest domyślnie skonfigurowane aby zgłaszać raporty błędów "
482+"twórcom systemu operacyjnego: firmie Canonical oraz jej partnerom"
470
483
471#: qml/Wizard/Pages/60-reporting.qml:43
484#: qml/Wizard/Pages/60-reporting.qml:43
472msgid ""
485msgid ""
473"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
486"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
474"Privacy</b>"
487"Privacy</b>"
475msgstr ""
488msgstr ""
489+"Opcję tę można wyłączyć w <b>ustawieniach systemu</b> przez menu "
490+"<b>Prywatność i zabezpieczenia</b>"
476
491
477#: qml/Wizard/Pages/80-finished.qml:24
492#: qml/Wizard/Pages/80-finished.qml:24
478msgid "All done"
493msgid "All done"
479−msgstr ""
494+msgstr "Gotowe"
480
495
481#: qml/Wizard/Pages/80-finished.qml:39
496#: qml/Wizard/Pages/80-finished.qml:39
482msgid "Nice work!"
497msgid "Nice work!"
483−msgstr ""
498+msgstr "Dobra robota!"
484
499
485#: qml/Wizard/Pages/80-finished.qml:46
500#: qml/Wizard/Pages/80-finished.qml:46
486msgid "Your phone is now ready to use."
501msgid "Your phone is now ready to use."
487−msgstr ""
502+msgstr "Twoje urządzenie jest gotowe do pracy"
488
503
489#: qml/Wizard/Pages/80-finished.qml:53
504#: qml/Wizard/Pages/80-finished.qml:53
490msgid "Finish"
505msgid "Finish"
491−msgstr ""
506+msgstr "Zakończ"
492
507
493#: qml/Wizard/Pages/here-terms.qml:27
508#: qml/Wizard/Pages/here-terms.qml:27
494msgid "Terms & Conditions"
509msgid "Terms & Conditions"
495−msgstr ""
510+msgstr "Zasady i warunki"
496
511
497#: qml/Wizard/Pages/passwd-confirm.qml:49
512#: qml/Wizard/Pages/passwd-confirm.qml:49
498msgid "Confirm passphrase"
513msgid "Confirm passphrase"
499−msgstr ""
514+msgstr "Potwierdź hasło"
500
515
501#: qml/Wizard/Pages/passwd-confirm.qml:50
516#: qml/Wizard/Pages/passwd-confirm.qml:50
502msgid "Confirm passcode"
517msgid "Confirm passcode"
503−msgstr ""
518+msgstr "potwierdź kod"
504
519
505#: qml/Wizard/Pages/passwd-confirm.qml:53
520#: qml/Wizard/Pages/passwd-confirm.qml:53
506#: qml/Wizard/Pages/passwd-confirm.qml:54
521#: qml/Wizard/Pages/passwd-confirm.qml:54
507msgid "Please try again."
522msgid "Please try again."
508−msgstr ""
523+msgstr "Proszę spróbować ponownie."
509
524
510#: qml/Wizard/Pages/passwd-set.qml:61
525#: qml/Wizard/Pages/passwd-set.qml:61
511msgid "Choose your passcode"
526msgid "Choose your passcode"
512−msgstr ""
527+msgstr "Wybierz kod"
513
528
514#: qml/Wizard/Pages/passwd-set.qml:68
529#: qml/Wizard/Pages/passwd-set.qml:68
515msgid "Passphrase must be 4 characters long"
530msgid "Passphrase must be 4 characters long"
516−msgstr ""
531+msgstr "Hasło musi składać się z 4 znaków"
517
532
518#: qml/Wizard/StackButton.qml:39
533#: qml/Wizard/StackButton.qml:39
519#, qt-format
534#, qt-format
520msgid "〈 %1"
535msgid "〈 %1"
521−msgstr ""
536+msgstr "〈 %1"
522
537
523#: qml/Wizard/StackButton.qml:42
538#: qml/Wizard/StackButton.qml:42
524#, qt-format
539#, qt-format
525msgid "%1 〉"
540msgid "%1 〉"
526−msgstr ""
541+msgstr "%1 〉"
527
542
528#~ msgid "Confirm"
543#~ msgid "Confirm"
529#~ msgstr "Potwierdź"
544#~ msgstr "Potwierdź"
po/ru.po+5−5
@@ -8,7 +8,7 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2014-08-29 08:06+0000\n"
11+"PO-Revision-Date: 2015-01-29 08:02+0000\n"
12"Last-Translator: ☠Jay ZDLin☠ <Unknown>\n"
12"Last-Translator: ☠Jay ZDLin☠ <Unknown>\n"
13"Language-Team: Russian <ru@li.org>\n"
13"Language-Team: Russian <ru@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
@@ -16,7 +16,7 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
19+"X-Launchpad-Export-Date: 2015-01-30 07:08+0000\n"
20"X-Generator: Launchpad (build 17306)\n"
20"X-Generator: Launchpad (build 17306)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:130
22#: plugins/LightDM/Greeter.cpp:130
@@ -292,7 +292,7 @@ msgstr "Сбой, коснитесь для повтора"
292
292
293#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
293#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
294msgid "Reply"
294msgid "Reply"
295−msgstr ""
295+msgstr "Ответить"
296
296
297#: qml/Panel/Indicators/ModemInfoItem.qml:105
297#: qml/Panel/Indicators/ModemInfoItem.qml:105
298msgid "Unlock SIM"
298msgid "Unlock SIM"
@@ -468,7 +468,7 @@ msgstr ""
468
468
469#: qml/Wizard/Pages/50-location.qml:112
469#: qml/Wizard/Pages/50-location.qml:112
470msgid "Not at all"
470msgid "Not at all"
471−msgstr ""
471+msgstr "Нет (никогда)"
472
472
473#: qml/Wizard/Pages/50-location.qml:124
473#: qml/Wizard/Pages/50-location.qml:124
474msgid "You can change your mind later in <b>System Settings</b>."
474msgid "You can change your mind later in <b>System Settings</b>."
@@ -477,7 +477,7 @@ msgstr ""
477
477
478#: qml/Wizard/Pages/60-reporting.qml:24
478#: qml/Wizard/Pages/60-reporting.qml:24
479msgid "Improving your experience"
479msgid "Improving your experience"
480−msgstr ""
480+msgstr "Улучшение производительности"
481
481
482#: qml/Wizard/Pages/60-reporting.qml:36
482#: qml/Wizard/Pages/60-reporting.qml:36
483msgid ""
483msgid ""
po/sl.po+3−3
@@ -8,7 +8,7 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2015-01-18 10:10+0000\n"
11+"PO-Revision-Date: 2015-01-30 13:43+0000\n"
12"Last-Translator: Damir Jerovšek <Unknown>\n"
12"Last-Translator: Damir Jerovšek <Unknown>\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"
@@ -16,7 +16,7 @@ msgstr ""
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-01-28 06:30+0000\n"
19+"X-Launchpad-Export-Date: 2015-01-31 06:19+0000\n"
20"X-Generator: Launchpad (build 17306)\n"
20"X-Generator: Launchpad (build 17306)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:130
22#: plugins/LightDM/Greeter.cpp:130
@@ -288,7 +288,7 @@ msgstr "Spodletelo, tapnite za ponovni poskus"
288
288
289#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
289#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
290msgid "Reply"
290msgid "Reply"
291−msgstr ""
291+msgstr "Odgovori"
292
292
293#: qml/Panel/Indicators/ModemInfoItem.qml:105
293#: qml/Panel/Indicators/ModemInfoItem.qml:105
294msgid "Unlock SIM"
294msgid "Unlock SIM"
po/tr.po+79−69
@@ -8,19 +8,19 @@ 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-01-22 11:09+0100\n"
10"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2014-07-16 17:58+0000\n"
11+"PO-Revision-Date: 2015-02-04 21:37+0000\n"
12"Last-Translator: Volkan Gezer <Unknown>\n"
12"Last-Translator: Volkan Gezer <Unknown>\n"
13"Language-Team: Turkish <tr@li.org>\n"
13"Language-Team: Turkish <tr@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-01-28 06:31+0000\n"
18+"X-Launchpad-Export-Date: 2015-02-05 07:41+0000\n"
19−"X-Generator: Launchpad (build 17306)\n"
19+"X-Generator: Launchpad (build 17331)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:130
21#: plugins/LightDM/Greeter.cpp:130
22msgid "Password: "
22msgid "Password: "
23−msgstr ""
23+msgstr "Parola: "
24
24
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
@@ -33,30 +33,30 @@ msgstr "Kısayol iğnesini kaldır"
33
33
34#: qml/Components/DelayedLockscreen.qml:47
34#: qml/Components/DelayedLockscreen.qml:47
35msgid "Device Locked"
35msgid "Device Locked"
36−msgstr ""
36+msgstr "Cihaz Kilitli"
37
37
38#: qml/Components/DelayedLockscreen.qml:62
38#: qml/Components/DelayedLockscreen.qml:62
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 ""
40+msgstr "Çok sayıda parola hatası yaptığınız için kilitlendiniz."
41
41
42#: qml/Components/DelayedLockscreen.qml:63
42#: qml/Components/DelayedLockscreen.qml:63
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."
44−msgstr ""
44+msgstr "Çok sayıda parola hatası yaptığınız için kilitlendiniz."
45
45
46#: qml/Components/DelayedLockscreen.qml:72
46#: qml/Components/DelayedLockscreen.qml:72
47#, qt-format
47#, qt-format
48msgid "Please wait %1 minute and then try again…"
48msgid "Please wait %1 minute and then try again…"
49msgid_plural "Please wait %1 minutes and then try again…"
49msgid_plural "Please wait %1 minutes and then try again…"
50−msgstr[0] ""
50+msgstr[0] "Lütfen %1 dakika bekleyin ve yeniden deneyin..."
51−msgstr[1] ""
51+msgstr[1] "Lütfen %1 dakika bekleyin ve yeniden deneyin..."
52
52
53#: qml/Components/Dialogs.qml:86
53#: qml/Components/Dialogs.qml:86
54msgid "Log out"
54msgid "Log out"
55−msgstr ""
55+msgstr "Oturumu kapat"
56
56
57#: qml/Components/Dialogs.qml:87
57#: qml/Components/Dialogs.qml:87
58msgid "Are you sure you want to log out?"
58msgid "Are you sure you want to log out?"
59−msgstr ""
59+msgstr "Oturumu kapatmak istediğinize emin misiniz?"
60
60
61#: qml/Components/Dialogs.qml:89 qml/Components/Dialogs.qml:111
61#: qml/Components/Dialogs.qml:89 qml/Components/Dialogs.qml:111
62#: qml/Components/Dialogs.qml:134
62#: qml/Components/Dialogs.qml:134
@@ -74,7 +74,7 @@ msgstr "Bilgisayarı Kapat"
74
74
75#: qml/Components/Dialogs.qml:109
75#: qml/Components/Dialogs.qml:109
76msgid "Are you sure you want to shut down?"
76msgid "Are you sure you want to shut down?"
77−msgstr ""
77+msgstr "Kapatmak istediğinize emin misiniz?"
78
78
79#: qml/Components/Dialogs.qml:131
79#: qml/Components/Dialogs.qml:131
80msgid "Reboot"
80msgid "Reboot"
@@ -86,7 +86,7 @@ msgstr "Bilgisayarı yeniden başlatmak istediğinize emin misiniz?"
86
86
87#: qml/Components/Dialogs.qml:154
87#: qml/Components/Dialogs.qml:154
88msgid "Power"
88msgid "Power"
89−msgstr ""
89+msgstr "Güç"
90
90
91#: qml/Components/Dialogs.qml:155
91#: qml/Components/Dialogs.qml:155
92msgid ""
92msgid ""
@@ -96,15 +96,15 @@ msgstr "Bilgisayarı kapatmak istediğinize emin misiniz?"
96
96
97#: qml/Components/Dialogs.qml:157
97#: qml/Components/Dialogs.qml:157
98msgid "Power off"
98msgid "Power off"
99−msgstr ""
99+msgstr "Kapat"
100
100
101#: qml/Components/Dialogs.qml:166
101#: qml/Components/Dialogs.qml:166
102msgid "Restart"
102msgid "Restart"
103−msgstr ""
103+msgstr "Yeniden başlat"
104
104
105#: qml/Components/Dialogs.qml:175
105#: qml/Components/Dialogs.qml:175
106msgid "Cancel"
106msgid "Cancel"
107−msgstr ""
107+msgstr "İptal"
108
108
109#: qml/Components/EdgeDemoOverlay.qml:151
109#: qml/Components/EdgeDemoOverlay.qml:151
110msgid "Skip intro"
110msgid "Skip intro"
@@ -157,7 +157,7 @@ msgstr ""
157
157
158#: qml/Components/Lockscreen.qml:238
158#: qml/Components/Lockscreen.qml:238
159msgid "Return to Call"
159msgid "Return to Call"
160−msgstr ""
160+msgstr "Çağrıya Dön"
161
161
162#: qml/Components/Lockscreen.qml:238
162#: qml/Components/Lockscreen.qml:238
163msgid "Emergency Call"
163msgid "Emergency Call"
@@ -186,11 +186,11 @@ msgstr "Mağaza"
186
186
187#: qml/Dash/PageHeader.qml:284
187#: qml/Dash/PageHeader.qml:284
188msgid "Settings"
188msgid "Settings"
189−msgstr ""
189+msgstr "Ayarlar"
190
190
191#: qml/Dash/PageHeader.qml:291
191#: qml/Dash/PageHeader.qml:291
192msgid "Remove from Favorites"
192msgid "Remove from Favorites"
193−msgstr ""
193+msgstr "Sık kullanılanlardan kaldır"
194
194
195#: qml/Dash/PageHeader.qml:291
195#: qml/Dash/PageHeader.qml:291
196msgid "Add to Favorites"
196msgid "Add to Favorites"
@@ -218,27 +218,27 @@ msgstr "Gönder"
218
218
219#: qml/Dash/PullToRefreshScopeStyle.qml:55
219#: qml/Dash/PullToRefreshScopeStyle.qml:55
220msgid "Pull to refresh…"
220msgid "Pull to refresh…"
221−msgstr ""
221+msgstr "Yenilemek için çekin..."
222
222
223#: qml/Dash/PullToRefreshScopeStyle.qml:60
223#: qml/Dash/PullToRefreshScopeStyle.qml:60
224msgid "Release to refresh…"
224msgid "Release to refresh…"
225−msgstr ""
225+msgstr "Yenilemek için bırakın..."
226
226
227#: qml/Dash/ScopesList.qml:66
227#: qml/Dash/ScopesList.qml:66
228msgid "Manage"
228msgid "Manage"
229−msgstr ""
229+msgstr "Yönet"
230
230
231#: qml/Dash/ScopesList.qml:112
231#: qml/Dash/ScopesList.qml:112
232msgid "Home"
232msgid "Home"
233−msgstr ""
233+msgstr "Ev"
234
234
235#: qml/Dash/ScopesList.qml:113
235#: qml/Dash/ScopesList.qml:113
236msgid "Also installed"
236msgid "Also installed"
237−msgstr ""
237+msgstr "Ayrıca kurulan"
238
238
239#: qml/Greeter/Greeter.qml:133
239#: qml/Greeter/Greeter.qml:133
240msgid "Unlock"
240msgid "Unlock"
241−msgstr ""
241+msgstr "Kilidi Aç"
242
242
243#: qml/Notifications/NotificationMenuItemFactory.qml:128
243#: qml/Notifications/NotificationMenuItemFactory.qml:128
244msgid "Show password"
244msgid "Show password"
@@ -246,15 +246,15 @@ msgstr "Parolayı göster"
246
246
247#: qml/Panel/ActiveCallHint.qml:79
247#: qml/Panel/ActiveCallHint.qml:79
248msgid "Tap to return to call..."
248msgid "Tap to return to call..."
249−msgstr ""
249+msgstr "Çağrıya dönmek için dokunun..."
250
250
251#: qml/Panel/ActiveCallHint.qml:92
251#: qml/Panel/ActiveCallHint.qml:92
252msgid "Conference"
252msgid "Conference"
253−msgstr ""
253+msgstr "Konferans"
254
254
255#: qml/Panel/Indicators/MenuItemFactory.qml:643
255#: qml/Panel/Indicators/MenuItemFactory.qml:643
256msgid "Nothing is playing"
256msgid "Nothing is playing"
257−msgstr ""
257+msgstr "Hiçbir şey çalmıyor"
258
258
259#: qml/Panel/Indicators/MenuItemFactory.qml:791
259#: qml/Panel/Indicators/MenuItemFactory.qml:791
260msgid "In queue…"
260msgid "In queue…"
@@ -282,7 +282,7 @@ msgstr "Başarısız, yeniden denemek için dokunun"
282
282
283#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
283#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
284msgid "Reply"
284msgid "Reply"
285−msgstr ""
285+msgstr "Yanıtla"
286
286
287#: qml/Panel/Indicators/ModemInfoItem.qml:105
287#: qml/Panel/Indicators/ModemInfoItem.qml:105
288msgid "Unlock SIM"
288msgid "Unlock SIM"
@@ -290,20 +290,20 @@ msgstr "SIM kilidini aç"
290
290
291#: qml/Panel/Indicators/RoamingIndication.qml:27
291#: qml/Panel/Indicators/RoamingIndication.qml:27
292msgid "Roaming"
292msgid "Roaming"
293−msgstr ""
293+msgstr "Dolaşım"
294
294
295#: qml/Shell.qml:390
295#: qml/Shell.qml:390
296#, qt-format
296#, qt-format
297msgid "Enter %1"
297msgid "Enter %1"
298−msgstr ""
298+msgstr "%1 Girin"
299
299
300#: qml/Shell.qml:391 qml/Wizard/Pages/passwd-set.qml:60
300#: qml/Shell.qml:391 qml/Wizard/Pages/passwd-set.qml:60
301msgid "Enter passphrase"
301msgid "Enter passphrase"
302−msgstr ""
302+msgstr "Parolayı girin"
303
303
304#: qml/Shell.qml:392
304#: qml/Shell.qml:392
305msgid "Enter passcode"
305msgid "Enter passcode"
306−msgstr ""
306+msgstr "Parolayı girin"
307
307
308#: qml/Shell.qml:393
308#: qml/Shell.qml:393
309#, qt-format
309#, qt-format
@@ -316,7 +316,7 @@ msgstr "Üzgünüm, hatalı parola"
316
316
317#: qml/Shell.qml:395
317#: qml/Shell.qml:395
318msgid "Please re-enter"
318msgid "Please re-enter"
319−msgstr ""
319+msgstr "Lütfen yeniden girin"
320
320
321#: qml/Shell.qml:396
321#: qml/Shell.qml:396
322msgid "Sorry, incorrect passcode"
322msgid "Sorry, incorrect passcode"
@@ -352,98 +352,101 @@ msgstr ""
352
352
353#: qml/Wizard/Page.qml:89
353#: qml/Wizard/Page.qml:89
354msgid "Back"
354msgid "Back"
355−msgstr ""
355+msgstr "Geri"
356
356
357#: qml/Wizard/Pages/10-welcome.qml:27
357#: qml/Wizard/Pages/10-welcome.qml:27
358msgid "Hi!"
358msgid "Hi!"
359−msgstr ""
359+msgstr "Merhaba!"
360
360
361#: qml/Wizard/Pages/10-welcome.qml:44
361#: qml/Wizard/Pages/10-welcome.qml:44
362msgid "Welcome to your Ubuntu phone."
362msgid "Welcome to your Ubuntu phone."
363−msgstr ""
363+msgstr "Ubuntu telefonunuza hoş geldiniz."
364
364
365#: qml/Wizard/Pages/10-welcome.qml:52
365#: qml/Wizard/Pages/10-welcome.qml:52
366msgid "Let’s get started."
366msgid "Let’s get started."
367−msgstr ""
367+msgstr "Hadi başlayalım."
368
368
369#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
369#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
370#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
370#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
371#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
371#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
372#: qml/Wizard/Pages/passwd-set.qml:90
372#: qml/Wizard/Pages/passwd-set.qml:90
373msgid "Continue"
373msgid "Continue"
374−msgstr ""
374+msgstr "Devam Et"
375
375
376#: qml/Wizard/Pages/20-sim.qml:25
376#: qml/Wizard/Pages/20-sim.qml:25
377msgid "Add a SIM card and restart your device"
377msgid "Add a SIM card and restart your device"
378−msgstr ""
378+msgstr "Bir SIM kartı ekleyin ve cihazınızı yeniden başlatın"
379
379
380#: qml/Wizard/Pages/20-sim.qml:55
380#: qml/Wizard/Pages/20-sim.qml:55
381msgid "Without it, you won’t be able to make calls or use text messaging."
381msgid "Without it, you won’t be able to make calls or use text messaging."
382msgstr ""
382msgstr ""
383+"Bir kart olmadan arama yapamaz veya metin mesaj gönderimini kullanamazsınız."
383
384
384#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
385#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
385msgid "Skip"
386msgid "Skip"
386−msgstr ""
387+msgstr "Atla"
387
388
388#: qml/Wizard/Pages/30-passwd-type.qml:39
389#: qml/Wizard/Pages/30-passwd-type.qml:39
389msgid "Lock security"
390msgid "Lock security"
390−msgstr ""
391+msgstr "Kilitleme güvenliği"
391
392
392#: qml/Wizard/Pages/30-passwd-type.qml:74
393#: qml/Wizard/Pages/30-passwd-type.qml:74
393msgid "Please select how you’d like to unlock your phone."
394msgid "Please select how you’d like to unlock your phone."
394−msgstr ""
395+msgstr "Lütfen telefonunuzun kilidini nasıl açmak istediğinizi seçin."
395
396
396#: qml/Wizard/Pages/30-passwd-type.qml:97
397#: qml/Wizard/Pages/30-passwd-type.qml:97
397msgid "Swipe"
398msgid "Swipe"
398−msgstr ""
399+msgstr "Kaydır"
399
400
400#: qml/Wizard/Pages/30-passwd-type.qml:98
401#: qml/Wizard/Pages/30-passwd-type.qml:98
401msgid "No security"
402msgid "No security"
402−msgstr ""
403+msgstr "Güvenlik yok"
403
404
404#: qml/Wizard/Pages/30-passwd-type.qml:100
405#: qml/Wizard/Pages/30-passwd-type.qml:100
405msgid "Passcode"
406msgid "Passcode"
406−msgstr ""
407+msgstr "Parola"
407
408
408#: qml/Wizard/Pages/30-passwd-type.qml:101
409#: qml/Wizard/Pages/30-passwd-type.qml:101
409msgid "4 digits only"
410msgid "4 digits only"
410−msgstr ""
411+msgstr "Sadece 4 hane"
411
412
412#: qml/Wizard/Pages/30-passwd-type.qml:103
413#: qml/Wizard/Pages/30-passwd-type.qml:103
413msgid "Passphrase"
414msgid "Passphrase"
414−msgstr ""
415+msgstr "Parola"
415
416
416#: qml/Wizard/Pages/30-passwd-type.qml:104
417#: qml/Wizard/Pages/30-passwd-type.qml:104
417msgid "Numbers and letters"
418msgid "Numbers and letters"
418−msgstr ""
419+msgstr "Sayı ve harfler"
419
420
420#: qml/Wizard/Pages/40-wifi.qml:28
421#: qml/Wizard/Pages/40-wifi.qml:28
421msgid "Connect to Wi‑Fi"
422msgid "Connect to Wi‑Fi"
422−msgstr ""
423+msgstr "Wi-Fi'ye Bağlan"
423
424
424#: qml/Wizard/Pages/40-wifi.qml:147
425#: qml/Wizard/Pages/40-wifi.qml:147
425msgid "Available networks…"
426msgid "Available networks…"
426−msgstr ""
427+msgstr "Kullanılabilir ağlar..."
427
428
428#: qml/Wizard/Pages/40-wifi.qml:148
429#: qml/Wizard/Pages/40-wifi.qml:148
429msgid "No available networks."
430msgid "No available networks."
430−msgstr ""
431+msgstr "Kullanılabilir ağ yok."
431
432
432#: qml/Wizard/Pages/50-location.qml:27
433#: qml/Wizard/Pages/50-location.qml:27
433msgid "Location"
434msgid "Location"
434−msgstr ""
435+msgstr "Konum"
435
436
436#: qml/Wizard/Pages/50-location.qml:62
437#: qml/Wizard/Pages/50-location.qml:62
437msgid "Let the phone detect your location:"
438msgid "Let the phone detect your location:"
438−msgstr ""
439+msgstr "Telefonun konumunuzu bulmasına izin verin:"
439
440
440#: qml/Wizard/Pages/50-location.qml:69
441#: qml/Wizard/Pages/50-location.qml:69
441msgid "Using GPS only (less accurate)"
442msgid "Using GPS only (less accurate)"
442−msgstr ""
443+msgstr "Sadece GPS kullanarak (daha az kesinlik)"
443
444
444#: qml/Wizard/Pages/50-location.qml:86
445#: qml/Wizard/Pages/50-location.qml:86
445msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
446msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
446msgstr ""
447msgstr ""
448+"GPS, anonimleştirilmiş Wi-Fi ve cep telefonu ağı bilgileri kullanarak "
449+"(önerilir)"
447
450
448#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
451#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
449#: qml/Wizard/Pages/50-location.qml:103
452#: qml/Wizard/Pages/50-location.qml:103
@@ -451,81 +454,88 @@ msgid ""
451"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
454"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
452"conditions</a>."
455"conditions</a>."
453msgstr ""
456msgstr ""
457+"Bu seçeneği işaretleyerek Nokia HERE <a href='#'>koşul ve şartlarını</a>nı "
458+"kabul ediyorsunuz."
454
459
455#: qml/Wizard/Pages/50-location.qml:112
460#: qml/Wizard/Pages/50-location.qml:112
456msgid "Not at all"
461msgid "Not at all"
457−msgstr ""
462+msgstr "Etmiyorum"
458
463
459#: qml/Wizard/Pages/50-location.qml:124
464#: qml/Wizard/Pages/50-location.qml:124
460msgid "You can change your mind later in <b>System Settings</b>."
465msgid "You can change your mind later in <b>System Settings</b>."
461msgstr ""
466msgstr ""
467+"Kararınızı daha sonra <b>Sistem Ayarları</b> içerisinden değiştirebilirsiniz."
462
468
463#: qml/Wizard/Pages/60-reporting.qml:24
469#: qml/Wizard/Pages/60-reporting.qml:24
464msgid "Improving your experience"
470msgid "Improving your experience"
465−msgstr ""
471+msgstr "Deneyiminizi arttırmak"
466
472
467#: qml/Wizard/Pages/60-reporting.qml:36
473#: qml/Wizard/Pages/60-reporting.qml:36
468msgid ""
474msgid ""
469"Your phone is set up to automatically report errors to Canonical and its "
475"Your phone is set up to automatically report errors to Canonical and its "
470"partners, the makers of the operating system."
476"partners, the makers of the operating system."
471msgstr ""
477msgstr ""
478+"Telefonunuz, işletim sisteminin üreticileri olan Canonical ve ortaklarına "
479+"hataları otomatik olarak bildirmek üzere yapılandırıldı."
472
480
473#: qml/Wizard/Pages/60-reporting.qml:43
481#: qml/Wizard/Pages/60-reporting.qml:43
474msgid ""
482msgid ""
475"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
483"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
476"Privacy</b>"
484"Privacy</b>"
477msgstr ""
485msgstr ""
486+"Bu <b>Sistem Ayarları</b> içerisindeki <b>Güvenlik ve Gizlilik</b> altından "
487+"devre dışı bırakılabilir"
478
488
479#: qml/Wizard/Pages/80-finished.qml:24
489#: qml/Wizard/Pages/80-finished.qml:24
480msgid "All done"
490msgid "All done"
481−msgstr ""
491+msgstr "Her şey tamam"
482
492
483#: qml/Wizard/Pages/80-finished.qml:39
493#: qml/Wizard/Pages/80-finished.qml:39
484msgid "Nice work!"
494msgid "Nice work!"
485−msgstr ""
495+msgstr "İyi iş!"
486
496
487#: qml/Wizard/Pages/80-finished.qml:46
497#: qml/Wizard/Pages/80-finished.qml:46
488msgid "Your phone is now ready to use."
498msgid "Your phone is now ready to use."
489−msgstr ""
499+msgstr "Telefonunuz artık kullanıma hazır."
490
500
491#: qml/Wizard/Pages/80-finished.qml:53
501#: qml/Wizard/Pages/80-finished.qml:53
492msgid "Finish"
502msgid "Finish"
493−msgstr ""
503+msgstr "Son"
494
504
495#: qml/Wizard/Pages/here-terms.qml:27
505#: qml/Wizard/Pages/here-terms.qml:27
496msgid "Terms & Conditions"
506msgid "Terms & Conditions"
497−msgstr ""
507+msgstr "Şartlar ve Koşullar"
498
508
499#: qml/Wizard/Pages/passwd-confirm.qml:49
509#: qml/Wizard/Pages/passwd-confirm.qml:49
500msgid "Confirm passphrase"
510msgid "Confirm passphrase"
501−msgstr ""
511+msgstr "Parolayı yinele"
502
512
503#: qml/Wizard/Pages/passwd-confirm.qml:50
513#: qml/Wizard/Pages/passwd-confirm.qml:50
504msgid "Confirm passcode"
514msgid "Confirm passcode"
505−msgstr ""
515+msgstr "Parolayı yinele"
506
516
507#: qml/Wizard/Pages/passwd-confirm.qml:53
517#: qml/Wizard/Pages/passwd-confirm.qml:53
508#: qml/Wizard/Pages/passwd-confirm.qml:54
518#: qml/Wizard/Pages/passwd-confirm.qml:54
509msgid "Please try again."
519msgid "Please try again."
510−msgstr ""
520+msgstr "Lütfen yeniden deneyin."
511
521
512#: qml/Wizard/Pages/passwd-set.qml:61
522#: qml/Wizard/Pages/passwd-set.qml:61
513msgid "Choose your passcode"
523msgid "Choose your passcode"
514−msgstr ""
524+msgstr "Parolanızı seçin"
515
525
516#: qml/Wizard/Pages/passwd-set.qml:68
526#: qml/Wizard/Pages/passwd-set.qml:68
517msgid "Passphrase must be 4 characters long"
527msgid "Passphrase must be 4 characters long"
518−msgstr ""
528+msgstr "Parola 4 karakter olmalı"
519
529
520#: qml/Wizard/StackButton.qml:39
530#: qml/Wizard/StackButton.qml:39
521#, qt-format
531#, qt-format
522msgid "〈 %1"
532msgid "〈 %1"
523−msgstr ""
533+msgstr "〈 %1"
524
534
525#: qml/Wizard/StackButton.qml:42
535#: qml/Wizard/StackButton.qml:42
526#, qt-format
536#, qt-format
527msgid "%1 〉"
537msgid "%1 〉"
528−msgstr ""
538+msgstr "%1 〉"
529
539
530#~ msgid "Confirm"
540#~ msgid "Confirm"
531#~ msgstr "Onayla"
541#~ msgstr "Onayla"
po/unity8.pot+83−73
@@ -8,7 +8,7 @@ msgid ""
8msgstr ""
8msgstr ""
9"Project-Id-Version: unity8\n"
9"Project-Id-Version: unity8\n"
10"Report-Msgid-Bugs-To: \n"
10"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11+"POT-Creation-Date: 2015-02-13 11:45+0100\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,7 +18,7 @@ msgstr ""
18"Content-Transfer-Encoding: 8bit\n"
18"Content-Transfer-Encoding: 8bit\n"
19"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
19"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
20
20
21−#: plugins/LightDM/Greeter.cpp:130
21+#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
23msgstr ""
23msgstr ""
24
24
@@ -106,52 +106,6 @@ msgstr ""
106msgid "Cancel"
106msgid "Cancel"
107msgstr ""
107msgstr ""
108
108
109−#: qml/Components/EdgeDemoOverlay.qml:151
110−msgid "Skip intro"
111−msgstr ""
112−
113−#: qml/Components/EdgeDemo.qml:125
114−msgid "Right edge"
115−msgstr ""
116−
117−#: qml/Components/EdgeDemo.qml:126
118−msgid "Try swiping from the right edge to unlock the phone"
119−msgstr ""
120−
121−#: qml/Components/EdgeDemo.qml:157
122−msgid "Top edge"
123−msgstr ""
124−
125−#: qml/Components/EdgeDemo.qml:158
126−msgid "Try swiping from the top edge to access the indicators"
127−msgstr ""
128−
129−#: qml/Components/EdgeDemo.qml:183
130−msgid "Close"
131−msgstr ""
132−
133−#: qml/Components/EdgeDemo.qml:184
134−msgid "Swipe up again to close the settings screen"
135−msgstr ""
136−
137−#: qml/Components/EdgeDemo.qml:214
138−msgid "Left edge"
139−msgstr ""
140−
141−#: qml/Components/EdgeDemo.qml:215
142−msgid "Swipe from the left to reveal the launcher for quick access to apps"
143−msgstr ""
144−
145−#: qml/Components/EdgeDemo.qml:242
146−msgid "Well done"
147−msgstr ""
148−
149−#: qml/Components/EdgeDemo.qml:243
150−msgid ""
151−"You have now mastered the edge gestures and can start using the "
152−"phone<br><br>Tap on the screen to start"
153−msgstr ""
154−
155#: qml/Components/Lockscreen.qml:238
109#: qml/Components/Lockscreen.qml:238
156msgid "Return to Call"
110msgid "Return to Call"
157msgstr ""
111msgstr ""
@@ -164,32 +118,32 @@ msgstr ""
164msgid "OK"
118msgid "OK"
165msgstr ""
119msgstr ""
166
120
167−#: qml/Dash/GenericScopeView.qml:485 qml/Dash/GenericScopeView.qml:642
121+#: qml/Dash/GenericScopeView.qml:528 qml/Dash/GenericScopeView.qml:686
168msgid "See less"
122msgid "See less"
169msgstr ""
123msgstr ""
170
124
171−#: qml/Dash/GenericScopeView.qml:485
125+#: qml/Dash/GenericScopeView.qml:528
172msgid "See all"
126msgid "See all"
173msgstr ""
127msgstr ""
174
128
175−#: qml/Dash/GenericScopeView.qml:547 qml/Dash/PageHeader.qml:274
129+#: qml/Dash/GenericScopeView.qml:590 qml/Dash/PageHeader.qml:276
176#: qml/Panel/SearchIndicator.qml:27
130#: qml/Panel/SearchIndicator.qml:27
177msgid "Search"
131msgid "Search"
178msgstr ""
132msgstr ""
179
133
180−#: qml/Dash/PageHeader.qml:267
134+#: qml/Dash/PageHeader.qml:269
181msgid "Store"
135msgid "Store"
182msgstr ""
136msgstr ""
183
137
184−#: qml/Dash/PageHeader.qml:284
138+#: qml/Dash/PageHeader.qml:286
185msgid "Settings"
139msgid "Settings"
186msgstr ""
140msgstr ""
187
141
188−#: qml/Dash/PageHeader.qml:291
142+#: qml/Dash/PageHeader.qml:293
189msgid "Remove from Favorites"
143msgid "Remove from Favorites"
190msgstr ""
144msgstr ""
191
145
192−#: qml/Dash/PageHeader.qml:291
146+#: qml/Dash/PageHeader.qml:293
193msgid "Add to Favorites"
147msgid "Add to Favorites"
194msgstr ""
148msgstr ""
195
149
@@ -221,6 +175,10 @@ msgstr ""
221msgid "Release to refresh…"
175msgid "Release to refresh…"
222msgstr ""
176msgstr ""
223
177
178+#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
179+msgid "Enable location data"
180+msgstr ""
181+
224#: qml/Dash/ScopesList.qml:66
182#: qml/Dash/ScopesList.qml:66
225msgid "Manage"
183msgid "Manage"
226msgstr ""
184msgstr ""
@@ -233,7 +191,7 @@ msgstr ""
233msgid "Also installed"
191msgid "Also installed"
234msgstr ""
192msgstr ""
235
193
236−#: qml/Greeter/Greeter.qml:133
194+#: qml/Greeter/Greeter.qml:141
237msgid "Unlock"
195msgid "Unlock"
238msgstr ""
196msgstr ""
239
197
@@ -289,60 +247,112 @@ msgstr ""
289msgid "Roaming"
247msgid "Roaming"
290msgstr ""
248msgstr ""
291
249
292−#: qml/Shell.qml:390
250+#: qml/Shell.qml:402
293#, qt-format
251#, qt-format
294msgid "Enter %1"
252msgid "Enter %1"
295msgstr ""
253msgstr ""
296
254
297−#: qml/Shell.qml:391 qml/Wizard/Pages/passwd-set.qml:60
255+#: qml/Shell.qml:403 qml/Wizard/Pages/passwd-set.qml:60
298msgid "Enter passphrase"
256msgid "Enter passphrase"
299msgstr ""
257msgstr ""
300
258
301−#: qml/Shell.qml:392
259+#: qml/Shell.qml:404
302msgid "Enter passcode"
260msgid "Enter passcode"
303msgstr ""
261msgstr ""
304
262
305−#: qml/Shell.qml:393
263+#: qml/Shell.qml:405
306#, qt-format
264#, qt-format
307msgid "Sorry, incorrect %1"
265msgid "Sorry, incorrect %1"
308msgstr ""
266msgstr ""
309
267
310−#: qml/Shell.qml:394
268+#: qml/Shell.qml:406
311msgid "Sorry, incorrect passphrase"
269msgid "Sorry, incorrect passphrase"
312msgstr ""
270msgstr ""
313
271
314−#: qml/Shell.qml:395
272+#: qml/Shell.qml:407
315msgid "Please re-enter"
273msgid "Please re-enter"
316msgstr ""
274msgstr ""
317
275
318−#: qml/Shell.qml:396
276+#: qml/Shell.qml:408
319msgid "Sorry, incorrect passcode"
277msgid "Sorry, incorrect passcode"
320msgstr ""
278msgstr ""
321
279
322−#: qml/Shell.qml:490 qml/Wizard/Pages/passwd-confirm.qml:53
280+#: qml/Shell.qml:502 qml/Wizard/Pages/passwd-confirm.qml:53
323msgid "Sorry, incorrect passphrase."
281msgid "Sorry, incorrect passphrase."
324msgstr ""
282msgstr ""
325
283
326−#: qml/Shell.qml:491 qml/Wizard/Pages/passwd-confirm.qml:54
284+#: qml/Shell.qml:503 qml/Wizard/Pages/passwd-confirm.qml:54
327msgid "Sorry, incorrect passcode."
285msgid "Sorry, incorrect passcode."
328msgstr ""
286msgstr ""
329
287
330−#: qml/Shell.qml:492
288+#: qml/Shell.qml:504
331msgid "This will be your last attempt."
289msgid "This will be your last attempt."
332msgstr ""
290msgstr ""
333
291
334−#: qml/Shell.qml:494
292+#: qml/Shell.qml:506
335msgid ""
293msgid ""
336"If passphrase is entered incorrectly, your phone will conduct a factory "
294"If passphrase is entered incorrectly, your phone will conduct a factory "
337"reset and all personal data will be deleted."
295"reset and all personal data will be deleted."
338msgstr ""
296msgstr ""
339
297
340−#: qml/Shell.qml:495
298+#: qml/Shell.qml:507
341msgid ""
299msgid ""
342"If passcode is entered incorrectly, your phone will conduct a factory reset "
300"If passcode is entered incorrectly, your phone will conduct a factory reset "
343"and all personal data will be deleted."
301"and all personal data will be deleted."
344msgstr ""
302msgstr ""
345
303
304+#: qml/Tutorial/TutorialBottomFinish.qml:24
305+msgid "This action does different things for different apps"
306+msgstr ""
307+
308+#: qml/Tutorial/TutorialBottomFinish.qml:25
309+msgid "Tap here to finish."
310+msgstr ""
311+
312+#: qml/Tutorial/TutorialBottom.qml:28
313+msgid "Open special menus"
314+msgstr ""
315+
316+#: qml/Tutorial/TutorialBottom.qml:29
317+msgid "Swipe up from the bottom edge."
318+msgstr ""
319+
320+#: qml/Tutorial/TutorialLeftFinish.qml:24
321+msgid "These are the shortcuts to favorite apps"
322+msgstr ""
323+
324+#: qml/Tutorial/TutorialLeftFinish.qml:25 qml/Tutorial/TutorialRight.qml:144
325+msgid "Tap here to continue."
326+msgstr ""
327+
328+#: qml/Tutorial/TutorialLeft.qml:26
329+msgid "Open the launcher"
330+msgstr ""
331+
332+#: qml/Tutorial/TutorialLeft.qml:27
333+msgid "Short swipe from the left edge."
334+msgstr ""
335+
336+#: qml/Tutorial/TutorialPage.qml:178
337+msgid "You almost got it!"
338+msgstr ""
339+
340+#: qml/Tutorial/TutorialPage.qml:195
341+msgid "Try again."
342+msgstr ""
343+
344+#: qml/Tutorial/TutorialRight.qml:31
345+msgid "To view open apps"
346+msgstr ""
347+
348+#: qml/Tutorial/TutorialRight.qml:32
349+msgid "Long swipe from the right edge."
350+msgstr ""
351+
352+#: qml/Tutorial/TutorialRight.qml:122
353+msgid "View all your running tasks."
354+msgstr ""
355+
346#: qml/Wizard/Page.qml:89
356#: qml/Wizard/Page.qml:89
347msgid "Back"
357msgid "Back"
348msgstr ""
358msgstr ""
@@ -360,7 +370,7 @@ msgid "Let’s get started."
360msgstr ""
370msgstr ""
361
371
362#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
372#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
363−#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
373+#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
364#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
374#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
365#: qml/Wizard/Pages/passwd-set.qml:90
375#: qml/Wizard/Pages/passwd-set.qml:90
366msgid "Continue"
376msgid "Continue"
@@ -374,7 +384,7 @@ msgstr ""
374msgid "Without it, you won’t be able to make calls or use text messaging."
384msgid "Without it, you won’t be able to make calls or use text messaging."
375msgstr ""
385msgstr ""
376
386
377−#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
387+#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:215
378msgid "Skip"
388msgid "Skip"
379msgstr ""
389msgstr ""
380
390
@@ -410,15 +420,15 @@ msgstr ""
410msgid "Numbers and letters"
420msgid "Numbers and letters"
411msgstr ""
421msgstr ""
412
422
413−#: qml/Wizard/Pages/40-wifi.qml:28
423+#: qml/Wizard/Pages/40-wifi.qml:29
414msgid "Connect to Wi‑Fi"
424msgid "Connect to Wi‑Fi"
415msgstr ""
425msgstr ""
416
426
417−#: qml/Wizard/Pages/40-wifi.qml:147
427+#: qml/Wizard/Pages/40-wifi.qml:168
418msgid "Available networks…"
428msgid "Available networks…"
419msgstr ""
429msgstr ""
420
430
421−#: qml/Wizard/Pages/40-wifi.qml:148
431+#: qml/Wizard/Pages/40-wifi.qml:169
422msgid "No available networks."
432msgid "No available networks."
423msgstr ""
433msgstr ""
424
434
qml/CMakeLists.txt+3−2
@@ -9,10 +9,11 @@ set(QML_DIRS
9 Dash
9 Dash
10 graphics
10 graphics
11 Greeter
11 Greeter
12− Panel
13 Launcher
12 Launcher
14− Stages
15 Notifications
13 Notifications
14+ Panel
15+ Stages
16+ Tutorial
16 Wizard
17 Wizard
17 )
18 )
18
19
qml/Components/Carousel.qml+6−2
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
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
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import "carousel.js" as CarouselJS
19import "carousel.js" as CarouselJS
20
20
@@ -38,6 +38,8 @@ Item {
38 property alias tileAspectRatio: listView.tileAspectRatio
38 property alias tileAspectRatio: listView.tileAspectRatio
39 /// Used to cache some delegates for performance reasons. See the ListView documentation for details
39 /// Used to cache some delegates for performance reasons. See the ListView documentation for details
40 property alias cacheBuffer: listView.cacheBuffer
40 property alias cacheBuffer: listView.cacheBuffer
41+ property alias displayMarginBeginning: listView.displayMarginBeginning
42+ property alias displayMarginEnd: listView.displayMarginEnd
41 /// Width of the "draw buffer" in pixel. The drawBuffer is an additional area at start/end where
43 /// Width of the "draw buffer" in pixel. The drawBuffer is an additional area at start/end where
42 /// items drawn, even if it is not in the visible area.
44 /// items drawn, even if it is not in the visible area.
43 /// cacheBuffer controls only the to retain delegates outside the visible area (and is used on top of the drawBuffer)
45 /// cacheBuffer controls only the to retain delegates outside the visible area (and is used on top of the drawBuffer)
@@ -51,6 +53,8 @@ Item {
51 readonly property alias currentItem: listView.currentItem
53 readonly property alias currentItem: listView.currentItem
52 /// exposes the distance to the next row (only one row in carousel, so it's the topMargins)
54 /// exposes the distance to the next row (only one row in carousel, so it's the topMargins)
53 readonly property alias verticalSpacing: listView.verticalMargin
55 readonly property alias verticalSpacing: listView.verticalMargin
56+ /// the width of the internal list
57+ readonly property alias innerWidth: listView.width
54
58
55 implicitHeight: listView.tileHeight * selectedItemScaleFactor
59 implicitHeight: listView.tileHeight * selectedItemScaleFactor
56 opacity: listView.highlightIndex === -1 ? 1 : 0.6
60 opacity: listView.highlightIndex === -1 ? 1 : 0.6
qml/Components/EdgeDemo.qmldeleted−254
@@ -1,254 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import AccountsService 0.1
18−import LightDM 0.1 as LightDM
19−import QtQuick 2.0
20−import Ubuntu.Components 1.1
21−
22−Item {
23− id: demo
24−
25− property Item greeter
26− property Item launcher
27− property Item panel
28− property Item stages
29−
30− property bool launcherEnabled: true
31− property bool stagesEnabled: true
32− property bool panelEnabled: true
33− property bool panelContentEnabled: true
34− property bool running: !launcherEnabled || !stagesEnabled || !panelEnabled || !panelContentEnabled
35−
36− property bool paused: false
37−
38− onPausedChanged: {
39− if (d.rightEdgeDemo) d.rightEdgeDemo.paused = paused
40− if (d.topEdgeDemo) d.topEdgeDemo.paused = paused
41− if (d.bottomEdgeDemo) d.bottomEdgeDemo.paused = paused
42− if (d.leftEdgeDemo) d.leftEdgeDemo.paused = paused
43− if (d.finalEdgeDemo) d.finalEdgeDemo.paused = paused
44− }
45−
46− function hideEdgeDemoInShell() {
47− AccountsService.demoEdges = false;
48− stopDemo();
49− }
50−
51− function hideEdgeDemoInGreeter() {
52− // TODO: AccountsService.demoEdges = false as lightdm user
53− }
54−
55− function hideEdgeDemos() {
56− hideEdgeDemoInGreeter();
57− hideEdgeDemoInShell();
58− }
59−
60− function stopDemo() {
61− launcherEnabled = true
62− stagesEnabled = true
63− panelEnabled = true
64− panelContentEnabled = true
65−
66− // Use a tiny delay for these destroy() calls because if a lot is
67− // happening at once (like creating and being destroyed in same event
68− // loop, as might happen when answering a call while demo is open),
69− // the destroy() call will be ignored.
70− if (d.rightEdgeDemo) d.rightEdgeDemo.destroy(1);
71− if (d.topEdgeDemo) d.topEdgeDemo.destroy(1);
72− if (d.bottomEdgeDemo) d.bottomEdgeDemo.destroy(1);
73− if (d.leftEdgeDemo) d.leftEdgeDemo.destroy(1);
74− if (d.finalEdgeDemo) d.finalEdgeDemo.destroy(1);
75− }
76−
77− function startDemo() {
78− if (!d.overlay) {
79− d.overlay = Qt.createComponent("EdgeDemoOverlay.qml")
80− }
81−
82− launcherEnabled = false;
83− stagesEnabled = false;
84− panelEnabled = false;
85− panelContentEnabled = false;
86−
87− // Begin with either greeter or top, depending on which is visible
88− if (greeter && greeter.shown) {
89− startRightEdgeDemo()
90− } else {
91− startTopEdgeDemo()
92− }
93− }
94−
95− QtObject {
96− id: d
97− property Component overlay
98− property QtObject rightEdgeDemo
99− property QtObject topEdgeDemo
100− property QtObject bottomEdgeDemo
101− property QtObject leftEdgeDemo
102− property QtObject finalEdgeDemo
103− property bool showEdgeDemo: AccountsService.demoEdges
104− property bool showEdgeDemoInGreeter: AccountsService.demoEdges // TODO: AccountsService.demoEdges as lightdm user
105−
106− function restartDemo() {
107− stopDemo()
108− if (d.showEdgeDemo) {
109− startDemo()
110− }
111− }
112−
113− onShowEdgeDemoChanged: restartDemo()
114− }
115−
116− Connections {
117− target: i18n
118− onLanguageChanged: d.restartDemo()
119− }
120−
121− function startRightEdgeDemo() {
122− if (demo.greeter) {
123− d.rightEdgeDemo = d.overlay.createObject(demo.greeter, {
124− "edge": "right",
125− "title": i18n.tr("Right edge"),
126− "text": i18n.tr("Try swiping from the right edge to unlock the phone"),
127− "anchors.fill": demo.greeter,
128− });
129− }
130− if (d.rightEdgeDemo) {
131− d.rightEdgeDemo.onSkip.connect(demo.hideEdgeDemos)
132− } else {
133− stopDemo();
134− }
135− }
136−
137− Connections {
138− target: demo.greeter
139−
140− function hide() {
141− if (d.rightEdgeDemo && d.rightEdgeDemo.available) {
142− d.rightEdgeDemo.hide()
143− hideEdgeDemoInGreeter()
144− startTopEdgeDemo()
145− }
146− }
147−
148− onUnlocked: hide()
149− onShownChanged: if (!greeter.shown) hide()
150− }
151−
152− function startTopEdgeDemo() {
153− demo.panelEnabled = true;
154− if (demo.stages) {
155− d.topEdgeDemo = d.overlay.createObject(demo.panel, {
156− "edge": "top",
157− "title": i18n.tr("Top edge"),
158− "text": i18n.tr("Try swiping from the top edge to access the indicators"),
159− "anchors.fill": demo.panel,
160− });
161− }
162− if (d.topEdgeDemo) {
163− d.topEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
164− } else {
165− stopDemo();
166− }
167− }
168−
169− Connections {
170− target: demo.panel.indicators
171− onFullyOpenedChanged: {
172− if (d.topEdgeDemo && d.topEdgeDemo.available && demo.panel.indicators.fullyOpened) {
173− d.topEdgeDemo.hideNow()
174− startBottomEdgeDemo()
175− }
176− }
177− }
178−
179− function startBottomEdgeDemo() {
180− if (demo.panel.indicators) {
181− d.bottomEdgeDemo = d.overlay.createObject(demo.panel.indicators, {
182− "edge": "bottom",
183− "title": i18n.tr("Close"),
184− "text": i18n.tr("Swipe up again to close the settings screen"),
185− "anchors.fill": demo.panel.indicators,
186− });
187− }
188− if (d.bottomEdgeDemo) {
189− d.bottomEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
190− } else {
191− stopDemo();
192− }
193− }
194−
195− Connections {
196− target: demo.panel.indicators
197− onPartiallyOpenedChanged: {
198− if (d.bottomEdgeDemo &&
199− d.bottomEdgeDemo.available &&
200− !demo.panel.indicators.partiallyOpened &&
201− !demo.panel.indicators.fullyOpened) {
202− d.bottomEdgeDemo.hideNow()
203− startLeftEdgeDemo()
204− }
205− }
206− }
207−
208− function startLeftEdgeDemo() {
209− demo.panelEnabled = false;
210− demo.launcherEnabled = true;
211− if (demo.stages) {
212− d.leftEdgeDemo = d.overlay.createObject(demo.stages, {
213− "edge": "left",
214− "title": i18n.tr("Left edge"),
215− "text": i18n.tr("Swipe from the left to reveal the launcher for quick access to apps"),
216− "anchors.fill": demo.stages,
217− });
218− }
219− if (d.leftEdgeDemo) {
220− d.leftEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
221− } else {
222− stopDemo();
223− }
224− }
225−
226− Connections {
227− target: demo.launcher
228− onStateChanged: {
229− if (d.leftEdgeDemo && d.leftEdgeDemo.available && launcher.state == "visible") {
230− d.leftEdgeDemo.hide()
231− launcher.hide()
232− startFinalEdgeDemo()
233− }
234− }
235− }
236−
237− function startFinalEdgeDemo() {
238− demo.launcherEnabled = false;
239− if (demo.stages) {
240− d.finalEdgeDemo = d.overlay.createObject(demo.stages, {
241− "edge": "none",
242− "title": i18n.tr("Well done"),
243− "text": i18n.tr("You have now mastered the edge gestures and can start using the phone<br><br>Tap on the screen to start"),
244− "anchors.fill": demo.stages,
245− "showSkip": false,
246− });
247− }
248− if (d.finalEdgeDemo) {
249− d.finalEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
250− } else {
251− stopDemo();
252− }
253− }
254−}
qml/Components/EdgeDemoOverlay.qmldeleted−273
@@ -1,273 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import Powerd 0.1
18−import QtQuick 2.0
19−import QtGraphicalEffects 1.0
20−import Unity.Application 0.1
21−import Ubuntu.Components 0.1
22−
23−Showable {
24− id: overlay
25−
26− /*
27− * Valid values are "left", "right", "top", "bottom", or "none".
28− */
29− property string edge: "top"
30−
31− /*
32− * This is the header displayed, like "Right edge".
33− */
34− property alias title: titleLabel.text
35−
36− /*
37− * This is the block of text displayed below the header.
38− */
39− property alias text: textLabel.text
40−
41− /*
42− * This is the text for the skip button.
43− */
44− property alias skipText: skipLabel.text
45−
46− /*
47− * This is the visible status of the skip button.
48− */
49− property alias showSkip: skipLabel.visible
50−
51− /*
52− * Whether this demo is running currently.
53− */
54− readonly property bool active: available && visible
55−
56− /*
57− * Whether animations are paused.
58− */
59− property alias paused: wholeAnimation.paused
60−
61− /*
62− * Whether animations are running.
63− */
64− readonly property alias running: wholeAnimation.running
65−
66− signal skip()
67−
68− function doSkip() {
69− d.skipOnHide = true;
70− hide();
71− }
72−
73− function hideNow() {
74− overlay.visible = false;
75− overlay.available = false;
76− if (d.skipOnHide) {
77− overlay.skip();
78− }
79− }
80−
81− showAnimation: StandardAnimation {
82− property: "opacity"
83− to: 1
84− onRunningChanged: if (running) overlay.visible = true
85− }
86− hideAnimation: StandardAnimation {
87− property: "opacity"
88− to: 0
89− duration: UbuntuAnimation.BriskDuration
90− onRunningChanged: if (!running) overlay.hideNow()
91− }
92−
93− QtObject {
94− id: d
95− property bool skipOnHide: false
96− property int edgeMargin: units.gu(4)
97− }
98−
99− Rectangle {
100− objectName: "backgroundShade"
101−
102− anchors.fill: parent
103− color: "black"
104− opacity: 0.8
105− visible: overlay.active
106−
107− MouseArea {
108− objectName: "backgroundShadeMouseArea"
109−
110− anchors.fill: parent
111− enabled: overlay.edge == "none" && overlay.opacity == 1.0
112− onClicked: overlay.doSkip()
113− }
114− }
115−
116− Item {
117− id: hintGroup
118− x: 0
119− y: 0
120− width: parent.width
121− height: parent.height
122− visible: overlay.active
123−
124− Column {
125− id: labelGroup
126− spacing: units.gu(3)
127−
128− anchors {
129− margins: d.edgeMargin
130− left: parent.left
131− top: overlay.edge == "bottom" ? undefined : parent.top
132− bottom: overlay.edge == "bottom" ? parent.bottom : undefined
133− }
134−
135− Label {
136− id: titleLabel
137− fontSize: "x-large"
138− width: units.gu(25)
139− wrapMode: Text.WordWrap
140− }
141−
142− Label {
143− id: textLabel
144− width: units.gu(25)
145− wrapMode: Text.WordWrap
146− }
147−
148− Label {
149− id: skipLabel
150− objectName: "skipLabel"
151− text: i18n.tr("Skip intro")
152− color: UbuntuColors.orange
153− fontSize: "small"
154−
155− Icon {
156− anchors.left: parent.right
157− anchors.verticalCenter: parent.verticalCenter
158− height: units.dp(12)
159− width: units.dp(12)
160− name: "chevron"
161− color: UbuntuColors.orange
162− }
163−
164− MouseArea {
165− // Make clickable area bigger than just the link because
166− // otherwise, the edge demo will feel hard to dismiss.
167− anchors.fill: parent
168− anchors.margins: -units.gu(5)
169− onClicked: overlay.doSkip()
170− }
171− }
172− }
173−
174− LinearGradient {
175− id: edgeHint
176− property int size: 1
177− cached: false
178− visible: overlay.edge != "none"
179− gradient: Gradient {
180− GradientStop {
181− position: 0.0
182− color: Qt.hsla(16.0/360.0, 0.83, 0.47, 0.4) // UbuntuColors.orange, but transparent
183− }
184− GradientStop {
185− position: 1.0
186− color: "transparent"
187− }
188− }
189− anchors.fill: parent
190− start: {
191− if (overlay.edge == "right") {
192− return Qt.point(width, 0);
193− } else if (overlay.edge == "left") {
194− return Qt.point(0, 0);
195− } else if (overlay.edge == "top") {
196− return Qt.point(0, 0);
197− } else {
198− return Qt.point(0, height);
199− }
200− }
201− end: {
202− if (overlay.edge == "right") {
203− return Qt.point(width - size, 0);
204− } else if (overlay.edge == "left") {
205− return Qt.point(size, 0);
206− } else if (overlay.edge == "top") {
207− return Qt.point(0, size);
208− } else {
209− return Qt.point(0, height - size);
210− }
211− }
212− }
213− }
214−
215− SequentialAnimation {
216− id: wholeAnimation
217− objectName: "wholeAnimation"
218− running: overlay.active
219−
220− ParallelAnimation {
221− id: fadeInAnimation
222−
223− StandardAnimation {
224− target: labelGroup
225− property: {
226− if (overlay.edge == "right" || overlay.edge == "left") {
227− return "anchors.leftMargin";
228− } else if (overlay.edge == "bottom") {
229− return "anchors.bottomMargin";
230− } else {
231− return "anchors.topMargin";
232− }
233− }
234− from: {
235− if (overlay.edge == "right") {
236− return d.edgeMargin + units.gu(3)
237− } else {
238− return d.edgeMargin - units.gu(3)
239− }
240− }
241− to: d.edgeMargin
242− duration: overlay.edge == "none" ? 0 : UbuntuAnimation.SleepyDuration
243− }
244− StandardAnimation {
245− target: overlay
246− property: "opacity"
247− from: 0.0
248− to: 1.0
249− duration: UbuntuAnimation.SleepyDuration
250− }
251− }
252−
253− SequentialAnimation {
254− id: hintAnimation
255− loops: Animation.Infinite
256− property string prop: (overlay.edge == "left" || overlay.edge == "right") ? "x" : "y"
257− property double endVal: units.dp(5) * ((overlay.edge == "left" || overlay.edge == "top") ? 1 : -1)
258− property double maxGlow: units.dp(20)
259− property int duration: overlay.edge == "none" ? 0 : UbuntuAnimation.SleepyDuration
260−
261− ParallelAnimation {
262− StandardAnimation { target: hintGroup; property: hintAnimation.prop; from: 0; to: hintAnimation.endVal; duration: hintAnimation.duration }
263− StandardAnimation { target: edgeHint; property: "size"; from: 1; to: hintAnimation.maxGlow; duration: hintAnimation.duration }
264− }
265−
266− // Undo the above
267− ParallelAnimation {
268− StandardAnimation { target: hintGroup; property: hintAnimation.prop; from: hintAnimation.endVal; to: 0; duration: hintAnimation.duration }
269− StandardAnimation { target: edgeHint; property: "size"; from: hintAnimation.maxGlow; to: 1; duration: hintAnimation.duration }
270− }
271− }
272− }
273−}
qml/Dash/CardCarousel.qml+6−2
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -23,6 +23,8 @@ DashRenderer {
23
23
24 expandedHeight: carousel.implicitHeight + units.gu(6)
24 expandedHeight: carousel.implicitHeight + units.gu(6)
25 collapsedHeight: expandedHeight
25 collapsedHeight: expandedHeight
26+ growsVertically: false
27+ innerWidth: carousel.innerWidth
26
28
27 Carousel {
29 Carousel {
28 id: carousel
30 id: carousel
@@ -32,7 +34,9 @@ DashRenderer {
32 // and push others back.
34 // and push others back.
33 minimumTileWidth: cardTool.cardWidth / selectedItemScaleFactor
35 minimumTileWidth: cardTool.cardWidth / selectedItemScaleFactor
34 selectedItemScaleFactor: cardTool.carouselSelectedItemScaleFactor
36 selectedItemScaleFactor: cardTool.carouselSelectedItemScaleFactor
35− cacheBuffer: 1404 // 18px * 13gu * 6
37+ cacheBuffer: cardCarousel.cacheBuffer
38+ displayMarginBeginning: cardCarousel.displayMarginBeginning
39+ displayMarginEnd: cardCarousel.displayMarginEnd
36 model: cardCarousel.model
40 model: cardCarousel.model
37
41
38 property real fontScale: 1 / selectedItemScaleFactor
42 property real fontScale: 1 / selectedItemScaleFactor
qml/Dash/CardHorizontalList.qml+7−2
@@ -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
@@ -14,7 +14,7 @@
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.2
17+import QtQuick 2.3
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19import "../Components"
19import "../Components"
20
20
@@ -23,6 +23,8 @@ DashRenderer {
23
23
24 expandedHeight: cardTool.cardHeight + units.gu(2)
24 expandedHeight: cardTool.cardHeight + units.gu(2)
25 collapsedHeight: expandedHeight
25 collapsedHeight: expandedHeight
26+ growsVertically: false
27+ innerWidth: Math.max(0, listView.width)
26 clip: true
28 clip: true
27
29
28 ListView {
30 ListView {
@@ -36,6 +38,9 @@ DashRenderer {
36 spacing: units.gu(1)
38 spacing: units.gu(1)
37 model: root.model
39 model: root.model
38 orientation: ListView.Horizontal
40 orientation: ListView.Horizontal
41+ cacheBuffer: root.cacheBuffer
42+ displayMarginBeginning: root.displayMarginBeginning
43+ displayMarginEnd: root.displayMarginEnd
39
44
40 delegate: Loader {
45 delegate: Loader {
41 id: loader
46 id: loader
qml/Dash/Dash.qml+6−8
@@ -122,7 +122,6 @@ Showable {
122 dash.setCurrentScope(scopeId, true, false);
122 dash.setCurrentScope(scopeId, true, false);
123 }
123 }
124 onOpenScope: {
124 onOpenScope: {
125− scopeItem.scopeThatOpenedScope = currentScope;
126 scopeItem.scope = scope;
125 scopeItem.scope = scope;
127 x = -width;
126 x = -width;
128 }
127 }
@@ -130,7 +129,7 @@ Showable {
130 UbuntuNumberAnimation {
129 UbuntuNumberAnimation {
131 onRunningChanged: {
130 onRunningChanged: {
132 if (!running && dashContent.x == 0) {
131 if (!running && dashContent.x == 0) {
133− scopeItem.scopeThatOpenedScope.closeScope(scopeItem.scope);
132+ scopes.closeScope(scopeItem.scope);
134 scopeItem.scope = null;
133 scopeItem.scope = null;
135 }
134 }
136 }
135 }
@@ -187,7 +186,6 @@ Showable {
187 onOpenScope: {
186 onOpenScope: {
188 bottomEdgeController.enableAnimation = true;
187 bottomEdgeController.enableAnimation = true;
189 bottomEdgeController.progress = 0;
188 bottomEdgeController.progress = 0;
190− scopeItem.scopeThatOpenedScope = scopesList.scope;
191 scopeItem.scope = scope;
189 scopeItem.scope = scope;
192 dashContent.x = -dashContent.width;
190 dashContent.x = -dashContent.width;
193 }
191 }
@@ -199,8 +197,7 @@ Showable {
199 }
197 }
200 }
198 }
201
199
202− DashBackground
200+ DashBackground {
203− {
204 anchors.fill: scopeItem
201 anchors.fill: scopeItem
205 visible: scopeItem.visible
202 visible: scopeItem.visible
206 }
203 }
@@ -209,8 +206,6 @@ Showable {
209 id: scopeItem
206 id: scopeItem
210 objectName: "dashTempScopeItem"
207 objectName: "dashTempScopeItem"
211
208
212− property var scopeThatOpenedScope: null
213−
214 x: dashContent.x + width
209 x: dashContent.x + width
215 y: dashContent.y
210 y: dashContent.y
216 width: parent.width
211 width: parent.width
@@ -229,7 +224,10 @@ Showable {
229 dashContent.gotoScope(scopeId);
224 dashContent.gotoScope(scopeId);
230 }
225 }
231 onOpenScope: {
226 onOpenScope: {
232− dashContent.openScope(scope);
227+ scopeItem.closePreview();
228+ var oldScope = scopeItem.scope;
229+ scopeItem.scope = scope;
230+ scopes.closeScope(oldScope);
233 }
231 }
234 }
232 }
235 }
233 }
qml/Dash/DashContent.qml+1−2
@@ -84,8 +84,7 @@ Item {
84
84
85 set_current_index = undefined;
85 set_current_index = undefined;
86
86
87− if (dashContentList.count > index)
87+ if (dashContentList.count > index) {
88− {
89 dashContentList.currentIndex = index
88 dashContentList.currentIndex = index
90
89
91 if (reset) {
90 if (reset) {
qml/Dash/DashRenderer.qml+6−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -31,6 +31,11 @@ Item {
31
31
32 property real originY: 0
32 property real originY: 0
33
33
34+ property bool growsVertically: true
35+
36+ // If growsVertically the width of the item inside the renderer
37+ property real innerWidth: 0
38+
34 // The model to renderer
39 // The model to renderer
35 property var model
40 property var model
36
41
qml/Dash/GenericScopeView.qml+92−48
@@ -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
@@ -401,59 +401,102 @@ FocusScope {
401 }
401 }
402
402
403 if (item && item.hasOwnProperty("displayMarginBeginning")) {
403 if (item && item.hasOwnProperty("displayMarginBeginning")) {
404+ if (item.growsVertically) {
404− // A item view creates its delegates synchronously from
405+ // A item view creates its delegates synchronously from
405− // -displayMarginBeginning
406+ // -displayMarginBeginning
406− // to
407+ // to
407− // height + displayMarginEnd
408+ // height + displayMarginEnd
408− // Around that area it adds the cacheBuffer area where delegates are created async
409+ // Around that area it adds the cacheBuffer area where delegates are created async
409− //
410+ //
410− // We adjust displayMarginBeginning and displayMarginEnd so
411+ // We adjust displayMarginBeginning and displayMarginEnd so
411− // * In non visible scopes nothing is considered visible and we set cacheBuffer
412+ // * In non visible scopes nothing is considered visible and we set cacheBuffer
412− // so that creates the items that would be in the viewport asynchronously
413+ // so that creates the items that would be in the viewport asynchronously
413− // * For the current scope set the visible range to the viewport and then
414+ // * For the current scope set the visible range to the viewport and then
414− // use cacheBuffer to create extra items for categoryView.height * 1.5
415+ // use cacheBuffer to create extra items for categoryView.height * 1.5
415− // to make scrolling nicer by mantaining a higher number of
416+ // to make scrolling nicer by mantaining a higher number of
416− // cached items
417+ // cached items
417− // * For non current but visible scopes (i.e. when the user changes from one scope
418+ // * For non current but visible scopes (i.e. when the user changes from one scope
418− // to the next, we set the visible range to the viewport so
419+ // to the next, we set the visible range to the viewport so
419− // items are not culled (invisible) but still use no cacheBuffer
420+ // items are not culled (invisible) but still use no cacheBuffer
420− // (it will be set once the scope is the current one)
421+ // (it will be set once the scope is the current one)
421− var displayMarginBeginning = baseItem.y;
422+ var displayMarginBeginning = baseItem.y;
422− displayMarginBeginning = -Math.max(-displayMarginBeginning, 0);
423+ displayMarginBeginning = -Math.max(-displayMarginBeginning, 0);
423− displayMarginBeginning = -Math.min(-displayMarginBeginning, baseItem.height);
424+ displayMarginBeginning = -Math.min(-displayMarginBeginning, baseItem.height);
424− displayMarginBeginning = Math.round(displayMarginBeginning);
425+ displayMarginBeginning = Math.round(displayMarginBeginning);
425− var displayMarginEnd = -baseItem.height + seeAll.height + categoryView.height - baseItem.y;
426+ var displayMarginEnd = -baseItem.height + seeAll.height + categoryView.height - baseItem.y;
426− displayMarginEnd = -Math.max(-displayMarginEnd, 0);
427+ displayMarginEnd = -Math.max(-displayMarginEnd, 0);
427− displayMarginEnd = -Math.min(-displayMarginEnd, baseItem.height);
428+ displayMarginEnd = -Math.min(-displayMarginEnd, baseItem.height);
428− displayMarginEnd = Math.round(displayMarginEnd);
429+ displayMarginEnd = Math.round(displayMarginEnd);
430+
429− if (scopeView.isCurrent || scopeView.visibleToParent) {
431+ if (scopeView.isCurrent || scopeView.visibleToParent) {
430− item.displayMarginBeginning = displayMarginBeginning;
432+ item.displayMarginBeginning = displayMarginBeginning;
431− item.displayMarginEnd = displayMarginEnd;
433+ item.displayMarginEnd = displayMarginEnd;
432− if (holdingList && holdingList.moving) {
434+ if (holdingList && holdingList.moving) {
433− // If we are moving we need to reset the cache buffer of the
435+ // If we are moving we need to reset the cache buffer of the
434− // view that was not visible (i.e. !wasCurrentOnMoveStart) to 0 since
436+ // view that was not visible (i.e. !wasCurrentOnMoveStart) to 0 since
435− // otherwise the cache buffer we had set to preload the items of the
437+ // otherwise the cache buffer we had set to preload the items of the
436− // visible range will trigger some item creations and we want move to
438+ // visible range will trigger some item creations and we want move to
437− // be as smooth as possible meaning no need creations
439+ // be as smooth as possible meaning no need creations
438− if (!wasCurrentOnMoveStart) {
440+ if (!wasCurrentOnMoveStart) {
441+ item.cacheBuffer = 0;
442+ }
443+ } else {
439− item.cacheBuffer = 0;
444+ item.cacheBuffer = categoryView.height * 1.5;
440 }
445 }
441 } else {
446 } else {
442− item.cacheBuffer = categoryView.height * 1.5;
447+ var visibleRange = baseItem.height + displayMarginEnd + displayMarginBeginning;
448+ if (visibleRange < 0) {
449+ item.displayMarginBeginning = displayMarginBeginning;
450+ item.displayMarginEnd = displayMarginEnd;
451+ item.cacheBuffer = 0;
452+ } else {
453+ // This should be visibleRange/2 in each of the properties
454+ // but some item views still (like GridView) like creating sync delegates even if
455+ // the visible range is 0 so let's make sure the visible range is negative
456+ item.displayMarginBeginning = displayMarginBeginning - visibleRange;
457+ item.displayMarginEnd = displayMarginEnd - visibleRange;
458+ item.cacheBuffer = visibleRange;
459+ }
443 }
460 }
444 } else {
461 } else {
445− var visibleRange = baseItem.height + displayMarginEnd + displayMarginBeginning;
462+ var buffer = wasCurrentOnMoveStart ? categoryView.height * 1.5 : 0;
446− if (visibleRange < 0) {
463+ var onViewport = baseItem.y + baseItem.height > 0 &&
464+ baseItem.y < categoryView.height;
465+ var onBufferViewport = baseItem.y + baseItem.height > -buffer &&
466+ baseItem.y < categoryView.height + buffer;
467+ if (!onBufferViewport) {
468+ // If not on the buffered viewport, don't load anything
447− item.displayMarginBeginning = displayMarginBeginning;
469+ item.displayMarginBeginning = 0;
448− item.displayMarginEnd = displayMarginEnd;
470+ item.displayMarginEnd = -item.innerWidth;
449 item.cacheBuffer = 0;
471 item.cacheBuffer = 0;
450 } else {
472 } else {
451− // This should be visibleRange/2 in each of the properties
473+ if (onViewport && (scopeView.isCurrent || scopeView.visibleToParent)) {
452− // but some item views still (like GridView) like creating sync delegates even if
474+ // If on the buffered viewport and the viewport and the on a visible scope
453− // the visible range is 0 so let's make sure the visible range is negative
475+ // Set displayMargin so that cards are rendered
476+ // And if not moving the parent list also give it some extra asynchronously
477+ // buffering
478+ item.displayMarginBeginning = 0;
479+ item.displayMarginEnd = 0;
480+ if (holdingList && holdingList.moving) {
481+ // If we are moving we need to reset the cache buffer of the
482+ // view that was not visible (i.e. !wasCurrentOnMoveStart) to 0 since
483+ // otherwise the cache buffer we had set to preload the items of the
484+ // visible range will trigger some item creations and we want move to
485+ // be as smooth as possible meaning no need creations
486+ if (!wasCurrentOnMoveStart) {
487+ item.cacheBuffer = 0;
488+ }
489+ } else {
490+ item.cacheBuffer = baseItem.width * 1.5;
491+ }
492+ } else {
493+ // If on the buffered viewport but either not in the real viewport
494+ // or in the viewport of the non current scope, use displayMargin + cacheBuffer
495+ // to render asynchronously the width of cards
454− item.displayMarginBeginning = displayMarginBeginning - visibleRange;
496+ item.displayMarginBeginning = 0;
455− item.displayMarginEnd = displayMarginEnd - visibleRange;
497+ item.displayMarginEnd = -item.innerWidth;
456− item.cacheBuffer = visibleRange;
498+ item.cacheBuffer = item.innerWidth;
499+ }
457 }
500 }
458 }
501 }
459 }
502 }
@@ -565,6 +608,7 @@ FocusScope {
565 onBackClicked: scopeView.backClicked()
608 onBackClicked: scopeView.backClicked()
566 onSettingsClicked: subPageLoader.openSubPage("settings")
609 onSettingsClicked: subPageLoader.openSubPage("settings")
567 onFavoriteClicked: scopeView.scope.favorite = !scopeView.scope.favorite
610 onFavoriteClicked: scopeView.scope.favorite = !scopeView.scope.favorite
611+ onSearchTextFieldFocused: scopeView.showHeader()
568 }
612 }
569 }
613 }
570 }
614 }
qml/Dash/PageHeader.qml+2
@@ -51,6 +51,7 @@ Item {
51 signal storeClicked()
51 signal storeClicked()
52 signal settingsClicked()
52 signal settingsClicked()
53 signal favoriteClicked()
53 signal favoriteClicked()
54+ signal searchTextFieldFocused()
54
55
55 onScopeStyleChanged: refreshLogo()
56 onScopeStyleChanged: refreshLogo()
56 onSearchQueryChanged: {
57 onSearchQueryChanged: {
@@ -228,6 +229,7 @@ Item {
228
229
229 onActiveFocusChanged: {
230 onActiveFocusChanged: {
230 if (activeFocus) {
231 if (activeFocus) {
232+ root.searchTextFieldFocused();
231 root.openSearchHistory();
233 root.openSearchHistory();
232 }
234 }
233 }
235 }
qml/Dash/Previews/Preview.qml+1
@@ -65,6 +65,7 @@ Item {
65
65
66 delegate: ListView {
66 delegate: ListView {
67 id: column
67 id: column
68+ objectName: "previewListRow" + index
68 anchors { top: parent.top; bottom: parent.bottom }
69 anchors { top: parent.top; bottom: parent.bottom }
69 width: row.columnWidth
70 width: row.columnWidth
70 spacing: row.spacing
71 spacing: row.spacing
qml/Dash/ScopeSettings/ScopeSettingBoolean.qml+3−1
@@ -38,7 +38,9 @@ ScopeSetting {
38 rightMargin: units.gu(1)
38 rightMargin: units.gu(1)
39 verticalCenter: parent.verticalCenter
39 verticalCenter: parent.verticalCenter
40 }
40 }
41+ // TODO The translation should ideally come from the bottom layers and not be hardcoded here
42+ // https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1393438
41− text: widgetData.displayName
43+ text: widgetData.settingId == "internal.location" ? i18n.tr("Enable location data") : widgetData.displayName
42 elide: Text.ElideMiddle
44 elide: Text.ElideMiddle
43 maximumLineCount: 2
45 maximumLineCount: 2
44 wrapMode: Text.Wrap
46 wrapMode: Text.Wrap
qml/Dash/ScopesListCategoryItem.qml+1
@@ -53,6 +53,7 @@ MouseArea {
53 id: shapeImage
53 id: shapeImage
54 cache: true
54 cache: true
55 fillMode: Image.PreserveAspectCrop
55 fillMode: Image.PreserveAspectCrop
56+ sourceSize { width: shape.width; height: shape.height }
56 }
57 }
57 }
58 }
58
59
qml/Greeter/Greeter.qml+8
@@ -30,6 +30,9 @@ Showable {
30
30
31 property url background
31 property url background
32
32
33+ // so that it can be replaced in tests with a mock object
34+ property var inputMethod: Qt.inputMethod
35+
33 prepareToHide: function () {
36 prepareToHide: function () {
34 hideTranslation.to = greeter.x > 0 || d.forceRightOnNextHideAnimation ? greeter.width : -greeter.width;
37 hideTranslation.to = greeter.x > 0 || d.forceRightOnNextHideAnimation ? greeter.width : -greeter.width;
35 d.forceRightOnNextHideAnimation = false;
38 d.forceRightOnNextHideAnimation = false;
@@ -102,6 +105,11 @@ Showable {
102 }
105 }
103 onUnlocked: greeter.unlocked(uid);
106 onUnlocked: greeter.unlocked(uid);
104 }
107 }
108+ Binding {
109+ target: greeterContentLoader.item
110+ property: "inputMethod"
111+ value: greeter.inputMethod
112+ }
105 }
113 }
106
114
107 DragHandle {
115 DragHandle {
qml/Greeter/GreeterContent.qml+7−2
@@ -24,6 +24,8 @@ Item {
24 id: root
24 id: root
25 anchors.fill: parent
25 anchors.fill: parent
26
26
27+ property var inputMethod
28+
27 property bool ready: background.source == "" || background.status == Image.Ready || background.status == Image.Error
29 property bool ready: background.source == "" || background.status == Image.Ready || background.status == Image.Error
28
30
29 signal selected(int uid)
31 signal selected(int uid)
@@ -74,10 +76,12 @@ Item {
74 anchors {
76 anchors {
75 left: parent.left
77 left: parent.left
76 leftMargin: Math.min(parent.width * 0.16, units.gu(20))
78 leftMargin: Math.min(parent.width * 0.16, units.gu(20))
77− verticalCenter: parent.verticalCenter
79+ top: parent.top
78 }
80 }
79 width: units.gu(29)
81 width: units.gu(29)
80− height: parent.height
82+ height: inputMethod && inputMethod.visible ? parent.height - inputMethod.keyboardRectangle.height
83+ : parent.height
84+ Behavior on height { UbuntuNumberAnimation {} }
81
85
82 // TODO: Once we have a system API for determining which mode we are
86 // TODO: Once we have a system API for determining which mode we are
83 // in, tablet/phone/desktop, that should be used instead of narrowMode.
87 // in, tablet/phone/desktop, that should be used instead of narrowMode.
@@ -117,6 +121,7 @@ Item {
117 objectName: "infographics"
121 objectName: "infographics"
118 height: narrowMode ? parent.height : 0.75 * parent.height
122 height: narrowMode ? parent.height : 0.75 * parent.height
119 model: greeterContentLoader.infographicModel
123 model: greeterContentLoader.infographicModel
124+ clip: true // clip large data bubbles
120
125
121 property string selectedUser
126 property string selectedUser
122 property string infographicUser: AccountsService.statsWelcomeScreen ? selectedUser : ""
127 property string infographicUser: AccountsService.statsWelcomeScreen ? selectedUser : ""
qml/Launcher/Launcher.qml+13−7
@@ -26,6 +26,7 @@ Item {
26 property bool autohideEnabled: false
26 property bool autohideEnabled: false
27 property bool available: true // can be used to disable all interactions
27 property bool available: true // can be used to disable all interactions
28 property alias inverted: panel.inverted
28 property alias inverted: panel.inverted
29+ property bool shadeBackground: true // can be used to disable background shade when launcher is visible
29
30
30 property int panelWidth: units.gu(8)
31 property int panelWidth: units.gu(8)
31 property int dragAreaWidth: units.gu(1)
32 property int dragAreaWidth: units.gu(1)
@@ -33,6 +34,10 @@ Item {
33 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
34 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
34 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
35 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
35
36
37+ readonly property bool dragging: dragArea.dragging
38+ readonly property real dragDistance: dragArea.dragging ? dragArea.touchX : 0
39+ readonly property real visibleWidth: panel.width + panel.x
40+
36 readonly property bool shown: panel.x > -panel.width
41 readonly property bool shown: panel.x > -panel.width
37
42
38 // emitted when an application is selected
43 // emitted when an application is selected
@@ -154,7 +159,7 @@ Item {
154
159
155 MouseArea {
160 MouseArea {
156 id: launcherDragArea
161 id: launcherDragArea
157− enabled: root.state == "visible"
162+ enabled: root.available && root.state == "visible"
158 anchors.fill: panel
163 anchors.fill: panel
159 anchors.rightMargin: -units.gu(2)
164 anchors.rightMargin: -units.gu(2)
160 drag {
165 drag {
@@ -180,7 +185,7 @@ Item {
180 right: parent.right
185 right: parent.right
181 bottom: parent.bottom
186 bottom: parent.bottom
182 }
187 }
183− enabled: root.state == "visible"
188+ enabled: root.shadeBackground && root.state == "visible"
184 onPressed: {
189 onPressed: {
185 root.state = ""
190 root.state = ""
186 }
191 }
@@ -190,7 +195,7 @@ Item {
190 id: backgroundShade
195 id: backgroundShade
191 anchors.fill: parent
196 anchors.fill: parent
192 color: "black"
197 color: "black"
193− opacity: root.state == "visible" ? 0.6 : 0
198+ opacity: root.shadeBackground && root.state == "visible" ? 0.6 : 0
194
199
195 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.BriskDuration } }
200 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.BriskDuration } }
196 }
201 }
@@ -198,14 +203,14 @@ Item {
198 LauncherPanel {
203 LauncherPanel {
199 id: panel
204 id: panel
200 objectName: "launcherPanel"
205 objectName: "launcherPanel"
201− enabled: root.available
206+ enabled: root.available && root.state == "visible"
202 width: root.panelWidth
207 width: root.panelWidth
203 anchors {
208 anchors {
204 top: parent.top
209 top: parent.top
205 bottom: parent.bottom
210 bottom: parent.bottom
206 }
211 }
207 x: -width
212 x: -width
208− visible: x > -width || dragArea.status === DirectionalDragArea.Undecided
213+ visible: root.x > 0 || x > -width || dragArea.status === DirectionalDragArea.Undecided
209 model: LauncherModel
214 model: LauncherModel
210
215
211 property bool animate: true
216 property bool animate: true
@@ -247,6 +252,7 @@ Item {
247 direction: Direction.Rightwards
252 direction: Direction.Rightwards
248
253
249 enabled: root.available
254 enabled: root.available
255+ x: -root.x // so if launcher is adjusted relative to screen, we stay put (like tutorial does when teasing)
250 width: root.dragAreaWidth
256 width: root.dragAreaWidth
251 height: root.height
257 height: root.height
252
258
@@ -260,7 +266,7 @@ Item {
260 // would appear right next to the user's finger out of nowhere.
266 // would appear right next to the user's finger out of nowhere.
261 // Instead, we make the panel go towards the user's finger in several
267 // Instead, we make the panel go towards the user's finger in several
262 // steps. ie., in an animated way.
268 // steps. ie., in an animated way.
263− var targetPanelX = Math.min(0, touchX - panel.width)
269+ var targetPanelX = Math.min(0, touchX - panel.width) - root.x
264 var delta = targetPanelX - panel.x
270 var delta = targetPanelX - panel.x
265 // the trick is not to go all the way (1.0) as it would cause a sudden jump
271 // the trick is not to go all the way (1.0) as it would cause a sudden jump
266 panel.x += 0.4 * delta
272 panel.x += 0.4 * delta
@@ -292,7 +298,7 @@ Item {
292 name: "visible"
298 name: "visible"
293 PropertyChanges {
299 PropertyChanges {
294 target: panel
300 target: panel
295− x: 0
301+ x: -root.x // so we never go past panelWidth, even when teased by tutorial
296 }
302 }
297 },
303 },
298 State {
304 State {
qml/Launcher/LauncherPanel.qml+7−5
@@ -149,14 +149,14 @@ Rectangle {
149 id: snapToBottomAnimation
149 id: snapToBottomAnimation
150 target: launcherListView
150 target: launcherListView
151 property: "contentY"
151 property: "contentY"
152− to: launcherListView.originY
152+ to: launcherListView.originY + launcherListView.topMargin
153 }
153 }
154
154
155 UbuntuNumberAnimation {
155 UbuntuNumberAnimation {
156 id: snapToTopAnimation
156 id: snapToTopAnimation
157 target: launcherListView
157 target: launcherListView
158 property: "contentY"
158 property: "contentY"
159− to: launcherListView.contentHeight - launcherListView.height + launcherListView.originY
159+ to: launcherListView.contentHeight - launcherListView.height + launcherListView.originY - launcherListView.topMargin
160 }
160 }
161
161
162 displaced: Transition {
162 displaced: Transition {
@@ -521,12 +521,12 @@ Rectangle {
521 left: parent.left
521 left: parent.left
522 leftMargin: (quickList.item.width - units.gu(1)) / 2 - width / 2
522 leftMargin: (quickList.item.width - units.gu(1)) / 2 - width / 2
523 verticalCenter: parent.verticalCenter
523 verticalCenter: parent.verticalCenter
524− verticalCenterOffset: (parent.height / 2 + units.dp(3)) * (quickList.offset > 0 ? 1 : -1)
524+ verticalCenterOffset: (parent.height / 2 + units.dp(3)) * (quickList.offset > 0 ? 1 : -1) * (root.inverted ? 1 : -1)
525 }
525 }
526 height: units.gu(1)
526 height: units.gu(1)
527 width: units.gu(2)
527 width: units.gu(2)
528 source: "graphics/quicklist_tooltip.png"
528 source: "graphics/quicklist_tooltip.png"
529− rotation: quickList.offset > 0 ? 0 : 180
529+ rotation: (quickList.offset > 0 ? 0 : 180) + (root.inverted ? 0 : 180)
530 }
530 }
531
531
532 InverseMouseArea {
532 InverseMouseArea {
@@ -543,7 +543,9 @@ Rectangle {
543 id: quickList
543 id: quickList
544 objectName: "quickList"
544 objectName: "quickList"
545 color: "#f5f5f5"
545 color: "#f5f5f5"
546− width: units.gu(30)
546+ // Because we're setting left/right anchors depending on orientation, it will break the
547+ // width setting after rotating twice. This makes sure we also re-apply width on rotation
548+ width: root.inverted ? units.gu(30) : units.gu(30)
547 height: quickListColumn.height
549 height: quickListColumn.height
548 visible: quickListShape.visible
550 visible: quickListShape.visible
549 anchors {
551 anchors {
qml/Notifications/Notification.qml+36−6
@@ -40,6 +40,7 @@ Item {
40 property bool fullscreen: false
40 property bool fullscreen: false
41 property int maxHeight
41 property int maxHeight
42 property int margins
42 property int margins
43+ readonly property bool draggable: (type === Notification.SnapDecision && state === "contracted") || type === Notification.Interactive || type === Notification.Ephemeral
43 readonly property bool darkOnBright: panel.indicators.shown || type === Notification.SnapDecision
44 readonly property bool darkOnBright: panel.indicators.shown || type === Notification.SnapDecision
44 readonly property color red: "#fc4949"
45 readonly property color red: "#fc4949"
45 readonly property color green: "#3fb24f"
46 readonly property color green: "#3fb24f"
@@ -52,7 +53,7 @@ Item {
52 implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : outterColumn.height - shapedBack.anchors.topMargin + contentSpacing * 2) : 0
53 implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : outterColumn.height - shapedBack.anchors.topMargin + contentSpacing * 2) : 0
53
54
54 color: (type === Notification.Confirmation && notificationList.useModal && !greeter.shown) || darkOnBright ? sdLightGrey : Qt.rgba(0.132, 0.117, 0.109, 0.97)
55 color: (type === Notification.Confirmation && notificationList.useModal && !greeter.shown) || darkOnBright ? sdLightGrey : Qt.rgba(0.132, 0.117, 0.109, 0.97)
55− opacity: 1 // FIXME: 1 because of LP: #1354406 workaround, has to be 0 really
56+ opacity: 1 - (x / notification.width) // FIXME: non-zero initially because of LP: #1354406 workaround, we want this to start at 0 upon creation eventually
56
57
57 state: {
58 state: {
58 var result = "";
59 var result = "";
@@ -80,18 +81,28 @@ Item {
80 id: sound
81 id: sound
81 objectName: "sound"
82 objectName: "sound"
82 audioRole: MediaPlayer.alert
83 audioRole: MediaPlayer.alert
83− source: hints["suppress-sound"] != "true" && hints["sound-file"] != undefined ? hints["sound-file"] : ""
84+ source: hints["suppress-sound"] !== "true" && hints["sound-file"] !== undefined ? hints["sound-file"] : ""
84 }
85 }
85
86
86 // FIXME: using onCompleted because of LP: #1354406 workaround, has to be onOpacityChanged really
87 // FIXME: using onCompleted because of LP: #1354406 workaround, has to be onOpacityChanged really
87 Component.onCompleted: {
88 Component.onCompleted: {
88− if (opacity == 1.0 && hints["suppress-sound"] != "true" && sound.source != "") {
89+ if (opacity == 1.0 && hints["suppress-sound"] !== "true" && sound.source !== "") {
89 sound.play();
90 sound.play();
90 }
91 }
91 }
92 }
92
93
94+ Behavior on x {
95+ id: normalXBehavior
96+
97+ enabled: draggable
98+ UbuntuNumberAnimation {
99+ duration: UbuntuAnimation.FastDuration
100+ easing.type: Easing.OutBounce
101+ }
102+ }
103+
93 onHintsChanged: {
104 onHintsChanged: {
94− if (type === Notification.Confirmation && opacity == 1.0 && hints["suppress-sound"] != "true" && sound.source != "") {
105+ if (type === Notification.Confirmation && opacity == 1.0 && hints["suppress-sound"] !== "true" && sound.source !== "") {
95 sound.play();
106 sound.play();
96 }
107 }
97 }
108 }
@@ -146,6 +157,12 @@ Item {
146 opacity: parent.opacity
157 opacity: parent.opacity
147 }
158 }
148
159
160+ onXChanged: {
161+ if (draggable && notification.x > 0.75 * notification.width) {
162+ notification.notification.close()
163+ }
164+ }
165+
149 Item {
166 Item {
150 id: contents
167 id: contents
151 anchors.fill: fullscreen ? nonShapedBack : shapedBack
168 anchors.fill: fullscreen ? nonShapedBack : shapedBack
@@ -169,7 +186,7 @@ Item {
169 actions: paths.actions
186 actions: paths.actions
170 menuObjectPath: paths.menuObjectPath
187 menuObjectPath: paths.menuObjectPath
171 onNameOwnerChanged: {
188 onNameOwnerChanged: {
172− if (lastNameOwner != "" && nameOwner == "" && notification.notification != undefined) {
189+ if (lastNameOwner !== "" && nameOwner === "" && notification.notification !== undefined) {
173 notification.notification.close()
190 notification.notification.close()
174 }
191 }
175 lastNameOwner = nameOwner
192 lastNameOwner = nameOwner
@@ -181,6 +198,12 @@ Item {
181
198
182 anchors.fill: parent
199 anchors.fill: parent
183 objectName: "interactiveArea"
200 objectName: "interactiveArea"
201+
202+ drag.target: draggable ? notification : undefined
203+ drag.axis: Drag.XAxis
204+ drag.minimumX: 0
205+ drag.maximumX: notification.width
206+
184 onClicked: {
207 onClicked: {
185 if (notification.type == Notification.Interactive) {
208 if (notification.type == Notification.Interactive) {
186 notification.notification.invokeAction(actionRepeater.itemAt(0).actionId)
209 notification.notification.invokeAction(actionRepeater.itemAt(0).actionId)
@@ -188,6 +211,13 @@ Item {
188 notificationList.currentIndex = index;
211 notificationList.currentIndex = index;
189 }
212 }
190 }
213 }
214+ onReleased: {
215+ if (notification.x < notification.width / 2) {
216+ notification.x = 0
217+ } else {
218+ notification.x = notification.width
219+ }
220+ }
191 }
221 }
192
222
193 Column {
223 Column {
@@ -447,7 +477,7 @@ Item {
447 right: parent.right
477 right: parent.right
448 margins: contentSpacing
478 margins: contentSpacing
449 }
479 }
450− visible: notification.type == Notification.SnapDecision && actionRepeater.count > 0 && !oneOverTwoCase.visible
480+ visible: notification.type === Notification.SnapDecision && actionRepeater.count > 0 && !oneOverTwoCase.visible
451 spacing: units.gu(2)
481 spacing: units.gu(2)
452 layoutDirection: Qt.RightToLeft
482 layoutDirection: Qt.RightToLeft
453
483
qml/Notifications/Notifications.qml+2−6
@@ -41,15 +41,11 @@ ListView {
41 property bool topmostIsFullscreen: false
41 property bool topmostIsFullscreen: false
42 spacing: topmostIsFullscreen ? 0 : units.gu(.5)
42 spacing: topmostIsFullscreen ? 0 : units.gu(.5)
43
43
44− // FIXME: This doesn't make any sense and results in a binding loop
45− currentIndex: (currentIndex < 1 && count > 1) ? 1 : -1
44+ currentIndex: count > 1 ? 1 : -1
46
45
47 delegate: Notification {
46 delegate: Notification {
48 objectName: "notification" + index
47 objectName: "notification" + index
49− anchors {
50− left: parent.left
48+ width: parent.width
51− right: parent.right
52− }
53 type: model.type
49 type: model.type
54 hints: model.hints
50 hints: model.hints
55 iconSource: model.icon
51 iconSource: model.icon
qml/Panel/Panel.qml+5−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -90,6 +90,9 @@ Item {
90 right: indicators.left
90 right: indicators.left
91 }
91 }
92 saturation: 1 - indicators.unitProgress
92 saturation: 1 - indicators.unitProgress
93+
94+ // Don't let input event pass trough
95+ MouseArea { anchors.fill: parent }
93 }
96 }
94
97
95 Image {
98 Image {
@@ -110,8 +113,7 @@ Item {
110 right: indicators.left
113 right: indicators.left
111 }
114 }
112 height: indicators.minimizedPanelHeight
115 height: indicators.minimizedPanelHeight
113− enabled: callHint.visible
114− onClicked: callHint.showLiveCall()
116+ onClicked: { if (callHint.visible) { callHint.showLiveCall(); } }
115 }
117 }
116
118
117 IndicatorsMenu {
119 IndicatorsMenu {
qml/Shell.qml+50−25
@@ -36,6 +36,7 @@ import "Panel"
36import "Components"
36import "Components"
37import "Notifications"
37import "Notifications"
38import "Stages"
38import "Stages"
39+import "Tutorial"
39import "Wizard"
40import "Wizard"
40import Unity.Notifications 1.0 as NotificationBackend
41import Unity.Notifications 1.0 as NotificationBackend
41import Unity.Session 0.1
42import Unity.Session 0.1
@@ -62,7 +63,7 @@ Item {
62
63
63 readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock
64 readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock
64 readonly property alias hasLockedApp: greeter.hasLockedApp
65 readonly property alias hasLockedApp: greeter.hasLockedApp
65− readonly property bool forcedUnlock: edgeDemo.running
66+ readonly property bool forcedUnlock: tutorial.running
66 onForcedUnlockChanged: if (forcedUnlock) lockscreen.hide()
67 onForcedUnlockChanged: if (forcedUnlock) lockscreen.hide()
67
68
68 property bool sideStageEnabled: shell.width >= units.gu(100)
69 property bool sideStageEnabled: shell.width >= units.gu(100)
@@ -171,7 +172,7 @@ Item {
171
172
172 ScreenGrabber {
173 ScreenGrabber {
173 id: screenGrabber
174 id: screenGrabber
174− z: edgeDemo.z + 10
175+ z: dialogs.z + 10
175 enabled: Powerd.status === Powerd.On
176 enabled: Powerd.status === Powerd.On
176 }
177 }
177
178
@@ -231,14 +232,16 @@ Item {
231 }
232 }
232
233
233 onApplicationAdded: {
234 onApplicationAdded: {
234− if (greeter.shown && appId != "unity8-dash") {
235+ if (appId != "unity8-dash") {
236+ if (greeter.shown) {
235− greeter.startUnlock()
237+ greeter.startUnlock();
238+ }
236
239
237 // If this happens on first boot, we may be in edge
240 // If this happens on first boot, we may be in edge
238 // tutorial or wizard while receiving a call. But a call
241 // tutorial or wizard while receiving a call. But a call
239 // is more important than wizard so just bail out of those.
242 // is more important than wizard so just bail out of those.
240− if (edgeDemo.running) {
243+ if (tutorial.running) {
241− edgeDemo.hideEdgeDemos();
244+ tutorial.finish();
242 wizard.hide();
245 wizard.hide();
243 }
246 }
244 }
247 }
@@ -266,6 +269,15 @@ Item {
266 source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
269 source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
267 : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
270 : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
268
271
272+ property bool interactive: tutorial.spreadEnabled
273+ && !greeter.shown
274+ && !lockscreen.shown
275+ && panel.indicators.fullyClosed
276+ && launcher.progress == 0
277+ && !notifications.useModal
278+
279+ onInteractiveChanged: { if (interactive) { focus = true; } }
280+
269 Binding {
281 Binding {
270 target: applicationsDisplayLoader.item
282 target: applicationsDisplayLoader.item
271 property: "objectName"
283 property: "objectName"
@@ -285,12 +297,12 @@ Item {
285 Binding {
297 Binding {
286 target: applicationsDisplayLoader.item
298 target: applicationsDisplayLoader.item
287 property: "interactive"
299 property: "interactive"
288− value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
300+ value: applicationsDisplayLoader.interactive
289 }
301 }
290 Binding {
302 Binding {
291 target: applicationsDisplayLoader.item
303 target: applicationsDisplayLoader.item
292 property: "spreadEnabled"
304 property: "spreadEnabled"
293− value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
305+ value: tutorial.spreadEnabled && !greeter.hasLockedApp
294 }
306 }
295 Binding {
307 Binding {
296 target: applicationsDisplayLoader.item
308 target: applicationsDisplayLoader.item
@@ -390,7 +402,7 @@ Item {
390 onShownChanged: if (shown) greeter.lockedApp = ""
402 onShownChanged: if (shown) greeter.lockedApp = ""
391
403
392 function maybeShow() {
404 function maybeShow() {
393− if (!shell.forcedUnlock) {
405+ if (!shell.forcedUnlock && !shown) {
394 showNow();
406 showNow();
395 }
407 }
396 }
408 }
@@ -623,9 +635,13 @@ Item {
623 LauncherModel.setUser(user);
635 LauncherModel.setUser(user);
624 }
636 }
625
637
638+ onTapped: {
639+ if (!tutorial.running) {
626− onTapped: launcher.tease()
640+ launcher.tease();
641+ }
642+ }
627 onDraggingChanged: {
643 onDraggingChanged: {
628− if (dragging) {
644+ if (dragging && !tutorial.running) {
629 launcher.tease();
645 launcher.tease();
630 }
646 }
631 }
647 }
@@ -663,7 +679,7 @@ Item {
663
679
664 onStatusChanged: {
680 onStatusChanged: {
665 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
681 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
666− !callManager.hasCalls && !edgeDemo.running) {
682+ !callManager.hasCalls && !tutorial.running) {
667 // We don't want to simply call greeter.showNow() here, because
683 // We don't want to simply call greeter.showNow() here, because
668 // that will take too long. Qt will delay button event
684 // that will take too long. Qt will delay button event
669 // handling until the greeter is done loading and may think the
685 // handling until the greeter is done loading and may think the
@@ -679,7 +695,7 @@ Item {
679 }
695 }
680
696
681 function showHome() {
697 function showHome() {
682− if (edgeDemo.running) {
698+ if (tutorial.running) {
683 return
699 return
684 }
700 }
685
701
@@ -721,15 +737,17 @@ Item {
721 anchors.fill: parent //because this draws indicator menus
737 anchors.fill: parent //because this draws indicator menus
722 indicators {
738 indicators {
723 hides: [launcher]
739 hides: [launcher]
724− available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
740+ available: tutorial.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
725− contentEnabled: edgeDemo.panelContentEnabled
741+ contentEnabled: tutorial.panelContentEnabled
726 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
742 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
727
743
728 minimizedPanelHeight: units.gu(3)
744 minimizedPanelHeight: units.gu(3)
729 expandedPanelHeight: units.gu(7)
745 expandedPanelHeight: units.gu(7)
730
746
731 indicatorsModel: Indicators.IndicatorsModel {
747 indicatorsModel: Indicators.IndicatorsModel {
748+ // TODO: This should be sourced by device type (e.g. "desktop", "tablet", "phone"...)
749+ profile: indicatorProfile
732− Component.onCompleted: load(indicatorProfile);
750+ Component.onCompleted: load()
733 }
751 }
734 }
752 }
735 callHint {
753 callHint {
@@ -755,8 +773,9 @@ Item {
755 anchors.bottom: parent.bottom
773 anchors.bottom: parent.bottom
756 width: parent.width
774 width: parent.width
757 dragAreaWidth: shell.edgeSize
775 dragAreaWidth: shell.edgeSize
758− available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
776+ available: tutorial.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
759 inverted: usageModeSettings.usageMode === "Staged"
777 inverted: usageModeSettings.usageMode === "Staged"
778+ shadeBackground: !tutorial.running
760
779
761 onShowDashHome: showHome()
780 onShowDashHome: showHome()
762 onDash: showDash()
781 onDash: showDash()
@@ -769,7 +788,7 @@ Item {
769 if (greeter.hasLockedApp) {
788 if (greeter.hasLockedApp) {
770 greeter.startUnlock()
789 greeter.startUnlock()
771 }
790 }
772− if (!edgeDemo.running)
791+ if (!tutorial.running)
773 shell.activateApplication(appId)
792 shell.activateApplication(appId)
774 }
793 }
775 onShownChanged: {
794 onShownChanged: {
@@ -842,15 +861,21 @@ Item {
842 }
861 }
843 }
862 }
844
863
845− EdgeDemo {
864+ Tutorial {
846− id: edgeDemo
865+ id: tutorial
847− objectName: "edgeDemo"
866+ objectName: "tutorial"
848− z: dialogs.z + 10
867+ active: AccountsService.demoEdges
849− paused: Powerd.status === Powerd.Off || wizard.active // Saves power
868+ paused: LightDM.Greeter.active
850− greeter: greeter
851 launcher: launcher
869 launcher: launcher
852 panel: panel
870 panel: panel
853 stages: stages
871 stages: stages
872+ overlay: overlay
873+ edgeSize: shell.edgeSize
874+
875+ onFinished: {
876+ AccountsService.demoEdges = false;
877+ active = false; // for immediate response / if AS is having problems
878+ }
854 }
879 }
855
880
856 Connections {
881 Connections {
@@ -860,7 +885,7 @@ Item {
860
885
861 Rectangle {
886 Rectangle {
862 id: shutdownFadeOutRectangle
887 id: shutdownFadeOutRectangle
863− z: edgeDemo.z + 10
888+ z: screenGrabber.z + 10
864 enabled: false
889 enabled: false
865 visible: false
890 visible: false
866 color: "black"
891 color: "black"
qml/Stages/ApplicationWindow.qml+17−2
@@ -18,7 +18,7 @@ import QtQuick 2.0
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19import Unity.Application 0.1
19import Unity.Application 0.1
20
20
21−Item {
21+FocusScope {
22 id: root
22 id: root
23
23
24 // to be read from outside
24 // to be read from outside
@@ -43,6 +43,7 @@ Item {
43 readonly property color splashColor: root.application ? root.application.splashColor : "#00000000"
43 readonly property color splashColor: root.application ? root.application.splashColor : "#00000000"
44 readonly property color splashColorHeader: root.application ? root.application.splashColorHeader : "#00000000"
44 readonly property color splashColorHeader: root.application ? root.application.splashColorHeader : "#00000000"
45 readonly property color splashColorFooter: root.application ? root.application.splashColorFooter : "#00000000"
45 readonly property color splashColorFooter: root.application ? root.application.splashColorFooter : "#00000000"
46+ readonly property url defaultScreenshot: root.application ? root.application.defaultScreenshot : ""
46
47
47 // Whether the Application had a surface before but lost it.
48 // Whether the Application had a surface before but lost it.
48 property bool hadSurface: sessionContainer.surfaceContainer.hadSurface
49 property bool hadSurface: sessionContainer.surfaceContainer.hadSurface
@@ -72,7 +73,7 @@ Item {
72 Image {
73 Image {
73 id: screenshotImage
74 id: screenshotImage
74 objectName: "screenshotImage"
75 objectName: "screenshotImage"
75− source: ""
76+ source: d.defaultScreenshot
76 anchors.fill: parent
77 anchors.fill: parent
77 antialiasing: !root.interactive
78 antialiasing: !root.interactive
78
79
@@ -120,9 +121,12 @@ Item {
120 d.surfaceInitialized = false;
121 d.surfaceInitialized = false;
121 }
122 }
122 }
123 }
124+
125+ focus: true
123 }
126 }
124
127
125 StateGroup {
128 StateGroup {
129+ id: stateGroup
126 objectName: "applicationWindowStateGroup"
130 objectName: "applicationWindowStateGroup"
127 states: [
131 states: [
128 State {
132 State {
@@ -215,6 +219,17 @@ Item {
215 PropertyAction { target: screenshotImage; property: "source"; value: "" }
219 PropertyAction { target: screenshotImage; property: "source"; value: "" }
216 }
220 }
217 },
221 },
222+ Transition {
223+ from: "splashScreen"; to: "screenshot"
224+ SequentialAnimation {
225+ PropertyAction { target: screenshotImage
226+ property: "visible"; value: true }
227+ UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
228+ from: 0.0; to: 1.0
229+ duration: UbuntuAnimation.BriskDuration }
230+ PropertyAction { target: splashLoader; property: "active"; value: false }
231+ }
232+ },
218 Transition {
233 Transition {
219 from: "surface"; to: "void"
234 from: "surface"; to: "void"
220 SequentialAnimation {
235 SequentialAnimation {
qml/Stages/DesktopStage.qml+2−2
@@ -38,8 +38,6 @@ Item {
38 Connections {
38 Connections {
39 target: ApplicationManager
39 target: ApplicationManager
40 onApplicationAdded: {
40 onApplicationAdded: {
41− // Initial placement to avoid having the window decoration behind the panel
42− appRepeater.itemAt(ApplicationManager.count-1).y = units.gu(3)
43 ApplicationManager.requestFocusApplication(ApplicationManager.get(ApplicationManager.count-1).appId)
41 ApplicationManager.requestFocusApplication(ApplicationManager.get(ApplicationManager.count-1).appId)
44 }
42 }
45
43
@@ -91,6 +89,7 @@ Item {
91 delegate: Item {
89 delegate: Item {
92 id: appDelegate
90 id: appDelegate
93 z: ApplicationManager.count - index
91 z: ApplicationManager.count - index
92+ y: units.gu(3)
94 width: units.gu(60)
93 width: units.gu(60)
95 height: units.gu(50)
94 height: units.gu(50)
96
95
@@ -121,6 +120,7 @@ Item {
121 minWidth: appDelegate.minWidth
120 minWidth: appDelegate.minWidth
122 minHeight: appDelegate.minHeight
121 minHeight: appDelegate.minHeight
123 resizeHandleWidth: units.gu(0.5)
122 resizeHandleWidth: units.gu(0.5)
123+ windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
124
124
125 onPressed: ApplicationManager.requestFocusApplication(model.appId)
125 onPressed: ApplicationManager.requestFocusApplication(model.appId)
126 }
126 }
qml/Stages/PhoneStage.qml+64−34
@@ -32,6 +32,21 @@ Rectangle {
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
34 property int orientation: Qt.PortraitOrientation
35+ property QtObject applicationManager: ApplicationManager
36+ property bool focusFirstApp: true // If false, focused app will appear on right edge like other apps
37+ property bool altTabEnabled: true
38+ property real startScale: 1.1
39+ property real endScale: 0.7
40+
41+ // How far left the stage has been dragged
42+ readonly property real dragProgress: spreadRepeater.count > 0 ? -spreadRepeater.itemAt(0).xTranslate : 0
43+
44+ readonly property alias dragging: spreadDragArea.dragging
45+
46+ // Only used by the tutorial right now, when it is teasing the right edge
47+ property real dragAreaOverlap
48+
49+ signal opened()
35
50
36 color: "#111111"
51 color: "#111111"
37
52
@@ -52,30 +67,30 @@ Rectangle {
52 if (inverseProgress == 0 && priv.oldInverseProgress > 0) {
67 if (inverseProgress == 0 && priv.oldInverseProgress > 0) {
53 // left edge drag released. Minimum distance is given by design.
68 // left edge drag released. Minimum distance is given by design.
54 if (priv.oldInverseProgress > units.gu(22)) {
69 if (priv.oldInverseProgress > units.gu(22)) {
55− ApplicationManager.requestFocusApplication("unity8-dash");
70+ applicationManager.requestFocusApplication("unity8-dash");
56 }
71 }
57 }
72 }
58 priv.oldInverseProgress = inverseProgress;
73 priv.oldInverseProgress = inverseProgress;
59 }
74 }
60
75
61 Connections {
76 Connections {
62− target: ApplicationManager
77+ target: applicationManager
63
78
64 onFocusRequested: {
79 onFocusRequested: {
65 if (spreadView.phase > 0) {
80 if (spreadView.phase > 0) {
66 spreadView.snapTo(priv.indexOf(appId));
81 spreadView.snapTo(priv.indexOf(appId));
67 } else {
82 } else {
68− ApplicationManager.focusApplication(appId);
83+ applicationManager.focusApplication(appId);
69 }
84 }
70 }
85 }
71
86
72 onApplicationAdded: {
87 onApplicationAdded: {
73 if (spreadView.phase == 2) {
88 if (spreadView.phase == 2) {
74− spreadView.snapTo(ApplicationManager.count - 1);
89+ spreadView.snapTo(applicationManager.count - 1);
75 } else {
90 } else {
76 spreadView.phase = 0;
91 spreadView.phase = 0;
77 spreadView.contentX = -spreadView.shift;
92 spreadView.contentX = -spreadView.shift;
78− ApplicationManager.focusApplication(appId);
93+ applicationManager.focusApplication(appId);
79 }
94 }
80 }
95 }
81
96
@@ -90,9 +105,9 @@ Rectangle {
90 }
105 }
91
106
92 function focusTopMostApp() {
107 function focusTopMostApp() {
93− if (ApplicationManager.count > 0) {
108+ if (applicationManager.count > 0) {
94− var topmostApp = ApplicationManager.get(0);
109+ var topmostApp = applicationManager.get(0);
95− ApplicationManager.focusApplication(topmostApp.appId);
110+ applicationManager.focusApplication(topmostApp.appId);
96 }
111 }
97 }
112 }
98 }
113 }
@@ -100,18 +115,21 @@ Rectangle {
100 QtObject {
115 QtObject {
101 id: priv
116 id: priv
102
117
118+ readonly property int firstSpreadIndex: root.focusFirstApp ? 1 : 0
103− property string focusedAppId: ApplicationManager.focusedApplicationId
119+ property string focusedAppId: applicationManager.focusedApplicationId
104− property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
120+ property var focusedApplication: applicationManager.findApplication(focusedAppId)
105 property var focusedAppDelegate: null
121 property var focusedAppDelegate: null
106
122
107 property real oldInverseProgress: 0
123 property real oldInverseProgress: 0
108− property bool animateX: true
124+ property bool animateX: false
109
125
110 onFocusedAppIdChanged: focusedAppDelegate = spreadRepeater.itemAt(0);
126 onFocusedAppIdChanged: focusedAppDelegate = spreadRepeater.itemAt(0);
111
127
128+ onFocusedAppDelegateChanged: focusedAppDelegate.focus = true;
129+
112 function indexOf(appId) {
130 function indexOf(appId) {
113− for (var i = 0; i < ApplicationManager.count; i++) {
131+ for (var i = 0; i < applicationManager.count; i++) {
114− if (ApplicationManager.get(i).appId == appId) {
132+ if (applicationManager.get(i).appId == appId) {
115 return i;
133 return i;
116 }
134 }
117 }
135 }
@@ -131,7 +149,7 @@ Rectangle {
131
149
132 // This indicates when the spreadView is active. That means, all the animations
150 // This indicates when the spreadView is active. That means, all the animations
133 // are activated and tiles need to line up for the spread.
151 // are activated and tiles need to line up for the spread.
134− readonly property bool active: shiftedContentX > 0 || spreadDragArea.status === DirectionalDragArea.Recognized
152+ readonly property bool active: shiftedContentX > 0 || spreadDragArea.status === DirectionalDragArea.Recognized || !root.focusFirstApp
135
153
136 // The flickable needs to fill the screen in order to get touch events all over.
154 // The flickable needs to fill the screen in order to get touch events all over.
137 // However, we don't want to the user to be able to scroll back all the way. For
155 // However, we don't want to the user to be able to scroll back all the way. For
@@ -197,10 +215,17 @@ Rectangle {
197 // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
215 // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
198 snapAnimation.targetContentX = width * positionMarker4 + 1 - shift;
216 snapAnimation.targetContentX = width * positionMarker4 + 1 - shift;
199 snapAnimation.start();
217 snapAnimation.start();
218+ root.opened();
200 }
219 }
201 }
220 }
202 function snapTo(index) {
221 function snapTo(index) {
203− if (ApplicationManager.count <= index) {
222+ if (!root.altTabEnabled) {
223+ // Reset to start instead
224+ snapAnimation.targetContentX = -shift;
225+ snapAnimation.start();
226+ return;
227+ }
228+ if (applicationManager.count <= index) {
204 // In case we're trying to snap to some non existing app, lets snap back to the first one
229 // In case we're trying to snap to some non existing app, lets snap back to the first one
205 index = 0;
230 index = 0;
206 }
231 }
@@ -215,10 +240,12 @@ Rectangle {
215 snapAnimation.start();
240 snapAnimation.start();
216 }
241 }
217
242
218− // In case the ApplicationManager already holds an app when starting up we're missing animations
243+ // In case the applicationManager already holds an app when starting up we're missing animations
219 // Make sure we end up in the same state
244 // Make sure we end up in the same state
220 Component.onCompleted: {
245 Component.onCompleted: {
221 spreadView.contentX = -spreadView.shift
246 spreadView.contentX = -spreadView.shift
247+ priv.animateX = true;
248+ snapAnimation.complete();
222 }
249 }
223
250
224 SequentialAnimation {
251 SequentialAnimation {
@@ -235,7 +262,7 @@ Rectangle {
235 ScriptAction {
262 ScriptAction {
236 script: {
263 script: {
237 if (spreadView.selectedIndex >= 0) {
264 if (spreadView.selectedIndex >= 0) {
238− ApplicationManager.focusApplication(ApplicationManager.get(spreadView.selectedIndex).appId);
265+ applicationManager.focusApplication(applicationManager.get(spreadView.selectedIndex).appId);
239
266
240 spreadView.selectedIndex = -1;
267 spreadView.selectedIndex = -1;
241 spreadView.phase = 0;
268 spreadView.phase = 0;
@@ -250,7 +277,7 @@ Rectangle {
250 // This width controls how much the spread can be flicked left/right. It's composed of:
277 // This width controls how much the spread can be flicked left/right. It's composed of:
251 // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)
278 // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)
252 // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen
279 // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen
253− width: Math.max(3, ApplicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
280+ width: Math.max(3, applicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
254 height: parent.height
281 height: parent.height
255 Behavior on width {
282 Behavior on width {
256 enabled: spreadView.closingIndex >= 0
283 enabled: spreadView.closingIndex >= 0
@@ -265,20 +292,22 @@ Rectangle {
265 x: spreadView.contentX
292 x: spreadView.contentX
266
293
267 onClicked: {
294 onClicked: {
295+ if (root.altTabEnabled) {
268− spreadView.snapTo(0);
296+ spreadView.snapTo(0);
297+ }
269 }
298 }
270
299
271 Repeater {
300 Repeater {
272 id: spreadRepeater
301 id: spreadRepeater
273 objectName: "spreadRepeater"
302 objectName: "spreadRepeater"
274− model: ApplicationManager
303+ model: applicationManager
275 delegate: TransformedSpreadDelegate {
304 delegate: TransformedSpreadDelegate {
276 id: appDelegate
305 id: appDelegate
277 objectName: "appDelegate" + index
306 objectName: "appDelegate" + index
278 startAngle: 45
307 startAngle: 45
279 endAngle: 5
308 endAngle: 5
280− startScale: 1.1
309+ startScale: root.startScale
281− endScale: 0.7
310+ endScale: root.endScale
282 startDistance: spreadView.tileDistance
311 startDistance: spreadView.tileDistance
283 endDistance: units.gu(.5)
312 endDistance: units.gu(.5)
284 width: spreadView.width
313 width: spreadView.width
@@ -286,11 +315,12 @@ Rectangle {
286 selected: spreadView.selectedIndex == index
315 selected: spreadView.selectedIndex == index
287 otherSelected: spreadView.selectedIndex >= 0 && !selected
316 otherSelected: spreadView.selectedIndex >= 0 && !selected
288 interactive: !spreadView.interactive && spreadView.phase === 0
317 interactive: !spreadView.interactive && spreadView.phase === 0
289− && spreadView.shiftedContentX === 0 && root.interactive && index === 0
318+ && spreadView.shiftedContentX === 0 && root.interactive && isFocused
290− swipeToCloseEnabled: spreadView.interactive && !snapAnimation.running
319+ swipeToCloseEnabled: spreadView.interactive && root.interactive && !snapAnimation.running
291 maximizedAppTopMargin: root.maximizedAppTopMargin
320 maximizedAppTopMargin: root.maximizedAppTopMargin
292 dropShadow: spreadView.active ||
321 dropShadow: spreadView.active ||
293 (priv.focusedAppDelegate && priv.focusedAppDelegate.x !== 0)
322 (priv.focusedAppDelegate && priv.focusedAppDelegate.x !== 0)
323+ focusFirstApp: root.focusFirstApp
294
324
295 readonly property bool isDash: model.appId == "unity8-dash"
325 readonly property bool isDash: model.appId == "unity8-dash"
296
326
@@ -298,7 +328,7 @@ Rectangle {
298
328
299 x: {
329 x: {
300 // focused app is always positioned at 0 except when following left edge drag
330 // focused app is always positioned at 0 except when following left edge drag
301− if (index == 0) {
331+ if (isFocused) {
302 if (!isDash && root.inverseProgress > 0 && spreadView.phase === 0) {
332 if (!isDash && root.inverseProgress > 0 && spreadView.phase === 0) {
303 return root.inverseProgress;
333 return root.inverseProgress;
304 }
334 }
@@ -309,10 +339,10 @@ Rectangle {
309 }
339 }
310
340
311 // Otherwise line up for the spread
341 // Otherwise line up for the spread
312− return spreadView.width + (index - 1) * spreadView.tileDistance;
342+ return spreadView.width + spreadIndex * spreadView.tileDistance;
313 }
343 }
314
344
315− application: ApplicationManager.get(index)
345+ application: applicationManager.get(index)
316 closeable: !isDash
346 closeable: !isDash
317
347
318 property real behavioredIndex: index
348 property real behavioredIndex: index
@@ -350,7 +380,7 @@ Rectangle {
350 progress: {
380 progress: {
351 var tileProgress = (spreadView.shiftedContentX - behavioredIndex * spreadView.tileDistance) / spreadView.width;
381 var tileProgress = (spreadView.shiftedContentX - behavioredIndex * spreadView.tileDistance) / spreadView.width;
352 // Tile 1 needs to move directly from the beginning...
382 // Tile 1 needs to move directly from the beginning...
353− if (behavioredIndex == 1 && spreadView.phase < 2) {
383+ if (root.focusFirstApp && behavioredIndex == 1 && spreadView.phase < 2) {
354 tileProgress += spreadView.tileDistance / spreadView.width;
384 tileProgress += spreadView.tileDistance / spreadView.width;
355 }
385 }
356 // Limiting progress to ~0 and 1.7 to avoid binding calculations when tiles are not
386 // Limiting progress to ~0 and 1.7 to avoid binding calculations when tiles are not
@@ -365,7 +395,7 @@ Rectangle {
365
395
366 // This mostly is the same as progress, just adds the snapping to phase 1 for tiles 0 and 1
396 // This mostly is the same as progress, just adds the snapping to phase 1 for tiles 0 and 1
367 animatedProgress: {
397 animatedProgress: {
368− if (spreadView.phase == 0 && index < 2) {
398+ if (spreadView.phase == 0 && index <= priv.firstSpreadIndex) {
369 if (progress < spreadView.positionMarker1) {
399 if (progress < spreadView.positionMarker1) {
370 return progress;
400 return progress;
371 } else if (progress < spreadView.positionMarker1 + 0.05){
401 } else if (progress < spreadView.positionMarker1 + 0.05){
@@ -390,11 +420,11 @@ Rectangle {
390 }
420 }
391
421
392 onClicked: {
422 onClicked: {
393− if (spreadView.phase == 2) {
423+ if (root.altTabEnabled && spreadView.phase == 2) {
394− if (ApplicationManager.focusedApplicationId == ApplicationManager.get(index).appId) {
424+ if (applicationManager.focusedApplicationId == applicationManager.get(index).appId) {
395 spreadView.snapTo(index);
425 spreadView.snapTo(index);
396 } else {
426 } else {
397− ApplicationManager.requestFocusApplication(ApplicationManager.get(index).appId);
427+ applicationManager.requestFocusApplication(applicationManager.get(index).appId);
398 }
428 }
399 }
429 }
400 }
430 }
@@ -409,7 +439,7 @@ Rectangle {
409
439
410 onClosed: {
440 onClosed: {
411 spreadView.closingIndex = index;
441 spreadView.closingIndex = index;
412− ApplicationManager.stopApplication(ApplicationManager.get(index).appId);
442+ applicationManager.stopApplication(applicationManager.get(index).appId);
413 }
443 }
414 }
444 }
415 }
445 }
@@ -422,7 +452,7 @@ Rectangle {
422 direction: Direction.Leftwards
452 direction: Direction.Leftwards
423 enabled: (spreadView.phase != 2 && root.spreadEnabled) || dragging
453 enabled: (spreadView.phase != 2 && root.spreadEnabled) || dragging
424
454
425− anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
455+ anchors { top: parent.top; right: parent.right; bottom: parent.bottom; rightMargin: -root.dragAreaOverlap }
426 width: root.dragAreaWidth
456 width: root.dragAreaWidth
427
457
428 property var gesturePoints: new Array()
458 property var gesturePoints: new Array()
qml/Stages/SessionContainer.qml+22−7
@@ -17,7 +17,7 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import "Animations"
18import "Animations"
19
19
20−Item {
20+FocusScope {
21 id: root
21 id: root
22 objectName: "sessionContainer"
22 objectName: "sessionContainer"
23 property QtObject session
23 property QtObject session
@@ -34,8 +34,8 @@ Item {
34 orientation: root.orientation
34 orientation: root.orientation
35 }
35 }
36
36
37−
38 Repeater {
37 Repeater {
38+ id: childSessionsRepeater
39 model: root.childSessions
39 model: root.childSessions
40
40
41 delegate: Loader {
41 delegate: Loader {
@@ -45,9 +45,17 @@ Item {
45 // Only way to do recursive qml items.
45 // Only way to do recursive qml items.
46 source: Qt.resolvedUrl("SessionContainer.qml")
46 source: Qt.resolvedUrl("SessionContainer.qml")
47
47
48+ z: index
49+
50+ // Since a Loader is a FocusScope, propagate its focus to the loaded Item
51+ Binding {
52+ target: item; when: item
53+ property: "focus"; value: focus
54+ }
55+
48 Binding {
56 Binding {
49 target: item; when: item
57 target: item; when: item
50− property: "interactive"; value: root.interactive
58+ property: "interactive"; value: index == (childSessionsRepeater.count - 1) && root.interactive
51 }
59 }
52
60
53 Binding {
61 Binding {
@@ -69,10 +77,6 @@ Item {
69 target: item; when: item
77 target: item; when: item
70 property: "orientation"; value: root.orientation
78 property: "orientation"; value: root.orientation
71 }
79 }
72−
73− Component.onDestruction: {
74− root.session.surface.forceActiveFocus();
75− }
76 }
80 }
77 }
81 }
78
82
@@ -138,5 +142,16 @@ Item {
138 QtObject {
142 QtObject {
139 id: d
143 id: d
140 property var animations: []
144 property var animations: []
145+
146+ property var focusedChild: {
147+ if (childSessionsRepeater.count == 0) {
148+ return _surfaceContainer;
149+ } else {
150+ return childSessionsRepeater.itemAt(childSessionsRepeater.count - 1);
151+ }
152+ }
153+ onFocusedChildChanged: {
154+ focusedChild.focus = true;
155+ }
141 }
156 }
142}
157}
qml/Stages/SpreadDelegate.qml+2−1
@@ -21,7 +21,7 @@ import QtQuick 2.0
21import Ubuntu.Components 1.1
21import Ubuntu.Components 1.1
22import "../Components"
22import "../Components"
23
23
24−Item {
24+FocusScope {
25 id: root
25 id: root
26
26
27 // to be read from outside
27 // to be read from outside
@@ -65,6 +65,7 @@ Item {
65 ApplicationWindow {
65 ApplicationWindow {
66 id: appWindow
66 id: appWindow
67 objectName: application ? "appWindow_" + application.appId : "appWindow_null"
67 objectName: application ? "appWindow_" + application.appId : "appWindow_null"
68+ focus: true
68 anchors {
69 anchors {
69 fill: parent
70 fill: parent
70 topMargin: appWindow.fullscreen ? 0 : maximizedAppTopMargin
71 topMargin: appWindow.fullscreen ? 0 : maximizedAppTopMargin
qml/Stages/SurfaceContainer.qml+11−25
@@ -18,7 +18,7 @@ import QtQuick 2.0
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19import Ubuntu.Gestures 0.1 // For TouchGate
19import Ubuntu.Gestures 0.1 // For TouchGate
20
20
21−Item {
21+FocusScope {
22 id: root
22 id: root
23 objectName: "surfaceContainer"
23 objectName: "surfaceContainer"
24 property Item surface: null
24 property Item surface: null
@@ -26,10 +26,15 @@ Item {
26 property int orientation
26 property int orientation
27 property bool interactive
27 property bool interactive
28
28
29+ signal surfacePressed()
30+
29 onSurfaceChanged: {
31 onSurfaceChanged: {
30 if (surface) {
32 if (surface) {
33+ // Set the surface focus *after* it is added to the scene to
34+ // ensure an update to the scene's active focus.
35+ surface.focus = false;
31 surface.parent = root;
36 surface.parent = root;
32− d.forceSurfaceActiveFocusIfReady();
37+ surface.focus = true;
33 } else {
38 } else {
34 hadSurface = true;
39 hadSurface = true;
35 }
40 }
@@ -38,7 +43,6 @@ Item {
38 Binding { target: surface; property: "orientation"; value: root.orientation }
43 Binding { target: surface; property: "orientation"; value: root.orientation }
39 Binding { target: surface; property: "z"; value: 1 }
44 Binding { target: surface; property: "z"; value: 1 }
40 Binding { target: surface; property: "enabled"; value: root.interactive; when: surface }
45 Binding { target: surface; property: "enabled"; value: root.interactive; when: surface }
41− Binding { target: surface; property: "focus"; value: root.interactive; when: surface }
42 Binding { target: surface; property: "antialiasing"; value: !root.interactive; when: surface }
46 Binding { target: surface; property: "antialiasing"; value: !root.interactive; when: surface }
43
47
44 TouchGate {
48 TouchGate {
@@ -46,28 +50,10 @@ Item {
46 anchors.fill: root
50 anchors.fill: root
47 enabled: root.surface ? root.surface.enabled : false
51 enabled: root.surface ? root.surface.enabled : false
48 z: 2
52 z: 2
49− }
53+ onPressed: {
50−
54+ root.focus = true;
51− Connections {
55+ if (root.interactive) {
52− target: root.surface
53− // FIXME: I would rather not need to do this, but currently it doesn't get
54− // active focus without it and I don't know why.
55− // Possibly because if an item get focus=true before it has a parent, once
56− // it gets a parent QQuickWindow won't check its focus and update its activeFocus
57− // accordingly. Unlike when you focus=true after the item already has a parent.
58− onFocusChanged: d.forceSurfaceActiveFocusIfReady();
59− onParentChanged: d.forceSurfaceActiveFocusIfReady();
60− onEnabledChanged: d.forceSurfaceActiveFocusIfReady();
61− }
62−
63− QtObject {
64− id: d
65− function forceSurfaceActiveFocusIfReady() {
66− if (root.surface !== null &&
67− root.surface.focus &&
68− root.surface.parent === root &&
69− root.surface.enabled) {
70− root.surface.forceActiveFocus();
56+ root.forceActiveFocus();
71 }
57 }
72 }
58 }
73 }
59 }
qml/Stages/TabletStage.qml+14
@@ -76,6 +76,20 @@ Rectangle {
76
76
77 appId0 = ApplicationManager.count >= 1 ? ApplicationManager.get(0).appId : "";
77 appId0 = ApplicationManager.count >= 1 ? ApplicationManager.get(0).appId : "";
78 appId1 = ApplicationManager.count > 1 ? ApplicationManager.get(1).appId : "";
78 appId1 = ApplicationManager.count > 1 ? ApplicationManager.get(1).appId : "";
79+
80+ // Update the QML focus accordingly
81+ updateSpreadDelegateFocus();
82+ }
83+
84+ function updateSpreadDelegateFocus() {
85+ if (priv.focusedAppId) {
86+ var focusedAppIndex = priv.indexOf(priv.focusedAppId);
87+ if (focusedAppIndex !== -1) {
88+ spreadRepeater.itemAt(focusedAppIndex).focus = true;
89+ } else {
90+ console.warn("TabletStage: Failed to find the SpreadDelegate for appID=" + priv.focusedAppId);
91+ }
92+ }
79 }
93 }
80
94
81 function indexOf(appId) {
95 function indexOf(appId) {
qml/Stages/TransformedSpreadDelegate.qml+27−21
@@ -46,6 +46,18 @@ SpreadDelegate {
46 property real startDistance: units.gu(5)
46 property real startDistance: units.gu(5)
47 property real endDistance: units.gu(.5)
47 property real endDistance: units.gu(.5)
48
48
49+ // Whether first app is displayed entirely vs just on the spread edge
50+ property bool focusFirstApp: true
51+
52+ // Whether this delegate is displayed to the user outside of the spread
53+ readonly property bool isFocused: focusFirstApp && index === 0
54+
55+ // Adjusted index for any spread animation calculations
56+ readonly property int spreadIndex: index - (focusFirstApp ? 1 : 0)
57+
58+ // How far left this delegate has been translated
59+ readonly property alias xTranslate: priv.xTranslate
60+
49 onSelectedChanged: {
61 onSelectedChanged: {
50 if (selected) {
62 if (selected) {
51 priv.snapshot();
63 priv.snapshot();
@@ -63,8 +75,8 @@ SpreadDelegate {
63 Connections {
75 Connections {
64 target: spreadView
76 target: spreadView
65 onPhaseChanged: {
77 onPhaseChanged: {
66− if (spreadView.phase == 1) {
78+ if (root.focusFirstApp && spreadView.phase == 1) {
67− if (index == 0) {
79+ if (index === 0) {
68 priv.phase2startTranslate = priv.easingAnimation(0, spreadView.positionMarker4, 0, -spreadView.width, spreadView.positionMarker4) + spreadView.width;
80 priv.phase2startTranslate = priv.easingAnimation(0, spreadView.positionMarker4, 0, -spreadView.width, spreadView.positionMarker4) + spreadView.width;
69 priv.phase2startAngle = priv.easingAnimation(0, spreadView.positionMarker4, root.startAngle, root.endAngle, spreadView.positionMarker4);
81 priv.phase2startAngle = priv.easingAnimation(0, spreadView.positionMarker4, root.startAngle, root.endAngle, spreadView.positionMarker4);
70 priv.phase2startScale = priv.easingAnimation(0, spreadView.positionMarker4, root.startScale, root.endScale, spreadView.positionMarker4);
82 priv.phase2startScale = priv.easingAnimation(0, spreadView.positionMarker4, root.startScale, root.endScale, spreadView.positionMarker4);
@@ -134,7 +146,7 @@ SpreadDelegate {
134 }
146 }
135
147
136 if (otherSelected) {
148 if (otherSelected) {
137− if (spreadView.phase < 2 && index == 0) {
149+ if (spreadView.phase < 2 && root.isFocused) {
138 return linearAnimation(selectedProgress, 0, selectedXTranslate,
150 return linearAnimation(selectedProgress, 0, selectedXTranslate,
139 selectedXTranslate - spreadView.tileDistance, root.progress);
151 selectedXTranslate - spreadView.tileDistance, root.progress);
140 }
152 }
@@ -142,8 +154,7 @@ SpreadDelegate {
142 return selectedXTranslate;
154 return selectedXTranslate;
143 }
155 }
144
156
145− switch (index) {
157+ if (root.focusFirstApp && index === 0) {
146− case 0:
147 if (spreadView.phase == 0) {
158 if (spreadView.phase == 0) {
148 return Math.min(0, linearAnimation(0, spreadView.positionMarker2,
159 return Math.min(0, linearAnimation(0, spreadView.positionMarker2,
149 0, -spreadView.width * .25, root.animatedProgress));
160 0, -spreadView.width * .25, root.animatedProgress));
@@ -153,11 +164,10 @@ SpreadDelegate {
153 } else if (!priv.isSelected){ // phase 2
164 } else if (!priv.isSelected){ // phase 2
154 // Apply the same animation as with the rest but add spreadView.width to align it with the others.
165 // Apply the same animation as with the rest but add spreadView.width to align it with the others.
155 return -easingCurve.value * spreadView.width + spreadView.width;
166 return -easingCurve.value * spreadView.width + spreadView.width;
156− } else if (priv.isSelected) {
167+ } else {
157 return linearAnimation(selectedProgress, 0, selectedXTranslate, 0, root.progress);
168 return linearAnimation(selectedProgress, 0, selectedXTranslate, 0, root.progress);
158 }
169 }
159−
170+ } else if (root.focusFirstApp && index === 1) {
160− case 1:
161 if (spreadView.phase == 0 && !priv.isSelected) {
171 if (spreadView.phase == 0 && !priv.isSelected) {
162 return linearAnimation(0, spreadView.positionMarker2,
172 return linearAnimation(0, spreadView.positionMarker2,
163 0, -spreadView.width * spreadView.snapPosition, root.animatedProgress);
173 0, -spreadView.width * spreadView.snapPosition, root.animatedProgress);
@@ -170,13 +180,13 @@ SpreadDelegate {
170
180
171 if (priv.isSelected) {
181 if (priv.isSelected) {
172 // Distance to left edge
182 // Distance to left edge
173− var targetTranslate = -spreadView.width - ((index - 1) * root.startDistance);
183+ var targetTranslate = -spreadView.width - (root.spreadIndex * root.startDistance);
174 return linearAnimation(selectedProgress, 0,
184 return linearAnimation(selectedProgress, 0,
175 selectedXTranslate, targetTranslate, root.progress);
185 selectedXTranslate, targetTranslate, root.progress);
176 }
186 }
177
187
178 // Fix it at the right edge...
188 // Fix it at the right edge...
179− var rightEdgeOffset = -((index - 1) * root.startDistance);
189+ var rightEdgeOffset = -(root.spreadIndex * root.startDistance);
180 // ...and use our easing to move them to the left. Stop a bit earlier for each tile
190 // ...and use our easing to move them to the left. Stop a bit earlier for each tile
181 var animatedEndDistance = linearAnimation(0, 2, root.endDistance, 0, root.progress);
191 var animatedEndDistance = linearAnimation(0, 2, root.endDistance, 0, root.progress);
182 return -easingCurve.value * spreadView.width + (index * animatedEndDistance) + rightEdgeOffset;
192 return -easingCurve.value * spreadView.width + (index * animatedEndDistance) + rightEdgeOffset;
@@ -194,8 +204,7 @@ SpreadDelegate {
194 if (priv.isSelected) {
204 if (priv.isSelected) {
195 return linearAnimation(selectedProgress, 0, selectedAngle, 0, root.progress);
205 return linearAnimation(selectedProgress, 0, selectedAngle, 0, root.progress);
196 }
206 }
197− switch (index) {
207+ if (root.focusFirstApp && index === 0) {
198− case 0:
199 if (spreadView.phase == 0) {
208 if (spreadView.phase == 0) {
200 return Math.max(0, linearAnimation(0, spreadView.positionMarker2,
209 return Math.max(0, linearAnimation(0, spreadView.positionMarker2,
201 0, root.tile0SnapAngle, root.animatedProgress));
210 0, root.tile0SnapAngle, root.animatedProgress));
@@ -203,7 +212,7 @@ SpreadDelegate {
203 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
212 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
204 root.tile0SnapAngle, phase2startAngle, root.progress);
213 root.tile0SnapAngle, phase2startAngle, root.progress);
205 }
214 }
206− case 1:
215+ } else if (root.focusFirstApp && index === 1) {
207 if (spreadView.phase == 0) {
216 if (spreadView.phase == 0) {
208 return linearAnimation(0, spreadView.positionMarker2, root.startAngle,
217 return linearAnimation(0, spreadView.positionMarker2, root.startAngle,
209 root.startAngle * (1-spreadView.snapPosition), root.animatedProgress);
218 root.startAngle * (1-spreadView.snapPosition), root.animatedProgress);
@@ -227,15 +236,14 @@ SpreadDelegate {
227 return linearAnimation(selectedProgress, 0, selectedScale, 1, root.progress);
236 return linearAnimation(selectedProgress, 0, selectedScale, 1, root.progress);
228 }
237 }
229
238
230− switch (index) {
239+ if (root.focusFirstApp && index === 0) {
231− case 0:
232 if (spreadView.phase == 0) {
240 if (spreadView.phase == 0) {
233 return 1;
241 return 1;
234 } else if (spreadView.phase == 1) {
242 } else if (spreadView.phase == 1) {
235 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
243 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
236 1, phase2startScale, root.progress);
244 1, phase2startScale, root.progress);
237 }
245 }
238− case 1:
246+ } else if (root.focusFirstApp && index === 1) {
239 if (spreadView.phase == 0) {
247 if (spreadView.phase == 0) {
240 var targetScale = tile1StartScale - ((tile1StartScale - 1) * spreadView.snapPosition);
248 var targetScale = tile1StartScale - ((tile1StartScale - 1) * spreadView.snapPosition);
241 return linearAnimation(0, spreadView.positionMarker2,
249 return linearAnimation(0, spreadView.positionMarker2,
@@ -254,7 +262,7 @@ SpreadDelegate {
254 return linearAnimation (selectedProgress, Math.max(0, selectedProgress - .5),
262 return linearAnimation (selectedProgress, Math.max(0, selectedProgress - .5),
255 selectedOpacity, 0, root.progress);
263 selectedOpacity, 0, root.progress);
256 }
264 }
257− if (index == 0) {
265+ if (root.isFocused) {
258 switch (spreadView.phase) {
266 switch (spreadView.phase) {
259 case 0:
267 case 0:
260 return linearAnimation(0, spreadView.positionMarker2, 1, .7, root.animatedProgress);
268 return linearAnimation(0, spreadView.positionMarker2, 1, .7, root.animatedProgress);
@@ -272,16 +280,14 @@ SpreadDelegate {
272 return linearAnimation(selectedProgress, 0, selectedTopMarginProgress, 0, root.progress);
280 return linearAnimation(selectedProgress, 0, selectedTopMarginProgress, 0, root.progress);
273 }
281 }
274
282
275− switch (index) {
283+ if (root.focusFirstApp && index === 0) {
276− case 0:
277 if (spreadView.phase == 0) {
284 if (spreadView.phase == 0) {
278 return 0;
285 return 0;
279 } else if (spreadView.phase == 1) {
286 } else if (spreadView.phase == 1) {
280 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
287 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
281 0, priv.phase2startTopMarginProgress, root.progress);
288 0, priv.phase2startTopMarginProgress, root.progress);
282 }
289 }
283− break;
290+ } else if (root.focusFirstApp && index === 1) {
284− case 1:
285 if (spreadView.phase == 0) {
291 if (spreadView.phase == 0) {
286 return 0;
292 return 0;
287 } else if (spreadView.phase == 1) {
293 } else if (spreadView.phase == 1) {
qml/Stages/TransformedTabletSpreadDelegate.qml+1−1
@@ -366,7 +366,7 @@ SpreadDelegate {
366 Scale {
366 Scale {
367 origin { x: 0; y: (spreadView.height * priv.scale) + maximizedAppTopMargin * 3 }
367 origin { x: 0; y: (spreadView.height * priv.scale) + maximizedAppTopMargin * 3 }
368 xScale: 1
368 xScale: 1
369− yScale: isFullscreen ? 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height : 1
369+ yScale: fullscreen ? 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height : 1
370 },
370 },
371 Translate {
371 Translate {
372 x: priv.xTranslate
372 x: priv.xTranslate
qml/Stages/WindowMoveResizeArea.qml+33−24
@@ -18,6 +18,7 @@
18
18
19import QtQuick 2.3
19import QtQuick 2.3
20import Ubuntu.Components 1.1
20import Ubuntu.Components 1.1
21+import Utils 0.1
21
22
22MouseArea {
23MouseArea {
23 id: root
24 id: root
@@ -26,7 +27,8 @@ MouseArea {
26
27
27 // The target item managed by this. Must be a parent or a sibling
28 // The target item managed by this. Must be a parent or a sibling
28 // The area will anchor to it and manage move and resize events
29 // The area will anchor to it and manage move and resize events
29− property var target: null
30+ property Item target: null
31+ property string windowId: ""
30 property int resizeHandleWidth: 0
32 property int resizeHandleWidth: 0
31 property int minWidth: 0
33 property int minWidth: 0
32 property int minHeight: 0
34 property int minHeight: 0
@@ -37,8 +39,6 @@ MouseArea {
37 readonly property int windowHeight: root.height - resizeHandleWidth * 2
39 readonly property int windowHeight: root.height - resizeHandleWidth * 2
38
40
39 property var startPoint
41 property var startPoint
40− property int startWidth
41− property int startHeight
42
42
43 property bool resizeTop: false
43 property bool resizeTop: false
44 property bool resizeBottom: false
44 property bool resizeBottom: false
@@ -47,10 +47,18 @@ MouseArea {
47
47
48 }
48 }
49
49
50+ Component.onCompleted: {
51+ var windowState = WindowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
52+ if (windowState !== undefined) {
53+ target.x = windowState.x
54+ target.y = windowState.y
55+ target.width = windowState.width
56+ target.height = windowState.height
57+ }
58+ }
59+
50 onPressed: {
60 onPressed: {
51− priv.startPoint = mapToItem(null, Qt.point(mouse.x, mouse.y)).x;
61+ priv.startPoint = Qt.point(mouse.x, mouse.y);
52− priv.startWidth = root.width;
53− priv.startHeight = root.height;
54 priv.resizeTop = mouseY < root.resizeHandleWidth;
62 priv.resizeTop = mouseY < root.resizeHandleWidth;
55 priv.resizeBottom = mouseY > (root.height - root.resizeHandleWidth);
63 priv.resizeBottom = mouseY > (root.height - root.resizeHandleWidth);
56 priv.resizeLeft = mouseX < root.resizeHandleWidth;
64 priv.resizeLeft = mouseX < root.resizeHandleWidth;
@@ -58,32 +66,28 @@ MouseArea {
58 }
66 }
59
67
60 onPositionChanged: {
68 onPositionChanged: {
61− var currentPoint = mapToItem(null, Qt.point(mouse.x, mouse.y)).x;
69+ var currentPoint = Qt.point(mouse.x, mouse.y);
62 var mouseDiff = Qt.point(currentPoint.x - priv.startPoint.x, currentPoint.y - priv.startPoint.y);
70 var mouseDiff = Qt.point(currentPoint.x - priv.startPoint.x, currentPoint.y - priv.startPoint.y);
63 var moveDiff = Qt.point(0, 0);
71 var moveDiff = Qt.point(0, 0);
64 var sizeDiff = Qt.point(0, 0);
72 var sizeDiff = Qt.point(0, 0);
73+ var maxSizeDiff = Qt.point(root.minWidth - root.target.width, root.minHeight - root.target.height)
74+
65 if (priv.resizeTop || priv.resizeBottom || priv.resizeLeft || priv.resizeRight) {
75 if (priv.resizeTop || priv.resizeBottom || priv.resizeLeft || priv.resizeRight) {
66 if (priv.resizeTop) {
76 if (priv.resizeTop) {
67− moveDiff.y += mouseDiff.y;
77+ sizeDiff.y = Math.max(maxSizeDiff.y, -currentPoint.y + priv.startPoint.y)
68− sizeDiff.y += -mouseDiff.y;
78+ moveDiff.y = -sizeDiff.y
69− } else if (priv.resizeBottom) {
70− sizeDiff.y += mouse.y - priv.startPoint.y;
71− priv.startPoint.y = mouse.y
72 }
79 }
73− if (priv.resizeLeft) {
80+ if (priv.resizeBottom) {
74− moveDiff.x += mouseDiff.x;
81+ sizeDiff.y = Math.max(maxSizeDiff.y, currentPoint.y - priv.startPoint.y)
75− sizeDiff.x += -mouseDiff.x;
82+ priv.startPoint.y += sizeDiff.y
76− } else if (priv.resizeRight) {
77− sizeDiff.x += mouse.x - priv.startPoint.x;
78− priv.startPoint.x = mouse.x
79 }
83 }
80− if (priv.windowWidth + sizeDiff.x < root.minWidth) {
84+ if (priv.resizeLeft) {
81− sizeDiff.x = root.minWidth - priv.windowWidth;
85+ sizeDiff.x = Math.max(maxSizeDiff.x, -currentPoint.x + priv.startPoint.x)
82− moveDiff.x = moveDiff.x == 0 ? 0 : -sizeDiff.x;
86+ moveDiff.x = -sizeDiff.x
83 }
87 }
84− if (priv.windowHeight + sizeDiff.y < root.minHeight) {
88+ if (priv.resizeRight) {
85− sizeDiff.y = root.minHeight - priv.windowHeight;
89+ sizeDiff.x = Math.max(maxSizeDiff.x, currentPoint.x - priv.startPoint.x)
86− moveDiff.y = moveDiff.y == 0 ? 0 : -sizeDiff.y;
90+ priv.startPoint.x += sizeDiff.x
87 }
91 }
88
92
89 target.x += moveDiff.x;
93 target.x += moveDiff.x;
@@ -94,5 +98,10 @@ MouseArea {
94 target.x += mouseDiff.x;
98 target.x += mouseDiff.x;
95 target.y += mouseDiff.y;
99 target.y += mouseDiff.y;
96 }
100 }
101+
102+ }
103+
104+ Component.onDestruction: {
105+ WindowStateStorage.saveGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
97 }
106 }
98}
107}
qml/Tutorial/Arrow.qmlnew file+56
@@ -0,0 +1,56 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import Ubuntu.Components 1.1
19+
20+Item {
21+ id: root
22+
23+ property alias color: circle.color
24+
25+ // Will make whole arrow darker
26+ property real darkenBy: 0
27+
28+ property alias chevronOpacity: chevron.opacity
29+
30+ ////
31+
32+ Rectangle {
33+ id: circle
34+ anchors.fill: parent
35+ radius: width / 2
36+ }
37+
38+ Image {
39+ id: chevron
40+ anchors.centerIn: parent
41+ source: Qt.resolvedUrl("graphics/chevron.png")
42+ fillMode: Image.PreserveAspectFit
43+ sourceSize.width: 152
44+ sourceSize.height: 152
45+ width: parent.width / 2
46+ height: parent.height / 2
47+ }
48+
49+ Rectangle {
50+ id: darkCircle
51+ anchors.fill: parent
52+ radius: width / 2
53+ color: "black"
54+ opacity: root.darkenBy
55+ }
56+}
qml/Tutorial/Slider.qmlnew file+123
@@ -0,0 +1,123 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import Ubuntu.Components 1.1
19+
20+Item {
21+ id: root
22+
23+ // Whether this slider is short or long
24+ property bool shortSwipe
25+
26+ // How far the user has slid
27+ property real offset
28+
29+ // Set to true when slider is being used
30+ property bool active
31+
32+ // How far in percentage terms
33+ readonly property real percent: d.slideOffset / target.x
34+
35+ QtObject {
36+ id: d
37+ readonly property color trayColor: "#424141"
38+ readonly property real margin: units.gu(0.5)
39+ readonly property real arrowSize: root.height - margin * 2
40+ readonly property real dotSize: units.dp(1)
41+ readonly property real slideOffset: MathUtils.clamp(root.offset - offscreenOffset, -offscreenOffset, target.x)
42+ readonly property real offscreenOffset: units.gu(2)
43+ }
44+
45+ implicitWidth: shortSwipe ? units.gu(15) : units.gu(27.5)
46+ implicitHeight: units.gu(6.5)
47+
48+ Rectangle {
49+ color: d.trayColor
50+ anchors.fill: parent
51+ anchors.rightMargin: clipBox.width - 1
52+ }
53+
54+ // We want to have a circular border around the target. But we can't just
55+ // do a radius on two of a rectangle's corners. So we clip a full circle.
56+ Item {
57+ id: clipBox
58+
59+ clip: true
60+ anchors.top: parent.top
61+ anchors.bottom: parent.bottom
62+ anchors.right: parent.right
63+ width: parent.height / 2
64+
65+ Rectangle {
66+ color: d.trayColor
67+ anchors.top: parent.top
68+ anchors.bottom: parent.bottom
69+ anchors.right: parent.right
70+ width: parent.width * 2
71+ radius: parent.width
72+ }
73+ }
74+
75+ Arrow {
76+ id: target
77+ width: d.arrowSize
78+ height: d.arrowSize
79+ color: "#73000000"
80+ chevronOpacity: 0.52
81+ anchors.right: parent.right
82+ anchors.rightMargin: d.margin
83+ anchors.verticalCenter: parent.verticalCenter
84+ }
85+
86+ Row {
87+ anchors.left: handle.horizontalCenter
88+ anchors.right: target.horizontalCenter
89+ anchors.verticalCenter: parent.verticalCenter
90+
91+ layoutDirection: Qt.RightToLeft
92+ spacing: d.dotSize * 2
93+
94+ Repeater {
95+ model: parent.width / (parent.spacing + d.dotSize)
96+ Rectangle {
97+ anchors.verticalCenter: parent ? parent.verticalCenter : undefined
98+ height: d.dotSize
99+ width: height
100+ radius: width
101+ color: "white"
102+ opacity: 0.2
103+ }
104+ }
105+ }
106+
107+ Arrow {
108+ id: handle
109+ width: d.arrowSize
110+ height: d.arrowSize
111+ color: UbuntuColors.orange
112+ darkenBy: root.active ? 0.5 : 0
113+ anchors.left: parent.left
114+ // We use a Translate transform rather than anchors.leftMargin because
115+ // the latter has weird performance problems on the TutorialRight page.
116+ transform: [
117+ Translate {
118+ x: d.slideOffset
119+ }
120+ ]
121+ anchors.verticalCenter: parent.verticalCenter
122+ }
123+}
tests/mocks/LightDM/single-pin/UsersModelPrivate.cpp → qml/Tutorial/Tick.qmlrenamed+12−16
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -12,22 +12,18 @@
12 *
12 *
13 * You should have received a copy of the GNU General Public License
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
17 */
15 */
18
16
19−#include "../UsersModelPrivate.h"
17+import QtQuick 2.3
20−
18+import Ubuntu.Components 1.1
21−namespace QLightDM
22−{
23−
24−UsersModelPrivate::UsersModelPrivate(UsersModel* parent)
25− : q_ptr(parent)
26−{
27− entries =
28− {
29− { "has-pin", "Has PIN", 0, 0, false, false, 0, 0 },
30− };
31−}
32
19
20+MouseArea {
21+ implicitHeight: tick.height
22+ implicitWidth: tick.width
23+ Image {
24+ id: tick
25+ source: Qt.resolvedUrl("graphics/tick.png")
26+ height: units.gu(6.5)
27+ width: units.gu(6.5)
28+ }
33}
29}
qml/Tutorial/Tutorial.qmlnew file+92
@@ -0,0 +1,92 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import Ubuntu.Components 1.1
19+
20+Item {
21+ id: root
22+
23+ property alias active: loader.active
24+ property bool paused
25+ property real edgeSize
26+
27+ property Item launcher
28+ property Item panel
29+ property Item stages
30+ property Item overlay
31+
32+ readonly property bool launcherEnabled: loader.item ? loader.item.launcherEnabled : true
33+ readonly property bool spreadEnabled: loader.item ? loader.item.spreadEnabled : true
34+ readonly property bool panelEnabled: loader.item ? loader.item.panelEnabled : true
35+ readonly property bool panelContentEnabled: loader.item ? loader.item.panelContentEnabled : true
36+ readonly property bool running: loader.item ? loader.item.running : false
37+
38+ function finish() {
39+ if (loader.item) {
40+ loader.item.finish();
41+ }
42+ }
43+
44+ signal finished()
45+
46+ Loader {
47+ id: loader
48+ anchors.fill: parent
49+ source: "TutorialContent.qml"
50+
51+ Binding {
52+ target: loader.item
53+ property: "paused"
54+ value: root.paused
55+ }
56+
57+ Binding {
58+ target: loader.item
59+ property: "edgeSize"
60+ value: root.edgeSize
61+ }
62+
63+ Binding {
64+ target: loader.item
65+ property: "launcher"
66+ value: root.launcher
67+ }
68+
69+ Binding {
70+ target: loader.item
71+ property: "panel"
72+ value: root.panel
73+ }
74+
75+ Binding {
76+ target: loader.item
77+ property: "stages"
78+ value: root.stages
79+ }
80+
81+ Binding {
82+ target: loader.item
83+ property: "overlay"
84+ value: root.overlay
85+ }
86+
87+ Connections {
88+ target: loader.item
89+ onFinished: root.finished()
90+ }
91+ }
92+}
qml/Tutorial/TutorialBottom.qmlnew file+104
@@ -0,0 +1,104 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import Ubuntu.Components 1.1
19+import Ubuntu.Gestures 0.1
20+import "../Components"
21+import "." as LocalComponents
22+
23+TutorialPage {
24+ id: root
25+
26+ property alias edgeSize: dragArea.height
27+
28+ title: i18n.tr("Open special menus")
29+ text: i18n.tr("Swipe up from the bottom edge.")
30+ fullTextWidth: true
31+
32+ SequentialAnimation {
33+ id: teaseAnimation
34+ paused: running && root.paused
35+ running: !dragArea.useTouchY && slider.dragOffset === 0
36+ loops: Animation.Infinite
37+
38+ UbuntuNumberAnimation {
39+ target: slider
40+ property: "teaseOffset"
41+ to: units.gu(1)
42+ duration: UbuntuAnimation.SleepyDuration
43+ }
44+ UbuntuNumberAnimation {
45+ target: slider
46+ property: "teaseOffset"
47+ to: 0
48+ duration: UbuntuAnimation.SleepyDuration
49+ }
50+ }
51+
52+ foreground {
53+ children: [
54+ LocalComponents.Slider {
55+ id: slider
56+ anchors {
57+ bottom: parent.bottom
58+ bottomMargin: width / 2 - height / 2
59+ horizontalCenter: parent.horizontalCenter
60+ }
61+ rotation: -90
62+ offset: teaseOffset + dragOffset
63+ active: dragArea.dragging
64+
65+ property real teaseOffset
66+ property real dragOffset: dragArea.useTouchY ? -dragArea.touchY : 0
67+
68+ Behavior on dragOffset {
69+ id: offsetAnimation
70+ UbuntuNumberAnimation {}
71+ }
72+ }
73+ ]
74+ }
75+
76+ EdgeDragArea {
77+ id: dragArea
78+ direction: Direction.Upwards
79+ anchors {
80+ bottom: parent.bottom
81+ left: parent.left
82+ right: parent.right
83+ }
84+
85+ property bool useTouchY
86+
87+ onDraggingChanged: {
88+ if (!dragging) {
89+ if (slider.percent >= 0.85) {
90+ root.hide();
91+ } else if (slider.percent >= 0.15) {
92+ root.showError();
93+ }
94+ }
95+
96+ // We use a separate vars here rather than just directly looking at
97+ // 'dragging' because we want to preserve our 'slider.offset'
98+ // value during the above percent check. Now that we made it,
99+ // we can have 'slider.offset' go back to zero.
100+ offsetAnimation.enabled = !dragging;
101+ useTouchY = dragging;
102+ }
103+ }
104+}
qml/Tutorial/TutorialBottomFinish.qmlnew file+41
@@ -0,0 +1,41 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import Ubuntu.Components 1.1
19+import "." as LocalComponents
20+
21+TutorialPage {
22+ id: root
23+
24+ title: i18n.tr("This action does different things for different apps")
25+ text: i18n.tr("Tap here to finish.")
26+ fullTextWidth: true
27+
28+ foreground {
29+ children: [
30+ LocalComponents.Tick {
31+ objectName: "tick"
32+ anchors {
33+ horizontalCenter: parent.horizontalCenter
34+ top: parent.top
35+ topMargin: root.textBottom + units.gu(3)
36+ }
37+ onClicked: root.hide()
38+ }
39+ ]
40+ }
41+}
qml/Tutorial/TutorialContent.qmlnew file+124
@@ -0,0 +1,124 @@
1+/*
2+ * Copyright (C) 2013,2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import Ubuntu.Components 1.1
19+
20+Item {
21+ id: root
22+
23+ property Item launcher
24+ property Item panel
25+ property Item stages
26+ property Item overlay
27+
28+ readonly property bool launcherEnabled: !running ||
29+ (!paused && tutorialLeft.shown)
30+ readonly property bool spreadEnabled: !running
31+ readonly property bool panelEnabled: !running
32+ readonly property bool panelContentEnabled: !running
33+ readonly property alias running: d.running
34+
35+ property bool paused: false
36+ property real edgeSize
37+
38+ signal finished()
39+
40+ function finish() {
41+ d.stop();
42+ finished();
43+ }
44+
45+ ////
46+
47+ Component.onCompleted: {
48+ d.start();
49+ }
50+
51+ QtObject {
52+ id: d
53+
54+ property bool running
55+
56+ function stop() {
57+ running = false;
58+ }
59+
60+ function start() {
61+ running = true;
62+ tutorialLeft.show();
63+ }
64+ }
65+
66+ TutorialLeft {
67+ id: tutorialLeft
68+ objectName: "tutorialLeft"
69+ parent: root.stages
70+ anchors.fill: parent
71+ launcher: root.launcher
72+ paused: !shown || root.paused
73+
74+ onFinished: tutorialLeftFinish.show()
75+ }
76+
77+ TutorialLeftFinish {
78+ id: tutorialLeftFinish
79+ objectName: "tutorialLeftFinish"
80+ parent: root.stages
81+ anchors.fill: parent
82+ textXOffset: root.launcher.panelWidth
83+ paused: !shown || root.paused
84+
85+ onFinished: {
86+ root.launcher.hide();
87+ tutorialRight.show();
88+ }
89+ }
90+
91+ TutorialRight {
92+ id: tutorialRight
93+ objectName: "tutorialRight"
94+ parent: root.stages
95+ anchors.fill: parent
96+ edgeSize: root.edgeSize
97+ panel: root.panel
98+ paused: !shown || root.paused
99+
100+ onFinished: tutorialBottom.show()
101+ }
102+
103+ TutorialBottom {
104+ id: tutorialBottom
105+ objectName: "tutorialBottom"
106+ parent: root.stages
107+ anchors.fill: parent
108+ edgeSize: root.edgeSize
109+ paused: !shown || root.paused
110+
111+ onFinished: tutorialBottomFinish.show()
112+ }
113+
114+ TutorialBottomFinish {
115+ id: tutorialBottomFinish
116+ objectName: "tutorialBottomFinish"
117+ parent: root.stages
118+ anchors.fill: parent
119+ backgroundFadesOut: true
120+ paused: !shown || root.paused
121+
122+ onFinished: root.finish()
123+ }
124+}
qml/Tutorial/TutorialLeft.qmlnew file+91
@@ -0,0 +1,91 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import Ubuntu.Components 1.1
19+import "." as LocalComponents
20+
21+TutorialPage {
22+ id: root
23+
24+ property var launcher
25+
26+ title: i18n.tr("Open the launcher")
27+ text: i18n.tr("Short swipe from the left edge.")
28+
29+ textXOffset: root.launcher.x + root.launcher.visibleWidth
30+
31+ Connections {
32+ target: root.launcher
33+
34+ onStateChanged: {
35+ if (root.launcher.state === "visible") {
36+ finishTimer.start();
37+ }
38+ }
39+
40+ onDash: {
41+ finishTimer.stop();
42+ root.showError();
43+ root.launcher.hide();
44+ }
45+ }
46+
47+ SequentialAnimation {
48+ id: teaseAnimation
49+ paused: running && root.paused
50+ running: !slider.active && root.launcher.visibleWidth === 0 && root.shown
51+ loops: Animation.Infinite
52+
53+ UbuntuNumberAnimation {
54+ target: root.launcher
55+ property: "x"
56+ to: units.gu(2)
57+ duration: UbuntuAnimation.SleepyDuration
58+ }
59+ UbuntuNumberAnimation {
60+ target: root.launcher
61+ property: "x"
62+ to: 0
63+ duration: UbuntuAnimation.SleepyDuration
64+ }
65+ }
66+
67+ Timer {
68+ id: finishTimer
69+ interval: 1
70+ onTriggered: {
71+ root.hide();
72+ root.launcher.x = 0; // make sure to reset launcher before we go
73+ }
74+ }
75+
76+ foreground {
77+ children: [
78+ LocalComponents.Slider {
79+ id: slider
80+ anchors {
81+ left: parent.left
82+ top: parent.top
83+ topMargin: root.textBottom + units.gu(3)
84+ }
85+ offset: root.launcher.x + root.launcher.visibleWidth + root.launcher.progress
86+ active: root.launcher.dragging
87+ shortSwipe: true
88+ }
89+ ]
90+ }
91+}
qml/Tutorial/TutorialLeftFinish.qmlnew file+41
@@ -0,0 +1,41 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import Ubuntu.Components 1.1
19+import "." as LocalComponents
20+
21+TutorialPage {
22+ id: root
23+
24+ title: i18n.tr("These are the shortcuts to favorite apps")
25+ text: i18n.tr("Tap here to continue.")
26+ fullTextWidth: true
27+
28+ foreground {
29+ children: [
30+ LocalComponents.Tick {
31+ objectName: "tick"
32+ anchors {
33+ horizontalCenter: parent.horizontalCenter
34+ top: parent.top
35+ topMargin: root.textBottom + units.gu(3)
36+ }
37+ onClicked: root.hide()
38+ }
39+ ]
40+ }
41+}
qml/Tutorial/TutorialPage.qmlnew file+245
@@ -0,0 +1,245 @@
1+/*
2+ * Copyright (C) 2013,2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import Ubuntu.Components 1.1
19+import "../Components"
20+
21+Showable {
22+ id: root
23+
24+ // This is the header displayed, like "Right edge"
25+ property alias title: titleLabel.text
26+
27+ // This is the block of text displayed below the header
28+ property alias text: textLabel.text
29+
30+ // Whether animations are paused
31+ property bool paused
32+
33+ // Whether to give the text the full width that the title has
34+ property bool fullTextWidth
35+
36+ // Whether whole page (background + foreground) or just the foreground fades in
37+ property bool backgroundFadesIn: false
38+
39+ // Whether whole page (background + foreground) or just the foreground fades out
40+ property bool backgroundFadesOut: false
41+
42+ // The foreground Item, add children to it that you want to fade in
43+ property alias foreground: foregroundExtra
44+
45+ // The text label bottom, so you can position elements relative to it
46+ readonly property real textBottom: Math.max(textLabel.y + textLabel.height, errorTextLabel.y + errorTextLabel.height)
47+
48+ // The MouseArea that eats events (so you can adjust size as you will)
49+ property alias mouseArea: mouseArea
50+
51+ // X/Y offsets for text
52+ property real textXOffset: 0
53+ property real textYOffset: 0
54+
55+ // Foreground text opacity
56+ property real textOpacity: 1
57+
58+ signal finished()
59+
60+ function showError() {
61+ errorTimer.start();
62+ }
63+
64+ ////
65+
66+ visible: false
67+ shown: false
68+
69+ property real _foregroundHideOpacity
70+
71+ showAnimation: StandardAnimation {
72+ property: root.backgroundFadesIn ? "opacity" : "_foregroundHideOpacity"
73+ from: 0
74+ to: 1
75+ duration: root.backgroundFadesIn ? UbuntuAnimation.SleepyDuration : UbuntuAnimation.BriskDuration
76+ onStarted: root.visible = true
77+ }
78+
79+ hideAnimation: StandardAnimation {
80+ property: root.backgroundFadesOut ? "opacity" : "_foregroundHideOpacity"
81+ to: 0
82+ duration: UbuntuAnimation.BriskDuration
83+ onStopped: {
84+ root.visible = false;
85+ root.finished();
86+ }
87+ }
88+
89+ QtObject {
90+ id: d
91+
92+ readonly property real sideMargin: units.gu(5.5)
93+ readonly property real verticalOffset: -units.gu(9)
94+ readonly property real textXOffset: Math.max(0, root.textXOffset - sideMargin + units.gu(2))
95+
96+ property real fadeInOffset: {
97+ if (showAnimation.running) {
98+ var opacity = root[root.showAnimation.property]
99+ return (1 - opacity) * units.gu(3);
100+ } else {
101+ return 0;
102+ }
103+ }
104+ }
105+
106+ Timer {
107+ id: errorTimer
108+ interval: 3500
109+ }
110+
111+ MouseArea { // eat any errant presses
112+ id: mouseArea
113+ anchors.fill: parent
114+ }
115+
116+ Rectangle {
117+ anchors.fill: parent
118+ color: "black"
119+ opacity: 0.82
120+ }
121+
122+ Item {
123+ id: foreground
124+ anchors.fill: parent
125+ opacity: root._foregroundHideOpacity
126+
127+ Item {
128+ anchors.fill: parent
129+ opacity: root.textOpacity
130+
131+ Label {
132+ id: titleLabel
133+ anchors {
134+ top: parent.verticalCenter
135+ topMargin: d.verticalOffset + root.textYOffset
136+ left: parent.left
137+ leftMargin: d.sideMargin + d.textXOffset
138+ }
139+ width: parent.width - d.sideMargin * 2
140+ horizontalAlignment: Text.AlignLeft
141+ wrapMode: Text.Wrap
142+ font.weight: Font.Light
143+ font.pixelSize: units.gu(3.5)
144+ }
145+
146+ Label {
147+ id: textLabel
148+ anchors {
149+ top: titleLabel.bottom
150+ topMargin: units.gu(2)
151+ left: parent.left
152+ leftMargin: d.sideMargin + d.textXOffset
153+ }
154+ width: (parent.width - d.sideMargin * 2) * (fullTextWidth ? 1 : 0.66)
155+ horizontalAlignment: Text.AlignLeft
156+ wrapMode: Text.Wrap
157+ font.weight: Font.Light
158+ font.pixelSize: units.gu(2.5)
159+ }
160+
161+ // We use two separate labels like this rather than just changing
162+ // the text of the above labels because we want to know where to place
163+ // sliders (via root.textBottom) without having that place change
164+ // as the text changes length.
165+ Label {
166+ id: errorTitleLabel
167+ objectName: "errorTitleLabel"
168+ anchors {
169+ top: titleLabel.top
170+ left: titleLabel.left
171+ }
172+ width: titleLabel.width
173+ horizontalAlignment: titleLabel.horizontalAlignment
174+ wrapMode: titleLabel.wrapMode
175+ font.weight: titleLabel.font.weight
176+ font.pixelSize: titleLabel.font.pixelSize
177+ opacity: 0
178+ text: i18n.tr("You almost got it!")
179+ }
180+
181+ Label {
182+ id: errorTextLabel
183+ objectName: "errorTextLabel"
184+ anchors {
185+ top: errorTitleLabel.bottom
186+ topMargin: textLabel.anchors.topMargin
187+ left: textLabel.left
188+ }
189+ width: textLabel.width
190+ horizontalAlignment: textLabel.horizontalAlignment
191+ wrapMode: textLabel.wrapMode
192+ font.weight: textLabel.font.weight
193+ font.pixelSize: textLabel.font.pixelSize
194+ opacity: 0
195+ text: i18n.tr("Try again.")
196+ }
197+ }
198+
199+ // A place for subclasses to add extra widgets
200+ Item {
201+ id: foregroundExtra
202+ anchors.fill: parent
203+ }
204+ }
205+
206+ states: State {
207+ name: "errorState"
208+ when: errorTimer.running
209+ PropertyChanges { target: titleLabel; opacity: 0 }
210+ PropertyChanges { target: textLabel; opacity: 0 }
211+ PropertyChanges { target: errorTitleLabel; opacity: 1 }
212+ PropertyChanges { target: errorTextLabel; opacity: 1 }
213+ }
214+
215+ transitions: Transition {
216+ to: "errorState"
217+ reversible: true
218+ SequentialAnimation {
219+ ParallelAnimation {
220+ StandardAnimation {
221+ target: titleLabel
222+ property: "opacity"
223+ duration: UbuntuAnimation.BriskDuration
224+ }
225+ StandardAnimation {
226+ target: textLabel
227+ property: "opacity"
228+ duration: UbuntuAnimation.BriskDuration
229+ }
230+ }
231+ ParallelAnimation {
232+ StandardAnimation {
233+ target: errorTitleLabel
234+ property: "opacity"
235+ duration: UbuntuAnimation.BriskDuration
236+ }
237+ StandardAnimation {
238+ target: errorTextLabel
239+ property: "opacity"
240+ duration: UbuntuAnimation.BriskDuration
241+ }
242+ }
243+ }
244+ }
245+}
qml/Tutorial/TutorialRight.qmlnew file+223
@@ -0,0 +1,223 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import Ubuntu.Components 1.1
19+import Ubuntu.Gestures 0.1
20+import Unity.Application 0.1
21+import "../Components"
22+import "../Stages"
23+import "." as LocalComponents
24+
25+TutorialPage {
26+ id: root
27+
28+ property var panel
29+ property alias edgeSize: stage.dragAreaWidth
30+
31+ title: i18n.tr("To view open apps")
32+ text: i18n.tr("Long swipe from the right edge.")
33+
34+ textOpacity: 1 - slider.percent
35+
36+ SequentialAnimation {
37+ id: teaseAnimation
38+ paused: running && root.paused
39+ running: !stage.dragging && stage.dragProgress === 0
40+ loops: Animation.Infinite
41+
42+ UbuntuNumberAnimation {
43+ target: stage
44+ property: "x"
45+ to: -stage.dragAreaOverlap
46+ duration: UbuntuAnimation.SleepyDuration
47+ }
48+ UbuntuNumberAnimation {
49+ target: stage
50+ property: "x"
51+ to: 0
52+ duration: UbuntuAnimation.SleepyDuration
53+ }
54+ }
55+
56+ foreground {
57+ children: [
58+ LocalComponents.Slider {
59+ id: slider
60+ anchors {
61+ right: parent.right
62+ top: parent.top
63+ topMargin: root.textBottom + units.gu(3)
64+ }
65+ rotation: 180
66+ offset: stage.dragProgress - stage.x
67+ active: stage.dragging
68+ },
69+
70+ // Just assume PhoneStage for now. The tablet version of the right-edge
71+ // tutorial is still being spec'd by the design team.
72+ PhoneStage {
73+ id: stage
74+ objectName: "stage"
75+ anchors.top: parent.top
76+ width: parent.width
77+ height: parent.height
78+ applicationManager: fakeAppManager
79+ color: "transparent"
80+ interactive: false
81+ altTabEnabled: false
82+ focusFirstApp: false
83+ startScale: 0.8
84+ endScale: 0.6
85+ dragAreaOverlap: units.gu(2)
86+
87+ onOpened: {
88+ overlay.show();
89+ root.textOpacity = 0;
90+ slider.visible = false;
91+ }
92+
93+ onDraggingChanged: {
94+ if (!dragging) {
95+ if (!overlay.shown) {
96+ root.showError();
97+ }
98+ teaseAnimation.complete();
99+ }
100+ }
101+ },
102+
103+ Showable {
104+ id: overlay
105+ objectName: "overlay"
106+ anchors.fill: parent
107+
108+ opacity: 0
109+ shown: false
110+ showAnimation: UbuntuNumberAnimation { property: "opacity"; to: 1 }
111+
112+ Label {
113+ anchors.top: parent.top
114+ anchors.topMargin: root.panel.panelHeight + units.gu(2)
115+ anchors.left: parent.left
116+ anchors.leftMargin: units.gu(2)
117+ anchors.right: parent.right
118+ anchors.rightMargin: units.gu(2)
119+ wrapMode: Text.Wrap
120+ horizontalAlignment: Text.AlignHCenter
121+ fontSize: "large"
122+ text: i18n.tr("View all your running tasks.")
123+ }
124+
125+ LocalComponents.Tick {
126+ objectName: "tick"
127+ anchors.bottom: bottomOverlayText.top
128+ anchors.bottomMargin: units.gu(1)
129+ anchors.horizontalCenter: bottomOverlayText.horizontalCenter
130+ onClicked: root.hide()
131+ }
132+
133+ Label {
134+ id: bottomOverlayText
135+ anchors.bottom: parent.bottom
136+ anchors.bottomMargin: units.gu(2)
137+ anchors.left: parent.left
138+ anchors.leftMargin: units.gu(2)
139+ anchors.right: parent.right
140+ anchors.rightMargin: units.gu(2)
141+ wrapMode: Text.Wrap
142+ horizontalAlignment: Text.AlignHCenter
143+ fontSize: "small"
144+ text: i18n.tr("Tap here to continue.")
145+ }
146+ }
147+ ]
148+ }
149+
150+ ListModel {
151+ id: fakeAppManager
152+
153+ readonly property string focusedApplicationId: "facebook"
154+
155+ function focusApplication(appId) {}
156+ function requestFocusApplication(appId) {}
157+ function findApplication(appId) {return null;}
158+
159+ signal applicationAdded(string appId)
160+ signal applicationRemoved(string appId)
161+ signal focusRequested(string appId)
162+
163+ ListElement {
164+ appId: "facebook"
165+ fullscreen: false
166+ name: ""
167+ icon: ""
168+ state: ApplicationInfoInterface.Stopped
169+ splashTitle: ""
170+ splashImage: ""
171+ splashShowHeader: false
172+ splashColor: "transparent"
173+ splashColorHeader: "transparent"
174+ splashColorFooter: "transparent"
175+ defaultScreenshot: "../Tutorial/graphics/facebook.png"
176+ }
177+
178+ ListElement {
179+ appId: "camera"
180+ fullscreen: false
181+ name: ""
182+ icon: ""
183+ state: ApplicationInfoInterface.Stopped
184+ splashTitle: ""
185+ splashImage: ""
186+ splashShowHeader: false
187+ splashColor: "transparent"
188+ splashColorHeader: "transparent"
189+ splashColorFooter: "transparent"
190+ defaultScreenshot: "../Tutorial/graphics/camera.png"
191+ }
192+
193+ ListElement {
194+ appId: "gallery"
195+ fullscreen: false
196+ name: ""
197+ icon: ""
198+ state: ApplicationInfoInterface.Stopped
199+ splashTitle: ""
200+ splashImage: ""
201+ splashShowHeader: false
202+ splashColor: "transparent"
203+ splashColorHeader: "transparent"
204+ splashColorFooter: "transparent"
205+ defaultScreenshot: "../Tutorial/graphics/gallery.png"
206+ }
207+
208+ ListElement {
209+ appId: "dialer"
210+ fullscreen: false
211+ name: ""
212+ icon: ""
213+ state: ApplicationInfoInterface.Stopped
214+ splashTitle: ""
215+ splashImage: ""
216+ splashShowHeader: false
217+ splashColor: "transparent"
218+ splashColorHeader: "transparent"
219+ splashColorFooter: "transparent"
220+ defaultScreenshot: "../Tutorial/graphics/dialer.png"
221+ }
222+ }
223+}
qml/Tutorial/graphics/camera.pngnew file

Binary file not shown.

qml/Tutorial/graphics/chevron.pngnew file

Binary file not shown.

qml/Tutorial/graphics/dialer.pngnew file

Binary file not shown.

qml/Tutorial/graphics/facebook.pngnew file

Binary file not shown.

qml/Tutorial/graphics/gallery.pngnew file

Binary file not shown.

qml/Tutorial/graphics/tick.pngnew file

Binary file not shown.

qml/Wizard/Pages.qml+2
@@ -41,6 +41,8 @@ Item {
41 }
41 }
42
42
43 function quitWizard() {
43 function quitWizard() {
44+ pageStack.currentPage.enabled = false;
45+
44 var errorMsg = securityPrivacy.setSecurity("", password, passwordMethod)
46 var errorMsg = securityPrivacy.setSecurity("", password, passwordMethod)
45 if (errorMsg !== "") {
47 if (errorMsg !== "") {
46 // Ignore (but log) any errors, since we're past where the user set
48 // Ignore (but log) any errors, since we're past where the user set
qml/Wizard/Pages/40-wifi.qml+27−9
@@ -16,6 +16,7 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import QMenuModel 0.1 as QMenuModel
18import QMenuModel 0.1 as QMenuModel
19+import QtSystemInfo 5.0
19import Ubuntu.Components 0.1
20import Ubuntu.Components 0.1
20import Ubuntu.Components.ListItems 0.1 as ListItem
21import Ubuntu.Components.ListItems 0.1 as ListItem
21import Ubuntu.Settings.Menus 0.1 as Menus
22import Ubuntu.Settings.Menus 0.1 as Menus
@@ -28,7 +29,7 @@ LocalComponents.Page {
28 title: i18n.tr("Connect to Wi‑Fi")
29 title: i18n.tr("Connect to Wi‑Fi")
29 forwardButtonSourceComponent: forwardButton
30 forwardButtonSourceComponent: forwardButton
30
31
31− readonly property bool connected: mainMenu.connectedAPs === 1
32+ readonly property bool connected: networkInfo.accessPointName
32
33
33 function getExtendedProperty(object, propertyName, defaultValue) {
34 function getExtendedProperty(object, propertyName, defaultValue) {
34 if (object && object.hasOwnProperty(propertyName)) {
35 if (object && object.hasOwnProperty(propertyName)) {
@@ -44,6 +45,31 @@ LocalComponents.Page {
44 menuObjectPath: "/com/canonical/indicator/network/phone_wifi_settings"
45 menuObjectPath: "/com/canonical/indicator/network/phone_wifi_settings"
45 }
46 }
46
47
48+ NetworkInfo {
49+ id: networkInfo
50+
51+ property string accessPointName
52+
53+ monitorCurrentNetworkMode: true
54+ monitorNetworkName: true
55+ monitorNetworkStatus: true
56+
57+ onCurrentNetworkModeChanged: getAccessPointName()
58+ onNetworkNameChanged: getAccessPointName()
59+ onNetworkStatusChanged: if (status !== NetworkInfo.HomeNetwork) accessPointName = ""
60+
61+ Component.onCompleted: getAccessPointName()
62+
63+ function getAccessPointName() {
64+ // 0 is the interface
65+ if (currentNetworkMode === NetworkInfo.WlanMode &&
66+ networkStatus(NetworkInfo.WlanMode, 0) === NetworkInfo.HomeNetwork)
67+ accessPointName = networkName(NetworkInfo.WlanMode, 0);
68+ else
69+ accessPointName = "";
70+ }
71+ }
72+
47 Component {
73 Component {
48 id: accessPointComponent
74 id: accessPointComponent
49 ListItem.Standard {
75 ListItem.Standard {
@@ -119,11 +145,6 @@ LocalComponents.Page {
119 onCheckedChanged: {
145 onCheckedChanged: {
120 // Can't rely on binding. Checked is assigned on click.
146 // Can't rely on binding. Checked is assigned on click.
121 checkBoxActive.checked = checked;
147 checkBoxActive.checked = checked;
122− if (checked) {
123− mainMenu.connectedAPs++
124− } else {
125− mainMenu.connectedAPs--
126− }
127 }
148 }
128 onActivate: unityMenuModel.activate(menuIndex);
149 onActivate: unityMenuModel.activate(menuIndex);
129 }
150 }
@@ -165,9 +186,6 @@ LocalComponents.Page {
165 Repeater {
186 Repeater {
166 id: mainMenu
187 id: mainMenu
167
188
168− // FIXME Check connection better https://bugs.launchpad.net/indicator-network/+bug/1349371
169− property int connectedAPs: 0
170−
171 model: menuModel
189 model: menuModel
172
190
173 delegate: Loader {
191 delegate: Loader {
run.sh+3−26
@@ -6,8 +6,6 @@ export QML2_IMPORT_PATH
6QML_PHONE_SHELL_PATH=./builddir/src/unity8
6QML_PHONE_SHELL_PATH=./builddir/src/unity8
7GDB=false
7GDB=false
8FAKE=false
8FAKE=false
9−PINLOCK=false
10−KEYLOCK=false
11USE_MOCKS=false
9USE_MOCKS=false
12MOUSE_TOUCH=true
10MOUSE_TOUCH=true
13
11
@@ -16,9 +14,6 @@ usage() {
16 echo "Script to run the shell.\n" >&2
14 echo "Script to run the shell.\n" >&2
17 echo "OPTIONS:" >&2
15 echo "OPTIONS:" >&2
18 echo " -f, --fake Force use of fake Qml modules." >&2
16 echo " -f, --fake Force use of fake Qml modules." >&2
19− echo " -p, --pinlock Use a pin protected user." >&2
20− echo " -k, --keylock Use a passphrase protected user." >&2
21− echo " -l, --lightdm Use the specified lightdm mock." >&2
22 echo " -g, --gdb Run through gdb." >&2
17 echo " -g, --gdb Run through gdb." >&2
23 echo " -h, --help Show this help." >&2
18 echo " -h, --help Show this help." >&2
24 echo " -m, --nomousetouch Run without -mousetouch argument." >&2
19 echo " -m, --nomousetouch Run without -mousetouch argument." >&2
@@ -26,7 +21,7 @@ usage() {
26 exit 1
21 exit 1
27}
22}
28
23
29−ARGS=`getopt -n$0 -u -a --longoptions="fake,pinlock,keylock,gdb,help,lightdm:,nomousetouch" -o "fpkl:ghm" -- "$@"`
24+ARGS=`getopt -n$0 -u -a --longoptions="fake,gdb,help:,nomousetouch" -o "fghm" -- "$@"`
30[ $? -ne 0 ] && usage
25[ $? -ne 0 ] && usage
31eval set -- "$ARGS"
26eval set -- "$ARGS"
32
27
@@ -34,12 +29,6 @@ while [ $# -gt 0 ]
34do
29do
35 case "$1" in
30 case "$1" in
36 -f|--fake) USE_MOCKS=true;;
31 -f|--fake) USE_MOCKS=true;;
37− -p|--pinlock) USE_MOCKS=true; LIGHTDM_MOCK=single-pin;;
38− -k|--keylock) USE_MOCKS=true; LIGHTDM_MOCK=single-passphrase;;
39− -l|--lightdm) LIGHTDM_MOCK=$2; shift;
40− if [ -z "$LIGHTDM_MOCK" ]; then
41− echo "Please specify an argument to --lightdm"
42− fi;;
43 -g|--gdb) GDB=true;;
32 -g|--gdb) GDB=true;;
44 -h|--help) usage;;
33 -h|--help) usage;;
45 -m|--nomousetouch) MOUSE_TOUCH=false;;
34 -m|--nomousetouch) MOUSE_TOUCH=false;;
@@ -48,23 +37,11 @@ do
48 shift
37 shift
49done
38done
50
39
51−if [ -z "$LIGHTDM_MOCK" ]; then
52− LIGHTDM_MOCK=single
53−fi
54−
55−# Even without USE_MOCKS set, we want to fake our lightdm backend, because it's
56−# annoying to be prompted for your password when testing. To get the same
57−# backend used in production, pass '--lightdm=demo'.
58−export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/LightDM/$LIGHTDM_MOCK
59−
60if $USE_MOCKS; then
40if $USE_MOCKS; then
61− rm -f $PWD/builddir/nonmirplugins/LightDM # undo symlink (from below) for cleanliness
62 export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$PWD/builddir/tests/mocks:$PWD/builddir/plugins:$PWD/builddir/modules
41 export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$PWD/builddir/tests/mocks:$PWD/builddir/plugins:$PWD/builddir/modules
63− export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/QMenuModel
42+ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/LightDM/liblightdm:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/QMenuModel
64else
43else
65− # Just link our LightDM mock into the nonmirplugins folder. We don't want
44+ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/plugins/LightDM/liblightdm
66− # the rest of our plugins to be used.
67− ln -sf $PWD/builddir/tests/mocks/LightDM $PWD/builddir/nonmirplugins/
68fi
45fi
69
46
70QML_PHONE_SHELL_ARGS=""
47QML_PHONE_SHELL_ARGS=""
tests/autopilot/unity8/shell/__init__.py−13
@@ -27,19 +27,6 @@ import logging
27
27
28logger = logging.getLogger(__name__)
28logger = logging.getLogger(__name__)
29
29
30−
31−def with_lightdm_mock(mock_type):
32− """A simple decorator that sets up the LightDM mock for a single test."""
33− def with_lightdm_mock_internal(fn):
34− @wraps(fn)
35− def wrapper(*args, **kwargs):
36− tests_self = args[0]
37− tests_self.patch_lightdm_mock(mock_type)
38− return fn(*args, **kwargs)
39− return wrapper
40− return with_lightdm_mock_internal
41−
42−
43def disable_qml_mocking(fn):
30def disable_qml_mocking(fn):
44 """Simple decorator that disables the QML mocks from being loaded."""
31 """Simple decorator that disables the QML mocks from being loaded."""
45 @wraps(fn)
32 @wraps(fn)
tests/autopilot/unity8/shell/emulators/edges_demo.pydeleted−140
@@ -1,140 +0,0 @@
1−# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−#
3−# Unity Autopilot Test Suite
4−# Copyright (C) 2014 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−import logging
21−import time
22−
23−import ubuntuuitoolkit
24−
25−import autopilot
26−from autopilot import introspection
27−
28−
29−logger = logging.getLogger(__name__)
30−
31−
32−class RightEdgeDemoOverlay(
33− ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
34−
35− @classmethod
36− def validate_dbus_object(cls, path, state):
37− name = introspection.get_classname_from_path(path)
38− if name == b'EdgeDemoOverlay':
39− if state['edge'][1] == 'right':
40− return True
41− return False
42−
43− @autopilot.logging.log_action(logger.info)
44− def swipe(self):
45− """Swipe to the left to complete this demo step."""
46− x, y, width, height = self.globalRect
47− start_x = x + width
48− stop_x = x
49− start_y = stop_y = y + height // 2
50− self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
51− return self.get_root_instance().wait_select_single(
52− edge='top', active=True)
53−
54−
55−class TopEdgeDemoOverlay(
56− ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
57−
58− @classmethod
59− def validate_dbus_object(cls, path, state):
60− name = introspection.get_classname_from_path(path)
61− if name == b'EdgeDemoOverlay':
62− if state['edge'][1] == 'top':
63− return True
64− return False
65−
66− @autopilot.logging.log_action(logger.info)
67− def swipe(self):
68− """Swipe to the bottom to complete this demo step."""
69− x, y, width, height = self.globalRect
70− start_x = stop_x = x + width // 2
71− start_y = y
72− stop_y = y + height
73− self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
74− return self.get_root_instance().wait_select_single(
75− edge='bottom', active=True)
76−
77−
78−class BottomEdgeDemoOverlay(
79− ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
80−
81− @classmethod
82− def validate_dbus_object(cls, path, state):
83− name = introspection.get_classname_from_path(path)
84− if name == b'EdgeDemoOverlay':
85− if state['edge'][1] == 'bottom':
86− return True
87− return False
88−
89− @autopilot.logging.log_action(logger.info)
90− def swipe(self):
91− """Swipe to the top to complete this demo step."""
92− x, y, width, height = self.globalRect
93− start_x = stop_x = x + width // 2
94− start_y = y + height
95− stop_y = y
96− self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
97− return self.get_root_instance().wait_select_single(
98− edge='left', active=True)
99−
100−
101−class LeftEdgeDemoOverlay(
102− ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
103−
104− @classmethod
105− def validate_dbus_object(cls, path, state):
106− name = introspection.get_classname_from_path(path)
107− if name == b'EdgeDemoOverlay':
108− if state['edge'][1] == 'left':
109− return True
110− return False
111−
112− @autopilot.logging.log_action(logger.info)
113− def swipe(self):
114− """Swipe to the right to complete this demo step."""
115− x, y, width, height = self.globalRect
116− start_x = x
117− stop_x = x + width
118− start_y = stop_y = y + height // 2
119− self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
120− return self.get_root_instance().wait_select_single(
121− edge='none', active=True)
122−
123−
124−class FinalEdgeDemoOverlay(
125− ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
126−
127− @classmethod
128− def validate_dbus_object(cls, path, state):
129− name = introspection.get_classname_from_path(path)
130− if name == b'EdgeDemoOverlay':
131− if state['edge'][1] == 'none':
132− return True
133− return False
134−
135− @autopilot.logging.log_action(logger.info)
136− def tap_to_start(self):
137− """Tap to finish the demo and start using the Ubuntu Touch."""
138− time.sleep(1)
139− self.pointing_device.click_object(self)
140− self.shown.wait_for(False)
tests/autopilot/unity8/shell/emulators/tutorial.pynew file+74
@@ -0,0 +1,74 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2014 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+import logging
21+import time
22+
23+import ubuntuuitoolkit
24+
25+import autopilot
26+from autopilot import introspection
27+
28+
29+logger = logging.getLogger(__name__)
30+
31+
32+class TutorialPage(
33+ ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
34+
35+ @classmethod
36+ def validate_dbus_object(cls, path, state):
37+ name = introspection.get_classname_from_path(path)
38+ return name in (b'TutorialPage', b'TutorialLeft',
39+ b'TutorialLeftFinish', b'TutorialRight',
40+ b'TutorialBottom', b'TutorialBottomFinish')
41+
42+ @autopilot.logging.log_action(logger.info)
43+ def short_swipe_right(self):
44+ self.shown.wait_for(True)
45+ x, y, width, height = self.globalRect
46+ start_x = x
47+ stop_x = x + width // 3
48+ start_y = stop_y = y + height // 2
49+ self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
50+
51+ @autopilot.logging.log_action(logger.info)
52+ def swipe_left(self):
53+ self.shown.wait_for(True)
54+ x, y, width, height = self.globalRect
55+ start_x = width
56+ stop_x = x
57+ start_y = stop_y = y + height // 2
58+ self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
59+
60+ @autopilot.logging.log_action(logger.info)
61+ def swipe_up(self):
62+ self.shown.wait_for(True)
63+ x, y, width, height = self.globalRect
64+ start_y = height
65+ stop_y = y
66+ start_x = stop_x = x + width // 2
67+ self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
68+
69+ @autopilot.logging.log_action(logger.info)
70+ def tap(self):
71+ """Tap the tick button to complete this step."""
72+ self.shown.wait_for(True)
73+ button = self.wait_select_single(objectName="tick")
74+ self.pointing_device.click_object(button)
tests/autopilot/unity8/shell/fixture_setup.py+6−6
@@ -54,7 +54,7 @@ class FakeScopes(fixtures.Fixture):
54 return ld_library_path
54 return ld_library_path
55
55
56
56
57−class EdgesDemo(fixtures.Fixture):
57+class Tutorial(fixtures.Fixture):
58
58
59 def __init__(self, enable):
59 def __init__(self, enable):
60 super().__init__()
60 super().__init__()
@@ -62,12 +62,12 @@ class EdgesDemo(fixtures.Fixture):
62
62
63 def setUp(self):
63 def setUp(self):
64 super().setUp()
64 super().setUp()
65− original_state = self._is_edges_demo_enabled()
65+ original_state = self._is_tutorial_enabled()
66 if self.enable != original_state:
66 if self.enable != original_state:
67− self.addCleanup(self._set_edges_demo, original_state)
67+ self.addCleanup(self._set_tutorial, original_state)
68− self._set_edges_demo(self.enable)
68+ self._set_tutorial(self.enable)
69
69
70− def _is_edges_demo_enabled(self):
70+ def _is_tutorial_enabled(self):
71 command = [
71 command = [
72 'dbus-send', '--system', '--print-reply',
72 'dbus-send', '--system', '--print-reply',
73 '--dest=org.freedesktop.Accounts',
73 '--dest=org.freedesktop.Accounts',
@@ -79,7 +79,7 @@ class EdgesDemo(fixtures.Fixture):
79 output = subprocess.check_output(command, universal_newlines=True)
79 output = subprocess.check_output(command, universal_newlines=True)
80 return True if output.count('true') else False
80 return True if output.count('true') else False
81
81
82− def _set_edges_demo(self, value):
82+ def _set_tutorial(self, value):
83 value_string = 'true' if value else 'false'
83 value_string = 'true' if value else 'false'
84 command = [
84 command = [
85 'dbus-send', '--system', '--print-reply',
85 'dbus-send', '--system', '--print-reply',
tests/autopilot/unity8/shell/tests/__init__.py+8−11
@@ -153,7 +153,6 @@ class UnityTestCase(AutopilotTestCase):
153 self.useFixture(toolkit_fixtures.HideUnity7Launcher())
153 self.useFixture(toolkit_fixtures.HideUnity7Launcher())
154
154
155 self._proxy = None
155 self._proxy = None
156− self._lightdm_mock_type = None
157 self._qml_mock_enabled = True
156 self._qml_mock_enabled = True
158 self._data_dirs_mock_enabled = True
157 self._data_dirs_mock_enabled = True
159 self._environment = {}
158 self._environment = {}
@@ -265,8 +264,7 @@ class UnityTestCase(AutopilotTestCase):
265 binary_path
264 binary_path
266 )
265 )
267
266
268− if self._lightdm_mock_type is None:
269− self.patch_lightdm_mock()
267+ self.patch_lightdm_mock()
270
268
271 if self._qml_mock_enabled:
269 if self._qml_mock_enabled:
272 self._environment['QML2_IMPORT_PATH'] = (
270 self._environment['QML2_IMPORT_PATH'] = (
@@ -338,12 +336,11 @@ class UnityTestCase(AutopilotTestCase):
338 if data_dirs is not None:
336 if data_dirs is not None:
339 self._environment['XDG_DATA_DIRS'] = data_dirs
337 self._environment['XDG_DATA_DIRS'] = data_dirs
340
338
341− def patch_lightdm_mock(self, mock_type='single'):
339+ def patch_lightdm_mock(self):
342− self._lightdm_mock_type = mock_type
343− logger.info("Setting up LightDM mock type '%s'", mock_type)
340+ logger.info("Setting up LightDM mock lib")
344 new_ld_library_path = [
341 new_ld_library_path = [
345 get_default_extra_mock_libraries(),
342 get_default_extra_mock_libraries(),
346− self._get_lightdm_mock_path(mock_type)
343+ self._get_lightdm_mock_path()
347 ]
344 ]
348 if os.getenv('LD_LIBRARY_PATH') is not None:
345 if os.getenv('LD_LIBRARY_PATH') is not None:
349 new_ld_library_path.append(os.getenv('LD_LIBRARY_PATH'))
346 new_ld_library_path.append(os.getenv('LD_LIBRARY_PATH'))
@@ -353,16 +350,16 @@ class UnityTestCase(AutopilotTestCase):
353
350
354 self._environment['LD_LIBRARY_PATH'] = new_ld_library_path
351 self._environment['LD_LIBRARY_PATH'] = new_ld_library_path
355
352
356− def _get_lightdm_mock_path(self, mock_type):
353+ def _get_lightdm_mock_path(self):
357 lib_path = get_mocks_library_path()
354 lib_path = get_mocks_library_path()
358 lightdm_mock_path = os.path.abspath(
355 lightdm_mock_path = os.path.abspath(
359− os.path.join(lib_path, "LightDM", mock_type)
356+ os.path.join(lib_path, "LightDM", "liblightdm")
360 )
357 )
361
358
362 if not os.path.exists(lightdm_mock_path):
359 if not os.path.exists(lightdm_mock_path):
363 raise RuntimeError(
360 raise RuntimeError(
364− "LightDM mock '%s' does not exist at path '%s'."
361+ "LightDM mock does not exist at path '%s'."
365− % (mock_type, lightdm_mock_path)
362+ % (lightdm_mock_path)
366 )
363 )
367 return lightdm_mock_path
364 return lightdm_mock_path
368
365
tests/autopilot/unity8/shell/tests/test_edges_demo.pydeleted−52
@@ -1,52 +0,0 @@
1−# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−#
3−# Unity Autopilot Test Suite
4−# Copyright (C) 2014 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−from autopilot.matchers import Eventually
21−from testtools.matchers import Equals
22−
23−from unity8.shell import (
24− fixture_setup,
25− tests
26−)
27−# unused import to load the edge emulators custom proxy objects.
28−from unity8.shell.emulators import edges_demo # NOQA
29−
30−
31−class EdgesDemoTestCase(tests.UnityTestCase):
32−
33− def setUp(self):
34− super(EdgesDemoTestCase, self).setUp()
35− self._qml_mock_enabled = False
36− self._data_dirs_mock_enabled = False
37− self._lightdm_mock_type = False
38−
39− self.useFixture(fixture_setup.EdgesDemo(True))
40− self.unity = self.launch_unity()
41−
42− def test_complete_edge_demo(self):
43− edge_demo = self.unity.select_single('EdgeDemo')
44− self.assertThat(edge_demo.running, Eventually(Equals(True)))
45− right_edge_overlay = self.unity.wait_select_single(
46− edge='right', active=True)
47− top_edge_overlay = right_edge_overlay.swipe()
48− bottom_edge_overlay = top_edge_overlay.swipe()
49− left_edge_overlay = bottom_edge_overlay.swipe()
50− final_overlay = left_edge_overlay.swipe()
51− final_overlay.tap_to_start()
52− self.assertThat(edge_demo.running, Eventually(Equals(False)))
tests/autopilot/unity8/shell/tests/test_lock_screen.py+6−5
@@ -20,7 +20,6 @@
20
20
21from __future__ import absolute_import
21from __future__ import absolute_import
22
22
23−from unity8.shell import with_lightdm_mock
24from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
23from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
25
24
26from autopilot.matchers import Eventually
25from autopilot.matchers import Eventually
@@ -41,9 +40,10 @@ class TestLockscreen(UnityTestCase):
41
40
42 scenarios = _get_device_emulation_scenarios()
41 scenarios = _get_device_emulation_scenarios()
43
42
44− @with_lightdm_mock("single-pin")
45 def test_can_unlock_pin_screen(self):
43 def test_can_unlock_pin_screen(self):
46 """Must be able to unlock the PIN entry lock screen."""
44 """Must be able to unlock the PIN entry lock screen."""
45+
46+ self._environment['LIBLIGHTDM_MOCK_MODE'] = "single-pin"
47 self.launch_unity()
47 self.launch_unity()
48 greeter = self.main_window.get_greeter()
48 greeter = self.main_window.get_greeter()
49
49
@@ -56,9 +56,10 @@ class TestLockscreen(UnityTestCase):
56 self._enter_prompt_passphrase("1234\n")
56 self._enter_prompt_passphrase("1234\n")
57 self.assertThat(greeter.shown, Eventually(Equals(False)))
57 self.assertThat(greeter.shown, Eventually(Equals(False)))
58
58
59− @with_lightdm_mock("single-passphrase")
60 def test_can_unlock_passphrase_screen(self):
59 def test_can_unlock_passphrase_screen(self):
61 """Must be able to unlock the passphrase entry screen."""
60 """Must be able to unlock the passphrase entry screen."""
61+
62+ self._environment['LIBLIGHTDM_MOCK_MODE'] = "single-passphrase"
62 self.launch_unity()
63 self.launch_unity()
63 greeter = self.main_window.get_greeter()
64 greeter = self.main_window.get_greeter()
64
65
@@ -71,9 +72,9 @@ class TestLockscreen(UnityTestCase):
71 self._enter_prompt_passphrase("password")
72 self._enter_prompt_passphrase("password")
72 self.assertThat(greeter.shown, Eventually(Equals(False)))
73 self.assertThat(greeter.shown, Eventually(Equals(False)))
73
74
74− @with_lightdm_mock("single-pin")
75 def test_pin_screen_wrong_code(self):
75 def test_pin_screen_wrong_code(self):
76 """Entering the wrong pin code must not dismiss the lock screen."""
76 """Entering the wrong pin code must not dismiss the lock screen."""
77+ self._environment['LIBLIGHTDM_MOCK_MODE'] = "single-pin"
77 self.launch_unity()
78 self.launch_unity()
78 greeter = self.main_window.get_greeter()
79 greeter = self.main_window.get_greeter()
79
80
@@ -90,9 +91,9 @@ class TestLockscreen(UnityTestCase):
90 self.assertThat(prompt.text, Eventually(Equals("")))
91 self.assertThat(prompt.text, Eventually(Equals("")))
91 self.assertThat(greeter.shown, Eventually(Equals(True)))
92 self.assertThat(greeter.shown, Eventually(Equals(True)))
92
93
93− @with_lightdm_mock("single-passphrase")
94 def test_passphrase_screen_wrong_password(self):
94 def test_passphrase_screen_wrong_password(self):
95 """Entering the wrong password must not dismiss the lock screen."""
95 """Entering the wrong password must not dismiss the lock screen."""
96+ self._environment['LIBLIGHTDM_MOCK_MODE'] = "single-passphrase"
96 self.launch_unity()
97 self.launch_unity()
97 greeter = self.main_window.get_greeter()
98 greeter = self.main_window.get_greeter()
98
99
tests/autopilot/unity8/shell/tests/test_tutorial.pynew file+57
@@ -0,0 +1,57 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2014 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+from autopilot.matchers import Eventually
21+from testtools.matchers import Equals
22+
23+from unity8.shell import (
24+ fixture_setup,
25+ tests
26+)
27+# unused import to load the tutorial emulators custom proxy objects.
28+from unity8.shell.emulators import tutorial # NOQA
29+
30+
31+class TutorialTestCase(tests.UnityTestCase):
32+
33+ def setUp(self):
34+ super(TutorialTestCase, self).setUp()
35+ self._qml_mock_enabled = False
36+ self._data_dirs_mock_enabled = False
37+
38+ self.useFixture(fixture_setup.Tutorial(True))
39+ self.unity = self.launch_unity()
40+
41+ def test_complete_tutorial(self):
42+ greeter = self.main_window.get_greeter()
43+ tutorial = self.unity.select_single('Tutorial')
44+ self.assertThat(tutorial.running, Eventually(Equals(True)))
45+ greeter.swipe()
46+ page = self.unity.wait_select_single(objectName='tutorialLeft')
47+ page.short_swipe_right()
48+ page = self.unity.wait_select_single(objectName='tutorialLeftFinish')
49+ page.tap()
50+ page = self.unity.wait_select_single(objectName='tutorialRight')
51+ page.swipe_left()
52+ page.tap()
53+ page = self.unity.wait_select_single(objectName='tutorialBottom')
54+ page.swipe_up()
55+ page = self.unity.wait_select_single(objectName='tutorialBottomFinish')
56+ page.tap()
57+ self.assertThat(tutorial.running, Eventually(Equals(False)))
tests/mocks/LightDM/CMakeLists.txt+8−9
@@ -4,16 +4,13 @@
4# actually using the system liblightdm in the normal plugin, this version can
4# actually using the system liblightdm in the normal plugin, this version can
5# be deleted.
5# be deleted.
6
6
7−add_subdirectory(demo)
7+add_subdirectory(liblightdm)
8−add_subdirectory(full)
9−add_subdirectory(single)
10−add_subdirectory(single-pin)
11−add_subdirectory(single-passphrase)
12
8
13include_directories(
9include_directories(
14 ${CMAKE_CURRENT_SOURCE_DIR}
10 ${CMAKE_CURRENT_SOURCE_DIR}
15 ${CMAKE_CURRENT_BINARY_DIR}
11 ${CMAKE_CURRENT_BINARY_DIR}
16 ${CMAKE_SOURCE_DIR}/plugins/Utils
12 ${CMAKE_SOURCE_DIR}/plugins/Utils
13+ ${CMAKE_SOURCE_DIR}/plugins/LightDM
17 ${CMAKE_SOURCE_DIR}/tests/mocks/libusermetrics
14 ${CMAKE_SOURCE_DIR}/tests/mocks/libusermetrics
18 ${libunity8-private_SOURCE_DIR}
15 ${libunity8-private_SOURCE_DIR}
19)
16)
@@ -22,9 +19,11 @@ set(QMLPLUGIN_SRC
22 ${CMAKE_SOURCE_DIR}/plugins/LightDM/DBusGreeter.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/LightDM/DBusGreeter.cpp
23 ${CMAKE_SOURCE_DIR}/plugins/LightDM/DBusGreeterList.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/LightDM/DBusGreeterList.cpp
24 ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
21 ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
25− ${CMAKE_SOURCE_DIR}/plugins/LightDM/plugin.cpp
26 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
22 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
27 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
23 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
24+ MockGreeter.cpp
25+ MockUsersModel.cpp
26+ plugin.cpp
28 )
27 )
29
28
30add_library(MockLightDM-qml MODULE
29add_library(MockLightDM-qml MODULE
@@ -35,9 +34,9 @@ add_library(MockLightDM-qml MODULE
35# installed on the system. So we make sure we link to our full fake version
34# installed on the system. So we make sure we link to our full fake version
36# At run time, we can point to whichever version we happen to be using via
35# At run time, we can point to whichever version we happen to be using via
37# LD_LIBRARY_PATH.
36# LD_LIBRARY_PATH.
38−add_dependencies(MockLightDM-qml MockLightDM-full MockUserMetrics)
37+add_dependencies(MockLightDM-qml MockLightDM MockUserMetrics)
39target_link_libraries(MockLightDM-qml
38target_link_libraries(MockLightDM-qml
40− -L${CMAKE_CURRENT_BINARY_DIR}/full
39+ -L${CMAKE_CURRENT_BINARY_DIR}/liblightdm
41 -llightdm-qt5-2
40 -llightdm-qt5-2
42 -L${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics
41 -L${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics
43 -lusermetricsoutput
42 -lusermetricsoutput
@@ -49,5 +48,5 @@ qt5_use_modules(MockLightDM-qml DBus Gui Qml)
49add_unity8_mock(LightDM 0.1 LightDM
48add_unity8_mock(LightDM 0.1 LightDM
50 PREFIX mocks
49 PREFIX mocks
51 TARGETS MockLightDM-qml
50 TARGETS MockLightDM-qml
52− ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_CURRENT_BINARY_DIR}/full"
51+ ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_CURRENT_BINARY_DIR}/liblightdm"
53)
52)
tests/mocks/LightDM/single/GreeterPrivate.cpp → tests/mocks/LightDM/MockGreeter.cpprenamed+12−23
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2014 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,34 +13,23 @@
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− * Author: Michael Terry <michael.terry@canonical.com>
17 */
16 */
18
17
19−#include "../Greeter.h"
18+#include "MockGreeter.h"
20−#include "../GreeterPrivate.h"
19+#include <GreeterPrivate.h>
21
20
22−namespace QLightDM
21+QString MockGreeter::mockMode() const
23−{
24−
25−GreeterPrivate::GreeterPrivate(Greeter* parent)
26− : authenticated(false),
27− authenticationUser(),
28− twoFactorDone(false),
29− q_ptr(parent)
30{
22{
23+ Q_D(const Greeter);
24+ return d->m_greeter->mockMode();
31}
25}
32
26
33−void GreeterPrivate::handleAuthenticate()
27+void MockGreeter::setMockMode(QString mockMode)
34{
28{
35− Q_Q(Greeter);
29+ Q_D(Greeter);
36−
37− authenticated = true;
38− Q_EMIT q->authenticationComplete();
39−}
40−
41−void GreeterPrivate::handleRespond(QString const &response)
42−{
43− Q_UNUSED(response)
44−}
45
30
31+ if (d->m_greeter->mockMode() != mockMode) {
32+ d->m_greeter->setMockMode(mockMode);
33+ Q_EMIT mockModeChanged(mockMode);
34+ }
46}
35}
tests/mocks/LightDM/MockGreeter.hnew file+37
@@ -0,0 +1,37 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ */
17+
18+// The real, production, Greeter
19+#include <Greeter.h>
20+
21+#ifndef MOCK_UNITY_GREETER_H
22+#define MOCK_UNITY_GREETER_H
23+
24+class MockGreeter : public Greeter {
25+ Q_OBJECT
26+
27+ Q_PROPERTY(QString mockMode READ mockMode WRITE setMockMode NOTIFY mockModeChanged)
28+
29+public:
30+ QString mockMode() const;
31+ void setMockMode(QString mockMode);
32+
33+Q_SIGNALS:
34+ void mockModeChanged(QString mode);
35+};
36+
37+#endif // MOCK_UNITY_GREETER_H
tests/mocks/LightDM/MockUsersModel.cppnew file+39
@@ -0,0 +1,39 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ */
17+
18+#include "MockUsersModel.h"
19+#include <QLightDM/UsersModel>
20+#include <QSortFilterProxyModel>
21+
22+QString MockUsersModel::mockMode() const
23+{
24+ QLightDM::UsersModel* qUsersModel =
25+ static_cast<QLightDM::UsersModel*>(static_cast<QSortFilterProxyModel*>(sourceModel())->sourceModel());
26+
27+ return qUsersModel->mockMode();
28+}
29+
30+void MockUsersModel::setMockMode(QString mockMode)
31+{
32+ QLightDM::UsersModel* qUsersModel =
33+ static_cast<QLightDM::UsersModel*>(static_cast<QSortFilterProxyModel*>(sourceModel())->sourceModel());
34+
35+ if (qUsersModel->mockMode() != mockMode) {
36+ qUsersModel->setMockMode(mockMode);
37+ Q_EMIT mockModeChanged(mockMode);
38+ }
39+}
tests/mocks/LightDM/MockUsersModel.hnew file+37
@@ -0,0 +1,37 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ */
17+
18+#ifndef MOCK_UNITY_USERSMODEL_H
19+#define MOCK_UNITY_USERSMODEL_H
20+
21+#include <UsersModel.h>
22+
23+class MockUsersModel : public UsersModel
24+{
25+ Q_OBJECT
26+
27+ Q_PROPERTY(QString mockMode READ mockMode WRITE setMockMode NOTIFY mockModeChanged)
28+
29+public:
30+ QString mockMode() const;
31+ void setMockMode(QString mockMode);
32+
33+Q_SIGNALS:
34+ void mockModeChanged(QString mode);
35+};
36+
37+#endif // MOCK_UNITY_USERSMODEL_H
tests/mocks/LightDM/QLightDM/Greetermode 120000 → 100644+17−1
@@ -1 +0,0 @@
1−../Greeter.h
@@ -0,0 +1,17 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "../liblightdm/Greeter.h"
tests/mocks/LightDM/QLightDM/InfographicModeldeleted−1
@@ -1 +0,0 @@
1−../InfographicModel.h
tests/mocks/LightDM/QLightDM/UsersModelmode 120000 → 100644+17−1
@@ -1 +0,0 @@
1−../UsersModel.h
@@ -0,0 +1,17 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "../liblightdm/UsersModel.h"
tests/mocks/LightDM/full/CMakeLists.txtdeleted−18
@@ -1,18 +0,0 @@
1−set(LibLightDM_SOURCES
2− ../Greeter.cpp
3− ../UsersModel.cpp
4− GreeterPrivate.cpp
5− UsersModelPrivate.cpp
6− ${CMAKE_SOURCE_DIR}/plugins/Utils/qvariantlistmodel.cpp
7− )
8−
9−add_library(MockLightDM-full SHARED ${LibLightDM_SOURCES})
10−
11−qt5_use_modules(MockLightDM-full Gui)
12−
13−set_target_properties(MockLightDM-full PROPERTIES
14− OUTPUT_NAME lightdm-qt5-2)
15−
16−install(TARGETS MockLightDM-full
17− DESTINATION ${SHELL_INSTALL_QML}/mocks/LightDM/full
18− )
tests/mocks/LightDM/liblightdm/CMakeLists.txtnew file+18
@@ -0,0 +1,18 @@
1+set(LibLightDM_SOURCES
2+ Greeter.cpp
3+ UsersModel.cpp
4+ GreeterPrivate.cpp
5+ UsersModelPrivate.cpp
6+ ${CMAKE_SOURCE_DIR}/plugins/Utils/qvariantlistmodel.cpp
7+ )
8+
9+add_library(MockLightDM SHARED ${LibLightDM_SOURCES})
10+
11+qt5_use_modules(MockLightDM Gui)
12+
13+set_target_properties(MockLightDM PROPERTIES
14+ OUTPUT_NAME lightdm-qt5-2)
15+
16+install(TARGETS MockLightDM
17+ DESTINATION ${SHELL_INSTALL_QML}/mocks/LightDM/liblightdm
18+ )
tests/mocks/LightDM/liblightdm/Greeter.cppnew file+199
@@ -0,0 +1,199 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "Greeter.h"
18+#include "GreeterPrivate.h"
19+#include <QtCore/QCoreApplication>
20+#include <QTimer>
21+
22+namespace QLightDM
23+{
24+
25+Greeter::Greeter(QObject *parent)
26+ : QObject(parent),
27+ d_ptr(new GreeterPrivate(this))
28+{
29+}
30+
31+Greeter::~Greeter()
32+{
33+}
34+
35+QString Greeter::authenticationUser() const
36+{
37+ Q_D(const Greeter);
38+ return d->authenticationUser;
39+}
40+
41+bool Greeter::hasGuestAccountHint() const
42+{
43+ return true;
44+}
45+
46+QString Greeter::getHint(const QString &name) const
47+{
48+ Q_UNUSED(name)
49+ return "";
50+}
51+
52+QString Greeter::defaultSessionHint() const
53+{
54+ return "ubuntu";
55+}
56+
57+bool Greeter::hideUsersHint() const
58+{
59+ return false;
60+}
61+
62+bool Greeter::showManualLoginHint() const
63+{
64+ return true;
65+}
66+
67+bool Greeter::showRemoteLoginHint() const
68+{
69+ return true;
70+}
71+
72+bool Greeter::lockHint () const
73+{
74+ return false;
75+}
76+
77+QString Greeter::selectUserHint() const
78+{
79+ return "";
80+}
81+
82+bool Greeter::selectGuestHint() const
83+{
84+ return false;
85+}
86+
87+QString Greeter::autologinUserHint() const
88+{
89+ return "";
90+}
91+
92+bool Greeter::autologinGuestHint() const
93+{
94+ return false;
95+}
96+
97+int Greeter::autologinTimeoutHint() const
98+{
99+ return 0;
100+}
101+
102+bool Greeter::inAuthentication() const
103+{
104+ return false;
105+}
106+
107+QString Greeter::hostname() const
108+{
109+ return "hostname1";
110+}
111+
112+bool Greeter::isAuthenticated() const
113+{
114+ Q_D(const Greeter);
115+ return d->authenticated;
116+}
117+
118+bool Greeter::connectSync()
119+{
120+ return true;
121+}
122+
123+void Greeter::authenticate(const QString &username)
124+{
125+ Q_D(Greeter);
126+
127+ d->authenticated = false;
128+ d->authenticationUser = username;
129+ d->twoFactorDone = false;
130+ QTimer::singleShot(0, this, SLOT(delayedAuthentication()));
131+}
132+
133+void Greeter::delayedAuthentication()
134+{
135+ Q_D(Greeter);
136+ d->handleAuthenticate();
137+}
138+
139+void Greeter::authenticateAsGuest()
140+{}
141+
142+void Greeter::authenticateAutologin()
143+{}
144+
145+void Greeter::authenticateRemote(const QString &session, const QString &username)
146+{
147+ Q_UNUSED(session)
148+ Q_UNUSED(username)
149+}
150+
151+void Greeter::cancelAuthentication()
152+{}
153+
154+void Greeter::setLanguage (const QString &language)
155+{
156+ Q_UNUSED(language)
157+}
158+
159+bool Greeter::startSessionSync(const QString &session)
160+{
161+ Q_UNUSED(session)
162+ return true;
163+}
164+
165+void Greeter::respond(const QString &response)
166+{
167+ Q_D(Greeter);
168+
169+ d->handleRespond(response);
170+}
171+
172+void Greeter::sendAuthenticationComplete()
173+{
174+ if (qgetenv("UNITY_TESTING").isEmpty()) {
175+ // simulate PAM's delay
176+ QTimer::singleShot(1000, this, SIGNAL(authenticationComplete()));
177+ } else {
178+ Q_EMIT authenticationComplete();
179+ }
180+}
181+
182+QString Greeter::mockMode() const
183+{
184+ Q_D(const Greeter);
185+ return d->mockMode;
186+}
187+
188+
189+void Greeter::setMockMode(QString mockMode)
190+{
191+ Q_D(Greeter);
192+
193+ if (d->mockMode != mockMode) {
194+ d->mockMode = mockMode;
195+ Q_EMIT mockModeChanged(mockMode);
196+ }
197+}
198+
199+}
tests/mocks/LightDM/liblightdm/Greeter.hnew file+118
@@ -0,0 +1,118 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ * Copyright (C) 2010-2011 David Edmundson.
4+ * Copyright (C) 2010-2011 Robert Ancell
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; version 3.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License
16+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17+ *
18+ * Author: David Edmundson <kde@davidedmundson.co.uk>
19+ */
20+
21+#ifndef UNITY_MOCK_GREETER_H
22+#define UNITY_MOCK_GREETER_H
23+
24+#include <QtCore/QObject>
25+#include <QtCore/QVariant>
26+
27+
28+namespace QLightDM
29+{
30+ class GreeterPrivate;
31+
32+class Q_DECL_EXPORT Greeter : public QObject
33+{
34+ Q_OBJECT
35+
36+ Q_PROPERTY(bool authenticated READ isAuthenticated ) //NOTFIY authenticationComplete
37+ Q_PROPERTY(QString authenticationUser READ authenticationUser )
38+ Q_PROPERTY(QString defaultSession READ defaultSessionHint CONSTANT)
39+ Q_PROPERTY(QString selectUser READ selectUserHint CONSTANT)
40+ Q_PROPERTY(bool selectGuest READ selectGuestHint CONSTANT)
41+
42+ Q_PROPERTY(QString hostname READ hostname CONSTANT)
43+ Q_PROPERTY(bool hasGuestAccount READ hasGuestAccountHint CONSTANT)
44+ Q_PROPERTY(bool locked READ lockHint CONSTANT)
45+
46+ Q_PROPERTY(QString hostname READ hostname CONSTANT)
47+
48+ Q_ENUMS(PromptType MessageType)
49+
50+ //Mock-only API for testing purposes
51+ Q_PROPERTY(QString mockMode READ mockMode WRITE setMockMode NOTIFY mockModeChanged)
52+
53+public:
54+ enum PromptType {
55+ PromptTypeQuestion,
56+ PromptTypeSecret
57+ };
58+
59+ enum MessageType {
60+ MessageTypeInfo,
61+ MessageTypeError
62+ };
63+
64+ explicit Greeter(QObject* parent=0);
65+ virtual ~Greeter();
66+
67+ QString getHint(const QString &name) const;
68+ QString defaultSessionHint() const;
69+ bool hideUsersHint() const;
70+ bool showManualLoginHint() const;
71+ bool showRemoteLoginHint() const;
72+ bool lockHint () const;
73+ bool hasGuestAccountHint() const;
74+ QString selectUserHint() const;
75+ bool selectGuestHint() const;
76+ QString autologinUserHint() const;
77+ bool autologinGuestHint() const;
78+ int autologinTimeoutHint() const;
79+
80+ bool inAuthentication() const;
81+ bool isAuthenticated() const;
82+ QString authenticationUser() const;
83+ QString hostname() const;
84+
85+ QString mockMode() const;
86+ void setMockMode(QString mockMode);
87+
88+public Q_SLOTS:
89+ bool connectSync();
90+ void authenticate(const QString &username=QString());
91+ void authenticateAsGuest();
92+ void authenticateAutologin();
93+ void authenticateRemote(const QString &session=QString(), const QString &username=QString());
94+ void respond(const QString &response);
95+ void cancelAuthentication();
96+ void setLanguage (const QString &language);
97+ bool startSessionSync(const QString &session=QString());
98+
99+Q_SIGNALS:
100+ void showMessage(QString text, QLightDM::Greeter::MessageType type);
101+ void showPrompt(QString text, QLightDM::Greeter::PromptType type);
102+ void authenticationComplete();
103+ void autologinTimerExpired();
104+ void mockModeChanged(QString mode);
105+
106+protected:
107+ void sendAuthenticationComplete();
108+
109+private Q_SLOTS:
110+ void delayedAuthentication();
111+
112+private:
113+ GreeterPrivate *d_ptr;
114+ Q_DECLARE_PRIVATE(Greeter)
115+};
116+}
117+
118+#endif // UNITY_MOCK_GREETER_H
tests/mocks/LightDM/full/GreeterPrivate.cpp → tests/mocks/LightDM/liblightdm/GreeterPrivate.cpprenamed+40−6
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2014 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -12,12 +12,10 @@
12 *
12 *
13 * You should have received a copy of the GNU General Public License
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
17 */
15 */
18
16
19−#include "../Greeter.h"
17+#include "Greeter.h"
20−#include "../GreeterPrivate.h"
18+#include "GreeterPrivate.h"
21
19
22namespace QLightDM
20namespace QLightDM
23{
21{
@@ -26,14 +24,33 @@ GreeterPrivate::GreeterPrivate(Greeter* parent)
26 : authenticated(false),
24 : authenticated(false),
27 authenticationUser(),
25 authenticationUser(),
28 twoFactorDone(false),
26 twoFactorDone(false),
27+ mockMode("single"),
29 q_ptr(parent)
28 q_ptr(parent)
30{
29{
30+ char *envMockMode = getenv("LIBLIGHTDM_MOCK_MODE");
31+ if (envMockMode) {
32+ mockMode = envMockMode;
33+ }
31}
34}
32
35
33void GreeterPrivate::handleAuthenticate()
36void GreeterPrivate::handleAuthenticate()
34{
37{
35 Q_Q(Greeter);
38 Q_Q(Greeter);
36
39
40+ if (mockMode == "single") {
41+ authenticated = true;
42+ Q_EMIT q->authenticationComplete();
43+ } else if (mockMode == "single-passphrase" || mockMode == "single-pin") {
44+ Q_EMIT q->showPrompt("Password: ", Greeter::PromptTypeSecret);
45+ } else if (mockMode == "full") {
46+ handleAuthenticate_full();
47+ }
48+}
49+
50+void GreeterPrivate::handleAuthenticate_full()
51+{
52+ Q_Q(Greeter);
53+
37 // Send out any messages we need to
54 // Send out any messages we need to
38 if (authenticationUser == "info-prompt")
55 if (authenticationUser == "info-prompt")
39 Q_EMIT q->showMessage("Welcome to Unity Greeter", Greeter::MessageTypeInfo);
56 Q_EMIT q->showMessage("Welcome to Unity Greeter", Greeter::MessageTypeInfo);
@@ -65,7 +82,24 @@ void GreeterPrivate::handleAuthenticate()
65 }
82 }
66}
83}
67
84
68−void GreeterPrivate::handleRespond(const QString &response)
85+void GreeterPrivate::handleRespond(QString const &response)
86+{
87+ Q_Q(Greeter);
88+
89+ if (mockMode == "single") {
90+ // NOOP
91+ } else if (mockMode == "single-passphrase") {
92+ authenticated = (response == "password");
93+ q->sendAuthenticationComplete();
94+ } else if (mockMode == "single-pin") {
95+ authenticated = (response == "1234");
96+ q->sendAuthenticationComplete();
97+ } else if (mockMode == "full") {
98+ handleRespond_full(response);
99+ }
100+}
101+
102+void GreeterPrivate::handleRespond_full(const QString &response)
69{
103{
70 Q_Q(Greeter);
104 Q_Q(Greeter);
71
105
tests/mocks/LightDM/liblightdm/GreeterPrivate.hnew file+54
@@ -0,0 +1,54 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef UNITY_MOCK_GREETER_PRIVATE_H
18+#define UNITY_MOCK_GREETER_PRIVATE_H
19+
20+#include <QtCore/QObject>
21+
22+namespace QLightDM
23+{
24+class Greeter;
25+class GreeterImpl;
26+
27+class GreeterPrivate
28+{
29+public:
30+ explicit GreeterPrivate(Greeter* parent=0);
31+ virtual ~GreeterPrivate() = default;
32+
33+ // These variables may not be used by all subclasses, that's no problem
34+ bool authenticated;
35+ QString authenticationUser;
36+ bool twoFactorDone;
37+
38+ QString mockMode;
39+
40+ void handleAuthenticate();
41+ void handleRespond(const QString &response);
42+
43+protected:
44+ GreeterImpl *m_impl; // if the backend needs more private data
45+ Greeter * const q_ptr;
46+
47+private:
48+ void handleAuthenticate_full();
49+ void handleRespond_full(const QString &response);
50+ Q_DECLARE_PUBLIC(Greeter)
51+};
52+}
53+
54+#endif // UNITY_MOCK_GREETER_PRIVATE_H
tests/mocks/LightDM/liblightdm/UsersModel.cppnew file+134
@@ -0,0 +1,134 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+// LightDM currently is Qt4 compatible, and so doesn't define setRoleNames.
20+// To use the same method of setting role name that it does, we
21+// set our compatibility to Qt4 here too.
22+#define QT_DISABLE_DEPRECATED_BEFORE QT_VERSION_CHECK(4, 0, 0)
23+
24+#include "UsersModel.h"
25+#include "UsersModelPrivate.h"
26+#include <QtCore/QDir>
27+#include <QtCore/QString>
28+#include <QtGui/QIcon>
29+
30+namespace QLightDM
31+{
32+
33+UsersModel::UsersModel(QObject *parent) :
34+ QAbstractListModel(parent),
35+ d_ptr(new UsersModelPrivate(this))
36+{
37+ Q_D(UsersModel);
38+
39+ // Extend roleNames (we want to keep the "display" role)
40+ QHash<int, QByteArray> roles = roleNames();
41+ roles[NameRole] = "name";
42+ roles[RealNameRole] = "realName";
43+ roles[LoggedInRole] = "loggedIn";
44+ roles[BackgroundRole] = "background";
45+ roles[BackgroundPathRole] = "backgroundPath";
46+ roles[SessionRole] = "session";
47+ roles[HasMessagesRole] = "hasMessages";
48+ roles[ImagePathRole] = "imagePath";
49+ setRoleNames(roles);
50+
51+ // Now modify our mock user backgrounds
52+ QDir bgdir = QDir("/usr/share/demo-assets/shell/backgrounds/");
53+ QStringList backgrounds = bgdir.entryList(QDir::Files | QDir::NoDotAndDotDot);
54+
55+ for (int i = 0, j = 0; i < d->entries.size(); i++) {
56+ Entry &entry = d->entries[i];
57+ if (entry.background.isNull() && !backgrounds.isEmpty()) {
58+ entry.background = bgdir.filePath(backgrounds[j++]);
59+ if (j >= backgrounds.length()) {
60+ j = 0;
61+ }
62+ }
63+ }
64+}
65+
66+UsersModel::~UsersModel()
67+{
68+ delete d_ptr;
69+}
70+
71+int UsersModel::rowCount(const QModelIndex &parent) const
72+{
73+ Q_D(const UsersModel);
74+
75+ if (parent.isValid()) {
76+ return 0;
77+ } else { // parent is root
78+ return d->entries.size();
79+ }
80+}
81+
82+QVariant UsersModel::data(const QModelIndex &index, int role) const
83+{
84+ Q_D(const UsersModel);
85+
86+ if (!index.isValid()) {
87+ return QVariant();
88+ }
89+
90+ int row = index.row();
91+ switch (role) {
92+ case Qt::DisplayRole:
93+ return d->entries[row].real_name;
94+ case Qt::DecorationRole:
95+ return QIcon();
96+ case UsersModel::NameRole:
97+ return d->entries[row].username;
98+ case UsersModel::RealNameRole:
99+ return d->entries[row].real_name;
100+ case UsersModel::SessionRole:
101+ return d->entries[row].session;
102+ case UsersModel::LoggedInRole:
103+ return d->entries[row].is_active;
104+ case UsersModel::BackgroundRole:
105+ return QPixmap(d->entries[row].background);
106+ case UsersModel::BackgroundPathRole:
107+ return d->entries[row].background;
108+ case UsersModel::HasMessagesRole:
109+ return d->entries[row].has_messages;
110+ case UsersModel::ImagePathRole:
111+ return "";
112+ default:
113+ return QVariant();
114+ }
115+}
116+
117+QString UsersModel::mockMode() const
118+{
119+ Q_D(const UsersModel);
120+ return d->mockMode;
121+}
122+
123+void UsersModel::setMockMode(QString mockMode)
124+{
125+ Q_D(UsersModel);
126+
127+ if (d->mockMode != mockMode) {
128+ d->mockMode = mockMode;
129+ Q_EMIT mockModeChanged(mockMode);
130+ d->resetEntries();
131+ }
132+}
133+
134+}
tests/mocks/LightDM/liblightdm/UsersModel.hnew file+69
@@ -0,0 +1,69 @@
1+/*
2+ * Copyright (C) 2013-2014 Canonical, Ltd.
3+ * Copyright (C) 2010-2011 David Edmundson.
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; version 3.
8+ *
9+ * This program is distributed in the hope that it will be useful,
10+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+ * GNU General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU General Public License
15+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
16+ */
17+
18+#ifndef UNITY_MOCK_USERSMODEL_H
19+#define UNITY_MOCK_USERSMODEL_H
20+
21+#include <QtCore/QString>
22+#include <QtCore/QSharedDataPointer>
23+#include <QAbstractListModel>
24+
25+
26+namespace QLightDM
27+{
28+class UsersModelPrivate;
29+
30+class Q_DECL_EXPORT UsersModel : public QAbstractListModel
31+{
32+ Q_OBJECT
33+
34+ Q_ENUMS(UserModelRoles)
35+
36+ //Mock-only API for testing purposes
37+ Q_PROPERTY(QString mockMode READ mockMode WRITE setMockMode NOTIFY mockModeChanged)
38+
39+Q_SIGNALS:
40+ void mockModeChanged(QString mode);
41+
42+public:
43+ explicit UsersModel(QObject *parent = 0);
44+ virtual ~UsersModel();
45+
46+ enum UserModelRoles {NameRole = Qt::UserRole,
47+ RealNameRole,
48+ LoggedInRole,
49+ BackgroundRole,
50+ SessionRole,
51+ HasMessagesRole,
52+ ImagePathRole,
53+ BackgroundPathRole
54+ };
55+
56+ int rowCount(const QModelIndex &parent) const;
57+ QVariant data(const QModelIndex &index, int role) const;
58+
59+ QString mockMode() const;
60+ void setMockMode(QString mockMode);
61+
62+private:
63+ UsersModelPrivate * const d_ptr;
64+ Q_DECLARE_PRIVATE(UsersModel)
65+};
66+
67+}
68+
69+#endif // UNITY_MOCK_USERSMODEL_H
tests/mocks/LightDM/full/UsersModelPrivate.cpp → tests/mocks/LightDM/liblightdm/UsersModelPrivate.cpprenamed+58−6
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2014 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -12,17 +12,69 @@
12 *
12 *
13 * You should have received a copy of the GNU General Public License
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
17 */
15 */
18
16
19−#include "../UsersModelPrivate.h"
17+#include "UsersModelPrivate.h"
18+#include "UsersModel.h"
20
19
21namespace QLightDM
20namespace QLightDM
22{
21{
23
22
24UsersModelPrivate::UsersModelPrivate(UsersModel* parent)
23UsersModelPrivate::UsersModelPrivate(UsersModel* parent)
24+ : mockMode("single")
25− : q_ptr(parent)
25+ , q_ptr(parent)
26+{
27+ char *envMockMode = getenv("LIBLIGHTDM_MOCK_MODE");
28+ if (envMockMode) {
29+ mockMode = envMockMode;
30+ }
31+ resetEntries();
32+}
33+
34+void UsersModelPrivate::resetEntries()
35+{
36+ Q_Q(UsersModel);
37+
38+ q->beginResetModel();
39+
40+ if (mockMode == "single") {
41+ resetEntries_single();
42+ } else if (mockMode == "single-passphrase") {
43+ resetEntries_singlePassphrase();
44+ } else if (mockMode == "single-pin") {
45+ resetEntries_singlePin();
46+ } else if (mockMode == "full") {
47+ resetEntries_full();
48+ }
49+
50+ q->endResetModel();
51+}
52+
53+void UsersModelPrivate::resetEntries_single()
54+{
55+ entries =
56+ {
57+ { "single", "Single User", 0, 0, false, false, 0, 0 },
58+ };
59+}
60+
61+void UsersModelPrivate::resetEntries_singlePassphrase()
62+{
63+ entries =
64+ {
65+ { "single", "Single User", 0, 0, false, false, 0, 0 },
66+ };
67+}
68+
69+void UsersModelPrivate::resetEntries_singlePin()
70+{
71+ entries =
72+ {
73+ { "has-pin", "Has PIN", 0, 0, false, false, 0, 0 },
74+ };
75+}
76+
77+void UsersModelPrivate::resetEntries_full()
26{
78{
27 entries =
79 entries =
28 {
80 {
@@ -49,4 +101,4 @@ UsersModelPrivate::UsersModelPrivate(UsersModel* parent)
49 };
101 };
50}
102}
51
103
52−}
104+} // namespace QLightDM
tests/mocks/LightDM/liblightdm/UsersModelPrivate.hnew file+63
@@ -0,0 +1,63 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef UNITY_MOCK_USERSMODEL_PRIVATE_H
18+#define UNITY_MOCK_USERSMODEL_PRIVATE_H
19+
20+#include <QtCore/QList>
21+#include <QtCore/QString>
22+
23+namespace QLightDM
24+{
25+class UsersModel;
26+
27+class Entry
28+{
29+public:
30+ QString username;
31+ QString real_name;
32+ QString background;
33+ QString layouts;
34+ bool is_active;
35+ bool has_messages;
36+ QString session;
37+ QString infographic;
38+};
39+
40+class UsersModelPrivate
41+{
42+public:
43+ explicit UsersModelPrivate(UsersModel *parent = 0);
44+ virtual ~UsersModelPrivate() = default;
45+
46+ QList<Entry> entries;
47+ QString mockMode;
48+
49+ void resetEntries();
50+protected:
51+ UsersModel * const q_ptr;
52+
53+private:
54+ void resetEntries_single();
55+ void resetEntries_singlePassphrase();
56+ void resetEntries_singlePin();
57+ void resetEntries_full();
58+ Q_DECLARE_PUBLIC(UsersModel)
59+};
60+
61+}
62+
63+#endif // UNITY_MOCK_USERSMODEL_PRIVATE_H
tests/mocks/LightDM/plugin.cppnew file+68
@@ -0,0 +1,68 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "plugin.h"
18+#include <DBusGreeter.h>
19+#include <DBusGreeterList.h>
20+#include "MockGreeter.h"
21+#include "MockUsersModel.h"
22+#include <libusermetricsoutput/ColorTheme.h>
23+#include <libusermetricsoutput/UserMetrics.h>
24+#include <QLightDM/UsersModel>
25+
26+#include <QAbstractItemModel>
27+#include <QDBusConnection>
28+#include <QtQml/qqml.h>
29+
30+static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
31+{
32+ Q_UNUSED(engine)
33+ Q_UNUSED(scriptEngine)
34+
35+ MockGreeter *greeter = new MockGreeter;
36+ new DBusGreeter(greeter, "/");
37+ new DBusGreeterList(greeter, "/list");
38+
39+ return greeter;
40+}
41+
42+static QObject *users_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
43+{
44+ Q_UNUSED(engine)
45+ Q_UNUSED(scriptEngine)
46+ return new MockUsersModel;
47+}
48+
49+static QObject *infographic_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
50+{
51+ Q_UNUSED(engine)
52+ Q_UNUSED(scriptEngine)
53+ return UserMetricsOutput::UserMetrics::getInstance();
54+}
55+
56+void LightDMPlugin::registerTypes(const char *uri)
57+{
58+ qmlRegisterType<QAbstractItemModel>();
59+ qmlRegisterType<UserMetricsOutput::ColorTheme>();
60+
61+ Q_ASSERT(uri == QLatin1String("LightDM"));
62+ qRegisterMetaType<QLightDM::Greeter::MessageType>("QLightDM::Greeter::MessageType");
63+ qRegisterMetaType<QLightDM::Greeter::PromptType>("QLightDM::Greeter::PromptType");
64+ qmlRegisterSingletonType<MockGreeter>(uri, 0, 1, "Greeter", greeter_provider);
65+ qmlRegisterSingletonType<MockUsersModel>(uri, 0, 1, "Users", users_provider);
66+ qmlRegisterUncreatableType<QLightDM::UsersModel>(uri, 0, 1, "UserRoles", "Type is not instantiable");
67+ qmlRegisterSingletonType<UserMetricsOutput::UserMetrics>(uri, 0, 1, "Infographic", infographic_provider);
68+}
tests/mocks/LightDM/single/UsersModelPrivate.cpp → tests/mocks/LightDM/plugin.hrenamed+13−14
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2014 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -12,22 +12,21 @@
12 *
12 *
13 * You should have received a copy of the GNU General Public License
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
17 */
15 */
18
16
19−#include "../UsersModelPrivate.h"
17+#ifndef MOCK_LIGHTDM_PLUGIN_H
18+#define MOCK_LIGHTDM_PLUGIN_H
20
19
21−namespace QLightDM
20+#include <QtQml/QQmlEngine>
22−{
21+#include <QtQml/QQmlExtensionPlugin>
23
22
24−UsersModelPrivate::UsersModelPrivate(UsersModel* parent)
23+class LightDMPlugin : public QQmlExtensionPlugin
25− : q_ptr(parent)
26{
24{
27− entries =
25+ Q_OBJECT
28− {
26+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29− { "single", "Single User", 0, 0, false, false, 0, 0 },
27+
28+public:
29+ void registerTypes(const char *uri);
30− };
30+};
31−}
32
31
33−}
32+#endif
tests/mocks/LightDM/single-passphrase/CMakeLists.txtdeleted−19
@@ -1,19 +0,0 @@
1−set(LibLightDM_SOURCES
2− ../Greeter.cpp
3− ../UsersModel.cpp
4− GreeterPrivate.cpp
5− UsersModelPrivate.cpp
6− ${CMAKE_SOURCE_DIR}/plugins/Utils/qvariantlistmodel.cpp
7− )
8−
9−
10−add_library(MockLightDM-single-passphrase SHARED ${LibLightDM_SOURCES})
11−
12−qt5_use_modules(MockLightDM-single-passphrase Gui)
13−
14−set_target_properties(MockLightDM-single-passphrase PROPERTIES
15− OUTPUT_NAME lightdm-qt5-2)
16−
17−install(TARGETS MockLightDM-single-passphrase
18− DESTINATION ${SHELL_INSTALL_QML}/mocks/LightDM/single-passphrase
19− )
tests/mocks/LightDM/single-passphrase/GreeterPrivate.cppdeleted−46
@@ -1,46 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
17− */
18−
19−#include "../Greeter.h"
20−#include "../GreeterPrivate.h"
21−
22−namespace QLightDM
23−{
24−
25−GreeterPrivate::GreeterPrivate(Greeter* parent)
26− : authenticated(false),
27− authenticationUser(),
28− q_ptr(parent)
29−{
30−}
31−
32−void GreeterPrivate::handleAuthenticate()
33−{
34− Q_Q(Greeter);
35− Q_EMIT q->showPrompt("Password: ", Greeter::PromptTypeSecret);
36−}
37−
38−void GreeterPrivate::handleRespond(const QString &response)
39−{
40− Q_Q(Greeter);
41−
42− authenticated = (response == "password");
43− q->sendAuthenticationComplete();
44−}
45−
46−}
tests/mocks/LightDM/single-pin/CMakeLists.txtdeleted−19
@@ -1,19 +0,0 @@
1−set(LibLightDM_SOURCES
2− ../Greeter.cpp
3− ../UsersModel.cpp
4− GreeterPrivate.cpp
5− UsersModelPrivate.cpp
6− ${CMAKE_SOURCE_DIR}/plugins/Utils/qvariantlistmodel.cpp
7− )
8−
9−
10−add_library(MockLightDM-single-pin SHARED ${LibLightDM_SOURCES})
11−
12−qt5_use_modules(MockLightDM-single-pin Gui)
13−
14−set_target_properties(MockLightDM-single-pin PROPERTIES
15− OUTPUT_NAME lightdm-qt5-2)
16−
17−install(TARGETS MockLightDM-single-pin
18− DESTINATION ${SHELL_INSTALL_QML}/mocks/LightDM/single-pin
19− )
tests/mocks/LightDM/single/CMakeLists.txtdeleted−18
@@ -1,18 +0,0 @@
1−set(LibLightDM_SOURCES
2− ../Greeter.cpp
3− ../UsersModel.cpp
4− GreeterPrivate.cpp
5− UsersModelPrivate.cpp
6− ${CMAKE_SOURCE_DIR}/plugins/Utils/qvariantlistmodel.cpp
7− )
8−
9−add_library(MockLightDM-single SHARED ${LibLightDM_SOURCES})
10−
11−qt5_use_modules(MockLightDM-single Gui)
12−
13−set_target_properties(MockLightDM-single PROPERTIES
14− OUTPUT_NAME lightdm-qt5-2)
15−
16−install(TARGETS MockLightDM-single
17− DESTINATION ${SHELL_INSTALL_QML}/mocks/LightDM/single
18− )
tests/mocks/Unity/Application/MirSurfaceItem.qml+16−1
@@ -44,9 +44,24 @@ Rectangle {
44 }
44 }
45
45
46 Text {
46 Text {
47+ text: surfaceText.text
48+ color: "black"
49+ font: surfaceText.font
50+ fontSizeMode: Text.Fit
51+ minimumPixelSize: 10
52+ verticalAlignment: Text.AlignVCenter
53+ x: surfaceText.x
54+ y: surfaceText.y
55+ width: surfaceText.width
56+ height: surfaceText.height
57+
58+ transform: Translate { x: -2; y: -2 }
59+ }
60+ Text {
61+ id: surfaceText
47 anchors.fill: parent
62 anchors.fill: parent
48 text: "SURFACE"
63 text: "SURFACE"
49− color: "yellow"
64+ color: root.parent && root.parent.activeFocus ? "yellow" : "blue"
50 font.bold: true
65 font.bold: true
51 fontSizeMode: Text.Fit
66 fontSizeMode: Text.Fit
52 minimumPixelSize: 10; font.pixelSize: 200
67 minimumPixelSize: 10; font.pixelSize: 200
tests/mocks/Unity/CMakeLists.txt+4−1
@@ -6,7 +6,7 @@ add_subdirectory(Notifications)
6add_subdirectory(DashCommunicator)
6add_subdirectory(DashCommunicator)
7
7
8pkg_search_module(GOBJECT gobject-2.0 REQUIRED)
8pkg_search_module(GOBJECT gobject-2.0 REQUIRED)
9−pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=5)
9+pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=6)
10
10
11include_directories(
11include_directories(
12 ${CMAKE_CURRENT_BINARY_DIR}
12 ${CMAKE_CURRENT_BINARY_DIR}
@@ -40,6 +40,9 @@ set(UnityQML_SOURCES
40 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/SettingsModelInterface.h
40 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/SettingsModelInterface.h
41)
41)
42
42
43+# Workaround for gcc failure LP: #1417664
44+set_source_files_properties(fake_scopes.cpp PROPERTIES COMPILE_FLAGS -O1)
45+
43add_library(FakeUnityQml MODULE ${UnityQML_SOURCES})
46add_library(FakeUnityQml MODULE ${UnityQML_SOURCES})
44target_link_libraries(FakeUnityQml
47target_link_libraries(FakeUnityQml
45 ${Qt5Core_LIBRARIES}
48 ${Qt5Core_LIBRARIES}
tests/mocks/Unity/Indicators/fakeindicatorsmodel.cpp+13−1
@@ -21,7 +21,8 @@
21#include "indicators.h"
21#include "indicators.h"
22
22
23FakeIndicatorsModel::FakeIndicatorsModel(QObject *parent)
23FakeIndicatorsModel::FakeIndicatorsModel(QObject *parent)
24− : QAbstractListModel(parent)
24+ : QAbstractListModel(parent),
25+ m_profile("phone")
25{
26{
26 QObject::connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
27 QObject::connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
27 QObject::connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
28 QObject::connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
@@ -38,6 +39,17 @@ int FakeIndicatorsModel::count() const
38 return rowCount();
39 return rowCount();
39}
40}
40
41
42+QString FakeIndicatorsModel::profile() const
43+{
44+ return m_profile;
45+}
46+
47+void FakeIndicatorsModel::setProfile(const QString& profile)
48+{
49+ m_profile = profile;
50+ Q_EMIT profileChanged();
51+}
52+
41void FakeIndicatorsModel::load(const QString&)
53void FakeIndicatorsModel::load(const QString&)
42{
54{
43}
55}
tests/mocks/Unity/Indicators/fakeindicatorsmodel.h+6
@@ -27,7 +27,9 @@ class FakeIndicatorsModel : public QAbstractListModel
27 Q_OBJECT
27 Q_OBJECT
28 Q_ENUMS(Roles)
28 Q_ENUMS(Roles)
29 Q_PROPERTY(int count READ count NOTIFY countChanged)
29 Q_PROPERTY(int count READ count NOTIFY countChanged)
30+ Q_PROPERTY(QString profile READ profile WRITE setProfile NOTIFY profileChanged)
30 Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
31 Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
32+
31public:
33public:
32
34
33 FakeIndicatorsModel(QObject *parent=0);
35 FakeIndicatorsModel(QObject *parent=0);
@@ -42,6 +44,8 @@ public:
42
44
43 Q_INVOKABLE QVariant data(int row, int role) const;
45 Q_INVOKABLE QVariant data(int row, int role) const;
44
46
47+ QString profile() const;
48+ void setProfile(const QString& profile);
45 void setModelData(const QVariant& data);
49 void setModelData(const QVariant& data);
46 QVariant modelData() const { return m_modelData; }
50 QVariant modelData() const { return m_modelData; }
47
51
@@ -54,11 +58,13 @@ public:
54
58
55Q_SIGNALS:
59Q_SIGNALS:
56 void countChanged();
60 void countChanged();
61+ void profileChanged();
57 void modelDataChanged();
62 void modelDataChanged();
58
63
59private:
64private:
60 int count() const;
65 int count() const;
61
66
67+ QString m_profile;
62 QVariant m_modelData;
68 QVariant m_modelData;
63};
69};
64
70
tests/mocks/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=5)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
2
2
3include_directories(
3include_directories(
4 ${CMAKE_CURRENT_SOURCE_DIR}
4 ${CMAKE_CURRENT_SOURCE_DIR}
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+10
@@ -209,6 +209,16 @@ void MockLauncherModel::setApplicationManager(unity::shell::application::Applica
209 Q_UNUSED(applicationManager)
209 Q_UNUSED(applicationManager)
210}
210}
211
211
212+bool MockLauncherModel::onlyPinned() const
213+{
214+ return false;
215+}
216+
217+void MockLauncherModel::setOnlyPinned(bool onlyPinned)
218+{
219+ Q_UNUSED(onlyPinned)
220+}
221+
212void MockLauncherModel::emitHint()
222void MockLauncherModel::emitHint()
213{
223{
214 Q_EMIT hint();
224 Q_EMIT hint();
tests/mocks/Unity/Launcher/MockLauncherModel.h+3
@@ -49,6 +49,9 @@ public:
49 unity::shell::application::ApplicationManagerInterface *applicationManager() const;
49 unity::shell::application::ApplicationManagerInterface *applicationManager() const;
50 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager);
50 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager);
51
51
52+ bool onlyPinned() const override;
53+ void setOnlyPinned(bool onlyPinned) override;
54+
52 // For testing
55 // For testing
53 Q_INVOKABLE void emitHint();
56 Q_INVOKABLE void emitHint();
54
57
tests/mocks/Unity/Notifications/CMakeLists.txt+2−1
@@ -4,7 +4,8 @@ include_directories(
4
4
5set(MockNotificationsPlugin_SOURCES
5set(MockNotificationsPlugin_SOURCES
6 plugin.cpp
6 plugin.cpp
7− MockNotificationTypes.cpp
7+ MockNotification.cpp
8+ MockNotificationModel.cpp
8 MockActionModel.cpp
9 MockActionModel.cpp
9)
10)
10
11
tests/mocks/Unity/Notifications/MockActionModel.cpp+6−4
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2015 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
5 * it under the terms of the GNU Lesser General Public License as published by
@@ -25,8 +25,6 @@ struct ActionModelPrivate {
25};
25};
26
26
27ActionModel::ActionModel(QObject *parent) : QStringListModel(parent), p(new ActionModelPrivate) {
27ActionModel::ActionModel(QObject *parent) : QStringListModel(parent), p(new ActionModelPrivate) {
28− insertAction("ok_id", "Ok");
29− insertAction("cancel_id", "Cancel");
30}
28}
31
29
32ActionModel::~ActionModel() {
30ActionModel::~ActionModel() {
@@ -66,7 +64,11 @@ Q_INVOKABLE QVariant ActionModel::data(int row, int role) const
66 return data(index(row, 0), role);
64 return data(index(row, 0), role);
67}
65}
68
66
69−void ActionModel::insertAction(const QString &id, const QString &label) {
67+void ActionModel::append(const QString &id, const QString &label) {
70 p->ids.push_back(id);
68 p->ids.push_back(id);
71 p->labels.push_back(label);
69 p->labels.push_back(label);
72}
70}
71+
72+int ActionModel::getCount() const {
73+ return p->labels.size();
74+}
tests/mocks/Unity/Notifications/MockActionModel.h+4−2
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 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
@@ -26,6 +26,7 @@ struct ActionModelPrivate;
26
26
27class ActionModel : public QStringListModel {
27class ActionModel : public QStringListModel {
28 Q_OBJECT
28 Q_OBJECT
29+ Q_PROPERTY(int count READ getCount)
29
30
30public:
31public:
31 ActionModel(QObject *parent=nullptr);
32 ActionModel(QObject *parent=nullptr);
@@ -42,7 +43,8 @@ public:
42 };
43 };
43 Q_INVOKABLE QVariant data(int row, int role) const;
44 Q_INVOKABLE QVariant data(int row, int role) const;
44
45
45− void insertAction(const QString &id, const QString &label);
46+ Q_INVOKABLE void append(const QString &id, const QString &label);
47+ int getCount() const;
46
48
47private:
49private:
48 QScopedPointer<ActionModelPrivate> p;
50 QScopedPointer<ActionModelPrivate> p;
tests/mocks/Unity/Notifications/MockNotification.cppnew file+178
@@ -0,0 +1,178 @@
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+ * Authors:
17+ * Mirco Mueller <mirco.mueller@canonical.com>
18+ */
19+
20+#include "MockNotification.h"
21+
22+#include <QDebug>
23+
24+struct MockNotificationPrivate {
25+ int id;
26+ QString summary;
27+ QString body;
28+ int value;
29+ MockNotification::Type type;
30+ QString icon;
31+ QString secondaryIcon;
32+ QStringList actions;
33+ ActionModel* actionsModel;
34+ QVariantMap hints;
35+};
36+
37+MockNotification::MockNotification(QObject *parent) : QObject(parent), p(new MockNotificationPrivate()) {
38+ p->actionsModel = new ActionModel();
39+}
40+
41+MockNotification::~MockNotification() {
42+}
43+
44+QString MockNotification::getSummary() const {
45+ return p->summary;
46+}
47+
48+void MockNotification::setSummary(const QString &summary) {
49+ if(p->summary != summary) {
50+ p->summary = summary;
51+ Q_EMIT summaryChanged(p->summary);
52+ Q_EMIT dataChanged(p->id);
53+ }
54+}
55+
56+QString MockNotification::getBody() const {
57+ return p->body;
58+}
59+
60+void MockNotification::setBody(const QString &body) {
61+ if(p->body != body) {
62+ p->body = body;
63+ Q_EMIT bodyChanged(p->body);
64+ Q_EMIT dataChanged(p->id);
65+ }
66+}
67+
68+int MockNotification::getID() const {
69+ return p->id;
70+}
71+
72+void MockNotification::setID(const int id) {
73+ p->id = id;
74+}
75+
76+int MockNotification::getValue() const {
77+ return p->value;
78+}
79+
80+void MockNotification::setValue(int value) {
81+ if(p->value != value) {
82+ p->value = value;
83+ Q_EMIT valueChanged(p->value);
84+ Q_EMIT dataChanged(p->id);
85+ }
86+}
87+
88+QString MockNotification::getIcon() const {
89+ return p->icon;
90+}
91+
92+void MockNotification::setIcon(const QString &icon) {
93+ if (icon.startsWith(" ") || icon.size() == 0) {
94+ p->icon = nullptr;
95+ }
96+ else {
97+ p->icon = icon;
98+
99+ if (icon.indexOf("/") == -1) {
100+ p->icon.prepend("image://theme/");
101+ }
102+ }
103+
104+ Q_EMIT iconChanged(p->icon);
105+ Q_EMIT dataChanged(p->id);
106+}
107+
108+QString MockNotification::getSecondaryIcon() const {
109+ return p->secondaryIcon;
110+}
111+
112+void MockNotification::setSecondaryIcon(const QString &secondaryIcon) {
113+ if (secondaryIcon.startsWith(" ") || secondaryIcon.size() == 0) {
114+ p->secondaryIcon = nullptr;
115+ }
116+ else {
117+ p->secondaryIcon = secondaryIcon;
118+
119+ if (secondaryIcon.indexOf("/") == -1) {
120+ p->secondaryIcon.prepend("image://theme/");
121+ }
122+ }
123+
124+ Q_EMIT secondaryIconChanged(p->secondaryIcon);
125+ Q_EMIT dataChanged(p->id);
126+}
127+
128+MockNotification::Type MockNotification::getType() const {
129+ return p->type;
130+}
131+
132+void MockNotification::setType(Type type) {
133+ if(p->type != type) {
134+ p->type = type;
135+ Q_EMIT typeChanged(p->type);
136+ }
137+}
138+
139+ActionModel* MockNotification::getActions() const {
140+ return p->actionsModel;
141+}
142+
143+void MockNotification::setActions(const QStringList &actions) {
144+ if(p->actions != actions) {
145+ p->actions = actions;
146+ Q_EMIT actionsChanged(p->actions);
147+
148+ for (int i = 0; i < p->actions.size(); i += 2) {
149+ p->actionsModel->append(p->actions[i], p->actions[i+1]);
150+ }
151+ }
152+}
153+
154+QVariantMap MockNotification::getHints() const {
155+ return p->hints;
156+}
157+
158+void MockNotification::setHints(const QVariantMap& hints) {
159+ if (p->hints != hints) {
160+ p->hints = hints;
161+ Q_EMIT hintsChanged(p->hints);
162+ }
163+}
164+
165+void MockNotification::invokeAction(const QString &action) {
166+ for(int i=0; i<p->actions.size(); i++) {
167+ if(p->actions[i] == action) {
168+ Q_EMIT actionInvoked(action);
169+ qDebug() << "Info: invoked action" << action;
170+ return;
171+ }
172+ }
173+ fprintf(stderr, "Error: tried to invoke action not in actionList.\n");
174+}
175+
176+void MockNotification::close() {
177+ Q_EMIT completed(p->id);
178+}
tests/mocks/Unity/Notifications/MockNotification.hnew file+94
@@ -0,0 +1,94 @@
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+ * Authors:
17+ * Mirco Mueller <mirco.mueller@canonical.com>
18+ */
19+
20+#ifndef MOCK_NOTIFICATION_H
21+#define MOCK_NOTIFICATION_H
22+
23+#include "MockActionModel.h"
24+#include <QObject>
25+#include <QString>
26+#include <QStringList>
27+#include <QScopedPointer>
28+
29+struct MockNotificationPrivate;
30+
31+class MockNotification : public QObject {
32+ Q_OBJECT
33+ Q_ENUMS(Type)
34+ Q_PROPERTY(QString summary READ getSummary WRITE setSummary NOTIFY summaryChanged)
35+ Q_PROPERTY(QString body READ getBody WRITE setBody NOTIFY bodyChanged)
36+ Q_PROPERTY(int nid READ getID WRITE setID NOTIFY idChanged)
37+ Q_PROPERTY(int value READ getValue WRITE setValue NOTIFY valueChanged)
38+ Q_PROPERTY(QString icon READ getIcon WRITE setIcon NOTIFY iconChanged)
39+ Q_PROPERTY(QString secondaryIcon READ getSecondaryIcon WRITE setSecondaryIcon NOTIFY secondaryIconChanged)
40+ Q_PROPERTY(Type type READ getType WRITE setType NOTIFY typeChanged)
41+ Q_PROPERTY(QStringList rawActions WRITE setActions)
42+ Q_PROPERTY(ActionModel* actions READ getActions NOTIFY actionsChanged)
43+ Q_PROPERTY(QVariantMap hints READ getHints WRITE setHints NOTIFY hintsChanged)
44+
45+private:
46+ QScopedPointer<MockNotificationPrivate> p;
47+
48+public:
49+ enum Urgency { Low, Normal, Critical };
50+ enum Type { PlaceHolder, Confirmation, Ephemeral, Interactive, SnapDecision };
51+
52+Q_SIGNALS:
53+ void summaryChanged(const QString &summary);
54+ void bodyChanged(const QString &body);
55+ void idChanged(const int id);
56+ void valueChanged(int value);
57+ void iconChanged(const QString &icon);
58+ void secondaryIconChanged(const QString &secondaryIcon);
59+ void typeChanged(Type type);
60+ void actionsChanged(const QStringList &actions);
61+ void hintsChanged(const QVariantMap& hints);
62+
63+ void dataChanged(int nid);
64+ void completed(int nid);
65+ void actionInvoked(const QString &action);
66+
67+public:
68+ MockNotification(QObject *parent=nullptr);
69+ virtual ~MockNotification();
70+
71+ QString getSummary() const;
72+ void setSummary(const QString &summary);
73+ QString getBody() const;
74+ void setBody(const QString &body);
75+ int getID() const;
76+ void setID(const int id);
77+ int getValue() const;
78+ void setValue(int value);
79+ QString getIcon() const;
80+ void setIcon(const QString &icon);
81+ QString getSecondaryIcon() const;
82+ void setSecondaryIcon(const QString &secondaryIcon);
83+ Type getType() const;
84+ void setType(Type type);
85+ ActionModel* getActions() const;
86+ void setActions(const QStringList &actions);
87+ QVariantMap getHints() const;
88+ void setHints(const QVariantMap& hints);
89+
90+ Q_INVOKABLE void invokeAction(const QString &action);
91+ Q_INVOKABLE void close();
92+};
93+
94+#endif // MOCK_NOTIFICATION_H
tests/mocks/Unity/Notifications/MockNotificationModel.cppnew file+172
@@ -0,0 +1,172 @@
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+ * Authors:
17+ * Mirco Mueller <mirco.mueller@canonical.com>
18+ */
19+
20+#include "MockNotificationModel.h"
21+#include "MockNotification.h"
22+
23+#include <unity/shell/notifications/ModelInterface.h>
24+
25+#include <QTimer>
26+#include <QList>
27+#include <QVector>
28+#include <QMap>
29+#include <QStringListModel>
30+#include <QQmlEngine>
31+
32+using namespace unity::shell::notifications;
33+
34+MockNotificationModel::MockNotificationModel(QObject *parent) : QAbstractListModel(parent) {
35+}
36+
37+MockNotificationModel::~MockNotificationModel() {
38+}
39+
40+int MockNotificationModel::rowCount(const QModelIndex &parent) const {
41+ return m_queue.size();
42+}
43+
44+int MockNotificationModel::getCount() const {
45+ return m_queue.size();
46+}
47+
48+QVariant MockNotificationModel::data(const QModelIndex &index, int role) const {
49+ if (!index.isValid())
50+ return QVariant();
51+
52+ switch(role) {
53+ case ModelInterface::RoleType:
54+ return QVariant(m_queue[index.row()]->getType());
55+
56+ case ModelInterface::RoleId:
57+ return QVariant(m_queue[index.row()]->getID());
58+
59+ case ModelInterface::RoleSummary:
60+ return QVariant(m_queue[index.row()]->getSummary());
61+
62+ case ModelInterface::RoleBody:
63+ return QVariant(m_queue[index.row()]->getBody());
64+
65+ case ModelInterface::RoleValue:
66+ return QVariant(m_queue[index.row()]->getValue());
67+
68+ case ModelInterface::RoleIcon:
69+ return QVariant(m_queue[index.row()]->getIcon());
70+
71+ case ModelInterface::RoleSecondaryIcon:
72+ return QVariant(m_queue[index.row()]->getSecondaryIcon());
73+
74+ case ModelInterface::RoleActions:
75+ return QVariant::fromValue(m_queue[index.row()]->getActions());
76+
77+ case ModelInterface::RoleHints:
78+ return QVariant(m_queue[index.row()]->getHints());
79+
80+ case ModelInterface::RoleNotification:
81+ return QVariant::fromValue(m_queue[index.row()]);
82+
83+ default:
84+ return QVariant();
85+ }
86+}
87+
88+void MockNotificationModel::append(MockNotification* n) {
89+ int location = m_queue.size();
90+ QModelIndex insertionPoint = QModelIndex();
91+ beginInsertRows(insertionPoint, location, location);
92+ m_queue.insert(location, n);
93+ endInsertRows();
94+}
95+
96+MockNotification* MockNotificationModel::getNotification(int id) const {
97+ for(int i=0; i < m_queue.size(); i++) {
98+ if(m_queue[i]->getID() == id) {
99+ return m_queue[i];
100+ }
101+ }
102+
103+ return nullptr;
104+}
105+
106+void MockNotificationModel::remove(const int id) {
107+ for(int i = 0; i < m_queue.size(); i++) {
108+ if(m_queue[i]->getID() == id) {
109+ removeInternal(i);
110+ return;
111+ }
112+ }
113+}
114+
115+void MockNotificationModel::removeSecond() {
116+ if(m_queue.size() < 2)
117+ return;
118+ removeInternal(1);
119+}
120+
121+void MockNotificationModel::removeInternal(int loc) {
122+ QModelIndex deletePoint = QModelIndex();
123+ beginRemoveRows(deletePoint, loc, loc);
124+ m_queue.erase(m_queue.begin() + loc);
125+ endRemoveRows();
126+}
127+
128+MockNotification* MockNotificationModel::getRaw(const int notificationId) const {
129+ for(int i = 0; i < m_queue.size(); i++) {
130+ if(m_queue[i]->getID() == notificationId) {
131+ MockNotification* n = m_queue[i];
132+ return n;
133+ }
134+ }
135+
136+ return nullptr;
137+}
138+
139+int MockNotificationModel::queued() const {
140+ return m_queue.size();
141+}
142+
143+QHash<int, QByteArray> MockNotificationModel::roleNames() const {
144+ QHash<int, QByteArray> roles;
145+
146+ roles.insert(ModelInterface::RoleType, "type");
147+ roles.insert(ModelInterface::RoleUrgency, "urgency");
148+ roles.insert(ModelInterface::RoleId, "id");
149+ roles.insert(ModelInterface::RoleSummary, "summary");
150+ roles.insert(ModelInterface::RoleBody, "body");
151+ roles.insert(ModelInterface::RoleValue, "value");
152+ roles.insert(ModelInterface::RoleIcon, "icon");
153+ roles.insert(ModelInterface::RoleSecondaryIcon, "secondaryIcon");
154+ roles.insert(ModelInterface::RoleActions, "actions");
155+ roles.insert(ModelInterface::RoleHints, "hints");
156+ roles.insert(ModelInterface::RoleNotification, "notification");
157+
158+ return roles;
159+}
160+
161+void MockNotificationModel::onCompleted(int id) {
162+ remove(id);
163+}
164+
165+void MockNotificationModel::onDataChanged(int id) {
166+ for(int i = 0; i < m_queue.size(); i++) {
167+ if(m_queue[i]->getID() == id) {
168+ Q_EMIT dataChanged(index(i, 0), index(i, 0));
169+ break;
170+ }
171+ }
172+}
tests/mocks/Unity/Notifications/MockNotificationModel.hnew file+72
@@ -0,0 +1,72 @@
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+ * Authors:
17+ * Mirco Mueller <mirco.mueller@canonical.com>
18+ */
19+
20+#ifndef MOCK_NOTIFICATION_MODEL_H
21+#define MOCK_NOTIFICATION_MODEL_H
22+
23+#include <QAbstractListModel>
24+#include <QSharedPointer>
25+#include <QScopedPointer>
26+#include "MockNotification.h"
27+
28+class MockNotification;
29+
30+class MockNotificationModel : public QAbstractListModel {
31+ Q_OBJECT
32+ Q_PROPERTY(int count READ getCount)
33+
34+public:
35+ MockNotificationModel(QObject *parent=nullptr);
36+ virtual ~MockNotificationModel();
37+
38+ virtual int rowCount(const QModelIndex &parent) const;
39+ virtual QVariant data(const QModelIndex &index, int role) const;
40+ virtual QHash<int, QByteArray> roleNames() const;
41+
42+ Q_INVOKABLE void append(MockNotification* n);
43+ MockNotification* getNotification(int id) const;
44+
45+ // getRaw() is only meant to be used from QML, since QML cannot handle
46+ // QSharedPointers... on C++-side only use getNotification()
47+ Q_INVOKABLE MockNotification* getRaw(const int notificationId) const;
48+
49+ Q_INVOKABLE int queued() const;
50+ Q_INVOKABLE void remove(const int id);
51+ Q_INVOKABLE void removeSecond();
52+
53+ int getCount() const;
54+
55+Q_SIGNALS:
56+ void actionInvoked(const QString &action);
57+
58+public Q_SLOTS:
59+ void onCompleted(int id);
60+
61+private Q_SLOTS:
62+ void onDataChanged(int id);
63+
64+Q_SIGNALS:
65+ void queueSizeChanged(int newSize);
66+
67+private:
68+ QList<MockNotification*> m_queue;
69+ void removeInternal(int loc);
70+};
71+
72+#endif
tests/mocks/Unity/Notifications/plugin.cpp+5−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 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
@@ -19,13 +19,15 @@
19
19
20#include "plugin.h"
20#include "plugin.h"
21#include "MockActionModel.h"
21#include "MockActionModel.h"
22−#include "MockNotificationTypes.h"
22+#include "MockNotification.h"
23+#include "MockNotificationModel.h"
23
24
24#include <QtQml/qqml.h>
25#include <QtQml/qqml.h>
25
26
26void TestNotificationPlugin::registerTypes(const char* uri)
27void TestNotificationPlugin::registerTypes(const char* uri)
27{
28{
28 // @uri Unity.Notifications
29 // @uri Unity.Notifications
29− qmlRegisterUncreatableType<MockNotification>(uri, 1, 0, "Notification", "Notification objects can only be created by the plugin");
30+ qmlRegisterType<MockNotification>(uri, 1, 0, "Notification");
31+ qmlRegisterType<MockNotificationModel>(uri, 1, 0, "NotificationModel");
30 qmlRegisterType<ActionModel>(uri, 1, 0, "ActionModel");
32 qmlRegisterType<ActionModel>(uri, 1, 0, "ActionModel");
31}
33}
tests/mocks/Unity/Notifications/plugin.h+1−2
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 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
@@ -17,7 +17,6 @@
17 * Mirco Mueller <mirco.mueller@canonical.com>
17 * Mirco Mueller <mirco.mueller@canonical.com>
18 */
18 */
19
19
20−
21#ifndef TESTNOTIFICATION_PLUGIN_H
20#ifndef TESTNOTIFICATION_PLUGIN_H
22#define TESTNOTIFICATION_PLUGIN_H
21#define TESTNOTIFICATION_PLUGIN_H
23
22
tests/mocks/Unity/fake_previewmodel.cpp+15−1
@@ -22,18 +22,23 @@
22#include "fake_previewmodel.h"
22#include "fake_previewmodel.h"
23
23
24// local
24// local
25+#include "fake_scope.h"
26+#include "fake_scopes.h"
25#include "fake_previewwidgetmodel.h"
27#include "fake_previewwidgetmodel.h"
26
28
27// Qt
29// Qt
28#include <QDebug>
30#include <QDebug>
29
31
30−PreviewModel::PreviewModel(QObject* parent)
32+PreviewModel::PreviewModel(QObject* parent, Scope* scope)
31 : unity::shell::scopes::PreviewModelInterface(parent)
33 : unity::shell::scopes::PreviewModelInterface(parent)
32 , m_loaded(true)
34 , m_loaded(true)
35+ , m_scope(scope)
33{
36{
34 // we have one column by default
37 // we have one column by default
35 PreviewWidgetModel* columnModel = new PreviewWidgetModel(this);
38 PreviewWidgetModel* columnModel = new PreviewWidgetModel(this);
36 m_previewWidgetModels.append(columnModel);
39 m_previewWidgetModels.append(columnModel);
40+ connect(this, SIGNAL(triggered(QString const&, QString const&, QVariantMap const&)),
41+ this, SLOT(triggeredSlot(QString const&, QString const&, QVariantMap const&)));
37}
42}
38
43
39void PreviewModel::setWidgetColumnCount(int count)
44void PreviewModel::setWidgetColumnCount(int count)
@@ -80,3 +85,12 @@ void PreviewModel::setLoaded(bool loaded)
80 Q_EMIT loadedChanged();
85 Q_EMIT loadedChanged();
81 }
86 }
82}
87}
88+
89+void PreviewModel::triggeredSlot(QString const&, QString const&, QVariantMap const&) {
90+ if (m_scope) {
91+ Scopes *scopes = dynamic_cast<Scopes*>(m_scope->parent());
92+ Scope* scope = scopes->getScopeFromAll("MockScope9");
93+ scopes->addTempScope(scope);
94+ Q_EMIT m_scope->openScope(scope);
95+ }
96+}
tests/mocks/Unity/fake_previewmodel.h+7−1
@@ -25,12 +25,14 @@
25
25
26class PreviewWidgetModel;
26class PreviewWidgetModel;
27
27
28+class Scope;
29+
28class PreviewModel : public unity::shell::scopes::PreviewModelInterface
30class PreviewModel : public unity::shell::scopes::PreviewModelInterface
29{
31{
30 Q_OBJECT
32 Q_OBJECT
31
33
32public:
34public:
33− explicit PreviewModel(QObject* parent = 0);
35+ explicit PreviewModel(QObject* parent = 0, Scope* scope = 0);
34
36
35 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
37 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
36 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
38 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
@@ -42,9 +44,13 @@ public:
42
44
43 Q_INVOKABLE void setLoaded(bool); // Only available for testing
45 Q_INVOKABLE void setLoaded(bool); // Only available for testing
44
46
47+private Q_SLOTS:
48+ void triggeredSlot(QString const&, QString const&, QVariantMap const&);
49+
45private:
50private:
46 QList<PreviewWidgetModel*> m_previewWidgetModels;
51 QList<PreviewWidgetModel*> m_previewWidgetModels;
47 bool m_loaded;
52 bool m_loaded;
53+ Scope* m_scope;
48};
54};
49
55
50Q_DECLARE_METATYPE(PreviewModel*)
56Q_DECLARE_METATYPE(PreviewModel*)
tests/mocks/Unity/fake_previewstack.cpp+2−2
@@ -21,10 +21,10 @@
21#include "fake_previewmodel.h"
21#include "fake_previewmodel.h"
22#include "fake_scope.h"
22#include "fake_scope.h"
23
23
24−PreviewStack::PreviewStack(QObject* parent)
24+PreviewStack::PreviewStack(QObject* parent, Scope *scope)
25 : unity::shell::scopes::PreviewStackInterface(parent)
25 : unity::shell::scopes::PreviewStackInterface(parent)
26{
26{
27− m_previews << new PreviewModel(this);
27+ m_previews << new PreviewModel(this, scope);
28}
28}
29
29
30PreviewStack::~PreviewStack()
30PreviewStack::~PreviewStack()
tests/mocks/Unity/fake_previewstack.h+3−1
@@ -25,12 +25,14 @@
25
25
26class PreviewModel;
26class PreviewModel;
27
27
28+class Scope;
29+
28class PreviewStack : public unity::shell::scopes::PreviewStackInterface
30class PreviewStack : public unity::shell::scopes::PreviewStackInterface
29{
31{
30 Q_OBJECT
32 Q_OBJECT
31
33
32public:
34public:
33− explicit PreviewStack(QObject* parent = 0);
35+ explicit PreviewStack(QObject* parent = 0, Scope* scope = 0);
34 virtual ~PreviewStack();
36 virtual ~PreviewStack();
35
37
36 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
38 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
tests/mocks/Unity/fake_previewwidgetmodel.cpp+11
@@ -53,6 +53,17 @@ void PreviewWidgetModel::populateWidgets()
53 PreviewData* preview_data = new PreviewData(QString("widget-%1").arg(i), QString("text"), attributes);
53 PreviewData* preview_data = new PreviewData(QString("widget-%1").arg(i), QString("text"), attributes);
54 m_previewWidgets.append(QSharedPointer<PreviewData>(preview_data));
54 m_previewWidgets.append(QSharedPointer<PreviewData>(preview_data));
55 }
55 }
56+
57+ QVariantMap attributes;
58+ QVariantMap buttonData;
59+ buttonData["label"] = "Button";
60+ buttonData["id"] = "open_click";
61+ QVariantList buttons;
62+ buttons << buttonData << buttonData << buttonData;
63+ attributes["actions"] = QVariant::fromValue(buttons);
64+ PreviewData* preview_data = new PreviewData(QString("widget-21"), QString("actions"), attributes);
65+ m_previewWidgets.append(QSharedPointer<PreviewData>(preview_data));
66+
56 endResetModel();
67 endResetModel();
57
68
58}
69}
tests/mocks/Unity/fake_scope.cpp+5−5
@@ -185,6 +185,7 @@ void Scope::activate(QVariant const& result)
185 if (result.toString() == "Result.2.2") {
185 if (result.toString() == "Result.2.2") {
186 Scopes *scopes = dynamic_cast<Scopes*>(parent());
186 Scopes *scopes = dynamic_cast<Scopes*>(parent());
187 m_openScope = scopes->getScopeFromAll("MockScope9");
187 m_openScope = scopes->getScopeFromAll("MockScope9");
188+ scopes->addTempScope(m_openScope);
188 Q_EMIT openScope(m_openScope);
189 Q_EMIT openScope(m_openScope);
189 }
190 }
190}
191}
@@ -198,7 +199,7 @@ PreviewStack* Scope::preview(QVariant const& result)
198 } else {
199 } else {
199 // This probably leaks, do we don't care
200 // This probably leaks, do we don't care
200 // it's a test after all
201 // it's a test after all
201− return new PreviewStack;
202+ return new PreviewStack(this, this);
202 }
203 }
203}
204}
204
205
@@ -208,11 +209,10 @@ void Scope::cancelActivation()
208
209
209void Scope::closeScope(unity::shell::scopes::ScopeInterface* scope)
210void Scope::closeScope(unity::shell::scopes::ScopeInterface* scope)
210{
211{
211− if (scope != m_openScope) {
212+ Scopes *scopes = dynamic_cast<Scopes*>(parent());
212− qDebug() << scope << m_openScope;
213+ if (scopes) {
213− qFatal("Scope::closeScope got wrong scope in closeScope");
214+ return scopes->closeScope(scope);
214 }
215 }
215− m_openScope = nullptr;
216}
216}
217
217
218QString Scope::currentNavigationId() const
218QString Scope::currentNavigationId() const
tests/mocks/Unity/fake_scopes.cpp+12
@@ -23,6 +23,7 @@
23// TODO: Implement remaining pieces, like Categories (i.e. LensView now gives warnings)
23// TODO: Implement remaining pieces, like Categories (i.e. LensView now gives warnings)
24
24
25// Qt
25// Qt
26+#include <QDebug>
26#include <QTimer>
27#include <QTimer>
27
28
28Scopes::Scopes(QObject *parent)
29Scopes::Scopes(QObject *parent)
@@ -177,6 +178,17 @@ unity::shell::scopes::ScopeInterface* Scopes::overviewScope() const
177 return m_scopesOverview;
178 return m_scopesOverview;
178}
179}
179
180
181+void Scopes::addTempScope(unity::shell::scopes::ScopeInterface* scope)
182+{
183+ m_tempScopes.insert(scope);
184+}
185+
186+void Scopes::closeScope(unity::shell::scopes::ScopeInterface* scope)
187+{
188+ Q_ASSERT(m_tempScopes.contains(scope));
189+ m_tempScopes.remove(scope);
190+}
191+
180void Scopes::setFavorite(const QString& scopeId, bool favorite)
192void Scopes::setFavorite(const QString& scopeId, bool favorite)
181{
193{
182 if (favorite) {
194 if (favorite) {
tests/mocks/Unity/fake_scopes.h+5
@@ -26,6 +26,7 @@
26
26
27// Qt
27// Qt
28#include <QList>
28#include <QList>
29+#include <QSet>
29#include <QTimer>
30#include <QTimer>
30
31
31class ScopesOverview;
32class ScopesOverview;
@@ -59,6 +60,9 @@ public:
59 Q_INVOKABLE void setFavorite(const QString& scopeId, bool favorite) override;
60 Q_INVOKABLE void setFavorite(const QString& scopeId, bool favorite) override;
60 Q_INVOKABLE void moveFavoriteTo(const QString& scopeId, int index) override;
61 Q_INVOKABLE void moveFavoriteTo(const QString& scopeId, int index) override;
61
62
63+ void addTempScope(unity::shell::scopes::ScopeInterface* scope);
64+ Q_INVOKABLE void closeScope(unity::shell::scopes::ScopeInterface* scope) override;
65+
62 // This is used as part of implementation of the other C++ code, not API
66 // This is used as part of implementation of the other C++ code, not API
63 QList<Scope*> favScopes() const;
67 QList<Scope*> favScopes() const;
64 QList<Scope*> nonFavScopes() const;
68 QList<Scope*> nonFavScopes() const;
@@ -70,6 +74,7 @@ private Q_SLOTS:
70private:
74private:
71 QList<Scope*> m_scopes; // the favorite ones
75 QList<Scope*> m_scopes; // the favorite ones
72 QList<Scope*> m_allScopes;
76 QList<Scope*> m_allScopes;
77+ QSet<unity::shell::scopes::ScopeInterface*> m_tempScopes;
73 ScopesOverview *m_scopesOverview;
78 ScopesOverview *m_scopesOverview;
74 bool m_loaded;
79 bool m_loaded;
75 QTimer timer;
80 QTimer timer;
tests/mocks/Unity/fake_scopesoverview.cpp+1
@@ -48,6 +48,7 @@ Q_INVOKABLE void ScopesOverview::activate(QVariant const& result)
48 Q_EMIT gotoScope(result.toString());
48 Q_EMIT gotoScope(result.toString());
49 } else {
49 } else {
50 m_openScope = scopes->getScopeFromAll(result.toString());
50 m_openScope = scopes->getScopeFromAll(result.toString());
51+ scopes->addTempScope(m_openScope);
51 Q_EMIT openScope(m_openScope);
52 Q_EMIT openScope(m_openScope);
52 }
53 }
53}
54}
tests/mocks/libusermetrics/UserMetrics.cpp+8
@@ -289,6 +289,7 @@ void UserMetricsPrivate::generateFakeData()
289 new UserMetricsData("<b>52km</b> travelled", first, firstMonth,
289 new UserMetricsData("<b>52km</b> travelled", first, firstMonth,
290 ninth, secondMonth, this));
290 ninth, secondMonth, this));
291 m_fakeData.insert("single", data);
291 m_fakeData.insert("single", data);
292+ m_fakeData.insert("has-pin", data);
292 }
293 }
293
294
294 {
295 {
@@ -304,12 +305,14 @@ void UserMetricsPrivate::generateFakeData()
304 new UserMetricsData("<b>33</b> messages today", second,
305 new UserMetricsData("<b>33</b> messages today", second,
305 firstMonth, eighth, secondMonth, this));
306 firstMonth, eighth, secondMonth, this));
306 m_fakeData.insert("single", data);
307 m_fakeData.insert("single", data);
308+ m_fakeData.insert("has-pin", data);
307 }
309 }
308
310
309 {
311 {
310 QVariantList firstMonth;
312 QVariantList firstMonth;
311 while (firstMonth.size() < 17)
313 while (firstMonth.size() < 17)
312 firstMonth.push_back(QVariant(rand()));
314 firstMonth.push_back(QVariant(rand()));
315+ firstMonth[8] = QVariant(1.0); // oversized 9th day, to test clipping
313 while (firstMonth.size() < 31)
316 while (firstMonth.size() < 31)
314 firstMonth.push_back(QVariant());
317 firstMonth.push_back(QVariant());
315 QVariantList secondMonth;
318 QVariantList secondMonth;
@@ -319,6 +322,7 @@ void UserMetricsPrivate::generateFakeData()
319 new UserMetricsData("<b>19</b> minutes talk time", eighth,
322 new UserMetricsData("<b>19</b> minutes talk time", eighth,
320 firstMonth, second, secondMonth, this));
323 firstMonth, second, secondMonth, this));
321 m_fakeData.insert("single", data);
324 m_fakeData.insert("single", data);
325+ m_fakeData.insert("has-pin", data);
322 // Also use same data for some tablet users
326 // Also use same data for some tablet users
323 m_fakeData.insert("has-password", data);
327 m_fakeData.insert("has-password", data);
324 m_fakeData.insert("no-password", data);
328 m_fakeData.insert("no-password", data);
@@ -386,6 +390,10 @@ void UserMetricsPrivate::nextFakeData()
386 if (m_dataIndex == m_fakeData.constEnd() || m_dataIndex.key() != m_username)
390 if (m_dataIndex == m_fakeData.constEnd() || m_dataIndex.key() != m_username)
387 {
391 {
388 m_dataIndex = m_fakeData.constFind(m_username);
392 m_dataIndex = m_fakeData.constFind(m_username);
393+ if (m_dataIndex == m_fakeData.constEnd())
394+ {
395+ m_dataIndex = m_fakeData.constFind("");
396+ }
389 }
397 }
390
398
391 loadFakeData();
399 loadFakeData();
tests/plugins/CMakeLists.txt+1
@@ -1,4 +1,5 @@
1add_subdirectory(AccountsService)
1add_subdirectory(AccountsService)
2+add_subdirectory(Greeter)
2add_subdirectory(LightDM)
3add_subdirectory(LightDM)
3add_subdirectory(Dash)
4add_subdirectory(Dash)
4add_subdirectory(Ubuntu)
5add_subdirectory(Ubuntu)
tests/plugins/Greeter/CMakeLists.txtnew file+1
@@ -0,0 +1 @@
1+add_subdirectory(Unity)
tests/plugins/Greeter/Unity/CMakeLists.txtnew file+1
@@ -0,0 +1 @@
1+add_subdirectory(Launcher)
tests/plugins/Greeter/Unity/Launcher/AccountsServiceDBusAdaptor.cppnew file+48
@@ -0,0 +1,48 @@
1+/*
2+ * Copyright (C) 2014-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 "AccountsServiceDBusAdaptor.h"
18+#include <QDebug>
19+#include <QDBusArgument>
20+#include <QDBusMessage>
21+#include <QDBusMetaType>
22+
23+AccountsServiceDBusAdaptor::AccountsServiceDBusAdaptor(QObject* parent)
24+ : QObject(parent)
25+{
26+ qDBusRegisterMetaType<QList<QVariantMap>>();
27+}
28+
29+QVariant AccountsServiceDBusAdaptor::getUserProperty(const QString &user, const QString &interface, const QString &property)
30+{
31+ Q_UNUSED(interface)
32+ Q_UNUSED(property) // We only fake one property here (LauncherItems)
33+
34+ QDBusMessage msg;
35+ QVariant v = QVariant::fromValue(mockProperties.value(user));
36+ QDBusVariant dv(v);
37+ QVariant packed = QVariant::fromValue(dv);
38+ msg << packed;
39+
40+ return packed.value<QDBusArgument>().asVariant();
41+}
42+
43+void AccountsServiceDBusAdaptor::simulatePropertyChange(const QString &user, const QString &property, const QVariant &value)
44+{
45+ Q_ASSERT(property == "LauncherItems");
46+ mockProperties[user] = value.value<QList<QVariantMap>>();
47+ Q_EMIT propertiesChanged(user, "com.canonical.unity.AccountsService", QStringList() << property);
48+}
tests/plugins/Greeter/Unity/Launcher/AccountsServiceDBusAdaptor.hnew file+53
@@ -0,0 +1,53 @@
1+/*
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
18+#define UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
19+
20+#include <QMap>
21+#include <QObject>
22+#include <QString>
23+#include <QHash>
24+#include <QDBusArgument>
25+
26+extern QHash<QString, QList<QVariantMap>> mockProperties;
27+
28+class AccountsServiceDBusAdaptor: public QObject
29+{
30+ Q_OBJECT
31+
32+public:
33+ explicit AccountsServiceDBusAdaptor(QObject *parent = 0);
34+
35+ Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &interface, const QString &property);
36+ template <typename T>
37+ inline T getUserProperty(const QString &user, const QString &interface, const QString &property) {
38+ Q_UNUSED(interface)
39+ Q_ASSERT(property == "LauncherItems");
40+ T ret = mockProperties[user];
41+ return ret;
42+ }
43+
44+Q_SIGNALS:
45+ void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
46+
47+private:
48+ void simulatePropertyChange(const QString &user, const QString &property, const QVariant &value);
49+
50+ friend class LauncherModelASTest;
51+};
52+
53+#endif
tests/plugins/Greeter/Unity/Launcher/CMakeLists.txtnew file+56
@@ -0,0 +1,56 @@
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
2+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
3+
4+include_directories(
5+ ${CMAKE_CURRENT_SOURCE_DIR}
6+ ${CMAKE_CURRENT_BINARY_DIR}
7+ ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher
8+ ${libunity8-private_SOURCE_DIR}
9+ )
10+
11+add_definitions(-DSM_BUSNAME=sessionBus)
12+add_definitions(-DSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}")
13+add_definitions(-DLAUNCHER_TESTING)
14+
15+### LauncherModelASTest
16+set(testModelCommand dbus-test-runner --task ${CMAKE_CURRENT_BINARY_DIR}/launchermodelastestExec
17+ --parameter -o --parameter ${CMAKE_BINARY_DIR}/launchermodeltest.xml,xunitxml
18+ --parameter -o --parameter -,txt)
19+add_test(NAME launchermodelastest COMMAND ${testModelCommand})
20+add_custom_target(launchermodelastest ${testModelCommand})
21+add_executable(launchermodelastestExec
22+ launchermodelastest.cpp
23+ AccountsServiceDBusAdaptor.cpp
24+ ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher/launchermodelas.cpp
25+ ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher/launcheritem.cpp
26+ ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher/quicklistmodel.cpp
27+ ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher/quicklistentry.cpp
28+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
29+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
30+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
31+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
32+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
33+ )
34+target_link_libraries(launchermodelastestExec
35+ unity8-private
36+ ${GSETTINGS_QT_LDFLAGS}
37+ )
38+qt5_use_modules(launchermodelastestExec Test Core DBus Gui)
39+
40+# copy .desktop files into build directory for shadow builds
41+file(GLOB DESKTOP_FILES *.desktop)
42+
43+foreach(DESKTOP_FILE ${DESKTOP_FILES})
44+ file(COPY "${DESKTOP_FILE}"
45+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
46+ )
47+endforeach()
48+
49+# copy .svg files into build directory for shadow builds
50+file(GLOB DESKTOP_FILES *.svg)
51+
52+foreach(DESKTOP_FILE ${DESKTOP_FILES})
53+ file(COPY "${DESKTOP_FILE}"
54+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
55+ )
56+endforeach()
tests/plugins/Greeter/Unity/Launcher/launchermodelastest.cppnew file+169
@@ -0,0 +1,169 @@
1+/*
2+ * Copyright 2014-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+// unity-api
18+#include <unity/shell/launcher/LauncherModelInterface.h>
19+#include <unity/shell/application/ApplicationInfoInterface.h>
20+
21+#include "launcheritem.h"
22+#include "launchermodelas.h"
23+#include "AccountsServiceDBusAdaptor.h"
24+
25+#include <QtTest>
26+
27+QHash<QString, QList<QVariantMap>> mockProperties;
28+
29+class LauncherModelASTest : public QObject
30+{
31+ Q_OBJECT
32+
33+private:
34+ LauncherModel *launcherModel;
35+
36+private Q_SLOTS:
37+
38+ void init() {
39+ // Prepare some fake list
40+ QList<QVariantMap> list;
41+ QVariantMap item;
42+ item.insert("id", "appId1");
43+ item.insert("name", "Item 1");
44+ item.insert("icon", "fake.svg");
45+ item.insert("count", 0);
46+ item.insert("countVisible", false);
47+ item.insert("pinned", true);
48+ list.append(item);
49+ item["id"] = "appId2";
50+ item["name"] = "Item 2";
51+ list.append(item);
52+ mockProperties["user1"] = list;
53+ }
54+
55+ bool isInSync(LauncherModel *model, const QList<QVariantMap> &properties) {
56+ QList<QVariantMap> list;
57+ // Strip unpinned if onlyPinned is set
58+ Q_FOREACH (const QVariantMap &map, properties) {
59+ if (!model->onlyPinned() || map.value("pinned").toBool()) {
60+ list << map;
61+ }
62+ }
63+
64+ bool inSync = model->rowCount() == list.count();
65+ for (int i = 0; inSync && i < model->rowCount(); i++) {
66+ inSync &= model->get(i)->appId() == list.at(i).value("id").toString();
67+ inSync &= model->get(i)->name() == list.at(i).value("name").toString();
68+ inSync &= model->get(i)->icon() == list.at(i).value("icon").toString();
69+ inSync &= model->get(i)->count() == list.at(i).value("count").toInt();
70+ inSync &= model->get(i)->countVisible() == list.at(i).value("countVisible").toBool();
71+ }
72+ return inSync;
73+ }
74+
75+ void testLoadASOnStartup() {
76+ // Load up the model
77+ LauncherModel* model = new LauncherModel(this);
78+
79+ // We didn't set a user yet. model should be empty
80+ QCOMPARE(model->rowCount(), 0);
81+
82+ model->setUser("user1");
83+
84+ QCOMPARE(isInSync(model, mockProperties["user1"]), true);
85+ model->deleteLater();
86+ }
87+
88+ void testASChangedUpdatesModel_data() {
89+ QTest::addColumn<QString>("modelUser");
90+ QTest::addColumn<QString>("changedUser");
91+ QTest::addColumn<bool>("inSync");
92+
93+ QTest::newRow("this user changed") << "user1" << "user1" << true;
94+ QTest::newRow("other user changed") << "user1" << "user2" << false;
95+ }
96+
97+ void testASChangedUpdatesModel() {
98+ QFETCH(QString, modelUser);
99+ QFETCH(QString, changedUser);
100+ QFETCH(bool, inSync);
101+
102+ LauncherModel* model = new LauncherModel(this);
103+ model->setUser(modelUser);
104+
105+ int oldCount = mockProperties[modelUser].count();
106+ QCOMPARE(model->rowCount(), oldCount);
107+
108+ QList<QVariantMap> newList;
109+ QVariantMap newEntry;
110+ newEntry.insert("id", "newappId");
111+ newEntry.insert("name", "New app");
112+ newEntry.insert("icon", "some-icon.svg");
113+ newEntry.insert("count", 0);
114+ newEntry.insert("countVisible", false);
115+ newEntry.insert("pinned", true);
116+ newList.append(newEntry);
117+ model->m_accounts->simulatePropertyChange(changedUser, "LauncherItems", QVariant::fromValue(newList));
118+
119+ QCOMPARE(isInSync(model, mockProperties[modelUser]), true);
120+ QCOMPARE(isInSync(model, mockProperties[changedUser]), inSync);
121+
122+ model->deleteLater();
123+ }
124+
125+ void testUpdateCount() {
126+ LauncherModel* model = new LauncherModel(this);
127+ model->setUser("user1");
128+
129+ QCOMPARE(model->get(0)->countVisible(), false);
130+ QCOMPARE(model->get(0)->count(), 0);
131+
132+ QList<QVariantMap> newList = mockProperties["user1"];
133+ QVariantMap entry = newList.at(0);
134+ entry["countVisible"] = true;
135+ entry["count"] = 55;
136+ newList[0] = entry;
137+ model->m_accounts->simulatePropertyChange("user1", "LauncherItems", QVariant::fromValue(newList));
138+
139+ QCOMPARE(isInSync(model, mockProperties["user1"]), true);
140+
141+ model->deleteLater();
142+ }
143+
144+ void testOnlyPinned() {
145+ LauncherModel *model = new LauncherModel(this);
146+ model->setUser("user1");
147+ model->setOnlyPinned(true);
148+
149+ QCOMPARE(isInSync(model, mockProperties["user1"]), true);
150+
151+ // Let's unpin one item
152+ QList<QVariantMap> newList = mockProperties["user1"];
153+ QVariantMap entry = newList.at(0);
154+ entry["pinned"] = false;
155+ newList[0] = entry;
156+ model->m_accounts->simulatePropertyChange("user1", "LauncherItems", QVariant::fromValue(newList));
157+ QCOMPARE(isInSync(model, mockProperties["user1"]), true);
158+
159+ // Now toggle onlyPinned and make sure the model keeps up
160+ model->setOnlyPinned(false);
161+ QCOMPARE(isInSync(model, mockProperties["user1"]), true);
162+
163+ model->setOnlyPinned(true);
164+ QCOMPARE(isInSync(model, mockProperties["user1"]), true);
165+ }
166+};
167+
168+QTEST_GUILESS_MAIN(LauncherModelASTest)
169+#include "launchermodelastest.moc"
tests/plugins/LightDM/CMakeLists.txt+3−3
@@ -11,10 +11,10 @@ include_directories(
11 ${LIBLIGHTDM_INCLUDE_DIRS}
11 ${LIBLIGHTDM_INCLUDE_DIRS}
12 )
12 )
13
13
14−add_dependencies(GreeterDBusTestExec MockLightDM-full)
14+add_dependencies(GreeterDBusTestExec MockLightDM)
15−target_link_libraries(GreeterDBusTestExec -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full
15+target_link_libraries(GreeterDBusTestExec -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm
16 -llightdm-qt5-2)
16 -llightdm-qt5-2)
17
17
18add_definitions(-DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
18add_definitions(-DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
19
19
20−add_binary_qml_test(GreeterDBus "${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full" MockLightDM-full "QML2_IMPORT_PATH=${CMAKE_BINARY_DIR}/tests/mocks")
20+add_binary_qml_test(GreeterDBus "${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm" MockLightDM "QML2_IMPORT_PATH=${CMAKE_BINARY_DIR}/tests/mocks")
tests/plugins/LightDM/greeter.qml+5
@@ -19,4 +19,9 @@ import LightDM 0.1 as LightDM
19
19
20Item {
20Item {
21 property var greeter: LightDM.Greeter
21 property var greeter: LightDM.Greeter
22+ Binding {
23+ target: LightDM.Greeter
24+ property: "mockMode"
25+ value: "full"
26+ }
22}
27}
tests/plugins/Unity/Indicators/indicatorsmanagertest.cpp+30−5
@@ -50,7 +50,8 @@ private Q_SLOTS:
50 QVERIFY(!manager.isLoaded());
50 QVERIFY(!manager.isLoaded());
51 QCOMPARE(manager.indicators().count(), 0);
51 QCOMPARE(manager.indicators().count(), 0);
52
52
53− manager.load("test1");
53+ manager.setProfile("test1");
54+ manager.load();
54
55
55 QVERIFY(manager.isLoaded());
56 QVERIFY(manager.isLoaded());
56 QCOMPARE(manager.indicators().count(), 4);
57 QCOMPARE(manager.indicators().count(), 4);
@@ -67,7 +68,8 @@ private Q_SLOTS:
67 void testPluginInterfaceProfile1()
68 void testPluginInterfaceProfile1()
68 {
69 {
69 IndicatorsManager manager;
70 IndicatorsManager manager;
70− manager.load("test1");
71+ manager.setProfile("test1");
72+ manager.load();
71
73
72 Indicator::Ptr indicator = manager.indicator("indicator-fake1");
74 Indicator::Ptr indicator = manager.indicator("indicator-fake1");
73 QVERIFY(indicator ? true : false);
75 QVERIFY(indicator ? true : false);
@@ -90,7 +92,8 @@ private Q_SLOTS:
90 void testPluginInterfaceProfile2()
92 void testPluginInterfaceProfile2()
91 {
93 {
92 IndicatorsManager manager;
94 IndicatorsManager manager;
93− manager.load("test2");
95+ manager.setProfile("test2");
96+ manager.load();
94
97
95 Indicator::Ptr indicator = manager.indicator("indicator-fake1");
98 Indicator::Ptr indicator = manager.indicator("indicator-fake1");
96 QVERIFY(indicator ? true : false);
99 QVERIFY(indicator ? true : false);
@@ -107,13 +110,34 @@ private Q_SLOTS:
107 QCOMPARE(props["menuObjectPath"].toString(), QString("/com/canonical/indicator/fake1/test2"));
110 QCOMPARE(props["menuObjectPath"].toString(), QString("/com/canonical/indicator/fake1/test2"));
108 }
111 }
109
112
113+ /*
114+ * Test switching the indicator profile data
115+ */
116+ void testPluginInterfaceProfileSwitch()
117+ {
118+ IndicatorsManager manager;
119+ manager.setProfile("test1");
120+ manager.load();
121+
122+ Indicator::Ptr indicator = manager.indicator("indicator-fake1");
123+ QVERIFY(indicator ? true : false);
124+
125+ QVariantMap props = indicator->indicatorProperties().toMap();
126+ QCOMPARE(props["menuObjectPath"].toString(), QString("/com/canonical/indicator/fake1/test1"));
127+
128+ manager.setProfile("test2");
129+ props = indicator->indicatorProperties().toMap();
130+ QCOMPARE(props["menuObjectPath"].toString(), QString("/com/canonical/indicator/fake1/test2"));
131+ }
132+
110 /*
133 /*
111 * Test if a new plugin object is create for each different plugin
134 * Test if a new plugin object is create for each different plugin
112 */
135 */
113 void testPluginInstance()
136 void testPluginInstance()
114 {
137 {
115 IndicatorsManager manager;
138 IndicatorsManager manager;
116− manager.load("test2");
139+ manager.setProfile("test2");
140+ manager.load();
117
141
118 Indicator::Ptr i0 = manager.indicator("indicator-fake1");
142 Indicator::Ptr i0 = manager.indicator("indicator-fake1");
119 Indicator::Ptr i1 = manager.indicator("indicator-fake1");
143 Indicator::Ptr i1 = manager.indicator("indicator-fake1");
@@ -133,7 +157,8 @@ private Q_SLOTS:
133 void testPluginInitAndShutdown()
157 void testPluginInitAndShutdown()
134 {
158 {
135 IndicatorsManager manager;
159 IndicatorsManager manager;
136− manager.load("test1");
160+ manager.setProfile("test1");
161+ manager.load();
137
162
138 QWeakPointer<Indicator> wp0;
163 QWeakPointer<Indicator> wp0;
139 QWeakPointer<Indicator> wp1;
164 QWeakPointer<Indicator> wp1;
tests/plugins/Unity/Indicators/indicatorsmodeltest.cpp+6−3
@@ -47,10 +47,11 @@ private Q_SLOTS:
47 void testLoad()
47 void testLoad()
48 {
48 {
49 IndicatorsModel model;
49 IndicatorsModel model;
50+ model.setProfile("test1");
50
51
51 QCOMPARE(model.property("count").toInt(), 0);
52 QCOMPARE(model.property("count").toInt(), 0);
52
53
53− model.load("test1");
54+ model.load();
54
55
55 QCOMPARE(model.property("count").toInt(), 4);
56 QCOMPARE(model.property("count").toInt(), 4);
56
57
@@ -67,7 +68,8 @@ private Q_SLOTS:
67 {
68 {
68 // Priority order. (2, 1, 4, 3)
69 // Priority order. (2, 1, 4, 3)
69 IndicatorsModel model;
70 IndicatorsModel model;
70− model.load("test1");
71+ model.setProfile("test1");
72+ model.load();
71
73
72 // should be in order:
74 // should be in order:
73 // fake3, fake4, fake1, fake2
75 // fake3, fake4, fake1, fake2
@@ -104,7 +106,8 @@ private Q_SLOTS:
104 {
106 {
105 // Priority order. (2, 1, 4, 3)
107 // Priority order. (2, 1, 4, 3)
106 IndicatorsModel model;
108 IndicatorsModel model;
107− model.load("test2");
109+ model.setProfile("test2");
110+ model.load();
108
111
109 // should be in order:
112 // should be in order:
110 // fake3, fake4, fake1, fake2
113 // fake3, fake4, fake1, fake2
tests/plugins/Unity/Indicators/sharedunitymenumodeltest.cpp+2−2
@@ -61,7 +61,7 @@ private Q_SLOTS:
61 QCOMPARE(model1->model(), model2->model());
61 QCOMPARE(model1->model(), model2->model());
62 }
62 }
63
63
64− void testSharedOwnership()
64+ void testSavedData()
65 {
65 {
66 QSharedPointer<SharedUnityMenuModel> model1(createFullModel("test1"));
66 QSharedPointer<SharedUnityMenuModel> model1(createFullModel("test1"));
67 QSharedPointer<SharedUnityMenuModel> model2(createFullModel("test1"));
67 QSharedPointer<SharedUnityMenuModel> model2(createFullModel("test1"));
@@ -70,7 +70,7 @@ private Q_SLOTS:
70 model1.clear();
70 model1.clear();
71 QCOMPARE(UnityMenuModelCache::singleton()->contains("/com/canonical/test1"), true);
71 QCOMPARE(UnityMenuModelCache::singleton()->contains("/com/canonical/test1"), true);
72 model2.clear();
72 model2.clear();
73− QCOMPARE(UnityMenuModelCache::singleton()->contains("/com/canonical/test1"), false);
73+ QCOMPARE(UnityMenuModelCache::singleton()->contains("/com/canonical/test1"), true);
74 }
74 }
75
75
76 // Tests that changing cached model data does not change the model path of others
76 // Tests that changing cached model data does not change the model path of others
tests/plugins/Unity/Launcher/AccountsServiceDBusAdaptor.cppnew file+42
@@ -0,0 +1,42 @@
1+/*
2+ * Copyright (C) 2014-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 "AccountsServiceDBusAdaptor.h"
18+#include <QDebug>
19+
20+AccountsServiceDBusAdaptor::AccountsServiceDBusAdaptor(QObject* parent)
21+ : QObject(parent)
22+{
23+}
24+
25+QVariant AccountsServiceDBusAdaptor::getUserProperty(const QString &user, const QString &interface, const QString &property)
26+{
27+ Q_UNUSED(user)
28+ Q_UNUSED(interface)
29+ return m_properties.value(property);
30+}
31+
32+void AccountsServiceDBusAdaptor::setUserProperty(const QString &user, const QString &interface, const QString &property, const QVariant &value)
33+{
34+ Q_UNUSED(user)
35+ Q_UNUSED(interface)
36+ m_properties[property] = value;
37+}
38+
39+void AccountsServiceDBusAdaptor::setUserPropertyAsync(const QString &user, const QString &interface, const QString &property, const QVariant &value)
40+{
41+ setUserProperty(user, interface, property, value);
42+}
tests/plugins/Unity/Launcher/AccountsServiceDBusAdaptor.hnew file+47
@@ -0,0 +1,47 @@
1+/*
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
18+#define UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
19+
20+#include <QMap>
21+#include <QObject>
22+#include <QString>
23+#include <QHash>
24+
25+class AccountsServiceDBusAdaptor: public QObject
26+{
27+ Q_OBJECT
28+
29+public:
30+ explicit AccountsServiceDBusAdaptor(QObject *parent = 0);
31+
32+ Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &interface, const QString &property);
33+ Q_INVOKABLE void setUserProperty(const QString &user, const QString &interface, const QString &property, const QVariant &value);
34+ Q_INVOKABLE void setUserPropertyAsync(const QString &user, const QString &interface, const QString &property, const QVariant &value);
35+
36+Q_SIGNALS:
37+ void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
38+ void maybeChanged(const QString &user); // Standard properties might have changed
39+
40+private:
41+ void simulatePropertyChange(const QString &property, const QVariant &value);
42+
43+private:
44+ QHash<QString, QVariant> m_properties;
45+};
46+
47+#endif
tests/plugins/Unity/Launcher/CMakeLists.txt+3−1
@@ -1,5 +1,5 @@
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=5)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
3pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
3pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
4
4
5include_directories(
5include_directories(
@@ -23,7 +23,9 @@ add_custom_target(launchermodeltest ${testModelCommand})
23add_executable(launchermodeltestExec
23add_executable(launchermodeltestExec
24 launchermodeltest.cpp
24 launchermodeltest.cpp
25 gsettings.cpp
25 gsettings.cpp
26+ AccountsServiceDBusAdaptor.cpp
26 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launchermodel.cpp
27 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launchermodel.cpp
28+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/asadapter.cpp
27 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launcheritem.cpp
29 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launcheritem.cpp
28 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistmodel.cpp
30 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistmodel.cpp
29 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/dbusinterface.cpp
31 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/dbusinterface.cpp
tests/plugins/Unity/Launcher/launchermodeltest.cpp+65−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
5 * it under the terms of the GNU Lesser General Public License as published by
@@ -25,6 +25,8 @@
25#include "launchermodel.h"
25#include "launchermodel.h"
26#include "dbusinterface.h"
26#include "dbusinterface.h"
27#include "gsettings.h"
27#include "gsettings.h"
28+#include "asadapter.h"
29+#include "AccountsServiceDBusAdaptor.h"
28
30
29#include <QtTest>
31#include <QtTest>
30#include <QDBusInterface>
32#include <QDBusInterface>
@@ -129,6 +131,11 @@ private:
129 LauncherModel *launcherModel;
131 LauncherModel *launcherModel;
130 MockAppManager *appManager;
132 MockAppManager *appManager;
131
133
134+ QList<QVariantMap> getASConfig() {
135+ AccountsServiceDBusAdaptor *as = launcherModel->m_asAdapter->m_accounts;
136+ return as->getUserProperty("", "", "LauncherItems").value<QList<QVariantMap>>();
137+ }
138+
132private Q_SLOTS:
139private Q_SLOTS:
133
140
134 void initTestCase() {
141 void initTestCase() {
@@ -467,6 +474,63 @@ private Q_SLOTS:
467 QCOMPARE(launcherModel->get(1)->appId(), QString("abs-icon"));
474 QCOMPARE(launcherModel->get(1)->appId(), QString("abs-icon"));
468 QCOMPARE(spy.count(), 2);
475 QCOMPARE(spy.count(), 2);
469 }
476 }
477+
478+ void testAddSyncsToAS() {
479+ // Make sure launcher and AS are in sync when we start the test
480+ QCOMPARE(launcherModel->rowCount(), getASConfig().count());
481+
482+ int oldCount = launcherModel->rowCount();
483+ appManager->addApplication(new MockApp("rel-icon"));
484+ QCOMPARE(launcherModel->rowCount(), oldCount + 1);
485+ QCOMPARE(launcherModel->rowCount(), getASConfig().count());
486+ }
487+
488+ void testRemoveSyncsToAS() {
489+ // Make sure launcher and AS are in sync when we start the test
490+ QCOMPARE(launcherModel->rowCount(), getASConfig().count());
491+
492+ int oldCount = launcherModel->rowCount();
493+ appManager->stopApplication("abs-icon");
494+ QCOMPARE(launcherModel->rowCount(), oldCount - 1);
495+ QCOMPARE(launcherModel->rowCount(), getASConfig().count());
496+ }
497+
498+ void testMoveSyncsToAS() {
499+ // Make sure launcher and AS are in sync when we start the test
500+ QCOMPARE(launcherModel->rowCount(), getASConfig().count());
501+
502+ for (int i = 0; i < launcherModel->rowCount(); i++) {
503+ QString launcherAppId = launcherModel->get(i)->appId();
504+ QString asAppId = getASConfig().at(i).value("id").toString();
505+ QCOMPARE(launcherAppId, asAppId);
506+ }
507+
508+ launcherModel->move(0, 1);
509+
510+ for (int i = 0; i < launcherModel->rowCount(); i++) {
511+ QString launcherAppId = launcherModel->get(i)->appId();
512+ QString asAppId = getASConfig().at(i).value("id").toString();
513+ QCOMPARE(launcherAppId, asAppId);
514+ }
515+ }
516+
517+ void testCountChangeSyncsToAS() {
518+ // Find the index of the abs-icon app
519+ int index = launcherModel->findApplication("abs-icon");
520+
521+ // Make sure it's invisible and 0 at the beginning
522+ QCOMPARE(getASConfig().at(index).value("countVisible").toBool(), false);
523+ QCOMPARE(getASConfig().at(index).value("count").toInt(), 0);
524+
525+ // Change the count of the abs-icon app through D-Bus
526+ QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "org.freedesktop.DBus.Properties");
527+ interface.call("Set", "com.canonical.Unity.Launcher.Item", "count", QVariant::fromValue(QDBusVariant(55)));
528+ interface.call("Set", "com.canonical.Unity.Launcher.Item", "countVisible", QVariant::fromValue(QDBusVariant(true)));
529+
530+ // Make sure it changed to visible and 55
531+ QCOMPARE(getASConfig().at(index).value("countVisible").toBool(), true);
532+ QCOMPARE(getASConfig().at(index).value("count").toInt(), 55);
533+ }
470};
534};
471
535
472QTEST_GUILESS_MAIN(LauncherModelTest)
536QTEST_GUILESS_MAIN(LauncherModelTest)
tests/qmltests/CMakeLists.txt+21−20
@@ -17,16 +17,15 @@ add_qml_test(Components PhysicalKeysMapper)
17
17
18set(qmltest_DEFAULT_TARGETS qmluitests)
18set(qmltest_DEFAULT_TARGETS qmluitests)
19set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
19set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
20−set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
20+set(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(. Shell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single:${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
22+add_qml_test(. Shell)
23−add_qml_test(. ShellWithPin ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single-pin:${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
23+add_qml_test(. ShellWithPin)
24−add_qml_test(. TabletShell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full:${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
24+add_qml_test(. TabletShell)
25add_qml_test(Components Background)
25add_qml_test(Components Background)
26add_qml_test(Components Carousel)
26add_qml_test(Components Carousel)
27add_qml_test(Components Dialogs)
27add_qml_test(Components Dialogs)
28add_qml_test(Components DraggingArea)
28add_qml_test(Components DraggingArea)
29−add_qml_test(Components EdgeDemoOverlay)
30add_qml_test(Components LazyImage)
29add_qml_test(Components LazyImage)
31add_qml_test(Components Lockscreen)
30add_qml_test(Components Lockscreen)
32add_qml_test(Components Rating)
31add_qml_test(Components Rating)
@@ -62,10 +61,10 @@ add_qml_test(Dash/ScopeSettings ScopeSettingList)
62add_qml_test(Dash/ScopeSettings ScopeSettingNumber)
61add_qml_test(Dash/ScopeSettings ScopeSettingNumber)
63add_qml_test(Dash/ScopeSettings ScopeSettingString)
62add_qml_test(Dash/ScopeSettings ScopeSettingString)
64add_qml_test(Dash/ScopeSettings ScopeSettingsWidgetFactory)
63add_qml_test(Dash/ScopeSettings ScopeSettingsWidgetFactory)
65−add_qml_test(Greeter MultiGreeter ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
64+add_qml_test(Greeter MultiGreeter)
66−add_qml_test(Greeter SingleGreeter ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
65+add_qml_test(Greeter SingleGreeter)
67−add_qml_test(Greeter Infographics ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
66+add_qml_test(Greeter Infographics)
68−add_qml_test(Greeter Clock ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
67+add_qml_test(Greeter Clock)
69add_qml_test(Launcher Launcher)
68add_qml_test(Launcher Launcher)
70add_qml_test(Notifications Notifications)
69add_qml_test(Notifications Notifications)
71add_qml_test(Notifications VisualSnapDecisionsQueue)
70add_qml_test(Notifications VisualSnapDecisionsQueue)
@@ -73,22 +72,24 @@ add_qml_test(Notifications OptionToggle)
73add_qml_test(Notifications SwipeToAct)
72add_qml_test(Notifications SwipeToAct)
74add_qml_test(Panel ActiveCallHint)
73add_qml_test(Panel ActiveCallHint)
75add_qml_test(Panel IndicatorItem)
74add_qml_test(Panel IndicatorItem)
76−add_qml_test(Panel IndicatorItemRow ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
75+add_qml_test(Panel IndicatorItemRow)
77−add_qml_test(Panel IndicatorPage ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
76+add_qml_test(Panel IndicatorPage)
78−add_qml_test(Panel IndicatorsBar ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
77+add_qml_test(Panel IndicatorsBar)
79−add_qml_test(Panel IndicatorsMenu ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
78+add_qml_test(Panel IndicatorsMenu)
80−add_qml_test(Panel MenuContent ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
79+add_qml_test(Panel MenuContent)
81−add_qml_test(Panel Panel ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
80+add_qml_test(Panel Panel)
82add_qml_test(Panel SearchIndicator)
81add_qml_test(Panel SearchIndicator)
83add_qml_test(Panel/Indicators IndicatorsLight ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
82add_qml_test(Panel/Indicators IndicatorsLight ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
84# These MenuItemFactory tests need the test/mocks/ to come before plugins/
83# These MenuItemFactory tests need the test/mocks/ to come before plugins/
85add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
84add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
86add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
85add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
87−add_qml_test(Stages ApplicationWindow ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
86+add_qml_test(Stages ApplicationWindow)
88−add_qml_test(Stages PhoneStage ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
87+add_qml_test(Stages PhoneStage ENVIRONMENT)
89−add_qml_test(Stages SpreadDelegate ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
88+add_qml_test(Stages SpreadDelegate ENVIRONMENT)
90−add_qml_test(Stages SurfaceContainer ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
89+add_qml_test(Stages SurfaceContainer ENVIRONMENT)
91−add_qml_test(Stages SessionContainer ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
90+add_qml_test(Stages SessionContainer ENVIRONMENT)
91+add_qml_test(Stages TabletStage)
92add_qml_test(Stages WindowMoveResizeArea)
92add_qml_test(Stages WindowMoveResizeArea)
93add_qml_test(Stages Splash)
93add_qml_test(Stages Splash)
94+add_qml_test(Tutorial Tutorial)
94add_qml_test(Wizard Wizard)
95add_qml_test(Wizard Wizard)
tests/qmltests/Components/tst_EdgeDemoOverlay.qmldeleted−123
@@ -1,123 +0,0 @@
1−/*
2− * Copyright 2013 Canonical Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import QtTest 1.0
19−import Unity.Test 0.1 as UT
20−import "../../../qml/Components"
21−
22−Item {
23− id: root
24− width: boxWidth * 3
25− height: boxHeight * 2
26−
27− property int boxWidth: 250
28− property int boxHeight: 250
29−
30− EdgeDemoOverlay {
31− id: top
32− edge: "top"
33− title: "Top"
34− text: "Displayed on top left"
35− anchors.left: parent.left
36− anchors.top: parent.top
37− width: boxWidth
38− height: boxHeight
39− }
40−
41− EdgeDemoOverlay {
42− id: right
43− edge: "right"
44− title: "Right"
45− text: "Displayed on top right"
46− anchors.right: parent.right
47− anchors.top: parent.top
48− width: boxWidth
49− height: boxHeight
50− }
51−
52− EdgeDemoOverlay {
53− id: left
54− edge: "left"
55− title: "Left"
56− text: "Displayed on bottom right"
57− anchors.right: parent.right
58− anchors.bottom: parent.bottom
59− width: boxWidth
60− height: boxHeight
61− available: false
62− }
63−
64− EdgeDemoOverlay {
65− id: bottom
66− edge: "bottom"
67− title: "Bottom"
68− text: "Displayed on bottom left"
69− anchors.left: parent.left
70− anchors.bottom: parent.bottom
71− width: boxWidth
72− height: boxHeight
73− }
74−
75− EdgeDemoOverlay {
76− id: none
77− edge: "none"
78− title: "None"
79− text: "Displayed on top middle"
80− anchors.horizontalCenter: parent.horizontalCenter
81− anchors.top: parent.top
82− width: boxWidth
83− height: boxHeight
84− }
85−
86− SignalSpy {
87− id: signalSpy
88− }
89−
90− UT.UnityTestCase {
91− name: "EdgeDemoOverlay"
92− when: windowShown
93−
94− function test_animations() {
95− compare(right.running, true)
96−
97− compare(left.running, false)
98− left.available = true
99− compare(left.running, true)
100− }
101−
102− function test_skip() {
103− signalSpy.target = bottom
104− signalSpy.signalName = "skip"
105− signalSpy.clear()
106− var bottomSkip = findChild(bottom, "skipLabel")
107− mousePress(bottomSkip, 1, 1)
108− mouseRelease(bottomSkip, 1, 1)
109− signalSpy.wait()
110− compare(bottom.available, false)
111−
112− // Test that the 'none' edge skips anywhere
113− signalSpy.target = none
114− signalSpy.clear()
115− var backgroundShade = findChild(none, "backgroundShadeMouseArea")
116− tryCompare(backgroundShade, "enabled", true)
117− mousePress(backgroundShade, 1, 1)
118− mouseRelease(backgroundShade, 1, 1)
119− signalSpy.wait()
120− compare(none.available, false)
121− }
122− }
123−}
tests/qmltests/Dash/Previews/tst_Preview.qml+31
@@ -54,6 +54,11 @@ Rectangle {
54 name: "Preview"
54 name: "Preview"
55 when: windowShown
55 when: windowShown
56
56
57+ function init() {
58+ var widget = findChild(preview, "previewListRow0");
59+ widget.positionViewAtBeginning();
60+ }
61+
57 function test_triggered() {
62 function test_triggered() {
58 waitForRendering(preview);
63 waitForRendering(preview);
59 var widget = findChild(preview, "widget-3");
64 var widget = findChild(preview, "widget-3");
@@ -102,5 +107,31 @@ Rectangle {
102 return bottomLeft.y <= preview.height
107 return bottomLeft.y <= preview.height
103 }, true);
108 }, true);
104 }
109 }
110+
111+ function test_comboEnsureVisible() {
112+ waitForRendering(preview);
113+
114+ // Scroll down
115+ var previewListRow0 = findChild(preview, "previewListRow0");
116+ touchFlick(preview, preview.width / 2, units.gu(20), preview.width / 2, units.gu(1));
117+ tryCompare(previewListRow0, "atYEnd", true);
118+ tryCompare(previewListRow0, "moving", false);
119+
120+ // Click on the combo
121+ var widget = findChild(preview, "widget-21");
122+ var initialWidgetHeight = widget.height;
123+ var moreLessButton = findChild(widget, "moreLessButton");
124+ mouseClick(moreLessButton);
125+
126+ // Make sure the combo is growing
127+ tryCompareFunction(function () { return widget.height > 2 * initialWidgetHeight; }, true);
128+
129+ // Wait for the combo to stop growing
130+ tryCompareFunction(function () { var currentWidgetHeight = widget.height; wait(200); return currentWidgetHeight === widget.height;}, true);
131+
132+ // Make sure the combo bottom is on the viewport
133+ var bottomLeft = preview.mapFromItem(widget, 0, widget.height);
134+ verify(bottomLeft.y <= preview.height);
135+ }
105 }
136 }
106}
137}
tests/qmltests/Dash/tst_Card.qml+7−1
@@ -60,7 +60,7 @@ Rectangle {
60 "layout": { "components": Helpers.update(JSON.parse(Helpers.fullMapping), { "art": { "aspect-ratio": 0.7 } }) }
60 "layout": { "components": Helpers.update(JSON.parse(Helpers.fullMapping), { "art": { "aspect-ratio": 0.7 } }) }
61 },
61 },
62 {
62 {
63− "name": "Art, header, summary - horizontal",
63+ "name": "Art, header, summary, background - horizontal",
64 "layout": { "template": { "card-layout": "horizontal", "card-background": { "type": "gradient", "elements": ["grey", "white"] } },
64 "layout": { "template": { "card-layout": "horizontal", "card-background": { "type": "gradient", "elements": ["grey", "white"] } },
65 "components": JSON.parse(Helpers.fullMapping) }
65 "components": JSON.parse(Helpers.fullMapping) }
66 },
66 },
@@ -101,6 +101,11 @@ Rectangle {
101 "layout": { "template": { "overlay": true },
101 "layout": { "template": { "overlay": true },
102 "components": { "art": "art", "title": "title" } }
102 "components": { "art": "art", "title": "title" } }
103 },
103 },
104+ {
105+ "name": "Art, header, summary - horizontal",
106+ "layout": { "template": { "card-layout": "horizontal" },
107+ "components": JSON.parse(Helpers.fullMapping) }
108+ },
104 ]
109 ]
105
110
106 CardTool {
111 CardTool {
@@ -415,6 +420,7 @@ Rectangle {
415 background: Qt.resolvedUrl("artwork/checkers.png"), index: 10 },
420 background: Qt.resolvedUrl("artwork/checkers.png"), index: 10 },
416 { tag: "Gradient background", visible: true, color: "#808080", gradientColor: "#ffffff", index: 11 },
421 { tag: "Gradient background", visible: true, color: "#808080", gradientColor: "#ffffff", index: 11 },
417 { tag: "Image background", visible: true, image: Qt.resolvedUrl("artwork/checkers.png"), index: 12 },
422 { tag: "Image background", visible: true, image: Qt.resolvedUrl("artwork/checkers.png"), index: 12 },
423+ { tag: "Horizontal no background", visible: false, index: 14 },
418 ];
424 ];
419 }
425 }
420
426
tests/qmltests/Dash/tst_Dash.qml+69
@@ -428,5 +428,74 @@ Item {
428 tryCompare(bottomEdgeController, "progress", 0);
428 tryCompare(bottomEdgeController, "progress", 0);
429 tryCompare(dashContentList, "currentIndex", 1)
429 tryCompare(dashContentList, "currentIndex", 1)
430 }
430 }
431+
432+ function test_close_temp_scope_preview_opening_scope() {
433+ // Show the manage dash
434+ touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
435+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
436+ tryCompare(bottomEdgeController, "progress", 1);
437+
438+ // Make sure stuff is loaded
439+ var nonfavScopesListCategory = findChild(dash, "scopesListCategoryother");
440+ var nonfavScopesListCategoryList = findChild(nonfavScopesListCategory, "scopesListCategoryInnerList");
441+ tryCompare(nonfavScopesListCategoryList, "currentIndex", 0);
442+
443+ // Click on a non favorite scope
444+ mouseClick(nonfavScopesListCategoryList.currentItem);
445+
446+ // Check the bottom edge (manage dash) is disabled from temp scope
447+ var overviewDragHandle = findChild(dash, "overviewDragHandle");
448+ compare(overviewDragHandle.enabled, false);
449+
450+ // Check temp scope is there
451+ var dashTempScopeItem = findChild(dash, "dashTempScopeItem");
452+ tryCompare(dashTempScopeItem, "x", 0);
453+ tryCompare(dashTempScopeItem, "visible", true);
454+
455+ // Check the manage dash is gone
456+ tryCompare(bottomEdgeController, "progress", 0);
457+
458+ // Open preview
459+ var categoryListView = findChild(dashTempScopeItem, "categoryListView");
460+ categoryListView.positionAtBeginning();
461+ tryCompareFunction(function() {
462+ var cardGrid = findChild(dashTempScopeItem, "dashCategory0");
463+ if (cardGrid != null) {
464+ var tile = findChild(cardGrid, "delegate0");
465+ return tile != null;
466+ }
467+ return false;
468+ },
469+ true);
470+ var tile = findChild(findChild(dashTempScopeItem, "dashCategory0"), "delegate0");
471+ waitForRendering(tile);
472+ mouseClick(tile);
473+ var subPageLoader = findChild(dashTempScopeItem, "subPageLoader");
474+ tryCompare(subPageLoader, "open", true);
475+ tryCompare(subPageLoader, "x", 0);
476+ tryCompare(findChild(dashTempScopeItem, "categoryListView"), "visible", false);
477+ var previewListRow0 = findChild(subPageLoader, "previewListRow0");
478+ touchFlick(previewListRow0, previewListRow0.width / 2, units.gu(20), previewListRow0.width / 2, units.gu(1));
479+ tryCompare(previewListRow0, "atYEnd", true);
480+ tryCompare(previewListRow0, "moving", false);
481+ var widget = findChild(subPageLoader, "widget-21");
482+ var initialWidgetHeight = widget.height;
483+ var openButton = findChild(widget, "buttonopen_click");
484+ mouseClick(openButton);
485+
486+ tryCompare(subPageLoader, "open", false);
487+ tryCompare(subPageLoader, "x", subPageLoader.width);
488+
489+ compare(dashTempScopeItem.scope.id, "MockScope9");
490+
491+ // Go back
492+ var dashTempScopeItemHeader = findChild(dashTempScopeItem, "scopePageHeader");
493+ var backButton = findChild(findChild(dashTempScopeItemHeader, "innerPageHeader"), "customBackButton");
494+ mouseClick(backButton);
495+
496+ // Check temp scope is gone
497+ tryCompare(dashTempScopeItem, "x", dash.width);
498+ tryCompare(dashTempScopeItem, "visible", false);
499+ }
431 }
500 }
432}
501}
tests/qmltests/Dash/tst_GenericScopeView.qml+1−1
@@ -380,8 +380,8 @@ Item {
380
380
381 tryCompare(testCase.subPageLoader, "open", false);
381 tryCompare(testCase.subPageLoader, "open", false);
382
382
383+ tryCompareFunction(function() { return findChild(category, "delegate1") != null; }, true);
383 var tile = findChild(category, "delegate1");
384 var tile = findChild(category, "delegate1");
384− verify(tile, "Could not find delegate");
385
385
386 mouseClick(tile);
386 mouseClick(tile);
387 tryCompare(testCase.subPageLoader, "open", true);
387 tryCompare(testCase.subPageLoader, "open", true);
tests/qmltests/Greeter/tst_Infographics.qml+10
@@ -23,6 +23,16 @@ import Unity.Test 0.1 as UT
23import LightDM 0.1 as LightDM
23import LightDM 0.1 as LightDM
24
24
25Item {
25Item {
26+ Binding {
27+ target: LightDM.Greeter
28+ property: "mockMode"
29+ value: "full"
30+ }
31+ Binding {
32+ target: LightDM.Users
33+ property: "mockMode"
34+ value: "full"
35+ }
26 property variant infographicModel: LightDM.Infographic
36 property variant infographicModel: LightDM.Infographic
27
37
28 width: units.gu(120)
38 width: units.gu(120)
tests/qmltests/Greeter/tst_MultiGreeter.qml+91−2
@@ -15,6 +15,7 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18+import QtQuick.Layouts 1.1
18import QtTest 1.0
19import QtTest 1.0
19import ".."
20import ".."
20import "../../../qml/Greeter"
21import "../../../qml/Greeter"
@@ -23,12 +24,37 @@ import LightDM 0.1 as LightDM
23import Unity.Test 0.1 as UT
24import Unity.Test 0.1 as UT
24
25
25Item {
26Item {
27+ id: root
26− width: units.gu(120)
28+ width: units.gu(140)
27 height: units.gu(80)
29 height: units.gu(80)
28
30
31+ QtObject {
32+ id: fakeInputMethod
33+ property bool visible: fakeKeyboard.visible
34+ property var keyboardRectangle: QtObject {
35+ property real x: fakeKeyboard.x
36+ property real y: fakeKeyboard.y
37+ property real width: fakeKeyboard.width
38+ property real height: fakeKeyboard.height
39+ }
40+ }
41+
42+ Binding {
43+ target: LightDM.Greeter
44+ property: "mockMode"
45+ value: "full"
46+ }
47+ Binding {
48+ target: LightDM.Users
49+ property: "mockMode"
50+ value: "full"
51+ }
52+
29 Greeter {
53 Greeter {
30 id: greeter
54 id: greeter
31− anchors.fill: parent
55+ width: units.gu(120)
56+ height: root.height
57+ inputMethod: fakeInputMethod
32 locked: !LightDM.Greeter.authenticated
58 locked: !LightDM.Greeter.authenticated
33 }
59 }
34
60
@@ -43,6 +69,44 @@ Item {
43 }
69 }
44 }
70 }
45
71
72+ Rectangle {
73+ id: fakeKeyboard
74+ color: "green"
75+ opacity: 0.7
76+ anchors.bottom: root.bottom
77+ width: greeter.width
78+ height: greeter.height * 0.6
79+ visible: keyboardVisibleCheckbox.checked
80+ Text {
81+ text: "Keyboard Rectangle"
82+ color: "yellow"
83+ font.bold: true
84+ fontSizeMode: Text.Fit
85+ minimumPixelSize: 10; font.pixelSize: 200
86+ verticalAlignment: Text.AlignVCenter
87+ x: (parent.width - width) / 2
88+ y: (parent.height - height) / 2
89+ width: parent.width
90+ height: parent.height
91+ }
92+ }
93+
94+ Item {
95+ anchors {
96+ top: root.top
97+ bottom: root.bottom
98+ left: greeter.right
99+ right: root.right
100+ }
101+ RowLayout {
102+ Layout.fillWidth: true
103+ CheckBox {
104+ id: keyboardVisibleCheckbox
105+ }
106+ Label { text: "Keyboard Visible"; anchors.verticalCenter: parent.verticalCenter }
107+ }
108+ }
109+
46 SignalSpy {
110 SignalSpy {
47 id: unlockSpy
111 id: unlockSpy
48 target: greeter
112 target: greeter
@@ -65,6 +129,10 @@ Item {
65 name: "MultiGreeter"
129 name: "MultiGreeter"
66 when: windowShown
130 when: windowShown
67
131
132+ function cleanup() {
133+ keyboardVisibleCheckbox.checked = false;
134+ }
135+
68 function select_index(i) {
136 function select_index(i) {
69 // We could be anywhere in list; find target index to know which direction
137 // We could be anywhere in list; find target index to know which direction
70 var userlist = findChild(greeter, "userList")
138 var userlist = findChild(greeter, "userList")
@@ -382,5 +450,26 @@ Item {
382 tryCompare(spy, "count", 1)
450 tryCompare(spy, "count", 1)
383 greeterObj.destroy()
451 greeterObj.destroy()
384 }
452 }
453+
454+ function test_login_list_not_covered_by_keyboard() {
455+ var loginList = findChild(greeter, "loginLoader").item;
456+ compare(loginList.height, greeter.height);
457+
458+ // when the vkb shows up, loginList is moved up to remain fully uncovered
459+
460+ keyboardVisibleCheckbox.checked = true;
461+
462+ tryCompare(loginList, "height", greeter.height - fakeInputMethod.keyboardRectangle.height);
463+ tryCompareFunction( function() {
464+ var loginListRect = loginList.mapToItem(greeter, 0, 0, loginList.width, loginList.height);
465+ return loginListRect.y + loginListRect.height <= fakeInputMethod.keyboardRectangle.y;
466+ }, true);
467+
468+ // once the vkb goes away, loginList goes back to its full height
469+
470+ keyboardVisibleCheckbox.checked = false;
471+
472+ tryCompare(loginList, "height", greeter.height);
473+ }
385 }
474 }
386}
475}
tests/qmltests/Launcher/tst_Launcher.qml+46−19
@@ -15,6 +15,7 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18+import QtQuick.Layouts 1.1
18import QtTest 1.0
19import QtTest 1.0
19import Unity.Test 0.1 as UT
20import Unity.Test 0.1 as UT
20import Ubuntu.Components 1.1
21import Ubuntu.Components 1.1
@@ -74,10 +75,22 @@ Item {
74 }
75 }
75 }
76 }
76
77
77− Button {
78+ ColumnLayout {
78 anchors { bottom: parent.bottom; right: parent.right; margins: units.gu(1) }
79 anchors { bottom: parent.bottom; right: parent.right; margins: units.gu(1) }
80+ spacing: units.gu(1)
81+ width: units.gu(20)
82+
83+ Button {
79− text: "emit hinting signal"
84+ text: "emit hinting signal"
80− onClicked: LauncherModel.emitHint()
85+ onClicked: LauncherModel.emitHint()
86+ Layout.fillWidth: true
87+ }
88+
89+ Button {
90+ text: "rotate"
91+ onClicked: launcherLoader.item.inverted = !launcherLoader.item.inverted
92+ Layout.fillWidth: true
93+ }
81 }
94 }
82
95
83 SignalSpy {
96 SignalSpy {
@@ -140,6 +153,18 @@ Item {
140 tryCompare(panel, "x", -panel.width, 1000);
153 tryCompare(panel, "x", -panel.width, 1000);
141 }
154 }
142
155
156+ function positionLauncherListAtBeginning() {
157+ var listView = testCase.findChild(launcherLoader.item, "launcherListView");
158+ listView.contentY = -listView.topMargin;
159+ }
160+ function positionLauncherListAtEnd() {
161+ var listView = testCase.findChild(launcherLoader.item, "launcherListView");
162+ if ((listView.contentHeight + listView.topMargin + listView.bottomMargin) > listView.height) {
163+ listView.contentY = listView.topMargin + listView.contentHeight
164+ - listView.height;
165+ }
166+ }
167+
143 // Drag from the left edge of the screen rightwards and check that the launcher
168 // Drag from the left edge of the screen rightwards and check that the launcher
144 // appears (as if being dragged by the finger/pointer)
169 // appears (as if being dragged by the finger/pointer)
145 function test_dragLeftEdgeToRevealLauncherAndTapCenterToDismiss() {
170 function test_dragLeftEdgeToRevealLauncherAndTapCenterToDismiss() {
@@ -166,22 +191,22 @@ Item {
166 launcherApplicationSelected("[...]dialer-app.desktop") */
191 launcherApplicationSelected("[...]dialer-app.desktop") */
167 function test_clickingOnAppIconCausesSignalEmission() {
192 function test_clickingOnAppIconCausesSignalEmission() {
168 dragLauncherIntoView();
193 dragLauncherIntoView();
169− launcher.lastSelectedApplication = ""
194+ launcher.lastSelectedApplication = "";
195+ launcher.inverted = false;
170
196
171− var listView = findChild(launcher, "launcherListView");
197+ positionLauncherListAtBeginning();
172− listView.positionViewAtEnd();
173
198
174− var appIcon = findChild(launcher, "launcherDelegate0")
199+ var appIcon = findChild(launcher, "launcherDelegate0");
175
200
176− verify(appIcon != undefined)
201+ verify(appIcon != undefined);
177
202
178− mouseClick(appIcon)
203+ mouseClick(appIcon);
179
204
180 tryCompare(launcher, "lastSelectedApplication",
205 tryCompare(launcher, "lastSelectedApplication",
181− "dialer-app")
206+ appIcon.appId);
182
207
183 // Tapping on an application icon also dismisses the launcher
208 // Tapping on an application icon also dismisses the launcher
184− waitUntilLauncherDisappears()
209+ waitUntilLauncherDisappears();
185 }
210 }
186
211
187 /* If I click on the dash icon on the launcher
212 /* If I click on the dash icon on the launcher
@@ -277,25 +302,26 @@ Item {
277 function test_clickFlick_data() {
302 function test_clickFlick_data() {
278 var listView = findChild(launcher, "launcherListView");
303 var listView = findChild(launcher, "launcherListView");
279 return [
304 return [
280− {tag: "unfolded top", positionViewAtBeginning: false,
305+ {tag: "unfolded top", positionViewAtBeginning: true,
281 clickY: listView.topMargin + units.gu(2),
306 clickY: listView.topMargin + units.gu(2),
282 expectFlick: false},
307 expectFlick: false},
283
308
284− {tag: "folded top", positionViewAtBeginning: true,
309+ {tag: "folded top", positionViewAtBeginning: false,
285 clickY: listView.topMargin + units.gu(2),
310 clickY: listView.topMargin + units.gu(2),
286 expectFlick: true},
311 expectFlick: true},
287
312
288− {tag: "unfolded bottom", positionViewAtBeginning: true,
313+ {tag: "unfolded bottom", positionViewAtBeginning: false,
289 clickY: listView.height - listView.topMargin - units.gu(1),
314 clickY: listView.height - listView.topMargin - units.gu(1),
290 expectFlick: false},
315 expectFlick: false},
291
316
292− {tag: "folded bottom", positionViewAtBeginning: false,
317+ {tag: "folded bottom", positionViewAtBeginning: true,
293 clickY: listView.height - listView.topMargin - units.gu(1),
318 clickY: listView.height - listView.topMargin - units.gu(1),
294 expectFlick: true},
319 expectFlick: true},
295 ];
320 ];
296 }
321 }
297
322
298 function test_clickFlick(data) {
323 function test_clickFlick(data) {
324+ launcher.inverted = false;
299 launcher.lastSelectedApplication = "";
325 launcher.lastSelectedApplication = "";
300 dragLauncherIntoView();
326 dragLauncherIntoView();
301 var listView = findChild(launcher, "launcherListView");
327 var listView = findChild(launcher, "launcherListView");
@@ -305,9 +331,9 @@ Item {
305 // So for stability's sake we just put the listView in the position
331 // So for stability's sake we just put the listView in the position
306 // we want to to actually start doing what this tests intends to check.
332 // we want to to actually start doing what this tests intends to check.
307 if (data.positionViewAtBeginning) {
333 if (data.positionViewAtBeginning) {
308− listView.positionViewAtBeginning();
334+ positionLauncherListAtBeginning();
309 } else {
335 } else {
310− listView.positionViewAtEnd();
336+ positionLauncherListAtEnd();
311 }
337 }
312 tryCompare(listView, "flicking", false);
338 tryCompare(listView, "flicking", false);
313
339
@@ -442,9 +468,9 @@ Item {
442 // Position launcher to where we need it
468 // Position launcher to where we need it
443 var listView = findChild(launcher, "launcherListView");
469 var listView = findChild(launcher, "launcherListView");
444 if (data.flickTo == "top") {
470 if (data.flickTo == "top") {
445− listView.positionViewAtEnd();
471+ positionLauncherListAtBeginning();
446 } else {
472 } else {
447− listView.positionViewAtBeginning();
473+ positionLauncherListAtEnd();
448 }
474 }
449
475
450 // Doing longpress
476 // Doing longpress
@@ -454,6 +480,7 @@ Item {
454
480
455 verify(quickList.y >= units.gu(1));
481 verify(quickList.y >= units.gu(1));
456 verify(quickList.y + quickList.height + units.gu(1) <= launcher.height);
482 verify(quickList.y + quickList.height + units.gu(1) <= launcher.height);
483+ compare(quickList.width, units.gu(30));
457
484
458 // Click somewhere in the empty space to dismiss the quicklist
485 // Click somewhere in the empty space to dismiss the quicklist
459 mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
486 mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
tests/mocks/Unity/Notifications/MockNotificationTypes.cpp → tests/qmltests/Notifications/Notification.qmlrenamed+11−6
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 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
@@ -17,10 +17,15 @@
17 * Mirco Mueller <mirco.mueller@canonical.com>
17 * Mirco Mueller <mirco.mueller@canonical.com>
18 */
18 */
19
19
20−#include "MockNotificationTypes.h"
20+import Unity.Notifications 1.0
21
21
22−MockNotification::MockNotification(QObject *parent) : QObject(parent) {
22+Notification {
23−}
23+ nid: 0
24−
24+ type: Notification.PlaceHolder
25−MockNotification::~MockNotification() {
25+ summary: ""
26+ body: ""
27+ icon: ""
28+ secondaryIcon: ""
29+ value: 0
30+ rawActions: []
26}
31}
tests/qmltests/Notifications/tst_Notifications.qml+279−213
@@ -1,17 +1,20 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright 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 Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
6 * the Free Software Foundation; version 3.
7 *
7 *
8 * This program is distributed in the hope that it will be useful,
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
11+ * GNU Lesser General Public License for more details.
12 *
12 *
13− * You should have received a copy of the GNU General Public License
13+ * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Mirco Mueller <mirco.mueller@canonical.com>
15 */
18 */
16
19
17import QtQuick 2.0
20import QtQuick 2.0
@@ -24,146 +27,134 @@ import Unity.Notifications 1.0
24import QtMultimedia 5.0
27import QtMultimedia 5.0
25
28
26Item {
29Item {
30+ id: foobar
31+
27 width: notificationsRect.width + interactiveControls.width
32 width: notificationsRect.width + interactiveControls.width
28 height: notificationsRect.height
33 height: notificationsRect.height
34+ property int index: 0
29
35
30 Row {
36 Row {
31 id: rootRow
37 id: rootRow
32
38
33− Component {
39+ NotificationModel {
34− id: mockNotification
35−
36− QtObject {
37− function invokeAction(actionId) {
38− mockModel.actionInvoked(actionId)
39− }
40− }
41− }
42−
43− ListModel {
44 id: mockModel
40 id: mockModel
45− dynamicRoles: true
46−
47− signal actionInvoked(string actionId)
48−
49− function getRaw(id) {
50− return mockNotification.createObject(mockModel)
51− }
52
41
53 // add the default/PlaceHolder notification to the model
42 // add the default/PlaceHolder notification to the model
54 Component.onCompleted: {
43 Component.onCompleted: {
55− var n = {
44+ var component = Qt.createComponent("Notification.qml")
45+ var n = component.createObject("notification", {"nid": index++,
56− type: Notification.PlaceHolder,
46+ "type": Notification.PlaceHolder,
57− hints: {},
47+ "hints": {},
58− summary: "",
48+ "summary": "",
59− body: "",
49+ "body": "",
60− icon: "",
50+ "icon": "",
61− secondaryIcon: "",
51+ "secondaryIcon": "",
62− actions: []
52+ "rawActions": []})
63− }
53+ n.completed.connect(mockModel.onCompleted)
64−
65 append(n)
54 append(n)
66 }
55 }
67 }
56 }
68
57
69 function add2over1SnapDecisionNotification() {
58 function add2over1SnapDecisionNotification() {
70− var n = {
59+ var component = Qt.createComponent("Notification.qml")
60+ var n = component.createObject("notification", {"nid": index++,
71− type: Notification.SnapDecision,
61+ "type": Notification.SnapDecision,
72− hints: {"x-canonical-private-affirmative-tint": "true"},
62+ "hints": {"x-canonical-private-affirmative-tint": "true"},
73− summary: "Theatre at Ferria Stadium",
63+ "summary": "Theatre at Ferria Stadium",
74− body: "at Ferria Stadium in Bilbao, Spain\n07578545317",
64+ "body": "at Ferria Stadium in Bilbao, Spain\n07578545317",
75− icon: "",
65+ "icon": "",
76− secondaryIcon: "",
66+ "secondaryIcon": "",
77− actions: [{ id: "ok_id", label: "Ok"},
67+ "rawActions": ["ok_id", "Ok",
78− { id: "snooze_id", label: "Snooze"},
68+ "snooze_id", "Snooze",
79− { id: "view_id", label: "View"}]
69+ "view_id", "View"]})
80− }
70+ n.completed.connect(mockModel.onCompleted)
81−
82 mockModel.append(n)
71 mockModel.append(n)
83 }
72 }
84
73
85 function addEphemeralNotification() {
74 function addEphemeralNotification() {
86− var n = {
75+ var component = Qt.createComponent("Notification.qml")
76+ var n = component.createObject("notification", {"nid": index++,
87− type: Notification.Ephemeral,
77+ "type": Notification.Ephemeral,
78+ "hints": {},
88− summary: "Cole Raby",
79+ "summary": "Cole Raby",
89− body: "I did not expect it to be that late.",
80+ "body": "I did not expect it to be that late.",
90− icon: "../graphics/avatars/amanda.png",
81+ "icon": "../graphics/avatars/amanda.png",
91− secondaryIcon: "../graphics/applicationIcons/facebook.png",
82+ "secondaryIcon": "../graphics/applicationIcons/facebook.png",
92− actions: []
83+ "rawActions": ["reply_id", "Dummy"]})
93− }
84+ n.completed.connect(mockModel.onCompleted)
94−
95 mockModel.append(n)
85 mockModel.append(n)
96 }
86 }
97
87
98 function addEphemeralNonShapedIconNotification() {
88 function addEphemeralNonShapedIconNotification() {
99− var n = {
89+ var component = Qt.createComponent("Notification.qml")
90+ var n = component.createObject("notification", {"nid": index++,
100− type: Notification.Ephemeral,
91+ "type": Notification.Ephemeral,
101− hints: {"x-canonical-non-shaped-icon": "true"},
92+ "hints": {"x-canonical-non-shaped-icon": "true"},
102− summary: "Contacts",
93+ "summary": "Contacts",
103− body: "Synchronised contacts-database with cloud-storage.",
94+ "body": "Synchronised contacts-database with cloud-storage.",
104− icon: "../graphics/applicationIcons/contacts-app.png",
95+ "icon": "../graphics/applicationIcons/contacts-app.png",
105− secondaryIcon: "",
96+ "secondaryIcon": "",
106− actions: []
97+ "rawActions": ["reply_id", "Dummy"]})
107− }
98+ n.completed.connect(mockModel.onCompleted)
108−
109 mockModel.append(n)
99 mockModel.append(n)
110 }
100 }
111
101
112 function addEphemeralIconSummaryNotification() {
102 function addEphemeralIconSummaryNotification() {
113− var n = {
103+ var component = Qt.createComponent("Notification.qml")
104+ var n = component.createObject("notification", {"nid": index++,
114− type: Notification.Ephemeral,
105+ "type": Notification.Ephemeral,
115− hints: {"x-canonical-non-shaped-icon": "false"},
106+ "hints": {"x-canonical-non-shaped-icon": "false"},
116− summary: "Photo upload completed",
107+ "summary": "Photo upload completed",
117− body: "",
108+ "body": "",
118− icon: "../graphics/applicationIcons/facebook.png",
109+ "icon": "../graphics/applicationIcons/facebook.png",
119− secondaryIcon: "",
110+ "secondaryIcon": "",
120− actions: []
111+ "rawActions": ["reply_id", "Dummy"]})
121− }
112+ n.completed.connect(mockModel.onCompleted)
122−
123 mockModel.append(n)
113 mockModel.append(n)
124 }
114 }
125
115
126 function addInteractiveNotification() {
116 function addInteractiveNotification() {
127− var n = {
117+ var component = Qt.createComponent("Notification.qml")
118+ var n = component.createObject("notification", {"nid": index++,
128− type: Notification.Interactive,
119+ "type": Notification.Interactive,
120+ "hints": {},
129− summary: "Interactive notification",
121+ "summary": "Interactive notification",
130− body: "This is a notification that can be clicked",
122+ "body": "This is a notification that can be clicked",
131− icon: "../graphics/avatars/anna_olsson.png",
123+ "icon": "../graphics/avatars/anna_olsson.png",
132− secondaryIcon: "",
124+ "secondaryIcon": "",
133− actions: [{ id: "reply_id", label: "Dummy"}],
125+ "rawActions": ["reply_id", "Dummy"]})
134− }
126+ n.completed.connect(mockModel.onCompleted)
135−
136 mockModel.append(n)
127 mockModel.append(n)
137 }
128 }
138
129
139 function addConfirmationNotification() {
130 function addConfirmationNotification() {
140− var n = {
131+ var component = Qt.createComponent("Notification.qml")
132+ var n = component.createObject("notification", {"nid": index++,
141− type: Notification.Confirmation,
133+ "type": Notification.Confirmation,
142− hints: {"x-canonical-non-shaped-icon": "true"},
134+ "hints": {"x-canonical-non-shaped-icon": "true"},
143− summary: "Confirmation notification",
135+ "summary": "Confirmation notification",
144− body: "",
136+ "body": "",
145− icon: "image://theme/audio-volume-medium",
137+ "icon": "image://theme/audio-volume-medium",
146− secondaryIcon: "",
138+ "secondaryIcon": "",
147− value: 50,
139+ "value": 50,
148− actions: [],
140+ "rawActions": ["reply_id", "Dummy"]})
149− }
141+ n.completed.connect(mockModel.onCompleted)
150−
151 mockModel.append(n)
142 mockModel.append(n)
152 }
143 }
153
144
154 function add2ndConfirmationNotification() {
145 function add2ndConfirmationNotification() {
155− var n = {
146+ var component = Qt.createComponent("Notification.qml")
147+ var n = component.createObject("notification", {"nid": index++,
156− type: Notification.Confirmation,
148+ "type": Notification.Confirmation,
157− hints: {"x-canonical-non-shaped-icon": "true",
149+ "hints": {"x-canonical-non-shaped-icon": "true",
158− "x-canonical-value-bar-tint": "true"},
150+ "x-canonical-value-bar-tint": "true"},
159− summary: "Confirmation notification",
151+ "summary": "Confirmation notification",
160− body: "High Volume",
152+ "body": "High Volume",
161− icon: "image://theme/audio-volume-high",
153+ "icon": "image://theme/audio-volume-high",
162− secondaryIcon: "",
154+ "secondaryIcon": "",
163− value: 85,
155+ "value": 85,
164− actions: [],
156+ "rawActions": ["reply_id", "Dummy"]})
165− }
157+ n.completed.connect(mockModel.onCompleted)
166−
167 mockModel.append(n)
158 mockModel.append(n)
168 }
159 }
169
160
@@ -174,8 +165,9 @@ Item {
174 }
165 }
175
166
176 function remove1stNotification() {
167 function remove1stNotification() {
177− if (mockModel.count > 1)
168+ if (mockModel.count > 1) {
178− mockModel.remove(1)
169+ mockModel.removeSecond()
170+ }
179 }
171 }
180
172
181 Rectangle {
173 Rectangle {
@@ -273,41 +265,122 @@ Item {
273 name: "NotificationRendererTest"
265 name: "NotificationRendererTest"
274 when: windowShown
266 when: windowShown
275
267
268+ property list<Notification> nlist: [
269+ Notification {
270+ nid: 1
271+ type: Notification.Ephemeral
272+ summary: "Photo upload completed"
273+ body: ""
274+ icon: "../graphics/applicationIcons/facebook.png"
275+ secondaryIcon: ""
276+ value: 0
277+ rawActions: []
278+ },
279+ Notification {
280+ nid: 2
281+ type: Notification.Ephemeral
282+ hints: {"x-canonical-private-affirmative-tint": "false",
283+ "sound-file": "dummy.ogg",
284+ "suppress-sound": "true"}
285+ summary: "New comment successfully published"
286+ body: ""
287+ icon: ""
288+ secondaryIcon: "../graphics/applicationIcons/facebook.png"
289+ value: 0
290+ rawActions: []
291+ },
292+ Notification {
293+ nid: 3
294+ type: Notification.Interactive
295+ hints: {"x-canonical-private-affirmative-tint": "false",
296+ "sound-file": "dummy.ogg"}
297+ summary: "Interactive notification"
298+ body: "This is a notification that can be clicked"
299+ icon: "../graphics/avatars/amanda.png"
300+ secondaryIcon: ""
301+ value: 0
302+ rawActions: ["reply_id", "Dummy"]
303+ },
304+ Notification {
305+ nid: 4
306+ type: Notification.SnapDecision
307+ hints: {"x-canonical-private-affirmative-tint": "false",
308+ "sound-file": "dummy.ogg"}
309+ summary: "Bro Coly"
310+ body: "At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."
311+ icon: "../graphics/avatars/anna_olsson.png"
312+ secondaryIcon: ""
313+ value: 0
314+ rawActions: ["accept_id", "Accept",
315+ "reject_id", "Reject"]
316+ },
317+ Notification {
318+ nid: 5
319+ type: Notification.Ephemeral
320+ hints: {"x-canonical-private-affirmative-tint": "false",
321+ "sound-file": "dummy.ogg"}
322+ summary: "Cole Raby"
323+ body: "I did not expect it to be that late."
324+ icon: "../graphics/avatars/funky.png"
325+ secondaryIcon: "../graphics/applicationIcons/facebook.png"
326+ value: 0
327+ rawActions: []
328+ },
329+ Notification {
330+ nid: 6
331+ type: Notification.Ephemeral
332+ hints: {"x-canonical-private-affirmative-tint": "false",
333+ "x-canonical-non-shaped-icon": "true"}
334+ summary: "Contacts"
335+ body: "Synchronised contacts-database with cloud-storage."
336+ icon: "image://theme/contacts-app"
337+ secondaryIcon: ""
338+ value: 0
339+ rawActions: []
340+ },
341+ Notification {
342+ nid: 7
343+ type: Notification.Confirmation
344+ hints: {"x-canonical-non-shaped-icon": "true"}
345+ summary: ""
346+ body: ""
347+ icon: "image://theme/audio-volume-medium"
348+ secondaryIcon: ""
349+ value: 50
350+ rawActions: []
351+ },
352+ Notification {
353+ nid: 8
354+ type: Notification.Confirmation
355+ hints: {"x-canonical-non-shaped-icon": "true",
356+ "x-canonical-value-bar-tint" : "true"}
357+ summary: ""
358+ body: "High Volume"
359+ icon: "image://theme/audio-volume-high"
360+ secondaryIcon: ""
361+ value: 85
362+ rawActions: []
363+ },
364+ Notification {
365+ nid: 9
366+ type: Notification.SnapDecision
367+ hints: {"x-canonical-private-affirmative-tint": "true"}
368+ summary: "Theatre at Ferria Stadium"
369+ body: "at Ferria Stadium in Bilbao, Spain\n07578545317"
370+ icon: ""
371+ secondaryIcon: ""
372+ value: 0
373+ rawActions: ["ok_id", "Ok",
374+ "snooze_id", "Snooze",
375+ "view_id", "View"]
376+ }
377+ ]
378+
276 function test_NotificationRenderer_data() {
379 function test_NotificationRenderer_data() {
277 return [
380 return [
278− {
279− tag: "2-over-1 Snap Decision with button-tint",
280− type: Notification.SnapDecision,
281− hints: {"x-canonical-private-affirmative-tint": "true"},
282− summary: "Theatre at Ferria Stadium",
283− body: "at Ferria Stadium in Bilbao, Spain\n07578545317",
284− icon: "",
285− secondaryIcon: "",
286− actions: [{ id: "ok_id", label: "Ok"},
287− { id: "snooze_id", label: "Snooze"},
288− { id: "view_id", label: "View"}],
289− summaryVisible: true,
290− bodyVisible: true,
291− iconVisible: false,
292− centeredIconVisible: false,
293− shaped: false,
294− secondaryIconVisible: false,
295− buttonRowVisible: false,
296− buttonTinted: true,
297− hasSound: false,
298− valueVisible: false,
299− valueLabelVisible: false,
300− valueTinted: false
301− },
302 {
381 {
303 tag: "Ephemeral notification - icon-summary layout",
382 tag: "Ephemeral notification - icon-summary layout",
304− type: Notification.Ephemeral,
383+ n: nlist[0],
305− hints: {},
306− summary: "Photo upload completed",
307− body: "",
308− icon: "../graphics/applicationIcons/facebook.png",
309− secondaryIcon: "",
310− actions: [],
311 summaryVisible: true,
384 summaryVisible: true,
312 bodyVisible: false,
385 bodyVisible: false,
313 iconVisible: true,
386 iconVisible: true,
@@ -323,15 +396,7 @@ Item {
323 },
396 },
324 {
397 {
325 tag: "Ephemeral notification - check suppression of secondary icon for icon-summary layout",
398 tag: "Ephemeral notification - check suppression of secondary icon for icon-summary layout",
326− type: Notification.Ephemeral,
399+ n: nlist[1],
327− hints: {"x-canonical-private-affirmative-tint": "false",
328− "sound-file": "dummy.ogg",
329− "suppress-sound": "true"},
330− summary: "New comment successfully published",
331− body: "",
332− icon: "",
333− secondaryIcon: "../graphics/applicationIcons/facebook.png",
334− actions: [],
335 summaryVisible: true,
400 summaryVisible: true,
336 bodyVisible: false,
401 bodyVisible: false,
337 interactiveAreaEnabled: false,
402 interactiveAreaEnabled: false,
@@ -348,14 +413,7 @@ Item {
348 },
413 },
349 {
414 {
350 tag: "Interactive notification",
415 tag: "Interactive notification",
351− type: Notification.Interactive,
416+ n: nlist[2],
352− hints: {"x-canonical-private-affirmative-tint": "false",
353− "sound-file": "dummy.ogg"},
354− summary: "Interactive notification",
355− body: "This is a notification that can be clicked",
356− icon: "../graphics/avatars/amanda.png",
357− secondaryIcon: "",
358− actions: [{ id: "reply_id", label: "Dummy"}],
359 summaryVisible: true,
417 summaryVisible: true,
360 bodyVisible: true,
418 bodyVisible: true,
361 iconVisible: true,
419 iconVisible: true,
@@ -371,15 +429,7 @@ Item {
371 },
429 },
372 {
430 {
373 tag: "Snap Decision without secondary icon and no button-tint",
431 tag: "Snap Decision without secondary icon and no button-tint",
374− type: Notification.SnapDecision,
432+ n: nlist[3],
375− hints: {"x-canonical-private-affirmative-tint": "false",
376− "sound-file": "dummy.ogg"},
377− summary: "Bro Coly",
378− body: "At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
379− icon: "../graphics/avatars/anna_olsson.png",
380− secondaryIcon: "",
381− actions: [{ id: "accept_id", label: "Accept"},
382− { id: "reject_id", label: "Reject"}],
383 summaryVisible: true,
433 summaryVisible: true,
384 bodyVisible: true,
434 bodyVisible: true,
385 iconVisible: true,
435 iconVisible: true,
@@ -395,14 +445,7 @@ Item {
395 },
445 },
396 {
446 {
397 tag: "Ephemeral notification",
447 tag: "Ephemeral notification",
398− type: Notification.Ephemeral,
448+ n: nlist[4],
399− hints: {"x-canonical-private-affirmative-tint": "false",
400− "sound-file": "dummy.ogg"},
401− summary: "Cole Raby",
402− body: "I did not expect it to be that late.",
403− icon: "../graphics/avatars/funky.png",
404− secondaryIcon: "../graphics/applicationIcons/facebook.png",
405− actions: [],
406 summaryVisible: true,
449 summaryVisible: true,
407 bodyVisible: true,
450 bodyVisible: true,
408 iconVisible: true,
451 iconVisible: true,
@@ -418,14 +461,7 @@ Item {
418 },
461 },
419 {
462 {
420 tag: "Ephemeral notification with non-shaped icon",
463 tag: "Ephemeral notification with non-shaped icon",
421− type: Notification.Ephemeral,
464+ n: nlist[5],
422− hints: {"x-canonical-private-affirmative-tint": "false",
423− "x-canonical-non-shaped-icon": "true"},
424− summary: "Contacts",
425− body: "Synchronised contacts-database with cloud-storage.",
426− icon: "image://theme/contacts-app",
427− secondaryIcon: "",
428− actions: [],
429 summaryVisible: true,
465 summaryVisible: true,
430 bodyVisible: true,
466 bodyVisible: true,
431 iconVisible: true,
467 iconVisible: true,
@@ -441,14 +477,7 @@ Item {
441 },
477 },
442 {
478 {
443 tag: "Confirmation notification with value",
479 tag: "Confirmation notification with value",
444− type: Notification.Confirmation,
480+ n: nlist[6],
445− hints: {"x-canonical-non-shaped-icon": "true"},
446− summary: "",
447− body: "",
448− icon: "image://theme/audio-volume-medium",
449− secondaryIcon: "",
450− value: 50,
451− actions: [],
452 summaryVisible: false,
481 summaryVisible: false,
453 bodyVisible: false,
482 bodyVisible: false,
454 iconVisible: false,
483 iconVisible: false,
@@ -464,15 +493,7 @@ Item {
464 },
493 },
465 {
494 {
466 tag: "Confirmation notification with value, label and tint",
495 tag: "Confirmation notification with value, label and tint",
467− type: Notification.Confirmation,
496+ n: nlist[7],
468− hints: {"x-canonical-non-shaped-icon": "true",
469− "x-canonical-value-bar-tint" : "true"},
470− summary: "",
471− body: "High Volume",
472− icon: "image://theme/audio-volume-high",
473− secondaryIcon: "",
474− value: 85,
475− actions: [],
476 summaryVisible: false,
497 summaryVisible: false,
477 bodyVisible: false,
498 bodyVisible: false,
478 iconVisible: false,
499 iconVisible: false,
@@ -485,6 +506,22 @@ Item {
485 valueVisible: true,
506 valueVisible: true,
486 valueLabelVisible: true,
507 valueLabelVisible: true,
487 valueTinted: true
508 valueTinted: true
509+ },
510+ {
511+ tag: "2-over-1 Snap Decision with button-tint",
512+ n: nlist[8],
513+ summaryVisible: true,
514+ bodyVisible: true,
515+ iconVisible: false,
516+ centeredIconVisible: false,
517+ shaped: false,
518+ secondaryIconVisible: false,
519+ buttonRowVisible: false,
520+ buttonTinted: true,
521+ hasSound: false,
522+ valueVisible: false,
523+ valueLabelVisible: false,
524+ valueTinted: false
488 }
525 }
489 ]
526 ]
490 }
527 }
@@ -509,8 +546,14 @@ Item {
509 }
546 }
510
547
511 function test_NotificationRenderer(data) {
548 function test_NotificationRenderer(data) {
549+ // make sure the clicks on mocked notifications can be checked against by "actionSpy" (mimicking the NotificationServer component)
550+ data.n.actionInvoked.connect(mockModel.actionInvoked)
551+
552+ // hook up notification's completed-signal with model's onCompleted-slot, so that remove() (model) happens on close() (notification)
553+ data.n.completed.connect(mockModel.onCompleted)
554+
512 // populate model with some mock notifications
555 // populate model with some mock notifications
513− mockModel.append(data)
556+ mockModel.append(data.n)
514
557
515 // make sure the view is properly updated before going on
558 // make sure the view is properly updated before going on
516 notifications.forceLayout();
559 notifications.forceLayout();
@@ -520,6 +563,7 @@ Item {
520 verify(notification !== undefined, "notification wasn't found");
563 verify(notification !== undefined, "notification wasn't found");
521
564
522 waitForRendering(notification);
565 waitForRendering(notification);
566+ tryCompare(notification, "height", notification.state === "contracted" ? units.gu(10) : notification.implicitHeight);
523
567
524 var icon = findChild(notification, "icon")
568 var icon = findChild(notification, "icon")
525 var centeredIcon = findChild(notification, "centeredIcon")
569 var centeredIcon = findChild(notification, "centeredIcon")
@@ -548,9 +592,9 @@ Item {
548
592
549 // test input does not fall through
593 // test input does not fall through
550 mouseClick(notification)
594 mouseClick(notification)
551− if(data.type == Notification.Interactive) {
595+ if(data.n.type === Notification.Interactive) {
552 actionSpy.wait()
596 actionSpy.wait()
553− compare(actionSpy.signalArguments[0][0], data.actions[0]["id"], "got wrong id for interactive action")
597+ compare(actionSpy.signalArguments[0][0], data.n.actions.data(0, ActionModel.RoleActionId), "got wrong id for interactive action")
554 }
598 }
555 compare(clickThroughSpy.count, 0, "click on notification fell through")
599 compare(clickThroughSpy.count, 0, "click on notification fell through")
556
600
@@ -559,17 +603,26 @@ Item {
559 compare(bodyLabel.visible, data.bodyVisible, "body-text visibility is incorrect")
603 compare(bodyLabel.visible, data.bodyVisible, "body-text visibility is incorrect")
560 compare(buttonRow.visible, data.buttonRowVisible, "button visibility is incorrect")
604 compare(buttonRow.visible, data.buttonRowVisible, "button visibility is incorrect")
561
605
606+ // After clicking the state of notifications can change so let's wait
607+ // for their height animations to finish before continuing
608+ for (var i = 0; i < mockModel.count; ++i) {
609+ var n = findChild(notifications, "notification" + i)
610+ tryCompare(n, "height", n.state === "contracted" ? units.gu(10) : n.implicitHeight);
611+ }
612+
613+ if (data.hasSound) {
562− var audioItem = findInvisibleChild(notification, "sound")
614+ var audioItem = findInvisibleChild(notification, "sound")
563− compare(audioItem.playbackState, data.hasSound ? Audio.PlayingState : Audio.StoppedState, "Audio has wrong state")
615+ compare(audioItem.playbackState, data.hasSound ? Audio.PlayingState : Audio.StoppedState, "Audio has wrong state")
616+ }
564
617
565 if(data.buttonRowVisible) {
618 if(data.buttonRowVisible) {
566 var buttonCancel = findChild(buttonRow, "notify_button1")
619 var buttonCancel = findChild(buttonRow, "notify_button1")
567 var buttonAccept = findChild(buttonRow, "notify_button0")
620 var buttonAccept = findChild(buttonRow, "notify_button0")
568
621
569 // only test the left/cancel-button if two actions have been passed in
622 // only test the left/cancel-button if two actions have been passed in
570− if (data.actions.length == 2) {
623+ if (data.n.actions.count === 2) {
571 tryCompareFunction(function() { mouseClick(buttonCancel); return actionSpy.signalArguments.length > 0; }, true);
624 tryCompareFunction(function() { mouseClick(buttonCancel); return actionSpy.signalArguments.length > 0; }, true);
572− compare(actionSpy.signalArguments[0][0], data.actions[1]["id"], "got wrong id for negative action")
625+ compare(actionSpy.signalArguments[0][0], data.n.actions.data(1, ActionModel.RoleActionId), "got wrong id for negative action")
573 actionSpy.clear()
626 actionSpy.clear()
574 }
627 }
575
628
@@ -578,36 +631,49 @@ Item {
578
631
579 // click the positive/right button
632 // click the positive/right button
580 tryCompareFunction(function() { mouseClick(buttonAccept); return actionSpy.signalArguments.length > 0; }, true);
633 tryCompareFunction(function() { mouseClick(buttonAccept); return actionSpy.signalArguments.length > 0; }, true);
581− compare(actionSpy.signalArguments[0][0], data.actions[0]["id"], "got wrong id positive action")
634+ compare(actionSpy.signalArguments[0][0], data.n.actions.data(0, ActionModel.RoleActionId), "got wrong id positive action")
582 actionSpy.clear()
635 actionSpy.clear()
583− waitForRendering (notification)
584
636
585 // check if there's a ComboButton created due to more actions being passed
637 // check if there's a ComboButton created due to more actions being passed
586− if (data.actions.length > 2) {
638+ if (data.n.actions.count > 3) {
587 var comboButton = findChild(notification, "notify_button2")
639 var comboButton = findChild(notification, "notify_button2")
588− tryCompareFunction(function() { return comboButton.expanded == false; }, true);
640+ tryCompareFunction(function() { return comboButton.expanded === false; }, true);
589
641
590 // click to expand
642 // click to expand
591− tryCompareFunction(function() { mouseClick(comboButton, comboButton.width - comboButton.__styleInstance.dropDownWidth / 2, comboButton.height / 2); return comboButton.expanded == true; }, true);
643+ tryCompareFunction(function() { mouseClick(comboButton, comboButton.width / 2, comboButton.height / 2); return comboButton.expanded === true; }, true);
592
644
593 // try clicking on choices in expanded comboList
645 // try clicking on choices in expanded comboList
594 var choiceButton1 = findChild(notification, "notify_button3")
646 var choiceButton1 = findChild(notification, "notify_button3")
595 tryCompareFunction(function() { mouseClick(choiceButton1); return actionSpy.signalArguments.length > 0; }, true);
647 tryCompareFunction(function() { mouseClick(choiceButton1); return actionSpy.signalArguments.length > 0; }, true);
596− compare(actionSpy.signalArguments[0][0], data.actions[3]["id"], "got wrong id choice action 1")
648+ compare(actionSpy.signalArguments[0][0], data.n.actions.data(3, ActionModel.RoleActionId), "got wrong id choice action 1")
597 actionSpy.clear()
649 actionSpy.clear()
598
650
599 var choiceButton2 = findChild(notification, "notify_button4")
651 var choiceButton2 = findChild(notification, "notify_button4")
600 tryCompareFunction(function() { mouseClick(choiceButton2); return actionSpy.signalArguments.length > 0; }, true);
652 tryCompareFunction(function() { mouseClick(choiceButton2); return actionSpy.signalArguments.length > 0; }, true);
601− compare(actionSpy.signalArguments[0][0], data.actions[4]["id"], "got wrong id choice action 2")
653+ compare(actionSpy.signalArguments[0][0], data.n.actions.data(4, ActionModel.RoleActionId), "got wrong id choice action 2")
602 actionSpy.clear()
654 actionSpy.clear()
603
655
604 // click to collapse
656 // click to collapse
605− //tryCompareFunction(function() { mouseClick(comboButton, comboButton.width - comboButton.__styleInstance.dropDownWidth / 2, comboButton.height / 2); return comboButton.expanded == false; }, true);
657+ tryCompareFunction(function() { mouseClick(comboButton, comboButton.width / 2, comboButton.height / 2); return comboButton.expanded == false; }, true);
606 } else {
658 } else {
607 mouseClick(buttonCancel)
659 mouseClick(buttonCancel)
608− compare(actionSpy.signalArguments[0][0], data.actions[1]["id"], "got wrong id for negative action")
660+ compare(actionSpy.signalArguments[0][0], data.n.actions.data(1, ActionModel.RoleActionId), "got wrong id for negative action")
609 }
661 }
610 }
662 }
663+
664+ // swipe-to-dismiss check
665+ waitForRendering(notification)
666+ var before = mockModel.count
667+ var dragStart = notification.width * 0.25;
668+ var dragEnd = notification.width;
669+ var dragY = notification.height / 2;
670+ touchFlick(notification, dragStart, dragY, dragEnd, dragY)
671+ waitForRendering(notification)
672+ if ((data.n.type === Notification.SnapDecision && notification.state === "expanded") || data.n.type === Notification.Confirmation) {
673+ tryCompare(mockModel, "count", before)
674+ } else {
675+ tryCompare(mockModel, "count", before - 1)
676+ }
611 }
677 }
612 }
678 }
613 }
679 }
tests/qmltests/Notifications/tst_OptionToggle.qml+11−6
@@ -1,17 +1,20 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright 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 Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
6 * the Free Software Foundation; version 3.
7 *
7 *
8 * This program is distributed in the hope that it will be useful,
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
11+ * GNU Lesser General Public License for more details.
12 *
12 *
13− * You should have received a copy of the GNU General Public License
13+ * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Mirco Mueller <mirco.mueller@canonical.com>
15 */
18 */
16
19
17import QtQuick 2.0
20import QtQuick 2.0
@@ -235,8 +238,10 @@ Row {
235 compare(bodyLabel.visible, data.bodyVisible, "body-text visibility is incorrect")
238 compare(bodyLabel.visible, data.bodyVisible, "body-text visibility is incorrect")
236 compare(buttonRow.visible, data.buttonRowVisible, "button visibility is incorrect")
239 compare(buttonRow.visible, data.buttonRowVisible, "button visibility is incorrect")
237
240
241+ if (data.hasSound) {
238− var audioItem = findInvisibleChild(notification, "sound")
242+ var audioItem = findInvisibleChild(notification, "sound")
239− compare(audioItem.playbackState, data.hasSound ? Audio.PlayingState : Audio.StoppedState, "Audio has wrong state")
243+ compare(audioItem.playbackState, data.hasSound ? Audio.PlayingState : Audio.StoppedState, "Audio has wrong state")
244+ }
240
245
241 if(data.buttonRowVisible) {
246 if(data.buttonRowVisible) {
242 var buttonCancel = findChild(buttonRow, "notify_button1")
247 var buttonCancel = findChild(buttonRow, "notify_button1")
tests/qmltests/Notifications/tst_SwipeToAct.qml+4
@@ -218,6 +218,10 @@ Item {
218 // populate model with some mock notifications
218 // populate model with some mock notifications
219 mockModel.append(data)
219 mockModel.append(data)
220
220
221+ // add actions to action-model to test against
222+ myActionModel.append("ok_id", "Ok")
223+ myActionModel.append("cancel_id", "Cancel")
224+
221 // make sure the view is properly updated before going on
225 // make sure the view is properly updated before going on
222 notifications.forceLayout();
226 notifications.forceLayout();
223 waitForRendering(notifications);
227 waitForRendering(notifications);
tests/qmltests/Panel/tst_Panel.qml+33−2
@@ -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 General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -41,7 +41,12 @@ IndicatorTest {
41 Layout.fillHeight: true
41 Layout.fillHeight: true
42
42
43 id: itemArea
43 id: itemArea
44− color: "blue"
44+ color: backgroundMouseArea.pressed ? "red" : "blue"
45+
46+ MouseArea {
47+ id: backgroundMouseArea
48+ anchors.fill: parent
49+ }
45
50
46 Panel {
51 Panel {
47 id: panel
52 id: panel
@@ -128,6 +133,12 @@ IndicatorTest {
128 name: "Panel"
133 name: "Panel"
129 when: windowShown
134 when: windowShown
130
135
136+ SignalSpy {
137+ id: backgroundPressedSpy
138+ target: backgroundMouseArea
139+ signalName: "pressedChanged"
140+ }
141+
131 function init() {
142 function init() {
132 panel.fullscreenMode = false;
143 panel.fullscreenMode = false;
133 callManager.foregroundCall = null;
144 callManager.foregroundCall = null;
@@ -140,6 +151,9 @@ IndicatorTest {
140 // (switches between normal and fullscreen modes are animated)
151 // (switches between normal and fullscreen modes are animated)
141 var indicatorArea = findChild(panel, "indicatorArea");
152 var indicatorArea = findChild(panel, "indicatorArea");
142 tryCompare(indicatorArea, "y", 0);
153 tryCompare(indicatorArea, "y", 0);
154+
155+ backgroundPressedSpy.clear();
156+ compare(backgroundPressedSpy.valid, true);
143 }
157 }
144
158
145 function get_indicator_item(index) {
159 function get_indicator_item(index) {
@@ -299,5 +313,22 @@ IndicatorTest {
299
313
300 touchRelease(panel, mappedPosition.x, panel.minimizedPanelHeight / 2);
314 touchRelease(panel, mappedPosition.x, panel.minimizedPanelHeight / 2);
301 }
315 }
316+
317+ /* Checks that no input reaches items behind the indicator bar.
318+ Ie., the indicator bar should eat all input events that hit it.
319+ */
320+ function test_indicatorBarEatsAllEvents() {
321+ // Perform several taps throughout the length of the indicator bar to ensure
322+ // that it doesn't have a "weak spot" from where taps pass through.
323+ var numTaps = 5;
324+ var stepLength = (panel.width / (numTaps + 1));
325+ var tapY = panel.indicators.minimizedPanelHeight / 2;
326+ for (var i = 1; i <= numTaps; ++i) {
327+ tap(panel, stepLength * i, tapY);
328+ tryCompare(panel.indicators, "fullyClosed", true);
329+ }
330+
331+ compare(backgroundPressedSpy.count, 0);
332+ }
302 }
333 }
303}
334}
tests/qmltests/Stages/ApplicationCheckBox.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.0
18+import QtQuick.Layouts 1.1
19+import Ubuntu.Components 1.1
20+import Unity.Application 0.1
21+
22+RowLayout {
23+ id: root
24+ property string appId
25+ property alias checked: checkbox.checked
26+
27+ Layout.fillWidth: true
28+ CheckBox {
29+ id: checkbox
30+ checked: false
31+ activeFocusOnPress: false
32+ onCheckedChanged: {
33+ if (checked) {
34+ ApplicationManager.startApplication(root.appId);
35+ } else {
36+ ApplicationManager.stopApplication(root.appId);
37+ }
38+ }
39+ }
40+ Label {
41+ text: root.appId
42+ color: "white"
43+ anchors.verticalCenter: parent.verticalCenter
44+ }
45+}
tests/qmltests/Stages/RecursingChildSessionControl.qml+3
@@ -89,6 +89,7 @@ ColumnLayout {
89 CheckBox {
89 CheckBox {
90 id: _surfaceCheckbox;
90 id: _surfaceCheckbox;
91 checked: false;
91 checked: false;
92+ activeFocusOnPress: false
92 enabled: root.session
93 enabled: root.session
93 onCheckedChanged: {
94 onCheckedChanged: {
94 if (checked) {
95 if (checked) {
@@ -117,6 +118,7 @@ ColumnLayout {
117
118
118 Button {
119 Button {
119 enabled: root.session
120 enabled: root.session
121+ activeFocusOnPress: false
120 text: removable ? "Remove" : "Release"
122 text: removable ? "Remove" : "Release"
121 onClicked: {
123 onClicked: {
122 if (removable) {
124 if (removable) {
@@ -131,6 +133,7 @@ ColumnLayout {
131
133
132 Button {
134 Button {
133 enabled: root.session !== null
135 enabled: root.session !== null
136+ activeFocusOnPress: false
134 text: "Add Child"
137 text: "Add Child"
135 onClicked: {
138 onClicked: {
136 var screenshot = Math.round(Math.random()*100 % (screenshotIds.length-1));
139 var screenshot = Math.round(Math.random()*100 % (screenshotIds.length-1));
tests/qmltests/Stages/tst_ApplicationWindow.qml+6−1
@@ -52,10 +52,12 @@ Rectangle {
52 application: fakeApplication
52 application: fakeApplication
53 orientation: Qt.PortraitOrientation
53 orientation: Qt.PortraitOrientation
54 interactive: true
54 interactive: true
55+ focus: true
55 }
56 }
56 }
57 }
57 Loader {
58 Loader {
58 id: applicationWindowLoader
59 id: applicationWindowLoader
60+ focus: true
59 anchors {
61 anchors {
60 top: parent.top
62 top: parent.top
61 bottom: parent.bottom
63 bottom: parent.bottom
@@ -83,6 +85,7 @@ Rectangle {
83
85
84 CheckBox {
86 CheckBox {
85 id: sessionCheckbox; checked: false
87 id: sessionCheckbox; checked: false
88+ activeFocusOnPress: false
86 onCheckedChanged: {
89 onCheckedChanged: {
87 if (applicationWindowLoader.status !== Loader.Ready)
90 if (applicationWindowLoader.status !== Loader.Ready)
88 return;
91 return;
@@ -121,6 +124,7 @@ Rectangle {
121
124
122 ListItem.ItemSelector {
125 ListItem.ItemSelector {
123 id: appStateSelector
126 id: appStateSelector
127+ activeFocusOnPress: false
124 anchors { left: parent.left; right: parent.right }
128 anchors { left: parent.left; right: parent.right }
125 text: "Application state"
129 text: "Application state"
126 model: ["Starting",
130 model: ["Starting",
@@ -146,6 +150,7 @@ Rectangle {
146
150
147 Button {
151 Button {
148 anchors { left: parent.left; right: parent.right }
152 anchors { left: parent.left; right: parent.right }
153+ activeFocusOnPress: false
149 text: "Rotate device \u27F3"
154 text: "Rotate device \u27F3"
150 onClicked: {
155 onClicked: {
151 var orientation = applicationWindowLoader.item.orientation
156 var orientation = applicationWindowLoader.item.orientation
@@ -398,7 +403,7 @@ Rectangle {
398 verify(stateGroup.state === "void");
403 verify(stateGroup.state === "void");
399 }
404 }
400
405
401− function test_forceActiveFocusFollowsInterative() {
406+ function test_surfaceActiveFocusFollowsAppWindowInterative() {
402 fakeApplication.createSession();
407 fakeApplication.createSession();
403 applicationWindowLoader.item.interactive = false;
408 applicationWindowLoader.item.interactive = false;
404 applicationWindowLoader.item.interactive = true;
409 applicationWindowLoader.item.interactive = true;
tests/qmltests/Stages/tst_PhoneStage.qml+4
@@ -29,6 +29,7 @@ Item {
29 PhoneStage {
29 PhoneStage {
30 id: phoneStage
30 id: phoneStage
31 anchors { fill: parent; rightMargin: units.gu(30) }
31 anchors { fill: parent; rightMargin: units.gu(30) }
32+ focus: true
32 dragAreaWidth: units.gu(2)
33 dragAreaWidth: units.gu(2)
33 maximizedAppTopMargin: units.gu(3) + units.dp(2)
34 maximizedAppTopMargin: units.gu(3) + units.dp(2)
34 interactive: true
35 interactive: true
@@ -51,6 +52,7 @@ Item {
51 Button {
52 Button {
52 anchors { left: parent.left; right: parent.right }
53 anchors { left: parent.left; right: parent.right }
53 text: "Add App"
54 text: "Add App"
55+ activeFocusOnPress: false
54 onClicked: {
56 onClicked: {
55 testCase.addApps();
57 testCase.addApps();
56 }
58 }
@@ -58,6 +60,7 @@ Item {
58 Button {
60 Button {
59 anchors { left: parent.left; right: parent.right }
61 anchors { left: parent.left; right: parent.right }
60 text: "Remove Selected"
62 text: "Remove Selected"
63+ activeFocusOnPress: false
61 onClicked: {
64 onClicked: {
62 ApplicationManager.stopApplication(ApplicationManager.get(appList.selectedAppIndex).appId);
65 ApplicationManager.stopApplication(ApplicationManager.get(appList.selectedAppIndex).appId);
63 }
66 }
@@ -65,6 +68,7 @@ Item {
65 Button {
68 Button {
66 anchors { left: parent.left; right: parent.right }
69 anchors { left: parent.left; right: parent.right }
67 text: "Stop Selected"
70 text: "Stop Selected"
71+ activeFocusOnPress: false
68 onClicked: {
72 onClicked: {
69 ApplicationManager.get(appList.selectedAppIndex).setState(ApplicationInfoInterface.Stopped);
73 ApplicationManager.get(appList.selectedAppIndex).setState(ApplicationInfoInterface.Stopped);
70 }
74 }
tests/qmltests/Stages/tst_SessionContainer.qml+14−7
@@ -42,11 +42,14 @@ Rectangle {
42 id: sessionContainer
42 id: sessionContainer
43 anchors.fill: parent
43 anchors.fill: parent
44 orientation: Qt.PortraitOrientation
44 orientation: Qt.PortraitOrientation
45+ focus: true
46+ interactive: true
45 }
47 }
46 }
48 }
47
49
48 Loader {
50 Loader {
49 id: sessionContainerLoader
51 id: sessionContainerLoader
52+ focus: true
50 anchors {
53 anchors {
51 top: parent.top
54 top: parent.top
52 bottom: parent.bottom
55 bottom: parent.bottom
@@ -75,6 +78,7 @@ Rectangle {
75 CheckBox {
78 CheckBox {
76 id: sessionCheckbox;
79 id: sessionCheckbox;
77 checked: false;
80 checked: false;
81+ activeFocusOnPress: false
78 onCheckedChanged: {
82 onCheckedChanged: {
79 if (sessionContainerLoader.status !== Loader.Ready)
83 if (sessionContainerLoader.status !== Loader.Ready)
80 return;
84 return;
@@ -190,8 +194,6 @@ Rectangle {
190 var sessionContainer = sessionContainerLoader.item;
194 var sessionContainer = sessionContainerLoader.item;
191 compare(sessionContainer.childSessions.count(), 0);
195 compare(sessionContainer.childSessions.count(), 0);
192
196
193− sessionContainer.interactive = true;
194−
195 var i;
197 var i;
196 var sessions = [];
198 var sessions = [];
197 // 3 sessions should cover all edge cases
199 // 3 sessions should cover all edge cases
@@ -209,14 +211,19 @@ Rectangle {
209
211
210 var a_session;
212 var a_session;
211 while(a_session = sessions.pop()) {
213 while(a_session = sessions.pop()) {
212− a_session.surface.forceActiveFocus();
213 compare(a_session.surface.activeFocus, true);
214 compare(a_session.surface.activeFocus, true);
214
215
215− var parentSession = a_session.parentSession;
216+ ApplicationTest.removeSurface(a_session.surface);
216− sessionContainer.session.removeChildSession(a_session);
217+ ApplicationTest.removeSession(a_session);
217− compare(a_session.surface.activeFocus, false);
218
218
219− compare(parentSession.surface.activeFocus, true);
219+ if (sessions.length > 0) {
220+ // active focus should have gone to the yongest remaining sibling
221+ var previousSiblingSurface = sessions[sessions.length - 1].surface
222+ tryCompare(previousSiblingSurface, "activeFocus", true);
223+ } else {
224+ // active focus should have gone to the parent surface
225+ tryCompare(sessionContainer.session.surface, "activeFocus", true);
226+ }
220 }
227 }
221 }
228 }
222
229
tests/qmltests/Stages/tst_TabletStage.qmlnew file+154
@@ -0,0 +1,154 @@
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 QtTest 1.0
19+import Ubuntu.Components 1.1
20+import Ubuntu.Components.ListItems 1.0 as ListItem
21+import Unity.Application 0.1
22+import Unity.Test 0.1
23+
24+import "../../../qml/Stages"
25+
26+Rectangle {
27+ id: root
28+ color: "grey"
29+ width: tabletStageLoader.width + controls.width
30+ height: tabletStageLoader.height
31+
32+ Loader {
33+ id: tabletStageLoader
34+
35+ x: ((root.width - controls.width) - width) / 2
36+ y: (root.height - height) / 2
37+ width: units.gu(160*0.7)
38+ height: units.gu(100*0.7)
39+
40+ focus: true
41+
42+ property bool itemDestroyed: false
43+ sourceComponent: Component {
44+ TabletStage {
45+ anchors.fill: parent
46+ Component.onDestruction: {
47+ tabletStageLoader.itemDestroyed = true;
48+ }
49+ dragAreaWidth: units.gu(2)
50+ maximizedAppTopMargin: units.gu(3) + units.dp(2)
51+ interactive: true
52+ focus: true
53+ }
54+ }
55+ }
56+
57+ Rectangle {
58+ id: controls
59+ color: "darkgrey"
60+ width: units.gu(30)
61+ anchors {
62+ top: parent.top
63+ bottom: parent.bottom
64+ right: parent.right
65+ }
66+
67+ Column {
68+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
69+ spacing: units.gu(1)
70+ ApplicationCheckBox {
71+ id: webbrowserCheckBox
72+ appId: "webbrowser-app"
73+ }
74+ ApplicationCheckBox {
75+ id: dialerCheckBox
76+ appId: "dialer-app"
77+ }
78+ }
79+ }
80+
81+ UnityTestCase {
82+ id: testCase
83+ name: "TabletStage"
84+ when: windowShown
85+
86+ property Item tabletStage: tabletStageLoader.status === Loader.Ready ? tabletStageLoader.item : null
87+
88+ function init() {
89+ tabletStageLoader.active = true;
90+ tryCompare(tabletStageLoader, "status", Loader.Ready);
91+ }
92+
93+ function cleanup() {
94+ tabletStageLoader.itemDestroyed = false;
95+ tabletStageLoader.active = false;
96+
97+ tryCompare(tabletStageLoader, "status", Loader.Null);
98+ tryCompare(tabletStageLoader, "item", null);
99+ // Loader.status might be Loader.Null and Loader.item might be null but the Loader
100+ // actually took place. Likely because Loader waits until the next event loop
101+ // iteration to do its work. So to ensure the reload, we will wait until the
102+ // Shell instance gets destroyed.
103+ tryCompare(tabletStageLoader, "itemDestroyed", true);
104+
105+ // kill all (fake) running apps
106+ webbrowserCheckBox.checked = false;
107+ dialerCheckBox.checked = false;
108+ }
109+
110+ function waitUntilAppSurfaceShowsUp(appId) {
111+ var appWindow = findChild(tabletStage, "appWindow_" + appId);
112+ verify(appWindow);
113+ var appWindowStates = findInvisibleChild(appWindow, "applicationWindowStateGroup");
114+ verify(appWindowStates);
115+ tryCompare(appWindowStates, "state", "surface");
116+ }
117+
118+ function test_tappingSwitchesFocusBetweenStages() {
119+ webbrowserCheckBox.checked = true;
120+ waitUntilAppSurfaceShowsUp(webbrowserCheckBox.appId);
121+ var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
122+ compare(webbrowserApp.stage, ApplicationInfoInterface.MainStage);
123+ tryCompare(webbrowserApp.session.surface, "activeFocus", true);
124+
125+ dialerCheckBox.checked = true;
126+ waitUntilAppSurfaceShowsUp(dialerCheckBox.appId);
127+ var dialerApp = ApplicationManager.findApplication(dialerCheckBox.appId);
128+ compare(dialerApp.stage, ApplicationInfoInterface.SideStage);
129+ tryCompare(dialerApp.session.surface, "activeFocus", true);
130+ tryCompare(webbrowserApp.session.surface, "activeFocus", false);
131+
132+ // Tap on the main stage application and check if the focus
133+ // has been passed to it.
134+
135+ var webbrowserWindow = findChild(tabletStage, "appWindow_" + webbrowserApp.appId);
136+ verify(webbrowserWindow);
137+ tap(webbrowserWindow);
138+
139+ tryCompare(dialerApp.session.surface, "activeFocus", false);
140+ tryCompare(webbrowserApp.session.surface, "activeFocus", true);
141+
142+ // Now tap on the side stage application and check if the focus
143+ // has been passed back to it.
144+
145+ var dialerWindow = findChild(tabletStage, "appWindow_" + dialerApp.appId);
146+ verify(dialerWindow);
147+ tap(dialerWindow);
148+
149+ tryCompare(dialerApp.session.surface, "activeFocus", true);
150+ tryCompare(webbrowserApp.session.surface, "activeFocus", false);
151+ }
152+ }
153+
154+}
tests/qmltests/Stages/tst_WindowMoveResizeArea.qml+117−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 General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -29,20 +29,46 @@ Item {
29 height: units.gu(60)
29 height: units.gu(60)
30 width: units.gu(60)
30 width: units.gu(60)
31
31
32− Rectangle {
32+ property var fakeWindow: windowLoader.item
33+
34+ Component {
35+ id: fakeWindowComponent
36+
37+ Item {
33− id: fakeWindow
38+ id: fakeWindow
39+ property alias minWidth: moveResizeArea.minWidth
40+ property alias minHeight: moveResizeArea.minHeight
41+ x: units.gu(20)
42+ y: units.gu(20)
34− height: units.gu(20)
43+ height: units.gu(20)
35− width: units.gu(20)
44+ width: units.gu(20)
36− color: "khaki"
45+
37− WindowMoveResizeArea {
46+ WindowMoveResizeArea {
38− id: moveResizeArea
47+ id: moveResizeArea
39− target: fakeWindow
48+ target: fakeWindow
40− resizeHandleWidth: units.gu(0.5)
49+ resizeHandleWidth: units.gu(0.5)
41− minWidth: units.gu(15)
50+ minWidth: units.gu(15)
42− minHeight: units.gu(10)
51+ minHeight: units.gu(10)
52+ windowId: "test-window-id"
53+ }
54+
55+ Rectangle {
56+ anchors.fill: moveResizeArea
57+ color: "red"
58+ }
59+
60+ Rectangle {
61+ anchors.fill: fakeWindow
62+ color: "blue"
63+ }
43 }
64 }
44 }
65 }
45
66
67+ Loader {
68+ id: windowLoader
69+ sourceComponent: fakeWindowComponent
70+ }
71+
46 UT.UnityTestCase {
72 UT.UnityTestCase {
47 when: windowShown
73 when: windowShown
48
74
@@ -100,8 +126,8 @@ Item {
100 var startDragY = initialWindowY + initialWindowHeight + 1
126 var startDragY = initialWindowY + initialWindowHeight + 1
101 mouseFlick(root, startDragX, startDragY, startDragX + data.dx, startDragY + data.dy, true, true, units.gu(.5), 10);
127 mouseFlick(root, startDragX, startDragY, startDragX + data.dx, startDragY + data.dy, true, true, units.gu(.5), 10);
102
128
103− tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + data.dx, moveResizeArea.minWidth));
129+ tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + data.dx, fakeWindow.minWidth));
104− tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + data.dy, moveResizeArea.minHeight));
130+ tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + data.dy, fakeWindow.minHeight));
105
131
106 compare(fakeWindow.x, initialWindowX);
132 compare(fakeWindow.x, initialWindowX);
107 compare(fakeWindow.y, initialWindowY);
133 compare(fakeWindow.y, initialWindowY);
@@ -126,13 +152,86 @@ Item {
126 var startDragY = initialWindowY - 1
152 var startDragY = initialWindowY - 1
127 mouseFlick(root, startDragX, startDragY, startDragX + data.dx, startDragY + data.dy, true, true, units.gu(.5), 10);
153 mouseFlick(root, startDragX, startDragY, startDragX + data.dx, startDragY + data.dy, true, true, units.gu(.5), 10);
128
154
129− tryCompare(fakeWindow, "width", Math.max(initialWindowWidth - data.dx, moveResizeArea.minWidth));
155+ tryCompare(fakeWindow, "width", Math.max(initialWindowWidth - data.dx, fakeWindow.minWidth));
130− tryCompare(fakeWindow, "height", Math.max(initialWindowHeight - data.dy, moveResizeArea.minHeight));
156+ tryCompare(fakeWindow, "height", Math.max(initialWindowHeight - data.dy, fakeWindow.minHeight));
131
157
132− var maxMoveX = initialWindowWidth - moveResizeArea.minWidth;
158+ var maxMoveX = initialWindowWidth - fakeWindow.minWidth;
133− var maxMoveY = initialWindowHeight - moveResizeArea.minHeight;
159+ var maxMoveY = initialWindowHeight - fakeWindow.minHeight;
134 compare(fakeWindow.x, Math.min(initialWindowX + data.dx, initialWindowX + maxMoveX));
160 compare(fakeWindow.x, Math.min(initialWindowX + data.dx, initialWindowX + maxMoveX));
135 compare(fakeWindow.y, Math.min(initialWindowY + data.dy, initialWindowY + maxMoveY));
161 compare(fakeWindow.y, Math.min(initialWindowY + data.dy, initialWindowY + maxMoveY));
136 }
162 }
163+
164+ function test_saveRestorePosition() {
165+ var initialWindowX = fakeWindow.x;
166+ var initialWindowY = fakeWindow.y;
167+ var initialWindowWidth = fakeWindow.width;
168+ var initialWindowHeight = fakeWindow.height;
169+
170+ var moveDelta = units.gu(5);
171+ var startDragX = initialWindowX + fakeWindow.width / 2;
172+ var startDragY = initialWindowY + fakeWindow.height / 2;
173+ mouseFlick(root, startDragX, startDragY, startDragX + moveDelta, startDragY + moveDelta, true, true, units.gu(.5), 10)
174+
175+ tryCompare(fakeWindow, "x", initialWindowX + moveDelta)
176+ tryCompare(fakeWindow, "y", initialWindowX + moveDelta)
177+
178+ // This will destroy the window and recreate it
179+ windowLoader.active = false;
180+ waitForRendering(root);
181+ windowLoader.active = true;
182+
183+ // Make sure it's again where we left it before destroying
184+ tryCompare(fakeWindow, "x", initialWindowX + moveDelta)
185+ tryCompare(fakeWindow, "y", initialWindowX + moveDelta)
186+ }
187+
188+ function test_saveRestoreSize() {
189+ var initialWindowX = fakeWindow.x;
190+ var initialWindowY = fakeWindow.y;
191+ var initialWindowWidth = fakeWindow.width
192+ var initialWindowHeight = fakeWindow.height
193+
194+ var resizeDelta = units.gu(5)
195+ var startDragX = initialWindowX + initialWindowWidth + 1
196+ var startDragY = initialWindowY + initialWindowHeight + 1
197+ mouseFlick(root, startDragX, startDragY, startDragX + resizeDelta, startDragY + resizeDelta, true, true, units.gu(.5), 10);
198+
199+ tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + resizeDelta, fakeWindow.minWidth));
200+ tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + resizeDelta, fakeWindow.minHeight));
201+
202+ // This will destroy the window and recreate it
203+ windowLoader.active = false;
204+ waitForRendering(root);
205+ windowLoader.active = true;
206+
207+ // Make sure its size is again the same as before
208+ tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + resizeDelta, fakeWindow.minWidth));
209+ tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + resizeDelta, fakeWindow.minHeight));
210+ }
211+
212+ // This tests if dragging smaller than minSize and then larger again, will keep the edge sticking
213+ // to the mouse, instead of immediately making the window grow again when switching direction
214+ function test_resizeSmallerAndLarger_data() {
215+ return [
216+ { tag: "topLeft", startX: -1, startY: -1, dx: units.gu(15), dy: units.gu(15) },
217+ { tag: "bottomRight", startX: fakeWindow.width + 1, startY: fakeWindow.height + 1, dx: -units.gu(15), dy: -units.gu(15) }
218+ ]
219+ }
220+
221+ function test_resizeSmallerAndLarger(data) {
222+ var initialWindowX = fakeWindow.x;
223+ var initialWindowY = fakeWindow.y;
224+ var initialWindowWidth = fakeWindow.width
225+ var initialWindowHeight = fakeWindow.height
226+
227+ var startDragX = initialWindowX + data.startX
228+ var startDragY = initialWindowY + data.startY
229+ mouseFlick(root, startDragX, startDragY, startDragX + data.dx, startDragY + data.dy, true, false, units.gu(.05), 10);
230+ tryCompare(fakeWindow, "width", Math.max(initialWindowWidth - Math.abs(data.dx), fakeWindow.minWidth));
231+ tryCompare(fakeWindow, "height", Math.max(initialWindowHeight - Math.abs(data.dy), fakeWindow.minHeight));
232+ mouseFlick(root, startDragX + data.dx, startDragY + data.dy, startDragX, startDragY, false, true, units.gu(.05), 10);
233+ tryCompare(fakeWindow, "width", initialWindowWidth);
234+ tryCompare(fakeWindow, "height", initialWindowHeight);
235+ }
137 }
236 }
138}
237}
tests/qmltests/Tutorial/tst_Tutorial.qmlnew file+348
@@ -0,0 +1,348 @@
1+/*
2+ * Copyright (C) 2013,2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtTest 1.0
19+import AccountsService 0.1
20+import LightDM 0.1 as LightDM
21+import Ubuntu.Components 1.1
22+import Unity.Application 0.1
23+import Unity.Test 0.1 as UT
24+
25+import "../../../qml"
26+
27+Item {
28+ id: root
29+ width: shellLoader.width + buttons.width
30+ height: shellLoader.height
31+
32+ QtObject {
33+ id: applicationArguments
34+
35+ function hasGeometry() {
36+ return false;
37+ }
38+
39+ function width() {
40+ return 0;
41+ }
42+
43+ function height() {
44+ return 0;
45+ }
46+ }
47+
48+ Component.onCompleted: {
49+ // must set the mock mode before loading the Shell
50+ LightDM.Greeter.mockMode = "single-pin";
51+ LightDM.Users.mockMode = "single-pin";
52+ shellLoader.active = true;
53+ }
54+
55+ Row {
56+ spacing: 0
57+ anchors.fill: parent
58+
59+ Loader {
60+ id: shellLoader
61+
62+ active: false
63+ width: units.gu(40)
64+ height: units.gu(71)
65+
66+ property bool itemDestroyed: false
67+ sourceComponent: Component {
68+ Shell {
69+ property string indicatorProfile: "phone"
70+
71+ Component.onDestruction: {
72+ shellLoader.itemDestroyed = true;
73+ }
74+ }
75+ }
76+ }
77+
78+ Rectangle {
79+ id: buttons
80+ color: "white"
81+ width: units.gu(30)
82+ height: shellLoader.height
83+
84+ Column {
85+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
86+ spacing: units.gu(1)
87+ Row {
88+ anchors { left: parent.left; right: parent.right }
89+ Button {
90+ text: "Restart Tutorial"
91+ onClicked: {
92+ if (shellLoader.status !== Loader.Ready)
93+ return;
94+
95+ AccountsService.demoEdges = false;
96+ AccountsService.demoEdges = true;
97+ }
98+ }
99+ }
100+ }
101+ }
102+ }
103+
104+ UT.UnityTestCase {
105+ id: testCase
106+ name: "Tutorial"
107+ when: windowShown
108+
109+ property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
110+ property real halfWidth: shell ? shell.width / 2 : 0
111+ property real halfHeight: shell ? shell.height / 2 : 0
112+
113+ function init() {
114+ tryCompare(shell, "enabled", true); // enabled by greeter when ready
115+ swipeAwayGreeter();
116+ AccountsService.demoEdges = false;
117+ AccountsService.demoEdges = true;
118+ }
119+
120+ function cleanup() {
121+ shellLoader.itemDestroyed = false;
122+
123+ shellLoader.active = false;
124+
125+ tryCompare(shellLoader, "status", Loader.Null);
126+ tryCompare(shellLoader, "item", null);
127+ // Loader.status might be Loader.Null and Loader.item might be null but the Loader
128+ // item might still be alive. So if we set Loader.active back to true
129+ // again right now we will get the very same Shell instance back. So no reload
130+ // actually took place. Likely because Loader waits until the next event loop
131+ // iteration to do its work. So to ensure the reload, we will wait until the
132+ // Shell instance gets destroyed.
133+ tryCompare(shellLoader, "itemDestroyed", true);
134+
135+ // kill all (fake) running apps
136+ killApps();
137+
138+ // reload our test subject to get it in a fresh state once again
139+ shellLoader.active = true;
140+
141+ tryCompare(shellLoader, "status", Loader.Ready);
142+ removeTimeConstraintsFromDirectionalDragAreas(shellLoader.item);
143+ }
144+
145+ function killApps() {
146+ while (ApplicationManager.count > 1) {
147+ var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
148+ ApplicationManager.stopApplication(ApplicationManager.get(appIndex).appId);
149+ }
150+ compare(ApplicationManager.count, 1)
151+ }
152+
153+ function swipeAwayGreeter() {
154+ var greeter = findChild(shell, "greeter");
155+ tryCompare(greeter, "showProgress", 1);
156+
157+ touchFlick(shell, halfWidth, halfHeight, shell.width, halfHeight);
158+
159+ // wait until the animation has finished
160+ tryCompare(greeter, "showProgress", 0);
161+ waitForRendering(greeter);
162+ }
163+
164+ function waitForPage(name) {
165+ waitForRendering(findChild(shell, name));
166+ var page = findChild(shell, name);
167+ tryCompare(page, "shown", true);
168+ tryCompare(page.showAnimation, "running", false);
169+ return page;
170+ }
171+
172+ function checkTopEdge() {
173+ touchFlick(shell, halfWidth, 0, halfWidth, halfHeight);
174+
175+ var panel = findChild(shell, "panel");
176+ tryCompare(panel.indicators, "fullyClosed", true);
177+ }
178+
179+ function checkLeftEdge() {
180+ touchFlick(shell, 0, halfHeight, halfWidth, halfHeight);
181+
182+ var launcher = findChild(shell, "launcher");
183+ tryCompare(launcher, "state", "");
184+ }
185+
186+ function checkRightEdge() {
187+ touchFlick(shell, shell.width, halfHeight, halfWidth, halfHeight);
188+
189+ var stage = findChild(shell, "stage");
190+ var spreadView = findChild(stage, "spreadView");
191+ tryCompare(spreadView, "phase", 0);
192+ }
193+
194+ function checkBottomEdge() {
195+ // Can't actually check effect of swipe, since dash isn't really loaded
196+ var applicationsDisplayLoader = findChild(shell, "applicationsDisplayLoader");
197+ tryCompare(applicationsDisplayLoader.item, "interactive", false);
198+ }
199+
200+ function checkFinished() {
201+ tryCompare(AccountsService, "demoEdges", false);
202+
203+ var tutorial = findChild(shell, "tutorial");
204+ tryCompare(tutorial, "running", false);
205+
206+ var launcher = findChild(shell, "launcher");
207+ tryCompare(launcher, "shown", false);
208+ }
209+
210+ function goToPage(name) {
211+ var page = waitForPage("tutorialLeft");
212+ checkTopEdge();
213+ checkRightEdge();
214+ checkBottomEdge();
215+ if (name === "tutorialLeft") return page;
216+ touchFlick(shell, 0, halfHeight, halfWidth, halfHeight);
217+
218+ page = waitForPage("tutorialLeftFinish");
219+ if (name === "tutorialLeftFinish") return page;
220+ var tick = findChild(page, "tick");
221+ tap(tick);
222+
223+ page = waitForPage("tutorialRight");
224+ checkTopEdge();
225+ checkLeftEdge();
226+ checkBottomEdge();
227+ if (name === "tutorialRight") return page;
228+ touchFlick(shell, shell.width, halfHeight, halfWidth, halfHeight);
229+ var overlay = findChild(page, "overlay");
230+ tryCompare(overlay, "shown", true);
231+ var tick = findChild(page, "tick");
232+ tap(tick);
233+
234+ var page = waitForPage("tutorialBottom");
235+ checkTopEdge();
236+ checkLeftEdge();
237+ checkRightEdge();
238+ if (name === "tutorialBottom") return page;
239+ touchFlick(shell, halfWidth, shell.height, halfWidth, halfHeight);
240+
241+ var page = waitForPage("tutorialBottomFinish");
242+ checkTopEdge();
243+ checkLeftEdge();
244+ checkRightEdge();
245+ checkBottomEdge();
246+ if (name === "tutorialBottomFinish") return page;
247+ var tick = findChild(page, "tick");
248+ tap(tick);
249+
250+ checkFinished();
251+ return null;
252+ }
253+
254+ function test_walkthrough() {
255+ goToPage(null);
256+ }
257+
258+ function test_launcherShortDrag() {
259+ // goToPage does a normal launcher pull. But here we want to test
260+ // just barely pulling the launcher out and letting go (i.e. not
261+ // triggering the "progress" property of Launcher).
262+
263+ var left = goToPage("tutorialLeft");
264+
265+ // Make sure we don't do anything if we don't pull the launcher
266+ // out much.
267+ var launcher = findChild(shell, "launcher");
268+ touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.4, halfHeight);
269+ tryCompare(launcher, "state", ""); // should remain hidden
270+ tryCompare(left, "shown", true); // and we should still be on left
271+
272+ // Now drag out but not past launcher itself
273+ touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.9, halfHeight);
274+
275+ waitForPage("tutorialLeftFinish");
276+ }
277+
278+ function test_launcherLongDrag() {
279+ // goToPage does a normal launcher pull. But here we want to test
280+ // a full pull across the page.
281+
282+ var left = goToPage("tutorialLeft");
283+
284+ var launcher = findChild(shell, "launcher");
285+ touchFlick(shell, 0, halfHeight, shell.width, halfHeight);
286+
287+ var errorTextLabel = findChild(left, "errorTextLabel");
288+ var errorTitleLabel = findChild(left, "errorTitleLabel");
289+ tryCompare(launcher, "state", ""); // launcher goes away
290+ tryCompare(left, "shown", true); // still on left page
291+ tryCompare(errorTextLabel, "opacity", 1); // show error
292+ tryCompare(errorTitleLabel, "opacity", 1); // show error
293+ }
294+
295+ function test_launcherDragBack() {
296+ // goToPage does a full launcher pull. But here we test pulling
297+ // all the way out, then dragging back into place.
298+
299+ var left = goToPage("tutorialLeft");
300+ touchFlick(shell, 0, halfHeight, halfWidth, halfHeight, true, false);
301+ touchFlick(shell, halfWidth, halfHeight, 0, halfHeight, false, true);
302+
303+ tryCompare(left, "shown", true); // and we should still be on left
304+ }
305+
306+ function test_spread() {
307+ // Unfortunately, most of what we want to test of the spread is
308+ // "did it render correctly?" but that's hard to test. So instead,
309+ // just poke and prod it a little bit to see if some of the values
310+ // we'd expect to be correct, are so.
311+
312+ var right = goToPage("tutorialRight");
313+ var stage = findChild(right, "stage");
314+ var delegate0 = findChild(right, "appDelegate0");
315+
316+ tryCompare(stage, "dragProgress", 0);
317+ touchFlick(shell, shell.width, halfHeight, shell.width * 0.8, halfHeight, true, false);
318+ verify(stage.dragProgress > 0);
319+ compare(stage.dragProgress, -delegate0.xTranslate);
320+ touchFlick(shell, shell.width * 0.8, halfHeight, shell.width, halfHeight, false, true);
321+ tryCompare(stage, "dragProgress", 0);
322+
323+ tryCompare(delegate0, "x", shell.width);
324+
325+ var screenshotImage = findChild(right, "screenshotImage");
326+ tryCompare(screenshotImage, "source", Qt.resolvedUrl("../../../qml/Tutorial/graphics/facebook.png"));
327+ tryCompare(screenshotImage, "visible", true);
328+ }
329+
330+ function test_bottomShortDrag() {
331+ var bottom = goToPage("tutorialBottom");
332+
333+ touchFlick(shell, halfWidth, shell.height, halfWidth, shell.height * 0.8);
334+
335+ var errorTextLabel = findChild(bottom, "errorTextLabel");
336+ var errorTitleLabel = findChild(bottom, "errorTitleLabel");
337+ tryCompare(bottom, "shown", true); // still on bottom page
338+ tryCompare(errorTextLabel, "opacity", 1); // show error
339+ tryCompare(errorTitleLabel, "opacity", 1); // show error
340+ }
341+
342+ function test_interrupted() {
343+ goToPage("tutorialLeft");
344+ ApplicationManager.startApplication("dialer-app");
345+ checkFinished();
346+ }
347+ }
348+}
tests/qmltests/tst_Shell.qml+62−5
@@ -25,6 +25,7 @@ import Ubuntu.Components 1.1
25import Ubuntu.Telephony 0.1 as Telephony
25import Ubuntu.Telephony 0.1 as Telephony
26import Unity.Application 0.1
26import Unity.Application 0.1
27import Unity.Connectivity 0.1
27import Unity.Connectivity 0.1
28+import Unity.Indicators 0.1
28import Unity.Notifications 1.0
29import Unity.Notifications 1.0
29import Unity.Test 0.1 as UT
30import Unity.Test 0.1 as UT
30import Powerd 0.1
31import Powerd 0.1
@@ -36,6 +37,13 @@ Item {
36 width: units.gu(60)
37 width: units.gu(60)
37 height: units.gu(71)
38 height: units.gu(71)
38
39
40+ Component.onCompleted: {
41+ // must set the mock mode before loading the Shell
42+ LightDM.Greeter.mockMode = "single";
43+ LightDM.Users.mockMode = "single";
44+ shellLoader.active = true;
45+ }
46+
39 QtObject {
47 QtObject {
40 id: applicationArguments
48 id: applicationArguments
41
49
@@ -56,7 +64,9 @@ Item {
56 anchors.fill: parent
64 anchors.fill: parent
57 Loader {
65 Loader {
58 id: shellLoader
66 id: shellLoader
67+ focus: true
59
68
69+ active: false
60 property bool itemDestroyed: false
70 property bool itemDestroyed: false
61 sourceComponent: Component {
71 sourceComponent: Component {
62 Shell {
72 Shell {
@@ -81,6 +91,7 @@ Item {
81 anchors { left: parent.left; right: parent.right }
91 anchors { left: parent.left; right: parent.right }
82 Button {
92 Button {
83 text: "Show Greeter"
93 text: "Show Greeter"
94+ activeFocusOnPress: false
84 onClicked: {
95 onClicked: {
85 if (shellLoader.status !== Loader.Ready)
96 if (shellLoader.status !== Loader.Ready)
86 return;
97 return;
@@ -162,6 +173,7 @@ Item {
162
173
163 function init() {
174 function init() {
164 tryCompare(shell, "enabled", true); // enabled by greeter when ready
175 tryCompare(shell, "enabled", true); // enabled by greeter when ready
176+ shell.indicatorProfile = "phone";
165
177
166 swipeAwayGreeter();
178 swipeAwayGreeter();
167
179
@@ -427,11 +439,11 @@ Item {
427 waitUntilTransitionsEnd(appWindowStateGroup);
439 waitUntilTransitionsEnd(appWindowStateGroup);
428 }
440 }
429
441
430− function test_surfaceLosesFocusWhilePanelIsOpen() {
442+ function test_surfaceLosesActiveFocusWhilePanelIsOpen() {
431 var app = ApplicationManager.startApplication("dialer-app");
443 var app = ApplicationManager.startApplication("dialer-app");
432 waitUntilAppWindowIsFullyLoaded(app);
444 waitUntilAppWindowIsFullyLoaded(app);
433
445
434− tryCompare(app.session.surface, "focus", true);
446+ tryCompare(app.session.surface, "activeFocus", true);
435
447
436 // Drag the indicators panel half-open
448 // Drag the indicators panel half-open
437 var touchX = shell.width / 2;
449 var touchX = shell.width / 2;
@@ -442,7 +454,7 @@ Item {
442 true /* beginTouch */, false /* endTouch */);
454 true /* beginTouch */, false /* endTouch */);
443 verify(indicators.partiallyOpened);
455 verify(indicators.partiallyOpened);
444
456
445− tryCompare(app.session.surface, "focus", false);
457+ tryCompare(app.session.surface, "activeFocus", false);
446
458
447 // And finish getting it open
459 // And finish getting it open
448 touchFlick(indicators,
460 touchFlick(indicators,
@@ -451,11 +463,21 @@ Item {
451 false /* beginTouch */, true /* endTouch */);
463 false /* beginTouch */, true /* endTouch */);
452 tryCompare(indicators, "fullyOpened", true);
464 tryCompare(indicators, "fullyOpened", true);
453
465
454− tryCompare(app.session.surface, "focus", false);
466+ tryCompare(app.session.surface, "activeFocus", false);
455
467
456 dragToCloseIndicatorsPanel();
468 dragToCloseIndicatorsPanel();
457
469
458− tryCompare(app.session.surface, "focus", true);
470+ tryCompare(app.session.surface, "activeFocus", true);
471+ }
472+
473+ function test_launchedAppHasActiveFocus() {
474+ var dialerApp = ApplicationManager.startApplication("dialer-app");
475+ verify(dialerApp);
476+ waitUntilAppSurfaceShowsUp("dialer-app")
477+
478+ verify(dialerApp.session.surface);
479+
480+ tryCompare(dialerApp.session.surface, "activeFocus", true);
459 }
481 }
460
482
461 // Wait for the whole UI to settle down
483 // Wait for the whole UI to settle down
@@ -470,6 +492,14 @@ Item {
470 waitForRendering(shell)
492 waitForRendering(shell)
471 }
493 }
472
494
495+ function waitUntilAppSurfaceShowsUp(appId) {
496+ var appWindow = findChild(shell, "appWindow_" + appId);
497+ verify(appWindow);
498+ var appWindowStates = findInvisibleChild(appWindow, "applicationWindowStateGroup");
499+ verify(appWindowStates);
500+ tryCompare(appWindowStates, "state", "surface");
501+ }
502+
473 function dragToCloseIndicatorsPanel() {
503 function dragToCloseIndicatorsPanel() {
474 var indicators = findChild(shell, "indicators");
504 var indicators = findChild(shell, "indicators");
475
505
@@ -539,6 +569,33 @@ Item {
539 && itemRectInShell.y + itemRectInShell.height <= shell.height;
569 && itemRectInShell.y + itemRectInShell.height <= shell.height;
540 }
570 }
541
571
572+ function test_greeterDoesNotChangeIndicatorProfile() {
573+ var panel = findChild(shell, "panel");
574+ tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
575+
576+ LightDM.Greeter.showGreeter();
577+ tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
578+
579+ LightDM.Greeter.hideGreeter();
580+ tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
581+ }
582+
583+ function test_shellProfileChangesReachIndicators() {
584+ var panel = findChild(shell, "panel");
585+
586+ shell.indicatorProfile = "test1";
587+ for (var i = 0; i < panel.indicators.indicatorsModel.count; ++i) {
588+ var properties = panel.indicators.indicatorsModel.data(i, IndicatorsModelRole.IndicatorProperties);
589+ verify(properties["menuObjectPath"].substr(-5), "test1");
590+ }
591+
592+ shell.indicatorProfile = "test2";
593+ for (var i = 0; i < panel.indicators.indicatorsModel.count; ++i) {
594+ var properties = panel.indicators.indicatorsModel.data(i, IndicatorsModelRole.IndicatorProperties);
595+ verify(properties["menuObjectPath"].substr(-5), "test2");
596+ }
597+ }
598+
542 function test_focusRequestedHidesGreeter() {
599 function test_focusRequestedHidesGreeter() {
543 var greeter = findChild(shell, "greeter");
600 var greeter = findChild(shell, "greeter");
544
601
tests/qmltests/tst_ShellWithPin.qml+72
@@ -33,6 +33,13 @@ Item {
33 width: contentRow.width
33 width: contentRow.width
34 height: contentRow.height
34 height: contentRow.height
35
35
36+ Component.onCompleted: {
37+ // must set the mock mode before loading the Shell
38+ LightDM.Greeter.mockMode = "single-pin";
39+ LightDM.Users.mockMode = "single-pin";
40+ shellLoader.active = true;
41+ }
42+
36 QtObject {
43 QtObject {
37 id: applicationArguments
44 id: applicationArguments
38
45
@@ -54,6 +61,7 @@ Item {
54
61
55 Loader {
62 Loader {
56 id: shellLoader
63 id: shellLoader
64+ active: false
57
65
58 width: units.gu(40)
66 width: units.gu(40)
59 height: units.gu(71)
67 height: units.gu(71)
@@ -224,6 +232,22 @@ Item {
224 tryCompare(ApplicationManager, "focusedApplicationId", app)
232 tryCompare(ApplicationManager, "focusedApplicationId", app)
225 }
233 }
226
234
235+ function test_greeterChangesIndicatorProfile() {
236+ skip("Not supported yet, waiting on design for new settings panel");
237+
238+ var panel = findChild(shell, "panel");
239+ tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile + "_greeter");
240+
241+ LightDM.Greeter.hideGreeter();
242+ tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
243+
244+ LightDM.Greeter.showGreeter();
245+ tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile + "_greeter");
246+
247+ LightDM.Greeter.hideGreeter();
248+ tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
249+ }
250+
227 function test_login() {
251 function test_login() {
228 sessionSpy.clear()
252 sessionSpy.clear()
229 tryCompare(sessionSpy, "count", 0)
253 tryCompare(sessionSpy, "count", 0)
@@ -503,5 +527,53 @@ Item {
503 var lockscreen = findChild(shell, "lockscreen");
527 var lockscreen = findChild(shell, "lockscreen");
504 verify(lockscreen.shown);
528 verify(lockscreen.shown);
505 }
529 }
530+
531+ /*
532+ Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1393447
533+
534+ Do a left edge drag that is long enough to start displacing the greeter
535+ but short engough so that the greeter comes back into place once the
536+ finger is lifted.
537+
538+ In this situation the launcher should remaing fully shown and hide itself
539+ only after its idle timeout is triggered.
540+ */
541+ function test_shortLeftEdgeSwipeMakesLauncherStayVisible() {
542+ var greeter = testCase.findChild(shell, "greeter")
543+ greeter.show();
544+ tryCompare(greeter, "showProgress", 1);
545+
546+ var launcher = testCase.findChild(shell, "launcher")
547+ {
548+ var dismissTimer = testCase.findInvisibleChild(launcher, "dismissTimer");
549+ // effectively disable the dismiss timer
550+ dismissTimer.interval = 24 * 60 * 60 * 1000 // 24 hours
551+ }
552+ var launcherPanel = testCase.findChild(launcher, "launcherPanel");
553+
554+ var toX = shell.width * 0.45;
555+ touchFlick(shell,
556+ 1 /* fromX */, shell.height * 0.5 /* fromY */,
557+ toX /* toX */, shell.height * 0.5 /* toY */,
558+ true /* beginTouch */, false /* endTouch */,
559+ 50, 100);
560+
561+ // Launcher must be fully shown by now
562+ tryCompare(launcherPanel, "x", 0);
563+
564+ // Greeter should be displaced
565+ tryCompareFunction(function() { return greeter.mapToItem(shell, 0, 0).x > shell.width*0.2; }, true);
566+
567+ touchRelease(shell, toX, shell.height * 0.5);
568+
569+ // Upon release the greeter should have slid back into full view
570+ tryCompareFunction(function() { return greeter.mapToItem(shell, 0, 0).x === 0; }, true);
571+
572+ // And the launcher should stay fully shown
573+ for (var i = 0; i < 10; ++i) {
574+ wait(50);
575+ compare(launcherPanel.x, 0);
576+ }
577+ }
506 }
578 }
507}
579}
tests/qmltests/tst_TabletShell.qml+10−2
@@ -32,6 +32,13 @@ Row {
32 id: root
32 id: root
33 spacing: 0
33 spacing: 0
34
34
35+ Component.onCompleted: {
36+ // must set the mock mode before loading the Shell
37+ LightDM.Greeter.mockMode = "full";
38+ LightDM.Users.mockMode = "full";
39+ shellLoader.active = true;
40+ }
41+
35 QtObject {
42 QtObject {
36 id: applicationArguments
43 id: applicationArguments
37
44
@@ -51,6 +58,7 @@ Row {
51 Loader {
58 Loader {
52 id: shellLoader
59 id: shellLoader
53
60
61+ active: false
54 width: units.gu(100)
62 width: units.gu(100)
55 height: units.gu(80)
63 height: units.gu(80)
56
64
@@ -295,8 +303,8 @@ Row {
295 selectUser(data.user)
303 selectUser(data.user)
296
304
297 AccountsService.demoEdges = data.demo
305 AccountsService.demoEdges = data.demo
298− var edgeDemo = findChild(shell, "edgeDemo")
306+ var tutorial = findChild(shell, "tutorial");
299− tryCompare(edgeDemo, "running", data.demo)
307+ tryCompare(tutorial, "running", data.demo);
300
308
301 swipeFromLeftEdge(shell.width * 0.75)
309 swipeFromLeftEdge(shell.width * 0.75)
302 wait(500) // to give time to handle dash() signal from Launcher
310 wait(500) // to give time to handle dash() signal from Launcher
tests/uqmlscene/ActiveFocusLogger.cppnew file+48
@@ -0,0 +1,48 @@
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 "ActiveFocusLogger.h"
18+
19+#include <QDebug>
20+#include <QQuickItem>
21+
22+void ActiveFocusLogger::setWindow(QQuickWindow *window)
23+{
24+ m_window = window;
25+ QObject::connect(window, &QQuickWindow::activeFocusItemChanged,
26+ this, &ActiveFocusLogger::printActiveFocusInfo);
27+}
28+
29+void ActiveFocusLogger::printActiveFocusInfo()
30+{
31+ if (!m_window) {
32+ return;
33+ }
34+
35+ qDebug() << "============== Active focus info START ================";
36+ if (m_window->activeFocusItem()) {
37+ qDebug() << m_window->activeFocusItem();
38+ qDebug() << "Ancestry:";
39+ QQuickItem *item = m_window->activeFocusItem()->parentItem();
40+ while (item != nullptr) {
41+ qDebug() << item << ", isFocusScope =" << item->isFocusScope();
42+ item = item->parentItem();
43+ }
44+ } else {
45+ qDebug() << "NULL";
46+ }
47+ qDebug() << "============== Active focus info END ================";
48+}
tests/uqmlscene/ActiveFocusLogger.hnew file+37
@@ -0,0 +1,37 @@
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 ACTIVE_FOCUS_LOGGER_H
18+#define ACTIVE_FOCUS_LOGGER_H
19+
20+#include <QObject>
21+#include <QQuickWindow>
22+#include <QPointer>
23+
24+class ActiveFocusLogger : public QObject {
25+ Q_OBJECT
26+
27+public:
28+ void setWindow(QQuickWindow *window);
29+
30+private Q_SLOTS:
31+ void printActiveFocusInfo();
32+
33+private:
34+ QPointer<QQuickWindow> m_window;
35+};
36+
37+#endif // ACTIVE_FOCUS_LOGGER_H
tests/uqmlscene/CMakeLists.txt+1
@@ -1,5 +1,6 @@
1add_executable(uqmlscene
1add_executable(uqmlscene
2 ${shellapplication_MOC_SRCS}
2 ${shellapplication_MOC_SRCS}
3+ ActiveFocusLogger.cpp
3 main.cpp
4 main.cpp
4 ${CMAKE_SOURCE_DIR}/src/MouseTouchAdaptor.cpp
5 ${CMAKE_SOURCE_DIR}/src/MouseTouchAdaptor.cpp
5 )
6 )
tests/uqmlscene/main.cpp+13
@@ -68,6 +68,12 @@
68// UbuntuGestures lib
68// UbuntuGestures lib
69#include <TouchRegistry.h>
69#include <TouchRegistry.h>
70
70
71+#define UQMLSCENE_DEBUG_ACTIVE_FOCUS 0
72+
73+#if UQMLSCENE_DEBUG_ACTIVE_FOCUS
74+ #include "ActiveFocusLogger.h"
75+#endif
76+
71#ifdef QML_RUNTIME_TESTING
77#ifdef QML_RUNTIME_TESTING
72class RenderStatistics
78class RenderStatistics
73{
79{
@@ -378,6 +384,10 @@ int main(int argc, char ** argv)
378{
384{
379 Options options;
385 Options options;
380
386
387+ #if UQMLSCENE_DEBUG_ACTIVE_FOCUS
388+ ActiveFocusLogger activeFocusLogger;
389+ #endif
390+
381 QStringList imports;
391 QStringList imports;
382 QList<QPair<QString, QString> > bundles;
392 QList<QPair<QString, QString> > bundles;
383 for (int i = 1; i < argc; ++i) {
393 for (int i = 1; i < argc; ++i) {
@@ -503,6 +513,9 @@ int main(int argc, char ** argv)
503 QQuickItem *contentItem = qobject_cast<QQuickItem *>(topLevel);
513 QQuickItem *contentItem = qobject_cast<QQuickItem *>(topLevel);
504 if (contentItem) {
514 if (contentItem) {
505 qxView = new QQuickView(&engine, nullptr);
515 qxView = new QQuickView(&engine, nullptr);
516+ #if UQMLSCENE_DEBUG_ACTIVE_FOCUS
517+ activeFocusLogger.setWindow(qxView);
518+ #endif
506 TouchRegistry::instance()->setParent(qxView);
519 TouchRegistry::instance()->setParent(qxView);
507 qxView->installEventFilter(TouchRegistry::instance());
520 qxView->installEventFilter(TouchRegistry::instance());
508 window = qxView;
521 window = qxView;