on xenial help sign in
update to latest launcher API for better integration with the AppManager.

Approved by PS Jenkins bot, Gerry Boland.

commita4d0699967d4d4c47929b804ae8f7861a8442dea authorMichael Zanetti <michael.zanetti@canonical.com> dateThu 12 Sep 2013, 13:35 (13 years ago) parent7c94ed8ce999dc
16 files changed+235−107
Launcher/Launcher.qml+2−2 Launcher/LauncherPanel.qml+3−3 Shell.qml+7−10 debian/control+5−5 plugins/Unity/Launcher/CMakeLists.txt+2−1 plugins/Unity/Launcher/launcheritem.cpp+1−7 plugins/Unity/Launcher/launcheritem.h+1−3 plugins/Unity/Launcher/launchermodel.cpp+94−46 plugins/Unity/Launcher/launchermodel.h+10−1 tests/mocks/Unity/Application/plugin.cpp+5−2 tests/mocks/Unity/Launcher/CMakeLists.txt+1−1 tests/mocks/Unity/Launcher/MockLauncherModel.cpp+7−4 tests/mocks/Unity/Launcher/MockLauncherModel.h+3−1 tests/plugins/Unity/Launcher/CMakeLists.txt+4−1 tests/plugins/Unity/Launcher/launchermodeltest.cpp+88−18 tests/qmltests/Launcher/tst_Launcher.qml+2−2
Launcher/Launcher.qml+2−2
@@ -35,7 +35,7 @@ Item {
3535 readonly property bool shown: panel.x > -panel.width
3636
3737 // emitted when an application is selected
38− signal launcherApplicationSelected(string desktopFile)
38+ signal launcherApplicationSelected(string appId)
3939
4040 // emitted when the apps dash should be shown because of a swipe gesture
4141 signal dash()
@@ -159,7 +159,7 @@ Item {
159159
160160 onApplicationSelected: {
161161 root.state = ""
162− launcherApplicationSelected(desktopFile)
162+ launcherApplicationSelected(appId)
163163 }
164164 onDashItemSelected: {
165165 root.state = ""
Launcher/LauncherPanel.qml+3−3
@@ -34,7 +34,7 @@ Item {
3434 property bool preventHiding: moving || dndArea.draggedIndex >= 0 || dndArea.quickListPopover !== null || dndArea.pressed
3535 property int highlightIndex: -1
3636
37− signal applicationSelected(string desktopFile)
37+ signal applicationSelected(string appId)
3838 signal dashItemSelected(int index)
3939
4040 BorderImage {
@@ -300,7 +300,7 @@ Item {
300300 } else if (clickedItem.angle < -12) {
301301 launcherListView.flick(0, launcherListView.clickFlickSpeed);
302302 } else {
303− root.applicationSelected(LauncherModel.get(index).desktopFile);
303+ root.applicationSelected(LauncherModel.get(index).appId);
304304 }
305305 return;
306306 }
@@ -311,7 +311,7 @@ Item {
311311 } else if (clickedItem.angle < -30) {
312312 launcherListView.flick(0, launcherListView.clickFlickSpeed);
313313 } else {
314− root.applicationSelected(LauncherModel.get(index).desktopFile);
314+ root.applicationSelected(LauncherModel.get(index).appId);
315315 }
316316 }
317317
Shell.qml+7−10
@@ -65,6 +65,12 @@ FocusScope {
6565
6666 property var applicationManager: ApplicationManagerWrapper {}
6767
68+ Binding {
69+ target: LauncherModel
70+ property: "applicationManager"
71+ value: ApplicationManager
72+ }
73+
6874 Component.onCompleted: {
6975 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
7076
@@ -99,15 +105,6 @@ FocusScope {
99105 greeter.hide();
100106 shell.activateApplication(desktopFile);
101107 }
102−
103− onMainStageFocusedApplicationChanged: {
104− var app = applicationManager.mainStageFocusedApplication
105− if (app != null) {
106− LauncherModel.applicationFocused(app.desktopFile);
107− } else {
108− LauncherModel.applicationFocused("");
109− }
110− }
111108 }
112109
113110 function activateApplication(desktopFile, argument) {
@@ -652,7 +649,7 @@ FocusScope {
652649 }
653650 onLauncherApplicationSelected:{
654651 greeter.hide()
655− shell.activateApplication(desktopFile)
652+ shell.activateApplication(appId)
656653 }
657654 onShownChanged: {
658655 if (shown) {
debian/control+5−5
@@ -73,7 +73,7 @@ Depends: gsettings-desktop-schemas,
7373 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.46+13.10.20130829.1-0ubuntu1),
7474 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
7575 qtdeclarative5-xmllistmodel-plugin,
76− unity-launcher-impl-2,
76+ unity-launcher-impl-3,
7777 unity-notifications-impl-1,
7878 unity8-fake-env | qtubuntu-shell,
7979 unity8-private (= ${binary:Version}),
@@ -89,18 +89,18 @@ Description: Unity 8 shell
8989
9090Package: unity8-autopilot
9191Architecture: all
92−Depends: indicators-client (>= ${source:Version}),
92+Depends: gir1.2-glib-2.0,
93+ indicators-client (>= ${source:Version}),
9394 libautopilot-qt,
9495 libqt5test5,
9596 libqt5widgets5,
9697 python-evdev,
98+ python-gi,
9799 unity8 (>= ${source:Version}),
98100 unity8-fake-env (>= ${source:Version}),
99101 ${misc:Depends},
100102 ${python:Depends},
101103 ${shlibs:Depends},
102−Recommends: gir1.2-glib-2.0,
103− python-gi,
104104Description: Test package for Unity 8 shell
105105 Autopilot tests for the unity8 package
106106
@@ -122,7 +122,7 @@ Pre-Depends: ${misc:Pre-Depends},
122122Depends: ${misc:Depends},
123123 ${shlibs:Depends},
124124Provides: unity-launcher-impl,
125− unity-launcher-impl-2,
125+ unity-launcher-impl-3,
126126Description: Unity 8 private libs
127127 The Unity 8 shell is the primary user interface for Ubuntu devices.
128128 .
plugins/Unity/Launcher/CMakeLists.txt+2−1
@@ -1,5 +1,6 @@
11include(FindPkgConfig)
2−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=2)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=3)
3+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=1)
34pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
45
56add_definitions(-DSM_BUSNAME=systemBus)
plugins/Unity/Launcher/launcheritem.cpp+1−7
@@ -20,10 +20,9 @@
2020#include "launcheritem.h"
2121#include "quicklistmodel.h"
2222
23−LauncherItem::LauncherItem(const QString &appId, const QString &desktopFile, const QString &name, const QString &icon, QObject *parent) :
23+LauncherItem::LauncherItem(const QString &appId, const QString &name, const QString &icon, QObject *parent) :
2424 LauncherItemInterface(parent),
2525 m_appId(appId),
26− m_desktopFile(desktopFile),
2726 m_name(name),
2827 m_icon(icon),
2928 m_pinned(false),
@@ -48,11 +47,6 @@ QString LauncherItem::appId() const
4847 return m_appId;
4948}
5049
51−QString LauncherItem::desktopFile() const
52−{
53− return m_desktopFile;
54−}
55−
5650QString LauncherItem::name() const
5751{
5852 return m_name;
plugins/Unity/Launcher/launcheritem.h+1−3
@@ -32,10 +32,9 @@ class LauncherItem: public LauncherItemInterface
3232{
3333 Q_OBJECT
3434public:
35− LauncherItem(const QString &appId, const QString &desktopFile, const QString &name, const QString &icon, QObject *parent = 0);
35+ LauncherItem(const QString &appId, const QString &name, const QString &icon, QObject *parent = 0);
3636
3737 QString appId() const;
38− QString desktopFile() const;
3938 QString name() const;
4039 QString icon() const;
4140
@@ -65,7 +64,6 @@ Q_SIGNALS:
6564
6665private:
6766 QString m_appId;
68− QString m_desktopFile;
6967 QString m_name;
7068 QString m_icon;
7169 bool m_pinned;
plugins/Unity/Launcher/launchermodel.cpp+94−46
@@ -21,16 +21,20 @@
2121#include "launcheritem.h"
2222#include "backend/launcherbackend.h"
2323
24+#include <unity/shell/application/ApplicationInfoInterface.h>
25+
26+using namespace unity::shell::application;
27+
2428LauncherModel::LauncherModel(QObject *parent):
2529 LauncherModelInterface(parent),
26− m_backend(new LauncherBackend(true, this))
30+ m_backend(new LauncherBackend(true, this)),
31+ m_appManager(0)
2732{
2833 connect(m_backend, SIGNAL(countChanged(QString,int)), SLOT(countChanged(QString,int)));
2934 connect(m_backend, SIGNAL(progressChanged(QString,int)), SLOT(progressChanged(QString,int)));
3035
3136 Q_FOREACH (const QString &entry, m_backend->storedApplications()) {
3237 LauncherItem *item = new LauncherItem(entry,
33− m_backend->desktopFile(entry),
3438 m_backend->displayName(entry),
3539 m_backend->icon(entry),
3640 this);
@@ -61,9 +65,7 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
6165 LauncherItem *item = m_list.at(index.row());
6266 switch(role) {
6367 case RoleAppId:
64− return item->desktopFile();
65− case RoleDesktopFile:
66− return item->desktopFile();
68+ return item->appId();
6769 case RoleName:
6870 return item->name();
6971 case RoleIcon:
@@ -137,7 +139,6 @@ void LauncherModel::pin(const QString &appId, int index)
137139 }
138140 beginInsertRows(QModelIndex(), index, index);
139141 LauncherItem *item = new LauncherItem(appId,
140− m_backend->desktopFile(appId),
141142 m_backend->displayName(appId),
142143 m_backend->icon(appId));
143144 item->setPinned(true);
@@ -193,45 +194,21 @@ void LauncherModel::setUser(const QString &username)
193194 m_backend->setUser(username);
194195}
195196
196−void LauncherModel::applicationFocused(const QString &appId)
197+ApplicationManagerInterface *LauncherModel::applicationManager() const
197198{
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();
199+ return m_appManager;
200+}
233201
234− // Clean up old recent apps
202+void LauncherModel::setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager)
203+{
204+ // Is there already another appmanager set?
205+ if (m_appManager) {
206+ // Disconnect any signals
207+ disconnect(this, SLOT(applicationAdded(QModelIndex,int)));
208+ disconnect(this, SLOT(applicationRemoved(QModelIndex,int)));
209+ disconnect(this, SLOT(focusedAppIdChanged()));
210+
211+ // remove any recent/running apps from the launcher
235212 QList<int> recentAppIndices;
236213 for (int i = 0; i < m_list.count(); ++i) {
237214 if (m_list.at(i)->recent()) {
@@ -239,22 +216,33 @@ void LauncherModel::applicationFocused(const QString &appId)
239216 }
240217 }
241218 int run = 0;
242− while (recentAppIndices.count() > 5) {
219+ while (recentAppIndices.count() > 0) {
243220 beginRemoveRows(QModelIndex(), recentAppIndices.first() - run, recentAppIndices.first() - run);
244221 m_list.takeAt(recentAppIndices.first() - run)->deleteLater();
245222 endRemoveRows();
246223 recentAppIndices.takeFirst();
247224 ++run;
248225 }
249− storeAppList();
226+ }
227+
228+ m_appManager = appManager;
229+ connect(m_appManager, SIGNAL(rowsInserted(QModelIndex, int, int)), SLOT(applicationAdded(QModelIndex,int)));
230+ connect(m_appManager, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), SLOT(applicationRemoved(QModelIndex,int)));
231+ connect(m_appManager, SIGNAL(focusedApplicationIdChanged()), SLOT(focusedAppIdChanged()));
232+
233+ Q_EMIT applicationManagerChanged();
234+
235+ for (int i = 0; i < appManager->count(); ++i) {
236+ applicationAdded(QModelIndex(), i);
250237 }
251238}
252239
240+
253241void LauncherModel::storeAppList()
254242{
255243 QStringList appIds;
256244 Q_FOREACH(LauncherItem *item, m_list) {
257− if (item->pinned() || item->recent()) {
245+ if (item->pinned()) {
258246 appIds << item->appId();
259247 }
260248 }
@@ -292,3 +280,63 @@ void LauncherModel::countChanged(const QString &appId, int count)
292280 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
293281 }
294282}
283+
284+void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
285+{
286+ Q_UNUSED(parent);
287+
288+ ApplicationInfoInterface *app = m_appManager->get(row);
289+
290+ bool found = false;
291+ Q_FOREACH(LauncherItem *item, m_list) {
292+ if (app->appId() == item->appId()) {
293+ found = true;
294+ break;
295+ }
296+ }
297+ if (found) {
298+ // Shall we paint some running/recent app highlight? If yes, do it here.
299+ } else {
300+ LauncherItem *item = new LauncherItem(app->appId(), app->name(), app->icon().toString());
301+ item->setRecent(true);
302+ item->setFocused(app->focused());
303+
304+ beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
305+ m_list.append(item);
306+ endInsertRows();
307+ }
308+}
309+
310+void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
311+{
312+ Q_UNUSED(parent)
313+
314+ int appIndex = -1;
315+ for (int i = 0; i < m_list.count(); ++i) {
316+ if (m_list.at(i)->appId() == m_appManager->get(row)->appId()) {
317+ appIndex = i;
318+ break;
319+ }
320+ }
321+
322+ if (appIndex > -1 && !m_list.at(appIndex)->pinned()) {
323+ beginRemoveRows(QModelIndex(), appIndex, appIndex);
324+ m_list.takeAt(appIndex)->deleteLater();
325+ endRemoveRows();
326+ }
327+}
328+
329+void LauncherModel::focusedAppIdChanged()
330+{
331+ QString appId = m_appManager->focusedApplicationId();
332+ for (int i = 0; i < m_list.count(); ++i) {
333+ LauncherItem *item = m_list.at(i);
334+ if (!item->focused() && item->appId() == appId) {
335+ item->setFocused(true);
336+ Q_EMIT dataChanged(index(i), index(i), QVector<int>() << RoleFocused);
337+ } else if (item->focused() && item->appId() != appId) {
338+ item->setFocused(false);
339+ Q_EMIT dataChanged(index(i), index(i), QVector<int>() << RoleFocused);
340+ }
341+ }
342+}
plugins/Unity/Launcher/launchermodel.h+10−1
@@ -22,6 +22,7 @@
2222
2323// unity-api
2424#include <unity/shell/launcher/LauncherModelInterface.h>
25+#include <unity/shell/application/ApplicationManagerInterface.h>
2526
2627// Qt
2728#include <QAbstractListModel>
@@ -30,6 +31,7 @@ class LauncherItem;
3031class LauncherBackend;
3132
3233using namespace unity::shell::launcher;
34+using namespace unity::shell::application;
3335
3436class LauncherModel: public LauncherModelInterface
3537{
@@ -49,7 +51,9 @@ public:
4951 Q_INVOKABLE void requestRemove(const QString &appId);
5052 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
5153 Q_INVOKABLE void setUser(const QString &username);
52− Q_INVOKABLE void applicationFocused(const QString &appId);
54+
55+ unity::shell::application::ApplicationManagerInterface* applicationManager() const;
56+ void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
5357
5458private:
5559 void storeAppList();
@@ -59,9 +63,14 @@ private Q_SLOTS:
5963 void progressChanged(const QString &appId, int progress);
6064 void countChanged(const QString &appId, int count);
6165
66+ void applicationAdded(const QModelIndex &parent, int row);
67+ void applicationRemoved(const QModelIndex &parent, int row);
68+ void focusedAppIdChanged();
69+
6270private:
6371 QList<LauncherItem*> m_list;
6472 LauncherBackend *m_backend;
73+ ApplicationManagerInterface *m_appManager;
6574};
6675
6776#endif // LAUNCHERMODEL_H
tests/mocks/Unity/Application/plugin.cpp+5−2
@@ -29,8 +29,11 @@ static QObject* applicationManagerSingleton(QQmlEngine* engine, QJSEngine* scrip
2929
3030void FakeUnityApplicationQmlPlugin::registerTypes(const char *uri)
3131{
32− qmlRegisterSingletonType<ApplicationManager>(
33− uri, 0, 1, "ApplicationManager", applicationManagerSingleton);
32+ qmlRegisterUncreatableType<unity::shell::application::ApplicationManagerInterface>(uri, 0, 1, "ApplicationManagerInterface", "Abstract interface. Cannot be created in QML");
33+ qmlRegisterSingletonType<ApplicationManager>(uri, 0, 1, "ApplicationManager", applicationManagerSingleton);
34+
35+ qmlRegisterUncreatableType<unity::shell::application::ApplicationInfoInterface>(uri, 0, 1, "ApplicationInfoInterface", "Abstract interface. Cannot be created in QML");
3436 qmlRegisterType<ApplicationInfo>(uri, 0, 1, "ApplicationInfo");
37+
3538 qmlRegisterType<ApplicationImage>(uri, 0, 1, "ApplicationImage");
3639}
tests/mocks/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,5 +1,5 @@
11include(FindPkgConfig)
2−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=2)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=3)
33
44add_definitions(-DTOP_SRCDIR="${CMAKE_SOURCE_DIR}")
55
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+7−4
@@ -71,8 +71,6 @@ QVariant MockLauncherModel::data(const QModelIndex& index, int role) const
7171 LauncherItemInterface *item = m_list.at(index.row());
7272 switch(role)
7373 {
74− case RoleDesktopFile:
75− return item->desktopFile();
7674 case RoleName:
7775 return item->name();
7876 case RoleIcon:
@@ -185,7 +183,12 @@ void MockLauncherModel::setUser(const QString &username)
185183 // TODO: implement this...
186184}
187185
188−void MockLauncherModel::applicationFocused(const QString &appId)
186+void MockLauncherModel::setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager)
189187{
190− Q_UNUSED(appId)
188+ Q_UNUSED(applicationManager)
189+}
190+
191+unity::shell::application::ApplicationManagerInterface *MockLauncherModel::applicationManager() const
192+{
193+ return nullptr;
191194}
tests/mocks/Unity/Launcher/MockLauncherModel.h+3−1
@@ -44,7 +44,9 @@ 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);
47+
48+ unity::shell::application::ApplicationManagerInterface *applicationManager() const;
49+ void setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager);
4850
4951Q_SIGNALS:
5052 void quickListTriggered(const QString &appId, int index);
tests/plugins/Unity/Launcher/CMakeLists.txt+4−1
@@ -1,5 +1,6 @@
11pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=2)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=3)
3+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=1)
34
45include_directories(
56 ${CMAKE_CURRENT_BINARY_DIR}
@@ -45,6 +46,8 @@ add_executable(launchermodeltestExec
4546 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
4647 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
4748 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
49+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
50+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
4851 )
4952target_link_libraries(launchermodeltestExec ${GSETTINGS_QT_LDFLAGS} )
5053qt5_use_modules(launchermodeltestExec Test Core DBus)
tests/plugins/Unity/Launcher/launchermodeltest.cpp+88−18
@@ -19,38 +19,109 @@
1919
2020// unity-api
2121#include <unity/shell/launcher/LauncherModelInterface.h>
22+#include <unity/shell/application/ApplicationInfoInterface.h>
2223
2324#include "launcheritem.h"
2425#include "launchermodel.h"
2526
2627#include <QtTest>
2728
29+// This is a mock, specifically to test the LauncherModel
30+class MockApp: public unity::shell::application::ApplicationInfoInterface
31+{
32+ Q_OBJECT
33+public:
34+ MockApp(const QString &appId, QObject *parent = 0): ApplicationInfoInterface(appId, parent), m_appId(appId), m_focused(false) { }
35+ QString appId() const { return m_appId; }
36+ QString name() const { return "mock"; }
37+ QString comment() const { return "this is a mock"; }
38+ QUrl icon() const { return QUrl(); }
39+ ApplicationInfoInterface::Stage stage() const { return ApplicationInfoInterface::MainStage; }
40+ ApplicationInfoInterface::State state() const { return ApplicationInfoInterface::Running; }
41+ bool focused() const { return m_focused; }
42+
43+ // Methods used for mocking (not in the interface)
44+ void setFocused(bool focused) { m_focused = focused; Q_EMIT focusedChanged(focused); }
45+private:
46+ QString m_appId;
47+ bool m_focused;
48+};
49+
50+// This is a mock, specifically to test the LauncherModel
51+class MockAppManager: public unity::shell::application::ApplicationManagerInterface
52+{
53+ Q_OBJECT
54+public:
55+ MockAppManager(QObject *parent = 0): ApplicationManagerInterface(parent) {}
56+ int rowCount(const QModelIndex &) const { return m_list.count(); }
57+ QVariant data(const QModelIndex &, int ) const { return QVariant(); }
58+ QString focusedApplicationId() const {
59+ Q_FOREACH(MockApp *app, m_list) {
60+ if (app->focused()) return app->appId();
61+ }
62+ return QString();
63+ }
64+ unity::shell::application::ApplicationInfoInterface *get(int index) const { return m_list.at(index); }
65+ unity::shell::application::ApplicationInfoInterface *findApplication(const QString &) const { return nullptr; }
66+ unity::shell::application::ApplicationInfoInterface *startApplication(const QString &, const QStringList &) { return nullptr; }
67+ bool stopApplication(const QString &) { return false; }
68+ bool focusApplication(const QString &appId) {
69+ Q_FOREACH(MockApp* app, m_list) {
70+ app->setFocused(app->appId() == appId);
71+ }
72+ Q_EMIT focusedApplicationIdChanged();
73+ return true;
74+ }
75+
76+ void unfocusCurrentApplication() { }
77+
78+ void addApplication(MockApp *app) {
79+ beginInsertRows(QModelIndex(), count(), count());
80+ m_list.append(app);
81+ endInsertRows();
82+ }
83+ void removeApplication(int index) {
84+ beginRemoveRows(QModelIndex(), index, index);
85+ m_list.takeAt(index)->deleteLater();
86+ endRemoveRows();
87+ }
88+
89+private:
90+ QList<MockApp*> m_list;
91+};
92+
2893class LauncherModelTest : public QObject
2994{
3095 Q_OBJECT
3196
3297private:
3398 LauncherModel *launcherModel;
99+ MockAppManager *appManager;
34100
35101private Q_SLOTS:
36102
37103 void initTestCase() {
38104 launcherModel = new LauncherModel(this);
39105 QCOMPARE(launcherModel->rowCount(QModelIndex()), 0);
106+
107+ appManager = new MockAppManager(this);
108+ launcherModel->setApplicationManager(appManager);
40109 }
41110
111+ // Adding 2 apps to the mock appmanager. Both should appear in the launcher.
42112 void init() {
43− qDebug() << "init";
44− launcherModel->applicationFocused("abs-icon");
113+ appManager->addApplication(new MockApp("abs-icon"));
45114 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
46115
47− launcherModel->applicationFocused("no-icon.desktop");
116+ appManager->addApplication(new MockApp("no-icon"));
48117 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
49−
50− launcherModel->applicationFocused(QString());
51118 }
52119
120+ // Removing apps from appmanager and launcher as pinned ones would stick
53121 void cleanup() {
122+ while (appManager->count() > 0) {
123+ appManager->removeApplication(0);
124+ }
54125 while (launcherModel->rowCount(QModelIndex()) > 0) {
55126 launcherModel->requestRemove(launcherModel->get(0)->appId());
56127 }
@@ -118,27 +189,26 @@ private Q_SLOTS:
118189 QCOMPARE(launcherModel->get(0)->focused(), false);
119190 QCOMPARE(launcherModel->get(1)->focused(), false);
120191
121− launcherModel->applicationFocused("abs-icon");
192+ appManager->focusApplication("abs-icon");
122193 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
123194 QCOMPARE(launcherModel->get(0)->focused(), true);
124195 QCOMPARE(launcherModel->get(1)->focused(), false);
125196
126− launcherModel->applicationFocused("no-icon");
197+ appManager->focusApplication("no-icon");
127198 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
128199 QCOMPARE(launcherModel->get(0)->focused(), false);
129200 QCOMPARE(launcherModel->get(1)->focused(), true);
201+ }
130202
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);
203+ void testClosingApps() {
204+ // At the start there are 2 items. Let's pin one.
205+ launcherModel->pin("abs-icon");
206+ while (appManager->count() > 0) {
207+ appManager->removeApplication(0);
208+ }
209+ // The pinned one needs to stay, the other needs to disappear
210+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
211+ QCOMPARE(launcherModel->get(0)->appId(), QLatin1String("abs-icon"));
142212 }
143213};
144214
tests/qmltests/Launcher/tst_Launcher.qml+2−2
@@ -38,7 +38,7 @@ Item {
3838 property string lastSelectedApplication
3939
4040 onLauncherApplicationSelected: {
41− lastSelectedApplication = desktopFile
41+ lastSelectedApplication = appId
4242 }
4343
4444 property int dashItemSelected_count: 0
@@ -131,7 +131,7 @@ Item {
131131 mouseClick(appIcon, appIcon.width/2, appIcon.height/2)
132132
133133 tryCompare(launcher, "lastSelectedApplication",
134− "/usr/share/applications/phone-app.desktop")
134+ "phone-app")
135135
136136 // Tapping on an application icon also dismisses the launcher
137137 revealer.waitUntilLauncherDisappears()