on xenial help sign in
Merge trunk.
commit5bb641c63f2af0c436dd3b98476a8f5ae49e53ab authorMichał Sawicz <michal.sawicz@canonical.com> dateMon 16 Feb 2015, 22:03 (12 years ago) parentae0308861022bc
237 files changed+8688−2271
CMakeLists.txt+1 debian/changelog+64 debian/control+4−2 debian/unity8-private.install+1 debian/unity8.install+1 plugins/AccountsService/AccountsServiceDBusAdaptor.cpp+9 plugins/AccountsService/AccountsServiceDBusAdaptor.h+12 plugins/AccountsService/com.canonical.unity.AccountsService.xml+1−2 plugins/CMakeLists.txt+1 plugins/Dash/CardCreator.js+2−1 plugins/Greeter/CMakeLists.txt+6 plugins/Greeter/Unity/CMakeLists.txt+1 plugins/Greeter/Unity/Launcher/CMakeLists.txt+38 plugins/Greeter/Unity/Launcher/Launcher.qmltypes+179 plugins/Greeter/Unity/Launcher/launcheritem.cpp+171 plugins/Greeter/Unity/Launcher/launcheritem.h+74 plugins/Greeter/Unity/Launcher/launchermodelas.cpp+277 plugins/Greeter/Unity/Launcher/launchermodelas.h+75 plugins/Greeter/Unity/Launcher/plugin.cpp+46 tests/mocks/LightDM/single-passphrase/UsersModelPrivate.cpp → plugins/Greeter/Unity/Launcher/plugin.h+13−14 plugins/Greeter/Unity/Launcher/qmldir+3 tests/mocks/LightDM/single-pin/GreeterPrivate.cpp → plugins/Greeter/Unity/Launcher/quicklistentry.cpp+28−18 plugins/Greeter/Unity/Launcher/quicklistentry.h+43 plugins/Greeter/Unity/Launcher/quicklistmodel.cpp+70 plugins/Greeter/Unity/Launcher/quicklistmodel.h+55 plugins/LightDM/CMakeLists.txt+3−3 plugins/LightDM/Greeter.cpp+1−19 plugins/LightDM/Greeter.h+1−1 plugins/LightDM/GreeterPrivate.h+39 tests/mocks/LightDM/demo/CMakeLists.txt → plugins/LightDM/liblightdm/CMakeLists.txt+6−6 tests/mocks/LightDM/Greeter.cpp → plugins/LightDM/liblightdm/Greeter.cpp+7−7 tests/mocks/LightDM/Greeter.h → plugins/LightDM/liblightdm/Greeter.h+4−3 tests/mocks/LightDM/demo/GreeterPrivate.cpp → plugins/LightDM/liblightdm/GreeterPrivate.cpp+2−2 tests/mocks/LightDM/GreeterPrivate.h → plugins/LightDM/liblightdm/GreeterPrivate.h−1 tests/mocks/LightDM/UsersModel.cpp → plugins/LightDM/liblightdm/UsersModel.cpp+6 tests/mocks/LightDM/UsersModel.h → plugins/LightDM/liblightdm/UsersModel.h+4 tests/mocks/LightDM/demo/UsersModelPrivate.cpp → plugins/LightDM/liblightdm/UsersModelPrivate.cpp+1−1 tests/mocks/LightDM/UsersModelPrivate.h → plugins/LightDM/liblightdm/UsersModelPrivate.h plugins/Lights/CMakeLists.txt−2 plugins/Powerd/CMakeLists.txt−2 plugins/Ubuntu/Gestures/TouchGate.cpp+2 plugins/Ubuntu/Gestures/TouchGate.h+2 plugins/Unity/Indicators/indicator.cpp+23−8 plugins/Unity/Indicators/indicator.h+5−1 plugins/Unity/Indicators/indicatorsmanager.cpp+17−3 plugins/Unity/Indicators/indicatorsmanager.h+6−1 plugins/Unity/Indicators/indicatorsmodel.cpp+25−2 plugins/Unity/Indicators/indicatorsmodel.h+6−1 plugins/Unity/Indicators/modelactionrootstate.cpp+4−2 plugins/Unity/Indicators/unitymenumodelcache.cpp+8−11 plugins/Unity/Indicators/unitymenumodelcache.h+2−2 plugins/Unity/Launcher/CMakeLists.txt+2−2 plugins/Unity/Launcher/asadapter.cpp+60 tests/mocks/Unity/Notifications/MockNotificationTypes.h → plugins/Unity/Launcher/asadapter.h+24−14 plugins/Unity/Launcher/launchermodel.cpp+20 plugins/Unity/Launcher/launchermodel.h+5 plugins/Unity/Session/CMakeLists.txt−2 plugins/Utils/CMakeLists.txt+2−4 plugins/Utils/plugin.cpp+9 plugins/Utils/windowstatestorage.cpp+96 plugins/Utils/windowstatestorage.h+38 po/am.po+1−1 po/ast.po+51−44 po/be.po+52−45 po/ce.po+521 po/de.po+3−3 po/el.po+68−50 po/en_AU.po+1−1 po/es.po+3−3 po/fa.po+4−4 po/fi.po+3−3 po/fr.po+4−4 po/gd.po+45−40 po/gl.po+7−7 po/hu.po+3−3 po/nl.po+4−4 po/oc.po+521 po/pl.po+95−80 po/ru.po+5−5 po/sl.po+3−3 po/tr.po+79−69 po/unity8.pot+83−73 qml/CMakeLists.txt+3−2 qml/Components/Carousel.qml+6−2 qml/Components/EdgeDemo.qml−254 qml/Components/EdgeDemoOverlay.qml−273 qml/Dash/CardCarousel.qml+6−2 qml/Dash/CardHorizontalList.qml+7−2 qml/Dash/Dash.qml+6−8 qml/Dash/DashContent.qml+1−2 qml/Dash/DashRenderer.qml+6−1 qml/Dash/GenericScopeView.qml+92−48 qml/Dash/PageHeader.qml+2 qml/Dash/Previews/Preview.qml+1 qml/Dash/ScopeSettings/ScopeSettingBoolean.qml+3−1 qml/Dash/ScopesListCategoryItem.qml+1 qml/Greeter/Greeter.qml+8 qml/Greeter/GreeterContent.qml+7−2 qml/Launcher/Launcher.qml+13−7 qml/Launcher/LauncherPanel.qml+7−5 qml/Notifications/Notification.qml+36−6 qml/Notifications/Notifications.qml+2−6 qml/Panel/Panel.qml+5−3 qml/Shell.qml+50−25 qml/Stages/ApplicationWindow.qml+17−2 qml/Stages/DesktopStage.qml+2−2 qml/Stages/PhoneStage.qml+64−34 qml/Stages/SessionContainer.qml+22−7 qml/Stages/SpreadDelegate.qml+2−1 qml/Stages/SurfaceContainer.qml+11−25 qml/Stages/TabletStage.qml+14 qml/Stages/TransformedSpreadDelegate.qml+27−21 qml/Stages/TransformedTabletSpreadDelegate.qml+1−1 qml/Stages/WindowMoveResizeArea.qml+33−24 qml/Tutorial/Arrow.qml+56 qml/Tutorial/Slider.qml+123 tests/mocks/LightDM/single-pin/UsersModelPrivate.cpp → qml/Tutorial/Tick.qml+12−16 qml/Tutorial/Tutorial.qml+92 qml/Tutorial/TutorialBottom.qml+104 qml/Tutorial/TutorialBottomFinish.qml+41 qml/Tutorial/TutorialContent.qml+124 qml/Tutorial/TutorialLeft.qml+91 qml/Tutorial/TutorialLeftFinish.qml+41 qml/Tutorial/TutorialPage.qml+245 qml/Tutorial/TutorialRight.qml+223 qml/Tutorial/graphics/camera.png qml/Tutorial/graphics/chevron.png qml/Tutorial/graphics/dialer.png qml/Tutorial/graphics/facebook.png qml/Tutorial/graphics/gallery.png qml/Tutorial/graphics/tick.png qml/Wizard/Pages.qml+2 qml/Wizard/Pages/40-wifi.qml+27−9 run.sh+3−26 tests/autopilot/unity8/shell/__init__.py−13 tests/autopilot/unity8/shell/emulators/edges_demo.py−140 tests/autopilot/unity8/shell/emulators/tutorial.py+74 tests/autopilot/unity8/shell/fixture_setup.py+6−6 tests/autopilot/unity8/shell/tests/__init__.py+8−11 tests/autopilot/unity8/shell/tests/test_edges_demo.py−52 tests/autopilot/unity8/shell/tests/test_lock_screen.py+6−5 tests/autopilot/unity8/shell/tests/test_tutorial.py+57 tests/mocks/LightDM/CMakeLists.txt+8−9 tests/mocks/LightDM/single/GreeterPrivate.cpp → tests/mocks/LightDM/MockGreeter.cpp+12−23 tests/mocks/LightDM/MockGreeter.h+37 tests/mocks/LightDM/MockUsersModel.cpp+39 tests/mocks/LightDM/MockUsersModel.h+37 tests/mocks/LightDM/QLightDM/Greeter+17−1 tests/mocks/LightDM/QLightDM/InfographicModel−1 tests/mocks/LightDM/QLightDM/UsersModel+17−1 tests/mocks/LightDM/full/CMakeLists.txt−18 tests/mocks/LightDM/liblightdm/CMakeLists.txt+18 tests/mocks/LightDM/liblightdm/Greeter.cpp+199 tests/mocks/LightDM/liblightdm/Greeter.h+118 tests/mocks/LightDM/full/GreeterPrivate.cpp → tests/mocks/LightDM/liblightdm/GreeterPrivate.cpp+40−6 tests/mocks/LightDM/liblightdm/GreeterPrivate.h+54 tests/mocks/LightDM/liblightdm/UsersModel.cpp+134 tests/mocks/LightDM/liblightdm/UsersModel.h+69 tests/mocks/LightDM/full/UsersModelPrivate.cpp → tests/mocks/LightDM/liblightdm/UsersModelPrivate.cpp+58−6 tests/mocks/LightDM/liblightdm/UsersModelPrivate.h+63 tests/mocks/LightDM/plugin.cpp+68 tests/mocks/LightDM/single/UsersModelPrivate.cpp → tests/mocks/LightDM/plugin.h+13−14 tests/mocks/LightDM/single-passphrase/CMakeLists.txt−19 tests/mocks/LightDM/single-passphrase/GreeterPrivate.cpp−46 tests/mocks/LightDM/single-pin/CMakeLists.txt−19 tests/mocks/LightDM/single/CMakeLists.txt−18 tests/mocks/Unity/Application/MirSurfaceItem.qml+16−1 tests/mocks/Unity/CMakeLists.txt+4−1 tests/mocks/Unity/Indicators/fakeindicatorsmodel.cpp+13−1 tests/mocks/Unity/Indicators/fakeindicatorsmodel.h+6 tests/mocks/Unity/Launcher/CMakeLists.txt+1−1 tests/mocks/Unity/Launcher/MockLauncherModel.cpp+10 tests/mocks/Unity/Launcher/MockLauncherModel.h+3 tests/mocks/Unity/Notifications/CMakeLists.txt+2−1 tests/mocks/Unity/Notifications/MockActionModel.cpp+6−4 tests/mocks/Unity/Notifications/MockActionModel.h+4−2 tests/mocks/Unity/Notifications/MockNotification.cpp+178 tests/mocks/Unity/Notifications/MockNotification.h+94 tests/mocks/Unity/Notifications/MockNotificationModel.cpp+172 tests/mocks/Unity/Notifications/MockNotificationModel.h+72 tests/mocks/Unity/Notifications/plugin.cpp+5−3 tests/mocks/Unity/Notifications/plugin.h+1−2 tests/mocks/Unity/fake_previewmodel.cpp+15−1 tests/mocks/Unity/fake_previewmodel.h+7−1 tests/mocks/Unity/fake_previewstack.cpp+2−2 tests/mocks/Unity/fake_previewstack.h+3−1 tests/mocks/Unity/fake_previewwidgetmodel.cpp+11 tests/mocks/Unity/fake_scope.cpp+5−5 tests/mocks/Unity/fake_scopes.cpp+12 tests/mocks/Unity/fake_scopes.h+5 tests/mocks/Unity/fake_scopesoverview.cpp+1 tests/mocks/libusermetrics/UserMetrics.cpp+8 tests/plugins/CMakeLists.txt+1 tests/plugins/Greeter/CMakeLists.txt+1 tests/plugins/Greeter/Unity/CMakeLists.txt+1 tests/plugins/Greeter/Unity/Launcher/AccountsServiceDBusAdaptor.cpp+48 tests/plugins/Greeter/Unity/Launcher/AccountsServiceDBusAdaptor.h+53 tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+56 tests/plugins/Greeter/Unity/Launcher/launchermodelastest.cpp+169 tests/plugins/LightDM/CMakeLists.txt+3−3 tests/plugins/LightDM/greeter.qml+5 tests/plugins/Unity/Indicators/indicatorsmanagertest.cpp+30−5 tests/plugins/Unity/Indicators/indicatorsmodeltest.cpp+6−3 tests/plugins/Unity/Indicators/sharedunitymenumodeltest.cpp+2−2 tests/plugins/Unity/Launcher/AccountsServiceDBusAdaptor.cpp+42 tests/plugins/Unity/Launcher/AccountsServiceDBusAdaptor.h+47 tests/plugins/Unity/Launcher/CMakeLists.txt+3−1 tests/plugins/Unity/Launcher/launchermodeltest.cpp+65−1 tests/qmltests/CMakeLists.txt+21−20 tests/qmltests/Components/tst_EdgeDemoOverlay.qml−123 tests/qmltests/Dash/Previews/tst_Preview.qml+31 tests/qmltests/Dash/tst_Card.qml+7−1 tests/qmltests/Dash/tst_Dash.qml+69 tests/qmltests/Dash/tst_GenericScopeView.qml+1−1 tests/qmltests/Greeter/tst_Infographics.qml+10 tests/qmltests/Greeter/tst_MultiGreeter.qml+91−2 tests/qmltests/Launcher/tst_Launcher.qml+46−19 tests/mocks/Unity/Notifications/MockNotificationTypes.cpp → tests/qmltests/Notifications/Notification.qml+11−6 tests/qmltests/Notifications/tst_Notifications.qml+279−213 tests/qmltests/Notifications/tst_OptionToggle.qml+11−6 tests/qmltests/Notifications/tst_SwipeToAct.qml+4 tests/qmltests/Panel/tst_Panel.qml+33−2 tests/qmltests/Stages/ApplicationCheckBox.qml+45 tests/qmltests/Stages/RecursingChildSessionControl.qml+3 tests/qmltests/Stages/tst_ApplicationWindow.qml+6−1 tests/qmltests/Stages/tst_PhoneStage.qml+4 tests/qmltests/Stages/tst_SessionContainer.qml+14−7 tests/qmltests/Stages/tst_TabletStage.qml+154 tests/qmltests/Stages/tst_WindowMoveResizeArea.qml+117−18 tests/qmltests/Tutorial/tst_Tutorial.qml+348 tests/qmltests/tst_Shell.qml+62−5 tests/qmltests/tst_ShellWithPin.qml+72 tests/qmltests/tst_TabletShell.qml+10−2 tests/uqmlscene/ActiveFocusLogger.cpp+48 tests/uqmlscene/ActiveFocusLogger.h+37 tests/uqmlscene/CMakeLists.txt+1 tests/uqmlscene/main.cpp+13
CMakeLists.txt+1
@@ -54,6 +54,7 @@ find_package(Qt5Quick 5.2 REQUIRED)
5454find_package(Qt5Gui 5.2 REQUIRED)
5555find_package(Qt5DBus 5.2 REQUIRED)
5656find_package(Qt5Concurrent 5.2 REQUIRED)
57+find_package(Qt5Sql 5.2 REQUIRED)
5758
5859# Standard install paths
5960include(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+
165unity8 (8.02+15.04.20150123.3-0ubuntu1) vivid; urgency=low
266
367 [ Sebastien Bacher ]
debian/control+4−2
@@ -27,7 +27,7 @@ Build-Depends: cmake,
2727 libqmenumodel-dev (>= 0.2.9),
2828 libqt5xmlpatterns5-dev,
2929 libsystemsettings-dev,
30− libunity-api-dev (>= 7.94),
30+ libunity-api-dev (>= 7.96),
3131 libusermetricsoutput1-dev,
3232 libxcb1-dev,
3333 pkg-config,
@@ -39,6 +39,7 @@ Build-Depends: cmake,
3939 qml-module-qtquick-layouts,
4040 qml-module-qtquick-xmllistmodel,
4141 qml-module-qtquick2,
42+ qml-module-qtsysteminfo,
4243 qml-module-qttest,
4344 qt5-default,
4445 qtbase5-dev (>= 5.2.1),
@@ -90,6 +91,7 @@ Depends: gsettings-desktop-schemas,
9091 qmenumodel-qml (>= 0.2.9),
9192 qml-module-qt-labs-folderlistmodel,
9293 qml-module-qtquick-xmllistmodel,
94+ qml-module-qtsysteminfo,
9395 qtdeclarative5-gsettings1.0,
9496 qtdeclarative5-qtmir-plugin (>= 0.4.4),
9597 qtdeclarative5-ubuntu-telephony0.1,
@@ -124,7 +126,7 @@ Depends: qml-module-qtquick-layouts,
124126 unity-application-impl-4,
125127 unity-notifications-impl-3,
126128 unity-plugin-scopes | unity-scopes-impl,
127− unity-scopes-impl-4,
129+ unity-scopes-impl-6,
128130 unity8-fake-env | unity-application-impl,
129131 ${misc:Depends},
130132Breaks: unity8 (<< 7.86),
debian/unity8-private.install+1
@@ -1,6 +1,7 @@
11usr/lib/*/libunity8-private.*
22usr/lib/*/unity8/qml/AccountsService
33usr/lib/*/unity8/qml/Dash
4+usr/lib/*/unity8/qml/Greeter
45usr/lib/*/unity8/qml/LightDM
56usr/lib/*/unity8/qml/Lights
67usr/lib/*/unity8/qml/Powerd
debian/unity8.install+1
@@ -10,5 +10,6 @@ usr/share/unity8/Launcher
1010usr/share/unity8/Panel
1111usr/share/unity8/Shell.qml
1212usr/share/unity8/Stages
13+usr/share/unity8/Tutorial
1314usr/share/unity8/Wizard
1415usr/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
5757 }
5858}
5959
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+
6069void AccountsServiceDBusAdaptor::propertiesChangedSlot(const QString &interface, const QVariantMap &changed, const QStringList &invalid)
6170{
6271 // Merge changed and invalidated together
plugins/AccountsService/AccountsServiceDBusAdaptor.h+12
@@ -19,6 +19,7 @@
1919#ifndef UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
2020#define UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
2121
22+#include <QDBusArgument>
2223#include <QDBusContext>
2324#include <QDBusInterface>
2425#include <QMap>
@@ -33,7 +34,18 @@ public:
3334 explicit AccountsServiceDBusAdaptor(QObject *parent = 0);
3435
3536 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+
3647 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);
3749
3850Q_SIGNALS:
3951 void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
plugins/AccountsService/com.canonical.unity.AccountsService.xml+1−2
@@ -14,8 +14,7 @@
1414 <annotation name="org.freedesktop.Accounts.DefaultValue" value="true"/>
1515 </property>
1616
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">
1918 <annotation name="org.freedesktop.Accounts.DefaultValue" value="[{'defaults': <true>}]"/>
2019 </property>
2120
plugins/CMakeLists.txt+1
@@ -12,6 +12,7 @@ macro(add_unity8_plugin PLUGIN VERSION PATH)
1212endmacro()
1313
1414add_subdirectory(AccountsService)
15+add_subdirectory(Greeter)
1516add_subdirectory(LightDM)
1617add_subdirectory(Lights)
1718add_subdirectory(Dash)
plugins/Dash/CardCreator.js+2−1
@@ -320,7 +320,8 @@ function cardString(template, components) {
320320 var hasSummary = components["summary"] || false;
321321 var artAndSummary = hasArt && hasSummary;
322322 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"]));
324325 var hasTitle = components["title"] || false;
325326 var hasMascot = components["mascot"] || false;
326327 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 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -12,22 +12,21 @@
1212 *
1313 * You should have received a copy of the GNU General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
1715 */
1816
19−#include "../UsersModelPrivate.h"
17+#ifndef UNITY_LAUNCHER_PLUGIN_H
18+#define UNITY_LAUNCHER_PLUGIN_H
2019
21−namespace QLightDM
22−{
20+#include <QtQml/QQmlEngine>
21+#include <QtQml/QQmlExtensionPlugin>
2322
24−UsersModelPrivate::UsersModelPrivate(UsersModel* parent)
25− : q_ptr(parent)
23+class UnityLauncherPlugin : public QQmlExtensionPlugin
2624{
27− entries =
28− {
29− { "single", "Has Passphrase", 0, 0, false, false, 0, 0 },
30− };
31−}
25+ Q_OBJECT
26+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27+
28+public:
29+ void registerTypes(const char *uri);
30+};
3231
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.
32 *
43 * This program is free software; you can redistribute it and/or modify
54 * it under the terms of the GNU General Public License as published by
@@ -12,35 +11,46 @@
1211 *
1312 * You should have received a copy of the GNU General Public License
1413 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
1714 */
1815
19−#include "../Greeter.h"
20−#include "../GreeterPrivate.h"
16+#include "quicklistentry.h"
17+
18+QuickListEntry::QuickListEntry()
19+{
20+
21+}
22+
23+QString QuickListEntry::actionId() const
24+{
25+ return m_actionId;
26+}
2127
22−namespace QLightDM
28+void QuickListEntry::setActionId(const QString &actionId)
2329{
30+ m_actionId = actionId;
31+}
2432
25−GreeterPrivate::GreeterPrivate(Greeter* parent)
26− : authenticated(false),
27− authenticationUser(),
28− q_ptr(parent)
33+QString QuickListEntry::text() const
2934{
35+ return m_text;
3036}
3137
32−void GreeterPrivate::handleAuthenticate()
38+void QuickListEntry::setText(const QString &text)
3339{
34− Q_Q(Greeter);
35− Q_EMIT q->showPrompt("Password: ", Greeter::PromptTypeSecret);
40+ m_text = text;
3641}
3742
38−void GreeterPrivate::handleRespond(const QString &response)
43+QString QuickListEntry::icon() const
3944{
40− Q_Q(Greeter);
45+ return m_icon;
46+}
4147
42− authenticated = (response == "1234");
43− q->sendAuthenticationComplete();
48+void QuickListEntry::setIcon(const QString &icon)
49+{
50+ m_icon = icon;
4451}
4552
53+bool QuickListEntry::clickable() const
54+{
55+ return !m_actionId.isEmpty();
4656}
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)
31pkg_check_modules(LIBUSERMETRICSOUTPUT REQUIRED libusermetricsoutput-1)
42# TODO: Once we split out a separate greeter process, uncomment these lines
53#pkg_check_modules(LIBLIGHTDM REQUIRED liblightdm-qt5-2)
64
5+add_subdirectory(liblightdm)
6+
77include_directories(
88 ${CMAKE_CURRENT_SOURCE_DIR}
99 ${CMAKE_CURRENT_BINARY_DIR}
@@ -28,7 +28,7 @@ add_library(LightDM-qml MODULE
2828 )
2929
3030target_link_libraries(LightDM-qml
31− MockLightDM-demo
31+ integratedLightDM
3232 unity8-private
3333# TODO: Once we split out a separate greeter process, uncomment these lines
3434# ${LIBLIGHTDM_LDFLAGS}
plugins/LightDM/Greeter.cpp+1−19
@@ -17,26 +17,8 @@
1717 */
1818
1919#include "Greeter.h"
20+#include "GreeterPrivate.h"
2021#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−
4022
4123GreeterPrivate::GreeterPrivate(Greeter* parent)
4224 : m_greeter(new QLightDM::Greeter(parent)),
plugins/LightDM/Greeter.h+1−1
@@ -68,7 +68,7 @@ Q_SIGNALS:
6868 // should switch to this user if possible.
6969 void requestAuthenticationUser(const QString &user);
7070
71−private:
71+protected:
7272 GreeterPrivate * const d_ptr;
7373
7474 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 @@
11pkg_check_modules(LIBUSERMETRICSOUTPUT REQUIRED libusermetricsoutput-1)
22
33set(LibLightDM_SOURCES
4− ../Greeter.cpp
5− ../UsersModel.cpp
4+ Greeter.cpp
5+ UsersModel.cpp
66 GreeterPrivate.cpp
77 UsersModelPrivate.cpp
88 ${CMAKE_SOURCE_DIR}/plugins/Utils/qvariantlistmodel.cpp
99 )
1010
11−add_library(MockLightDM-demo STATIC ${LibLightDM_SOURCES})
11+add_library(integratedLightDM STATIC ${LibLightDM_SOURCES})
1212add_library(MockLightDM-demo-shared SHARED ${LibLightDM_SOURCES})
1313
1414include_directories(
@@ -16,7 +16,7 @@ include_directories(
1616 ${LIBUSERMETRICSOUTPUT_INCLUDE_DIRS}
1717)
1818
19−target_link_libraries(MockLightDM-demo
19+target_link_libraries(integratedLightDM
2020 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
2121 -lpam
2222)
@@ -25,10 +25,10 @@ target_link_libraries(MockLightDM-demo-shared
2525 -lpam
2626)
2727
28−qt5_use_modules(MockLightDM-demo Concurrent Gui)
28+qt5_use_modules(integratedLightDM Concurrent Gui)
2929qt5_use_modules(MockLightDM-demo-shared Concurrent Gui)
3030
31−set_target_properties(MockLightDM-demo PROPERTIES COMPILE_FLAGS -fPIC)
31+set_target_properties(integratedLightDM PROPERTIES COMPILE_FLAGS -fPIC)
3232set_target_properties(MockLightDM-demo-shared PROPERTIES
3333 OUTPUT_NAME lightdm-qt5-2)
3434
tests/mocks/LightDM/Greeter.cpp → plugins/LightDM/liblightdm/Greeter.cpprenamed+7−7
@@ -16,6 +16,13 @@
1616 * Author: Michael Terry <michael.terry@canonical.com>
1717 */
1818
19+
20+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
21+ * CHANGES MADE HERE MUST BE REFLECTED ON THE MOCK LIB
22+ * COUNTERPART IN tests/mocks/Lightdm/liblightdm
23+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
24+
25+
1926#include "Greeter.h"
2027#include "GreeterPrivate.h"
2128#include <QtCore/QCoreApplication>
@@ -128,13 +135,6 @@ void Greeter::authenticate(const QString &username)
128135
129136 d->authenticated = false;
130137 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);
138138 d->handleAuthenticate();
139139}
140140
tests/mocks/LightDM/Greeter.h → plugins/LightDM/liblightdm/Greeter.hrenamed+4−3
@@ -24,6 +24,10 @@
2424#include <QtCore/QObject>
2525#include <QtCore/QVariant>
2626
27+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
28+ * CHANGES MADE HERE MUST BE REFLECTED ON THE MOCK LIB
29+ * COUNTERPART IN tests/mocks/Lightdm/liblightdm
30+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
2731
2832namespace QLightDM
2933{
@@ -99,9 +103,6 @@ Q_SIGNALS:
99103protected:
100104 void sendAuthenticationComplete();
101105
102−private Q_SLOTS:
103− void delayedAuthentication();
104−
105106private:
106107 GreeterPrivate *d_ptr;
107108 Q_DECLARE_PRIVATE(Greeter)
tests/mocks/LightDM/demo/GreeterPrivate.cpp → plugins/LightDM/liblightdm/GreeterPrivate.cpprenamed+2−2
@@ -16,8 +16,8 @@
1616 * Author: Michael Terry <michael.terry@canonical.com>
1717 */
1818
19−#include "../Greeter.h"
20−#include "../GreeterPrivate.h"
19+#include "Greeter.h"
20+#include "GreeterPrivate.h"
2121#include <QFuture>
2222#include <QFutureInterface>
2323#include <QFutureWatcher>
tests/mocks/LightDM/GreeterPrivate.h → plugins/LightDM/liblightdm/GreeterPrivate.hrenamed−1
@@ -35,7 +35,6 @@ public:
3535 // These variables may not be used by all subclasses, that's no problem
3636 bool authenticated;
3737 QString authenticationUser;
38− bool twoFactorDone;
3938
4039 void handleAuthenticate();
4140 void handleRespond(const QString &response);
tests/mocks/LightDM/UsersModel.cpp → plugins/LightDM/liblightdm/UsersModel.cpprenamed+6
@@ -16,6 +16,12 @@
1616 * Author: Michael Terry <michael.terry@canonical.com>
1717 */
1818
19+
20+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
21+ * CHANGES MADE HERE MUST BE REFLECTED ON THE MOCK LIB
22+ * COUNTERPART IN tests/mocks/Lightdm/liblightdm
23+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
24+
1925// LightDM currently is Qt4 compatible, and so doesn't define setRoleNames.
2026// To use the same method of setting role name that it does, we
2127// set our compatibility to Qt4 here too.
tests/mocks/LightDM/UsersModel.h → plugins/LightDM/liblightdm/UsersModel.hrenamed+4
@@ -24,6 +24,10 @@
2424#include <QtCore/QSharedDataPointer>
2525#include <QAbstractListModel>
2626
27+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
28+ * CHANGES MADE HERE MUST BE REFLECTED ON THE MOCK LIB
29+ * COUNTERPART IN tests/mocks/Lightdm/liblightdm
30+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
2731
2832namespace QLightDM
2933{
tests/mocks/LightDM/demo/UsersModelPrivate.cpp → plugins/LightDM/liblightdm/UsersModelPrivate.cpprenamed+1−1
@@ -16,7 +16,7 @@
1616 * Author: Michael Terry <michael.terry@canonical.com>
1717 */
1818
19−#include "../UsersModelPrivate.h"
19+#include "UsersModelPrivate.h"
2020
2121#include <QDir>
2222#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−
31pkg_check_modules(ANDROID_HEADERS REQUIRED android-headers)
42pkg_check_modules(ANDROID_HARDWARE REQUIRED libhardware)
53
plugins/Powerd/CMakeLists.txt−2
@@ -1,5 +1,3 @@
1−include(FindPkgConfig)
2−
31pkg_search_module(GIO REQUIRED gio-2.0)
42
53include_directories(
plugins/Ubuntu/Gestures/TouchGate.cpp+2
@@ -53,6 +53,8 @@ void TouchGate::touchEvent(QTouchEvent *event)
5353 m_touchInfoMap[touchPoint.id()].ownership = OwnershipRequested;
5454 m_touchInfoMap[touchPoint.id()].ended = false;
5555 TouchRegistry::instance()->requestTouchOwnership(touchPoint.id(), this);
56+
57+ Q_EMIT pressed();
5658 }
5759
5860 goodToGo &= m_touchInfoMap.contains(touchPoint.id())
plugins/Ubuntu/Gestures/TouchGate.h+2
@@ -53,6 +53,8 @@ public:
5353Q_SIGNALS:
5454 void targetItemChanged(QQuickItem *item);
5555
56+ void pressed();
57+
5658protected:
5759 void touchEvent(QTouchEvent *event) override;
5860private:
plugins/Unity/Indicators/indicator.cpp+23−8
@@ -31,24 +31,26 @@ Indicator::~Indicator()
3131{
3232}
3333
34−void Indicator::init(const QString &profile, const QString& busName, const QSettings& settings)
34+void Indicator::init(const QString& busName, const QSettings& settings)
3535{
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+ }
3744
38− QVariant pos = settings.value(profile + "/Position");
39− if (!pos.isValid())
40− pos = settings.value("Indicator Service/Position", QVariant::fromValue(0));
41− setPosition(pos.toInt());
45+ setId(settings.value("Indicator Service/Name").toString());
4246
4347 QString actionObjectPath = settings.value("Indicator Service/ObjectPath").toString();
44− QString menuObjectPath = settings.value(profile + "/ObjectPath").toString();
4548
4649 QVariantMap properties;
4750 properties.clear();
4851 properties.insert("busType", 1);
4952 properties.insert("busName", busName);
5053 properties.insert("actionsObjectPath", actionObjectPath);
51− properties.insert("menuObjectPath", menuObjectPath);
5254 setIndicatorProperties(properties);
5355}
5456
@@ -78,6 +80,19 @@ void Indicator::setPosition(int position)
7880 }
7981}
8082
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+
8196QVariant Indicator::indicatorProperties() const
8297{
8398 return m_properties;
plugins/Unity/Indicators/indicator.h+5−1
@@ -35,12 +35,15 @@ public:
3535 Indicator(QObject *parent = 0);
3636 virtual ~Indicator();
3737
38− void init(const QString &profile, const QString& busName, const QSettings& settings);
38+ void init(const QString& busName, const QSettings& settings);
3939
4040 QString identifier() const;
4141 int position() const;
4242 QVariant indicatorProperties() const;
4343
44+public Q_SLOTS:
45+ void setProfile(const QString& profile);
46+
4447Q_SIGNALS:
4548 void identifierChanged(const QString &identifier);
4649 void positionChanged(int position);
@@ -55,6 +58,7 @@ private:
5558 QString m_identifier;
5659 int m_position;
5760 QVariant m_properties;
61+ QVariantMap m_settings;
5862};
5963
6064#endif // INDICATOR_H
plugins/Unity/Indicators/indicatorsmanager.cpp+17−3
@@ -54,10 +54,9 @@ IndicatorsManager::~IndicatorsManager()
5454
5555}
5656
57−void IndicatorsManager::load(const QString& profile)
57+void IndicatorsManager::load()
5858{
5959 unload();
60− m_profile = profile;
6160
6261 QStringList xdgLocations = shellDataDirs();//QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
6362
@@ -216,6 +215,19 @@ void IndicatorsManager::setLoaded(bool loaded)
216215 }
217216}
218217
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+
219231void IndicatorsManager::startVerify(const QString& path)
220232{
221233 QHashIterator<QString, IndicatorData*> iter(m_indicatorsData);
@@ -268,7 +280,9 @@ Indicator::Ptr IndicatorsManager::indicator(const QString& indicator_name)
268280 Indicator::Ptr new_indicator(new Indicator(this));
269281 data->m_indicator = new_indicator;
270282 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&)));
272286 return new_indicator;
273287}
274288
plugins/Unity/Indicators/indicatorsmanager.h+6−1
@@ -32,13 +32,17 @@ class UNITYINDICATORS_EXPORT IndicatorsManager : public QObject
3232{
3333 Q_OBJECT
3434 Q_PROPERTY(bool loaded READ isLoaded NOTIFY loadedChanged)
35+ Q_PROPERTY(QString profile READ profile WRITE setProfile NOTIFY profileChanged)
3536public:
3637 explicit IndicatorsManager(QObject* parent = 0);
3738 ~IndicatorsManager();
3839
39− Q_INVOKABLE void load(const QString& profile);
40+ Q_INVOKABLE void load();
4041 Q_INVOKABLE void unload();
4142
43+ QString profile() const;
44+ void setProfile(const QString& profile);
45+
4246 Indicator::Ptr indicator(const QString& indicator_name);
4347
4448 QList<Indicator::Ptr> indicators();
@@ -47,6 +51,7 @@ public:
4751
4852Q_SIGNALS:
4953 void loadedChanged(bool);
54+ void profileChanged(const QString&);
5055
5156 void indicatorLoaded(const QString& indicator_name);
5257 void indicatorAboutToBeUnloaded(const QString& indicator_name);
plugins/Unity/Indicators/indicatorsmodel.cpp+25−2
@@ -56,6 +56,7 @@ IndicatorsModel::IndicatorsModel(QObject *parent)
5656 m_manager = new IndicatorsManager(this);
5757 QObject::connect(m_manager, SIGNAL(indicatorLoaded(const QString&)), this, SLOT(onIndicatorLoaded(const QString&)));
5858 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()));
5960
6061 QObject::connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
6162 QObject::connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
@@ -80,15 +81,37 @@ int IndicatorsModel::count() const
8081 return rowCount();
8182}
8283
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+
83106/*!
84107 \qmlmethod IndicatorsModel::unload()
85108
86109 Load all indicators.
87110*/
88−void IndicatorsModel::load(const QString& profile)
111+void IndicatorsModel::load()
89112{
90113 m_indicators.clear();
91− m_manager->load(profile);
114+ m_manager->load();
92115}
93116
94117/*!
plugins/Unity/Indicators/indicatorsmodel.h+6−1
@@ -34,17 +34,21 @@ class UNITYINDICATORS_EXPORT IndicatorsModel : public QAbstractListModel
3434 Q_OBJECT
3535 Q_ENUMS(Roles)
3636 Q_PROPERTY(int count READ count NOTIFY countChanged)
37+ Q_PROPERTY(QString profile READ profile WRITE setProfile NOTIFY profileChanged)
3738
3839public:
3940
4041 IndicatorsModel(QObject *parent=0);
4142 ~IndicatorsModel();
4243
43− Q_INVOKABLE void load(const QString& profile);
44+ Q_INVOKABLE void load();
4445 Q_INVOKABLE void unload();
4546
4647 Q_INVOKABLE QVariant data(int row, int role) const;
4748
49+ QString profile() const;
50+ void setProfile(const QString& profile);
51+
4852 /* QAbstractItemModel */
4953 QHash<int, QByteArray> roleNames() const;
5054 int columnCount(const QModelIndex &parent = QModelIndex()) const;
@@ -55,6 +59,7 @@ public:
5559
5660Q_SIGNALS:
5761 void countChanged();
62+ void profileChanged();
5863 void indicatorDataChanged(const QVariant& data);
5964
6065private Q_SLOTS:
plugins/Unity/Indicators/modelactionrootstate.cpp+4−2
@@ -71,7 +71,7 @@ void ModelActionRootState::setMenu(UnityMenuModel* menu)
7171
7272bool ModelActionRootState::valid() const
7373{
74− return m_menu && m_menu->rowCount() > 0;
74+ return !currentState().empty();
7575}
7676
7777void ModelActionRootState::onModelRowsAdded(const QModelIndex& parent, int start, int end)
@@ -121,7 +121,9 @@ void ModelActionRootState::updateActionState()
121121 m_menu->setActionStateParser(oldParser);
122122
123123 setCurrentState(state);
124− } else {
124+ } else if (!m_menu) {
125125 setCurrentState(QVariantMap());
126126 }
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
127129}
plugins/Unity/Indicators/unitymenumodelcache.cpp+8−11
@@ -46,17 +46,14 @@ QSharedPointer<UnityMenuModel> UnityMenuModelCache::model(const QByteArray& path
4646 QQmlEngine::setObjectOwnership(model, QQmlEngine::CppOwnership);
4747
4848 QSharedPointer<UnityMenuModel> menuModel(model);
49− connect(model, &QObject::destroyed, this, [this] {
50− QMutableHashIterator<QByteArray, QWeakPointer<UnityMenuModel>> iter(m_registry);
51− while(iter.hasNext()) {
52− auto keyVal = iter.next();
53− if (keyVal.value().isNull()) {
54− iter.remove();
55− break;
56− }
57− }
58− });
59− m_registry[path] = menuModel.toWeakRef();
49+
50+ // Keep a shared pointer (rather than weak pointer which would cause the
51+ // model to be deleted when all shared pointers we give out are deleted).
52+ // We want to keep all models cached because when we switch indicator
53+ // profiles, we will be switching paths often. And we want to keep the
54+ // old model around, ready to be used. Otherwise the UI might momentarily
55+ // wait as we populate the model from DBus yet again.
56+ m_registry[path] = menuModel;
6057
6158 menuModel->setMenuObjectPath(path);
6259 return menuModel;
plugins/Unity/Indicators/unitymenumodelcache.h+2−2
@@ -25,7 +25,7 @@
2525#include <QObject>
2626#include <QHash>
2727#include <QPointer>
28−#include <QWeakPointer>
28+#include <QSharedPointer>
2929
3030class UnityMenuModel;
3131
@@ -43,7 +43,7 @@ public:
4343 Q_INVOKABLE virtual bool contains(const QByteArray& path);
4444
4545protected:
46− QHash<QByteArray, QWeakPointer<UnityMenuModel>> m_registry;
46+ QHash<QByteArray, QSharedPointer<UnityMenuModel>> m_registry;
4747 static QPointer<UnityMenuModelCache> theCache;
4848};
4949
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)
32pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
43pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
54
@@ -21,6 +20,7 @@ set(QMLLAUNCHERPLUGIN_SRC
2120 dbusinterface.cpp
2221 gsettings.cpp
2322 desktopfilehandler.cpp
23+ asadapter.cpp
2424 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
2525 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
2626 ${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 @@
11/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2014-2015 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -12,25 +12,35 @@
1212 *
1313 * You should have received a copy of the GNU Lesser General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors:
17− * Mirco Mueller <mirco.mueller@canonical.com>
1815 */
1916
20−#ifndef MOCK_NOTIFICATION_TYPES_H
21−#define MOCK_NOTIFICATION_TYPES_H
17+#ifndef ASADAPTER_H
18+#define ASADAPTER_H
2219
23−#include <QObject>
20+#include <QVariantMap>
2421
25−class MockNotification : public QObject {
26− Q_OBJECT
27− Q_ENUMS(Type)
22+class LauncherItem;
23+class AccountsServiceDBusAdaptor;
24+class QDBusInterface;
2825
26+class ASAdapter
27+{
2928public:
30− MockNotification(QObject *parent=nullptr);
31− virtual ~MockNotification();
29+ ASAdapter();
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;
3242
33− enum Type { PlaceHolder, Confirmation, Ephemeral, Interactive, SnapDecision };
43+ friend class LauncherModelTest;
3444};
3545
36−#endif // MOCK_NOTIFICATION_TYPES_H
46+#endif
plugins/Unity/Launcher/launchermodel.cpp+20
@@ -22,6 +22,7 @@
2222#include "gsettings.h"
2323#include "desktopfilehandler.h"
2424#include "dbusinterface.h"
25+#include "asadapter.h"
2526
2627#include <unity/shell/application/ApplicationInfoInterface.h>
2728
@@ -34,6 +35,7 @@ LauncherModel::LauncherModel(QObject *parent):
3435 LauncherModelInterface(parent),
3536 m_settings(new GSettings(this)),
3637 m_dbusIface(new DBusInterface(this)),
38+ m_asAdapter(new ASAdapter()),
3739 m_appManager(0)
3840{
3941 connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged);
@@ -50,6 +52,8 @@ LauncherModel::~LauncherModel()
5052 while (!m_list.empty()) {
5153 m_list.takeFirst()->deleteLater();
5254 }
55+
56+ delete m_asAdapter;
5357}
5458
5559int LauncherModel::rowCount(const QModelIndex &parent) const
@@ -261,6 +265,15 @@ void LauncherModel::setApplicationManager(unity::shell::application::Application
261265 }
262266}
263267
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+}
264277
265278void LauncherModel::storeAppList()
266279{
@@ -271,6 +284,7 @@ void LauncherModel::storeAppList()
271284 }
272285 }
273286 m_settings->setStoredApplications(appIds);
287+ m_asAdapter->syncItems(m_list);
274288}
275289
276290void LauncherModel::unpin(const QString &appId)
@@ -321,6 +335,7 @@ void LauncherModel::countChanged(const QString &appId, int count)
321335 LauncherItem *item = m_list.at(idx);
322336 item->setCount(count);
323337 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
338+ m_asAdapter->syncItems(m_list);
324339 }
325340}
326341
@@ -352,6 +367,7 @@ void LauncherModel::countVisibleChanged(const QString &appId, int countVisible)
352367 Q_EMIT hint();
353368 }
354369 }
370+ m_asAdapter->syncItems(m_list);
355371}
356372
357373void LauncherModel::refresh()
@@ -433,6 +449,8 @@ void LauncherModel::refresh()
433449 if (changed) {
434450 Q_EMIT hint();
435451 }
452+
453+ m_asAdapter->syncItems(m_list);
436454}
437455
438456void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
@@ -467,6 +485,7 @@ void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
467485 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
468486 m_list.append(item);
469487 endInsertRows();
488+ m_asAdapter->syncItems(m_list);
470489 }
471490}
472491
@@ -486,6 +505,7 @@ void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
486505 beginRemoveRows(QModelIndex(), appIndex, appIndex);
487506 m_list.takeAt(appIndex)->deleteLater();
488507 endRemoveRows();
508+ m_asAdapter->syncItems(m_list);
489509 }
490510}
491511
plugins/Unity/Launcher/launchermodel.h+5
@@ -28,6 +28,7 @@
2828class LauncherItem;
2929class GSettings;
3030class DBusInterface;
31+class ASAdapter;
3132
3233using namespace unity::shell::launcher;
3334using namespace unity::shell::application;
@@ -54,6 +55,9 @@ public:
5455 unity::shell::application::ApplicationManagerInterface* applicationManager() const;
5556 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
5657
58+ bool onlyPinned() const override;
59+ void setOnlyPinned(bool onlyPinned) override;
60+
5761 int findApplication(const QString &appId);
5862
5963public Q_SLOTS:
@@ -79,6 +83,7 @@ private:
7983
8084 GSettings *m_settings;
8185 DBusInterface *m_dbusIface;
86+ ASAdapter *m_asAdapter;
8287
8388 ApplicationManagerInterface *m_appManager;
8489
plugins/Unity/Session/CMakeLists.txt−2
@@ -1,5 +1,3 @@
1−include(FindPkgConfig)
2−
31pkg_search_module(GIO REQUIRED gio-2.0)
42
53include_directories(
plugins/Utils/CMakeLists.txt+2−4
@@ -1,6 +1,3 @@
1−# Dependencies
2−include(FindPkgConfig)
3−
41pkg_search_module(GIO REQUIRED gio-2.0)
52
63include_directories(
@@ -18,6 +15,7 @@ set(QMLPLUGIN_SRC
1815 unitymenumodelpaths.cpp
1916 windowkeysfilter.cpp
2017 easingcurve.cpp
18+ windowstatestorage.cpp
2119 plugin.cpp
2220 )
2321
@@ -33,6 +31,6 @@ target_link_libraries(Utils-qml ${GIO_LDFLAGS})
3331# files directly in targets.
3432set_target_properties(Utils-qml PROPERTIES COMPILE_FLAGS -fvisibility=default)
3533
36−qt5_use_modules(Utils-qml Qml Quick DBus Network Gui)
34+qt5_use_modules(Utils-qml Qml Quick DBus Network Gui Sql Concurrent)
3735
3836add_unity8_plugin(Utils 0.1 Utils TARGETS Utils-qml)
plugins/Utils/plugin.cpp+9
@@ -33,6 +33,14 @@
3333#include "unitymenumodelpaths.h"
3434#include "windowkeysfilter.h"
3535#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+}
3644
3745void UtilsPlugin::registerTypes(const char *uri)
3846{
@@ -46,6 +54,7 @@ void UtilsPlugin::registerTypes(const char *uri)
4654 qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
4755 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
4856 qmlRegisterType<RelativeTimeFormatter>(uri, 0, 1, "RelativeTimeFormatter");
57+ qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
4958}
5059
5160void 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 ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-01-29 06:00+0000\n"
18+"X-Launchpad-Export-Date: 2015-01-30 07:08+0000\n"
1919"X-Generator: Launchpad (build 17306)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:130
po/ast.po+51−44
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-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"
1212"Last-Translator: ivarela <ivarela@ubuntu.com>\n"
1313"Language-Team: Asturian <ast@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-01-28 06:29+0000\n"
19−"X-Generator: Launchpad (build 17306)\n"
18+"X-Launchpad-Export-Date: 2015-02-14 06:34+0000\n"
19+"X-Generator: Launchpad (build 17341)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:130
2222msgid "Password: "
@@ -230,15 +230,15 @@ msgstr "Soltar p'anovar..."
230230
231231#: qml/Dash/ScopesList.qml:66
232232msgid "Manage"
233−msgstr ""
233+msgstr "Alministrar"
234234
235235#: qml/Dash/ScopesList.qml:112
236236msgid "Home"
237−msgstr ""
237+msgstr "Aniciu"
238238
239239#: qml/Dash/ScopesList.qml:113
240240msgid "Also installed"
241−msgstr ""
241+msgstr "Tamién instalaos"
242242
243243#: qml/Greeter/Greeter.qml:133
244244msgid "Unlock"
@@ -286,7 +286,7 @@ msgstr "Falló, toca pa reintentar"
286286
287287#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
288288msgid "Reply"
289−msgstr ""
289+msgstr "Responder"
290290
291291#: qml/Panel/Indicators/ModemInfoItem.qml:105
292292msgid "Unlock SIM"
@@ -356,98 +356,99 @@ msgstr ""
356356
357357#: qml/Wizard/Page.qml:89
358358msgid "Back"
359−msgstr ""
359+msgstr "Atrás"
360360
361361#: qml/Wizard/Pages/10-welcome.qml:27
362362msgid "Hi!"
363−msgstr ""
363+msgstr "Hola!"
364364
365365#: qml/Wizard/Pages/10-welcome.qml:44
366366msgid "Welcome to your Ubuntu phone."
367−msgstr ""
367+msgstr "Dámoste l'acoyida a Ubuntu Phone"
368368
369369#: qml/Wizard/Pages/10-welcome.qml:52
370370msgid "Let’s get started."
371−msgstr ""
371+msgstr "Entamamos."
372372
373373#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
374374#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
375375#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
376376#: qml/Wizard/Pages/passwd-set.qml:90
377377msgid "Continue"
378−msgstr ""
378+msgstr "Siguir"
379379
380380#: qml/Wizard/Pages/20-sim.qml:25
381381msgid "Add a SIM card and restart your device"
382−msgstr ""
382+msgstr "Amiesta una tarxeta SIM y reanicia'l preséu"
383383
384384#: qml/Wizard/Pages/20-sim.qml:55
385385msgid "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."
387387
388388#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
389389msgid "Skip"
390−msgstr ""
390+msgstr "Omitir"
391391
392392#: qml/Wizard/Pages/30-passwd-type.qml:39
393393msgid "Lock security"
394−msgstr ""
394+msgstr "Seguridá de bloquéu"
395395
396396#: qml/Wizard/Pages/30-passwd-type.qml:74
397397msgid "Please select how you’d like to unlock your phone."
398−msgstr ""
398+msgstr "Seleiciona cómo te prestaría desbloquiar el teléfonu."
399399
400400#: qml/Wizard/Pages/30-passwd-type.qml:97
401401msgid "Swipe"
402−msgstr ""
402+msgstr "Eslizar"
403403
404404#: qml/Wizard/Pages/30-passwd-type.qml:98
405405msgid "No security"
406−msgstr ""
406+msgstr "Ensin seguridá"
407407
408408#: qml/Wizard/Pages/30-passwd-type.qml:100
409409msgid "Passcode"
410−msgstr ""
410+msgstr "Códigu de pasu"
411411
412412#: qml/Wizard/Pages/30-passwd-type.qml:101
413413msgid "4 digits only"
414−msgstr ""
414+msgstr "namái 4 díxitos"
415415
416416#: qml/Wizard/Pages/30-passwd-type.qml:103
417417msgid "Passphrase"
418−msgstr ""
418+msgstr "Fras de pasu"
419419
420420#: qml/Wizard/Pages/30-passwd-type.qml:104
421421msgid "Numbers and letters"
422−msgstr ""
422+msgstr "Númberos y lletres"
423423
424424#: qml/Wizard/Pages/40-wifi.qml:28
425425msgid "Connect to Wi‑Fi"
426−msgstr ""
426+msgstr "Coneutar a WiFi"
427427
428428#: qml/Wizard/Pages/40-wifi.qml:147
429429msgid "Available networks…"
430−msgstr ""
430+msgstr "Redes disponibles..."
431431
432432#: qml/Wizard/Pages/40-wifi.qml:148
433433msgid "No available networks."
434−msgstr ""
434+msgstr "Nun hai redes disponibles"
435435
436436#: qml/Wizard/Pages/50-location.qml:27
437437msgid "Location"
438−msgstr ""
438+msgstr "Llocalización"
439439
440440#: qml/Wizard/Pages/50-location.qml:62
441441msgid "Let the phone detect your location:"
442−msgstr ""
442+msgstr "Permitir al teléfonu deteutar la llocalización:"
443443
444444#: qml/Wizard/Pages/50-location.qml:69
445445msgid "Using GPS only (less accurate)"
446−msgstr ""
446+msgstr "Namái usando GPS (menos precisión)"
447447
448448#: qml/Wizard/Pages/50-location.qml:86
449449msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
450450msgstr ""
451+"Usando GPS, Wifi anónimes ya información de redes móviles (recomiéndase)"
451452
452453#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
453454#: qml/Wizard/Pages/50-location.qml:103
@@ -455,81 +456,87 @@ msgid ""
455456"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
456457"conditions</a>."
457458msgstr ""
459+"Seleicionando esta opción, aceutes los <a href='#'>términos y "
460+"condiciones</a> de Nokia HERE."
458461
459462#: qml/Wizard/Pages/50-location.qml:112
460463msgid "Not at all"
461−msgstr ""
464+msgstr "De nengún mou"
462465
463466#: qml/Wizard/Pages/50-location.qml:124
464467msgid "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>."
466469
467470#: qml/Wizard/Pages/60-reporting.qml:24
468471msgid "Improving your experience"
469−msgstr ""
472+msgstr "Ameyorar la esperiencia"
470473
471474#: qml/Wizard/Pages/60-reporting.qml:36
472475msgid ""
473476"Your phone is set up to automatically report errors to Canonical and its "
474477"partners, the makers of the operating system."
475478msgstr ""
479+"El teléfonu ta configuráu pa informar automáticamente de los fallos a "
480+"Canonical y asociaos, los fabricantes del sistema operativu."
476481
477482#: qml/Wizard/Pages/60-reporting.qml:43
478483msgid ""
479484"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
480485"Privacy</b>"
481486msgstr ""
487+"Esto pue desactivase nos <b>Axustes del sistema</b> en <b>Seguranza y "
488+"privacidá</b>"
482489
483490#: qml/Wizard/Pages/80-finished.qml:24
484491msgid "All done"
485−msgstr ""
492+msgstr "Too fecho"
486493
487494#: qml/Wizard/Pages/80-finished.qml:39
488495msgid "Nice work!"
489−msgstr ""
496+msgstr "Bon trabayu!"
490497
491498#: qml/Wizard/Pages/80-finished.qml:46
492499msgid "Your phone is now ready to use."
493−msgstr ""
500+msgstr "El teléfonu yá ta preparáu pa usar."
494501
495502#: qml/Wizard/Pages/80-finished.qml:53
496503msgid "Finish"
497−msgstr ""
504+msgstr "Finar"
498505
499506#: qml/Wizard/Pages/here-terms.qml:27
500507msgid "Terms & Conditions"
501−msgstr ""
508+msgstr "Términos y condiciones"
502509
503510#: qml/Wizard/Pages/passwd-confirm.qml:49
504511msgid "Confirm passphrase"
505−msgstr ""
512+msgstr "Confirmar fras secreta"
506513
507514#: qml/Wizard/Pages/passwd-confirm.qml:50
508515msgid "Confirm passcode"
509−msgstr ""
516+msgstr "Confirmar el códigu de pasu"
510517
511518#: qml/Wizard/Pages/passwd-confirm.qml:53
512519#: qml/Wizard/Pages/passwd-confirm.qml:54
513520msgid "Please try again."
514−msgstr ""
521+msgstr "Vuelvi a intentalo."
515522
516523#: qml/Wizard/Pages/passwd-set.qml:61
517524msgid "Choose your passcode"
518−msgstr ""
525+msgstr "Escueyi'l códigu de pasu"
519526
520527#: qml/Wizard/Pages/passwd-set.qml:68
521528msgid "Passphrase must be 4 characters long"
522−msgstr ""
529+msgstr "La fras de contraseña tien de ser de 4 caráuteres"
523530
524531#: qml/Wizard/StackButton.qml:39
525532#, qt-format
526533msgid "〈 %1"
527−msgstr ""
534+msgstr "〈 %1"
528535
529536#: qml/Wizard/StackButton.qml:42
530537#, qt-format
531538msgid "%1 〉"
532−msgstr ""
539+msgstr "%1 〉"
533540
534541#~ msgid "Confirm"
535542#~ msgstr "Confirmar"
po/be.po+52−45
@@ -8,16 +8,16 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2014-09-24 17:12+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2015-02-07 19:04+0000\n"
12+"Last-Translator: Mikhail_SaTuRn <mikhail_saturn@tut.by>\n"
1313"Language-Team: Belarusian <be@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
19−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
20−"X-Generator: Launchpad (build 17306)\n"
19+"X-Launchpad-Export-Date: 2015-02-08 06:11+0000\n"
20+"X-Generator: Launchpad (build 17331)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:130
2323msgid "Password: "
@@ -87,7 +87,7 @@ msgstr "Перазагрузіць"
8787
8888#: qml/Components/Dialogs.qml:132
8989msgid "Are you sure you want to reboot?"
90−msgstr "Вы ўпэўненыя, што жадаеце перазагрузіць?"
90+msgstr "Вы ўпэўнены, што жадаеце перазагрузіць?"
9191
9292#: qml/Components/Dialogs.qml:154
9393msgid "Power"
@@ -166,11 +166,11 @@ msgstr ""
166166
167167#: qml/Components/Lockscreen.qml:238
168168msgid "Return to Call"
169−msgstr "Вярнуцца да Выкліку"
169+msgstr "Вярнуцца да выкліку"
170170
171171#: qml/Components/Lockscreen.qml:238
172172msgid "Emergency Call"
173−msgstr "Терміновы Выклік"
173+msgstr "Терміновы выклік"
174174
175175#: qml/Components/Lockscreen.qml:270
176176msgid "OK"
@@ -291,7 +291,7 @@ msgstr "Памылка, націсніце для паўтору"
291291
292292#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
293293msgid "Reply"
294−msgstr ""
294+msgstr "Адказаць"
295295
296296#: qml/Panel/Indicators/ModemInfoItem.qml:105
297297msgid "Unlock SIM"
@@ -361,98 +361,99 @@ msgstr ""
361361
362362#: qml/Wizard/Page.qml:89
363363msgid "Back"
364−msgstr ""
364+msgstr "Назад"
365365
366366#: qml/Wizard/Pages/10-welcome.qml:27
367367msgid "Hi!"
368−msgstr ""
368+msgstr "Вітай!"
369369
370370#: qml/Wizard/Pages/10-welcome.qml:44
371371msgid "Welcome to your Ubuntu phone."
372−msgstr ""
372+msgstr "Вітаем Вас у сістэме тэлефона Ubuntu."
373373
374374#: qml/Wizard/Pages/10-welcome.qml:52
375375msgid "Let’s get started."
376−msgstr ""
376+msgstr "Пачнем."
377377
378378#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
379379#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
380380#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
381381#: qml/Wizard/Pages/passwd-set.qml:90
382382msgid "Continue"
383−msgstr ""
383+msgstr "Працягнуць"
384384
385385#: qml/Wizard/Pages/20-sim.qml:25
386386msgid "Add a SIM card and restart your device"
387−msgstr ""
387+msgstr "Дадайце SIM-карту і перазагрузіце Вашую прыладу"
388388
389389#: qml/Wizard/Pages/20-sim.qml:55
390390msgid "Without it, you won’t be able to make calls or use text messaging."
391−msgstr ""
391+msgstr "Без яе вы не зможаце тэлефанаваць ці адпраўляць паведамленні."
392392
393393#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
394394msgid "Skip"
395−msgstr ""
395+msgstr "Прапусціць"
396396
397397#: qml/Wizard/Pages/30-passwd-type.qml:39
398398msgid "Lock security"
399−msgstr ""
399+msgstr "Блакаванне бяспекі"
400400
401401#: qml/Wizard/Pages/30-passwd-type.qml:74
402402msgid "Please select how you’d like to unlock your phone."
403−msgstr ""
403+msgstr "Калі ласка, абярыце спосаб разблакавання тэлефона."
404404
405405#: qml/Wizard/Pages/30-passwd-type.qml:97
406406msgid "Swipe"
407−msgstr ""
407+msgstr "Свайп(Рух пальцам)"
408408
409409#: qml/Wizard/Pages/30-passwd-type.qml:98
410410msgid "No security"
411−msgstr ""
411+msgstr "Без абароны"
412412
413413#: qml/Wizard/Pages/30-passwd-type.qml:100
414414msgid "Passcode"
415−msgstr ""
415+msgstr "Код-пароль"
416416
417417#: qml/Wizard/Pages/30-passwd-type.qml:101
418418msgid "4 digits only"
419−msgstr ""
419+msgstr "Толькі 4 лічбы"
420420
421421#: qml/Wizard/Pages/30-passwd-type.qml:103
422422msgid "Passphrase"
423−msgstr ""
423+msgstr "Пароль"
424424
425425#: qml/Wizard/Pages/30-passwd-type.qml:104
426426msgid "Numbers and letters"
427−msgstr ""
427+msgstr "Лічбы і літары"
428428
429429#: qml/Wizard/Pages/40-wifi.qml:28
430430msgid "Connect to Wi‑Fi"
431−msgstr ""
431+msgstr "Злучыцца з Wi-Fi"
432432
433433#: qml/Wizard/Pages/40-wifi.qml:147
434434msgid "Available networks…"
435−msgstr ""
435+msgstr "Даступныя сеткі..."
436436
437437#: qml/Wizard/Pages/40-wifi.qml:148
438438msgid "No available networks."
439−msgstr ""
439+msgstr "Няма даступных сетак."
440440
441441#: qml/Wizard/Pages/50-location.qml:27
442442msgid "Location"
443−msgstr ""
443+msgstr "Месца"
444444
445445#: qml/Wizard/Pages/50-location.qml:62
446446msgid "Let the phone detect your location:"
447−msgstr ""
447+msgstr "Дазволіць тэлефону вызначаць месцазнаходжанне:"
448448
449449#: qml/Wizard/Pages/50-location.qml:69
450450msgid "Using GPS only (less accurate)"
451−msgstr ""
451+msgstr "З дапамогай толькі GPS (найменш дакладна)"
452452
453453#: qml/Wizard/Pages/50-location.qml:86
454454msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
455455msgstr ""
456+"На аснове GPS, ананімізаванага Wi-Fi і дадзеных сотавай сеткі (рэкамендуецца)"
456457
457458#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
458459#: qml/Wizard/Pages/50-location.qml:103
@@ -460,81 +461,87 @@ msgid ""
460461"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
461462"conditions</a>."
462463msgstr ""
464+"Выбарам гэтага варыянту дзеянняў вы згаджаецеся з <a href='#'> умовамі "
465+"прадстаўлення паслуг Nokia HERE</a>."
463466
464467#: qml/Wizard/Pages/50-location.qml:112
465468msgid "Not at all"
466−msgstr ""
469+msgstr "Не дазваляць"
467470
468471#: qml/Wizard/Pages/50-location.qml:124
469472msgid "You can change your mind later in <b>System Settings</b>."
470−msgstr ""
473+msgstr "Пасля змяніць налады можна з дапамогай <b>Сістэмных параметраў</b>."
471474
472475#: qml/Wizard/Pages/60-reporting.qml:24
473476msgid "Improving your experience"
474−msgstr ""
477+msgstr "Спрашчэнне вашай працы"
475478
476479#: qml/Wizard/Pages/60-reporting.qml:36
477480msgid ""
478481"Your phone is set up to automatically report errors to Canonical and its "
479482"partners, the makers of the operating system."
480483msgstr ""
484+"Ваш тэлефон настроены на аўтаматычную адпраўку справаздач пра памылкі ў "
485+"Canonical і партнёрскія установы, вытворцам аперацыйнай сістэмы."
481486
482487#: qml/Wizard/Pages/60-reporting.qml:43
483488msgid ""
484489"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
485490"Privacy</b>"
486491msgstr ""
492+"Гэтае можна адключыць у <b>Сістэмных параметрах</b>, <b>Бяспека &amp; "
493+"канфедэнцыйнасць</b>"
487494
488495#: qml/Wizard/Pages/80-finished.qml:24
489496msgid "All done"
490−msgstr ""
497+msgstr "Зроблена"
491498
492499#: qml/Wizard/Pages/80-finished.qml:39
493500msgid "Nice work!"
494−msgstr ""
501+msgstr "Выдатная праца!"
495502
496503#: qml/Wizard/Pages/80-finished.qml:46
497504msgid "Your phone is now ready to use."
498−msgstr ""
505+msgstr "Цяпер Вашым тэлефонам можна карыстацца."
499506
500507#: qml/Wizard/Pages/80-finished.qml:53
501508msgid "Finish"
502−msgstr ""
509+msgstr "Скончыць"
503510
504511#: qml/Wizard/Pages/here-terms.qml:27
505512msgid "Terms & Conditions"
506−msgstr ""
513+msgstr "Умовы карыстання"
507514
508515#: qml/Wizard/Pages/passwd-confirm.qml:49
509516msgid "Confirm passphrase"
510−msgstr ""
517+msgstr "Пацвердзіце пароль"
511518
512519#: qml/Wizard/Pages/passwd-confirm.qml:50
513520msgid "Confirm passcode"
514−msgstr ""
521+msgstr "Пацвердзіце код"
515522
516523#: qml/Wizard/Pages/passwd-confirm.qml:53
517524#: qml/Wizard/Pages/passwd-confirm.qml:54
518525msgid "Please try again."
519−msgstr ""
526+msgstr "Калі ласка, паспрабуйце яшчэ раз."
520527
521528#: qml/Wizard/Pages/passwd-set.qml:61
522529msgid "Choose your passcode"
523−msgstr ""
530+msgstr "Выберыце ваш код-пароль"
524531
525532#: qml/Wizard/Pages/passwd-set.qml:68
526533msgid "Passphrase must be 4 characters long"
527−msgstr ""
534+msgstr "Пароль мусіць складацца мінімум з 4 сімвалаў"
528535
529536#: qml/Wizard/StackButton.qml:39
530537#, qt-format
531538msgid "〈 %1"
532−msgstr ""
539+msgstr "〈 %1"
533540
534541#: qml/Wizard/StackButton.qml:42
535542#, qt-format
536543msgid "%1 〉"
537−msgstr ""
544+msgstr "%1 〉"
538545
539546#~ msgid "Confirm"
540547#~ 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 ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"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"
1212"Last-Translator: Niklas Wenzel <nikwen.developer@gmail.com>\n"
1313"Language-Team: German <de@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
18+"X-Launchpad-Export-Date: 2015-01-30 07:08+0000\n"
1919"X-Generator: Launchpad (build 17306)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:130
@@ -290,7 +290,7 @@ msgstr "Fehlgeschlagen, tippen Sie zum Wiederholen"
290290
291291#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
292292msgid "Reply"
293−msgstr ""
293+msgstr "Antworten"
294294
295295#: qml/Panel/Indicators/ModemInfoItem.qml:105
296296msgid "Unlock SIM"
po/el.po+68−50
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2014-11-08 18:38+0000\n"
12−"Last-Translator: Efstathios Iosifidis <Unknown>\n"
11+"PO-Revision-Date: 2015-02-15 20:45+0000\n"
12+"Last-Translator: Filippos Kolyvas <Unknown>\n"
1313"Language-Team: Greek <el@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
19−"X-Generator: Launchpad (build 17306)\n"
18+"X-Launchpad-Export-Date: 2015-02-16 06:52+0000\n"
19+"X-Generator: Launchpad (build 17341)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:130
2222msgid "Password: "
@@ -90,7 +90,7 @@ msgstr "Επιθυμείτε σίγουρα επανεκκίνηση;"
9090
9191#: qml/Components/Dialogs.qml:154
9292msgid "Power"
93−msgstr ""
93+msgstr "Ενέργεια"
9494
9595#: qml/Components/Dialogs.qml:155
9696msgid ""
@@ -225,23 +225,23 @@ msgstr "Αποστολή"
225225
226226#: qml/Dash/PullToRefreshScopeStyle.qml:55
227227msgid "Pull to refresh…"
228−msgstr ""
228+msgstr "Σύρτε για ανανέωση…"
229229
230230#: qml/Dash/PullToRefreshScopeStyle.qml:60
231231msgid "Release to refresh…"
232−msgstr ""
232+msgstr "Αφήστε για ανανέωση…"
233233
234234#: qml/Dash/ScopesList.qml:66
235235msgid "Manage"
236−msgstr ""
236+msgstr "Διαχείριση"
237237
238238#: qml/Dash/ScopesList.qml:112
239239msgid "Home"
240−msgstr ""
240+msgstr "Αρχή"
241241
242242#: qml/Dash/ScopesList.qml:113
243243msgid "Also installed"
244−msgstr ""
244+msgstr "Επίσης εγκατεστημένα"
245245
246246#: qml/Greeter/Greeter.qml:133
247247msgid "Unlock"
@@ -289,7 +289,7 @@ msgstr "Αποτυχία, πατήστε για να ξαναπροσπαθήσ
289289
290290#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
291291msgid "Reply"
292−msgstr ""
292+msgstr "Απάντηση"
293293
294294#: qml/Panel/Indicators/ModemInfoItem.qml:105
295295msgid "Unlock SIM"
@@ -327,126 +327,136 @@ msgstr "Παρακαλώ πληκτρολογήστε ξανά"
327327
328328#: qml/Shell.qml:396
329329msgid "Sorry, incorrect passcode"
330−msgstr ""
330+msgstr "Συγνώμη, μη έγκυρος κωδικός πρόσβασης"
331331
332332#: qml/Shell.qml:490 qml/Wizard/Pages/passwd-confirm.qml:53
333333msgid "Sorry, incorrect passphrase."
334−msgstr ""
334+msgstr "Συγνώμη, μη έγκυρη συνθηματική φράση."
335335
336336#: qml/Shell.qml:491 qml/Wizard/Pages/passwd-confirm.qml:54
337337msgid "Sorry, incorrect passcode."
338−msgstr ""
338+msgstr "Συγνώμη, μη έγκυρος κωδικός πρόσβασης."
339339
340340#: qml/Shell.qml:492
341341msgid "This will be your last attempt."
342−msgstr ""
342+msgstr "Αυτή θα είναι η τελευταία σας προσπάθεια."
343343
344344#: qml/Shell.qml:494
345345msgid ""
346346"If passphrase is entered incorrectly, your phone will conduct a factory "
347347"reset and all personal data will be deleted."
348348msgstr ""
349+"Εάν η συνθηματική φράση που θα εισάγετε είναι μη έγκυρη, το τηλέφωνο σας θα "
350+"κάνει επαναφορά των εργοστασιακών ρυθμίσεων και όλα τα δεδομένα σας θα "
351+"διαγραφούν."
349352
350353#: qml/Shell.qml:495
351354msgid ""
352355"If passcode is entered incorrectly, your phone will conduct a factory reset "
353356"and all personal data will be deleted."
354357msgstr ""
358+"Εάν ο κωδικός πρόσβασης που θα εισάγετε είναι μη έγκυρος, το τηλέφωνο σας θα "
359+"κάνει επαναφορά των εργοστασιακών ρυθμίσεων και όλα τα δεδομένα σας θα "
360+"διαγραφούν."
355361
356362#: qml/Wizard/Page.qml:89
357363msgid "Back"
358−msgstr ""
364+msgstr "Πίσω"
359365
360366#: qml/Wizard/Pages/10-welcome.qml:27
361367msgid "Hi!"
362−msgstr ""
368+msgstr "Γεια!"
363369
364370#: qml/Wizard/Pages/10-welcome.qml:44
365371msgid "Welcome to your Ubuntu phone."
366−msgstr ""
372+msgstr "Καλώς ήρθατε στο Ubuntu phone σας."
367373
368374#: qml/Wizard/Pages/10-welcome.qml:52
369375msgid "Let’s get started."
370−msgstr ""
376+msgstr "Ας ξεκινήσουμε."
371377
372378#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
373379#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
374380#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
375381#: qml/Wizard/Pages/passwd-set.qml:90
376382msgid "Continue"
377−msgstr ""
383+msgstr "Συνέχιση"
378384
379385#: qml/Wizard/Pages/20-sim.qml:25
380386msgid "Add a SIM card and restart your device"
381−msgstr ""
387+msgstr "Εισάγετε μια κάρτα SIM και επανεκκινήστε τη συσκευή σας"
382388
383389#: qml/Wizard/Pages/20-sim.qml:55
384390msgid "Without it, you won’t be able to make calls or use text messaging."
385391msgstr ""
392+"Χωρίς αυτήν, δεν θα μπορείτε να κάνετε κλήσεις ή να χρησιμοποιήσετε μηνύματα "
393+"κειμένου."
386394
387395#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
388396msgid "Skip"
389−msgstr ""
397+msgstr "Παράβλεψη"
390398
391399#: qml/Wizard/Pages/30-passwd-type.qml:39
392400msgid "Lock security"
393−msgstr ""
401+msgstr "Κλείδωμα ασφαλείας"
394402
395403#: qml/Wizard/Pages/30-passwd-type.qml:74
396404msgid "Please select how you’d like to unlock your phone."
397−msgstr ""
405+msgstr "Παρακαλούμε επιλέξτε πως θα θέλατε να ξεκλειδώνετε το τηλέφωνό σας."
398406
399407#: qml/Wizard/Pages/30-passwd-type.qml:97
400408msgid "Swipe"
401−msgstr ""
409+msgstr "Χειρονομία"
402410
403411#: qml/Wizard/Pages/30-passwd-type.qml:98
404412msgid "No security"
405−msgstr ""
413+msgstr "Χωρίς ασφάλεια"
406414
407415#: qml/Wizard/Pages/30-passwd-type.qml:100
408416msgid "Passcode"
409−msgstr ""
417+msgstr "Κωδικός πρόσβασης"
410418
411419#: qml/Wizard/Pages/30-passwd-type.qml:101
412420msgid "4 digits only"
413−msgstr ""
421+msgstr "Μόνο με 4 ψηφία"
414422
415423#: qml/Wizard/Pages/30-passwd-type.qml:103
416424msgid "Passphrase"
417−msgstr ""
425+msgstr "Συνθηματική φράση"
418426
419427#: qml/Wizard/Pages/30-passwd-type.qml:104
420428msgid "Numbers and letters"
421−msgstr ""
429+msgstr "Αριθμοί και γράμματα"
422430
423431#: qml/Wizard/Pages/40-wifi.qml:28
424432msgid "Connect to Wi‑Fi"
425−msgstr ""
433+msgstr "Σύνδεση σε δίκτυο Wi-Fi"
426434
427435#: qml/Wizard/Pages/40-wifi.qml:147
428436msgid "Available networks…"
429−msgstr ""
437+msgstr "Διαθέσιμα δίκτυα..."
430438
431439#: qml/Wizard/Pages/40-wifi.qml:148
432440msgid "No available networks."
433−msgstr ""
441+msgstr "Δεν υπάρχουν διαθέσιμα δίκτυα."
434442
435443#: qml/Wizard/Pages/50-location.qml:27
436444msgid "Location"
437−msgstr ""
445+msgstr "Τοποθεσία"
438446
439447#: qml/Wizard/Pages/50-location.qml:62
440448msgid "Let the phone detect your location:"
441−msgstr ""
449+msgstr "Επιτρέψετε το τηλέφωνο να εντοπίσει την τοποθεσία σας:"
442450
443451#: qml/Wizard/Pages/50-location.qml:69
444452msgid "Using GPS only (less accurate)"
445−msgstr ""
453+msgstr "Χρήση μόνο GPS (λιγότερο ακριβή)"
446454
447455#: qml/Wizard/Pages/50-location.qml:86
448456msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
449457msgstr ""
458+"Χρήση GPS, ανωνυνοποιημένα δίκτυα Wi-Fi και πληροφορίες δικτύων κινητής "
459+"τηλεφωνίας (συνιστάται)"
450460
451461#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
452462#: qml/Wizard/Pages/50-location.qml:103
@@ -454,81 +464,89 @@ msgid ""
454464"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
455465"conditions</a>."
456466msgstr ""
467+"Ενεργοποιώντας αυτή την επιλογή, αποδέχεστε τους <a href='#'>όρους και "
468+"προϋποθέσεις</a> της Nokia HERE."
457469
458470#: qml/Wizard/Pages/50-location.qml:112
459471msgid "Not at all"
460−msgstr ""
472+msgstr "Καθόλου"
461473
462474#: qml/Wizard/Pages/50-location.qml:124
463475msgid "You can change your mind later in <b>System Settings</b>."
464476msgstr ""
477+"Μπορείτε να αλλάξετε τη γνώμη σας αργότερα στις <b>Ρυθμίσεις Συστήματος</b>."
465478
466479#: qml/Wizard/Pages/60-reporting.qml:24
467480msgid "Improving your experience"
468−msgstr ""
481+msgstr "Βελτιώστε την εμπειρία σας"
469482
470483#: qml/Wizard/Pages/60-reporting.qml:36
471484msgid ""
472485"Your phone is set up to automatically report errors to Canonical and its "
473486"partners, the makers of the operating system."
474487msgstr ""
488+"Το τηλέφωνό σας είναι ρυθμισμένο να στέλνει αυτόματα αναφορές σφαλμάτων στην "
489+"Canonical και τους συνεργάτες της, τους δημιουργούς τους λειτουργικού "
490+"συστήματος."
475491
476492#: qml/Wizard/Pages/60-reporting.qml:43
477493msgid ""
478494"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
479495"Privacy</b>"
480496msgstr ""
497+"Αυτό μπορεί να απενεργοποιηθεί στις <b>Ρυθμίσεις Συστήματος</b> στο πεδίο "
498+"<b>Ασφάλεια &amp; Ιδιωτικότητα</b>"
481499
482500#: qml/Wizard/Pages/80-finished.qml:24
483501msgid "All done"
484−msgstr ""
502+msgstr "Όλα έτοιμα"
485503
486504#: qml/Wizard/Pages/80-finished.qml:39
487505msgid "Nice work!"
488−msgstr ""
506+msgstr "Ωραία δουλειά!"
489507
490508#: qml/Wizard/Pages/80-finished.qml:46
491509msgid "Your phone is now ready to use."
492−msgstr ""
510+msgstr "Το τηλέφωνό σας είναι πλέον έτοιμο για χρήση."
493511
494512#: qml/Wizard/Pages/80-finished.qml:53
495513msgid "Finish"
496−msgstr ""
514+msgstr "Ολοκλήρωση"
497515
498516#: qml/Wizard/Pages/here-terms.qml:27
499517msgid "Terms & Conditions"
500−msgstr ""
518+msgstr "Όροι και προϋποθέσεις"
501519
502520#: qml/Wizard/Pages/passwd-confirm.qml:49
503521msgid "Confirm passphrase"
504−msgstr ""
522+msgstr "Επιβεβαίωση φράσης πρόσβασης"
505523
506524#: qml/Wizard/Pages/passwd-confirm.qml:50
507525msgid "Confirm passcode"
508−msgstr ""
526+msgstr "Επιβεβαιώστε τον κωδικό πρόσβασης"
509527
510528#: qml/Wizard/Pages/passwd-confirm.qml:53
511529#: qml/Wizard/Pages/passwd-confirm.qml:54
512530msgid "Please try again."
513−msgstr ""
531+msgstr "Παρακαλώ προσπαθήστε ξανά."
514532
515533#: qml/Wizard/Pages/passwd-set.qml:61
516534msgid "Choose your passcode"
517−msgstr ""
535+msgstr "Επιλογή κωδικού πρόσβασης"
518536
519537#: qml/Wizard/Pages/passwd-set.qml:68
520538msgid "Passphrase must be 4 characters long"
521−msgstr ""
539+msgstr "Η φράση πρόσβασης πρέπει να έχει 4 χαρακτήρες"
522540
523541#: qml/Wizard/StackButton.qml:39
524542#, qt-format
525543msgid "〈 %1"
526−msgstr ""
544+msgstr "〈 %1"
527545
528546#: qml/Wizard/StackButton.qml:42
529547#, qt-format
530548msgid "%1 〉"
531−msgstr ""
549+msgstr "%1 〉"
532550
533551#~ msgid "Confirm"
534552#~ msgstr "Επιβεβαίωση"
po/en_AU.po+1−1
@@ -15,7 +15,7 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-01-29 06:00+0000\n"
18+"X-Launchpad-Export-Date: 2015-01-30 07:08+0000\n"
1919"X-Generator: Launchpad (build 17306)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:130
po/es.po+3−3
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-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"
1212"Last-Translator: Adolfo Jayme <fitoschido@gmail.com>\n"
1313"Language-Team: Spanish <es@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
18+"X-Launchpad-Export-Date: 2015-02-02 06:54+0000\n"
1919"X-Generator: Launchpad (build 17306)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:130
@@ -290,7 +290,7 @@ msgstr "Falló, toque para reintentar"
290290
291291#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
292292msgid "Reply"
293−msgstr ""
293+msgstr "Responder"
294294
295295#: qml/Panel/Indicators/ModemInfoItem.qml:105
296296msgid "Unlock SIM"
po/fa.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-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"
1212"Last-Translator: Danial Behzadi <dani.behzi@gmail.com>\n"
1313"Language-Team: Persian <fa@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
19−"X-Generator: Launchpad (build 17306)\n"
18+"X-Launchpad-Export-Date: 2015-02-05 07:41+0000\n"
19+"X-Generator: Launchpad (build 17331)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:130
2222msgid "Password: "
@@ -283,7 +283,7 @@ msgstr "شکست خورد، برای تلاش دوباره ضربه بزنید"
283283
284284#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
285285msgid "Reply"
286−msgstr ""
286+msgstr "پاسخ"
287287
288288#: qml/Panel/Indicators/ModemInfoItem.qml:105
289289msgid "Unlock SIM"
po/fi.po+3−3
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-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"
1212"Last-Translator: Jiri Grönroos <Unknown>\n"
1313"Language-Team: Finnish <fi@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
18+"X-Launchpad-Export-Date: 2015-01-30 07:08+0000\n"
1919"X-Generator: Launchpad (build 17306)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:130
@@ -285,7 +285,7 @@ msgstr "Epäonnistunut, napauta yrittääksesi uudelleen"
285285
286286#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
287287msgid "Reply"
288−msgstr ""
288+msgstr "Vastaa"
289289
290290#: qml/Panel/Indicators/ModemInfoItem.qml:105
291291msgid "Unlock SIM"
po/fr.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-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"
1212"Last-Translator: Jean Marc <Unknown>\n"
1313"Language-Team: French <fr@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
19−"X-Generator: Launchpad (build 17306)\n"
18+"X-Launchpad-Export-Date: 2015-02-04 07:13+0000\n"
19+"X-Generator: Launchpad (build 17331)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:130
2222msgid "Password: "
@@ -294,7 +294,7 @@ msgstr "Échec, appuyez pour réessayer"
294294
295295#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
296296msgid "Reply"
297−msgstr ""
297+msgstr "Répondre"
298298
299299#: qml/Panel/Indicators/ModemInfoItem.qml:105
300300msgid "Unlock SIM"
po/gd.po+45−40
@@ -8,7 +8,7 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-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"
1212"Last-Translator: GunChleoc <Unknown>\n"
1313"Language-Team: Fòram na Gàidhlig\n"
1414"MIME-Version: 1.0\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : "
1818"(n > 2 && n < 20) ? 2 : 3;\n"
19−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
20−"X-Generator: Launchpad (build 17306)\n"
19+"X-Launchpad-Export-Date: 2015-02-13 06:54+0000\n"
20+"X-Generator: Launchpad (build 17336)\n"
2121"Language: gd\n"
2222
2323#: plugins/LightDM/Greeter.cpp:130
@@ -241,11 +241,11 @@ msgstr "Leig às airson ath-nuadhachadh…"
241241
242242#: qml/Dash/ScopesList.qml:66
243243msgid "Manage"
244−msgstr ""
244+msgstr "Stiùirich"
245245
246246#: qml/Dash/ScopesList.qml:112
247247msgid "Home"
248−msgstr ""
248+msgstr "Dhachaigh"
249249
250250#: qml/Dash/ScopesList.qml:113
251251msgid "Also installed"
@@ -297,7 +297,7 @@ msgstr "Dh'fhàillig leis, thoir gnogag gus feuchainn a-rithist"
297297
298298#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
299299msgid "Reply"
300−msgstr ""
300+msgstr "Freagair"
301301
302302#: qml/Panel/Indicators/ModemInfoItem.qml:105
303303msgid "Unlock SIM"
@@ -369,58 +369,58 @@ msgstr ""
369369
370370#: qml/Wizard/Page.qml:89
371371msgid "Back"
372−msgstr ""
372+msgstr "Air ais"
373373
374374#: qml/Wizard/Pages/10-welcome.qml:27
375375msgid "Hi!"
376−msgstr ""
376+msgstr "Shin thu!"
377377
378378#: qml/Wizard/Pages/10-welcome.qml:44
379379msgid "Welcome to your Ubuntu phone."
380−msgstr ""
380+msgstr "Fàilte dhan fhòn Ubuntu agad."
381381
382382#: qml/Wizard/Pages/10-welcome.qml:52
383383msgid "Let’s get started."
384−msgstr ""
384+msgstr "Tòisicheamaid."
385385
386386#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
387387#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
388388#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
389389#: qml/Wizard/Pages/passwd-set.qml:90
390390msgid "Continue"
391−msgstr ""
391+msgstr "Lean air adhart"
392392
393393#: qml/Wizard/Pages/20-sim.qml:25
394394msgid "Add a SIM card and restart your device"
395−msgstr ""
395+msgstr "Cuir cairt SIM ris 's tòisich an uidheam agad às ùr"
396396
397397#: qml/Wizard/Pages/20-sim.qml:55
398398msgid "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."
400400
401401#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
402402msgid "Skip"
403−msgstr ""
403+msgstr "Leum thairis air"
404404
405405#: qml/Wizard/Pages/30-passwd-type.qml:39
406406msgid "Lock security"
407−msgstr ""
407+msgstr "Glas tèarainteachd"
408408
409409#: qml/Wizard/Pages/30-passwd-type.qml:74
410410msgid "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."
412412
413413#: qml/Wizard/Pages/30-passwd-type.qml:97
414414msgid "Swipe"
415−msgstr ""
415+msgstr "Grad-shlaighd"
416416
417417#: qml/Wizard/Pages/30-passwd-type.qml:98
418418msgid "No security"
419−msgstr ""
419+msgstr "Gun tèarainteachd"
420420
421421#: qml/Wizard/Pages/30-passwd-type.qml:100
422422msgid "Passcode"
423−msgstr ""
423+msgstr "Còd-faire"
424424
425425#: qml/Wizard/Pages/30-passwd-type.qml:101
426426msgid "4 digits only"
@@ -428,31 +428,31 @@ msgstr ""
428428
429429#: qml/Wizard/Pages/30-passwd-type.qml:103
430430msgid "Passphrase"
431−msgstr ""
431+msgstr "Abairt-fhaire"
432432
433433#: qml/Wizard/Pages/30-passwd-type.qml:104
434434msgid "Numbers and letters"
435−msgstr ""
435+msgstr "Àireamhan 's litrichean"
436436
437437#: qml/Wizard/Pages/40-wifi.qml:28
438438msgid "Connect to Wi‑Fi"
439−msgstr ""
439+msgstr "Ceangail ri Wi-Fi"
440440
441441#: qml/Wizard/Pages/40-wifi.qml:147
442442msgid "Available networks…"
443−msgstr ""
443+msgstr "Lìonraidhean ri làimh…"
444444
445445#: qml/Wizard/Pages/40-wifi.qml:148
446446msgid "No available networks."
447−msgstr ""
447+msgstr "Chan eil lìonra ri làimh."
448448
449449#: qml/Wizard/Pages/50-location.qml:27
450450msgid "Location"
451−msgstr ""
451+msgstr "Àite"
452452
453453#: qml/Wizard/Pages/50-location.qml:62
454454msgid "Let the phone detect your location:"
455−msgstr ""
455+msgstr "Faodaidh am fòn aithneachadh far a bheil thu:"
456456
457457#: qml/Wizard/Pages/50-location.qml:69
458458msgid "Using GPS only (less accurate)"
@@ -471,7 +471,7 @@ msgstr ""
471471
472472#: qml/Wizard/Pages/50-location.qml:112
473473msgid "Not at all"
474−msgstr ""
474+msgstr "Chan fhaod idir"
475475
476476#: qml/Wizard/Pages/50-location.qml:124
477477msgid "You can change your mind later in <b>System Settings</b>."
@@ -479,70 +479,75 @@ msgstr ""
479479
480480#: qml/Wizard/Pages/60-reporting.qml:24
481481msgid "Improving your experience"
482−msgstr ""
482+msgstr "A' cur piseach air a' ghnàth-eòlas agad"
483483
484484#: qml/Wizard/Pages/60-reporting.qml:36
485485msgid ""
486486"Your phone is set up to automatically report errors to Canonical and its "
487487"partners, the makers of the operating system."
488488msgstr ""
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."
489492
490493#: qml/Wizard/Pages/60-reporting.qml:43
491494msgid ""
492495"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
493496"Privacy</b>"
494497msgstr ""
498+"Gabhaidh seo cur à comas ann an <b>Roghainnean an t-siostaim</b> fo "
499+"<b>Thèarainteachd ⁊ prìobhaideachd</b>"
495500
496501#: qml/Wizard/Pages/80-finished.qml:24
497502msgid "All done"
498−msgstr ""
503+msgstr "Coileanta"
499504
500505#: qml/Wizard/Pages/80-finished.qml:39
501506msgid "Nice work!"
502−msgstr ""
507+msgstr "Shin thu!"
503508
504509#: qml/Wizard/Pages/80-finished.qml:46
505510msgid "Your phone is now ready to use."
506−msgstr ""
511+msgstr "Gabhaidh am fòn agad cleachdadh a-nis."
507512
508513#: qml/Wizard/Pages/80-finished.qml:53
509514msgid "Finish"
510−msgstr ""
515+msgstr "Crìochnaich"
511516
512517#: qml/Wizard/Pages/here-terms.qml:27
513518msgid "Terms & Conditions"
514−msgstr ""
519+msgstr "Teirmichean ⁊ cumhaichean"
515520
516521#: qml/Wizard/Pages/passwd-confirm.qml:49
517522msgid "Confirm passphrase"
518−msgstr ""
523+msgstr "Dearbhaich an abairt-fhaire"
519524
520525#: qml/Wizard/Pages/passwd-confirm.qml:50
521526msgid "Confirm passcode"
522−msgstr ""
527+msgstr "Dearbhaich an còd-faire"
523528
524529#: qml/Wizard/Pages/passwd-confirm.qml:53
525530#: qml/Wizard/Pages/passwd-confirm.qml:54
526531msgid "Please try again."
527−msgstr ""
532+msgstr "Feuch ris a-rithist."
528533
529534#: qml/Wizard/Pages/passwd-set.qml:61
530535msgid "Choose your passcode"
531−msgstr ""
536+msgstr "Tagh an còd-faire agad"
532537
533538#: qml/Wizard/Pages/passwd-set.qml:68
534539msgid "Passphrase must be 4 characters long"
535−msgstr ""
540+msgstr "Feumaidh an abairt-fhaire a bhith 4 caractaran a dh'fhaid"
536541
537542#: qml/Wizard/StackButton.qml:39
538543#, qt-format
539544msgid "〈 %1"
540−msgstr ""
545+msgstr "〈 %1"
541546
542547#: qml/Wizard/StackButton.qml:42
543548#, qt-format
544549msgid "%1 〉"
545−msgstr ""
550+msgstr "%1 〉"
546551
547552#~ msgid "Confirm"
548553#~ msgstr "Dearbhaich"
po/gl.po+7−7
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-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"
1212"Last-Translator: Marcos Lans <Unknown>\n"
1313"Language-Team: Galician <gl@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-01-29 06:00+0000\n"
19−"X-Generator: Launchpad (build 17306)\n"
18+"X-Launchpad-Export-Date: 2015-02-13 06:54+0000\n"
19+"X-Generator: Launchpad (build 17336)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:130
2222msgid "Password: "
@@ -56,7 +56,7 @@ msgstr "Saír da sesión"
5656
5757#: qml/Components/Dialogs.qml:87
5858msgid "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?"
6060
6161#: qml/Components/Dialogs.qml:89 qml/Components/Dialogs.qml:111
6262#: qml/Components/Dialogs.qml:134
@@ -74,7 +74,7 @@ msgstr "Apagar"
7474
7575#: qml/Components/Dialogs.qml:109
7676msgid "Are you sure you want to shut down?"
77−msgstr "Está seguro de querer apagar?"
77+msgstr "Confirma que desexa apagar?"
7878
7979#: qml/Components/Dialogs.qml:131
8080msgid "Reboot"
@@ -82,7 +82,7 @@ msgstr "Reiniciar"
8282
8383#: qml/Components/Dialogs.qml:132
8484msgid "Are you sure you want to reboot?"
85−msgstr "Está seguro de querer reiniciar?"
85+msgstr "Confirma que desexa reiniciar?"
8686
8787#: qml/Components/Dialogs.qml:154
8888msgid "Power"
@@ -92,7 +92,7 @@ msgstr "Enerxía"
9292msgid ""
9393"Are you sure you would like\n"
9494"to power off?"
95−msgstr "Está seguro de querer apagar?"
95+msgstr "Confirma que desexa apagar?"
9696
9797#: qml/Components/Dialogs.qml:157
9898msgid "Power off"
po/hu.po+3−3
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-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"
1212"Last-Translator: Richard Somlói <ricsipontaz@gmail.com>\n"
1313"Language-Team: Hungarian <hu@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
18+"X-Launchpad-Export-Date: 2015-01-30 07:08+0000\n"
1919"X-Generator: Launchpad (build 17306)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:130
@@ -285,7 +285,7 @@ msgstr "Sikertelen, koppintson az újrapróbáláshoz"
285285
286286#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
287287msgid "Reply"
288−msgstr ""
288+msgstr "Válasz"
289289
290290#: qml/Panel/Indicators/ModemInfoItem.qml:105
291291msgid "Unlock SIM"
po/nl.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-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"
1212"Last-Translator: rob <linuxned@gmail.com>\n"
1313"Language-Team: Dutch <nl@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
19−"X-Generator: Launchpad (build 17306)\n"
18+"X-Launchpad-Export-Date: 2015-02-13 06:54+0000\n"
19+"X-Generator: Launchpad (build 17336)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:130
2222msgid "Password: "
@@ -287,7 +287,7 @@ msgstr "Mislukt, tik om het opnieuw te proberen"
287287
288288#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
289289msgid "Reply"
290−msgstr ""
290+msgstr "Beantwoorden"
291291
292292#: qml/Panel/Indicators/ModemInfoItem.qml:105
293293msgid "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 ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-01-22 11:09+0100\n"
11−"PO-Revision-Date: 2014-08-18 20:25+0000\n"
12−"Last-Translator: Paweł Stroka <pawstr@gmail.com>\n"
11+"PO-Revision-Date: 2015-02-14 23:41+0000\n"
12+"Last-Translator: GTriderXC <gtriderxc@yahoo.com>\n"
1313"Language-Team: Polish <pl@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
1818"|| n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
20−"X-Generator: Launchpad (build 17306)\n"
19+"X-Launchpad-Export-Date: 2015-02-15 06:35+0000\n"
20+"X-Generator: Launchpad (build 17341)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:130
2323msgid "Password: "
24−msgstr ""
24+msgstr "Hasło: "
2525
2626#: plugins/Unity/Launcher/launcheritem.cpp:45
2727#: plugins/Unity/Launcher/launcheritem.cpp:100
@@ -34,78 +34,82 @@ msgstr "Odepnij skrót"
3434
3535#: qml/Components/DelayedLockscreen.qml:47
3636msgid "Device Locked"
37−msgstr ""
37+msgstr "Urządzenie zablokowane"
3838
3939#: qml/Components/DelayedLockscreen.qml:62
4040msgid "You have been locked out due to too many failed passphrase attempts."
4141msgstr ""
42+"Zostałeś zablokowany z powodu zbyt wielu błędnych prób podania hasła."
4243
4344#: qml/Components/DelayedLockscreen.qml:63
4445msgid "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"
4647
4748#: qml/Components/DelayedLockscreen.qml:72
4849#, qt-format
4950msgid "Please wait %1 minute and then try again…"
5051msgid_plural "Please wait %1 minutes and then try again…"
51−msgstr[0] ""
52−msgstr[1] ""
52+msgstr[0] "Poczekaj %1 minutę i spróbuj ponownie…"
53+msgstr[1] "Poczekaj %1 minuty i spróbuj ponownie…"
54+msgstr[2] "Poczekaj %1 minut i spróbuj ponownie…"
5355
5456#: qml/Components/Dialogs.qml:86
5557msgid "Log out"
56−msgstr ""
58+msgstr "Wyloguj"
5759
5860#: qml/Components/Dialogs.qml:87
5961msgid "Are you sure you want to log out?"
60−msgstr ""
62+msgstr "Czy na pewno wylogować?"
6163
6264#: qml/Components/Dialogs.qml:89 qml/Components/Dialogs.qml:111
6365#: qml/Components/Dialogs.qml:134
6466msgid "No"
65−msgstr ""
67+msgstr "Nie"
6668
6769#: qml/Components/Dialogs.qml:95 qml/Components/Dialogs.qml:117
6870#: qml/Components/Dialogs.qml:140
6971msgid "Yes"
70−msgstr ""
72+msgstr "Tak"
7173
7274#: qml/Components/Dialogs.qml:108
7375msgid "Shut down"
74−msgstr ""
76+msgstr "Wyłącz"
7577
7678#: qml/Components/Dialogs.qml:109
7779msgid "Are you sure you want to shut down?"
78−msgstr ""
80+msgstr "Czy na pewno wyłączyć?"
7981
8082#: qml/Components/Dialogs.qml:131
8183msgid "Reboot"
82−msgstr ""
84+msgstr "Uruchom ponownie"
8385
8486#: qml/Components/Dialogs.qml:132
8587msgid "Are you sure you want to reboot?"
86−msgstr ""
88+msgstr "Czy na pewno uruchomić ponownie?"
8789
8890#: qml/Components/Dialogs.qml:154
8991msgid "Power"
90−msgstr ""
92+msgstr "Zasilanie"
9193
9294#: qml/Components/Dialogs.qml:155
9395msgid ""
9496"Are you sure you would like\n"
9597"to power off?"
9698msgstr ""
99+"Jesteś pewny że chcesz\n"
100+"wyłączyć?"
97101
98102#: qml/Components/Dialogs.qml:157
99103msgid "Power off"
100−msgstr ""
104+msgstr "Wyłącz"
101105
102106#: qml/Components/Dialogs.qml:166
103107msgid "Restart"
104−msgstr ""
108+msgstr "Uruchom ponownie"
105109
106110#: qml/Components/Dialogs.qml:175
107111msgid "Cancel"
108−msgstr ""
112+msgstr "Anuluj"
109113
110114#: qml/Components/EdgeDemoOverlay.qml:151
111115msgid "Skip intro"
@@ -159,7 +163,7 @@ msgstr ""
159163
160164#: qml/Components/Lockscreen.qml:238
161165msgid "Return to Call"
162−msgstr ""
166+msgstr "Powróć do rozmowy"
163167
164168#: qml/Components/Lockscreen.qml:238
165169msgid "Emergency Call"
@@ -184,19 +188,19 @@ msgstr "Szukaj"
184188
185189#: qml/Dash/PageHeader.qml:267
186190msgid "Store"
187−msgstr ""
191+msgstr "Zachowaj"
188192
189193#: qml/Dash/PageHeader.qml:284
190194msgid "Settings"
191−msgstr ""
195+msgstr "Ustawienia"
192196
193197#: qml/Dash/PageHeader.qml:291
194198msgid "Remove from Favorites"
195−msgstr ""
199+msgstr "Usuń z ulubionych"
196200
197201#: qml/Dash/PageHeader.qml:291
198202msgid "Add to Favorites"
199−msgstr ""
203+msgstr "Dodaj do ulubionych"
200204
201205#: qml/Dash/Previews/PreviewActionCombo.qml:35
202206msgid "More..."
@@ -220,15 +224,15 @@ msgstr "Wyślij"
220224
221225#: qml/Dash/PullToRefreshScopeStyle.qml:55
222226msgid "Pull to refresh…"
223−msgstr ""
227+msgstr "Przeciągnij by odświeżyć"
224228
225229#: qml/Dash/PullToRefreshScopeStyle.qml:60
226230msgid "Release to refresh…"
227−msgstr ""
231+msgstr "Zwolnij by odświeżyć"
228232
229233#: qml/Dash/ScopesList.qml:66
230234msgid "Manage"
231−msgstr ""
235+msgstr "Zarządzaj"
232236
233237#: qml/Dash/ScopesList.qml:112
234238msgid "Home"
@@ -236,11 +240,11 @@ msgstr ""
236240
237241#: qml/Dash/ScopesList.qml:113
238242msgid "Also installed"
239−msgstr ""
243+msgstr "Również zainstalowane"
240244
241245#: qml/Greeter/Greeter.qml:133
242246msgid "Unlock"
243−msgstr ""
247+msgstr "Odblokuj"
244248
245249#: qml/Notifications/NotificationMenuItemFactory.qml:128
246250msgid "Show password"
@@ -256,7 +260,7 @@ msgstr "Konferencja"
256260
257261#: qml/Panel/Indicators/MenuItemFactory.qml:643
258262msgid "Nothing is playing"
259−msgstr ""
263+msgstr "Nic nie jest odtwarzane"
260264
261265#: qml/Panel/Indicators/MenuItemFactory.qml:791
262266msgid "In queue…"
@@ -284,45 +288,45 @@ msgstr "Błąd, kliknij aby powtórzyć"
284288
285289#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
286290msgid "Reply"
287−msgstr ""
291+msgstr "Odpowiedz"
288292
289293#: qml/Panel/Indicators/ModemInfoItem.qml:105
290294msgid "Unlock SIM"
291−msgstr ""
295+msgstr "Odblokuj kartę SIM"
292296
293297#: qml/Panel/Indicators/RoamingIndication.qml:27
294298msgid "Roaming"
295−msgstr ""
299+msgstr "Roaming"
296300
297301#: qml/Shell.qml:390
298302#, qt-format
299303msgid "Enter %1"
300−msgstr ""
304+msgstr "Wprowadź %1"
301305
302306#: qml/Shell.qml:391 qml/Wizard/Pages/passwd-set.qml:60
303307msgid "Enter passphrase"
304−msgstr ""
308+msgstr "Wprowadź hasło"
305309
306310#: qml/Shell.qml:392
307311msgid "Enter passcode"
308−msgstr ""
312+msgstr "Podaj kod"
309313
310314#: qml/Shell.qml:393
311315#, qt-format
312316msgid "Sorry, incorrect %1"
313−msgstr ""
317+msgstr "Niestety niepoprawne %1"
314318
315319#: qml/Shell.qml:394
316320msgid "Sorry, incorrect passphrase"
317−msgstr ""
321+msgstr "Niestety, błędne hasło"
318322
319323#: qml/Shell.qml:395
320324msgid "Please re-enter"
321−msgstr ""
325+msgstr "Proszę wprowadź ponownie"
322326
323327#: qml/Shell.qml:396
324328msgid "Sorry, incorrect passcode"
325−msgstr ""
329+msgstr "Niestety ten kod nie jest poprawny"
326330
327331#: qml/Shell.qml:490 qml/Wizard/Pages/passwd-confirm.qml:53
328332msgid "Sorry, incorrect passphrase."
@@ -334,114 +338,119 @@ msgstr "Niestety, błędny kod"
334338
335339#: qml/Shell.qml:492
336340msgid "This will be your last attempt."
337−msgstr ""
341+msgstr "To już jest ostatnia próba."
338342
339343#: qml/Shell.qml:494
340344msgid ""
341345"If passphrase is entered incorrectly, your phone will conduct a factory "
342346"reset and all personal data will be deleted."
343347msgstr ""
348+"Jeśli zostanie wprowadzone błędne hasło, telefon przywróci ustawienia "
349+"fabryczne i wszystkie dane osobiste zostaną usunięte."
344350
345351#: qml/Shell.qml:495
346352msgid ""
347353"If passcode is entered incorrectly, your phone will conduct a factory reset "
348354"and all personal data will be deleted."
349355msgstr ""
356+"Jeśli zostanie wprowadzony błędny kod, telefon przywróci ustawienia "
357+"fabryczne i wszystkie dane osobiste zostaną usunięte."
350358
351359#: qml/Wizard/Page.qml:89
352360msgid "Back"
353−msgstr ""
361+msgstr "Cofnij"
354362
355363#: qml/Wizard/Pages/10-welcome.qml:27
356364msgid "Hi!"
357−msgstr ""
365+msgstr "Witaj!"
358366
359367#: qml/Wizard/Pages/10-welcome.qml:44
360368msgid "Welcome to your Ubuntu phone."
361−msgstr ""
369+msgstr "Witamy w telefonie z systemem Ubuntu."
362370
363371#: qml/Wizard/Pages/10-welcome.qml:52
364372msgid "Let’s get started."
365−msgstr ""
373+msgstr "Zaczynajmy."
366374
367375#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
368376#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
369377#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
370378#: qml/Wizard/Pages/passwd-set.qml:90
371379msgid "Continue"
372−msgstr ""
380+msgstr "Kontynuuj"
373381
374382#: qml/Wizard/Pages/20-sim.qml:25
375383msgid "Add a SIM card and restart your device"
376−msgstr ""
384+msgstr "Włóż kartę SIM i uruchom urządzenie ponownie"
377385
378386#: qml/Wizard/Pages/20-sim.qml:55
379387msgid "Without it, you won’t be able to make calls or use text messaging."
380388msgstr ""
389+"Inaczej wykonywanie połączeń i wysyłanie wiadomości nie będzie możliwe."
381390
382391#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
383392msgid "Skip"
384−msgstr ""
393+msgstr "Pomiń"
385394
386395#: qml/Wizard/Pages/30-passwd-type.qml:39
387396msgid "Lock security"
388−msgstr ""
397+msgstr "Blokada"
389398
390399#: qml/Wizard/Pages/30-passwd-type.qml:74
391400msgid "Please select how you’d like to unlock your phone."
392−msgstr ""
401+msgstr "Ustaw proszę jak ma być odblokowywane to urządzenie"
393402
394403#: qml/Wizard/Pages/30-passwd-type.qml:97
395404msgid "Swipe"
396−msgstr ""
405+msgstr "Przeciągnięcie"
397406
398407#: qml/Wizard/Pages/30-passwd-type.qml:98
399408msgid "No security"
400−msgstr ""
409+msgstr "Brak zabezpieczenia"
401410
402411#: qml/Wizard/Pages/30-passwd-type.qml:100
403412msgid "Passcode"
404−msgstr ""
413+msgstr "Kod"
405414
406415#: qml/Wizard/Pages/30-passwd-type.qml:101
407416msgid "4 digits only"
408−msgstr ""
417+msgstr "Tylko 4 cyfry"
409418
410419#: qml/Wizard/Pages/30-passwd-type.qml:103
411420msgid "Passphrase"
412−msgstr ""
421+msgstr "Hasło"
413422
414423#: qml/Wizard/Pages/30-passwd-type.qml:104
415424msgid "Numbers and letters"
416−msgstr ""
425+msgstr "Cyfry i litery"
417426
418427#: qml/Wizard/Pages/40-wifi.qml:28
419428msgid "Connect to Wi‑Fi"
420−msgstr ""
429+msgstr "Połącz z Wi-Fi"
421430
422431#: qml/Wizard/Pages/40-wifi.qml:147
423432msgid "Available networks…"
424−msgstr ""
433+msgstr "Dostępne sieci"
425434
426435#: qml/Wizard/Pages/40-wifi.qml:148
427436msgid "No available networks."
428−msgstr ""
437+msgstr "Brak dostępnych sieci"
429438
430439#: qml/Wizard/Pages/50-location.qml:27
431440msgid "Location"
432−msgstr ""
441+msgstr "Lokalizacja"
433442
434443#: qml/Wizard/Pages/50-location.qml:62
435444msgid "Let the phone detect your location:"
436−msgstr ""
445+msgstr "Pozwól temu urządzeniu na wykrywanie lokalizacji:"
437446
438447#: qml/Wizard/Pages/50-location.qml:69
439448msgid "Using GPS only (less accurate)"
440−msgstr ""
449+msgstr "Używając tylko GPS (mniej dokładne)"
441450
442451#: qml/Wizard/Pages/50-location.qml:86
443452msgid "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)"
445454
446455#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
447456#: qml/Wizard/Pages/50-location.qml:103
@@ -449,81 +458,87 @@ msgid ""
449458"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
450459"conditions</a>."
451460msgstr ""
461+"Zaznaczając tę opcję akceptujesz <a href='#'>warunki i zasady regulaminu</a> "
462+"Nokia HERE."
452463
453464#: qml/Wizard/Pages/50-location.qml:112
454465msgid "Not at all"
455−msgstr ""
466+msgstr "Brak zgody"
456467
457468#: qml/Wizard/Pages/50-location.qml:124
458469msgid "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>."
460471
461472#: qml/Wizard/Pages/60-reporting.qml:24
462473msgid "Improving your experience"
463−msgstr ""
474+msgstr "Rozwój dostarczanych usług"
464475
465476#: qml/Wizard/Pages/60-reporting.qml:36
466477msgid ""
467478"Your phone is set up to automatically report errors to Canonical and its "
468479"partners, the makers of the operating system."
469480msgstr ""
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"
470483
471484#: qml/Wizard/Pages/60-reporting.qml:43
472485msgid ""
473486"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
474487"Privacy</b>"
475488msgstr ""
489+"Opcję tę można wyłączyć w <b>ustawieniach systemu</b> przez menu "
490+"<b>Prywatność i zabezpieczenia</b>"
476491
477492#: qml/Wizard/Pages/80-finished.qml:24
478493msgid "All done"
479−msgstr ""
494+msgstr "Gotowe"
480495
481496#: qml/Wizard/Pages/80-finished.qml:39
482497msgid "Nice work!"
483−msgstr ""
498+msgstr "Dobra robota!"
484499
485500#: qml/Wizard/Pages/80-finished.qml:46
486501msgid "Your phone is now ready to use."
487−msgstr ""
502+msgstr "Twoje urządzenie jest gotowe do pracy"
488503
489504#: qml/Wizard/Pages/80-finished.qml:53
490505msgid "Finish"
491−msgstr ""
506+msgstr "Zakończ"
492507
493508#: qml/Wizard/Pages/here-terms.qml:27
494509msgid "Terms & Conditions"
495−msgstr ""
510+msgstr "Zasady i warunki"
496511
497512#: qml/Wizard/Pages/passwd-confirm.qml:49
498513msgid "Confirm passphrase"
499−msgstr ""
514+msgstr "Potwierdź hasło"
500515
501516#: qml/Wizard/Pages/passwd-confirm.qml:50
502517msgid "Confirm passcode"
503−msgstr ""
518+msgstr "potwierdź kod"
504519
505520#: qml/Wizard/Pages/passwd-confirm.qml:53
506521#: qml/Wizard/Pages/passwd-confirm.qml:54
507522msgid "Please try again."
508−msgstr ""
523+msgstr "Proszę spróbować ponownie."
509524
510525#: qml/Wizard/Pages/passwd-set.qml:61
511526msgid "Choose your passcode"
512−msgstr ""
527+msgstr "Wybierz kod"
513528
514529#: qml/Wizard/Pages/passwd-set.qml:68
515530msgid "Passphrase must be 4 characters long"
516−msgstr ""
531+msgstr "Hasło musi składać się z 4 znaków"
517532
518533#: qml/Wizard/StackButton.qml:39
519534#, qt-format
520535msgid "〈 %1"
521−msgstr ""
536+msgstr "〈 %1"
522537
523538#: qml/Wizard/StackButton.qml:42
524539#, qt-format
525540msgid "%1 〉"
526−msgstr ""
541+msgstr "%1 〉"
527542
528543#~ msgid "Confirm"
529544#~ msgstr "Potwierdź"
po/ru.po+5−5
@@ -8,7 +8,7 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-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"
1212"Last-Translator: ☠Jay ZDLin☠ <Unknown>\n"
1313"Language-Team: Russian <ru@li.org>\n"
1414"MIME-Version: 1.0\n"
@@ -16,7 +16,7 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-01-28 06:30+0000\n"
19+"X-Launchpad-Export-Date: 2015-01-30 07:08+0000\n"
2020"X-Generator: Launchpad (build 17306)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:130
@@ -292,7 +292,7 @@ msgstr "Сбой, коснитесь для повтора"
292292
293293#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
294294msgid "Reply"
295−msgstr ""
295+msgstr "Ответить"
296296
297297#: qml/Panel/Indicators/ModemInfoItem.qml:105
298298msgid "Unlock SIM"
@@ -468,7 +468,7 @@ msgstr ""
468468
469469#: qml/Wizard/Pages/50-location.qml:112
470470msgid "Not at all"
471−msgstr ""
471+msgstr "Нет (никогда)"
472472
473473#: qml/Wizard/Pages/50-location.qml:124
474474msgid "You can change your mind later in <b>System Settings</b>."
@@ -477,7 +477,7 @@ msgstr ""
477477
478478#: qml/Wizard/Pages/60-reporting.qml:24
479479msgid "Improving your experience"
480−msgstr ""
480+msgstr "Улучшение производительности"
481481
482482#: qml/Wizard/Pages/60-reporting.qml:36
483483msgid ""
po/sl.po+3−3
@@ -8,7 +8,7 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-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"
1212"Last-Translator: Damir Jerovšek <Unknown>\n"
1313"Language-Team: Slovenian <sl@li.org>\n"
1414"MIME-Version: 1.0\n"
@@ -16,7 +16,7 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
1818"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"
2020"X-Generator: Launchpad (build 17306)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:130
@@ -288,7 +288,7 @@ msgstr "Spodletelo, tapnite za ponovni poskus"
288288
289289#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
290290msgid "Reply"
291−msgstr ""
291+msgstr "Odgovori"
292292
293293#: qml/Panel/Indicators/ModemInfoItem.qml:105
294294msgid "Unlock SIM"
po/tr.po+79−69
@@ -8,19 +8,19 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"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"
1212"Last-Translator: Volkan Gezer <Unknown>\n"
1313"Language-Team: Turkish <tr@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-01-28 06:31+0000\n"
19−"X-Generator: Launchpad (build 17306)\n"
18+"X-Launchpad-Export-Date: 2015-02-05 07:41+0000\n"
19+"X-Generator: Launchpad (build 17331)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:130
2222msgid "Password: "
23−msgstr ""
23+msgstr "Parola: "
2424
2525#: plugins/Unity/Launcher/launcheritem.cpp:45
2626#: plugins/Unity/Launcher/launcheritem.cpp:100
@@ -33,30 +33,30 @@ msgstr "Kısayol iğnesini kaldır"
3333
3434#: qml/Components/DelayedLockscreen.qml:47
3535msgid "Device Locked"
36−msgstr ""
36+msgstr "Cihaz Kilitli"
3737
3838#: qml/Components/DelayedLockscreen.qml:62
3939msgid "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."
4141
4242#: qml/Components/DelayedLockscreen.qml:63
4343msgid "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."
4545
4646#: qml/Components/DelayedLockscreen.qml:72
4747#, qt-format
4848msgid "Please wait %1 minute and then try again…"
4949msgid_plural "Please wait %1 minutes and then try again…"
50−msgstr[0] ""
51−msgstr[1] ""
50+msgstr[0] "Lütfen %1 dakika bekleyin ve yeniden deneyin..."
51+msgstr[1] "Lütfen %1 dakika bekleyin ve yeniden deneyin..."
5252
5353#: qml/Components/Dialogs.qml:86
5454msgid "Log out"
55−msgstr ""
55+msgstr "Oturumu kapat"
5656
5757#: qml/Components/Dialogs.qml:87
5858msgid "Are you sure you want to log out?"
59−msgstr ""
59+msgstr "Oturumu kapatmak istediğinize emin misiniz?"
6060
6161#: qml/Components/Dialogs.qml:89 qml/Components/Dialogs.qml:111
6262#: qml/Components/Dialogs.qml:134
@@ -74,7 +74,7 @@ msgstr "Bilgisayarı Kapat"
7474
7575#: qml/Components/Dialogs.qml:109
7676msgid "Are you sure you want to shut down?"
77−msgstr ""
77+msgstr "Kapatmak istediğinize emin misiniz?"
7878
7979#: qml/Components/Dialogs.qml:131
8080msgid "Reboot"
@@ -86,7 +86,7 @@ msgstr "Bilgisayarı yeniden başlatmak istediğinize emin misiniz?"
8686
8787#: qml/Components/Dialogs.qml:154
8888msgid "Power"
89−msgstr ""
89+msgstr "Güç"
9090
9191#: qml/Components/Dialogs.qml:155
9292msgid ""
@@ -96,15 +96,15 @@ msgstr "Bilgisayarı kapatmak istediğinize emin misiniz?"
9696
9797#: qml/Components/Dialogs.qml:157
9898msgid "Power off"
99−msgstr ""
99+msgstr "Kapat"
100100
101101#: qml/Components/Dialogs.qml:166
102102msgid "Restart"
103−msgstr ""
103+msgstr "Yeniden başlat"
104104
105105#: qml/Components/Dialogs.qml:175
106106msgid "Cancel"
107−msgstr ""
107+msgstr "İptal"
108108
109109#: qml/Components/EdgeDemoOverlay.qml:151
110110msgid "Skip intro"
@@ -157,7 +157,7 @@ msgstr ""
157157
158158#: qml/Components/Lockscreen.qml:238
159159msgid "Return to Call"
160−msgstr ""
160+msgstr "Çağrıya Dön"
161161
162162#: qml/Components/Lockscreen.qml:238
163163msgid "Emergency Call"
@@ -186,11 +186,11 @@ msgstr "Mağaza"
186186
187187#: qml/Dash/PageHeader.qml:284
188188msgid "Settings"
189−msgstr ""
189+msgstr "Ayarlar"
190190
191191#: qml/Dash/PageHeader.qml:291
192192msgid "Remove from Favorites"
193−msgstr ""
193+msgstr "Sık kullanılanlardan kaldır"
194194
195195#: qml/Dash/PageHeader.qml:291
196196msgid "Add to Favorites"
@@ -218,27 +218,27 @@ msgstr "Gönder"
218218
219219#: qml/Dash/PullToRefreshScopeStyle.qml:55
220220msgid "Pull to refresh…"
221−msgstr ""
221+msgstr "Yenilemek için çekin..."
222222
223223#: qml/Dash/PullToRefreshScopeStyle.qml:60
224224msgid "Release to refresh…"
225−msgstr ""
225+msgstr "Yenilemek için bırakın..."
226226
227227#: qml/Dash/ScopesList.qml:66
228228msgid "Manage"
229−msgstr ""
229+msgstr "Yönet"
230230
231231#: qml/Dash/ScopesList.qml:112
232232msgid "Home"
233−msgstr ""
233+msgstr "Ev"
234234
235235#: qml/Dash/ScopesList.qml:113
236236msgid "Also installed"
237−msgstr ""
237+msgstr "Ayrıca kurulan"
238238
239239#: qml/Greeter/Greeter.qml:133
240240msgid "Unlock"
241−msgstr ""
241+msgstr "Kilidi Aç"
242242
243243#: qml/Notifications/NotificationMenuItemFactory.qml:128
244244msgid "Show password"
@@ -246,15 +246,15 @@ msgstr "Parolayı göster"
246246
247247#: qml/Panel/ActiveCallHint.qml:79
248248msgid "Tap to return to call..."
249−msgstr ""
249+msgstr "Çağrıya dönmek için dokunun..."
250250
251251#: qml/Panel/ActiveCallHint.qml:92
252252msgid "Conference"
253−msgstr ""
253+msgstr "Konferans"
254254
255255#: qml/Panel/Indicators/MenuItemFactory.qml:643
256256msgid "Nothing is playing"
257−msgstr ""
257+msgstr "Hiçbir şey çalmıyor"
258258
259259#: qml/Panel/Indicators/MenuItemFactory.qml:791
260260msgid "In queue…"
@@ -282,7 +282,7 @@ msgstr "Başarısız, yeniden denemek için dokunun"
282282
283283#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
284284msgid "Reply"
285−msgstr ""
285+msgstr "Yanıtla"
286286
287287#: qml/Panel/Indicators/ModemInfoItem.qml:105
288288msgid "Unlock SIM"
@@ -290,20 +290,20 @@ msgstr "SIM kilidini aç"
290290
291291#: qml/Panel/Indicators/RoamingIndication.qml:27
292292msgid "Roaming"
293−msgstr ""
293+msgstr "Dolaşım"
294294
295295#: qml/Shell.qml:390
296296#, qt-format
297297msgid "Enter %1"
298−msgstr ""
298+msgstr "%1 Girin"
299299
300300#: qml/Shell.qml:391 qml/Wizard/Pages/passwd-set.qml:60
301301msgid "Enter passphrase"
302−msgstr ""
302+msgstr "Parolayı girin"
303303
304304#: qml/Shell.qml:392
305305msgid "Enter passcode"
306−msgstr ""
306+msgstr "Parolayı girin"
307307
308308#: qml/Shell.qml:393
309309#, qt-format
@@ -316,7 +316,7 @@ msgstr "Üzgünüm, hatalı parola"
316316
317317#: qml/Shell.qml:395
318318msgid "Please re-enter"
319−msgstr ""
319+msgstr "Lütfen yeniden girin"
320320
321321#: qml/Shell.qml:396
322322msgid "Sorry, incorrect passcode"
@@ -352,98 +352,101 @@ msgstr ""
352352
353353#: qml/Wizard/Page.qml:89
354354msgid "Back"
355−msgstr ""
355+msgstr "Geri"
356356
357357#: qml/Wizard/Pages/10-welcome.qml:27
358358msgid "Hi!"
359−msgstr ""
359+msgstr "Merhaba!"
360360
361361#: qml/Wizard/Pages/10-welcome.qml:44
362362msgid "Welcome to your Ubuntu phone."
363−msgstr ""
363+msgstr "Ubuntu telefonunuza hoş geldiniz."
364364
365365#: qml/Wizard/Pages/10-welcome.qml:52
366366msgid "Let’s get started."
367−msgstr ""
367+msgstr "Hadi başlayalım."
368368
369369#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
370370#: qml/Wizard/Pages/40-wifi.qml:197 qml/Wizard/Pages/50-location.qml:131
371371#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
372372#: qml/Wizard/Pages/passwd-set.qml:90
373373msgid "Continue"
374−msgstr ""
374+msgstr "Devam Et"
375375
376376#: qml/Wizard/Pages/20-sim.qml:25
377377msgid "Add a SIM card and restart your device"
378−msgstr ""
378+msgstr "Bir SIM kartı ekleyin ve cihazınızı yeniden başlatın"
379379
380380#: qml/Wizard/Pages/20-sim.qml:55
381381msgid "Without it, you won’t be able to make calls or use text messaging."
382382msgstr ""
383+"Bir kart olmadan arama yapamaz veya metin mesaj gönderimini kullanamazsınız."
383384
384385#: qml/Wizard/Pages/20-sim.qml:69 qml/Wizard/Pages/40-wifi.qml:197
385386msgid "Skip"
386−msgstr ""
387+msgstr "Atla"
387388
388389#: qml/Wizard/Pages/30-passwd-type.qml:39
389390msgid "Lock security"
390−msgstr ""
391+msgstr "Kilitleme güvenliği"
391392
392393#: qml/Wizard/Pages/30-passwd-type.qml:74
393394msgid "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."
395396
396397#: qml/Wizard/Pages/30-passwd-type.qml:97
397398msgid "Swipe"
398−msgstr ""
399+msgstr "Kaydır"
399400
400401#: qml/Wizard/Pages/30-passwd-type.qml:98
401402msgid "No security"
402−msgstr ""
403+msgstr "Güvenlik yok"
403404
404405#: qml/Wizard/Pages/30-passwd-type.qml:100
405406msgid "Passcode"
406−msgstr ""
407+msgstr "Parola"
407408
408409#: qml/Wizard/Pages/30-passwd-type.qml:101
409410msgid "4 digits only"
410−msgstr ""
411+msgstr "Sadece 4 hane"
411412
412413#: qml/Wizard/Pages/30-passwd-type.qml:103
413414msgid "Passphrase"
414−msgstr ""
415+msgstr "Parola"
415416
416417#: qml/Wizard/Pages/30-passwd-type.qml:104
417418msgid "Numbers and letters"
418−msgstr ""
419+msgstr "Sayı ve harfler"
419420
420421#: qml/Wizard/Pages/40-wifi.qml:28
421422msgid "Connect to Wi‑Fi"
422−msgstr ""
423+msgstr "Wi-Fi'ye Bağlan"
423424
424425#: qml/Wizard/Pages/40-wifi.qml:147
425426msgid "Available networks…"
426−msgstr ""
427+msgstr "Kullanılabilir ağlar..."
427428
428429#: qml/Wizard/Pages/40-wifi.qml:148
429430msgid "No available networks."
430−msgstr ""
431+msgstr "Kullanılabilir ağ yok."
431432
432433#: qml/Wizard/Pages/50-location.qml:27
433434msgid "Location"
434−msgstr ""
435+msgstr "Konum"
435436
436437#: qml/Wizard/Pages/50-location.qml:62
437438msgid "Let the phone detect your location:"
438−msgstr ""
439+msgstr "Telefonun konumunuzu bulmasına izin verin:"
439440
440441#: qml/Wizard/Pages/50-location.qml:69
441442msgid "Using GPS only (less accurate)"
442−msgstr ""
443+msgstr "Sadece GPS kullanarak (daha az kesinlik)"
443444
444445#: qml/Wizard/Pages/50-location.qml:86
445446msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
446447msgstr ""
448+"GPS, anonimleştirilmiş Wi-Fi ve cep telefonu ağı bilgileri kullanarak "
449+"(önerilir)"
447450
448451#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
449452#: qml/Wizard/Pages/50-location.qml:103
@@ -451,81 +454,88 @@ msgid ""
451454"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
452455"conditions</a>."
453456msgstr ""
457+"Bu seçeneği işaretleyerek Nokia HERE <a href='#'>koşul ve şartlarını</a>nı "
458+"kabul ediyorsunuz."
454459
455460#: qml/Wizard/Pages/50-location.qml:112
456461msgid "Not at all"
457−msgstr ""
462+msgstr "Etmiyorum"
458463
459464#: qml/Wizard/Pages/50-location.qml:124
460465msgid "You can change your mind later in <b>System Settings</b>."
461466msgstr ""
467+"Kararınızı daha sonra <b>Sistem Ayarları</b> içerisinden değiştirebilirsiniz."
462468
463469#: qml/Wizard/Pages/60-reporting.qml:24
464470msgid "Improving your experience"
465−msgstr ""
471+msgstr "Deneyiminizi arttırmak"
466472
467473#: qml/Wizard/Pages/60-reporting.qml:36
468474msgid ""
469475"Your phone is set up to automatically report errors to Canonical and its "
470476"partners, the makers of the operating system."
471477msgstr ""
478+"Telefonunuz, işletim sisteminin üreticileri olan Canonical ve ortaklarına "
479+"hataları otomatik olarak bildirmek üzere yapılandırıldı."
472480
473481#: qml/Wizard/Pages/60-reporting.qml:43
474482msgid ""
475483"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
476484"Privacy</b>"
477485msgstr ""
486+"Bu <b>Sistem Ayarları</b> içerisindeki <b>Güvenlik ve Gizlilik</b> altından "
487+"devre dışı bırakılabilir"
478488
479489#: qml/Wizard/Pages/80-finished.qml:24
480490msgid "All done"
481−msgstr ""
491+msgstr "Her şey tamam"
482492
483493#: qml/Wizard/Pages/80-finished.qml:39
484494msgid "Nice work!"
485−msgstr ""
495+msgstr "İyi iş!"
486496
487497#: qml/Wizard/Pages/80-finished.qml:46
488498msgid "Your phone is now ready to use."
489−msgstr ""
499+msgstr "Telefonunuz artık kullanıma hazır."
490500
491501#: qml/Wizard/Pages/80-finished.qml:53
492502msgid "Finish"
493−msgstr ""
503+msgstr "Son"
494504
495505#: qml/Wizard/Pages/here-terms.qml:27
496506msgid "Terms & Conditions"
497−msgstr ""
507+msgstr "Şartlar ve Koşullar"
498508
499509#: qml/Wizard/Pages/passwd-confirm.qml:49
500510msgid "Confirm passphrase"
501−msgstr ""
511+msgstr "Parolayı yinele"
502512
503513#: qml/Wizard/Pages/passwd-confirm.qml:50
504514msgid "Confirm passcode"
505−msgstr ""
515+msgstr "Parolayı yinele"
506516
507517#: qml/Wizard/Pages/passwd-confirm.qml:53
508518#: qml/Wizard/Pages/passwd-confirm.qml:54
509519msgid "Please try again."
510−msgstr ""
520+msgstr "Lütfen yeniden deneyin."
511521
512522#: qml/Wizard/Pages/passwd-set.qml:61
513523msgid "Choose your passcode"
514−msgstr ""
524+msgstr "Parolanızı seçin"
515525
516526#: qml/Wizard/Pages/passwd-set.qml:68
517527msgid "Passphrase must be 4 characters long"
518−msgstr ""
528+msgstr "Parola 4 karakter olmalı"
519529
520530#: qml/Wizard/StackButton.qml:39
521531#, qt-format
522532msgid "〈 %1"
523−msgstr ""
533+msgstr "〈 %1"
524534
525535#: qml/Wizard/StackButton.qml:42
526536#, qt-format
527537msgid "%1 〉"
528−msgstr ""
538+msgstr "%1 〉"
529539
530540#~ msgid "Confirm"
531541#~ msgstr "Onayla"
po/unity8.pot+83−73
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"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"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,7 +18,7 @@ msgstr ""
1818"Content-Transfer-Encoding: 8bit\n"
1919"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
2020
21−#: plugins/LightDM/Greeter.cpp:130
21+#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
2323msgstr ""
2424
@@ -106,52 +106,6 @@ msgstr ""
106106msgid "Cancel"
107107msgstr ""
108108
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−
155109#: qml/Components/Lockscreen.qml:238
156110msgid "Return to Call"
157111msgstr ""
@@ -164,32 +118,32 @@ msgstr ""
164118msgid "OK"
165119msgstr ""
166120
167−#: qml/Dash/GenericScopeView.qml:485 qml/Dash/GenericScopeView.qml:642
121+#: qml/Dash/GenericScopeView.qml:528 qml/Dash/GenericScopeView.qml:686
168122msgid "See less"
169123msgstr ""
170124
171−#: qml/Dash/GenericScopeView.qml:485
125+#: qml/Dash/GenericScopeView.qml:528
172126msgid "See all"
173127msgstr ""
174128
175−#: qml/Dash/GenericScopeView.qml:547 qml/Dash/PageHeader.qml:274
129+#: qml/Dash/GenericScopeView.qml:590 qml/Dash/PageHeader.qml:276
176130#: qml/Panel/SearchIndicator.qml:27
177131msgid "Search"
178132msgstr ""
179133
180−#: qml/Dash/PageHeader.qml:267
134+#: qml/Dash/PageHeader.qml:269
181135msgid "Store"
182136msgstr ""
183137
184−#: qml/Dash/PageHeader.qml:284
138+#: qml/Dash/PageHeader.qml:286
185139msgid "Settings"
186140msgstr ""
187141
188−#: qml/Dash/PageHeader.qml:291
142+#: qml/Dash/PageHeader.qml:293
189143msgid "Remove from Favorites"
190144msgstr ""
191145
192−#: qml/Dash/PageHeader.qml:291
146+#: qml/Dash/PageHeader.qml:293
193147msgid "Add to Favorites"
194148msgstr ""
195149
@@ -221,6 +175,10 @@ msgstr ""
221175msgid "Release to refresh…"
222176msgstr ""
223177
178+#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
179+msgid "Enable location data"
180+msgstr ""
181+
224182#: qml/Dash/ScopesList.qml:66
225183msgid "Manage"
226184msgstr ""
@@ -233,7 +191,7 @@ msgstr ""
233191msgid "Also installed"
234192msgstr ""
235193
236−#: qml/Greeter/Greeter.qml:133
194+#: qml/Greeter/Greeter.qml:141
237195msgid "Unlock"
238196msgstr ""
239197
@@ -289,60 +247,112 @@ msgstr ""
289247msgid "Roaming"
290248msgstr ""
291249
292−#: qml/Shell.qml:390
250+#: qml/Shell.qml:402
293251#, qt-format
294252msgid "Enter %1"
295253msgstr ""
296254
297−#: qml/Shell.qml:391 qml/Wizard/Pages/passwd-set.qml:60
255+#: qml/Shell.qml:403 qml/Wizard/Pages/passwd-set.qml:60
298256msgid "Enter passphrase"
299257msgstr ""
300258
301−#: qml/Shell.qml:392
259+#: qml/Shell.qml:404
302260msgid "Enter passcode"
303261msgstr ""
304262
305−#: qml/Shell.qml:393
263+#: qml/Shell.qml:405
306264#, qt-format
307265msgid "Sorry, incorrect %1"
308266msgstr ""
309267
310−#: qml/Shell.qml:394
268+#: qml/Shell.qml:406
311269msgid "Sorry, incorrect passphrase"
312270msgstr ""
313271
314−#: qml/Shell.qml:395
272+#: qml/Shell.qml:407
315273msgid "Please re-enter"
316274msgstr ""
317275
318−#: qml/Shell.qml:396
276+#: qml/Shell.qml:408
319277msgid "Sorry, incorrect passcode"
320278msgstr ""
321279
322−#: qml/Shell.qml:490 qml/Wizard/Pages/passwd-confirm.qml:53
280+#: qml/Shell.qml:502 qml/Wizard/Pages/passwd-confirm.qml:53
323281msgid "Sorry, incorrect passphrase."
324282msgstr ""
325283
326−#: qml/Shell.qml:491 qml/Wizard/Pages/passwd-confirm.qml:54
284+#: qml/Shell.qml:503 qml/Wizard/Pages/passwd-confirm.qml:54
327285msgid "Sorry, incorrect passcode."
328286msgstr ""
329287
330−#: qml/Shell.qml:492
288+#: qml/Shell.qml:504
331289msgid "This will be your last attempt."
332290msgstr ""
333291
334−#: qml/Shell.qml:494
292+#: qml/Shell.qml:506
335293msgid ""
336294"If passphrase is entered incorrectly, your phone will conduct a factory "
337295"reset and all personal data will be deleted."
338296msgstr ""
339297
340−#: qml/Shell.qml:495
298+#: qml/Shell.qml:507
341299msgid ""
342300"If passcode is entered incorrectly, your phone will conduct a factory reset "
343301"and all personal data will be deleted."
344302msgstr ""
345303
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+
346356#: qml/Wizard/Page.qml:89
347357msgid "Back"
348358msgstr ""
@@ -360,7 +370,7 @@ msgid "Let’s get started."
360370msgstr ""
361371
362372#: 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
364374#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
365375#: qml/Wizard/Pages/passwd-set.qml:90
366376msgid "Continue"
@@ -374,7 +384,7 @@ msgstr ""
374384msgid "Without it, you won’t be able to make calls or use text messaging."
375385msgstr ""
376386
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
378388msgid "Skip"
379389msgstr ""
380390
@@ -410,15 +420,15 @@ msgstr ""
410420msgid "Numbers and letters"
411421msgstr ""
412422
413−#: qml/Wizard/Pages/40-wifi.qml:28
423+#: qml/Wizard/Pages/40-wifi.qml:29
414424msgid "Connect to Wi‑Fi"
415425msgstr ""
416426
417−#: qml/Wizard/Pages/40-wifi.qml:147
427+#: qml/Wizard/Pages/40-wifi.qml:168
418428msgid "Available networks…"
419429msgstr ""
420430
421−#: qml/Wizard/Pages/40-wifi.qml:148
431+#: qml/Wizard/Pages/40-wifi.qml:169
422432msgid "No available networks."
423433msgstr ""
424434
qml/CMakeLists.txt+3−2
@@ -9,10 +9,11 @@ set(QML_DIRS
99 Dash
1010 graphics
1111 Greeter
12− Panel
1312 Launcher
14− Stages
1513 Notifications
14+ Panel
15+ Stages
16+ Tutorial
1617 Wizard
1718 )
1819
qml/Components/Carousel.qml+6−2
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−import QtQuick 2.0
17+import QtQuick 2.3
1818import Ubuntu.Components 0.1
1919import "carousel.js" as CarouselJS
2020
@@ -38,6 +38,8 @@ Item {
3838 property alias tileAspectRatio: listView.tileAspectRatio
3939 /// Used to cache some delegates for performance reasons. See the ListView documentation for details
4040 property alias cacheBuffer: listView.cacheBuffer
41+ property alias displayMarginBeginning: listView.displayMarginBeginning
42+ property alias displayMarginEnd: listView.displayMarginEnd
4143 /// Width of the "draw buffer" in pixel. The drawBuffer is an additional area at start/end where
4244 /// items drawn, even if it is not in the visible area.
4345 /// cacheBuffer controls only the to retain delegates outside the visible area (and is used on top of the drawBuffer)
@@ -51,6 +53,8 @@ Item {
5153 readonly property alias currentItem: listView.currentItem
5254 /// exposes the distance to the next row (only one row in carousel, so it's the topMargins)
5355 readonly property alias verticalSpacing: listView.verticalMargin
56+ /// the width of the internal list
57+ readonly property alias innerWidth: listView.width
5458
5559 implicitHeight: listView.tileHeight * selectedItemScaleFactor
5660 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 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -23,6 +23,8 @@ DashRenderer {
2323
2424 expandedHeight: carousel.implicitHeight + units.gu(6)
2525 collapsedHeight: expandedHeight
26+ growsVertically: false
27+ innerWidth: carousel.innerWidth
2628
2729 Carousel {
2830 id: carousel
@@ -32,7 +34,9 @@ DashRenderer {
3234 // and push others back.
3335 minimumTileWidth: cardTool.cardWidth / selectedItemScaleFactor
3436 selectedItemScaleFactor: cardTool.carouselSelectedItemScaleFactor
35− cacheBuffer: 1404 // 18px * 13gu * 6
37+ cacheBuffer: cardCarousel.cacheBuffer
38+ displayMarginBeginning: cardCarousel.displayMarginBeginning
39+ displayMarginEnd: cardCarousel.displayMarginEnd
3640 model: cardCarousel.model
3741
3842 property real fontScale: 1 / selectedItemScaleFactor
qml/Dash/CardHorizontalList.qml+7−2
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−import QtQuick 2.2
17+import QtQuick 2.3
1818import Ubuntu.Components 1.1
1919import "../Components"
2020
@@ -23,6 +23,8 @@ DashRenderer {
2323
2424 expandedHeight: cardTool.cardHeight + units.gu(2)
2525 collapsedHeight: expandedHeight
26+ growsVertically: false
27+ innerWidth: Math.max(0, listView.width)
2628 clip: true
2729
2830 ListView {
@@ -36,6 +38,9 @@ DashRenderer {
3638 spacing: units.gu(1)
3739 model: root.model
3840 orientation: ListView.Horizontal
41+ cacheBuffer: root.cacheBuffer
42+ displayMarginBeginning: root.displayMarginBeginning
43+ displayMarginEnd: root.displayMarginEnd
3944
4045 delegate: Loader {
4146 id: loader
qml/Dash/Dash.qml+6−8
@@ -122,7 +122,6 @@ Showable {
122122 dash.setCurrentScope(scopeId, true, false);
123123 }
124124 onOpenScope: {
125− scopeItem.scopeThatOpenedScope = currentScope;
126125 scopeItem.scope = scope;
127126 x = -width;
128127 }
@@ -130,7 +129,7 @@ Showable {
130129 UbuntuNumberAnimation {
131130 onRunningChanged: {
132131 if (!running && dashContent.x == 0) {
133− scopeItem.scopeThatOpenedScope.closeScope(scopeItem.scope);
132+ scopes.closeScope(scopeItem.scope);
134133 scopeItem.scope = null;
135134 }
136135 }
@@ -187,7 +186,6 @@ Showable {
187186 onOpenScope: {
188187 bottomEdgeController.enableAnimation = true;
189188 bottomEdgeController.progress = 0;
190− scopeItem.scopeThatOpenedScope = scopesList.scope;
191189 scopeItem.scope = scope;
192190 dashContent.x = -dashContent.width;
193191 }
@@ -199,8 +197,7 @@ Showable {
199197 }
200198 }
201199
202− DashBackground
203− {
200+ DashBackground {
204201 anchors.fill: scopeItem
205202 visible: scopeItem.visible
206203 }
@@ -209,8 +206,6 @@ Showable {
209206 id: scopeItem
210207 objectName: "dashTempScopeItem"
211208
212− property var scopeThatOpenedScope: null
213−
214209 x: dashContent.x + width
215210 y: dashContent.y
216211 width: parent.width
@@ -229,7 +224,10 @@ Showable {
229224 dashContent.gotoScope(scopeId);
230225 }
231226 onOpenScope: {
232− dashContent.openScope(scope);
227+ scopeItem.closePreview();
228+ var oldScope = scopeItem.scope;
229+ scopeItem.scope = scope;
230+ scopes.closeScope(oldScope);
233231 }
234232 }
235233 }
qml/Dash/DashContent.qml+1−2
@@ -84,8 +84,7 @@ Item {
8484
8585 set_current_index = undefined;
8686
87− if (dashContentList.count > index)
88− {
87+ if (dashContentList.count > index) {
8988 dashContentList.currentIndex = index
9089
9190 if (reset) {
qml/Dash/DashRenderer.qml+6−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -31,6 +31,11 @@ Item {
3131
3232 property real originY: 0
3333
34+ property bool growsVertically: true
35+
36+ // If growsVertically the width of the item inside the renderer
37+ property real innerWidth: 0
38+
3439 // The model to renderer
3540 property var model
3641
qml/Dash/GenericScopeView.qml+92−48
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -401,59 +401,102 @@ FocusScope {
401401 }
402402
403403 if (item && item.hasOwnProperty("displayMarginBeginning")) {
404− // A item view creates its delegates synchronously from
405− // -displayMarginBeginning
406− // to
407− // height + displayMarginEnd
408− // Around that area it adds the cacheBuffer area where delegates are created async
409− //
410− // We adjust displayMarginBeginning and displayMarginEnd so
411− // * 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− // * 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− // to make scrolling nicer by mantaining a higher number of
416− // cached items
417− // * 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− // items are not culled (invisible) but still use no cacheBuffer
420− // (it will be set once the scope is the current one)
421− var displayMarginBeginning = baseItem.y;
422− displayMarginBeginning = -Math.max(-displayMarginBeginning, 0);
423− displayMarginBeginning = -Math.min(-displayMarginBeginning, baseItem.height);
424− displayMarginBeginning = Math.round(displayMarginBeginning);
425− var displayMarginEnd = -baseItem.height + seeAll.height + categoryView.height - baseItem.y;
426− displayMarginEnd = -Math.max(-displayMarginEnd, 0);
427− displayMarginEnd = -Math.min(-displayMarginEnd, baseItem.height);
428− displayMarginEnd = Math.round(displayMarginEnd);
429− if (scopeView.isCurrent || scopeView.visibleToParent) {
430− item.displayMarginBeginning = displayMarginBeginning;
431− item.displayMarginEnd = displayMarginEnd;
432− if (holdingList && holdingList.moving) {
433− // 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
435− // 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
437− // be as smooth as possible meaning no need creations
438− if (!wasCurrentOnMoveStart) {
439− item.cacheBuffer = 0;
404+ if (item.growsVertically) {
405+ // A item view creates its delegates synchronously from
406+ // -displayMarginBeginning
407+ // to
408+ // height + displayMarginEnd
409+ // Around that area it adds the cacheBuffer area where delegates are created async
410+ //
411+ // We adjust displayMarginBeginning and displayMarginEnd so
412+ // * In non visible scopes nothing is considered visible and we set cacheBuffer
413+ // so that creates the items that would be in the viewport asynchronously
414+ // * For the current scope set the visible range to the viewport and then
415+ // use cacheBuffer to create extra items for categoryView.height * 1.5
416+ // to make scrolling nicer by mantaining a higher number of
417+ // cached items
418+ // * For non current but visible scopes (i.e. when the user changes from one scope
419+ // to the next, we set the visible range to the viewport so
420+ // items are not culled (invisible) but still use no cacheBuffer
421+ // (it will be set once the scope is the current one)
422+ var displayMarginBeginning = baseItem.y;
423+ displayMarginBeginning = -Math.max(-displayMarginBeginning, 0);
424+ displayMarginBeginning = -Math.min(-displayMarginBeginning, baseItem.height);
425+ displayMarginBeginning = Math.round(displayMarginBeginning);
426+ var displayMarginEnd = -baseItem.height + seeAll.height + categoryView.height - baseItem.y;
427+ displayMarginEnd = -Math.max(-displayMarginEnd, 0);
428+ displayMarginEnd = -Math.min(-displayMarginEnd, baseItem.height);
429+ displayMarginEnd = Math.round(displayMarginEnd);
430+
431+ if (scopeView.isCurrent || scopeView.visibleToParent) {
432+ item.displayMarginBeginning = displayMarginBeginning;
433+ item.displayMarginEnd = displayMarginEnd;
434+ if (holdingList && holdingList.moving) {
435+ // If we are moving we need to reset the cache buffer of the
436+ // view that was not visible (i.e. !wasCurrentOnMoveStart) to 0 since
437+ // otherwise the cache buffer we had set to preload the items of the
438+ // visible range will trigger some item creations and we want move to
439+ // be as smooth as possible meaning no need creations
440+ if (!wasCurrentOnMoveStart) {
441+ item.cacheBuffer = 0;
442+ }
443+ } else {
444+ item.cacheBuffer = categoryView.height * 1.5;
440445 }
441446 } 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+ }
443460 }
444461 } else {
445− var visibleRange = baseItem.height + displayMarginEnd + displayMarginBeginning;
446− if (visibleRange < 0) {
447− item.displayMarginBeginning = displayMarginBeginning;
448− item.displayMarginEnd = displayMarginEnd;
462+ var buffer = wasCurrentOnMoveStart ? categoryView.height * 1.5 : 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
469+ item.displayMarginBeginning = 0;
470+ item.displayMarginEnd = -item.innerWidth;
449471 item.cacheBuffer = 0;
450472 } else {
451− // This should be visibleRange/2 in each of the properties
452− // but some item views still (like GridView) like creating sync delegates even if
453− // the visible range is 0 so let's make sure the visible range is negative
454− item.displayMarginBeginning = displayMarginBeginning - visibleRange;
455− item.displayMarginEnd = displayMarginEnd - visibleRange;
456− item.cacheBuffer = visibleRange;
473+ if (onViewport && (scopeView.isCurrent || scopeView.visibleToParent)) {
474+ // If on the buffered viewport and the viewport and the on a visible scope
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
496+ item.displayMarginBeginning = 0;
497+ item.displayMarginEnd = -item.innerWidth;
498+ item.cacheBuffer = item.innerWidth;
499+ }
457500 }
458501 }
459502 }
@@ -565,6 +608,7 @@ FocusScope {
565608 onBackClicked: scopeView.backClicked()
566609 onSettingsClicked: subPageLoader.openSubPage("settings")
567610 onFavoriteClicked: scopeView.scope.favorite = !scopeView.scope.favorite
611+ onSearchTextFieldFocused: scopeView.showHeader()
568612 }
569613 }
570614 }
qml/Dash/PageHeader.qml+2
@@ -51,6 +51,7 @@ Item {
5151 signal storeClicked()
5252 signal settingsClicked()
5353 signal favoriteClicked()
54+ signal searchTextFieldFocused()
5455
5556 onScopeStyleChanged: refreshLogo()
5657 onSearchQueryChanged: {
@@ -228,6 +229,7 @@ Item {
228229
229230 onActiveFocusChanged: {
230231 if (activeFocus) {
232+ root.searchTextFieldFocused();
231233 root.openSearchHistory();
232234 }
233235 }
qml/Dash/Previews/Preview.qml+1
@@ -65,6 +65,7 @@ Item {
6565
6666 delegate: ListView {
6767 id: column
68+ objectName: "previewListRow" + index
6869 anchors { top: parent.top; bottom: parent.bottom }
6970 width: row.columnWidth
7071 spacing: row.spacing
qml/Dash/ScopeSettings/ScopeSettingBoolean.qml+3−1
@@ -38,7 +38,9 @@ ScopeSetting {
3838 rightMargin: units.gu(1)
3939 verticalCenter: parent.verticalCenter
4040 }
41− text: widgetData.displayName
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
43+ text: widgetData.settingId == "internal.location" ? i18n.tr("Enable location data") : widgetData.displayName
4244 elide: Text.ElideMiddle
4345 maximumLineCount: 2
4446 wrapMode: Text.Wrap
qml/Dash/ScopesListCategoryItem.qml+1
@@ -53,6 +53,7 @@ MouseArea {
5353 id: shapeImage
5454 cache: true
5555 fillMode: Image.PreserveAspectCrop
56+ sourceSize { width: shape.width; height: shape.height }
5657 }
5758 }
5859
qml/Greeter/Greeter.qml+8
@@ -30,6 +30,9 @@ Showable {
3030
3131 property url background
3232
33+ // so that it can be replaced in tests with a mock object
34+ property var inputMethod: Qt.inputMethod
35+
3336 prepareToHide: function () {
3437 hideTranslation.to = greeter.x > 0 || d.forceRightOnNextHideAnimation ? greeter.width : -greeter.width;
3538 d.forceRightOnNextHideAnimation = false;
@@ -102,6 +105,11 @@ Showable {
102105 }
103106 onUnlocked: greeter.unlocked(uid);
104107 }
108+ Binding {
109+ target: greeterContentLoader.item
110+ property: "inputMethod"
111+ value: greeter.inputMethod
112+ }
105113 }
106114
107115 DragHandle {
qml/Greeter/GreeterContent.qml+7−2
@@ -24,6 +24,8 @@ Item {
2424 id: root
2525 anchors.fill: parent
2626
27+ property var inputMethod
28+
2729 property bool ready: background.source == "" || background.status == Image.Ready || background.status == Image.Error
2830
2931 signal selected(int uid)
@@ -74,10 +76,12 @@ Item {
7476 anchors {
7577 left: parent.left
7678 leftMargin: Math.min(parent.width * 0.16, units.gu(20))
77− verticalCenter: parent.verticalCenter
79+ top: parent.top
7880 }
7981 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 {} }
8185
8286 // TODO: Once we have a system API for determining which mode we are
8387 // in, tablet/phone/desktop, that should be used instead of narrowMode.
@@ -117,6 +121,7 @@ Item {
117121 objectName: "infographics"
118122 height: narrowMode ? parent.height : 0.75 * parent.height
119123 model: greeterContentLoader.infographicModel
124+ clip: true // clip large data bubbles
120125
121126 property string selectedUser
122127 property string infographicUser: AccountsService.statsWelcomeScreen ? selectedUser : ""
qml/Launcher/Launcher.qml+13−7
@@ -26,6 +26,7 @@ Item {
2626 property bool autohideEnabled: false
2727 property bool available: true // can be used to disable all interactions
2828 property alias inverted: panel.inverted
29+ property bool shadeBackground: true // can be used to disable background shade when launcher is visible
2930
3031 property int panelWidth: units.gu(8)
3132 property int dragAreaWidth: units.gu(1)
@@ -33,6 +34,10 @@ Item {
3334 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
3435 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
3536
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+
3641 readonly property bool shown: panel.x > -panel.width
3742
3843 // emitted when an application is selected
@@ -154,7 +159,7 @@ Item {
154159
155160 MouseArea {
156161 id: launcherDragArea
157− enabled: root.state == "visible"
162+ enabled: root.available && root.state == "visible"
158163 anchors.fill: panel
159164 anchors.rightMargin: -units.gu(2)
160165 drag {
@@ -180,7 +185,7 @@ Item {
180185 right: parent.right
181186 bottom: parent.bottom
182187 }
183− enabled: root.state == "visible"
188+ enabled: root.shadeBackground && root.state == "visible"
184189 onPressed: {
185190 root.state = ""
186191 }
@@ -190,7 +195,7 @@ Item {
190195 id: backgroundShade
191196 anchors.fill: parent
192197 color: "black"
193− opacity: root.state == "visible" ? 0.6 : 0
198+ opacity: root.shadeBackground && root.state == "visible" ? 0.6 : 0
194199
195200 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.BriskDuration } }
196201 }
@@ -198,14 +203,14 @@ Item {
198203 LauncherPanel {
199204 id: panel
200205 objectName: "launcherPanel"
201− enabled: root.available
206+ enabled: root.available && root.state == "visible"
202207 width: root.panelWidth
203208 anchors {
204209 top: parent.top
205210 bottom: parent.bottom
206211 }
207212 x: -width
208− visible: x > -width || dragArea.status === DirectionalDragArea.Undecided
213+ visible: root.x > 0 || x > -width || dragArea.status === DirectionalDragArea.Undecided
209214 model: LauncherModel
210215
211216 property bool animate: true
@@ -247,6 +252,7 @@ Item {
247252 direction: Direction.Rightwards
248253
249254 enabled: root.available
255+ x: -root.x // so if launcher is adjusted relative to screen, we stay put (like tutorial does when teasing)
250256 width: root.dragAreaWidth
251257 height: root.height
252258
@@ -260,7 +266,7 @@ Item {
260266 // would appear right next to the user's finger out of nowhere.
261267 // Instead, we make the panel go towards the user's finger in several
262268 // 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
264270 var delta = targetPanelX - panel.x
265271 // the trick is not to go all the way (1.0) as it would cause a sudden jump
266272 panel.x += 0.4 * delta
@@ -292,7 +298,7 @@ Item {
292298 name: "visible"
293299 PropertyChanges {
294300 target: panel
295− x: 0
301+ x: -root.x // so we never go past panelWidth, even when teased by tutorial
296302 }
297303 },
298304 State {
qml/Launcher/LauncherPanel.qml+7−5
@@ -149,14 +149,14 @@ Rectangle {
149149 id: snapToBottomAnimation
150150 target: launcherListView
151151 property: "contentY"
152− to: launcherListView.originY
152+ to: launcherListView.originY + launcherListView.topMargin
153153 }
154154
155155 UbuntuNumberAnimation {
156156 id: snapToTopAnimation
157157 target: launcherListView
158158 property: "contentY"
159− to: launcherListView.contentHeight - launcherListView.height + launcherListView.originY
159+ to: launcherListView.contentHeight - launcherListView.height + launcherListView.originY - launcherListView.topMargin
160160 }
161161
162162 displaced: Transition {
@@ -521,12 +521,12 @@ Rectangle {
521521 left: parent.left
522522 leftMargin: (quickList.item.width - units.gu(1)) / 2 - width / 2
523523 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)
525525 }
526526 height: units.gu(1)
527527 width: units.gu(2)
528528 source: "graphics/quicklist_tooltip.png"
529− rotation: quickList.offset > 0 ? 0 : 180
529+ rotation: (quickList.offset > 0 ? 0 : 180) + (root.inverted ? 0 : 180)
530530 }
531531
532532 InverseMouseArea {
@@ -543,7 +543,9 @@ Rectangle {
543543 id: quickList
544544 objectName: "quickList"
545545 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)
547549 height: quickListColumn.height
548550 visible: quickListShape.visible
549551 anchors {
qml/Notifications/Notification.qml+36−6
@@ -40,6 +40,7 @@ Item {
4040 property bool fullscreen: false
4141 property int maxHeight
4242 property int margins
43+ readonly property bool draggable: (type === Notification.SnapDecision && state === "contracted") || type === Notification.Interactive || type === Notification.Ephemeral
4344 readonly property bool darkOnBright: panel.indicators.shown || type === Notification.SnapDecision
4445 readonly property color red: "#fc4949"
4546 readonly property color green: "#3fb24f"
@@ -52,7 +53,7 @@ Item {
5253 implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : outterColumn.height - shapedBack.anchors.topMargin + contentSpacing * 2) : 0
5354
5455 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
5657
5758 state: {
5859 var result = "";
@@ -80,18 +81,28 @@ Item {
8081 id: sound
8182 objectName: "sound"
8283 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"] : ""
8485 }
8586
8687 // FIXME: using onCompleted because of LP: #1354406 workaround, has to be onOpacityChanged really
8788 Component.onCompleted: {
88− if (opacity == 1.0 && hints["suppress-sound"] != "true" && sound.source != "") {
89+ if (opacity == 1.0 && hints["suppress-sound"] !== "true" && sound.source !== "") {
8990 sound.play();
9091 }
9192 }
9293
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+
93104 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 !== "") {
95106 sound.play();
96107 }
97108 }
@@ -146,6 +157,12 @@ Item {
146157 opacity: parent.opacity
147158 }
148159
160+ onXChanged: {
161+ if (draggable && notification.x > 0.75 * notification.width) {
162+ notification.notification.close()
163+ }
164+ }
165+
149166 Item {
150167 id: contents
151168 anchors.fill: fullscreen ? nonShapedBack : shapedBack
@@ -169,7 +186,7 @@ Item {
169186 actions: paths.actions
170187 menuObjectPath: paths.menuObjectPath
171188 onNameOwnerChanged: {
172− if (lastNameOwner != "" && nameOwner == "" && notification.notification != undefined) {
189+ if (lastNameOwner !== "" && nameOwner === "" && notification.notification !== undefined) {
173190 notification.notification.close()
174191 }
175192 lastNameOwner = nameOwner
@@ -181,6 +198,12 @@ Item {
181198
182199 anchors.fill: parent
183200 objectName: "interactiveArea"
201+
202+ drag.target: draggable ? notification : undefined
203+ drag.axis: Drag.XAxis
204+ drag.minimumX: 0
205+ drag.maximumX: notification.width
206+
184207 onClicked: {
185208 if (notification.type == Notification.Interactive) {
186209 notification.notification.invokeAction(actionRepeater.itemAt(0).actionId)
@@ -188,6 +211,13 @@ Item {
188211 notificationList.currentIndex = index;
189212 }
190213 }
214+ onReleased: {
215+ if (notification.x < notification.width / 2) {
216+ notification.x = 0
217+ } else {
218+ notification.x = notification.width
219+ }
220+ }
191221 }
192222
193223 Column {
@@ -447,7 +477,7 @@ Item {
447477 right: parent.right
448478 margins: contentSpacing
449479 }
450− visible: notification.type == Notification.SnapDecision && actionRepeater.count > 0 && !oneOverTwoCase.visible
480+ visible: notification.type === Notification.SnapDecision && actionRepeater.count > 0 && !oneOverTwoCase.visible
451481 spacing: units.gu(2)
452482 layoutDirection: Qt.RightToLeft
453483
qml/Notifications/Notifications.qml+2−6
@@ -41,15 +41,11 @@ ListView {
4141 property bool topmostIsFullscreen: false
4242 spacing: topmostIsFullscreen ? 0 : units.gu(.5)
4343
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
4645
4746 delegate: Notification {
4847 objectName: "notification" + index
49− anchors {
50− left: parent.left
51− right: parent.right
52− }
48+ width: parent.width
5349 type: model.type
5450 hints: model.hints
5551 iconSource: model.icon
qml/Panel/Panel.qml+5−3
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -90,6 +90,9 @@ Item {
9090 right: indicators.left
9191 }
9292 saturation: 1 - indicators.unitProgress
93+
94+ // Don't let input event pass trough
95+ MouseArea { anchors.fill: parent }
9396 }
9497
9598 Image {
@@ -110,8 +113,7 @@ Item {
110113 right: indicators.left
111114 }
112115 height: indicators.minimizedPanelHeight
113− enabled: callHint.visible
114− onClicked: callHint.showLiveCall()
116+ onClicked: { if (callHint.visible) { callHint.showLiveCall(); } }
115117 }
116118
117119 IndicatorsMenu {
qml/Shell.qml+50−25
@@ -36,6 +36,7 @@ import "Panel"
3636import "Components"
3737import "Notifications"
3838import "Stages"
39+import "Tutorial"
3940import "Wizard"
4041import Unity.Notifications 1.0 as NotificationBackend
4142import Unity.Session 0.1
@@ -62,7 +63,7 @@ Item {
6263
6364 readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock
6465 readonly property alias hasLockedApp: greeter.hasLockedApp
65− readonly property bool forcedUnlock: edgeDemo.running
66+ readonly property bool forcedUnlock: tutorial.running
6667 onForcedUnlockChanged: if (forcedUnlock) lockscreen.hide()
6768
6869 property bool sideStageEnabled: shell.width >= units.gu(100)
@@ -171,7 +172,7 @@ Item {
171172
172173 ScreenGrabber {
173174 id: screenGrabber
174− z: edgeDemo.z + 10
175+ z: dialogs.z + 10
175176 enabled: Powerd.status === Powerd.On
176177 }
177178
@@ -231,14 +232,16 @@ Item {
231232 }
232233
233234 onApplicationAdded: {
234− if (greeter.shown && appId != "unity8-dash") {
235− greeter.startUnlock()
235+ if (appId != "unity8-dash") {
236+ if (greeter.shown) {
237+ greeter.startUnlock();
238+ }
236239
237240 // If this happens on first boot, we may be in edge
238241 // tutorial or wizard while receiving a call. But a call
239242 // is more important than wizard so just bail out of those.
240− if (edgeDemo.running) {
241− edgeDemo.hideEdgeDemos();
243+ if (tutorial.running) {
244+ tutorial.finish();
242245 wizard.hide();
243246 }
244247 }
@@ -266,6 +269,15 @@ Item {
266269 source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
267270 : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
268271
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+
269281 Binding {
270282 target: applicationsDisplayLoader.item
271283 property: "objectName"
@@ -285,12 +297,12 @@ Item {
285297 Binding {
286298 target: applicationsDisplayLoader.item
287299 property: "interactive"
288− value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
300+ value: applicationsDisplayLoader.interactive
289301 }
290302 Binding {
291303 target: applicationsDisplayLoader.item
292304 property: "spreadEnabled"
293− value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
305+ value: tutorial.spreadEnabled && !greeter.hasLockedApp
294306 }
295307 Binding {
296308 target: applicationsDisplayLoader.item
@@ -390,7 +402,7 @@ Item {
390402 onShownChanged: if (shown) greeter.lockedApp = ""
391403
392404 function maybeShow() {
393− if (!shell.forcedUnlock) {
405+ if (!shell.forcedUnlock && !shown) {
394406 showNow();
395407 }
396408 }
@@ -623,9 +635,13 @@ Item {
623635 LauncherModel.setUser(user);
624636 }
625637
626− onTapped: launcher.tease()
638+ onTapped: {
639+ if (!tutorial.running) {
640+ launcher.tease();
641+ }
642+ }
627643 onDraggingChanged: {
628− if (dragging) {
644+ if (dragging && !tutorial.running) {
629645 launcher.tease();
630646 }
631647 }
@@ -663,7 +679,7 @@ Item {
663679
664680 onStatusChanged: {
665681 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
666− !callManager.hasCalls && !edgeDemo.running) {
682+ !callManager.hasCalls && !tutorial.running) {
667683 // We don't want to simply call greeter.showNow() here, because
668684 // that will take too long. Qt will delay button event
669685 // handling until the greeter is done loading and may think the
@@ -679,7 +695,7 @@ Item {
679695 }
680696
681697 function showHome() {
682− if (edgeDemo.running) {
698+ if (tutorial.running) {
683699 return
684700 }
685701
@@ -721,15 +737,17 @@ Item {
721737 anchors.fill: parent //because this draws indicator menus
722738 indicators {
723739 hides: [launcher]
724− available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
725− contentEnabled: edgeDemo.panelContentEnabled
740+ available: tutorial.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
741+ contentEnabled: tutorial.panelContentEnabled
726742 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
727743
728744 minimizedPanelHeight: units.gu(3)
729745 expandedPanelHeight: units.gu(7)
730746
731747 indicatorsModel: Indicators.IndicatorsModel {
732− Component.onCompleted: load(indicatorProfile);
748+ // TODO: This should be sourced by device type (e.g. "desktop", "tablet", "phone"...)
749+ profile: indicatorProfile
750+ Component.onCompleted: load()
733751 }
734752 }
735753 callHint {
@@ -755,8 +773,9 @@ Item {
755773 anchors.bottom: parent.bottom
756774 width: parent.width
757775 dragAreaWidth: shell.edgeSize
758− available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
776+ available: tutorial.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
759777 inverted: usageModeSettings.usageMode === "Staged"
778+ shadeBackground: !tutorial.running
760779
761780 onShowDashHome: showHome()
762781 onDash: showDash()
@@ -769,7 +788,7 @@ Item {
769788 if (greeter.hasLockedApp) {
770789 greeter.startUnlock()
771790 }
772− if (!edgeDemo.running)
791+ if (!tutorial.running)
773792 shell.activateApplication(appId)
774793 }
775794 onShownChanged: {
@@ -842,15 +861,21 @@ Item {
842861 }
843862 }
844863
845− EdgeDemo {
846− id: edgeDemo
847− objectName: "edgeDemo"
848− z: dialogs.z + 10
849− paused: Powerd.status === Powerd.Off || wizard.active // Saves power
850− greeter: greeter
864+ Tutorial {
865+ id: tutorial
866+ objectName: "tutorial"
867+ active: AccountsService.demoEdges
868+ paused: LightDM.Greeter.active
851869 launcher: launcher
852870 panel: panel
853871 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+ }
854879 }
855880
856881 Connections {
@@ -860,7 +885,7 @@ Item {
860885
861886 Rectangle {
862887 id: shutdownFadeOutRectangle
863− z: edgeDemo.z + 10
888+ z: screenGrabber.z + 10
864889 enabled: false
865890 visible: false
866891 color: "black"
qml/Stages/ApplicationWindow.qml+17−2
@@ -18,7 +18,7 @@ import QtQuick 2.0
1818import Ubuntu.Components 1.1
1919import Unity.Application 0.1
2020
21−Item {
21+FocusScope {
2222 id: root
2323
2424 // to be read from outside
@@ -43,6 +43,7 @@ Item {
4343 readonly property color splashColor: root.application ? root.application.splashColor : "#00000000"
4444 readonly property color splashColorHeader: root.application ? root.application.splashColorHeader : "#00000000"
4545 readonly property color splashColorFooter: root.application ? root.application.splashColorFooter : "#00000000"
46+ readonly property url defaultScreenshot: root.application ? root.application.defaultScreenshot : ""
4647
4748 // Whether the Application had a surface before but lost it.
4849 property bool hadSurface: sessionContainer.surfaceContainer.hadSurface
@@ -72,7 +73,7 @@ Item {
7273 Image {
7374 id: screenshotImage
7475 objectName: "screenshotImage"
75− source: ""
76+ source: d.defaultScreenshot
7677 anchors.fill: parent
7778 antialiasing: !root.interactive
7879
@@ -120,9 +121,12 @@ Item {
120121 d.surfaceInitialized = false;
121122 }
122123 }
124+
125+ focus: true
123126 }
124127
125128 StateGroup {
129+ id: stateGroup
126130 objectName: "applicationWindowStateGroup"
127131 states: [
128132 State {
@@ -215,6 +219,17 @@ Item {
215219 PropertyAction { target: screenshotImage; property: "source"; value: "" }
216220 }
217221 },
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+ },
218233 Transition {
219234 from: "surface"; to: "void"
220235 SequentialAnimation {
qml/Stages/DesktopStage.qml+2−2
@@ -38,8 +38,6 @@ Item {
3838 Connections {
3939 target: ApplicationManager
4040 onApplicationAdded: {
41− // Initial placement to avoid having the window decoration behind the panel
42− appRepeater.itemAt(ApplicationManager.count-1).y = units.gu(3)
4341 ApplicationManager.requestFocusApplication(ApplicationManager.get(ApplicationManager.count-1).appId)
4442 }
4543
@@ -91,6 +89,7 @@ Item {
9189 delegate: Item {
9290 id: appDelegate
9391 z: ApplicationManager.count - index
92+ y: units.gu(3)
9493 width: units.gu(60)
9594 height: units.gu(50)
9695
@@ -121,6 +120,7 @@ Item {
121120 minWidth: appDelegate.minWidth
122121 minHeight: appDelegate.minHeight
123122 resizeHandleWidth: units.gu(0.5)
123+ windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
124124
125125 onPressed: ApplicationManager.requestFocusApplication(model.appId)
126126 }
qml/Stages/PhoneStage.qml+64−34
@@ -32,6 +32,21 @@ Rectangle {
3232 property bool spreadEnabled: true // If false, animations and right edge will be disabled
3333 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
3434 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()
3550
3651 color: "#111111"
3752
@@ -52,30 +67,30 @@ Rectangle {
5267 if (inverseProgress == 0 && priv.oldInverseProgress > 0) {
5368 // left edge drag released. Minimum distance is given by design.
5469 if (priv.oldInverseProgress > units.gu(22)) {
55− ApplicationManager.requestFocusApplication("unity8-dash");
70+ applicationManager.requestFocusApplication("unity8-dash");
5671 }
5772 }
5873 priv.oldInverseProgress = inverseProgress;
5974 }
6075
6176 Connections {
62− target: ApplicationManager
77+ target: applicationManager
6378
6479 onFocusRequested: {
6580 if (spreadView.phase > 0) {
6681 spreadView.snapTo(priv.indexOf(appId));
6782 } else {
68− ApplicationManager.focusApplication(appId);
83+ applicationManager.focusApplication(appId);
6984 }
7085 }
7186
7287 onApplicationAdded: {
7388 if (spreadView.phase == 2) {
74− spreadView.snapTo(ApplicationManager.count - 1);
89+ spreadView.snapTo(applicationManager.count - 1);
7590 } else {
7691 spreadView.phase = 0;
7792 spreadView.contentX = -spreadView.shift;
78− ApplicationManager.focusApplication(appId);
93+ applicationManager.focusApplication(appId);
7994 }
8095 }
8196
@@ -90,9 +105,9 @@ Rectangle {
90105 }
91106
92107 function focusTopMostApp() {
93− if (ApplicationManager.count > 0) {
94− var topmostApp = ApplicationManager.get(0);
95− ApplicationManager.focusApplication(topmostApp.appId);
108+ if (applicationManager.count > 0) {
109+ var topmostApp = applicationManager.get(0);
110+ applicationManager.focusApplication(topmostApp.appId);
96111 }
97112 }
98113 }
@@ -100,18 +115,21 @@ Rectangle {
100115 QtObject {
101116 id: priv
102117
103− property string focusedAppId: ApplicationManager.focusedApplicationId
104− property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
118+ readonly property int firstSpreadIndex: root.focusFirstApp ? 1 : 0
119+ property string focusedAppId: applicationManager.focusedApplicationId
120+ property var focusedApplication: applicationManager.findApplication(focusedAppId)
105121 property var focusedAppDelegate: null
106122
107123 property real oldInverseProgress: 0
108− property bool animateX: true
124+ property bool animateX: false
109125
110126 onFocusedAppIdChanged: focusedAppDelegate = spreadRepeater.itemAt(0);
111127
128+ onFocusedAppDelegateChanged: focusedAppDelegate.focus = true;
129+
112130 function indexOf(appId) {
113− for (var i = 0; i < ApplicationManager.count; i++) {
114− if (ApplicationManager.get(i).appId == appId) {
131+ for (var i = 0; i < applicationManager.count; i++) {
132+ if (applicationManager.get(i).appId == appId) {
115133 return i;
116134 }
117135 }
@@ -131,7 +149,7 @@ Rectangle {
131149
132150 // This indicates when the spreadView is active. That means, all the animations
133151 // 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
135153
136154 // The flickable needs to fill the screen in order to get touch events all over.
137155 // However, we don't want to the user to be able to scroll back all the way. For
@@ -197,10 +215,17 @@ Rectangle {
197215 // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
198216 snapAnimation.targetContentX = width * positionMarker4 + 1 - shift;
199217 snapAnimation.start();
218+ root.opened();
200219 }
201220 }
202221 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) {
204229 // In case we're trying to snap to some non existing app, lets snap back to the first one
205230 index = 0;
206231 }
@@ -215,10 +240,12 @@ Rectangle {
215240 snapAnimation.start();
216241 }
217242
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
219244 // Make sure we end up in the same state
220245 Component.onCompleted: {
221246 spreadView.contentX = -spreadView.shift
247+ priv.animateX = true;
248+ snapAnimation.complete();
222249 }
223250
224251 SequentialAnimation {
@@ -235,7 +262,7 @@ Rectangle {
235262 ScriptAction {
236263 script: {
237264 if (spreadView.selectedIndex >= 0) {
238− ApplicationManager.focusApplication(ApplicationManager.get(spreadView.selectedIndex).appId);
265+ applicationManager.focusApplication(applicationManager.get(spreadView.selectedIndex).appId);
239266
240267 spreadView.selectedIndex = -1;
241268 spreadView.phase = 0;
@@ -250,7 +277,7 @@ Rectangle {
250277 // This width controls how much the spread can be flicked left/right. It's composed of:
251278 // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)
252279 // + 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
254281 height: parent.height
255282 Behavior on width {
256283 enabled: spreadView.closingIndex >= 0
@@ -265,20 +292,22 @@ Rectangle {
265292 x: spreadView.contentX
266293
267294 onClicked: {
268− spreadView.snapTo(0);
295+ if (root.altTabEnabled) {
296+ spreadView.snapTo(0);
297+ }
269298 }
270299
271300 Repeater {
272301 id: spreadRepeater
273302 objectName: "spreadRepeater"
274− model: ApplicationManager
303+ model: applicationManager
275304 delegate: TransformedSpreadDelegate {
276305 id: appDelegate
277306 objectName: "appDelegate" + index
278307 startAngle: 45
279308 endAngle: 5
280− startScale: 1.1
281− endScale: 0.7
309+ startScale: root.startScale
310+ endScale: root.endScale
282311 startDistance: spreadView.tileDistance
283312 endDistance: units.gu(.5)
284313 width: spreadView.width
@@ -286,11 +315,12 @@ Rectangle {
286315 selected: spreadView.selectedIndex == index
287316 otherSelected: spreadView.selectedIndex >= 0 && !selected
288317 interactive: !spreadView.interactive && spreadView.phase === 0
289− && spreadView.shiftedContentX === 0 && root.interactive && index === 0
290− swipeToCloseEnabled: spreadView.interactive && !snapAnimation.running
318+ && spreadView.shiftedContentX === 0 && root.interactive && isFocused
319+ swipeToCloseEnabled: spreadView.interactive && root.interactive && !snapAnimation.running
291320 maximizedAppTopMargin: root.maximizedAppTopMargin
292321 dropShadow: spreadView.active ||
293322 (priv.focusedAppDelegate && priv.focusedAppDelegate.x !== 0)
323+ focusFirstApp: root.focusFirstApp
294324
295325 readonly property bool isDash: model.appId == "unity8-dash"
296326
@@ -298,7 +328,7 @@ Rectangle {
298328
299329 x: {
300330 // focused app is always positioned at 0 except when following left edge drag
301− if (index == 0) {
331+ if (isFocused) {
302332 if (!isDash && root.inverseProgress > 0 && spreadView.phase === 0) {
303333 return root.inverseProgress;
304334 }
@@ -309,10 +339,10 @@ Rectangle {
309339 }
310340
311341 // Otherwise line up for the spread
312− return spreadView.width + (index - 1) * spreadView.tileDistance;
342+ return spreadView.width + spreadIndex * spreadView.tileDistance;
313343 }
314344
315− application: ApplicationManager.get(index)
345+ application: applicationManager.get(index)
316346 closeable: !isDash
317347
318348 property real behavioredIndex: index
@@ -350,7 +380,7 @@ Rectangle {
350380 progress: {
351381 var tileProgress = (spreadView.shiftedContentX - behavioredIndex * spreadView.tileDistance) / spreadView.width;
352382 // 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) {
354384 tileProgress += spreadView.tileDistance / spreadView.width;
355385 }
356386 // Limiting progress to ~0 and 1.7 to avoid binding calculations when tiles are not
@@ -365,7 +395,7 @@ Rectangle {
365395
366396 // This mostly is the same as progress, just adds the snapping to phase 1 for tiles 0 and 1
367397 animatedProgress: {
368− if (spreadView.phase == 0 && index < 2) {
398+ if (spreadView.phase == 0 && index <= priv.firstSpreadIndex) {
369399 if (progress < spreadView.positionMarker1) {
370400 return progress;
371401 } else if (progress < spreadView.positionMarker1 + 0.05){
@@ -390,11 +420,11 @@ Rectangle {
390420 }
391421
392422 onClicked: {
393− if (spreadView.phase == 2) {
394− if (ApplicationManager.focusedApplicationId == ApplicationManager.get(index).appId) {
423+ if (root.altTabEnabled && spreadView.phase == 2) {
424+ if (applicationManager.focusedApplicationId == applicationManager.get(index).appId) {
395425 spreadView.snapTo(index);
396426 } else {
397− ApplicationManager.requestFocusApplication(ApplicationManager.get(index).appId);
427+ applicationManager.requestFocusApplication(applicationManager.get(index).appId);
398428 }
399429 }
400430 }
@@ -409,7 +439,7 @@ Rectangle {
409439
410440 onClosed: {
411441 spreadView.closingIndex = index;
412− ApplicationManager.stopApplication(ApplicationManager.get(index).appId);
442+ applicationManager.stopApplication(applicationManager.get(index).appId);
413443 }
414444 }
415445 }
@@ -422,7 +452,7 @@ Rectangle {
422452 direction: Direction.Leftwards
423453 enabled: (spreadView.phase != 2 && root.spreadEnabled) || dragging
424454
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 }
426456 width: root.dragAreaWidth
427457
428458 property var gesturePoints: new Array()
qml/Stages/SessionContainer.qml+22−7
@@ -17,7 +17,7 @@
1717import QtQuick 2.0
1818import "Animations"
1919
20−Item {
20+FocusScope {
2121 id: root
2222 objectName: "sessionContainer"
2323 property QtObject session
@@ -34,8 +34,8 @@ Item {
3434 orientation: root.orientation
3535 }
3636
37−
3837 Repeater {
38+ id: childSessionsRepeater
3939 model: root.childSessions
4040
4141 delegate: Loader {
@@ -45,9 +45,17 @@ Item {
4545 // Only way to do recursive qml items.
4646 source: Qt.resolvedUrl("SessionContainer.qml")
4747
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+
4856 Binding {
4957 target: item; when: item
50− property: "interactive"; value: root.interactive
58+ property: "interactive"; value: index == (childSessionsRepeater.count - 1) && root.interactive
5159 }
5260
5361 Binding {
@@ -69,10 +77,6 @@ Item {
6977 target: item; when: item
7078 property: "orientation"; value: root.orientation
7179 }
72−
73− Component.onDestruction: {
74− root.session.surface.forceActiveFocus();
75− }
7680 }
7781 }
7882
@@ -138,5 +142,16 @@ Item {
138142 QtObject {
139143 id: d
140144 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+ }
141156 }
142157}
qml/Stages/SpreadDelegate.qml+2−1
@@ -21,7 +21,7 @@ import QtQuick 2.0
2121import Ubuntu.Components 1.1
2222import "../Components"
2323
24−Item {
24+FocusScope {
2525 id: root
2626
2727 // to be read from outside
@@ -65,6 +65,7 @@ Item {
6565 ApplicationWindow {
6666 id: appWindow
6767 objectName: application ? "appWindow_" + application.appId : "appWindow_null"
68+ focus: true
6869 anchors {
6970 fill: parent
7071 topMargin: appWindow.fullscreen ? 0 : maximizedAppTopMargin
qml/Stages/SurfaceContainer.qml+11−25
@@ -18,7 +18,7 @@ import QtQuick 2.0
1818import Ubuntu.Components 1.1
1919import Ubuntu.Gestures 0.1 // For TouchGate
2020
21−Item {
21+FocusScope {
2222 id: root
2323 objectName: "surfaceContainer"
2424 property Item surface: null
@@ -26,10 +26,15 @@ Item {
2626 property int orientation
2727 property bool interactive
2828
29+ signal surfacePressed()
30+
2931 onSurfaceChanged: {
3032 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;
3136 surface.parent = root;
32− d.forceSurfaceActiveFocusIfReady();
37+ surface.focus = true;
3338 } else {
3439 hadSurface = true;
3540 }
@@ -38,7 +43,6 @@ Item {
3843 Binding { target: surface; property: "orientation"; value: root.orientation }
3944 Binding { target: surface; property: "z"; value: 1 }
4045 Binding { target: surface; property: "enabled"; value: root.interactive; when: surface }
41− Binding { target: surface; property: "focus"; value: root.interactive; when: surface }
4246 Binding { target: surface; property: "antialiasing"; value: !root.interactive; when: surface }
4347
4448 TouchGate {
@@ -46,28 +50,10 @@ Item {
4650 anchors.fill: root
4751 enabled: root.surface ? root.surface.enabled : false
4852 z: 2
49− }
50−
51− Connections {
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();
53+ onPressed: {
54+ root.focus = true;
55+ if (root.interactive) {
56+ root.forceActiveFocus();
7157 }
7258 }
7359 }
qml/Stages/TabletStage.qml+14
@@ -76,6 +76,20 @@ Rectangle {
7676
7777 appId0 = ApplicationManager.count >= 1 ? ApplicationManager.get(0).appId : "";
7878 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+ }
7993 }
8094
8195 function indexOf(appId) {
qml/Stages/TransformedSpreadDelegate.qml+27−21
@@ -46,6 +46,18 @@ SpreadDelegate {
4646 property real startDistance: units.gu(5)
4747 property real endDistance: units.gu(.5)
4848
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+
4961 onSelectedChanged: {
5062 if (selected) {
5163 priv.snapshot();
@@ -63,8 +75,8 @@ SpreadDelegate {
6375 Connections {
6476 target: spreadView
6577 onPhaseChanged: {
66− if (spreadView.phase == 1) {
67− if (index == 0) {
78+ if (root.focusFirstApp && spreadView.phase == 1) {
79+ if (index === 0) {
6880 priv.phase2startTranslate = priv.easingAnimation(0, spreadView.positionMarker4, 0, -spreadView.width, spreadView.positionMarker4) + spreadView.width;
6981 priv.phase2startAngle = priv.easingAnimation(0, spreadView.positionMarker4, root.startAngle, root.endAngle, spreadView.positionMarker4);
7082 priv.phase2startScale = priv.easingAnimation(0, spreadView.positionMarker4, root.startScale, root.endScale, spreadView.positionMarker4);
@@ -134,7 +146,7 @@ SpreadDelegate {
134146 }
135147
136148 if (otherSelected) {
137− if (spreadView.phase < 2 && index == 0) {
149+ if (spreadView.phase < 2 && root.isFocused) {
138150 return linearAnimation(selectedProgress, 0, selectedXTranslate,
139151 selectedXTranslate - spreadView.tileDistance, root.progress);
140152 }
@@ -142,8 +154,7 @@ SpreadDelegate {
142154 return selectedXTranslate;
143155 }
144156
145− switch (index) {
146− case 0:
157+ if (root.focusFirstApp && index === 0) {
147158 if (spreadView.phase == 0) {
148159 return Math.min(0, linearAnimation(0, spreadView.positionMarker2,
149160 0, -spreadView.width * .25, root.animatedProgress));
@@ -153,11 +164,10 @@ SpreadDelegate {
153164 } else if (!priv.isSelected){ // phase 2
154165 // Apply the same animation as with the rest but add spreadView.width to align it with the others.
155166 return -easingCurve.value * spreadView.width + spreadView.width;
156− } else if (priv.isSelected) {
167+ } else {
157168 return linearAnimation(selectedProgress, 0, selectedXTranslate, 0, root.progress);
158169 }
159−
160− case 1:
170+ } else if (root.focusFirstApp && index === 1) {
161171 if (spreadView.phase == 0 && !priv.isSelected) {
162172 return linearAnimation(0, spreadView.positionMarker2,
163173 0, -spreadView.width * spreadView.snapPosition, root.animatedProgress);
@@ -170,13 +180,13 @@ SpreadDelegate {
170180
171181 if (priv.isSelected) {
172182 // Distance to left edge
173− var targetTranslate = -spreadView.width - ((index - 1) * root.startDistance);
183+ var targetTranslate = -spreadView.width - (root.spreadIndex * root.startDistance);
174184 return linearAnimation(selectedProgress, 0,
175185 selectedXTranslate, targetTranslate, root.progress);
176186 }
177187
178188 // Fix it at the right edge...
179− var rightEdgeOffset = -((index - 1) * root.startDistance);
189+ var rightEdgeOffset = -(root.spreadIndex * root.startDistance);
180190 // ...and use our easing to move them to the left. Stop a bit earlier for each tile
181191 var animatedEndDistance = linearAnimation(0, 2, root.endDistance, 0, root.progress);
182192 return -easingCurve.value * spreadView.width + (index * animatedEndDistance) + rightEdgeOffset;
@@ -194,8 +204,7 @@ SpreadDelegate {
194204 if (priv.isSelected) {
195205 return linearAnimation(selectedProgress, 0, selectedAngle, 0, root.progress);
196206 }
197− switch (index) {
198− case 0:
207+ if (root.focusFirstApp && index === 0) {
199208 if (spreadView.phase == 0) {
200209 return Math.max(0, linearAnimation(0, spreadView.positionMarker2,
201210 0, root.tile0SnapAngle, root.animatedProgress));
@@ -203,7 +212,7 @@ SpreadDelegate {
203212 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
204213 root.tile0SnapAngle, phase2startAngle, root.progress);
205214 }
206− case 1:
215+ } else if (root.focusFirstApp && index === 1) {
207216 if (spreadView.phase == 0) {
208217 return linearAnimation(0, spreadView.positionMarker2, root.startAngle,
209218 root.startAngle * (1-spreadView.snapPosition), root.animatedProgress);
@@ -227,15 +236,14 @@ SpreadDelegate {
227236 return linearAnimation(selectedProgress, 0, selectedScale, 1, root.progress);
228237 }
229238
230− switch (index) {
231− case 0:
239+ if (root.focusFirstApp && index === 0) {
232240 if (spreadView.phase == 0) {
233241 return 1;
234242 } else if (spreadView.phase == 1) {
235243 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
236244 1, phase2startScale, root.progress);
237245 }
238− case 1:
246+ } else if (root.focusFirstApp && index === 1) {
239247 if (spreadView.phase == 0) {
240248 var targetScale = tile1StartScale - ((tile1StartScale - 1) * spreadView.snapPosition);
241249 return linearAnimation(0, spreadView.positionMarker2,
@@ -254,7 +262,7 @@ SpreadDelegate {
254262 return linearAnimation (selectedProgress, Math.max(0, selectedProgress - .5),
255263 selectedOpacity, 0, root.progress);
256264 }
257− if (index == 0) {
265+ if (root.isFocused) {
258266 switch (spreadView.phase) {
259267 case 0:
260268 return linearAnimation(0, spreadView.positionMarker2, 1, .7, root.animatedProgress);
@@ -272,16 +280,14 @@ SpreadDelegate {
272280 return linearAnimation(selectedProgress, 0, selectedTopMarginProgress, 0, root.progress);
273281 }
274282
275− switch (index) {
276− case 0:
283+ if (root.focusFirstApp && index === 0) {
277284 if (spreadView.phase == 0) {
278285 return 0;
279286 } else if (spreadView.phase == 1) {
280287 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
281288 0, priv.phase2startTopMarginProgress, root.progress);
282289 }
283− break;
284− case 1:
290+ } else if (root.focusFirstApp && index === 1) {
285291 if (spreadView.phase == 0) {
286292 return 0;
287293 } else if (spreadView.phase == 1) {
qml/Stages/TransformedTabletSpreadDelegate.qml+1−1
@@ -366,7 +366,7 @@ SpreadDelegate {
366366 Scale {
367367 origin { x: 0; y: (spreadView.height * priv.scale) + maximizedAppTopMargin * 3 }
368368 xScale: 1
369− yScale: isFullscreen ? 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height : 1
369+ yScale: fullscreen ? 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height : 1
370370 },
371371 Translate {
372372 x: priv.xTranslate
qml/Stages/WindowMoveResizeArea.qml+33−24
@@ -18,6 +18,7 @@
1818
1919import QtQuick 2.3
2020import Ubuntu.Components 1.1
21+import Utils 0.1
2122
2223MouseArea {
2324 id: root
@@ -26,7 +27,8 @@ MouseArea {
2627
2728 // The target item managed by this. Must be a parent or a sibling
2829 // 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: ""
3032 property int resizeHandleWidth: 0
3133 property int minWidth: 0
3234 property int minHeight: 0
@@ -37,8 +39,6 @@ MouseArea {
3739 readonly property int windowHeight: root.height - resizeHandleWidth * 2
3840
3941 property var startPoint
40− property int startWidth
41− property int startHeight
4242
4343 property bool resizeTop: false
4444 property bool resizeBottom: false
@@ -47,10 +47,18 @@ MouseArea {
4747
4848 }
4949
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+
5060 onPressed: {
51− priv.startPoint = mapToItem(null, Qt.point(mouse.x, mouse.y)).x;
52− priv.startWidth = root.width;
53− priv.startHeight = root.height;
61+ priv.startPoint = Qt.point(mouse.x, mouse.y);
5462 priv.resizeTop = mouseY < root.resizeHandleWidth;
5563 priv.resizeBottom = mouseY > (root.height - root.resizeHandleWidth);
5664 priv.resizeLeft = mouseX < root.resizeHandleWidth;
@@ -58,32 +66,28 @@ MouseArea {
5866 }
5967
6068 onPositionChanged: {
61− var currentPoint = mapToItem(null, Qt.point(mouse.x, mouse.y)).x;
69+ var currentPoint = Qt.point(mouse.x, mouse.y);
6270 var mouseDiff = Qt.point(currentPoint.x - priv.startPoint.x, currentPoint.y - priv.startPoint.y);
6371 var moveDiff = Qt.point(0, 0);
6472 var sizeDiff = Qt.point(0, 0);
73+ var maxSizeDiff = Qt.point(root.minWidth - root.target.width, root.minHeight - root.target.height)
74+
6575 if (priv.resizeTop || priv.resizeBottom || priv.resizeLeft || priv.resizeRight) {
6676 if (priv.resizeTop) {
67− moveDiff.y += mouseDiff.y;
68− sizeDiff.y += -mouseDiff.y;
69− } else if (priv.resizeBottom) {
70− sizeDiff.y += mouse.y - priv.startPoint.y;
71− priv.startPoint.y = mouse.y
77+ sizeDiff.y = Math.max(maxSizeDiff.y, -currentPoint.y + priv.startPoint.y)
78+ moveDiff.y = -sizeDiff.y
7279 }
73− if (priv.resizeLeft) {
74− moveDiff.x += mouseDiff.x;
75− sizeDiff.x += -mouseDiff.x;
76− } else if (priv.resizeRight) {
77− sizeDiff.x += mouse.x - priv.startPoint.x;
78− priv.startPoint.x = mouse.x
80+ if (priv.resizeBottom) {
81+ sizeDiff.y = Math.max(maxSizeDiff.y, currentPoint.y - priv.startPoint.y)
82+ priv.startPoint.y += sizeDiff.y
7983 }
80− if (priv.windowWidth + sizeDiff.x < root.minWidth) {
81− sizeDiff.x = root.minWidth - priv.windowWidth;
82− moveDiff.x = moveDiff.x == 0 ? 0 : -sizeDiff.x;
84+ if (priv.resizeLeft) {
85+ sizeDiff.x = Math.max(maxSizeDiff.x, -currentPoint.x + priv.startPoint.x)
86+ moveDiff.x = -sizeDiff.x
8387 }
84− if (priv.windowHeight + sizeDiff.y < root.minHeight) {
85− sizeDiff.y = root.minHeight - priv.windowHeight;
86− moveDiff.y = moveDiff.y == 0 ? 0 : -sizeDiff.y;
88+ if (priv.resizeRight) {
89+ sizeDiff.x = Math.max(maxSizeDiff.x, currentPoint.x - priv.startPoint.x)
90+ priv.startPoint.x += sizeDiff.x
8791 }
8892
8993 target.x += moveDiff.x;
@@ -94,5 +98,10 @@ MouseArea {
9498 target.x += mouseDiff.x;
9599 target.y += mouseDiff.y;
96100 }
101+
102+ }
103+
104+ Component.onDestruction: {
105+ WindowStateStorage.saveGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
97106 }
98107}
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 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -12,22 +12,18 @@
1212 *
1313 * You should have received a copy of the GNU General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
1715 */
1816
19−#include "../UsersModelPrivate.h"
20−
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−}
17+import QtQuick 2.3
18+import Ubuntu.Components 1.1
3219
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+ }
3329}
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 {
4141 }
4242
4343 function quitWizard() {
44+ pageStack.currentPage.enabled = false;
45+
4446 var errorMsg = securityPrivacy.setSecurity("", password, passwordMethod)
4547 if (errorMsg !== "") {
4648 // 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 @@
1616
1717import QtQuick 2.0
1818import QMenuModel 0.1 as QMenuModel
19+import QtSystemInfo 5.0
1920import Ubuntu.Components 0.1
2021import Ubuntu.Components.ListItems 0.1 as ListItem
2122import Ubuntu.Settings.Menus 0.1 as Menus
@@ -28,7 +29,7 @@ LocalComponents.Page {
2829 title: i18n.tr("Connect to Wi‑Fi")
2930 forwardButtonSourceComponent: forwardButton
3031
31− readonly property bool connected: mainMenu.connectedAPs === 1
32+ readonly property bool connected: networkInfo.accessPointName
3233
3334 function getExtendedProperty(object, propertyName, defaultValue) {
3435 if (object && object.hasOwnProperty(propertyName)) {
@@ -44,6 +45,31 @@ LocalComponents.Page {
4445 menuObjectPath: "/com/canonical/indicator/network/phone_wifi_settings"
4546 }
4647
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+
4773 Component {
4874 id: accessPointComponent
4975 ListItem.Standard {
@@ -119,11 +145,6 @@ LocalComponents.Page {
119145 onCheckedChanged: {
120146 // Can't rely on binding. Checked is assigned on click.
121147 checkBoxActive.checked = checked;
122− if (checked) {
123− mainMenu.connectedAPs++
124− } else {
125− mainMenu.connectedAPs--
126− }
127148 }
128149 onActivate: unityMenuModel.activate(menuIndex);
129150 }
@@ -165,9 +186,6 @@ LocalComponents.Page {
165186 Repeater {
166187 id: mainMenu
167188
168− // FIXME Check connection better https://bugs.launchpad.net/indicator-network/+bug/1349371
169− property int connectedAPs: 0
170−
171189 model: menuModel
172190
173191 delegate: Loader {
run.sh+3−26
@@ -6,8 +6,6 @@ export QML2_IMPORT_PATH
66QML_PHONE_SHELL_PATH=./builddir/src/unity8
77GDB=false
88FAKE=false
9−PINLOCK=false
10−KEYLOCK=false
119USE_MOCKS=false
1210MOUSE_TOUCH=true
1311
@@ -16,9 +14,6 @@ usage() {
1614 echo "Script to run the shell.\n" >&2
1715 echo "OPTIONS:" >&2
1816 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
2217 echo " -g, --gdb Run through gdb." >&2
2318 echo " -h, --help Show this help." >&2
2419 echo " -m, --nomousetouch Run without -mousetouch argument." >&2
@@ -26,7 +21,7 @@ usage() {
2621 exit 1
2722}
2823
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" -- "$@"`
3025[ $? -ne 0 ] && usage
3126eval set -- "$ARGS"
3227
@@ -34,12 +29,6 @@ while [ $# -gt 0 ]
3429do
3530 case "$1" in
3631 -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;;
4332 -g|--gdb) GDB=true;;
4433 -h|--help) usage;;
4534 -m|--nomousetouch) MOUSE_TOUCH=false;;
@@ -48,23 +37,11 @@ do
4837 shift
4938done
5039
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−
6040if $USE_MOCKS; then
61− rm -f $PWD/builddir/nonmirplugins/LightDM # undo symlink (from below) for cleanliness
6241 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
6443else
65− # Just link our LightDM mock into the nonmirplugins folder. We don't want
66− # the rest of our plugins to be used.
67− ln -sf $PWD/builddir/tests/mocks/LightDM $PWD/builddir/nonmirplugins/
44+ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/plugins/LightDM/liblightdm
6845fi
6946
7047QML_PHONE_SHELL_ARGS=""
tests/autopilot/unity8/shell/__init__.py−13
@@ -27,19 +27,6 @@ import logging
2727
2828logger = logging.getLogger(__name__)
2929
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−
4330def disable_qml_mocking(fn):
4431 """Simple decorator that disables the QML mocks from being loaded."""
4532 @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):
5454 return ld_library_path
5555
5656
57−class EdgesDemo(fixtures.Fixture):
57+class Tutorial(fixtures.Fixture):
5858
5959 def __init__(self, enable):
6060 super().__init__()
@@ -62,12 +62,12 @@ class EdgesDemo(fixtures.Fixture):
6262
6363 def setUp(self):
6464 super().setUp()
65− original_state = self._is_edges_demo_enabled()
65+ original_state = self._is_tutorial_enabled()
6666 if self.enable != original_state:
67− self.addCleanup(self._set_edges_demo, original_state)
68− self._set_edges_demo(self.enable)
67+ self.addCleanup(self._set_tutorial, original_state)
68+ self._set_tutorial(self.enable)
6969
70− def _is_edges_demo_enabled(self):
70+ def _is_tutorial_enabled(self):
7171 command = [
7272 'dbus-send', '--system', '--print-reply',
7373 '--dest=org.freedesktop.Accounts',
@@ -79,7 +79,7 @@ class EdgesDemo(fixtures.Fixture):
7979 output = subprocess.check_output(command, universal_newlines=True)
8080 return True if output.count('true') else False
8181
82− def _set_edges_demo(self, value):
82+ def _set_tutorial(self, value):
8383 value_string = 'true' if value else 'false'
8484 command = [
8585 'dbus-send', '--system', '--print-reply',
tests/autopilot/unity8/shell/tests/__init__.py+8−11
@@ -153,7 +153,6 @@ class UnityTestCase(AutopilotTestCase):
153153 self.useFixture(toolkit_fixtures.HideUnity7Launcher())
154154
155155 self._proxy = None
156− self._lightdm_mock_type = None
157156 self._qml_mock_enabled = True
158157 self._data_dirs_mock_enabled = True
159158 self._environment = {}
@@ -265,8 +264,7 @@ class UnityTestCase(AutopilotTestCase):
265264 binary_path
266265 )
267266
268− if self._lightdm_mock_type is None:
269− self.patch_lightdm_mock()
267+ self.patch_lightdm_mock()
270268
271269 if self._qml_mock_enabled:
272270 self._environment['QML2_IMPORT_PATH'] = (
@@ -338,12 +336,11 @@ class UnityTestCase(AutopilotTestCase):
338336 if data_dirs is not None:
339337 self._environment['XDG_DATA_DIRS'] = data_dirs
340338
341− def patch_lightdm_mock(self, mock_type='single'):
342− self._lightdm_mock_type = mock_type
343− logger.info("Setting up LightDM mock type '%s'", mock_type)
339+ def patch_lightdm_mock(self):
340+ logger.info("Setting up LightDM mock lib")
344341 new_ld_library_path = [
345342 get_default_extra_mock_libraries(),
346− self._get_lightdm_mock_path(mock_type)
343+ self._get_lightdm_mock_path()
347344 ]
348345 if os.getenv('LD_LIBRARY_PATH') is not None:
349346 new_ld_library_path.append(os.getenv('LD_LIBRARY_PATH'))
@@ -353,16 +350,16 @@ class UnityTestCase(AutopilotTestCase):
353350
354351 self._environment['LD_LIBRARY_PATH'] = new_ld_library_path
355352
356− def _get_lightdm_mock_path(self, mock_type):
353+ def _get_lightdm_mock_path(self):
357354 lib_path = get_mocks_library_path()
358355 lightdm_mock_path = os.path.abspath(
359− os.path.join(lib_path, "LightDM", mock_type)
356+ os.path.join(lib_path, "LightDM", "liblightdm")
360357 )
361358
362359 if not os.path.exists(lightdm_mock_path):
363360 raise RuntimeError(
364− "LightDM mock '%s' does not exist at path '%s'."
365− % (mock_type, lightdm_mock_path)
361+ "LightDM mock does not exist at path '%s'."
362+ % (lightdm_mock_path)
366363 )
367364 return lightdm_mock_path
368365
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 @@
2020
2121from __future__ import absolute_import
2222
23−from unity8.shell import with_lightdm_mock
2423from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
2524
2625from autopilot.matchers import Eventually
@@ -41,9 +40,10 @@ class TestLockscreen(UnityTestCase):
4140
4241 scenarios = _get_device_emulation_scenarios()
4342
44− @with_lightdm_mock("single-pin")
4543 def test_can_unlock_pin_screen(self):
4644 """Must be able to unlock the PIN entry lock screen."""
45+
46+ self._environment['LIBLIGHTDM_MOCK_MODE'] = "single-pin"
4747 self.launch_unity()
4848 greeter = self.main_window.get_greeter()
4949
@@ -56,9 +56,10 @@ class TestLockscreen(UnityTestCase):
5656 self._enter_prompt_passphrase("1234\n")
5757 self.assertThat(greeter.shown, Eventually(Equals(False)))
5858
59− @with_lightdm_mock("single-passphrase")
6059 def test_can_unlock_passphrase_screen(self):
6160 """Must be able to unlock the passphrase entry screen."""
61+
62+ self._environment['LIBLIGHTDM_MOCK_MODE'] = "single-passphrase"
6263 self.launch_unity()
6364 greeter = self.main_window.get_greeter()
6465
@@ -71,9 +72,9 @@ class TestLockscreen(UnityTestCase):
7172 self._enter_prompt_passphrase("password")
7273 self.assertThat(greeter.shown, Eventually(Equals(False)))
7374
74− @with_lightdm_mock("single-pin")
7575 def test_pin_screen_wrong_code(self):
7676 """Entering the wrong pin code must not dismiss the lock screen."""
77+ self._environment['LIBLIGHTDM_MOCK_MODE'] = "single-pin"
7778 self.launch_unity()
7879 greeter = self.main_window.get_greeter()
7980
@@ -90,9 +91,9 @@ class TestLockscreen(UnityTestCase):
9091 self.assertThat(prompt.text, Eventually(Equals("")))
9192 self.assertThat(greeter.shown, Eventually(Equals(True)))
9293
93− @with_lightdm_mock("single-passphrase")
9494 def test_passphrase_screen_wrong_password(self):
9595 """Entering the wrong password must not dismiss the lock screen."""
96+ self._environment['LIBLIGHTDM_MOCK_MODE'] = "single-passphrase"
9697 self.launch_unity()
9798 greeter = self.main_window.get_greeter()
9899
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 @@
44# actually using the system liblightdm in the normal plugin, this version can
55# be deleted.
66
7−add_subdirectory(demo)
8−add_subdirectory(full)
9−add_subdirectory(single)
10−add_subdirectory(single-pin)
11−add_subdirectory(single-passphrase)
7+add_subdirectory(liblightdm)
128
139include_directories(
1410 ${CMAKE_CURRENT_SOURCE_DIR}
1511 ${CMAKE_CURRENT_BINARY_DIR}
1612 ${CMAKE_SOURCE_DIR}/plugins/Utils
13+ ${CMAKE_SOURCE_DIR}/plugins/LightDM
1714 ${CMAKE_SOURCE_DIR}/tests/mocks/libusermetrics
1815 ${libunity8-private_SOURCE_DIR}
1916)
@@ -22,9 +19,11 @@ set(QMLPLUGIN_SRC
2219 ${CMAKE_SOURCE_DIR}/plugins/LightDM/DBusGreeter.cpp
2320 ${CMAKE_SOURCE_DIR}/plugins/LightDM/DBusGreeterList.cpp
2421 ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
25− ${CMAKE_SOURCE_DIR}/plugins/LightDM/plugin.cpp
2622 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
2723 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
24+ MockGreeter.cpp
25+ MockUsersModel.cpp
26+ plugin.cpp
2827 )
2928
3029add_library(MockLightDM-qml MODULE
@@ -35,9 +34,9 @@ add_library(MockLightDM-qml MODULE
3534# installed on the system. So we make sure we link to our full fake version
3635# At run time, we can point to whichever version we happen to be using via
3736# LD_LIBRARY_PATH.
38−add_dependencies(MockLightDM-qml MockLightDM-full MockUserMetrics)
37+add_dependencies(MockLightDM-qml MockLightDM MockUserMetrics)
3938target_link_libraries(MockLightDM-qml
40− -L${CMAKE_CURRENT_BINARY_DIR}/full
39+ -L${CMAKE_CURRENT_BINARY_DIR}/liblightdm
4140 -llightdm-qt5-2
4241 -L${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics
4342 -lusermetricsoutput
@@ -49,5 +48,5 @@ qt5_use_modules(MockLightDM-qml DBus Gui Qml)
4948add_unity8_mock(LightDM 0.1 LightDM
5049 PREFIX mocks
5150 TARGETS MockLightDM-qml
52− ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_CURRENT_BINARY_DIR}/full"
51+ ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_CURRENT_BINARY_DIR}/liblightdm"
5352)
tests/mocks/LightDM/single/GreeterPrivate.cpp → tests/mocks/LightDM/MockGreeter.cpprenamed+12−23
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2014 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -13,34 +13,23 @@
1313 * You should have received a copy of the GNU General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 *
16− * Author: Michael Terry <michael.terry@canonical.com>
1716 */
1817
19−#include "../Greeter.h"
20−#include "../GreeterPrivate.h"
18+#include "MockGreeter.h"
19+#include <GreeterPrivate.h>
2120
22−namespace QLightDM
23−{
24−
25−GreeterPrivate::GreeterPrivate(Greeter* parent)
26− : authenticated(false),
27− authenticationUser(),
28− twoFactorDone(false),
29− q_ptr(parent)
21+QString MockGreeter::mockMode() const
3022{
23+ Q_D(const Greeter);
24+ return d->m_greeter->mockMode();
3125}
3226
33−void GreeterPrivate::handleAuthenticate()
27+void MockGreeter::setMockMode(QString mockMode)
3428{
35− Q_Q(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−}
29+ Q_D(Greeter);
4530
31+ if (d->m_greeter->mockMode() != mockMode) {
32+ d->m_greeter->setMockMode(mockMode);
33+ Q_EMIT mockModeChanged(mockMode);
34+ }
4635}
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 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2014 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -12,12 +12,10 @@
1212 *
1313 * You should have received a copy of the GNU General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
1715 */
1816
19−#include "../Greeter.h"
20−#include "../GreeterPrivate.h"
17+#include "Greeter.h"
18+#include "GreeterPrivate.h"
2119
2220namespace QLightDM
2321{
@@ -26,14 +24,33 @@ GreeterPrivate::GreeterPrivate(Greeter* parent)
2624 : authenticated(false),
2725 authenticationUser(),
2826 twoFactorDone(false),
27+ mockMode("single"),
2928 q_ptr(parent)
3029{
30+ char *envMockMode = getenv("LIBLIGHTDM_MOCK_MODE");
31+ if (envMockMode) {
32+ mockMode = envMockMode;
33+ }
3134}
3235
3336void GreeterPrivate::handleAuthenticate()
3437{
3538 Q_Q(Greeter);
3639
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+
3754 // Send out any messages we need to
3855 if (authenticationUser == "info-prompt")
3956 Q_EMIT q->showMessage("Welcome to Unity Greeter", Greeter::MessageTypeInfo);
@@ -65,7 +82,24 @@ void GreeterPrivate::handleAuthenticate()
6582 }
6683}
6784
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)
69103{
70104 Q_Q(Greeter);
71105
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 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2014 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -12,17 +12,69 @@
1212 *
1313 * You should have received a copy of the GNU General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
1715 */
1816
19−#include "../UsersModelPrivate.h"
17+#include "UsersModelPrivate.h"
18+#include "UsersModel.h"
2019
2120namespace QLightDM
2221{
2322
2423UsersModelPrivate::UsersModelPrivate(UsersModel* parent)
25− : q_ptr(parent)
24+ : mockMode("single")
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()
2678{
2779 entries =
2880 {
@@ -49,4 +101,4 @@ UsersModelPrivate::UsersModelPrivate(UsersModel* parent)
49101 };
50102}
51103
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 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2014 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -12,22 +12,21 @@
1212 *
1313 * You should have received a copy of the GNU General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
1715 */
1816
19−#include "../UsersModelPrivate.h"
17+#ifndef MOCK_LIGHTDM_PLUGIN_H
18+#define MOCK_LIGHTDM_PLUGIN_H
2019
21−namespace QLightDM
22−{
20+#include <QtQml/QQmlEngine>
21+#include <QtQml/QQmlExtensionPlugin>
2322
24−UsersModelPrivate::UsersModelPrivate(UsersModel* parent)
25− : q_ptr(parent)
23+class LightDMPlugin : public QQmlExtensionPlugin
2624{
27− entries =
28− {
29− { "single", "Single User", 0, 0, false, false, 0, 0 },
30− };
31−}
25+ Q_OBJECT
26+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27+
28+public:
29+ void registerTypes(const char *uri);
30+};
3231
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 {
4444 }
4545
4646 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
4762 anchors.fill: parent
4863 text: "SURFACE"
49− color: "yellow"
64+ color: root.parent && root.parent.activeFocus ? "yellow" : "blue"
5065 font.bold: true
5166 fontSizeMode: Text.Fit
5267 minimumPixelSize: 10; font.pixelSize: 200
tests/mocks/Unity/CMakeLists.txt+4−1
@@ -6,7 +6,7 @@ add_subdirectory(Notifications)
66add_subdirectory(DashCommunicator)
77
88pkg_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)
1010
1111include_directories(
1212 ${CMAKE_CURRENT_BINARY_DIR}
@@ -40,6 +40,9 @@ set(UnityQML_SOURCES
4040 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/SettingsModelInterface.h
4141)
4242
43+# Workaround for gcc failure LP: #1417664
44+set_source_files_properties(fake_scopes.cpp PROPERTIES COMPILE_FLAGS -O1)
45+
4346add_library(FakeUnityQml MODULE ${UnityQML_SOURCES})
4447target_link_libraries(FakeUnityQml
4548 ${Qt5Core_LIBRARIES}
tests/mocks/Unity/Indicators/fakeindicatorsmodel.cpp+13−1
@@ -21,7 +21,8 @@
2121#include "indicators.h"
2222
2323FakeIndicatorsModel::FakeIndicatorsModel(QObject *parent)
24− : QAbstractListModel(parent)
24+ : QAbstractListModel(parent),
25+ m_profile("phone")
2526{
2627 QObject::connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
2728 QObject::connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
@@ -38,6 +39,17 @@ int FakeIndicatorsModel::count() const
3839 return rowCount();
3940}
4041
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+
4153void FakeIndicatorsModel::load(const QString&)
4254{
4355}
tests/mocks/Unity/Indicators/fakeindicatorsmodel.h+6
@@ -27,7 +27,9 @@ class FakeIndicatorsModel : public QAbstractListModel
2727 Q_OBJECT
2828 Q_ENUMS(Roles)
2929 Q_PROPERTY(int count READ count NOTIFY countChanged)
30+ Q_PROPERTY(QString profile READ profile WRITE setProfile NOTIFY profileChanged)
3031 Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
32+
3133public:
3234
3335 FakeIndicatorsModel(QObject *parent=0);
@@ -42,6 +44,8 @@ public:
4244
4345 Q_INVOKABLE QVariant data(int row, int role) const;
4446
47+ QString profile() const;
48+ void setProfile(const QString& profile);
4549 void setModelData(const QVariant& data);
4650 QVariant modelData() const { return m_modelData; }
4751
@@ -54,11 +58,13 @@ public:
5458
5559Q_SIGNALS:
5660 void countChanged();
61+ void profileChanged();
5762 void modelDataChanged();
5863
5964private:
6065 int count() const;
6166
67+ QString m_profile;
6268 QVariant m_modelData;
6369};
6470
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)
22
33include_directories(
44 ${CMAKE_CURRENT_SOURCE_DIR}
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+10
@@ -209,6 +209,16 @@ void MockLauncherModel::setApplicationManager(unity::shell::application::Applica
209209 Q_UNUSED(applicationManager)
210210}
211211
212+bool MockLauncherModel::onlyPinned() const
213+{
214+ return false;
215+}
216+
217+void MockLauncherModel::setOnlyPinned(bool onlyPinned)
218+{
219+ Q_UNUSED(onlyPinned)
220+}
221+
212222void MockLauncherModel::emitHint()
213223{
214224 Q_EMIT hint();
tests/mocks/Unity/Launcher/MockLauncherModel.h+3
@@ -49,6 +49,9 @@ public:
4949 unity::shell::application::ApplicationManagerInterface *applicationManager() const;
5050 void setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager);
5151
52+ bool onlyPinned() const override;
53+ void setOnlyPinned(bool onlyPinned) override;
54+
5255 // For testing
5356 Q_INVOKABLE void emitHint();
5457
tests/mocks/Unity/Notifications/CMakeLists.txt+2−1
@@ -4,7 +4,8 @@ include_directories(
44
55set(MockNotificationsPlugin_SOURCES
66 plugin.cpp
7− MockNotificationTypes.cpp
7+ MockNotification.cpp
8+ MockNotificationModel.cpp
89 MockActionModel.cpp
910)
1011
tests/mocks/Unity/Notifications/MockActionModel.cpp+6−4
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2015 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -25,8 +25,6 @@ struct ActionModelPrivate {
2525};
2626
2727ActionModel::ActionModel(QObject *parent) : QStringListModel(parent), p(new ActionModelPrivate) {
28− insertAction("ok_id", "Ok");
29− insertAction("cancel_id", "Cancel");
3028}
3129
3230ActionModel::~ActionModel() {
@@ -66,7 +64,11 @@ Q_INVOKABLE QVariant ActionModel::data(int row, int role) const
6664 return data(index(row, 0), role);
6765}
6866
69−void ActionModel::insertAction(const QString &id, const QString &label) {
67+void ActionModel::append(const QString &id, const QString &label) {
7068 p->ids.push_back(id);
7169 p->labels.push_back(label);
7270}
71+
72+int ActionModel::getCount() const {
73+ return p->labels.size();
74+}
tests/mocks/Unity/Notifications/MockActionModel.h+4−2
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2015 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -26,6 +26,7 @@ struct ActionModelPrivate;
2626
2727class ActionModel : public QStringListModel {
2828 Q_OBJECT
29+ Q_PROPERTY(int count READ getCount)
2930
3031public:
3132 ActionModel(QObject *parent=nullptr);
@@ -42,7 +43,8 @@ public:
4243 };
4344 Q_INVOKABLE QVariant data(int row, int role) const;
4445
45− void insertAction(const QString &id, const QString &label);
46+ Q_INVOKABLE void append(const QString &id, const QString &label);
47+ int getCount() const;
4648
4749private:
4850 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 @@
11/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2015 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -19,13 +19,15 @@
1919
2020#include "plugin.h"
2121#include "MockActionModel.h"
22−#include "MockNotificationTypes.h"
22+#include "MockNotification.h"
23+#include "MockNotificationModel.h"
2324
2425#include <QtQml/qqml.h>
2526
2627void TestNotificationPlugin::registerTypes(const char* uri)
2728{
2829 // @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");
3032 qmlRegisterType<ActionModel>(uri, 1, 0, "ActionModel");
3133}
tests/mocks/Unity/Notifications/plugin.h+1−2
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2015 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -17,7 +17,6 @@
1717 * Mirco Mueller <mirco.mueller@canonical.com>
1818 */
1919
20−
2120#ifndef TESTNOTIFICATION_PLUGIN_H
2221#define TESTNOTIFICATION_PLUGIN_H
2322
tests/mocks/Unity/fake_previewmodel.cpp+15−1
@@ -22,18 +22,23 @@
2222#include "fake_previewmodel.h"
2323
2424// local
25+#include "fake_scope.h"
26+#include "fake_scopes.h"
2527#include "fake_previewwidgetmodel.h"
2628
2729// Qt
2830#include <QDebug>
2931
30−PreviewModel::PreviewModel(QObject* parent)
32+PreviewModel::PreviewModel(QObject* parent, Scope* scope)
3133 : unity::shell::scopes::PreviewModelInterface(parent)
3234 , m_loaded(true)
35+ , m_scope(scope)
3336{
3437 // we have one column by default
3538 PreviewWidgetModel* columnModel = new PreviewWidgetModel(this);
3639 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&)));
3742}
3843
3944void PreviewModel::setWidgetColumnCount(int count)
@@ -80,3 +85,12 @@ void PreviewModel::setLoaded(bool loaded)
8085 Q_EMIT loadedChanged();
8186 }
8287}
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 @@
2525
2626class PreviewWidgetModel;
2727
28+class Scope;
29+
2830class PreviewModel : public unity::shell::scopes::PreviewModelInterface
2931{
3032 Q_OBJECT
3133
3234public:
33− explicit PreviewModel(QObject* parent = 0);
35+ explicit PreviewModel(QObject* parent = 0, Scope* scope = 0);
3436
3537 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
3638 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
@@ -42,9 +44,13 @@ public:
4244
4345 Q_INVOKABLE void setLoaded(bool); // Only available for testing
4446
47+private Q_SLOTS:
48+ void triggeredSlot(QString const&, QString const&, QVariantMap const&);
49+
4550private:
4651 QList<PreviewWidgetModel*> m_previewWidgetModels;
4752 bool m_loaded;
53+ Scope* m_scope;
4854};
4955
5056Q_DECLARE_METATYPE(PreviewModel*)
tests/mocks/Unity/fake_previewstack.cpp+2−2
@@ -21,10 +21,10 @@
2121#include "fake_previewmodel.h"
2222#include "fake_scope.h"
2323
24−PreviewStack::PreviewStack(QObject* parent)
24+PreviewStack::PreviewStack(QObject* parent, Scope *scope)
2525 : unity::shell::scopes::PreviewStackInterface(parent)
2626{
27− m_previews << new PreviewModel(this);
27+ m_previews << new PreviewModel(this, scope);
2828}
2929
3030PreviewStack::~PreviewStack()
tests/mocks/Unity/fake_previewstack.h+3−1
@@ -25,12 +25,14 @@
2525
2626class PreviewModel;
2727
28+class Scope;
29+
2830class PreviewStack : public unity::shell::scopes::PreviewStackInterface
2931{
3032 Q_OBJECT
3133
3234public:
33− explicit PreviewStack(QObject* parent = 0);
35+ explicit PreviewStack(QObject* parent = 0, Scope* scope = 0);
3436 virtual ~PreviewStack();
3537
3638 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()
5353 PreviewData* preview_data = new PreviewData(QString("widget-%1").arg(i), QString("text"), attributes);
5454 m_previewWidgets.append(QSharedPointer<PreviewData>(preview_data));
5555 }
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+
5667 endResetModel();
5768
5869}
tests/mocks/Unity/fake_scope.cpp+5−5
@@ -185,6 +185,7 @@ void Scope::activate(QVariant const& result)
185185 if (result.toString() == "Result.2.2") {
186186 Scopes *scopes = dynamic_cast<Scopes*>(parent());
187187 m_openScope = scopes->getScopeFromAll("MockScope9");
188+ scopes->addTempScope(m_openScope);
188189 Q_EMIT openScope(m_openScope);
189190 }
190191}
@@ -198,7 +199,7 @@ PreviewStack* Scope::preview(QVariant const& result)
198199 } else {
199200 // This probably leaks, do we don't care
200201 // it's a test after all
201− return new PreviewStack;
202+ return new PreviewStack(this, this);
202203 }
203204}
204205
@@ -208,11 +209,10 @@ void Scope::cancelActivation()
208209
209210void Scope::closeScope(unity::shell::scopes::ScopeInterface* scope)
210211{
211− if (scope != m_openScope) {
212− qDebug() << scope << m_openScope;
213− qFatal("Scope::closeScope got wrong scope in closeScope");
212+ Scopes *scopes = dynamic_cast<Scopes*>(parent());
213+ if (scopes) {
214+ return scopes->closeScope(scope);
214215 }
215− m_openScope = nullptr;
216216}
217217
218218QString Scope::currentNavigationId() const
tests/mocks/Unity/fake_scopes.cpp+12
@@ -23,6 +23,7 @@
2323// TODO: Implement remaining pieces, like Categories (i.e. LensView now gives warnings)
2424
2525// Qt
26+#include <QDebug>
2627#include <QTimer>
2728
2829Scopes::Scopes(QObject *parent)
@@ -177,6 +178,17 @@ unity::shell::scopes::ScopeInterface* Scopes::overviewScope() const
177178 return m_scopesOverview;
178179}
179180
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+
180192void Scopes::setFavorite(const QString& scopeId, bool favorite)
181193{
182194 if (favorite) {
tests/mocks/Unity/fake_scopes.h+5
@@ -26,6 +26,7 @@
2626
2727// Qt
2828#include <QList>
29+#include <QSet>
2930#include <QTimer>
3031
3132class ScopesOverview;
@@ -59,6 +60,9 @@ public:
5960 Q_INVOKABLE void setFavorite(const QString& scopeId, bool favorite) override;
6061 Q_INVOKABLE void moveFavoriteTo(const QString& scopeId, int index) override;
6162
63+ void addTempScope(unity::shell::scopes::ScopeInterface* scope);
64+ Q_INVOKABLE void closeScope(unity::shell::scopes::ScopeInterface* scope) override;
65+
6266 // This is used as part of implementation of the other C++ code, not API
6367 QList<Scope*> favScopes() const;
6468 QList<Scope*> nonFavScopes() const;
@@ -70,6 +74,7 @@ private Q_SLOTS:
7074private:
7175 QList<Scope*> m_scopes; // the favorite ones
7276 QList<Scope*> m_allScopes;
77+ QSet<unity::shell::scopes::ScopeInterface*> m_tempScopes;
7378 ScopesOverview *m_scopesOverview;
7479 bool m_loaded;
7580 QTimer timer;
tests/mocks/Unity/fake_scopesoverview.cpp+1
@@ -48,6 +48,7 @@ Q_INVOKABLE void ScopesOverview::activate(QVariant const& result)
4848 Q_EMIT gotoScope(result.toString());
4949 } else {
5050 m_openScope = scopes->getScopeFromAll(result.toString());
51+ scopes->addTempScope(m_openScope);
5152 Q_EMIT openScope(m_openScope);
5253 }
5354}
tests/mocks/libusermetrics/UserMetrics.cpp+8
@@ -289,6 +289,7 @@ void UserMetricsPrivate::generateFakeData()
289289 new UserMetricsData("<b>52km</b> travelled", first, firstMonth,
290290 ninth, secondMonth, this));
291291 m_fakeData.insert("single", data);
292+ m_fakeData.insert("has-pin", data);
292293 }
293294
294295 {
@@ -304,12 +305,14 @@ void UserMetricsPrivate::generateFakeData()
304305 new UserMetricsData("<b>33</b> messages today", second,
305306 firstMonth, eighth, secondMonth, this));
306307 m_fakeData.insert("single", data);
308+ m_fakeData.insert("has-pin", data);
307309 }
308310
309311 {
310312 QVariantList firstMonth;
311313 while (firstMonth.size() < 17)
312314 firstMonth.push_back(QVariant(rand()));
315+ firstMonth[8] = QVariant(1.0); // oversized 9th day, to test clipping
313316 while (firstMonth.size() < 31)
314317 firstMonth.push_back(QVariant());
315318 QVariantList secondMonth;
@@ -319,6 +322,7 @@ void UserMetricsPrivate::generateFakeData()
319322 new UserMetricsData("<b>19</b> minutes talk time", eighth,
320323 firstMonth, second, secondMonth, this));
321324 m_fakeData.insert("single", data);
325+ m_fakeData.insert("has-pin", data);
322326 // Also use same data for some tablet users
323327 m_fakeData.insert("has-password", data);
324328 m_fakeData.insert("no-password", data);
@@ -386,6 +390,10 @@ void UserMetricsPrivate::nextFakeData()
386390 if (m_dataIndex == m_fakeData.constEnd() || m_dataIndex.key() != m_username)
387391 {
388392 m_dataIndex = m_fakeData.constFind(m_username);
393+ if (m_dataIndex == m_fakeData.constEnd())
394+ {
395+ m_dataIndex = m_fakeData.constFind("");
396+ }
389397 }
390398
391399 loadFakeData();
tests/plugins/CMakeLists.txt+1
@@ -1,4 +1,5 @@
11add_subdirectory(AccountsService)
2+add_subdirectory(Greeter)
23add_subdirectory(LightDM)
34add_subdirectory(Dash)
45add_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(
1111 ${LIBLIGHTDM_INCLUDE_DIRS}
1212 )
1313
14−add_dependencies(GreeterDBusTestExec MockLightDM-full)
15−target_link_libraries(GreeterDBusTestExec -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full
14+add_dependencies(GreeterDBusTestExec MockLightDM)
15+target_link_libraries(GreeterDBusTestExec -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm
1616 -llightdm-qt5-2)
1717
1818add_definitions(-DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
1919
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
1919
2020Item {
2121 property var greeter: LightDM.Greeter
22+ Binding {
23+ target: LightDM.Greeter
24+ property: "mockMode"
25+ value: "full"
26+ }
2227}
tests/plugins/Unity/Indicators/indicatorsmanagertest.cpp+30−5
@@ -50,7 +50,8 @@ private Q_SLOTS:
5050 QVERIFY(!manager.isLoaded());
5151 QCOMPARE(manager.indicators().count(), 0);
5252
53− manager.load("test1");
53+ manager.setProfile("test1");
54+ manager.load();
5455
5556 QVERIFY(manager.isLoaded());
5657 QCOMPARE(manager.indicators().count(), 4);
@@ -67,7 +68,8 @@ private Q_SLOTS:
6768 void testPluginInterfaceProfile1()
6869 {
6970 IndicatorsManager manager;
70− manager.load("test1");
71+ manager.setProfile("test1");
72+ manager.load();
7173
7274 Indicator::Ptr indicator = manager.indicator("indicator-fake1");
7375 QVERIFY(indicator ? true : false);
@@ -90,7 +92,8 @@ private Q_SLOTS:
9092 void testPluginInterfaceProfile2()
9193 {
9294 IndicatorsManager manager;
93− manager.load("test2");
95+ manager.setProfile("test2");
96+ manager.load();
9497
9598 Indicator::Ptr indicator = manager.indicator("indicator-fake1");
9699 QVERIFY(indicator ? true : false);
@@ -107,13 +110,34 @@ private Q_SLOTS:
107110 QCOMPARE(props["menuObjectPath"].toString(), QString("/com/canonical/indicator/fake1/test2"));
108111 }
109112
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+
110133 /*
111134 * Test if a new plugin object is create for each different plugin
112135 */
113136 void testPluginInstance()
114137 {
115138 IndicatorsManager manager;
116− manager.load("test2");
139+ manager.setProfile("test2");
140+ manager.load();
117141
118142 Indicator::Ptr i0 = manager.indicator("indicator-fake1");
119143 Indicator::Ptr i1 = manager.indicator("indicator-fake1");
@@ -133,7 +157,8 @@ private Q_SLOTS:
133157 void testPluginInitAndShutdown()
134158 {
135159 IndicatorsManager manager;
136− manager.load("test1");
160+ manager.setProfile("test1");
161+ manager.load();
137162
138163 QWeakPointer<Indicator> wp0;
139164 QWeakPointer<Indicator> wp1;
tests/plugins/Unity/Indicators/indicatorsmodeltest.cpp+6−3
@@ -47,10 +47,11 @@ private Q_SLOTS:
4747 void testLoad()
4848 {
4949 IndicatorsModel model;
50+ model.setProfile("test1");
5051
5152 QCOMPARE(model.property("count").toInt(), 0);
5253
53− model.load("test1");
54+ model.load();
5455
5556 QCOMPARE(model.property("count").toInt(), 4);
5657
@@ -67,7 +68,8 @@ private Q_SLOTS:
6768 {
6869 // Priority order. (2, 1, 4, 3)
6970 IndicatorsModel model;
70− model.load("test1");
71+ model.setProfile("test1");
72+ model.load();
7173
7274 // should be in order:
7375 // fake3, fake4, fake1, fake2
@@ -104,7 +106,8 @@ private Q_SLOTS:
104106 {
105107 // Priority order. (2, 1, 4, 3)
106108 IndicatorsModel model;
107− model.load("test2");
109+ model.setProfile("test2");
110+ model.load();
108111
109112 // should be in order:
110113 // fake3, fake4, fake1, fake2
tests/plugins/Unity/Indicators/sharedunitymenumodeltest.cpp+2−2
@@ -61,7 +61,7 @@ private Q_SLOTS:
6161 QCOMPARE(model1->model(), model2->model());
6262 }
6363
64− void testSharedOwnership()
64+ void testSavedData()
6565 {
6666 QSharedPointer<SharedUnityMenuModel> model1(createFullModel("test1"));
6767 QSharedPointer<SharedUnityMenuModel> model2(createFullModel("test1"));
@@ -70,7 +70,7 @@ private Q_SLOTS:
7070 model1.clear();
7171 QCOMPARE(UnityMenuModelCache::singleton()->contains("/com/canonical/test1"), true);
7272 model2.clear();
73− QCOMPARE(UnityMenuModelCache::singleton()->contains("/com/canonical/test1"), false);
73+ QCOMPARE(UnityMenuModelCache::singleton()->contains("/com/canonical/test1"), true);
7474 }
7575
7676 // 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 @@
11pkg_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)
33pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
44
55include_directories(
@@ -23,7 +23,9 @@ add_custom_target(launchermodeltest ${testModelCommand})
2323add_executable(launchermodeltestExec
2424 launchermodeltest.cpp
2525 gsettings.cpp
26+ AccountsServiceDBusAdaptor.cpp
2627 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launchermodel.cpp
28+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/asadapter.cpp
2729 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launcheritem.cpp
2830 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistmodel.cpp
2931 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/dbusinterface.cpp
tests/plugins/Unity/Launcher/launchermodeltest.cpp+65−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -25,6 +25,8 @@
2525#include "launchermodel.h"
2626#include "dbusinterface.h"
2727#include "gsettings.h"
28+#include "asadapter.h"
29+#include "AccountsServiceDBusAdaptor.h"
2830
2931#include <QtTest>
3032#include <QDBusInterface>
@@ -129,6 +131,11 @@ private:
129131 LauncherModel *launcherModel;
130132 MockAppManager *appManager;
131133
134+ QList<QVariantMap> getASConfig() {
135+ AccountsServiceDBusAdaptor *as = launcherModel->m_asAdapter->m_accounts;
136+ return as->getUserProperty("", "", "LauncherItems").value<QList<QVariantMap>>();
137+ }
138+
132139private Q_SLOTS:
133140
134141 void initTestCase() {
@@ -467,6 +474,63 @@ private Q_SLOTS:
467474 QCOMPARE(launcherModel->get(1)->appId(), QString("abs-icon"));
468475 QCOMPARE(spy.count(), 2);
469476 }
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+ }
470534};
471535
472536QTEST_GUILESS_MAIN(LauncherModelTest)
tests/qmltests/CMakeLists.txt+21−20
@@ -17,16 +17,15 @@ add_qml_test(Components PhysicalKeysMapper)
1717
1818set(qmltest_DEFAULT_TARGETS qmluitests)
1919set(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")
2121
22−add_qml_test(. Shell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single:${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
23−add_qml_test(. ShellWithPin ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single-pin:${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
24−add_qml_test(. TabletShell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full:${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
22+add_qml_test(. Shell)
23+add_qml_test(. ShellWithPin)
24+add_qml_test(. TabletShell)
2525add_qml_test(Components Background)
2626add_qml_test(Components Carousel)
2727add_qml_test(Components Dialogs)
2828add_qml_test(Components DraggingArea)
29−add_qml_test(Components EdgeDemoOverlay)
3029add_qml_test(Components LazyImage)
3130add_qml_test(Components Lockscreen)
3231add_qml_test(Components Rating)
@@ -62,10 +61,10 @@ add_qml_test(Dash/ScopeSettings ScopeSettingList)
6261add_qml_test(Dash/ScopeSettings ScopeSettingNumber)
6362add_qml_test(Dash/ScopeSettings ScopeSettingString)
6463add_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")
66−add_qml_test(Greeter SingleGreeter ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
67−add_qml_test(Greeter Infographics ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
68−add_qml_test(Greeter Clock ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
64+add_qml_test(Greeter MultiGreeter)
65+add_qml_test(Greeter SingleGreeter)
66+add_qml_test(Greeter Infographics)
67+add_qml_test(Greeter Clock)
6968add_qml_test(Launcher Launcher)
7069add_qml_test(Notifications Notifications)
7170add_qml_test(Notifications VisualSnapDecisionsQueue)
@@ -73,22 +72,24 @@ add_qml_test(Notifications OptionToggle)
7372add_qml_test(Notifications SwipeToAct)
7473add_qml_test(Panel ActiveCallHint)
7574add_qml_test(Panel IndicatorItem)
76−add_qml_test(Panel IndicatorItemRow ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
77−add_qml_test(Panel IndicatorPage ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
78−add_qml_test(Panel IndicatorsBar ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
79−add_qml_test(Panel IndicatorsMenu ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
80−add_qml_test(Panel MenuContent ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
81−add_qml_test(Panel Panel ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
75+add_qml_test(Panel IndicatorItemRow)
76+add_qml_test(Panel IndicatorPage)
77+add_qml_test(Panel IndicatorsBar)
78+add_qml_test(Panel IndicatorsMenu)
79+add_qml_test(Panel MenuContent)
80+add_qml_test(Panel Panel)
8281add_qml_test(Panel SearchIndicator)
8382add_qml_test(Panel/Indicators IndicatorsLight ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
8483# These MenuItemFactory tests need the test/mocks/ to come before plugins/
8584add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
8685add_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")
88−add_qml_test(Stages PhoneStage ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
89−add_qml_test(Stages SpreadDelegate ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
90−add_qml_test(Stages SurfaceContainer ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
91−add_qml_test(Stages SessionContainer ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
86+add_qml_test(Stages ApplicationWindow)
87+add_qml_test(Stages PhoneStage ENVIRONMENT)
88+add_qml_test(Stages SpreadDelegate ENVIRONMENT)
89+add_qml_test(Stages SurfaceContainer ENVIRONMENT)
90+add_qml_test(Stages SessionContainer ENVIRONMENT)
91+add_qml_test(Stages TabletStage)
9292add_qml_test(Stages WindowMoveResizeArea)
9393add_qml_test(Stages Splash)
94+add_qml_test(Tutorial Tutorial)
9495add_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 {
5454 name: "Preview"
5555 when: windowShown
5656
57+ function init() {
58+ var widget = findChild(preview, "previewListRow0");
59+ widget.positionViewAtBeginning();
60+ }
61+
5762 function test_triggered() {
5863 waitForRendering(preview);
5964 var widget = findChild(preview, "widget-3");
@@ -102,5 +107,31 @@ Rectangle {
102107 return bottomLeft.y <= preview.height
103108 }, true);
104109 }
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+ }
105136 }
106137}
tests/qmltests/Dash/tst_Card.qml+7−1
@@ -60,7 +60,7 @@ Rectangle {
6060 "layout": { "components": Helpers.update(JSON.parse(Helpers.fullMapping), { "art": { "aspect-ratio": 0.7 } }) }
6161 },
6262 {
63− "name": "Art, header, summary - horizontal",
63+ "name": "Art, header, summary, background - horizontal",
6464 "layout": { "template": { "card-layout": "horizontal", "card-background": { "type": "gradient", "elements": ["grey", "white"] } },
6565 "components": JSON.parse(Helpers.fullMapping) }
6666 },
@@ -101,6 +101,11 @@ Rectangle {
101101 "layout": { "template": { "overlay": true },
102102 "components": { "art": "art", "title": "title" } }
103103 },
104+ {
105+ "name": "Art, header, summary - horizontal",
106+ "layout": { "template": { "card-layout": "horizontal" },
107+ "components": JSON.parse(Helpers.fullMapping) }
108+ },
104109 ]
105110
106111 CardTool {
@@ -415,6 +420,7 @@ Rectangle {
415420 background: Qt.resolvedUrl("artwork/checkers.png"), index: 10 },
416421 { tag: "Gradient background", visible: true, color: "#808080", gradientColor: "#ffffff", index: 11 },
417422 { tag: "Image background", visible: true, image: Qt.resolvedUrl("artwork/checkers.png"), index: 12 },
423+ { tag: "Horizontal no background", visible: false, index: 14 },
418424 ];
419425 }
420426
tests/qmltests/Dash/tst_Dash.qml+69
@@ -428,5 +428,74 @@ Item {
428428 tryCompare(bottomEdgeController, "progress", 0);
429429 tryCompare(dashContentList, "currentIndex", 1)
430430 }
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+ }
431500 }
432501}
tests/qmltests/Dash/tst_GenericScopeView.qml+1−1
@@ -380,8 +380,8 @@ Item {
380380
381381 tryCompare(testCase.subPageLoader, "open", false);
382382
383+ tryCompareFunction(function() { return findChild(category, "delegate1") != null; }, true);
383384 var tile = findChild(category, "delegate1");
384− verify(tile, "Could not find delegate");
385385
386386 mouseClick(tile);
387387 tryCompare(testCase.subPageLoader, "open", true);
tests/qmltests/Greeter/tst_Infographics.qml+10
@@ -23,6 +23,16 @@ import Unity.Test 0.1 as UT
2323import LightDM 0.1 as LightDM
2424
2525Item {
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+ }
2636 property variant infographicModel: LightDM.Infographic
2737
2838 width: units.gu(120)
tests/qmltests/Greeter/tst_MultiGreeter.qml+91−2
@@ -15,6 +15,7 @@
1515 */
1616
1717import QtQuick 2.0
18+import QtQuick.Layouts 1.1
1819import QtTest 1.0
1920import ".."
2021import "../../../qml/Greeter"
@@ -23,12 +24,37 @@ import LightDM 0.1 as LightDM
2324import Unity.Test 0.1 as UT
2425
2526Item {
26− width: units.gu(120)
27+ id: root
28+ width: units.gu(140)
2729 height: units.gu(80)
2830
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+
2953 Greeter {
3054 id: greeter
31− anchors.fill: parent
55+ width: units.gu(120)
56+ height: root.height
57+ inputMethod: fakeInputMethod
3258 locked: !LightDM.Greeter.authenticated
3359 }
3460
@@ -43,6 +69,44 @@ Item {
4369 }
4470 }
4571
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+
46110 SignalSpy {
47111 id: unlockSpy
48112 target: greeter
@@ -65,6 +129,10 @@ Item {
65129 name: "MultiGreeter"
66130 when: windowShown
67131
132+ function cleanup() {
133+ keyboardVisibleCheckbox.checked = false;
134+ }
135+
68136 function select_index(i) {
69137 // We could be anywhere in list; find target index to know which direction
70138 var userlist = findChild(greeter, "userList")
@@ -382,5 +450,26 @@ Item {
382450 tryCompare(spy, "count", 1)
383451 greeterObj.destroy()
384452 }
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+ }
385474 }
386475}
tests/qmltests/Launcher/tst_Launcher.qml+46−19
@@ -15,6 +15,7 @@
1515 */
1616
1717import QtQuick 2.0
18+import QtQuick.Layouts 1.1
1819import QtTest 1.0
1920import Unity.Test 0.1 as UT
2021import Ubuntu.Components 1.1
@@ -74,10 +75,22 @@ Item {
7475 }
7576 }
7677
77− Button {
78+ ColumnLayout {
7879 anchors { bottom: parent.bottom; right: parent.right; margins: units.gu(1) }
79− text: "emit hinting signal"
80− onClicked: LauncherModel.emitHint()
80+ spacing: units.gu(1)
81+ width: units.gu(20)
82+
83+ Button {
84+ text: "emit hinting signal"
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+ }
8194 }
8295
8396 SignalSpy {
@@ -140,6 +153,18 @@ Item {
140153 tryCompare(panel, "x", -panel.width, 1000);
141154 }
142155
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+
143168 // Drag from the left edge of the screen rightwards and check that the launcher
144169 // appears (as if being dragged by the finger/pointer)
145170 function test_dragLeftEdgeToRevealLauncherAndTapCenterToDismiss() {
@@ -166,22 +191,22 @@ Item {
166191 launcherApplicationSelected("[...]dialer-app.desktop") */
167192 function test_clickingOnAppIconCausesSignalEmission() {
168193 dragLauncherIntoView();
169− launcher.lastSelectedApplication = ""
194+ launcher.lastSelectedApplication = "";
195+ launcher.inverted = false;
170196
171− var listView = findChild(launcher, "launcherListView");
172− listView.positionViewAtEnd();
197+ positionLauncherListAtBeginning();
173198
174− var appIcon = findChild(launcher, "launcherDelegate0")
199+ var appIcon = findChild(launcher, "launcherDelegate0");
175200
176− verify(appIcon != undefined)
201+ verify(appIcon != undefined);
177202
178− mouseClick(appIcon)
203+ mouseClick(appIcon);
179204
180205 tryCompare(launcher, "lastSelectedApplication",
181− "dialer-app")
206+ appIcon.appId);
182207
183208 // Tapping on an application icon also dismisses the launcher
184− waitUntilLauncherDisappears()
209+ waitUntilLauncherDisappears();
185210 }
186211
187212 /* If I click on the dash icon on the launcher
@@ -277,25 +302,26 @@ Item {
277302 function test_clickFlick_data() {
278303 var listView = findChild(launcher, "launcherListView");
279304 return [
280− {tag: "unfolded top", positionViewAtBeginning: false,
305+ {tag: "unfolded top", positionViewAtBeginning: true,
281306 clickY: listView.topMargin + units.gu(2),
282307 expectFlick: false},
283308
284− {tag: "folded top", positionViewAtBeginning: true,
309+ {tag: "folded top", positionViewAtBeginning: false,
285310 clickY: listView.topMargin + units.gu(2),
286311 expectFlick: true},
287312
288− {tag: "unfolded bottom", positionViewAtBeginning: true,
313+ {tag: "unfolded bottom", positionViewAtBeginning: false,
289314 clickY: listView.height - listView.topMargin - units.gu(1),
290315 expectFlick: false},
291316
292− {tag: "folded bottom", positionViewAtBeginning: false,
317+ {tag: "folded bottom", positionViewAtBeginning: true,
293318 clickY: listView.height - listView.topMargin - units.gu(1),
294319 expectFlick: true},
295320 ];
296321 }
297322
298323 function test_clickFlick(data) {
324+ launcher.inverted = false;
299325 launcher.lastSelectedApplication = "";
300326 dragLauncherIntoView();
301327 var listView = findChild(launcher, "launcherListView");
@@ -305,9 +331,9 @@ Item {
305331 // So for stability's sake we just put the listView in the position
306332 // we want to to actually start doing what this tests intends to check.
307333 if (data.positionViewAtBeginning) {
308− listView.positionViewAtBeginning();
334+ positionLauncherListAtBeginning();
309335 } else {
310− listView.positionViewAtEnd();
336+ positionLauncherListAtEnd();
311337 }
312338 tryCompare(listView, "flicking", false);
313339
@@ -442,9 +468,9 @@ Item {
442468 // Position launcher to where we need it
443469 var listView = findChild(launcher, "launcherListView");
444470 if (data.flickTo == "top") {
445− listView.positionViewAtEnd();
471+ positionLauncherListAtBeginning();
446472 } else {
447− listView.positionViewAtBeginning();
473+ positionLauncherListAtEnd();
448474 }
449475
450476 // Doing longpress
@@ -454,6 +480,7 @@ Item {
454480
455481 verify(quickList.y >= units.gu(1));
456482 verify(quickList.y + quickList.height + units.gu(1) <= launcher.height);
483+ compare(quickList.width, units.gu(30));
457484
458485 // Click somewhere in the empty space to dismiss the quicklist
459486 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 @@
11/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2015 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -17,10 +17,15 @@
1717 * Mirco Mueller <mirco.mueller@canonical.com>
1818 */
1919
20−#include "MockNotificationTypes.h"
20+import Unity.Notifications 1.0
2121
22−MockNotification::MockNotification(QObject *parent) : QObject(parent) {
23−}
24−
25−MockNotification::~MockNotification() {
22+Notification {
23+ nid: 0
24+ type: Notification.PlaceHolder
25+ summary: ""
26+ body: ""
27+ icon: ""
28+ secondaryIcon: ""
29+ value: 0
30+ rawActions: []
2631}
tests/qmltests/Notifications/tst_Notifications.qml+279−213
@@ -1,17 +1,20 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright 2015 Canonical Ltd.
33 *
44 * 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
66 * the Free Software Foundation; version 3.
77 *
88 * This program is distributed in the hope that it will be useful,
99 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1010 * 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.
1212 *
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
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Mirco Mueller <mirco.mueller@canonical.com>
1518 */
1619
1720import QtQuick 2.0
@@ -24,146 +27,134 @@ import Unity.Notifications 1.0
2427import QtMultimedia 5.0
2528
2629Item {
30+ id: foobar
31+
2732 width: notificationsRect.width + interactiveControls.width
2833 height: notificationsRect.height
34+ property int index: 0
2935
3036 Row {
3137 id: rootRow
3238
33− Component {
34− id: mockNotification
35−
36− QtObject {
37− function invokeAction(actionId) {
38− mockModel.actionInvoked(actionId)
39− }
40− }
41− }
42−
43− ListModel {
39+ NotificationModel {
4440 id: mockModel
45− dynamicRoles: true
46−
47− signal actionInvoked(string actionId)
48−
49− function getRaw(id) {
50− return mockNotification.createObject(mockModel)
51− }
5241
5342 // add the default/PlaceHolder notification to the model
5443 Component.onCompleted: {
55− var n = {
56− type: Notification.PlaceHolder,
57− hints: {},
58− summary: "",
59− body: "",
60− icon: "",
61− secondaryIcon: "",
62− actions: []
63− }
64−
44+ var component = Qt.createComponent("Notification.qml")
45+ var n = component.createObject("notification", {"nid": index++,
46+ "type": Notification.PlaceHolder,
47+ "hints": {},
48+ "summary": "",
49+ "body": "",
50+ "icon": "",
51+ "secondaryIcon": "",
52+ "rawActions": []})
53+ n.completed.connect(mockModel.onCompleted)
6554 append(n)
6655 }
6756 }
6857
6958 function add2over1SnapDecisionNotification() {
70− var n = {
71− type: Notification.SnapDecision,
72− hints: {"x-canonical-private-affirmative-tint": "true"},
73− summary: "Theatre at Ferria Stadium",
74− body: "at Ferria Stadium in Bilbao, Spain\n07578545317",
75− icon: "",
76− secondaryIcon: "",
77− actions: [{ id: "ok_id", label: "Ok"},
78− { id: "snooze_id", label: "Snooze"},
79− { id: "view_id", label: "View"}]
80− }
81−
59+ var component = Qt.createComponent("Notification.qml")
60+ var n = component.createObject("notification", {"nid": index++,
61+ "type": Notification.SnapDecision,
62+ "hints": {"x-canonical-private-affirmative-tint": "true"},
63+ "summary": "Theatre at Ferria Stadium",
64+ "body": "at Ferria Stadium in Bilbao, Spain\n07578545317",
65+ "icon": "",
66+ "secondaryIcon": "",
67+ "rawActions": ["ok_id", "Ok",
68+ "snooze_id", "Snooze",
69+ "view_id", "View"]})
70+ n.completed.connect(mockModel.onCompleted)
8271 mockModel.append(n)
8372 }
8473
8574 function addEphemeralNotification() {
86− var n = {
87− type: Notification.Ephemeral,
88− summary: "Cole Raby",
89− body: "I did not expect it to be that late.",
90− icon: "../graphics/avatars/amanda.png",
91− secondaryIcon: "../graphics/applicationIcons/facebook.png",
92− actions: []
93− }
94−
75+ var component = Qt.createComponent("Notification.qml")
76+ var n = component.createObject("notification", {"nid": index++,
77+ "type": Notification.Ephemeral,
78+ "hints": {},
79+ "summary": "Cole Raby",
80+ "body": "I did not expect it to be that late.",
81+ "icon": "../graphics/avatars/amanda.png",
82+ "secondaryIcon": "../graphics/applicationIcons/facebook.png",
83+ "rawActions": ["reply_id", "Dummy"]})
84+ n.completed.connect(mockModel.onCompleted)
9585 mockModel.append(n)
9686 }
9787
9888 function addEphemeralNonShapedIconNotification() {
99− var n = {
100− type: Notification.Ephemeral,
101− hints: {"x-canonical-non-shaped-icon": "true"},
102− summary: "Contacts",
103− body: "Synchronised contacts-database with cloud-storage.",
104− icon: "../graphics/applicationIcons/contacts-app.png",
105− secondaryIcon: "",
106− actions: []
107− }
108−
89+ var component = Qt.createComponent("Notification.qml")
90+ var n = component.createObject("notification", {"nid": index++,
91+ "type": Notification.Ephemeral,
92+ "hints": {"x-canonical-non-shaped-icon": "true"},
93+ "summary": "Contacts",
94+ "body": "Synchronised contacts-database with cloud-storage.",
95+ "icon": "../graphics/applicationIcons/contacts-app.png",
96+ "secondaryIcon": "",
97+ "rawActions": ["reply_id", "Dummy"]})
98+ n.completed.connect(mockModel.onCompleted)
10999 mockModel.append(n)
110100 }
111101
112102 function addEphemeralIconSummaryNotification() {
113− var n = {
114− type: Notification.Ephemeral,
115− hints: {"x-canonical-non-shaped-icon": "false"},
116− summary: "Photo upload completed",
117− body: "",
118− icon: "../graphics/applicationIcons/facebook.png",
119− secondaryIcon: "",
120− actions: []
121− }
122−
103+ var component = Qt.createComponent("Notification.qml")
104+ var n = component.createObject("notification", {"nid": index++,
105+ "type": Notification.Ephemeral,
106+ "hints": {"x-canonical-non-shaped-icon": "false"},
107+ "summary": "Photo upload completed",
108+ "body": "",
109+ "icon": "../graphics/applicationIcons/facebook.png",
110+ "secondaryIcon": "",
111+ "rawActions": ["reply_id", "Dummy"]})
112+ n.completed.connect(mockModel.onCompleted)
123113 mockModel.append(n)
124114 }
125115
126116 function addInteractiveNotification() {
127− var n = {
128− type: Notification.Interactive,
129− summary: "Interactive notification",
130− body: "This is a notification that can be clicked",
131− icon: "../graphics/avatars/anna_olsson.png",
132− secondaryIcon: "",
133− actions: [{ id: "reply_id", label: "Dummy"}],
134− }
135−
117+ var component = Qt.createComponent("Notification.qml")
118+ var n = component.createObject("notification", {"nid": index++,
119+ "type": Notification.Interactive,
120+ "hints": {},
121+ "summary": "Interactive notification",
122+ "body": "This is a notification that can be clicked",
123+ "icon": "../graphics/avatars/anna_olsson.png",
124+ "secondaryIcon": "",
125+ "rawActions": ["reply_id", "Dummy"]})
126+ n.completed.connect(mockModel.onCompleted)
136127 mockModel.append(n)
137128 }
138129
139130 function addConfirmationNotification() {
140− var n = {
141− type: Notification.Confirmation,
142− hints: {"x-canonical-non-shaped-icon": "true"},
143− summary: "Confirmation notification",
144− body: "",
145− icon: "image://theme/audio-volume-medium",
146− secondaryIcon: "",
147− value: 50,
148− actions: [],
149− }
150−
131+ var component = Qt.createComponent("Notification.qml")
132+ var n = component.createObject("notification", {"nid": index++,
133+ "type": Notification.Confirmation,
134+ "hints": {"x-canonical-non-shaped-icon": "true"},
135+ "summary": "Confirmation notification",
136+ "body": "",
137+ "icon": "image://theme/audio-volume-medium",
138+ "secondaryIcon": "",
139+ "value": 50,
140+ "rawActions": ["reply_id", "Dummy"]})
141+ n.completed.connect(mockModel.onCompleted)
151142 mockModel.append(n)
152143 }
153144
154145 function add2ndConfirmationNotification() {
155− var n = {
156− type: Notification.Confirmation,
157− hints: {"x-canonical-non-shaped-icon": "true",
158− "x-canonical-value-bar-tint": "true"},
159− summary: "Confirmation notification",
160− body: "High Volume",
161− icon: "image://theme/audio-volume-high",
162− secondaryIcon: "",
163− value: 85,
164− actions: [],
165− }
166−
146+ var component = Qt.createComponent("Notification.qml")
147+ var n = component.createObject("notification", {"nid": index++,
148+ "type": Notification.Confirmation,
149+ "hints": {"x-canonical-non-shaped-icon": "true",
150+ "x-canonical-value-bar-tint": "true"},
151+ "summary": "Confirmation notification",
152+ "body": "High Volume",
153+ "icon": "image://theme/audio-volume-high",
154+ "secondaryIcon": "",
155+ "value": 85,
156+ "rawActions": ["reply_id", "Dummy"]})
157+ n.completed.connect(mockModel.onCompleted)
167158 mockModel.append(n)
168159 }
169160
@@ -174,8 +165,9 @@ Item {
174165 }
175166
176167 function remove1stNotification() {
177− if (mockModel.count > 1)
178− mockModel.remove(1)
168+ if (mockModel.count > 1) {
169+ mockModel.removeSecond()
170+ }
179171 }
180172
181173 Rectangle {
@@ -273,41 +265,122 @@ Item {
273265 name: "NotificationRendererTest"
274266 when: windowShown
275267
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+
276379 function test_NotificationRenderer_data() {
277380 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− },
302381 {
303382 tag: "Ephemeral notification - icon-summary layout",
304− type: Notification.Ephemeral,
305− hints: {},
306− summary: "Photo upload completed",
307− body: "",
308− icon: "../graphics/applicationIcons/facebook.png",
309− secondaryIcon: "",
310− actions: [],
383+ n: nlist[0],
311384 summaryVisible: true,
312385 bodyVisible: false,
313386 iconVisible: true,
@@ -323,15 +396,7 @@ Item {
323396 },
324397 {
325398 tag: "Ephemeral notification - check suppression of secondary icon for icon-summary layout",
326− type: Notification.Ephemeral,
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: [],
399+ n: nlist[1],
335400 summaryVisible: true,
336401 bodyVisible: false,
337402 interactiveAreaEnabled: false,
@@ -348,14 +413,7 @@ Item {
348413 },
349414 {
350415 tag: "Interactive notification",
351− type: Notification.Interactive,
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"}],
416+ n: nlist[2],
359417 summaryVisible: true,
360418 bodyVisible: true,
361419 iconVisible: true,
@@ -371,15 +429,7 @@ Item {
371429 },
372430 {
373431 tag: "Snap Decision without secondary icon and no button-tint",
374− type: Notification.SnapDecision,
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"}],
432+ n: nlist[3],
383433 summaryVisible: true,
384434 bodyVisible: true,
385435 iconVisible: true,
@@ -395,14 +445,7 @@ Item {
395445 },
396446 {
397447 tag: "Ephemeral notification",
398− type: Notification.Ephemeral,
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: [],
448+ n: nlist[4],
406449 summaryVisible: true,
407450 bodyVisible: true,
408451 iconVisible: true,
@@ -418,14 +461,7 @@ Item {
418461 },
419462 {
420463 tag: "Ephemeral notification with non-shaped icon",
421− type: Notification.Ephemeral,
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: [],
464+ n: nlist[5],
429465 summaryVisible: true,
430466 bodyVisible: true,
431467 iconVisible: true,
@@ -441,14 +477,7 @@ Item {
441477 },
442478 {
443479 tag: "Confirmation notification with value",
444− type: Notification.Confirmation,
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: [],
480+ n: nlist[6],
452481 summaryVisible: false,
453482 bodyVisible: false,
454483 iconVisible: false,
@@ -464,15 +493,7 @@ Item {
464493 },
465494 {
466495 tag: "Confirmation notification with value, label and tint",
467− type: Notification.Confirmation,
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: [],
496+ n: nlist[7],
476497 summaryVisible: false,
477498 bodyVisible: false,
478499 iconVisible: false,
@@ -485,6 +506,22 @@ Item {
485506 valueVisible: true,
486507 valueLabelVisible: true,
487508 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
488525 }
489526 ]
490527 }
@@ -509,8 +546,14 @@ Item {
509546 }
510547
511548 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+
512555 // populate model with some mock notifications
513− mockModel.append(data)
556+ mockModel.append(data.n)
514557
515558 // make sure the view is properly updated before going on
516559 notifications.forceLayout();
@@ -520,6 +563,7 @@ Item {
520563 verify(notification !== undefined, "notification wasn't found");
521564
522565 waitForRendering(notification);
566+ tryCompare(notification, "height", notification.state === "contracted" ? units.gu(10) : notification.implicitHeight);
523567
524568 var icon = findChild(notification, "icon")
525569 var centeredIcon = findChild(notification, "centeredIcon")
@@ -548,9 +592,9 @@ Item {
548592
549593 // test input does not fall through
550594 mouseClick(notification)
551− if(data.type == Notification.Interactive) {
595+ if(data.n.type === Notification.Interactive) {
552596 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")
554598 }
555599 compare(clickThroughSpy.count, 0, "click on notification fell through")
556600
@@ -559,17 +603,26 @@ Item {
559603 compare(bodyLabel.visible, data.bodyVisible, "body-text visibility is incorrect")
560604 compare(buttonRow.visible, data.buttonRowVisible, "button visibility is incorrect")
561605
562− var audioItem = findInvisibleChild(notification, "sound")
563− compare(audioItem.playbackState, data.hasSound ? Audio.PlayingState : Audio.StoppedState, "Audio has wrong state")
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) {
614+ var audioItem = findInvisibleChild(notification, "sound")
615+ compare(audioItem.playbackState, data.hasSound ? Audio.PlayingState : Audio.StoppedState, "Audio has wrong state")
616+ }
564617
565618 if(data.buttonRowVisible) {
566619 var buttonCancel = findChild(buttonRow, "notify_button1")
567620 var buttonAccept = findChild(buttonRow, "notify_button0")
568621
569622 // 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) {
571624 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")
573626 actionSpy.clear()
574627 }
575628
@@ -578,36 +631,49 @@ Item {
578631
579632 // click the positive/right button
580633 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")
582635 actionSpy.clear()
583− waitForRendering (notification)
584636
585637 // 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) {
587639 var comboButton = findChild(notification, "notify_button2")
588− tryCompareFunction(function() { return comboButton.expanded == false; }, true);
640+ tryCompareFunction(function() { return comboButton.expanded === false; }, true);
589641
590642 // 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);
592644
593645 // try clicking on choices in expanded comboList
594646 var choiceButton1 = findChild(notification, "notify_button3")
595647 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")
597649 actionSpy.clear()
598650
599651 var choiceButton2 = findChild(notification, "notify_button4")
600652 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")
602654 actionSpy.clear()
603655
604656 // 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);
606658 } else {
607659 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")
609661 }
610662 }
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+ }
611677 }
612678 }
613679 }
tests/qmltests/Notifications/tst_OptionToggle.qml+11−6
@@ -1,17 +1,20 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright 2015 Canonical Ltd.
33 *
44 * 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
66 * the Free Software Foundation; version 3.
77 *
88 * This program is distributed in the hope that it will be useful,
99 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1010 * 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.
1212 *
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
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Mirco Mueller <mirco.mueller@canonical.com>
1518 */
1619
1720import QtQuick 2.0
@@ -235,8 +238,10 @@ Row {
235238 compare(bodyLabel.visible, data.bodyVisible, "body-text visibility is incorrect")
236239 compare(buttonRow.visible, data.buttonRowVisible, "button visibility is incorrect")
237240
238− var audioItem = findInvisibleChild(notification, "sound")
239− compare(audioItem.playbackState, data.hasSound ? Audio.PlayingState : Audio.StoppedState, "Audio has wrong state")
241+ if (data.hasSound) {
242+ var audioItem = findInvisibleChild(notification, "sound")
243+ compare(audioItem.playbackState, data.hasSound ? Audio.PlayingState : Audio.StoppedState, "Audio has wrong state")
244+ }
240245
241246 if(data.buttonRowVisible) {
242247 var buttonCancel = findChild(buttonRow, "notify_button1")
tests/qmltests/Notifications/tst_SwipeToAct.qml+4
@@ -218,6 +218,10 @@ Item {
218218 // populate model with some mock notifications
219219 mockModel.append(data)
220220
221+ // add actions to action-model to test against
222+ myActionModel.append("ok_id", "Ok")
223+ myActionModel.append("cancel_id", "Cancel")
224+
221225 // make sure the view is properly updated before going on
222226 notifications.forceLayout();
223227 waitForRendering(notifications);
tests/qmltests/Panel/tst_Panel.qml+33−2
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013-2014 Canonical Ltd.
2+ * Copyright 2013-2015 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -41,7 +41,12 @@ IndicatorTest {
4141 Layout.fillHeight: true
4242
4343 id: itemArea
44− color: "blue"
44+ color: backgroundMouseArea.pressed ? "red" : "blue"
45+
46+ MouseArea {
47+ id: backgroundMouseArea
48+ anchors.fill: parent
49+ }
4550
4651 Panel {
4752 id: panel
@@ -128,6 +133,12 @@ IndicatorTest {
128133 name: "Panel"
129134 when: windowShown
130135
136+ SignalSpy {
137+ id: backgroundPressedSpy
138+ target: backgroundMouseArea
139+ signalName: "pressedChanged"
140+ }
141+
131142 function init() {
132143 panel.fullscreenMode = false;
133144 callManager.foregroundCall = null;
@@ -140,6 +151,9 @@ IndicatorTest {
140151 // (switches between normal and fullscreen modes are animated)
141152 var indicatorArea = findChild(panel, "indicatorArea");
142153 tryCompare(indicatorArea, "y", 0);
154+
155+ backgroundPressedSpy.clear();
156+ compare(backgroundPressedSpy.valid, true);
143157 }
144158
145159 function get_indicator_item(index) {
@@ -299,5 +313,22 @@ IndicatorTest {
299313
300314 touchRelease(panel, mappedPosition.x, panel.minimizedPanelHeight / 2);
301315 }
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+ }
302333 }
303334}
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 {
8989 CheckBox {
9090 id: _surfaceCheckbox;
9191 checked: false;
92+ activeFocusOnPress: false
9293 enabled: root.session
9394 onCheckedChanged: {
9495 if (checked) {
@@ -117,6 +118,7 @@ ColumnLayout {
117118
118119 Button {
119120 enabled: root.session
121+ activeFocusOnPress: false
120122 text: removable ? "Remove" : "Release"
121123 onClicked: {
122124 if (removable) {
@@ -131,6 +133,7 @@ ColumnLayout {
131133
132134 Button {
133135 enabled: root.session !== null
136+ activeFocusOnPress: false
134137 text: "Add Child"
135138 onClicked: {
136139 var screenshot = Math.round(Math.random()*100 % (screenshotIds.length-1));
tests/qmltests/Stages/tst_ApplicationWindow.qml+6−1
@@ -52,10 +52,12 @@ Rectangle {
5252 application: fakeApplication
5353 orientation: Qt.PortraitOrientation
5454 interactive: true
55+ focus: true
5556 }
5657 }
5758 Loader {
5859 id: applicationWindowLoader
60+ focus: true
5961 anchors {
6062 top: parent.top
6163 bottom: parent.bottom
@@ -83,6 +85,7 @@ Rectangle {
8385
8486 CheckBox {
8587 id: sessionCheckbox; checked: false
88+ activeFocusOnPress: false
8689 onCheckedChanged: {
8790 if (applicationWindowLoader.status !== Loader.Ready)
8891 return;
@@ -121,6 +124,7 @@ Rectangle {
121124
122125 ListItem.ItemSelector {
123126 id: appStateSelector
127+ activeFocusOnPress: false
124128 anchors { left: parent.left; right: parent.right }
125129 text: "Application state"
126130 model: ["Starting",
@@ -146,6 +150,7 @@ Rectangle {
146150
147151 Button {
148152 anchors { left: parent.left; right: parent.right }
153+ activeFocusOnPress: false
149154 text: "Rotate device \u27F3"
150155 onClicked: {
151156 var orientation = applicationWindowLoader.item.orientation
@@ -398,7 +403,7 @@ Rectangle {
398403 verify(stateGroup.state === "void");
399404 }
400405
401− function test_forceActiveFocusFollowsInterative() {
406+ function test_surfaceActiveFocusFollowsAppWindowInterative() {
402407 fakeApplication.createSession();
403408 applicationWindowLoader.item.interactive = false;
404409 applicationWindowLoader.item.interactive = true;
tests/qmltests/Stages/tst_PhoneStage.qml+4
@@ -29,6 +29,7 @@ Item {
2929 PhoneStage {
3030 id: phoneStage
3131 anchors { fill: parent; rightMargin: units.gu(30) }
32+ focus: true
3233 dragAreaWidth: units.gu(2)
3334 maximizedAppTopMargin: units.gu(3) + units.dp(2)
3435 interactive: true
@@ -51,6 +52,7 @@ Item {
5152 Button {
5253 anchors { left: parent.left; right: parent.right }
5354 text: "Add App"
55+ activeFocusOnPress: false
5456 onClicked: {
5557 testCase.addApps();
5658 }
@@ -58,6 +60,7 @@ Item {
5860 Button {
5961 anchors { left: parent.left; right: parent.right }
6062 text: "Remove Selected"
63+ activeFocusOnPress: false
6164 onClicked: {
6265 ApplicationManager.stopApplication(ApplicationManager.get(appList.selectedAppIndex).appId);
6366 }
@@ -65,6 +68,7 @@ Item {
6568 Button {
6669 anchors { left: parent.left; right: parent.right }
6770 text: "Stop Selected"
71+ activeFocusOnPress: false
6872 onClicked: {
6973 ApplicationManager.get(appList.selectedAppIndex).setState(ApplicationInfoInterface.Stopped);
7074 }
tests/qmltests/Stages/tst_SessionContainer.qml+14−7
@@ -42,11 +42,14 @@ Rectangle {
4242 id: sessionContainer
4343 anchors.fill: parent
4444 orientation: Qt.PortraitOrientation
45+ focus: true
46+ interactive: true
4547 }
4648 }
4749
4850 Loader {
4951 id: sessionContainerLoader
52+ focus: true
5053 anchors {
5154 top: parent.top
5255 bottom: parent.bottom
@@ -75,6 +78,7 @@ Rectangle {
7578 CheckBox {
7679 id: sessionCheckbox;
7780 checked: false;
81+ activeFocusOnPress: false
7882 onCheckedChanged: {
7983 if (sessionContainerLoader.status !== Loader.Ready)
8084 return;
@@ -190,8 +194,6 @@ Rectangle {
190194 var sessionContainer = sessionContainerLoader.item;
191195 compare(sessionContainer.childSessions.count(), 0);
192196
193− sessionContainer.interactive = true;
194−
195197 var i;
196198 var sessions = [];
197199 // 3 sessions should cover all edge cases
@@ -209,14 +211,19 @@ Rectangle {
209211
210212 var a_session;
211213 while(a_session = sessions.pop()) {
212− a_session.surface.forceActiveFocus();
213214 compare(a_session.surface.activeFocus, true);
214215
215− var parentSession = a_session.parentSession;
216− sessionContainer.session.removeChildSession(a_session);
217− compare(a_session.surface.activeFocus, false);
216+ ApplicationTest.removeSurface(a_session.surface);
217+ ApplicationTest.removeSession(a_session);
218218
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+ }
220227 }
221228 }
222229
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 @@
11/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2014-2015 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -29,20 +29,46 @@ Item {
2929 height: units.gu(60)
3030 width: units.gu(60)
3131
32− Rectangle {
33− id: fakeWindow
34− height: units.gu(20)
35− width: units.gu(20)
36− color: "khaki"
37− WindowMoveResizeArea {
38− id: moveResizeArea
39− target: fakeWindow
40− resizeHandleWidth: units.gu(0.5)
41− minWidth: units.gu(15)
42− minHeight: units.gu(10)
32+ property var fakeWindow: windowLoader.item
33+
34+ Component {
35+ id: fakeWindowComponent
36+
37+ Item {
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)
43+ height: units.gu(20)
44+ width: units.gu(20)
45+
46+ WindowMoveResizeArea {
47+ id: moveResizeArea
48+ target: fakeWindow
49+ resizeHandleWidth: units.gu(0.5)
50+ minWidth: units.gu(15)
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+ }
4364 }
4465 }
4566
67+ Loader {
68+ id: windowLoader
69+ sourceComponent: fakeWindowComponent
70+ }
71+
4672 UT.UnityTestCase {
4773 when: windowShown
4874
@@ -100,8 +126,8 @@ Item {
100126 var startDragY = initialWindowY + initialWindowHeight + 1
101127 mouseFlick(root, startDragX, startDragY, startDragX + data.dx, startDragY + data.dy, true, true, units.gu(.5), 10);
102128
103− tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + data.dx, moveResizeArea.minWidth));
104− tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + data.dy, moveResizeArea.minHeight));
129+ tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + data.dx, fakeWindow.minWidth));
130+ tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + data.dy, fakeWindow.minHeight));
105131
106132 compare(fakeWindow.x, initialWindowX);
107133 compare(fakeWindow.y, initialWindowY);
@@ -126,13 +152,86 @@ Item {
126152 var startDragY = initialWindowY - 1
127153 mouseFlick(root, startDragX, startDragY, startDragX + data.dx, startDragY + data.dy, true, true, units.gu(.5), 10);
128154
129− tryCompare(fakeWindow, "width", Math.max(initialWindowWidth - data.dx, moveResizeArea.minWidth));
130− tryCompare(fakeWindow, "height", Math.max(initialWindowHeight - data.dy, moveResizeArea.minHeight));
155+ tryCompare(fakeWindow, "width", Math.max(initialWindowWidth - data.dx, fakeWindow.minWidth));
156+ tryCompare(fakeWindow, "height", Math.max(initialWindowHeight - data.dy, fakeWindow.minHeight));
131157
132− var maxMoveX = initialWindowWidth - moveResizeArea.minWidth;
133− var maxMoveY = initialWindowHeight - moveResizeArea.minHeight;
158+ var maxMoveX = initialWindowWidth - fakeWindow.minWidth;
159+ var maxMoveY = initialWindowHeight - fakeWindow.minHeight;
134160 compare(fakeWindow.x, Math.min(initialWindowX + data.dx, initialWindowX + maxMoveX));
135161 compare(fakeWindow.y, Math.min(initialWindowY + data.dy, initialWindowY + maxMoveY));
136162 }
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+ }
137236 }
138237}
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
2525import Ubuntu.Telephony 0.1 as Telephony
2626import Unity.Application 0.1
2727import Unity.Connectivity 0.1
28+import Unity.Indicators 0.1
2829import Unity.Notifications 1.0
2930import Unity.Test 0.1 as UT
3031import Powerd 0.1
@@ -36,6 +37,13 @@ Item {
3637 width: units.gu(60)
3738 height: units.gu(71)
3839
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+
3947 QtObject {
4048 id: applicationArguments
4149
@@ -56,7 +64,9 @@ Item {
5664 anchors.fill: parent
5765 Loader {
5866 id: shellLoader
67+ focus: true
5968
69+ active: false
6070 property bool itemDestroyed: false
6171 sourceComponent: Component {
6272 Shell {
@@ -81,6 +91,7 @@ Item {
8191 anchors { left: parent.left; right: parent.right }
8292 Button {
8393 text: "Show Greeter"
94+ activeFocusOnPress: false
8495 onClicked: {
8596 if (shellLoader.status !== Loader.Ready)
8697 return;
@@ -162,6 +173,7 @@ Item {
162173
163174 function init() {
164175 tryCompare(shell, "enabled", true); // enabled by greeter when ready
176+ shell.indicatorProfile = "phone";
165177
166178 swipeAwayGreeter();
167179
@@ -427,11 +439,11 @@ Item {
427439 waitUntilTransitionsEnd(appWindowStateGroup);
428440 }
429441
430− function test_surfaceLosesFocusWhilePanelIsOpen() {
442+ function test_surfaceLosesActiveFocusWhilePanelIsOpen() {
431443 var app = ApplicationManager.startApplication("dialer-app");
432444 waitUntilAppWindowIsFullyLoaded(app);
433445
434− tryCompare(app.session.surface, "focus", true);
446+ tryCompare(app.session.surface, "activeFocus", true);
435447
436448 // Drag the indicators panel half-open
437449 var touchX = shell.width / 2;
@@ -442,7 +454,7 @@ Item {
442454 true /* beginTouch */, false /* endTouch */);
443455 verify(indicators.partiallyOpened);
444456
445− tryCompare(app.session.surface, "focus", false);
457+ tryCompare(app.session.surface, "activeFocus", false);
446458
447459 // And finish getting it open
448460 touchFlick(indicators,
@@ -451,11 +463,21 @@ Item {
451463 false /* beginTouch */, true /* endTouch */);
452464 tryCompare(indicators, "fullyOpened", true);
453465
454− tryCompare(app.session.surface, "focus", false);
466+ tryCompare(app.session.surface, "activeFocus", false);
455467
456468 dragToCloseIndicatorsPanel();
457469
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);
459481 }
460482
461483 // Wait for the whole UI to settle down
@@ -470,6 +492,14 @@ Item {
470492 waitForRendering(shell)
471493 }
472494
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+
473503 function dragToCloseIndicatorsPanel() {
474504 var indicators = findChild(shell, "indicators");
475505
@@ -539,6 +569,33 @@ Item {
539569 && itemRectInShell.y + itemRectInShell.height <= shell.height;
540570 }
541571
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+
542599 function test_focusRequestedHidesGreeter() {
543600 var greeter = findChild(shell, "greeter");
544601
tests/qmltests/tst_ShellWithPin.qml+72
@@ -33,6 +33,13 @@ Item {
3333 width: contentRow.width
3434 height: contentRow.height
3535
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+
3643 QtObject {
3744 id: applicationArguments
3845
@@ -54,6 +61,7 @@ Item {
5461
5562 Loader {
5663 id: shellLoader
64+ active: false
5765
5866 width: units.gu(40)
5967 height: units.gu(71)
@@ -224,6 +232,22 @@ Item {
224232 tryCompare(ApplicationManager, "focusedApplicationId", app)
225233 }
226234
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+
227251 function test_login() {
228252 sessionSpy.clear()
229253 tryCompare(sessionSpy, "count", 0)
@@ -503,5 +527,53 @@ Item {
503527 var lockscreen = findChild(shell, "lockscreen");
504528 verify(lockscreen.shown);
505529 }
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+ }
506578 }
507579}
tests/qmltests/tst_TabletShell.qml+10−2
@@ -32,6 +32,13 @@ Row {
3232 id: root
3333 spacing: 0
3434
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+
3542 QtObject {
3643 id: applicationArguments
3744
@@ -51,6 +58,7 @@ Row {
5158 Loader {
5259 id: shellLoader
5360
61+ active: false
5462 width: units.gu(100)
5563 height: units.gu(80)
5664
@@ -295,8 +303,8 @@ Row {
295303 selectUser(data.user)
296304
297305 AccountsService.demoEdges = data.demo
298− var edgeDemo = findChild(shell, "edgeDemo")
299− tryCompare(edgeDemo, "running", data.demo)
306+ var tutorial = findChild(shell, "tutorial");
307+ tryCompare(tutorial, "running", data.demo);
300308
301309 swipeFromLeftEdge(shell.width * 0.75)
302310 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 @@
11add_executable(uqmlscene
22 ${shellapplication_MOC_SRCS}
3+ ActiveFocusLogger.cpp
34 main.cpp
45 ${CMAKE_SOURCE_DIR}/src/MouseTouchAdaptor.cpp
56 )
tests/uqmlscene/main.cpp+13
@@ -68,6 +68,12 @@
6868// UbuntuGestures lib
6969#include <TouchRegistry.h>
7070
71+#define UQMLSCENE_DEBUG_ACTIVE_FOCUS 0
72+
73+#if UQMLSCENE_DEBUG_ACTIVE_FOCUS
74+ #include "ActiveFocusLogger.h"
75+#endif
76+
7177#ifdef QML_RUNTIME_TESTING
7278class RenderStatistics
7379{
@@ -378,6 +384,10 @@ int main(int argc, char ** argv)
378384{
379385 Options options;
380386
387+ #if UQMLSCENE_DEBUG_ACTIVE_FOCUS
388+ ActiveFocusLogger activeFocusLogger;
389+ #endif
390+
381391 QStringList imports;
382392 QList<QPair<QString, QString> > bundles;
383393 for (int i = 1; i < argc; ++i) {
@@ -503,6 +513,9 @@ int main(int argc, char ** argv)
503513 QQuickItem *contentItem = qobject_cast<QQuickItem *>(topLevel);
504514 if (contentItem) {
505515 qxView = new QQuickView(&engine, nullptr);
516+ #if UQMLSCENE_DEBUG_ACTIVE_FOCUS
517+ activeFocusLogger.setWindow(qxView);
518+ #endif
506519 TouchRegistry::instance()->setParent(qxView);
507520 qxView->installEventFilter(TouchRegistry::instance());
508521 window = qxView;