Merge lp:~lukas-kde/unity8/closeAppsFromQuicklist into lp:unity8

Proposed by Lukáš Tinkl
Status: Merged
Approved by: Michael Zanetti
Approved revision: 1826
Merged at revision: 1889
Proposed branch: lp:~lukas-kde/unity8/closeAppsFromQuicklist
Merge into: lp:unity8
Prerequisite: lp:~macslow/unity8/use-set-progress-api
Diff against target: 788 lines (+198/-71)
17 files modified
plugins/Greeter/Unity/Launcher/launchermodelas.cpp (+13/-14)
plugins/Unity/Launcher/asadapter.cpp (+3/-2)
plugins/Unity/Launcher/asadapter.h (+1/-4)
plugins/Unity/Launcher/gsettings.cpp (+1/-1)
plugins/Unity/Launcher/launcheritem.cpp (+10/-0)
plugins/Unity/Launcher/launcheritem.h (+1/-0)
plugins/Unity/Launcher/launchermodel.cpp (+45/-24)
plugins/Unity/Launcher/quicklistentry.cpp (+6/-2)
plugins/Unity/Launcher/quicklistentry.h (+3/-1)
plugins/Unity/Launcher/quicklistmodel.cpp (+12/-1)
plugins/Unity/Launcher/quicklistmodel.h (+3/-1)
qml/Launcher/LauncherPanel.qml (+22/-13)
tests/mocks/Unity/Launcher/MockLauncherItem.cpp (+3/-0)
tests/mocks/Unity/Launcher/MockLauncherItem.h (+1/-0)
tests/mocks/Unity/Launcher/MockLauncherModel.cpp (+4/-1)
tests/plugins/Unity/Launcher/launchermodeltest.cpp (+41/-0)
tests/qmltests/Launcher/tst_Launcher.qml (+29/-7)
To merge this branch: bzr merge lp:~lukas-kde/unity8/closeAppsFromQuicklist
Reviewer Review Type Date Requested Status
Michael Zanetti (community) Approve
Daniel d'Andrada Pending
PS Jenkins bot continuous-integration Pending
Albert Astals Cid tags clean & merges fine Pending
Review via email: mp+265669@code.launchpad.net

This proposal supersedes a proposal from 2015-06-18.

Commit message

launcher parity: close apps from quicklist

Description of the change

launcher parity: close apps from quicklist

- adds a Quit item to the launcher quicklist
- partially reverts the visual appearance to the old design of popup menus
- invokes the popup menu also on the right mouse click

 * Are there any related MPs required for this MP to build/function as expected? Please list.

No

 * Did you perform an exploratory manual test run of your code change and any related functionality?

Yes

 * Did you make sure that your branch does not contain spurious tags?

Yes

 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?

N/A

 * If you changed the UI, has there been a design review?

No (however, there is a revert to the old design)

 * Did you have a look at the warnings when running tests? Can they be reduced?

Yes

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

I see you have lp:~lukas-kde/unity8/highdpi-mousetouchadaptor in this branch. Either remove those changes or make lp:~lukas-kde/unity8/highdpi-mousetouchadaptor a prerequisite.

review: Needs Fixing
Revision history for this message
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

In qml/Launcher/LauncherPanel.qml

"""
- __foregroundColor: "black"
+ __foregroundColor: Theme.palette.selected.backgroundText
"""

"Theme" is deprecated. Please use the context variable "theme" instead. We have tons of warnings on this already. See https://code.launchpad.net/~dandrader/unity8/deprecatedTheme/+merge/262216

review: Needs Fixing
Revision history for this message
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

I would like to see a qml test launches an app and then closes it using this "quit" quick list menu entry.

I should also be able to manually test this feature or try it out (eg, via "make tryShell"), without having to flash a device for that.

review: Needs Fixing
Revision history for this message
Lukáš Tinkl (lukas-kde) wrote : Posted in a previous version of this proposal

> I would like to see a qml test launches an app and then closes it using this
> "quit" quick list menu entry.

There is already a test for this, albeit not a QML one: launchermodeltest.cp, void testQuitMenuItem()

> I should also be able to manually test this feature or try it out (eg, via
> "make tryShell"), without having to flash a device for that.

Right, I'll see what I can do with that but I'd prefer doing that in a separate MP; it goes a bit beyond the scope of this review, none of the current quick list actions are testable from QML yet (Pin/Unpin etc).

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote : Posted in a previous version of this proposal

Some inline comments.

review: Needs Fixing
Revision history for this message
Lukáš Tinkl (lukas-kde) wrote : Posted in a previous version of this proposal

> Some inline comments.

Should be fixed by r. 1812

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote : Posted in a previous version of this proposal

Hmm... testing it, the quicklist visuals seem to be the expected ones any more. Sorry if I gave bad advice on reverting the complete commit. We should only revert the positioning to what it was before, but not the coloring. Also the arrow points to the wrong direction. It should point from the menu box towards the clicked launcher item, not the other way round.

review: Needs Fixing
Revision history for this message
Lukáš Tinkl (lukas-kde) wrote : Posted in a previous version of this proposal

Ack, will fix

Revision history for this message
Michael Zanetti (mzanetti) wrote : Posted in a previous version of this proposal

http://i.imgur.com/bGXaemm.png

Font is white on white, also I'm missing the close entry.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote : Posted in a previous version of this proposal

Text conflict in qml/Launcher/LauncherPanel.qml
1 conflicts encountered.

review: Needs Fixing
Revision history for this message
Albert Astals Cid (aacid) : Posted in a previous version of this proposal
review: Abstain (tags clean & merges fine)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote : Posted in a previous version of this proposal

Looks ok and works, except the dbusinterface changes are not needed. Please drop them.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote : Posted in a previous version of this proposal

 * Did you perform an exploratory manual test run of the code change and any related functionality?

yes

 * Did CI run pass? If not, please explain why.

as much as it can, yes

 * Did you make sure that the branch does not contain spurious tags?

yes

review: Approve
Revision history for this message
Michael Zanetti (mzanetti) wrote :

re-approving as per superseded MP.

Jenkins won't work on this any more as it now depends on a unity-api change...

review: Approve
1827. By Lukáš Tinkl

re-merge again on top of use-set-progress-api to fix failing QML tests

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'plugins/Greeter/Unity/Launcher/launchermodelas.cpp'
2--- plugins/Greeter/Unity/Launcher/launchermodelas.cpp 2015-07-29 12:33:59 +0000
3+++ plugins/Greeter/Unity/Launcher/launchermodelas.cpp 2015-07-29 12:33:59 +0000
4@@ -67,6 +67,8 @@
5 return item->progress();
6 case RoleFocused:
7 return item->focused();
8+ case RoleRunning:
9+ return item->running();
10 }
11
12 return QVariant();
13@@ -217,14 +219,10 @@
14 item->setCount(cachedMap.value("count").toInt());
15 item->setCountVisible(cachedMap.value("countVisible").toBool());
16 item->setProgress(cachedMap.value("progress").toInt());
17+ item->setRunning(cachedMap.value("running").toBool());
18+
19 int idx = m_list.indexOf(item);
20- Q_EMIT dataChanged(index(idx),
21- index(idx),
22- {RoleName,
23- RoleIcon,
24- RoleCount,
25- RoleCountVisible,
26- RoleProgress});
27+ Q_EMIT dataChanged(index(idx), index(idx), {RoleName, RoleIcon, RoleCount, RoleCountVisible, RoleRunning, RoleProgress});
28 }
29 break;
30 }
31@@ -262,16 +260,17 @@
32 }
33
34 if (itemIndex == -1) {
35- QVariantMap chachedMap = entry.toMap();
36+ QVariantMap cachedMap = entry.toMap();
37 // Need to add it. Just add it into the addedIndex to keep same ordering as the list in AS.
38- LauncherItem *item = new LauncherItem(chachedMap.value("id").toString(),
39- chachedMap.value("name").toString(),
40- chachedMap.value("icon").toString(),
41+ LauncherItem *item = new LauncherItem(cachedMap.value("id").toString(),
42+ cachedMap.value("name").toString(),
43+ cachedMap.value("icon").toString(),
44 this);
45 item->setPinned(true);
46- item->setCount(chachedMap.value("count").toInt());
47- item->setCountVisible(chachedMap.value("countVisible").toBool());
48- item->setProgress(chachedMap.value("progress").toInt());
49+ item->setCount(cachedMap.value("count").toInt());
50+ item->setCountVisible(cachedMap.value("countVisible").toBool());
51+ item->setProgress(cachedMap.value("progress").toInt());
52+ item->setRunning(cachedMap.value("running").toBool());
53 beginInsertRows(QModelIndex(), newPosition, newPosition);
54 m_list.insert(newPosition, item);
55 endInsertRows();
56
57=== modified file 'plugins/Unity/Launcher/asadapter.cpp'
58--- plugins/Unity/Launcher/asadapter.cpp 2015-07-29 12:33:59 +0000
59+++ plugins/Unity/Launcher/asadapter.cpp 2015-07-29 12:33:59 +0000
60@@ -34,12 +34,12 @@
61 m_accounts->deleteLater();
62 }
63
64-void ASAdapter::syncItems(QList<LauncherItem *> m_list)
65+void ASAdapter::syncItems(const QList<LauncherItem*> &list)
66 {
67 if (m_accounts && !m_user.isEmpty()) {
68 QList<QVariantMap> items;
69
70- Q_FOREACH(LauncherItem *item, m_list) {
71+ Q_FOREACH(LauncherItem *item, list) {
72 items << itemToVariant(item);
73 }
74
75@@ -56,6 +56,7 @@
76 details.insert("count", item->count());
77 details.insert("countVisible", item->countVisible());
78 details.insert("pinned", item->pinned());
79+ details.insert("running", item->running());
80 details.insert("progress", item->progress());
81 return details;
82 }
83
84=== modified file 'plugins/Unity/Launcher/asadapter.h'
85--- plugins/Unity/Launcher/asadapter.h 2015-02-11 14:13:26 +0000
86+++ plugins/Unity/Launcher/asadapter.h 2015-07-29 12:33:59 +0000
87@@ -21,7 +21,6 @@
88
89 class LauncherItem;
90 class AccountsServiceDBusAdaptor;
91-class QDBusInterface;
92
93 class ASAdapter
94 {
95@@ -29,7 +28,7 @@
96 ASAdapter();
97 ~ASAdapter();
98
99- void syncItems(QList<LauncherItem*> m_list);
100+ void syncItems(const QList<LauncherItem*> &list);
101
102 private:
103 QVariantMap itemToVariant(LauncherItem *item) const;
104@@ -38,8 +37,6 @@
105 AccountsServiceDBusAdaptor *m_accounts;
106 QString m_user;
107
108- QDBusInterface *m_userInterface;
109-
110 friend class LauncherModelTest;
111 };
112
113
114=== modified file 'plugins/Unity/Launcher/gsettings.cpp'
115--- plugins/Unity/Launcher/gsettings.cpp 2015-03-04 08:54:12 +0000
116+++ plugins/Unity/Launcher/gsettings.cpp 2015-07-29 12:33:59 +0000
117@@ -68,7 +68,7 @@
118 void GSettings::onSettingsChanged(const QString &key)
119 {
120 if (key == "items") {
121- QStringList cachedItems = m_gSettings->get("items").toStringList();
122+ const QStringList cachedItems = m_gSettings->get("items").toStringList();
123 if (m_cachedItems != cachedItems) {
124 m_cachedItems = cachedItems;
125 Q_EMIT changed();
126
127=== modified file 'plugins/Unity/Launcher/launcheritem.cpp'
128--- plugins/Unity/Launcher/launcheritem.cpp 2015-07-29 12:33:59 +0000
129+++ plugins/Unity/Launcher/launcheritem.cpp 2015-07-29 12:33:59 +0000
130@@ -41,10 +41,15 @@
131 nameAction.setActionId("launch_item");
132 nameAction.setText(m_name);
133 m_quickList->appendAction(nameAction);
134+
135 QuickListEntry pinningAction;
136 pinningAction.setActionId("pin_item");
137 pinningAction.setText(gettext("Pin shortcut"));
138 m_quickList->appendAction(pinningAction);
139+
140+ m_quitAction.setActionId("stop_item");
141+ m_quitAction.setIcon("application-exit");
142+ m_quitAction.setText(gettext("Quit"));
143 }
144
145 QString LauncherItem::appId() const
146@@ -111,6 +116,11 @@
147 {
148 if (m_running != running) {
149 m_running = running;
150+ if (m_running) { // add the quit action
151+ m_quickList->appendAction(m_quitAction);
152+ } else { // remove the quit action
153+ m_quickList->removeAction(m_quitAction);
154+ }
155 Q_EMIT runningChanged(running);
156 }
157 }
158
159=== modified file 'plugins/Unity/Launcher/launcheritem.h'
160--- plugins/Unity/Launcher/launcheritem.h 2015-07-29 12:33:59 +0000
161+++ plugins/Unity/Launcher/launcheritem.h 2015-07-29 12:33:59 +0000
162@@ -73,6 +73,7 @@
163 bool m_focused;
164 bool m_alerting;
165 QuickListModel *m_quickList;
166+ QuickListEntry m_quitAction;
167
168 friend class LauncherModel;
169 };
170
171=== modified file 'plugins/Unity/Launcher/launchermodel.cpp'
172--- plugins/Unity/Launcher/launchermodel.cpp 2015-07-29 12:33:59 +0000
173+++ plugins/Unity/Launcher/launchermodel.cpp 2015-07-29 12:33:59 +0000
174@@ -36,7 +36,7 @@
175 m_settings(new GSettings(this)),
176 m_dbusIface(new DBusInterface(this)),
177 m_asAdapter(new ASAdapter()),
178- m_appManager(0)
179+ m_appManager(nullptr)
180 {
181 connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged);
182 connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged);
183@@ -86,6 +86,11 @@
184 return item->focused();
185 case RoleAlerting:
186 return item->alerting();
187+ case RoleRunning:
188+ return item->running();
189+ default:
190+ qWarning() << Q_FUNC_INFO << "missing role, implement me";
191+ return QVariant();
192 }
193
194 return QVariant();
195@@ -150,7 +155,7 @@
196 if (index == -1 || index == currentIndex) {
197 m_list.at(currentIndex)->setPinned(true);
198 QModelIndex modelIndex = this->index(currentIndex);
199- Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RolePinned);
200+ Q_EMIT dataChanged(modelIndex, modelIndex, {RolePinned});
201 } else {
202 move(currentIndex, index);
203 // move() will store the list to the backend itself, so just exit at this point.
204@@ -163,7 +168,7 @@
205
206 DesktopFileHandler desktopFile(appId);
207 if (!desktopFile.isValid()) {
208- qWarning() << "Can't pin this application, there is no .destkop file available.";
209+ qWarning() << "Can't pin this application, there is no .desktop file available.";
210 return;
211 }
212
213@@ -188,7 +193,7 @@
214
215 void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex)
216 {
217- int index = findApplication(appId);
218+ const int index = findApplication(appId);
219 if (index < 0) {
220 return;
221 }
222@@ -196,7 +201,7 @@
223 LauncherItem *item = m_list.at(index);
224 QuickListModel *model = qobject_cast<QuickListModel*>(item->quickList());
225 if (model) {
226- QString actionId = model->get(actionIndex).actionId();
227+ const QString actionId = model->get(actionIndex).actionId();
228
229 // Check if this is one of the launcher actions we handle ourselves
230 if (actionId == "pin_item") {
231@@ -207,7 +212,10 @@
232 }
233 } else if (actionId == "launch_item") {
234 QDesktopServices::openUrl(getUrlForAppId(appId));
235-
236+ } else if (actionId == "stop_item") { // Quit
237+ if (m_appManager) {
238+ m_appManager->stopApplication(appId);
239+ }
240 // Nope, we don't know this action, let the backend forward it to the application
241 } else {
242 // TODO: forward quicklist action to app, possibly via m_dbusIface
243@@ -305,7 +313,7 @@
244
245 void LauncherModel::unpin(const QString &appId)
246 {
247- int index = findApplication(appId);
248+ const int index = findApplication(appId);
249 if (index < 0) {
250 return;
251 }
252@@ -314,7 +322,7 @@
253 if (m_list.at(index)->pinned()) {
254 m_list.at(index)->setPinned(false);
255 QModelIndex modelIndex = this->index(index);
256- Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RolePinned);
257+ Q_EMIT dataChanged(modelIndex, modelIndex, {RolePinned});
258 }
259 } else {
260 beginRemoveRows(QModelIndex(), index, index);
261@@ -336,25 +344,25 @@
262
263 void LauncherModel::progressChanged(const QString &appId, int progress)
264 {
265- int idx = findApplication(appId);
266+ const int idx = findApplication(appId);
267 if (idx >= 0) {
268 LauncherItem *item = m_list.at(idx);
269 item->setProgress(progress);
270- Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleProgress);
271+ Q_EMIT dataChanged(index(idx), index(idx), {RoleProgress});
272 }
273 }
274
275 void LauncherModel::countChanged(const QString &appId, int count)
276 {
277- int idx = findApplication(appId);
278+ const int idx = findApplication(appId);
279 if (idx >= 0) {
280 LauncherItem *item = m_list.at(idx);
281 item->setCount(count);
282 if (item->countVisible()) {
283 setAlerting(item->appId(), true);
284 }
285- Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
286 m_asAdapter->syncItems(m_list);
287+ Q_EMIT dataChanged(index(idx), index(idx), {RoleCount});
288 }
289 }
290
291@@ -367,7 +375,7 @@
292 if (countVisible) {
293 setAlerting(item->appId(), true);
294 }
295- Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCountVisible);
296+ Q_EMIT dataChanged(index(idx), index(idx), {RoleCountVisible});
297
298 // If countVisible goes to false, and the item is neither pinned nor recent we can drop it
299 if (!countVisible && !item->pinned() && !item->recent()) {
300@@ -408,7 +416,8 @@
301 item->setName(desktopFile.displayName());
302 item->setIcon(desktopFile.icon());
303 item->setPinned(item->pinned()); // update pinned text if needed
304- Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleName << RoleIcon);
305+ item->setRunning(item->running());
306+ Q_EMIT dataChanged(index(idx), index(idx), {RoleName, RoleIcon, RoleRunning});
307 }
308 }
309
310@@ -426,7 +435,7 @@
311
312 // Now walk through settings and see if we need to add something
313 for (int settingsIndex = 0; settingsIndex < m_settings->storedApplications().count(); ++settingsIndex) {
314- QString entry = m_settings->storedApplications().at(settingsIndex);
315+ const QString entry = m_settings->storedApplications().at(settingsIndex);
316 int itemIndex = -1;
317 for (int i = 0; i < m_list.count(); ++i) {
318 if (m_list.at(i)->appId() == entry) {
319@@ -499,25 +508,27 @@
320 return;
321 }
322
323- int itemIndex = findApplication(app->appId());
324+ const int itemIndex = findApplication(app->appId());
325 if (itemIndex != -1) {
326 LauncherItem *item = m_list.at(itemIndex);
327 if (!item->recent()) {
328 item->setRecent(true);
329- m_asAdapter->syncItems(m_list);
330- Q_EMIT dataChanged(index(itemIndex), index(itemIndex), QVector<int>() << RoleRecent);
331+ Q_EMIT dataChanged(index(itemIndex), index(itemIndex), {RoleRecent});
332 }
333- // Shall we paint some running/recent app highlight? If yes, do it here.
334+ item->setRunning(true);
335+ // TODO Shall we paint some running/recent app highlight? If yes, do it here.
336 } else {
337 LauncherItem *item = new LauncherItem(app->appId(), app->name(), app->icon().toString(), this);
338 item->setRecent(true);
339+ item->setRunning(true);
340 item->setFocused(app->focused());
341
342 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
343 m_list.append(item);
344 endInsertRows();
345- m_asAdapter->syncItems(m_list);
346 }
347+ m_asAdapter->syncItems(m_list);
348+ Q_EMIT dataChanged(index(itemIndex), index(itemIndex), {RoleRunning});
349 }
350
351 void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
352@@ -532,25 +543,35 @@
353 }
354 }
355
356- if (appIndex > -1 && !m_list.at(appIndex)->pinned()) {
357+ if (appIndex < 0) {
358+ qWarning() << Q_FUNC_INFO << "appIndex not found";
359+ return;
360+ }
361+
362+ LauncherItem * item = m_list.at(appIndex);
363+ item->setRunning(false);
364+
365+ if (!item->pinned()) {
366 beginRemoveRows(QModelIndex(), appIndex, appIndex);
367 m_list.takeAt(appIndex)->deleteLater();
368 endRemoveRows();
369 m_asAdapter->syncItems(m_list);
370+ Q_EMIT dataChanged(index(appIndex), index(appIndex), {RolePinned});
371 }
372+ Q_EMIT dataChanged(index(appIndex), index(appIndex), {RoleRunning});
373 }
374
375 void LauncherModel::focusedAppIdChanged()
376 {
377- QString appId = m_appManager->focusedApplicationId();
378+ const QString appId = m_appManager->focusedApplicationId();
379 for (int i = 0; i < m_list.count(); ++i) {
380 LauncherItem *item = m_list.at(i);
381 if (!item->focused() && item->appId() == appId) {
382 item->setFocused(true);
383- Q_EMIT dataChanged(index(i), index(i), QVector<int>() << RoleFocused);
384+ Q_EMIT dataChanged(index(i), index(i), {RoleFocused});
385 } else if (item->focused() && item->appId() != appId) {
386 item->setFocused(false);
387- Q_EMIT dataChanged(index(i), index(i), QVector<int>() << RoleFocused);
388+ Q_EMIT dataChanged(index(i), index(i), {RoleFocused});
389 }
390 }
391 }
392
393=== modified file 'plugins/Unity/Launcher/quicklistentry.cpp'
394--- plugins/Unity/Launcher/quicklistentry.cpp 2014-08-26 11:34:22 +0000
395+++ plugins/Unity/Launcher/quicklistentry.cpp 2015-07-29 12:33:59 +0000
396@@ -1,4 +1,4 @@
397-/* Copyright (C) 2013 Canonical, Ltd.
398+/* Copyright (C) 2013, 2015 Canonical, Ltd.
399 *
400 * Authors:
401 * Michael Zanetti <michael.zanetti@canonical.com>
402@@ -20,7 +20,6 @@
403
404 QuickListEntry::QuickListEntry()
405 {
406-
407 }
408
409 QString QuickListEntry::actionId() const
410@@ -57,3 +56,8 @@
411 {
412 return !m_actionId.isEmpty();
413 }
414+
415+bool QuickListEntry::operator==(const QuickListEntry &other)
416+{
417+ return !other.actionId().isEmpty() && other.actionId() == m_actionId;
418+}
419
420=== modified file 'plugins/Unity/Launcher/quicklistentry.h'
421--- plugins/Unity/Launcher/quicklistentry.h 2014-08-26 11:34:22 +0000
422+++ plugins/Unity/Launcher/quicklistentry.h 2015-07-29 12:33:59 +0000
423@@ -1,4 +1,4 @@
424-/* Copyright (C) 2013 Canonical, Ltd.
425+/* Copyright (C) 2013, 2015 Canonical, Ltd.
426 *
427 * Authors:
428 * Michael Zanetti <michael.zanetti@canonical.com>
429@@ -37,6 +37,8 @@
430
431 bool clickable() const;
432
433+ bool operator==(const QuickListEntry & other);
434+
435 private:
436 QString m_actionId;
437 QString m_text;
438
439=== modified file 'plugins/Unity/Launcher/quicklistmodel.cpp'
440--- plugins/Unity/Launcher/quicklistmodel.cpp 2013-10-02 11:00:53 +0000
441+++ plugins/Unity/Launcher/quicklistmodel.cpp 2015-07-29 12:33:59 +0000
442@@ -1,5 +1,5 @@
443 /*
444- * Copyright 2013 Canonical Ltd.
445+ * Copyright 2013, 2015 Canonical Ltd.
446 *
447 * This program is free software; you can redistribute it and/or modify
448 * it under the terms of the GNU Lesser General Public License as published by
449@@ -48,6 +48,17 @@
450 }
451 }
452
453+void QuickListModel::removeAction(const QuickListEntry &entry)
454+{
455+ const int start = m_list.indexOf(entry);
456+ if (start > -1) {
457+ beginRemoveRows(QModelIndex(), start, start);
458+ m_list.removeOne(entry);
459+ Q_EMIT dataChanged(index(start), index(start));
460+ endRemoveRows();
461+ }
462+}
463+
464 QuickListEntry QuickListModel::get(int index) const
465 {
466 return m_list.at(index);
467
468=== modified file 'plugins/Unity/Launcher/quicklistmodel.h'
469--- plugins/Unity/Launcher/quicklistmodel.h 2015-04-30 09:31:51 +0000
470+++ plugins/Unity/Launcher/quicklistmodel.h 2015-07-29 12:33:59 +0000
471@@ -1,5 +1,5 @@
472 /*
473- * Copyright 2013 Canonical Ltd.
474+ * Copyright 201, 2015 Canonical Ltd.
475 *
476 * This program is free software; you can redistribute it and/or modify
477 * it under the terms of the GNU Lesser General Public License as published by
478@@ -46,6 +46,8 @@
479 */
480 void updateAction(const QuickListEntry &entry);
481
482+ void removeAction(const QuickListEntry &entry);
483+
484 QuickListEntry get(int index) const;
485
486 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
487
488=== modified file 'qml/Launcher/LauncherPanel.qml'
489--- qml/Launcher/LauncherPanel.qml 2015-07-29 12:33:59 +0000
490+++ qml/Launcher/LauncherPanel.qml 2015-07-29 12:33:59 +0000
491@@ -15,7 +15,7 @@
492 */
493
494 import QtQuick 2.3
495-import Ubuntu.Components 1.1
496+import Ubuntu.Components 1.2
497 import Ubuntu.Components.ListItems 1.0 as ListItems
498 import Unity.Launcher 0.1
499 import Ubuntu.Components.Popups 0.1
500@@ -358,6 +358,7 @@
501 MouseArea {
502 id: dndArea
503 objectName: "dndArea"
504+ acceptedButtons: Qt.LeftButton | Qt.RightButton
505 anchors {
506 fill: parent
507 topMargin: launcherListView.topMargin
508@@ -369,7 +370,7 @@
509 property int draggedIndex: -1
510 property var selectedItem
511 property bool preDragging: false
512- property bool dragging: selectedItem !== undefined && selectedItem !== null && selectedItem.dragging
513+ property bool dragging: !!selectedItem && selectedItem.dragging
514 property bool postDragging: false
515 property int startX
516 property int startY
517@@ -387,6 +388,15 @@
518 return;
519 }
520
521+ if (mouse.button & Qt.RightButton) { // context menu
522+ // Opening QuickList
523+ quickList.item = clickedItem;
524+ quickList.model = launcherListView.model.get(index).quickList;
525+ quickList.appId = launcherListView.model.get(index).appId;
526+ quickList.state = "open";
527+ return
528+ }
529+
530 // First/last item do the scrolling at more than 12 degrees
531 if (index == 0 || index == launcherListView.count - 1) {
532 if (clickedItem.angle > 12) {
533@@ -580,7 +590,7 @@
534 id: quickListShape
535 objectName: "quickListShape"
536 anchors.fill: quickList
537- opacity: quickList.state === "open" ? 0.96 : 0
538+ opacity: quickList.state === "open" ? 0.8 : 0
539 visible: opacity > 0
540 rotation: root.rotation
541
542@@ -592,15 +602,15 @@
543
544 Image {
545 anchors {
546- left: parent.left
547- leftMargin: quickList.item !== undefined ? (quickList.item.width - units.gu(1)) / 2 - width / 2 : 0
548+ right: parent.left
549+ rightMargin: -units.dp(4)
550 verticalCenter: parent.verticalCenter
551- verticalCenterOffset: (parent.height / 2 + units.dp(3)) * (quickList.offset > 0 ? 1 : -1) * (root.inverted ? 1 : -1)
552+ verticalCenterOffset: -quickList.offset * (root.inverted ? -1 : 1)
553 }
554 height: units.gu(1)
555 width: units.gu(2)
556 source: "graphics/quicklist_tooltip.png"
557- rotation: (quickList.offset > 0 ? 0 : 180) + (root.inverted ? 0 : 180)
558+ rotation: 90
559 }
560
561 InverseMouseArea {
562@@ -623,11 +633,11 @@
563 height: quickListColumn.height
564 visible: quickListShape.visible
565 anchors {
566- left: root.inverted ? undefined : parent.left
567- right: root.inverted ? parent.right : undefined
568+ left: root.inverted ? undefined : parent.right
569+ right: root.inverted ? parent.left : undefined
570 margins: units.gu(1)
571 }
572- y: itemCenter + offset
573+ y: itemCenter - (height / 2) + offset
574 rotation: root.rotation
575
576 property var model
577@@ -636,9 +646,8 @@
578
579 // internal
580 property int itemCenter: item ? root.mapFromItem(quickList.item).y + (item.height / 2) : units.gu(1)
581- property int offset: item !== undefined ? (itemCenter + (item.height/2) + height + units.gu(1) > parent.height ?
582- -(item.height/2) - height - units.gu(.5) :
583- (item.height/2) + units.gu(.5)) : 0
584+ property int offset: itemCenter + (height/2) + units.gu(1) > parent.height ? -itemCenter - (height/2) - units.gu(1) + parent.height :
585+ itemCenter - (height/2) < units.gu(1) ? (height/2) - itemCenter + units.gu(1) : 0
586
587 Column {
588 id: quickListColumn
589
590=== modified file 'tests/mocks/Unity/Launcher/MockLauncherItem.cpp'
591--- tests/mocks/Unity/Launcher/MockLauncherItem.cpp 2015-07-29 12:33:59 +0000
592+++ tests/mocks/Unity/Launcher/MockLauncherItem.cpp 2015-07-29 12:33:59 +0000
593@@ -40,7 +40,10 @@
594 m_alerting(false),
595 m_quickList(new MockQuickListModel(this))
596 {
597+}
598
599+MockLauncherItem::~MockLauncherItem()
600+{
601 }
602
603 QString MockLauncherItem::appId() const
604
605=== modified file 'tests/mocks/Unity/Launcher/MockLauncherItem.h'
606--- tests/mocks/Unity/Launcher/MockLauncherItem.h 2015-07-29 12:33:59 +0000
607+++ tests/mocks/Unity/Launcher/MockLauncherItem.h 2015-07-29 12:33:59 +0000
608@@ -31,6 +31,7 @@
609 Q_OBJECT
610 public:
611 MockLauncherItem(const QString &appId, const QString& desktopFile, const QString& name, const QString& icon, QObject* parent = 0);
612+ ~MockLauncherItem();
613
614 QString appId() const override;
615 QString desktopFile() const;
616
617=== modified file 'tests/mocks/Unity/Launcher/MockLauncherModel.cpp'
618--- tests/mocks/Unity/Launcher/MockLauncherModel.cpp 2015-07-29 12:33:59 +0000
619+++ tests/mocks/Unity/Launcher/MockLauncherModel.cpp 2015-07-29 12:33:59 +0000
620@@ -44,6 +44,7 @@
621 item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
622 item->setCount(1);
623 item->setCountVisible(true);
624+ item->setRunning(true);
625 item->setAlerting(false);
626 m_list.append(item);
627 item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
628@@ -194,7 +195,9 @@
629 int index = findApp(appId);
630 if (index >= 0) {
631 beginRemoveRows(QModelIndex(), index, 0);
632- m_list.takeAt(index)->deleteLater();
633+ MockLauncherItem * item = m_list.takeAt(index);
634+ item->setRunning(false);
635+ item->deleteLater();
636 endRemoveRows();
637 }
638 }
639
640=== modified file 'tests/plugins/Unity/Launcher/launchermodeltest.cpp'
641--- tests/plugins/Unity/Launcher/launchermodeltest.cpp 2015-07-29 12:33:59 +0000
642+++ tests/plugins/Unity/Launcher/launchermodeltest.cpp 2015-07-29 12:33:59 +0000
643@@ -68,6 +68,7 @@
644 Q_OBJECT
645 public:
646 MockAppManager(QObject *parent = 0): ApplicationManagerInterface(parent) {}
647+ ~MockAppManager() {}
648 int rowCount(const QModelIndex &) const override { return m_list.count(); }
649 QVariant data(const QModelIndex &, int ) const override { return QVariant(); }
650 QString focusedApplicationId() const override {
651@@ -325,6 +326,46 @@
652 QCOMPARE(launcherModel->get(0)->appId(), QLatin1String("abs-icon"));
653 }
654
655+ void testQuitMenuItem() {
656+ // we have 2 apps running, both should have the Quit action in its quick list
657+ QCOMPARE(launcherModel->rowCount(), 2);
658+
659+ // stop the second one keeping it pinned so that it doesn't go away
660+ launcherModel->pin("no-icon");
661+ appManager->stopApplication("no-icon");
662+
663+ // find the first Quit item, should be there
664+ QuickListModel *model = qobject_cast<QuickListModel*>(launcherModel->get(0)->quickList());
665+ int quitActionIndex = -1;
666+ for (int i = 0; i < model->rowCount(); ++i) {
667+ if (model->get(i).actionId() == "stop_item") {
668+ quitActionIndex = i;
669+ break;
670+ }
671+ }
672+ QVERIFY(quitActionIndex >= 0);
673+
674+ // find the second Quit item, should NOT be there, the app is stopped
675+ QuickListModel *model2 = qobject_cast<QuickListModel*>(launcherModel->get(1)->quickList());
676+ int quitActionIndex2 = -1;
677+ for (int i = 0; i < model2->rowCount(); ++i) {
678+ if (model2->get(i).actionId() == "stop_item") {
679+ quitActionIndex2 = i;
680+ break;
681+ }
682+ }
683+ QVERIFY(quitActionIndex2 == -1);
684+
685+ // trigger the first quit item quicklist action
686+ launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), quitActionIndex);
687+ // first app should be gone...
688+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
689+ // ... the second app (now at index 0) should still be there, pinned and stopped
690+ QCOMPARE(launcherModel->get(0)->appId(), QStringLiteral("no-icon"));
691+ QCOMPARE(launcherModel->get(0)->pinned(), true);
692+ QCOMPARE(launcherModel->get(0)->running(), false);
693+ }
694+
695 void testGetUrlForAppId() {
696 QCOMPARE(launcherModel->getUrlForAppId(QString()), QString());
697 QCOMPARE(launcherModel->getUrlForAppId(""), QString());
698
699=== modified file 'tests/qmltests/Launcher/tst_Launcher.qml'
700--- tests/qmltests/Launcher/tst_Launcher.qml 2015-07-29 12:33:59 +0000
701+++ tests/qmltests/Launcher/tst_Launcher.qml 2015-07-29 12:33:59 +0000
702@@ -197,7 +197,7 @@
703 startX+units.gu(8), startY);
704
705 var panel = findChild(launcher, "launcherPanel");
706- verify(panel != undefined);
707+ verify(!!panel);
708
709 // wait until it gets fully extended
710 tryCompare(panel, "x", 0);
711@@ -208,7 +208,7 @@
712 mouseMove(root, 1, root.height / 2);
713
714 var panel = findChild(launcher, "launcherPanel");
715- verify(panel != undefined);
716+ verify(!!panel);
717
718 // wait until it gets fully extended
719 tryCompare(panel, "x", 0);
720@@ -248,7 +248,7 @@
721 waitUntilLauncherDisappears();
722
723 var panel = findChild(launcher, "launcherPanel")
724- verify(panel != undefined)
725+ verify(!!panel)
726
727 // it starts out hidden just left of the left launcher edge
728 compare(panel.x, -panel.width)
729@@ -286,7 +286,7 @@
730
731 var appIcon = findChild(launcher, "launcherDelegate0");
732
733- verify(appIcon != undefined);
734+ verify(!!appIcon);
735
736 if (data.mouse) {
737 mouseClick(appIcon);
738@@ -309,7 +309,7 @@
739 dragLauncherIntoView()
740
741 var dashIcon = findChild(launcher, "dashItem")
742- verify(dashIcon != undefined)
743+ verify(!!dashIcon)
744
745 mouseClick(dashIcon)
746
747@@ -599,7 +599,7 @@
748
749 // Doing longpress
750 mousePress(draggedItem);
751- tryCompare(quickListShape, "opacity", 0.96);
752+ tryCompare(quickListShape, "opacity", 0.8);
753 mouseRelease(draggedItem);
754
755 verify(quickList.y >= units.gu(1));
756@@ -684,9 +684,31 @@
757 tryCompare(quickList, "state", "");
758 }
759
760+ function test_quickListMenuOnRMB() {
761+ dragLauncherIntoView();
762+ var clickedItem = findChild(launcher, "launcherDelegate5")
763+ var quickList = findChild(launcher, "quickList")
764+ var quickListShape = findChild(launcher, "quickListShape")
765+ var dndArea = findChild(launcher, "dndArea");
766+
767+ // Initial state
768+ tryCompare(quickListShape, "visible", false)
769+
770+ // Doing RMB click
771+ mouseClick(clickedItem, clickedItem.width / 2, clickedItem.height / 2, Qt.RightButton)
772+ tryCompare(quickListShape, "visible", true)
773+ verify(quickList, "state", "open")
774+ verify(dndArea, "dragging", false)
775+
776+ // Click somewhere in the empty space to dismiss the quicklist
777+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
778+ tryCompare(quickListShape, "visible", false);
779+ verify(quickList, "state", "")
780+ }
781+
782 function test_revealByHover() {
783 var panel = findChild(launcher, "launcherPanel");
784- verify(panel != undefined);
785+ verify(!!panel);
786
787 revealByHover();
788 tryCompare(launcher, "state", "visibleTemporary");

Subscribers

People subscribed via source and target branches