on xenial help sign in
Merge trunk
commit0783d15f0fb72a885f449c47e9237950973360e3 authorMichal Hruby <michal.mhr@gmail.com> dateFri 6 Sep 2013, 10:36 (13 years ago) parent4ca9c7856827d4
31 files changed+794−82
Dash/GenericScopeView.qml+4−4 Launcher/LauncherDelegate.qml+20−9 Launcher/LauncherPanel.qml+3−2 Launcher/graphics/focused_app_arrow@30.png Shell.qml+9 debian/changelog+13 plugins/Unity/Launcher/backend/launcherbackend.cpp+32−4 plugins/Unity/Launcher/launcheritem.cpp+14 plugins/Unity/Launcher/launcheritem.h+4 plugins/Unity/Launcher/launchermodel.cpp+59 plugins/Unity/Launcher/launchermodel.h+1 po/es.po+24−22 tests/mocks/LightDM/demo/GreeterPrivate.cpp+4−2 tests/mocks/Unity/CMakeLists.txt+2 tests/mocks/Unity/Launcher/MockLauncherItem.cpp+16 tests/mocks/Unity/Launcher/MockLauncherItem.h+4 tests/mocks/Unity/Launcher/MockLauncherModel.cpp+9 tests/mocks/Unity/Launcher/MockLauncherModel.h+1 tests/mocks/Unity/fake_applications_scope.cpp+68 tests/mocks/Unity/fake_applications_scope.h+33 tests/mocks/Unity/fake_categories.cpp+84−9 tests/mocks/Unity/fake_categories.h+6−1 tests/mocks/Unity/fake_preview.cpp+66 tests/mocks/Unity/fake_preview.h+62 tests/mocks/Unity/fake_scope.cpp+33−9 tests/mocks/Unity/fake_scope.h+15−1 tests/mocks/Unity/fake_scopes.cpp+4−1 tests/plugins/Unity/Launcher/CMakeLists.txt+36−3 tests/plugins/Unity/Launcher/launcherbackendtest.cpp+14−15 tests/plugins/Unity/Launcher/launchermodeltest.cpp+146 tests/qmltests/Launcher/tst_Launcher.qml+8
Dash/GenericScopeView.qml+4−4
@@ -73,12 +73,12 @@ ScopeView {
7373 onLoaded: {
7474 if (source.toString().indexOf("Apps/RunningApplicationsGrid.qml") != -1) {
7575 // TODO: the running apps grid doesn't support standard scope results model yet
76− item.firstModel = results.firstModel
77− item.secondModel = results.secondModel
76+ item.firstModel = Qt.binding(function() { return results.firstModel })
77+ item.secondModel = Qt.binding(function() { return results.secondModel })
7878 } else {
79− item.model = results
79+ item.model = Qt.binding(function() { return results })
8080 }
81− item.objectName = categoryId
81+ item.objectName = Qt.binding(function() { return categoryId })
8282 if (item.expandable) {
8383 var shouldFilter = index != categoryView.expandedIndex;
8484 if (shouldFilter != item.filter) {
Launcher/LauncherDelegate.qml+20−9
@@ -24,6 +24,7 @@ Item {
2424 property int count: -1
2525 property int progress: -1
2626 property bool highlighted: false
27+ property bool itemFocused: false
2728 property real maxAngle: 0
2829 property bool inverted: false
2930
@@ -43,17 +44,18 @@ Item {
4344 onCountChanged: shaderEffectSource.scheduleUpdate();
4445 onProgressChanged: shaderEffectSource.scheduleUpdate();
4546 onHighlightedChanged: shaderEffectSource.scheduleUpdate();
47+ onItemFocusedChanged: shaderEffectSource.scheduleUpdate();
4648
4749 Item {
4850 id: iconItem
49− width: parent.itemWidth
50− height: parent.itemHeight
51+ width: parent.itemWidth + units.gu(1)
52+ height: parent.itemHeight + units.gu(1)
5153 anchors.centerIn: parent
5254
5355 UbuntuShape {
5456 id: iconShape
5557 anchors.fill: parent
56− anchors.margins: units.gu(0.5)
58+ anchors.margins: units.gu(1)
5759 radius: "medium"
5860
5961 image: Image {
@@ -78,7 +80,7 @@ Item {
7880 anchors {
7981 right: parent.right
8082 top: parent.top
81− margins: -units.dp(1)
83+ margins: units.dp(3)
8284 }
8385 width: Math.min(root.itemWidth, Math.max(units.gu(3), countLabel.implicitWidth + units.gu(2.5)))
8486 height: units.gu(3)
@@ -105,9 +107,9 @@ Item {
105107 left: iconItem.left
106108 right: iconItem.right
107109 bottom: iconItem.bottom
108− leftMargin: units.gu(0.5)
109− rightMargin: units.gu(0.5)
110− bottomMargin: units.gu(0.5)
110+ leftMargin: units.gu(1)
111+ rightMargin: units.gu(1)
112+ bottomMargin: units.gu(1)
111113 }
112114 height: units.gu(1.5)
113115 visible: root.progress > -1
@@ -136,14 +138,23 @@ Item {
136138 }
137139 }
138140 }
141+ Image {
142+ objectName: "focusedHighlight"
143+ anchors {
144+ right: parent.right
145+ verticalCenter: parent.verticalCenter
146+ }
147+ visible: root.itemFocused
148+ source: "graphics/focused_app_arrow.png"
149+ }
139150 }
140151
141152 ShaderEffect {
142153 id: transformEffect
143154 anchors.centerIn: parent
144155 anchors.verticalCenterOffset: root.offset
145− width: parent.itemWidth
146− height: parent.itemHeight
156+ width: iconItem.width
157+ height: iconItem.height
147158 property real itemOpacity: root.itemOpacity
148159 property real brightness: Math.max(-1, root.brightness)
149160 property real angle: root.angle
Launcher/LauncherPanel.qml+3−2
@@ -50,8 +50,6 @@ Item {
5050 id: mainColumn
5151 anchors {
5252 fill: parent
53− leftMargin: units.gu(0.5)
54− rightMargin: units.gu(0.5)
5553 }
5654
5755 MouseArea {
@@ -94,6 +92,8 @@ Item {
9492 fill: parent
9593 topMargin: -extensionSize + units.gu(0.5)
9694 bottomMargin: -extensionSize + units.gu(1)
95+ leftMargin: units.gu(0.5)
96+ rightMargin: units.gu(0.5)
9797 }
9898 topMargin: extensionSize
9999 bottomMargin: extensionSize
@@ -156,6 +156,7 @@ Item {
156156 iconName: model.icon
157157 count: model.count
158158 progress: model.progress
159+ itemFocused: model.focused
159160 inverted: root.inverted
160161 z: -Math.abs(offset)
161162 maxAngle: 55
Launcher/graphics/focused_app_arrow@30.pngnew file

Binary file not shown.

Shell.qml+9
@@ -95,6 +95,15 @@ FocusScope {
9595 greeter.hide();
9696 shell.activateApplication(desktopFile);
9797 }
98+
99+ onMainStageFocusedApplicationChanged: {
100+ var app = applicationManager.mainStageFocusedApplication
101+ if (app != null) {
102+ LauncherModel.applicationFocused(app.desktopFile);
103+ } else {
104+ LauncherModel.applicationFocused("");
105+ }
106+ }
98107 }
99108
100109 function activateApplication(desktopFile, argument) {
debian/changelog+13
@@ -1,3 +1,16 @@
1+unity8 (7.81.3+13.10.20130905.2-0ubuntu1) saucy; urgency=low
2+
3+ [ Michael Zanetti ]
4+ * Integrate Launcher with AppManager.
5+
6+ [ Nick Dedekind ]
7+ * Removed FIXME from slider int->double conversion.
8+
9+ [ Ubuntu daily release ]
10+ * Automatic snapshot from revision 291
11+
12+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 05 Sep 2013 10:48:02 +0000
13+
114unity8 (7.81.3+13.10.20130904.1-0ubuntu1) saucy; urgency=low
215
316 [ Nicolas d'Offay ]
plugins/Unity/Launcher/backend/launcherbackend.cpp+32−4
@@ -25,6 +25,7 @@
2525#include <QDBusArgument>
2626#include <QFileInfo>
2727#include <QGSettings>
28+#include <QDebug>
2829
2930class LauncherBackendItem
3031{
@@ -73,6 +74,19 @@ void LauncherBackend::setStoredApplications(const QStringList &appIds)
7374 }
7475 }
7576 m_storedApps = appIds;
77+ Q_FOREACH(const QString &appId, appIds) {
78+ if (!m_itemCache.contains(appId)) {
79+ QString df = findDesktopFile(appId);
80+ if (!df.isEmpty()) {
81+ LauncherBackendItem *item = parseDesktopFile(df);
82+ m_itemCache.insert(appId, item);
83+ } else {
84+ // Cannot find any data for that app... ignoring it.
85+ qWarning() << "cannot find desktop file for" << appId << ". discarding app.";
86+ m_storedApps.removeAll(appId);
87+ }
88+ }
89+ }
7690 syncToAccounts();
7791}
7892
@@ -199,7 +213,7 @@ void LauncherBackend::syncFromAccounts()
199213
200214 m_storedApps.clear();
201215
202− if (m_accounts && m_user.isEmpty()) {
216+ if (m_accounts && !m_user.isEmpty()) {
203217 QVariant variant = m_accounts->getUserProperty(m_user, "launcher-items");
204218 apps = qdbus_cast<QList<QVariantMap>>(variant.value<QDBusArgument>());
205219 defaults = isDefaultsItem(apps);
@@ -210,7 +224,11 @@ void LauncherBackend::syncFromAccounts()
210224 Q_FOREACH(const QString &entry, gSettings.get("favorites").toStringList()) {
211225 if (entry.startsWith("application://")) {
212226 QString appId = entry;
227+ // Transform "application://foobar.desktop" to "foobar"
213228 appId.remove("application://");
229+ if (appId.endsWith(".desktop")) {
230+ appId.chop(8);
231+ }
214232 QString df = findDesktopFile(appId);
215233
216234 if (!df.isEmpty()) {
@@ -248,14 +266,24 @@ QString LauncherBackend::findDesktopFile(const QString &appId) const
248266 int dashPos = -1;
249267 QString helper = appId;
250268
269+ QStringList searchDirs;
270+ searchDirs << QDir::homePath() + "/.local/share/applications";
271+ searchDirs << "/usr/share/applications";
272+
273+#ifdef LAUNCHER_TESTING
274+ searchDirs << "";
275+#endif
276+
251277 do {
252278 if (dashPos != -1) {
253279 helper = helper.replace(dashPos, 1, '/');
254280 }
255281
256− QFileInfo fileInfo(QDir("/usr/share/applications"), helper);
257− if (fileInfo.exists()) {
258− return fileInfo.absoluteFilePath();
282+ Q_FOREACH(const QString &searchDir, searchDirs) {
283+ QFileInfo fileInfo(QDir(searchDir), helper + ".desktop");
284+ if (fileInfo.exists()) {
285+ return fileInfo.absoluteFilePath();
286+ }
259287 }
260288
261289 dashPos = helper.indexOf("-");
plugins/Unity/Launcher/launcheritem.cpp+14
@@ -31,6 +31,7 @@ LauncherItem::LauncherItem(const QString &appId, const QString &desktopFile, con
3131 m_recent(false),
3232 m_progress(-1),
3333 m_count(0),
34+ m_focused(false),
3435 m_quickList(new QuickListModel(this))
3536{
3637 QuickListEntry nameAction;
@@ -131,6 +132,19 @@ void LauncherItem::setCount(int count)
131132 }
132133}
133134
135+bool LauncherItem::focused() const
136+{
137+ return m_focused;
138+}
139+
140+void LauncherItem::setFocused(bool focused)
141+{
142+ if (m_focused != focused) {
143+ m_focused = focused;
144+ Q_EMIT focusedChanged(focused);
145+ }
146+}
147+
134148unity::shell::launcher::QuickListModelInterface *LauncherItem::quickList() const
135149{
136150 return m_quickList;
plugins/Unity/Launcher/launcheritem.h+4
@@ -54,6 +54,9 @@ public:
5454 int count() const;
5555 void setCount(int count);
5656
57+ bool focused() const;
58+ void setFocused(bool focused);
59+
5760 unity::shell::launcher::QuickListModelInterface *quickList() const;
5861
5962Q_SIGNALS:
@@ -70,6 +73,7 @@ private:
7073 bool m_recent;
7174 int m_progress;
7275 int m_count;
76+ bool m_focused;
7377 QuickListModel *m_quickList;
7478};
7579
plugins/Unity/Launcher/launchermodel.cpp+59
@@ -74,6 +74,8 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
7474 return item->count();
7575 case RoleProgress:
7676 return item->progress();
77+ case RoleFocused:
78+ return item->focused();
7779 }
7880
7981 return QVariant();
@@ -191,6 +193,63 @@ void LauncherModel::setUser(const QString &username)
191193 m_backend->setUser(username);
192194}
193195
196+void LauncherModel::applicationFocused(const QString &appId)
197+{
198+ // Unfocus all apps
199+ for(int i = 0; i < m_list.count(); ++i) {
200+ LauncherItem *item = m_list.at(i);
201+ if (item->focused()) {
202+ item->setFocused(false);
203+ Q_EMIT dataChanged(this->index(i), this->index(i), QVector<int>() << RoleFocused);
204+ }
205+ }
206+
207+ if (appId.isEmpty()) {
208+ return;
209+ }
210+
211+ // FIXME: drop this once we get real appIds from the AppManager
212+ QString helper = appId.split('/').last();
213+ if (helper.endsWith(".desktop")) {
214+ helper.chop(8);
215+ }
216+
217+ int index = findApplication(helper);
218+ if (index >= 0) {
219+ m_list.at(index)->setFocused(true);
220+ Q_EMIT dataChanged(this->index(index), this->index(index), QVector<int>() << RoleFocused);
221+ } else {
222+ // Add app to recent apps
223+ QString desktopFile = m_backend->desktopFile(helper);
224+ QString appName = m_backend->displayName(helper);
225+ QString icon = m_backend->icon(helper);
226+
227+ LauncherItem *item = new LauncherItem(helper, desktopFile, appName, icon);
228+ item->setRecent(true);
229+ item->setFocused(true);
230+ beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
231+ m_list.append(item);
232+ endInsertRows();
233+
234+ // Clean up old recent apps
235+ QList<int> recentAppIndices;
236+ for (int i = 0; i < m_list.count(); ++i) {
237+ if (m_list.at(i)->recent()) {
238+ recentAppIndices << i;
239+ }
240+ }
241+ int run = 0;
242+ while (recentAppIndices.count() > 5) {
243+ beginRemoveRows(QModelIndex(), recentAppIndices.first() - run, recentAppIndices.first() - run);
244+ m_list.takeAt(recentAppIndices.first() - run)->deleteLater();
245+ endRemoveRows();
246+ recentAppIndices.takeFirst();
247+ ++run;
248+ }
249+ storeAppList();
250+ }
251+}
252+
194253void LauncherModel::storeAppList()
195254{
196255 QStringList appIds;
plugins/Unity/Launcher/launchermodel.h+1
@@ -49,6 +49,7 @@ public:
4949 Q_INVOKABLE void requestRemove(const QString &appId);
5050 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
5151 Q_INVOKABLE void setUser(const QString &username);
52+ Q_INVOKABLE void applicationFocused(const QString &appId);
5253
5354private:
5455 void storeAppList();
po/es.po+24−22
@@ -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: 2013-08-27 13:36-0400\n"
11−"PO-Revision-Date: 2013-07-08 07:25+0000\n"
11+"PO-Revision-Date: 2013-09-04 22:12+0000\n"
1212"Last-Translator: Adolfo Jayme Barrientos <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: 2013-08-30 05:37+0000\n"
19−"X-Generator: Launchpad (build 16750)\n"
18+"X-Launchpad-Export-Date: 2013-09-05 05:35+0000\n"
19+"X-Generator: Launchpad (build 16758)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -88,47 +88,49 @@ msgstr "YouTube"
8888
8989#: Components/EdgeDemoOverlay.qml:145
9090msgid "Skip intro"
91−msgstr ""
91+msgstr "Omitir la introducción"
9292
9393#: Components/EdgeDemo.qml:98
9494msgid "Right edge"
95−msgstr ""
95+msgstr "Borde derecho"
9696
9797#: Components/EdgeDemo.qml:99
9898msgid "Try swiping from the right edge to unlock the phone"
99−msgstr ""
99+msgstr "Deslice el dedo desde el borde derecho para desbloquear el teléfono"
100100
101101#: Components/EdgeDemo.qml:135
102102msgid "Top edge"
103−msgstr ""
103+msgstr "Borde superior"
104104
105105#: Components/EdgeDemo.qml:136
106106msgid "Try swiping from the top edge to access the indicators"
107−msgstr ""
107+msgstr "Deslice el dedo desde el borde superior para acceder a los menús"
108108
109109#: Components/EdgeDemo.qml:161
110110msgid "Close"
111−msgstr ""
111+msgstr "Cerrar"
112112
113113#: Components/EdgeDemo.qml:162
114114msgid "Swipe up again to close the settings screen"
115−msgstr ""
115+msgstr "Deslice hacia arriba para cerrar la pantalla de configuración"
116116
117117#: Components/EdgeDemo.qml:189
118118msgid "Left edge"
119−msgstr ""
119+msgstr "Borde izquierdo"
120120
121121#: Components/EdgeDemo.qml:190
122122msgid "Swipe from the left to reveal the launcher for quick access to apps"
123123msgstr ""
124+"Deslice el dedo desde la izquierda para mostrar el lanzador y acceder "
125+"rápidamente a las aplicaciones"
124126
125127#: Components/EdgeDemo.qml:217
126128msgid "Well done"
127−msgstr ""
129+msgstr "Bien hecho"
128130
129131#: Components/EdgeDemo.qml:218
130132msgid "You have now mastered the edge gestures and can start using the phone"
131−msgstr ""
133+msgstr "Ahora domina el uso de gestos de los bordes. Puede usar el teléfono"
132134
133135#: Components/PageHeader.qml:298
134136msgid "Recent searches"
@@ -138,35 +140,35 @@ msgstr "Búsquedas recientes"
138140#: Dash/Apps/AppInfo.qml:73
139141#, qt-format
140142msgid "(%1)"
141−msgstr ""
143+msgstr "(%1)"
142144
143145#. TRANSLATORS: Number of persons who wrote reviews for this app
144146#: Dash/Apps/AppInfo.qml:85
145147#, qt-format
146148msgid "%1 review"
147149msgid_plural "%1 reviews"
148−msgstr[0] ""
149−msgstr[1] ""
150+msgstr[0] "%1 reseña"
151+msgstr[1] "%1 reseñas"
150152
151153#: Dash/Apps/AppPreview.qml:175
152154msgid "Rate this"
153−msgstr ""
155+msgstr "Valorar esto"
154156
155157#: Dash/Apps/AppReviews.qml:72
156158msgid "Add a review"
157−msgstr ""
159+msgstr "Añadir una reseña"
158160
159161#: Dash/Apps/AppReviews.qml:84
160162msgid "Review"
161−msgstr ""
163+msgstr "Reseñar"
162164
163165#: Dash/Apps/AppReviews.qml:118
164166msgid "Send"
165−msgstr ""
167+msgstr "Enviar"
166168
167169#: Dash/Apps/AppReviews.qml:136
168170msgid "Comments:"
169−msgstr ""
171+msgstr "Comentarios:"
170172
171173#: Dash/DashMusic.qml:27 Dash/DashVideos.qml:28
172174msgid "Featured"
@@ -210,7 +212,7 @@ msgstr "Autor:"
210212
211213#: Greeter/Lockscreen.qml:134
212214msgid "Emergency Call"
213−msgstr ""
215+msgstr "Llamada de emergencia"
214216
215217#: Greeter/PassphraseLockscreen.qml:59
216218#, qt-format
tests/mocks/LightDM/demo/GreeterPrivate.cpp+4−2
@@ -55,11 +55,13 @@ void GreeterPrivate::handleRespond(const QString &response)
5555 QSettings settings(QDir::homePath() + "/.unity8-greeter-demo", QSettings::NativeFormat);
5656 QVariant password = settings.value("password", "none");
5757
58+ QString passwordValue;
5859 if (password == "pin") {
59− authenticated = (response == "1234");
60+ passwordValue = settings.value("passwordValue", "1234").toString();
6061 } else {
61− authenticated = (response == "password");
62+ passwordValue = settings.value("passwordValue", "password").toString();
6263 }
64+ authenticated = (response == passwordValue);
6365 Q_EMIT q->authenticationComplete();
6466}
6567
tests/mocks/Unity/CMakeLists.txt+2
@@ -18,6 +18,8 @@ add_definitions(-DQT_NO_KEYWORDS)
1818set(UnityQML_SOURCES
1919 ${CMAKE_SOURCE_DIR}/plugins/Unity/categoryresults.cpp
2020 ${CMAKE_SOURCE_DIR}/plugins/Unity/iconutils.cpp
21+ fake_applications_scope.cpp
22+ fake_preview.cpp
2123 fake_scope.cpp
2224 fake_scopes.cpp
2325 fake_categories.cpp
tests/mocks/Unity/Launcher/MockLauncherItem.cpp+16
@@ -33,6 +33,7 @@ MockLauncherItem::MockLauncherItem(const QString &appId, const QString& desktopF
3333 m_recent(false),
3434 m_progress(-1),
3535 m_count(0),
36+ m_focused(false),
3637 m_quickList(new MockQuickListModel(this))
3738{
3839
@@ -128,6 +129,21 @@ void MockLauncherItem::setCount(int count)
128129 }
129130}
130131
132+bool MockLauncherItem::focused() const
133+{
134+ return m_focused;
135+}
136+
137+void MockLauncherItem::setFocused(bool focused)
138+{
139+ if (m_focused != focused)
140+ {
141+ m_focused = focused;
142+ Q_EMIT focusedChanged(focused);
143+ }
144+}
145+
146+
131147unity::shell::launcher::QuickListModelInterface *MockLauncherItem::quickList() const
132148{
133149 return m_quickList;
tests/mocks/Unity/Launcher/MockLauncherItem.h+4
@@ -52,6 +52,9 @@ public:
5252 int count() const;
5353 void setCount(int count);
5454
55+ bool focused() const;
56+ void setFocused(bool focused);
57+
5558 unity::shell::launcher::QuickListModelInterface *quickList() const;
5659
5760private:
@@ -64,6 +67,7 @@ private:
6467 bool m_recent;
6568 int m_progress;
6669 int m_count;
70+ bool m_focused;
6771 MockQuickListModel *m_quickList;
6872};
6973
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+9
@@ -25,6 +25,7 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
2525 MockLauncherItem *item = new MockLauncherItem("phone-app", "/usr/share/applications/phone-app.desktop", "Phone", "phone-app", this);
2626 item->setProgress(0);
2727 m_list.append(item);
28+ item->setFocused(true);
2829 item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
2930 item->setProgress(10);
3031 m_list.append(item);
@@ -49,6 +50,7 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
4950 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
5051 item->setProgress(50);
5152 item->setCount(5);
53+ item->setFocused(true);
5254 m_list.append(item);
5355 item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
5456 m_list.append(item);
@@ -85,6 +87,8 @@ QVariant MockLauncherModel::data(const QModelIndex& index, int role) const
8587 return item->progress();
8688 case RoleCount:
8789 return item->count();
90+ case RoleFocused:
91+ return item->focused();
8892 }
8993
9094 return QVariant();
@@ -180,3 +184,8 @@ void MockLauncherModel::setUser(const QString &username)
180184 Q_UNUSED(username)
181185 // TODO: implement this...
182186}
187+
188+void MockLauncherModel::applicationFocused(const QString &appId)
189+{
190+ Q_UNUSED(appId)
191+}
tests/mocks/Unity/Launcher/MockLauncherModel.h+1
@@ -44,6 +44,7 @@ public:
4444 Q_INVOKABLE void requestRemove(const QString &appId);
4545 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
4646 Q_INVOKABLE void setUser(const QString &username);
47+ Q_INVOKABLE void applicationFocused(const QString &appId);
4748
4849Q_SIGNALS:
4950 void quickListTriggered(const QString &appId, int index);
tests/mocks/Unity/fake_applications_scope.cppnew file+68
@@ -0,0 +1,68 @@
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+#include "fake_applications_scope.h"
18+
19+static const gchar * categories_model_schema[] = {
20+ "s", //ID
21+ "s", // DISPLAY_NAME
22+ "s", // ICON_HINT
23+ "s", // RENDERER_NAME
24+ "a{sv}" // HINTS
25+};
26+
27+ApplicationsScope::ApplicationsScope(bool visible, QObject* parent)
28+ : Scope(parent)
29+{
30+ m_id = "applications.scope";
31+ m_name = "Applications";
32+ m_visible = visible;
33+ m_categories->setModel(createCategoriesModel());
34+}
35+
36+DeeModel* ApplicationsScope::createCategoriesModel()
37+{
38+ DeeModel* category_model = dee_sequence_model_new();
39+ dee_model_set_schema_full(category_model, categories_model_schema, G_N_ELEMENTS(categories_model_schema));
40+ GVariant* hints = g_variant_new_array(g_variant_type_element(G_VARIANT_TYPE_VARDICT), NULL, 0);
41+
42+ GVariant* children[1];
43+ children[0] = g_variant_new_dict_entry(g_variant_new_string("content-type"),
44+ g_variant_new_variant(g_variant_new_string("apps")));
45+ GVariant* recentHints = g_variant_new_array(g_variant_type_element(G_VARIANT_TYPE_VARDICT), children, 1);
46+ dee_model_append(category_model,
47+ "recent",
48+ "Recent",
49+ "gtk-apply",
50+ "special",
51+ recentHints);
52+
53+ dee_model_append(category_model,
54+ "installed",
55+ "Installed",
56+ "gtk-apply",
57+ "grid",
58+ hints);
59+
60+ dee_model_append(category_model,
61+ "suggested",
62+ "Suggested",
63+ "gtk-apply",
64+ "grid",
65+ hints);
66+
67+ return category_model;
68+}
tests/mocks/Unity/fake_applications_scope.hnew file+33
@@ -0,0 +1,33 @@
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+#ifndef FAKE_APPLICATIONS_SCOPE_H
18+#define FAKE_APPLICATIONS_SCOPE_H
19+
20+#include "fake_scope.h"
21+
22+class ApplicationsScope : public Scope
23+{
24+ Q_OBJECT
25+
26+public:
27+ ApplicationsScope(bool visible, QObject* parent = 0);
28+
29+private:
30+ DeeModel* createCategoriesModel();
31+};
32+
33+#endif // FAKE_APPLICATIONS_SCOPE_H
tests/mocks/Unity/fake_categories.cpp+84−9
@@ -24,11 +24,20 @@
2424
2525#define CATEGORY_COLUMN 2
2626
27+// TODO: use something from libunity once it's public
28+enum CategoryColumn {
29+ ID,
30+ DISPLAY_NAME,
31+ ICON_HINT,
32+ RENDERER_NAME,
33+ HINTS
34+};
35+
2736Categories::Categories(QObject* parent)
2837 : DeeListModel(parent)
2938{
3039 // FIXME: need to clean up unused filters on countChanged
31− m_roles[Categories::RoleId] = "id";
40+ m_roles[Categories::RoleCategoryId] = "categoryId";
3241 m_roles[Categories::RoleName] = "name";
3342 m_roles[Categories::RoleIcon] = "icon";
3443 m_roles[Categories::RoleRenderer] = "renderer";
@@ -73,6 +82,25 @@ Categories::onCountChanged()
7382 }
7483}
7584
85+void Categories::onRowCountChanged()
86+{
87+ QAbstractItemModel* model = qobject_cast<QAbstractItemModel*>(sender());
88+ // find the corresponding category index
89+ for (auto iter = m_overriddenCategories.begin(); iter != m_overriddenCategories.end(); ++iter) {
90+ if (iter.value() == model) {
91+ for (int i = 0; i < rowCount(); i++) {
92+ auto id = data(index(i), RoleCategoryId).toString();
93+ if (id != iter.key()) continue;
94+ QVector<int> roles;
95+ roles.append(RoleCount);
96+ QModelIndex changedIndex = index(i);
97+ Q_EMIT dataChanged(changedIndex, changedIndex, roles);
98+ break;
99+ }
100+ }
101+ }
102+}
103+
76104void
77105Categories::onEmitCountChanged()
78106{
@@ -92,6 +120,41 @@ Categories::roleNames() const
92120 return m_roles;
93121}
94122
123+void Categories::onOverrideModelDestroyed()
124+{
125+ QObject* model = sender();
126+ auto iter = m_overriddenCategories.begin();
127+ while (iter != m_overriddenCategories.end()) {
128+ if (iter.value() == model) {
129+ iter = m_overriddenCategories.erase(iter);
130+ continue;
131+ }
132+ ++iter;
133+ }
134+}
135+
136+void Categories::overrideResults(const QString& categoryId, QAbstractItemModel* model)
137+{
138+ m_overriddenCategories[categoryId] = model;
139+ // watch the model
140+ connect(model, &QObject::destroyed, this, &Categories::onOverrideModelDestroyed);
141+ connect(model, &QAbstractItemModel::rowsInserted, this, &Categories::onRowCountChanged);
142+ connect(model, &QAbstractItemModel::rowsRemoved, this, &Categories::onRowCountChanged);
143+ connect(model, &QAbstractItemModel::modelReset, this, &Categories::onRowCountChanged);
144+
145+ // emit the dataChanged signal if the category is already in the model
146+ for (int i = 0; i < rowCount(); i++) {
147+ auto id = data(index(i), RoleCategoryId).toString();
148+ if (id != categoryId) continue;
149+ QVector<int> roles;
150+ roles.append(RoleCount);
151+ roles.append(RoleResults);
152+ QModelIndex changedIndex = index(i);
153+ Q_EMIT dataChanged(changedIndex, changedIndex, roles);
154+ break;
155+ }
156+}
157+
95158void Categories::setResultModel(DeeModel* model)
96159{
97160 // FIXME: should ref it
@@ -153,24 +216,36 @@ Categories::data(const QModelIndex& index, int role) const
153216 }
154217
155218 switch (role) {
156− case RoleId:
157− return DeeListModel::data(index, 0); //ID
219+ case RoleCategoryId:
220+ return DeeListModel::data(index, CategoryColumn::ID);
158221 case RoleName:
159− return DeeListModel::data(index, 1); //DISPLAY_NAME
222+ return DeeListModel::data(index, CategoryColumn::DISPLAY_NAME);
160223 case RoleIcon:
161− return DeeListModel::data(index, 2); //ICON_HINT
224+ return DeeListModel::data(index, CategoryColumn::ICON_HINT);
162225 case RoleRenderer:
163− return DeeListModel::data(index, 3); //RENDERER_NAME
226+ return DeeListModel::data(index, CategoryColumn::RENDERER_NAME);
164227 case RoleContentType:
165228 {
166− auto hints = DeeListModel::data(index, 4).toHash();
167− return hints.contains("content-type") ? hints["content-type"] : QVariant(QString("default"));
229+ auto hints = DeeListModel::data(index, CategoryColumn::HINTS).toHash();
230+ return hints.contains("content-type") ? hints["content-type"] : QVariant(QString("default"));
168231 }
169232 case RoleHints:
170− return DeeListModel::data(index, 4); //HINTS
233+ return DeeListModel::data(index, CategoryColumn::HINTS);
171234 case RoleResults:
235+ if (m_overriddenCategories.size() > 0)
236+ {
237+ auto id = DeeListModel::data(index, CategoryColumn::ID).toString();
238+ if (m_overriddenCategories.find(id) != m_overriddenCategories.end())
239+ return QVariant::fromValue(m_overriddenCategories[id]);
240+ }
172241 return QVariant::fromValue(getFilter(index.row()));
173242 case RoleCount:
243+ if (m_overriddenCategories.size() > 0)
244+ {
245+ auto id = DeeListModel::data(index, CategoryColumn::ID).toString();
246+ if (m_overriddenCategories.find(id) != m_overriddenCategories.end())
247+ return QVariant::fromValue(m_overriddenCategories[id]->rowCount());
248+ }
174249 return QVariant::fromValue(getFilter(index.row())->rowCount());
175250 default:
176251 return QVariant();
tests/mocks/Unity/fake_categories.h+6−1
@@ -34,7 +34,7 @@ class Categories : public DeeListModel
3434public:
3535 Categories(QObject* parent = 0);
3636 enum Roles {
37− RoleId,
37+ RoleCategoryId,
3838 RoleName,
3939 RoleIcon,
4040 RoleRenderer,
@@ -44,6 +44,8 @@ public:
4444 RoleCount
4545 };
4646
47+ Q_INVOKABLE void overrideResults(const QString& categoryId, QAbstractItemModel* model);
48+
4749 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
4850
4951 QHash<int, QByteArray> roleNames() const;
@@ -53,7 +55,9 @@ public:
5355
5456private Q_SLOTS:
5557 void onCountChanged();
58+ void onRowCountChanged();
5659 void onEmitCountChanged();
60+ void onOverrideModelDestroyed();
5761
5862private:
5963 DeeModel* getResultsForCategory(unsigned index) const;
@@ -63,6 +67,7 @@ private:
6367 QTimer m_timer;
6468 QSet<DeeListModel*> m_timerFilters;
6569 QHash<int, QByteArray> m_roles;
70+ QMap<QString, QAbstractItemModel*> m_overriddenCategories;
6671 mutable QMap<int, DeeListModel*> m_filters;
6772};
6873
tests/mocks/Unity/fake_preview.cppnew file+66
@@ -0,0 +1,66 @@
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+#include "fake_preview.h"
18+
19+QString Preview::rendererName() const
20+{
21+ return "foo";
22+}
23+
24+QString Preview::title() const
25+{
26+ return "Title";
27+}
28+
29+QString Preview::subtitle() const
30+{
31+ return "Subtitle";
32+}
33+
34+QString Preview::description() const
35+{
36+ return "Description";
37+}
38+
39+QVariant Preview::actions()
40+{
41+ return QVariant();
42+}
43+
44+QVariant Preview::infoHints()
45+{
46+ return QVariant();
47+}
48+
49+QVariantMap Preview::infoHintsHash() const
50+{
51+ return QVariantMap();
52+}
53+
54+QString Preview::image() const
55+{
56+ return "";
57+}
58+
59+QString Preview::imageSourceUri() const
60+{
61+ return "";
62+}
63+
64+void Preview::execute(const QString& actionId, const QHash<QString, QVariant>& hints)
65+{
66+}
tests/mocks/Unity/fake_preview.hnew file+62
@@ -0,0 +1,62 @@
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+#ifndef FAKE_PREVIEW_H
18+#define FAKE_PREVIEW_H
19+
20+// Qt
21+#include <QObject>
22+#include <QString>
23+#include <QMetaType>
24+#include <QList>
25+#include <QVariantMap>
26+
27+class Q_DECL_EXPORT Preview : public QObject
28+{
29+ Q_OBJECT
30+
31+ Q_PROPERTY(QString rendererName READ rendererName NOTIFY previewChanged)
32+ Q_PROPERTY(QString title READ title NOTIFY previewChanged)
33+ Q_PROPERTY(QString subtitle READ subtitle NOTIFY previewChanged)
34+ Q_PROPERTY(QString description READ description NOTIFY previewChanged)
35+ Q_PROPERTY(QVariant actions READ actions NOTIFY previewChanged)
36+ Q_PROPERTY(QVariant infoHints READ infoHints NOTIFY previewChanged)
37+ Q_PROPERTY(QVariantMap infoMap READ infoHintsHash NOTIFY previewChanged)
38+ Q_PROPERTY(QString image READ image NOTIFY previewChanged)
39+ Q_PROPERTY(QString imageSourceUri READ imageSourceUri NOTIFY previewChanged)
40+
41+public:
42+ explicit Preview(QObject *parent = 0);
43+
44+ QString rendererName() const;
45+ QString title() const;
46+ QString subtitle() const;
47+ QString description() const;
48+ QVariant actions();
49+ QVariant infoHints();
50+ QVariantMap infoHintsHash() const;
51+ QString image() const;
52+ QString imageSourceUri() const;
53+
54+ Q_INVOKABLE void execute(const QString& actionId, const QHash<QString, QVariant>& hints);
55+
56+Q_SIGNALS:
57+ void previewChanged();
58+};
59+
60+Q_DECLARE_METATYPE(Preview *)
61+
62+#endif // FAKE_PREVIEW_H
tests/mocks/Unity/fake_scope.cpp+33−9
@@ -20,6 +20,8 @@
2020#include <dee.h>
2121#include "paths.h"
2222
23+#include "fake_preview.h"
24+
2325static DeeModel* create_categories_model(unsigned category_count);
2426static DeeModel* create_results_model(unsigned category_count, unsigned result_count);
2527
@@ -127,6 +129,36 @@ void Scope::setNoResultsHint(const QString& str) {
127129 }
128130}
129131
132+void Scope::activate(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
133+ const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
134+ const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata)
135+{
136+ Q_UNUSED(uri);
137+ Q_UNUSED(icon_hint);
138+ Q_UNUSED(category);
139+ Q_UNUSED(result_type);
140+ Q_UNUSED(mimetype);
141+ Q_UNUSED(title);
142+ Q_UNUSED(comment);
143+ Q_UNUSED(dnd_uri);
144+ Q_UNUSED(metadata);
145+}
146+
147+void Scope::preview(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
148+ const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
149+ const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata)
150+{
151+ Q_UNUSED(uri);
152+ Q_UNUSED(icon_hint);
153+ Q_UNUSED(category);
154+ Q_UNUSED(result_type);
155+ Q_UNUSED(mimetype);
156+ Q_UNUSED(title);
157+ Q_UNUSED(comment);
158+ Q_UNUSED(dnd_uri);
159+ Q_UNUSED(metadata);
160+}
161+
130162static const gchar * categories_model_schema[] = {
131163 "s", //ID
132164 "s", // DISPLAY_NAME
@@ -168,14 +200,6 @@ static const gchar * results_model_schema[] = {
168200 "a{sv}" // METADATA
169201};
170202
171−static const gchar * icons[] = {
172− "Applications.png",
173− "Home.png",
174− "Music.png",
175− "People.png",
176− "Videos.png",
177−};
178−
179203DeeModel* create_results_model(unsigned category_count, unsigned result_count) {
180204 DeeModel* results_model = dee_sequence_model_new();
181205 dee_model_set_schema_full(results_model, results_model_schema, G_N_ELEMENTS(results_model_schema));
@@ -187,7 +211,7 @@ DeeModel* create_results_model(unsigned category_count, unsigned result_count) {
187211
188212 dee_model_append(results_model,
189213 ("uri://result."+std::to_string(i)).c_str(),
190− (shellAppDirectory() + "Dash/graphics/scopeIcons/" + (icons[i%G_N_ELEMENTS(icons)])).toLatin1().data(),
214+ (shellAppDirectory() + "graphics/applicationIcons/dash.png").toLatin1().data(),
191215 category,
192216 0,
193217 "application/x-desktop",
tests/mocks/Unity/fake_scope.h+15−1
@@ -21,6 +21,8 @@
2121#include <QObject>
2222#include "fake_categories.h"
2323
24+class Preview;
25+
2426class Scope : public QObject
2527{
2628 Q_OBJECT
@@ -63,6 +65,13 @@ public:
6365 void setNoResultsHint(const QString& hint);
6466 void setFormFactor(const QString& form_factor);
6567
68+ Q_INVOKABLE void activate(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
69+ const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
70+ const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata);
71+ Q_INVOKABLE void preview(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
72+ const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
73+ const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata);
74+
6675Q_SIGNALS:
6776 void idChanged(const QString&);
6877 void nameChanged(const QString&);
@@ -78,7 +87,12 @@ Q_SIGNALS:
7887 void noResultsHintChanged();
7988 void formFactorChanged();
8089
81−private:
90+ // signals triggered by activate(..) or preview(..) requests.
91+ void previewReady(Preview *preview);
92+ void activateApplication(const QString &desktop);
93+
94+protected:
95+
8296 QString m_id;
8397 QString m_iconHint;
8498 QString m_description;
tests/mocks/Unity/fake_scopes.cpp+4−1
@@ -19,6 +19,9 @@
1919// Self
2020#include "fake_scopes.h"
2121
22+// Local
23+#include "fake_applications_scope.h"
24+
2225// TODO: Implement remaining pieces, like Categories (i.e. LensView now gives warnings)
2326
2427// Qt
@@ -53,7 +56,7 @@ void Scopes::updateScopes()
5356 addScope(new Scope("MockScope1", "People", true, this));
5457 addScope(new Scope("MockScope2", "Music", false, this));
5558 addScope(new Scope("home.scope", "Home", true, this));
56− addScope(new Scope("applications.scope", "Applications", true, this));
59+ addScope(new ApplicationsScope(true, this));
5760 addScope(new Scope("MockScope5", "Videos", true, this));
5861
5962 if (!m_loaded) {
tests/plugins/Unity/Launcher/CMakeLists.txt+36−3
@@ -1,4 +1,5 @@
11pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=2)
23
34include_directories(
45 ${CMAKE_CURRENT_BINARY_DIR}
@@ -10,12 +11,14 @@ include_directories(
1011
1112add_definitions(-DSM_BUSNAME=sessionBus)
1213add_definitions(-DSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}")
14+add_definitions(-DLAUNCHER_TESTING)
1315
14−set(testCommand ${CMAKE_CURRENT_BINARY_DIR}/launcherbackendtestExec
16+### LauncherBackendTest
17+set(testBackendCommand ${CMAKE_CURRENT_BINARY_DIR}/launcherbackendtestExec
1518 -o ${CMAKE_BINARY_DIR}/launcherbackendtest.xml,xunitxml
1619 -o -,txt)
17−add_test(NAME launcherbackendtest COMMAND ${testCommand})
18−add_custom_target(launcherbackendtest ${testCommand})
20+add_test(NAME launcherbackendtest COMMAND ${testBackendCommand})
21+add_custom_target(launcherbackendtest ${testBackendCommand})
1922add_executable(launcherbackendtestExec
2023 launcherbackendtest.cpp
2124 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsService.cpp
@@ -24,3 +27,33 @@ add_executable(launcherbackendtestExec
2427 )
2528target_link_libraries(launcherbackendtestExec ${GSETTINGS_QT_LDFLAGS})
2629qt5_use_modules(launcherbackendtestExec Test Core DBus)
30+
31+### LauncherModelTest
32+set(testModelCommand ${CMAKE_CURRENT_BINARY_DIR}/launchermodeltestExec
33+ -o ${CMAKE_BINARY_DIR}/launchermodeltest.xml,xunitxml
34+ -o -,txt)
35+add_test(NAME launchermodeltest COMMAND ${testModelCommand})
36+add_custom_target(launchermodeltest ${testModelCommand})
37+add_executable(launchermodeltestExec
38+ launchermodeltest.cpp
39+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launchermodel.cpp
40+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launcheritem.cpp
41+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistmodel.cpp
42+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsService.cpp
43+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/backend/launcherbackend.cpp
44+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/common/quicklistentry.cpp
45+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
46+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
47+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
48+ )
49+target_link_libraries(launchermodeltestExec ${GSETTINGS_QT_LDFLAGS} )
50+qt5_use_modules(launchermodeltestExec Test Core DBus)
51+
52+# copy .desktop files into build directory for shadow builds
53+file(GLOB DESKTOP_FILES *.desktop)
54+
55+foreach(DESKTOP_FILE ${DESKTOP_FILES})
56+ file(COPY "${DESKTOP_FILE}"
57+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
58+ )
59+endforeach()
tests/plugins/Unity/Launcher/launcherbackendtest.cpp+14−15
@@ -20,7 +20,6 @@
2020#include "launcherbackend.h"
2121
2222#include <QtTest>
23−#include <QDebug>
2423
2524class LauncherBackendTest : public QObject
2625{
@@ -31,29 +30,29 @@ private Q_SLOTS:
3130 {
3231 LauncherBackend backend(false);
3332
34− backend.setStoredApplications(QStringList() << "relative.path" << "/full/path");
35− QCOMPARE(backend.storedApplications(), QStringList() << "relative.path" << "/full/path");
33+ backend.setStoredApplications(QStringList() << "rel-icon" << "abs-icon" << "invalid");
34+ QCOMPARE(backend.storedApplications(), QStringList() << "rel-icon" << "abs-icon");
3635 }
3736
3837 void testPinning()
3938 {
4039 LauncherBackend backend(false);
4140
42− backend.setStoredApplications(QStringList() << "one" << "two");
43− QCOMPARE(backend.isPinned("one"), false);
44− QCOMPARE(backend.isPinned("two"), false);
41+ backend.setStoredApplications(QStringList() << "rel-icon" << "abs-icon");
42+ QCOMPARE(backend.isPinned("rel-icon"), false);
43+ QCOMPARE(backend.isPinned("abs-icon"), false);
4544
46− backend.setPinned("two", true);
47− QCOMPARE(backend.isPinned("two"), true);
45+ backend.setPinned("rel-icon", true);
46+ QCOMPARE(backend.isPinned("rel-icon"), true);
4847
49− backend.setStoredApplications(QStringList() << "one" << "two" << "three");
50− QCOMPARE(backend.isPinned("two"), true);
51− QCOMPARE(backend.isPinned("three"), false);
48+ backend.setStoredApplications(QStringList() << "rel-icon" << "abs-icon" << "no-name");
49+ QCOMPARE(backend.isPinned("rel-icon"), true);
50+ QCOMPARE(backend.isPinned("no-name"), false);
5251
53− backend.setPinned("three", true);
54− backend.setStoredApplications(QStringList() << "one" << "two");
55− QCOMPARE(backend.isPinned("two"), true);
56− QCOMPARE(backend.isPinned("three"), false); // doesn't exist anymore!
52+ backend.setPinned("no-name", true);
53+ backend.setStoredApplications(QStringList() << "rel-icon" << "abs-icon");
54+ QCOMPARE(backend.isPinned("rel-icon"), true);
55+ QCOMPARE(backend.isPinned("no-name"), false); // doesn't exist anymore!
5756 }
5857};
5958
tests/plugins/Unity/Launcher/launchermodeltest.cppnew file+146
@@ -0,0 +1,146 @@
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 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+ * Michael Zanetti <michael.zanetti@canonical.com>
18+ */
19+
20+// unity-api
21+#include <unity/shell/launcher/LauncherModelInterface.h>
22+
23+#include "launcheritem.h"
24+#include "launchermodel.h"
25+
26+#include <QtTest>
27+
28+class LauncherModelTest : public QObject
29+{
30+ Q_OBJECT
31+
32+private:
33+ LauncherModel *launcherModel;
34+
35+private Q_SLOTS:
36+
37+ void initTestCase() {
38+ launcherModel = new LauncherModel(this);
39+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 0);
40+ }
41+
42+ void init() {
43+ qDebug() << "init";
44+ launcherModel->applicationFocused("abs-icon");
45+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
46+
47+ launcherModel->applicationFocused("no-icon.desktop");
48+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
49+
50+ launcherModel->applicationFocused(QString());
51+ }
52+
53+ void cleanup() {
54+ while (launcherModel->rowCount(QModelIndex()) > 0) {
55+ launcherModel->requestRemove(launcherModel->get(0)->appId());
56+ }
57+ }
58+
59+ void testMove() {
60+ QCOMPARE(launcherModel->get(0)->pinned(), false);
61+ QCOMPARE(launcherModel->get(1)->pinned(), false);
62+
63+ LauncherItemInterface *item0BeforeMove = launcherModel->get(0);
64+ LauncherItemInterface *item1BeforeMove = launcherModel->get(1);
65+ launcherModel->move(1, 0);
66+
67+ QCOMPARE(item0BeforeMove, launcherModel->get(1));
68+ QCOMPARE(item1BeforeMove, launcherModel->get(0));
69+
70+ // moved item must be pinned now
71+ QCOMPARE(item0BeforeMove->pinned(), false);
72+ QCOMPARE(item1BeforeMove->pinned(), true);
73+ }
74+
75+ void testPinning() {
76+ QCOMPARE(launcherModel->get(0)->pinned(), false);
77+ QCOMPARE(launcherModel->get(1)->pinned(), false);
78+ launcherModel->pin(launcherModel->get(0)->appId());
79+ QCOMPARE(launcherModel->get(0)->pinned(), true);
80+ QCOMPARE(launcherModel->get(1)->pinned(), false);
81+ }
82+
83+ void testRemove() {
84+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
85+ launcherModel->requestRemove(launcherModel->get(0)->appId());
86+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
87+ }
88+
89+ void testQuickListPinningRemoving() {
90+ // we start with 2 unpinned items
91+ QCOMPARE(launcherModel->get(0)->pinned(), false);
92+ QCOMPARE(launcherModel->get(1)->pinned(), false);
93+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
94+
95+ // find the Pin item in the quicklist
96+ QuickListModel *model = qobject_cast<QuickListModel*>(launcherModel->get(0)->quickList());
97+ int pinActionIndex = -1;
98+ for (int i = 0; i < model->rowCount(QModelIndex()); ++i) {
99+ if (model->get(i).actionId() == "pin_item") {
100+ pinActionIndex = i;
101+ break;
102+ }
103+ }
104+ QVERIFY(pinActionIndex >= 0);
105+
106+ // trigger pin item quicklist action => Item must be pinned now.
107+ launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), pinActionIndex);
108+ QCOMPARE(launcherModel->get(0)->pinned(), true);
109+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
110+
111+ // quicklist needs to transform to remove item. trigger it and check it item goes away
112+ launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), pinActionIndex);
113+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
114+ }
115+
116+ void testApplicationFocused() {
117+ // all apps unfocused at beginning...
118+ QCOMPARE(launcherModel->get(0)->focused(), false);
119+ QCOMPARE(launcherModel->get(1)->focused(), false);
120+
121+ launcherModel->applicationFocused("abs-icon");
122+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
123+ QCOMPARE(launcherModel->get(0)->focused(), true);
124+ QCOMPARE(launcherModel->get(1)->focused(), false);
125+
126+ launcherModel->applicationFocused("no-icon");
127+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
128+ QCOMPARE(launcherModel->get(0)->focused(), false);
129+ QCOMPARE(launcherModel->get(1)->focused(), true);
130+
131+ launcherModel->applicationFocused("rel-icon");
132+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 3);
133+ QCOMPARE(launcherModel->get(0)->focused(), false);
134+ QCOMPARE(launcherModel->get(1)->focused(), false);
135+ QCOMPARE(launcherModel->get(2)->focused(), true);
136+
137+ launcherModel->applicationFocused(QString());
138+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 3);
139+ QCOMPARE(launcherModel->get(0)->focused(), false);
140+ QCOMPARE(launcherModel->get(1)->focused(), false);
141+ QCOMPARE(launcherModel->get(2)->focused(), false);
142+ }
143+};
144+
145+QTEST_GUILESS_MAIN(LauncherModelTest)
146+#include "launchermodeltest.moc"
tests/qmltests/Launcher/tst_Launcher.qml+8
@@ -198,6 +198,14 @@ Item {
198198 compare(findChild(delegate, "progressOverlay").visible, LauncherModel.get(i).progress >= 0)
199199 }
200200 }
201+
202+ function test_focusedHighlight() {
203+ var launcherListView = findChild(launcher, "launcherListView");
204+ for (var i = 0; i < launcherListView.count; ++i) {
205+ var delegate = findChild(launcherListView, "launcherDelegate" + i)
206+ compare(findChild(delegate, "focusedHighlight").visible, LauncherModel.get(i).focused)
207+ }
208+ }
201209 }
202210
203211 UT.UnityTestCase {