on xenial help sign in
update to latest launcher API for better integration with the AppManager.
a4d0699Michael Zanetti
Launcher/Launcher.qml+2−2
@@ -35,7 +35,7 @@ Item {
35 readonly property bool shown: panel.x > -panel.width
35 readonly property bool shown: panel.x > -panel.width
36
36
37 // emitted when an application is selected
37 // emitted when an application is selected
38− signal launcherApplicationSelected(string desktopFile)
38+ signal launcherApplicationSelected(string appId)
39
39
40 // emitted when the apps dash should be shown because of a swipe gesture
40 // emitted when the apps dash should be shown because of a swipe gesture
41 signal dash()
41 signal dash()
@@ -159,7 +159,7 @@ Item {
159
159
160 onApplicationSelected: {
160 onApplicationSelected: {
161 root.state = ""
161 root.state = ""
162− launcherApplicationSelected(desktopFile)
162+ launcherApplicationSelected(appId)
163 }
163 }
164 onDashItemSelected: {
164 onDashItemSelected: {
165 root.state = ""
165 root.state = ""
Launcher/LauncherPanel.qml+3−3
@@ -34,7 +34,7 @@ Item {
34 property bool preventHiding: moving || dndArea.draggedIndex >= 0 || dndArea.quickListPopover !== null || dndArea.pressed
34 property bool preventHiding: moving || dndArea.draggedIndex >= 0 || dndArea.quickListPopover !== null || dndArea.pressed
35 property int highlightIndex: -1
35 property int highlightIndex: -1
36
36
37− signal applicationSelected(string desktopFile)
37+ signal applicationSelected(string appId)
38 signal dashItemSelected(int index)
38 signal dashItemSelected(int index)
39
39
40 BorderImage {
40 BorderImage {
@@ -300,7 +300,7 @@ Item {
300 } else if (clickedItem.angle < -12) {
300 } else if (clickedItem.angle < -12) {
301 launcherListView.flick(0, launcherListView.clickFlickSpeed);
301 launcherListView.flick(0, launcherListView.clickFlickSpeed);
302 } else {
302 } else {
303− root.applicationSelected(LauncherModel.get(index).desktopFile);
303+ root.applicationSelected(LauncherModel.get(index).appId);
304 }
304 }
305 return;
305 return;
306 }
306 }
@@ -311,7 +311,7 @@ Item {
311 } else if (clickedItem.angle < -30) {
311 } else if (clickedItem.angle < -30) {
312 launcherListView.flick(0, launcherListView.clickFlickSpeed);
312 launcherListView.flick(0, launcherListView.clickFlickSpeed);
313 } else {
313 } else {
314− root.applicationSelected(LauncherModel.get(index).desktopFile);
314+ root.applicationSelected(LauncherModel.get(index).appId);
315 }
315 }
316 }
316 }
317
317
Shell.qml+7−10
@@ -65,6 +65,12 @@ FocusScope {
65
65
66 property var applicationManager: ApplicationManagerWrapper {}
66 property var applicationManager: ApplicationManagerWrapper {}
67
67
68+ Binding {
69+ target: LauncherModel
70+ property: "applicationManager"
71+ value: ApplicationManager
72+ }
73+
68 Component.onCompleted: {
74 Component.onCompleted: {
69 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
75 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
70
76
@@ -99,15 +105,6 @@ FocusScope {
99 greeter.hide();
105 greeter.hide();
100 shell.activateApplication(desktopFile);
106 shell.activateApplication(desktopFile);
101 }
107 }
102−
103− onMainStageFocusedApplicationChanged: {
104− var app = applicationManager.mainStageFocusedApplication
105− if (app != null) {
106− LauncherModel.applicationFocused(app.desktopFile);
107− } else {
108− LauncherModel.applicationFocused("");
109− }
110− }
111 }
108 }
112
109
113 function activateApplication(desktopFile, argument) {
110 function activateApplication(desktopFile, argument) {
@@ -652,7 +649,7 @@ FocusScope {
652 }
649 }
653 onLauncherApplicationSelected:{
650 onLauncherApplicationSelected:{
654 greeter.hide()
651 greeter.hide()
655− shell.activateApplication(desktopFile)
652+ shell.activateApplication(appId)
656 }
653 }
657 onShownChanged: {
654 onShownChanged: {
658 if (shown) {
655 if (shown) {
debian/control+5−5
@@ -73,7 +73,7 @@ Depends: gsettings-desktop-schemas,
73 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.46+13.10.20130829.1-0ubuntu1),
73 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.46+13.10.20130829.1-0ubuntu1),
74 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
74 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
75 qtdeclarative5-xmllistmodel-plugin,
75 qtdeclarative5-xmllistmodel-plugin,
76− unity-launcher-impl-2,
76+ unity-launcher-impl-3,
77 unity-notifications-impl-1,
77 unity-notifications-impl-1,
78 unity8-fake-env | qtubuntu-shell,
78 unity8-fake-env | qtubuntu-shell,
79 unity8-private (= ${binary:Version}),
79 unity8-private (= ${binary:Version}),
@@ -89,18 +89,18 @@ Description: Unity 8 shell
89
89
90Package: unity8-autopilot
90Package: unity8-autopilot
91Architecture: all
91Architecture: all
92+Depends: gir1.2-glib-2.0,
92−Depends: indicators-client (>= ${source:Version}),
93+ indicators-client (>= ${source:Version}),
93 libautopilot-qt,
94 libautopilot-qt,
94 libqt5test5,
95 libqt5test5,
95 libqt5widgets5,
96 libqt5widgets5,
96 python-evdev,
97 python-evdev,
98+ python-gi,
97 unity8 (>= ${source:Version}),
99 unity8 (>= ${source:Version}),
98 unity8-fake-env (>= ${source:Version}),
100 unity8-fake-env (>= ${source:Version}),
99 ${misc:Depends},
101 ${misc:Depends},
100 ${python:Depends},
102 ${python:Depends},
101 ${shlibs:Depends},
103 ${shlibs:Depends},
102−Recommends: gir1.2-glib-2.0,
103− python-gi,
104Description: Test package for Unity 8 shell
104Description: Test package for Unity 8 shell
105 Autopilot tests for the unity8 package
105 Autopilot tests for the unity8 package
106
106
@@ -122,7 +122,7 @@ Pre-Depends: ${misc:Pre-Depends},
122Depends: ${misc:Depends},
122Depends: ${misc:Depends},
123 ${shlibs:Depends},
123 ${shlibs:Depends},
124Provides: unity-launcher-impl,
124Provides: unity-launcher-impl,
125− unity-launcher-impl-2,
125+ unity-launcher-impl-3,
126Description: Unity 8 private libs
126Description: Unity 8 private libs
127 The Unity 8 shell is the primary user interface for Ubuntu devices.
127 The Unity 8 shell is the primary user interface for Ubuntu devices.
128 .
128 .
plugins/Unity/Launcher/CMakeLists.txt+2−1
@@ -1,5 +1,6 @@
1include(FindPkgConfig)
1include(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)
3pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
4pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
4
5
5add_definitions(-DSM_BUSNAME=systemBus)
6add_definitions(-DSM_BUSNAME=systemBus)
plugins/Unity/Launcher/launcheritem.cpp+1−7
@@ -20,10 +20,9 @@
20#include "launcheritem.h"
20#include "launcheritem.h"
21#include "quicklistmodel.h"
21#include "quicklistmodel.h"
22
22
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) :
24 LauncherItemInterface(parent),
24 LauncherItemInterface(parent),
25 m_appId(appId),
25 m_appId(appId),
26− m_desktopFile(desktopFile),
27 m_name(name),
26 m_name(name),
28 m_icon(icon),
27 m_icon(icon),
29 m_pinned(false),
28 m_pinned(false),
@@ -48,11 +47,6 @@ QString LauncherItem::appId() const
48 return m_appId;
47 return m_appId;
49}
48}
50
49
51−QString LauncherItem::desktopFile() const
52−{
53− return m_desktopFile;
54−}
55−
56QString LauncherItem::name() const
50QString LauncherItem::name() const
57{
51{
58 return m_name;
52 return m_name;
plugins/Unity/Launcher/launcheritem.h+1−3
@@ -32,10 +32,9 @@ class LauncherItem: public LauncherItemInterface
32{
32{
33 Q_OBJECT
33 Q_OBJECT
34public:
34public:
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);
36
36
37 QString appId() const;
37 QString appId() const;
38− QString desktopFile() const;
39 QString name() const;
38 QString name() const;
40 QString icon() const;
39 QString icon() const;
41
40
@@ -65,7 +64,6 @@ Q_SIGNALS:
65
64
66private:
65private:
67 QString m_appId;
66 QString m_appId;
68− QString m_desktopFile;
69 QString m_name;
67 QString m_name;
70 QString m_icon;
68 QString m_icon;
71 bool m_pinned;
69 bool m_pinned;
plugins/Unity/Launcher/launchermodel.cpp+94−46
@@ -21,16 +21,20 @@
21#include "launcheritem.h"
21#include "launcheritem.h"
22#include "backend/launcherbackend.h"
22#include "backend/launcherbackend.h"
23
23
24+#include <unity/shell/application/ApplicationInfoInterface.h>
25+
26+using namespace unity::shell::application;
27+
24LauncherModel::LauncherModel(QObject *parent):
28LauncherModel::LauncherModel(QObject *parent):
25 LauncherModelInterface(parent),
29 LauncherModelInterface(parent),
26− m_backend(new LauncherBackend(true, this))
30+ m_backend(new LauncherBackend(true, this)),
31+ m_appManager(0)
27{
32{
28 connect(m_backend, SIGNAL(countChanged(QString,int)), SLOT(countChanged(QString,int)));
33 connect(m_backend, SIGNAL(countChanged(QString,int)), SLOT(countChanged(QString,int)));
29 connect(m_backend, SIGNAL(progressChanged(QString,int)), SLOT(progressChanged(QString,int)));
34 connect(m_backend, SIGNAL(progressChanged(QString,int)), SLOT(progressChanged(QString,int)));
30
35
31 Q_FOREACH (const QString &entry, m_backend->storedApplications()) {
36 Q_FOREACH (const QString &entry, m_backend->storedApplications()) {
32 LauncherItem *item = new LauncherItem(entry,
37 LauncherItem *item = new LauncherItem(entry,
33− m_backend->desktopFile(entry),
34 m_backend->displayName(entry),
38 m_backend->displayName(entry),
35 m_backend->icon(entry),
39 m_backend->icon(entry),
36 this);
40 this);
@@ -61,9 +65,7 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
61 LauncherItem *item = m_list.at(index.row());
65 LauncherItem *item = m_list.at(index.row());
62 switch(role) {
66 switch(role) {
63 case RoleAppId:
67 case RoleAppId:
64− return item->desktopFile();
68+ return item->appId();
65− case RoleDesktopFile:
66− return item->desktopFile();
67 case RoleName:
69 case RoleName:
68 return item->name();
70 return item->name();
69 case RoleIcon:
71 case RoleIcon:
@@ -137,7 +139,6 @@ void LauncherModel::pin(const QString &appId, int index)
137 }
139 }
138 beginInsertRows(QModelIndex(), index, index);
140 beginInsertRows(QModelIndex(), index, index);
139 LauncherItem *item = new LauncherItem(appId,
141 LauncherItem *item = new LauncherItem(appId,
140− m_backend->desktopFile(appId),
141 m_backend->displayName(appId),
142 m_backend->displayName(appId),
142 m_backend->icon(appId));
143 m_backend->icon(appId));
143 item->setPinned(true);
144 item->setPinned(true);
@@ -193,45 +194,21 @@ void LauncherModel::setUser(const QString &username)
193 m_backend->setUser(username);
194 m_backend->setUser(username);
194}
195}
195
196
196−void LauncherModel::applicationFocused(const QString &appId)
197+ApplicationManagerInterface *LauncherModel::applicationManager() const
197{
198{
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;
199+ return m_appManager;
209− }
200+}
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
201
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
235 QList<int> recentAppIndices;
212 QList<int> recentAppIndices;
236 for (int i = 0; i < m_list.count(); ++i) {
213 for (int i = 0; i < m_list.count(); ++i) {
237 if (m_list.at(i)->recent()) {
214 if (m_list.at(i)->recent()) {
@@ -239,22 +216,33 @@ void LauncherModel::applicationFocused(const QString &appId)
239 }
216 }
240 }
217 }
241 int run = 0;
218 int run = 0;
242− while (recentAppIndices.count() > 5) {
219+ while (recentAppIndices.count() > 0) {
243 beginRemoveRows(QModelIndex(), recentAppIndices.first() - run, recentAppIndices.first() - run);
220 beginRemoveRows(QModelIndex(), recentAppIndices.first() - run, recentAppIndices.first() - run);
244 m_list.takeAt(recentAppIndices.first() - run)->deleteLater();
221 m_list.takeAt(recentAppIndices.first() - run)->deleteLater();
245 endRemoveRows();
222 endRemoveRows();
246 recentAppIndices.takeFirst();
223 recentAppIndices.takeFirst();
247 ++run;
224 ++run;
248 }
225 }
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);
250 }
237 }
251}
238}
252
239
240+
253void LauncherModel::storeAppList()
241void LauncherModel::storeAppList()
254{
242{
255 QStringList appIds;
243 QStringList appIds;
256 Q_FOREACH(LauncherItem *item, m_list) {
244 Q_FOREACH(LauncherItem *item, m_list) {
257− if (item->pinned() || item->recent()) {
245+ if (item->pinned()) {
258 appIds << item->appId();
246 appIds << item->appId();
259 }
247 }
260 }
248 }
@@ -292,3 +280,63 @@ void LauncherModel::countChanged(const QString &appId, int count)
292 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
280 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
293 }
281 }
294}
282}
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 @@
22
22
23// unity-api
23// unity-api
24#include <unity/shell/launcher/LauncherModelInterface.h>
24#include <unity/shell/launcher/LauncherModelInterface.h>
25+#include <unity/shell/application/ApplicationManagerInterface.h>
25
26
26// Qt
27// Qt
27#include <QAbstractListModel>
28#include <QAbstractListModel>
@@ -30,6 +31,7 @@ class LauncherItem;
30class LauncherBackend;
31class LauncherBackend;
31
32
32using namespace unity::shell::launcher;
33using namespace unity::shell::launcher;
34+using namespace unity::shell::application;
33
35
34class LauncherModel: public LauncherModelInterface
36class LauncherModel: public LauncherModelInterface
35{
37{
@@ -49,7 +51,9 @@ public:
49 Q_INVOKABLE void requestRemove(const QString &appId);
51 Q_INVOKABLE void requestRemove(const QString &appId);
50 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
52 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
51 Q_INVOKABLE void setUser(const QString &username);
53 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);
53
57
54private:
58private:
55 void storeAppList();
59 void storeAppList();
@@ -59,9 +63,14 @@ private Q_SLOTS:
59 void progressChanged(const QString &appId, int progress);
63 void progressChanged(const QString &appId, int progress);
60 void countChanged(const QString &appId, int count);
64 void countChanged(const QString &appId, int count);
61
65
66+ void applicationAdded(const QModelIndex &parent, int row);
67+ void applicationRemoved(const QModelIndex &parent, int row);
68+ void focusedAppIdChanged();
69+
62private:
70private:
63 QList<LauncherItem*> m_list;
71 QList<LauncherItem*> m_list;
64 LauncherBackend *m_backend;
72 LauncherBackend *m_backend;
73+ ApplicationManagerInterface *m_appManager;
65};
74};
66
75
67#endif // LAUNCHERMODEL_H
76#endif // LAUNCHERMODEL_H
tests/mocks/Unity/Application/plugin.cpp+5−2
@@ -29,8 +29,11 @@ static QObject* applicationManagerSingleton(QQmlEngine* engine, QJSEngine* scrip
29
29
30void FakeUnityApplicationQmlPlugin::registerTypes(const char *uri)
30void FakeUnityApplicationQmlPlugin::registerTypes(const char *uri)
31{
31{
32− qmlRegisterSingletonType<ApplicationManager>(
32+ qmlRegisterUncreatableType<unity::shell::application::ApplicationManagerInterface>(uri, 0, 1, "ApplicationManagerInterface", "Abstract interface. Cannot be created in QML");
33− uri, 0, 1, "ApplicationManager", applicationManagerSingleton);
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");
34 qmlRegisterType<ApplicationInfo>(uri, 0, 1, "ApplicationInfo");
36 qmlRegisterType<ApplicationInfo>(uri, 0, 1, "ApplicationInfo");
37+
35 qmlRegisterType<ApplicationImage>(uri, 0, 1, "ApplicationImage");
38 qmlRegisterType<ApplicationImage>(uri, 0, 1, "ApplicationImage");
36}
39}
tests/mocks/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,5 +1,5 @@
1include(FindPkgConfig)
1include(FindPkgConfig)
2−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=2)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=3)
3
3
4add_definitions(-DTOP_SRCDIR="${CMAKE_SOURCE_DIR}")
4add_definitions(-DTOP_SRCDIR="${CMAKE_SOURCE_DIR}")
5
5
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+7−4
@@ -71,8 +71,6 @@ QVariant MockLauncherModel::data(const QModelIndex& index, int role) const
71 LauncherItemInterface *item = m_list.at(index.row());
71 LauncherItemInterface *item = m_list.at(index.row());
72 switch(role)
72 switch(role)
73 {
73 {
74− case RoleDesktopFile:
75− return item->desktopFile();
76 case RoleName:
74 case RoleName:
77 return item->name();
75 return item->name();
78 case RoleIcon:
76 case RoleIcon:
@@ -185,7 +183,12 @@ void MockLauncherModel::setUser(const QString &username)
185 // TODO: implement this...
183 // TODO: implement this...
186}
184}
187
185
188−void MockLauncherModel::applicationFocused(const QString &appId)
186+void MockLauncherModel::setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager)
189{
187{
190− Q_UNUSED(appId)
188+ Q_UNUSED(applicationManager)
189+}
190+
191+unity::shell::application::ApplicationManagerInterface *MockLauncherModel::applicationManager() const
192+{
193+ return nullptr;
191}
194}
tests/mocks/Unity/Launcher/MockLauncherModel.h+3−1
@@ -44,7 +44,9 @@ public:
44 Q_INVOKABLE void requestRemove(const QString &appId);
44 Q_INVOKABLE void requestRemove(const QString &appId);
45 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
45 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
46 Q_INVOKABLE void setUser(const QString &username);
46 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);
48
50
49Q_SIGNALS:
51Q_SIGNALS:
50 void quickListTriggered(const QString &appId, int index);
52 void quickListTriggered(const QString &appId, int index);
tests/plugins/Unity/Launcher/CMakeLists.txt+4−1
@@ -1,5 +1,6 @@
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=2)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=3)
3+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=1)
3
4
4include_directories(
5include_directories(
5 ${CMAKE_CURRENT_BINARY_DIR}
6 ${CMAKE_CURRENT_BINARY_DIR}
@@ -45,6 +46,8 @@ add_executable(launchermodeltestExec
45 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
46 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
46 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
47 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
47 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
48 ${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
48 )
51 )
49target_link_libraries(launchermodeltestExec ${GSETTINGS_QT_LDFLAGS} )
52target_link_libraries(launchermodeltestExec ${GSETTINGS_QT_LDFLAGS} )
50qt5_use_modules(launchermodeltestExec Test Core DBus)
53qt5_use_modules(launchermodeltestExec Test Core DBus)
tests/plugins/Unity/Launcher/launchermodeltest.cpp+88−18
@@ -19,38 +19,109 @@
19
19
20// unity-api
20// unity-api
21#include <unity/shell/launcher/LauncherModelInterface.h>
21#include <unity/shell/launcher/LauncherModelInterface.h>
22+#include <unity/shell/application/ApplicationInfoInterface.h>
22
23
23#include "launcheritem.h"
24#include "launcheritem.h"
24#include "launchermodel.h"
25#include "launchermodel.h"
25
26
26#include <QtTest>
27#include <QtTest>
27
28
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+
28class LauncherModelTest : public QObject
93class LauncherModelTest : public QObject
29{
94{
30 Q_OBJECT
95 Q_OBJECT
31
96
32private:
97private:
33 LauncherModel *launcherModel;
98 LauncherModel *launcherModel;
99+ MockAppManager *appManager;
34
100
35private Q_SLOTS:
101private Q_SLOTS:
36
102
37 void initTestCase() {
103 void initTestCase() {
38 launcherModel = new LauncherModel(this);
104 launcherModel = new LauncherModel(this);
39 QCOMPARE(launcherModel->rowCount(QModelIndex()), 0);
105 QCOMPARE(launcherModel->rowCount(QModelIndex()), 0);
106+
107+ appManager = new MockAppManager(this);
108+ launcherModel->setApplicationManager(appManager);
40 }
109 }
41
110
111+ // Adding 2 apps to the mock appmanager. Both should appear in the launcher.
42 void init() {
112 void init() {
43− qDebug() << "init";
113+ appManager->addApplication(new MockApp("abs-icon"));
44− launcherModel->applicationFocused("abs-icon");
45 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
114 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
46
115
47− launcherModel->applicationFocused("no-icon.desktop");
116+ appManager->addApplication(new MockApp("no-icon"));
48 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
117 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
49−
50− launcherModel->applicationFocused(QString());
51 }
118 }
52
119
120+ // Removing apps from appmanager and launcher as pinned ones would stick
53 void cleanup() {
121 void cleanup() {
122+ while (appManager->count() > 0) {
123+ appManager->removeApplication(0);
124+ }
54 while (launcherModel->rowCount(QModelIndex()) > 0) {
125 while (launcherModel->rowCount(QModelIndex()) > 0) {
55 launcherModel->requestRemove(launcherModel->get(0)->appId());
126 launcherModel->requestRemove(launcherModel->get(0)->appId());
56 }
127 }
@@ -118,27 +189,26 @@ private Q_SLOTS:
118 QCOMPARE(launcherModel->get(0)->focused(), false);
189 QCOMPARE(launcherModel->get(0)->focused(), false);
119 QCOMPARE(launcherModel->get(1)->focused(), false);
190 QCOMPARE(launcherModel->get(1)->focused(), false);
120
191
121− launcherModel->applicationFocused("abs-icon");
192+ appManager->focusApplication("abs-icon");
122 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
193 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
123 QCOMPARE(launcherModel->get(0)->focused(), true);
194 QCOMPARE(launcherModel->get(0)->focused(), true);
124 QCOMPARE(launcherModel->get(1)->focused(), false);
195 QCOMPARE(launcherModel->get(1)->focused(), false);
125
196
126− launcherModel->applicationFocused("no-icon");
197+ appManager->focusApplication("no-icon");
127 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
198 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
128 QCOMPARE(launcherModel->get(0)->focused(), false);
199 QCOMPARE(launcherModel->get(0)->focused(), false);
129 QCOMPARE(launcherModel->get(1)->focused(), true);
200 QCOMPARE(launcherModel->get(1)->focused(), true);
201+ }
130
202
203+ void testClosingApps() {
204+ // At the start there are 2 items. Let's pin one.
131− launcherModel->applicationFocused("rel-icon");
205+ launcherModel->pin("abs-icon");
132− QCOMPARE(launcherModel->rowCount(QModelIndex()), 3);
206+ while (appManager->count() > 0) {
133− QCOMPARE(launcherModel->get(0)->focused(), false);
207+ appManager->removeApplication(0);
134− QCOMPARE(launcherModel->get(1)->focused(), false);
208+ }
135− QCOMPARE(launcherModel->get(2)->focused(), true);
209+ // The pinned one needs to stay, the other needs to disappear
136−
137− launcherModel->applicationFocused(QString());
138− QCOMPARE(launcherModel->rowCount(QModelIndex()), 3);
210+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
139− QCOMPARE(launcherModel->get(0)->focused(), false);
211+ QCOMPARE(launcherModel->get(0)->appId(), QLatin1String("abs-icon"));
140− QCOMPARE(launcherModel->get(1)->focused(), false);
141− QCOMPARE(launcherModel->get(2)->focused(), false);
142 }
212 }
143};
213};
144
214
tests/qmltests/Launcher/tst_Launcher.qml+2−2
@@ -38,7 +38,7 @@ Item {
38 property string lastSelectedApplication
38 property string lastSelectedApplication
39
39
40 onLauncherApplicationSelected: {
40 onLauncherApplicationSelected: {
41− lastSelectedApplication = desktopFile
41+ lastSelectedApplication = appId
42 }
42 }
43
43
44 property int dashItemSelected_count: 0
44 property int dashItemSelected_count: 0
@@ -131,7 +131,7 @@ Item {
131 mouseClick(appIcon, appIcon.width/2, appIcon.height/2)
131 mouseClick(appIcon, appIcon.width/2, appIcon.height/2)
132
132
133 tryCompare(launcher, "lastSelectedApplication",
133 tryCompare(launcher, "lastSelectedApplication",
134− "/usr/share/applications/phone-app.desktop")
134+ "phone-app")
135
135
136 // Tapping on an application icon also dismisses the launcher
136 // Tapping on an application icon also dismisses the launcher
137 revealer.waitUntilLauncherDisappears()
137 revealer.waitUntilLauncherDisappears()