Merge lp:~lukas-kde/unity8/closeAppsFromQuicklist into lp:unity8
- closeAppsFromQuicklist
- Merge into trunk
Status: | Superseded | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~lukas-kde/unity8/closeAppsFromQuicklist | ||||
Merge into: | lp:unity8 | ||||
Diff against target: |
736 lines (+184/-59) 16 files modified
plugins/Unity/Launcher/asadapter.cpp (+2/-2) plugins/Unity/Launcher/asadapter.h (+1/-1) plugins/Unity/Launcher/launcheritem.cpp (+10/-0) plugins/Unity/Launcher/launcheritem.h (+1/-0) plugins/Unity/Launcher/launchermodel.cpp (+38/-22) 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 (+24/-15) src/MouseTouchAdaptor.cpp (+6/-6) 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 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Daniel d'Andrada (community) | Needs Fixing | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email:
|
This proposal has been superseded by 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
- 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
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Daniel d'Andrada (dandrader) wrote : | # |
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.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Daniel d'Andrada (dandrader) wrote : | # |
In qml/Launcher/
"""
- __foregroundColor: "black"
+ __foregroundColor: Theme.palette.
"""
"Theme" is deprecated. Please use the context variable "theme" instead. We have tons of warnings on this already. See https:/
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Daniel d'Andrada (dandrader) wrote : | # |
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.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Lukáš Tinkl (lukas-kde) wrote : | # |
> 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: launchermodelte
> 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).
- 1812. By Lukáš Tinkl
-
address MP issues
- 1813. By Lukáš Tinkl
-
[ Albert Astals Cid ]
* Refactor QmlTest.cmake module so that all tests can go through it.
Also a bit of cleanup around tests.
* Support fallback images for dash card and preview widgets (LP:
#1324142)
[ CI Train Bot ]
* New rebuild forced.
* Resync trunk.
[ Daniel d'Andrada ]
* Fix Shell tests
[ Josh Arenson ]
* Refactor greeter emulator to unlock the greeter via dbus
[ Lukáš Tinkl ]
* respect target window's devicePixelRatio in MouseTouchAdaptor
[ Michael Zanetti ]
* Refactor QmlTest.cmake module so that all tests can go through it.
Also a bit of cleanup around tests.
[ Michał Sawicz ]
* Refactor QmlTest.cmake module so that all tests can go through it.
Also a bit of cleanup around tests.
[ handsome_feng ]
* Forbid closing apps during the edge gesture. (LP: #1445572)
* Removed the horizonal rule on pin unlock screen. (LP: #1368798)
[ handsome_feng<email address hidden> ]
* Forbid closing apps during the edge gesture. (LP: #1445572)
* No-change rebuild against Qt 5.4.2.
[ Michał Sawicz ]
* Implement full-shell rotation (LP: #1210199)
[ CI Train Bot ]
* New rebuild forced.
* Resync trunk.
[ Daniel d'Andrada ]
* Implemented autopilot-test and fake-sensors for shell-rotation.
[ Mirco Müller ]
* Implemented autopilot-test and fake-sensors for shell-rotation. - 1814. By Lukáš Tinkl
-
restore light menu color, arrow pointing always towards the launcher
- 1815. By Lukáš Tinkl
-
calculate the arrow's offset correctly
- 1816. By Lukáš Tinkl
-
implement missing bits of the "running" interfaces
- 1817. By Lukáš Tinkl
-
fix white-on-white text in the quicklist
- 1818. By Lukáš Tinkl
-
emit dataChanged
- 1819. By Lukáš Tinkl
-
emit dataChanged(
Running) correctly when the app appears/disappears - 1820. By Lukáš Tinkl
-
no need to emit RoleRunning twice
- 1821. By Lukáš Tinkl
-
sync items when changing running state; set the initial running state in refresh()
- 1822. By Lukáš Tinkl
-
set the running status also for the already pinned apps
- 1823. By Lukáš Tinkl
-
sset the item to "running" in all cases when it appears
- 1824. By Lukáš Tinkl
-
merge and fix conflict
- 1825. By Lukáš Tinkl
-
drop dbusinterface changes
- 1826. By Lukáš Tinkl
-
rebase on top of lp:~macslow/unity8/use-set-progress-api
- 1827. By Lukáš Tinkl
-
re-merge again on top of use-set-
progress- api to fix failing QML tests
Unmerged revisions
Preview Diff
1 | === modified file 'plugins/Unity/Launcher/asadapter.cpp' |
2 | --- plugins/Unity/Launcher/asadapter.cpp 2015-02-11 14:13:26 +0000 |
3 | +++ plugins/Unity/Launcher/asadapter.cpp 2015-06-18 11:46:46 +0000 |
4 | @@ -34,12 +34,12 @@ |
5 | m_accounts->deleteLater(); |
6 | } |
7 | |
8 | -void ASAdapter::syncItems(QList<LauncherItem *> m_list) |
9 | +void ASAdapter::syncItems(const QList<LauncherItem*> &list) |
10 | { |
11 | if (m_accounts && !m_user.isEmpty()) { |
12 | QList<QVariantMap> items; |
13 | |
14 | - Q_FOREACH(LauncherItem *item, m_list) { |
15 | + Q_FOREACH(LauncherItem *item, list) { |
16 | items << itemToVariant(item); |
17 | } |
18 | |
19 | |
20 | === modified file 'plugins/Unity/Launcher/asadapter.h' |
21 | --- plugins/Unity/Launcher/asadapter.h 2015-02-11 14:13:26 +0000 |
22 | +++ plugins/Unity/Launcher/asadapter.h 2015-06-18 11:46:46 +0000 |
23 | @@ -29,7 +29,7 @@ |
24 | ASAdapter(); |
25 | ~ASAdapter(); |
26 | |
27 | - void syncItems(QList<LauncherItem*> m_list); |
28 | + void syncItems(const QList<LauncherItem*> &list); |
29 | |
30 | private: |
31 | QVariantMap itemToVariant(LauncherItem *item) const; |
32 | |
33 | === modified file 'plugins/Unity/Launcher/launcheritem.cpp' |
34 | --- plugins/Unity/Launcher/launcheritem.cpp 2014-11-19 17:43:09 +0000 |
35 | +++ plugins/Unity/Launcher/launcheritem.cpp 2015-06-18 11:46:46 +0000 |
36 | @@ -40,10 +40,15 @@ |
37 | nameAction.setActionId("launch_item"); |
38 | nameAction.setText(m_name); |
39 | m_quickList->appendAction(nameAction); |
40 | + |
41 | QuickListEntry pinningAction; |
42 | pinningAction.setActionId("pin_item"); |
43 | pinningAction.setText(gettext("Pin shortcut")); |
44 | m_quickList->appendAction(pinningAction); |
45 | + |
46 | + m_quitAction.setActionId("stop_item"); |
47 | + m_quitAction.setIcon("application-exit"); |
48 | + m_quitAction.setText(gettext("Quit")); |
49 | } |
50 | |
51 | QString LauncherItem::appId() const |
52 | @@ -110,6 +115,11 @@ |
53 | { |
54 | if (m_running != running) { |
55 | m_running = running; |
56 | + if (m_running) { // add the quit action |
57 | + m_quickList->appendAction(m_quitAction); |
58 | + } else { // remove the quit action |
59 | + m_quickList->removeAction(m_quitAction); |
60 | + } |
61 | Q_EMIT runningChanged(running); |
62 | } |
63 | } |
64 | |
65 | === modified file 'plugins/Unity/Launcher/launcheritem.h' |
66 | --- plugins/Unity/Launcher/launcheritem.h 2014-09-02 18:22:37 +0000 |
67 | +++ plugins/Unity/Launcher/launcheritem.h 2015-06-18 11:46:46 +0000 |
68 | @@ -70,6 +70,7 @@ |
69 | bool m_countVisible; |
70 | bool m_focused; |
71 | QuickListModel *m_quickList; |
72 | + QuickListEntry m_quitAction; |
73 | |
74 | friend class LauncherModel; |
75 | }; |
76 | |
77 | === modified file 'plugins/Unity/Launcher/launchermodel.cpp' |
78 | --- plugins/Unity/Launcher/launchermodel.cpp 2015-03-30 17:36:42 +0000 |
79 | +++ plugins/Unity/Launcher/launchermodel.cpp 2015-06-18 11:46:46 +0000 |
80 | @@ -82,9 +82,14 @@ |
81 | return item->progress(); |
82 | case RoleFocused: |
83 | return item->focused(); |
84 | + case RoleRunning: |
85 | + return item->running(); |
86 | + default: |
87 | + qWarning() << Q_FUNC_INFO << "missing role, implement me"; |
88 | + return QVariant(); |
89 | } |
90 | |
91 | - return QVariant(); |
92 | + return LauncherModelInterface::data(index, role); |
93 | } |
94 | |
95 | unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) const |
96 | @@ -134,7 +139,7 @@ |
97 | if (index == -1 || index == currentIndex) { |
98 | m_list.at(currentIndex)->setPinned(true); |
99 | QModelIndex modelIndex = this->index(currentIndex); |
100 | - Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RolePinned); |
101 | + Q_EMIT dataChanged(modelIndex, modelIndex, {RolePinned}); |
102 | } else { |
103 | move(currentIndex, index); |
104 | // move() will store the list to the backend itself, so just exit at this point. |
105 | @@ -147,7 +152,7 @@ |
106 | |
107 | DesktopFileHandler desktopFile(appId); |
108 | if (!desktopFile.isValid()) { |
109 | - qWarning() << "Can't pin this application, there is no .destkop file available."; |
110 | + qWarning() << "Can't pin this application, there is no .desktop file available."; |
111 | return; |
112 | } |
113 | |
114 | @@ -172,7 +177,7 @@ |
115 | |
116 | void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex) |
117 | { |
118 | - int index = findApplication(appId); |
119 | + const int index = findApplication(appId); |
120 | if (index < 0) { |
121 | return; |
122 | } |
123 | @@ -180,7 +185,7 @@ |
124 | LauncherItem *item = m_list.at(index); |
125 | QuickListModel *model = qobject_cast<QuickListModel*>(item->quickList()); |
126 | if (model) { |
127 | - QString actionId = model->get(actionIndex).actionId(); |
128 | + const QString actionId = model->get(actionIndex).actionId(); |
129 | |
130 | // Check if this is one of the launcher actions we handle ourselves |
131 | if (actionId == "pin_item") { |
132 | @@ -191,7 +196,8 @@ |
133 | } |
134 | } else if (actionId == "launch_item") { |
135 | QDesktopServices::openUrl(getUrlForAppId(appId)); |
136 | - |
137 | + } else if (actionId == "stop_item") { // Quit |
138 | + m_appManager->stopApplication(appId); |
139 | // Nope, we don't know this action, let the backend forward it to the application |
140 | } else { |
141 | // TODO: forward quicklist action to app, possibly via m_dbusIface |
142 | @@ -272,6 +278,7 @@ |
143 | |
144 | void LauncherModel::setOnlyPinned(bool onlyPinned) { |
145 | Q_UNUSED(onlyPinned); |
146 | + // FIXME implement showing only pinned apps |
147 | qWarning() << "This launcher implementation does not support showing only pinned apps"; |
148 | } |
149 | |
150 | @@ -289,7 +296,7 @@ |
151 | |
152 | void LauncherModel::unpin(const QString &appId) |
153 | { |
154 | - int index = findApplication(appId); |
155 | + const int index = findApplication(appId); |
156 | if (index < 0) { |
157 | return; |
158 | } |
159 | @@ -298,7 +305,7 @@ |
160 | if (m_list.at(index)->pinned()) { |
161 | m_list.at(index)->setPinned(false); |
162 | QModelIndex modelIndex = this->index(index); |
163 | - Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RolePinned); |
164 | + Q_EMIT dataChanged(modelIndex, modelIndex, {RolePinned}); |
165 | } |
166 | } else { |
167 | beginRemoveRows(QModelIndex(), index, index); |
168 | @@ -320,21 +327,21 @@ |
169 | |
170 | void LauncherModel::progressChanged(const QString &appId, int progress) |
171 | { |
172 | - int idx = findApplication(appId); |
173 | + const int idx = findApplication(appId); |
174 | if (idx >= 0) { |
175 | LauncherItem *item = m_list.at(idx); |
176 | item->setProgress(progress); |
177 | - Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleProgress); |
178 | + Q_EMIT dataChanged(index(idx), index(idx), {RoleProgress}); |
179 | } |
180 | } |
181 | |
182 | void LauncherModel::countChanged(const QString &appId, int count) |
183 | { |
184 | - int idx = findApplication(appId); |
185 | + const int idx = findApplication(appId); |
186 | if (idx >= 0) { |
187 | LauncherItem *item = m_list.at(idx); |
188 | item->setCount(count); |
189 | - Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount); |
190 | + Q_EMIT dataChanged(index(idx), index(idx), {RoleCount}); |
191 | m_asAdapter->syncItems(m_list); |
192 | } |
193 | } |
194 | @@ -345,7 +352,7 @@ |
195 | if (idx >= 0) { |
196 | LauncherItem *item = m_list.at(idx); |
197 | item->setCountVisible(countVisible); |
198 | - Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCountVisible); |
199 | + Q_EMIT dataChanged(index(idx), index(idx), {RoleCountVisible}); |
200 | |
201 | // If countVisible goes to false, and the item is neither pinned nor recent we can drop it |
202 | if (!countVisible && !item->pinned() && !item->recent()) { |
203 | @@ -387,7 +394,7 @@ |
204 | item->setName(desktopFile.displayName()); |
205 | item->setIcon(desktopFile.icon()); |
206 | item->setPinned(item->pinned()); // update pinned text if needed |
207 | - Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleName << RoleIcon); |
208 | + Q_EMIT dataChanged(index(idx), index(idx), {RoleName, RoleIcon}); |
209 | } |
210 | } |
211 | |
212 | @@ -405,7 +412,7 @@ |
213 | |
214 | // Now walk through settings and see if we need to add something |
215 | for (int settingsIndex = 0; settingsIndex < m_settings->storedApplications().count(); ++settingsIndex) { |
216 | - QString entry = m_settings->storedApplications().at(settingsIndex); |
217 | + const QString entry = m_settings->storedApplications().at(settingsIndex); |
218 | int itemIndex = -1; |
219 | for (int i = 0; i < m_list.count(); ++i) { |
220 | if (m_list.at(i)->appId() == entry) { |
221 | @@ -468,18 +475,19 @@ |
222 | return; |
223 | } |
224 | |
225 | - int itemIndex = findApplication(app->appId()); |
226 | + const int itemIndex = findApplication(app->appId()); |
227 | if (itemIndex != -1) { |
228 | LauncherItem *item = m_list.at(itemIndex); |
229 | if (!item->recent()) { |
230 | item->setRecent(true); |
231 | m_asAdapter->syncItems(m_list); |
232 | - Q_EMIT dataChanged(index(itemIndex), index(itemIndex), QVector<int>() << RoleRecent); |
233 | + Q_EMIT dataChanged(index(itemIndex), index(itemIndex), {RoleRecent}); |
234 | } |
235 | - // Shall we paint some running/recent app highlight? If yes, do it here. |
236 | + // TODO Shall we paint some running/recent app highlight? If yes, do it here. |
237 | } else { |
238 | LauncherItem *item = new LauncherItem(app->appId(), app->name(), app->icon().toString(), this); |
239 | item->setRecent(true); |
240 | + item->setRunning(true); |
241 | item->setFocused(app->focused()); |
242 | |
243 | beginInsertRows(QModelIndex(), m_list.count(), m_list.count()); |
244 | @@ -501,7 +509,15 @@ |
245 | } |
246 | } |
247 | |
248 | - if (appIndex > -1 && !m_list.at(appIndex)->pinned()) { |
249 | + if (appIndex < 0) { |
250 | + qWarning() << Q_FUNC_INFO << "appIndex not found"; |
251 | + return; |
252 | + } |
253 | + |
254 | + LauncherItem * item = m_list.at(appIndex); |
255 | + item->setRunning(false); |
256 | + |
257 | + if (!item->pinned()) { |
258 | beginRemoveRows(QModelIndex(), appIndex, appIndex); |
259 | m_list.takeAt(appIndex)->deleteLater(); |
260 | endRemoveRows(); |
261 | @@ -511,15 +527,15 @@ |
262 | |
263 | void LauncherModel::focusedAppIdChanged() |
264 | { |
265 | - QString appId = m_appManager->focusedApplicationId(); |
266 | + const QString appId = m_appManager->focusedApplicationId(); |
267 | for (int i = 0; i < m_list.count(); ++i) { |
268 | LauncherItem *item = m_list.at(i); |
269 | if (!item->focused() && item->appId() == appId) { |
270 | item->setFocused(true); |
271 | - Q_EMIT dataChanged(index(i), index(i), QVector<int>() << RoleFocused); |
272 | + Q_EMIT dataChanged(index(i), index(i), {RoleFocused}); |
273 | } else if (item->focused() && item->appId() != appId) { |
274 | item->setFocused(false); |
275 | - Q_EMIT dataChanged(index(i), index(i), QVector<int>() << RoleFocused); |
276 | + Q_EMIT dataChanged(index(i), index(i), {RoleFocused}); |
277 | } |
278 | } |
279 | } |
280 | |
281 | === modified file 'plugins/Unity/Launcher/quicklistentry.cpp' |
282 | --- plugins/Unity/Launcher/quicklistentry.cpp 2014-08-26 11:34:22 +0000 |
283 | +++ plugins/Unity/Launcher/quicklistentry.cpp 2015-06-18 11:46:46 +0000 |
284 | @@ -1,4 +1,4 @@ |
285 | -/* Copyright (C) 2013 Canonical, Ltd. |
286 | +/* Copyright (C) 2013, 2015 Canonical, Ltd. |
287 | * |
288 | * Authors: |
289 | * Michael Zanetti <michael.zanetti@canonical.com> |
290 | @@ -20,7 +20,6 @@ |
291 | |
292 | QuickListEntry::QuickListEntry() |
293 | { |
294 | - |
295 | } |
296 | |
297 | QString QuickListEntry::actionId() const |
298 | @@ -57,3 +56,8 @@ |
299 | { |
300 | return !m_actionId.isEmpty(); |
301 | } |
302 | + |
303 | +bool QuickListEntry::operator==(const QuickListEntry &other) |
304 | +{ |
305 | + return !other.actionId().isEmpty() && other.actionId() == m_actionId; |
306 | +} |
307 | |
308 | === modified file 'plugins/Unity/Launcher/quicklistentry.h' |
309 | --- plugins/Unity/Launcher/quicklistentry.h 2014-08-26 11:34:22 +0000 |
310 | +++ plugins/Unity/Launcher/quicklistentry.h 2015-06-18 11:46:46 +0000 |
311 | @@ -1,4 +1,4 @@ |
312 | -/* Copyright (C) 2013 Canonical, Ltd. |
313 | +/* Copyright (C) 2013, 2015 Canonical, Ltd. |
314 | * |
315 | * Authors: |
316 | * Michael Zanetti <michael.zanetti@canonical.com> |
317 | @@ -37,6 +37,8 @@ |
318 | |
319 | bool clickable() const; |
320 | |
321 | + bool operator==(const QuickListEntry & other); |
322 | + |
323 | private: |
324 | QString m_actionId; |
325 | QString m_text; |
326 | |
327 | === modified file 'plugins/Unity/Launcher/quicklistmodel.cpp' |
328 | --- plugins/Unity/Launcher/quicklistmodel.cpp 2013-10-02 11:00:53 +0000 |
329 | +++ plugins/Unity/Launcher/quicklistmodel.cpp 2015-06-18 11:46:46 +0000 |
330 | @@ -1,5 +1,5 @@ |
331 | /* |
332 | - * Copyright 2013 Canonical Ltd. |
333 | + * Copyright 2013, 2015 Canonical Ltd. |
334 | * |
335 | * This program is free software; you can redistribute it and/or modify |
336 | * it under the terms of the GNU Lesser General Public License as published by |
337 | @@ -48,6 +48,17 @@ |
338 | } |
339 | } |
340 | |
341 | +void QuickListModel::removeAction(const QuickListEntry &entry) |
342 | +{ |
343 | + const int start = m_list.indexOf(entry); |
344 | + if (start > -1) { |
345 | + beginRemoveRows(QModelIndex(), start, start); |
346 | + m_list.removeOne(entry); |
347 | + Q_EMIT dataChanged(index(start), index(start)); |
348 | + endRemoveRows(); |
349 | + } |
350 | +} |
351 | + |
352 | QuickListEntry QuickListModel::get(int index) const |
353 | { |
354 | return m_list.at(index); |
355 | |
356 | === modified file 'plugins/Unity/Launcher/quicklistmodel.h' |
357 | --- plugins/Unity/Launcher/quicklistmodel.h 2015-04-30 09:31:51 +0000 |
358 | +++ plugins/Unity/Launcher/quicklistmodel.h 2015-06-18 11:46:46 +0000 |
359 | @@ -1,5 +1,5 @@ |
360 | /* |
361 | - * Copyright 2013 Canonical Ltd. |
362 | + * Copyright 201, 2015 Canonical Ltd. |
363 | * |
364 | * This program is free software; you can redistribute it and/or modify |
365 | * it under the terms of the GNU Lesser General Public License as published by |
366 | @@ -46,6 +46,8 @@ |
367 | */ |
368 | void updateAction(const QuickListEntry &entry); |
369 | |
370 | + void removeAction(const QuickListEntry &entry); |
371 | + |
372 | QuickListEntry get(int index) const; |
373 | |
374 | int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
375 | |
376 | === modified file 'qml/Launcher/LauncherPanel.qml' |
377 | --- qml/Launcher/LauncherPanel.qml 2015-04-17 13:31:27 +0000 |
378 | +++ qml/Launcher/LauncherPanel.qml 2015-06-18 11:46:46 +0000 |
379 | @@ -15,7 +15,7 @@ |
380 | */ |
381 | |
382 | import QtQuick 2.3 |
383 | -import Ubuntu.Components 1.1 |
384 | +import Ubuntu.Components 1.3 |
385 | import Ubuntu.Components.ListItems 1.0 as ListItems |
386 | import Unity.Launcher 0.1 |
387 | import Ubuntu.Components.Popups 0.1 |
388 | @@ -286,6 +286,7 @@ |
389 | MouseArea { |
390 | id: dndArea |
391 | objectName: "dndArea" |
392 | + acceptedButtons: Qt.LeftButton | Qt.RightButton |
393 | anchors { |
394 | fill: parent |
395 | topMargin: launcherListView.topMargin |
396 | @@ -297,7 +298,7 @@ |
397 | property int draggedIndex: -1 |
398 | property var selectedItem |
399 | property bool preDragging: false |
400 | - property bool dragging: selectedItem !== undefined && selectedItem !== null && selectedItem.dragging |
401 | + property bool dragging: !!selectedItem && selectedItem.dragging |
402 | property bool postDragging: false |
403 | property int startX |
404 | property int startY |
405 | @@ -315,6 +316,15 @@ |
406 | return; |
407 | } |
408 | |
409 | + if (mouse.button & Qt.RightButton) { // context menu |
410 | + // Opening QuickList |
411 | + quickList.item = clickedItem; |
412 | + quickList.model = launcherListView.model.get(index).quickList; |
413 | + quickList.appId = launcherListView.model.get(index).appId; |
414 | + quickList.state = "open"; |
415 | + return |
416 | + } |
417 | + |
418 | // First/last item do the scrolling at more than 12 degrees |
419 | if (index == 0 || index == launcherListView.count - 1) { |
420 | if (clickedItem.angle > 12) { |
421 | @@ -508,7 +518,7 @@ |
422 | id: quickListShape |
423 | objectName: "quickListShape" |
424 | anchors.fill: quickList |
425 | - opacity: quickList.state === "open" ? 0.96 : 0 |
426 | + opacity: quickList.state === "open" ? 0.8 : 0 |
427 | visible: opacity > 0 |
428 | rotation: root.rotation |
429 | |
430 | @@ -520,15 +530,15 @@ |
431 | |
432 | Image { |
433 | anchors { |
434 | - left: parent.left |
435 | - leftMargin: (quickList.item.width - units.gu(1)) / 2 - width / 2 |
436 | + right: parent.left |
437 | + rightMargin: -units.dp(4) |
438 | verticalCenter: parent.verticalCenter |
439 | - verticalCenterOffset: (parent.height / 2 + units.dp(3)) * (quickList.offset > 0 ? 1 : -1) * (root.inverted ? 1 : -1) |
440 | + verticalCenterOffset: -quickList.offset * (root.inverted ? 1 : -1) |
441 | } |
442 | height: units.gu(1) |
443 | width: units.gu(2) |
444 | source: "graphics/quicklist_tooltip.png" |
445 | - rotation: (quickList.offset > 0 ? 0 : 180) + (root.inverted ? 0 : 180) |
446 | + rotation: root.inverted ? 90 : 270 |
447 | } |
448 | |
449 | InverseMouseArea { |
450 | @@ -544,18 +554,18 @@ |
451 | Rectangle { |
452 | id: quickList |
453 | objectName: "quickList" |
454 | - color: "#f5f5f5" |
455 | + color: "#221e1c" |
456 | // Because we're setting left/right anchors depending on orientation, it will break the |
457 | // width setting after rotating twice. This makes sure we also re-apply width on rotation |
458 | width: root.inverted ? units.gu(30) : units.gu(30) |
459 | height: quickListColumn.height |
460 | visible: quickListShape.visible |
461 | anchors { |
462 | - left: root.inverted ? undefined : parent.left |
463 | - right: root.inverted ? parent.right : undefined |
464 | + left: root.inverted ? undefined : parent.right |
465 | + right: root.inverted ? parent.left : undefined |
466 | margins: units.gu(1) |
467 | } |
468 | - y: itemCenter + offset |
469 | + y: itemCenter - (height / 2) + offset |
470 | rotation: root.rotation |
471 | |
472 | property var model |
473 | @@ -564,9 +574,8 @@ |
474 | |
475 | // internal |
476 | property int itemCenter: item ? root.mapFromItem(quickList.item).y + (item.height / 2) : units.gu(1) |
477 | - property int offset: itemCenter + (item.height/2) + height + units.gu(1) > parent.height ? |
478 | - -(item.height/2) - height - units.gu(.5) : |
479 | - (item.height/2) + units.gu(.5) |
480 | + property int offset: itemCenter + (height/2) + units.gu(1) > parent.height ? -itemCenter - (height/2) - units.gu(1) + parent.height : |
481 | + itemCenter - (height/2) < units.gu(1) ? (height/2) - itemCenter + units.gu(1) : 0 |
482 | |
483 | Column { |
484 | id: quickListColumn |
485 | @@ -585,7 +594,7 @@ |
486 | // FIXME: This is a workaround for the theme not being context sensitive. I.e. the |
487 | // ListItems don't know that they are sitting in a themed Popover where the color |
488 | // needs to be inverted. |
489 | - __foregroundColor: "black" |
490 | + __foregroundColor: theme.palette.selected.backgroundText |
491 | |
492 | onClicked: { |
493 | if (!model.clickable) { |
494 | |
495 | === modified file 'src/MouseTouchAdaptor.cpp' |
496 | --- src/MouseTouchAdaptor.cpp 2015-03-02 12:41:17 +0000 |
497 | +++ src/MouseTouchAdaptor.cpp 2015-06-18 11:46:46 +0000 |
498 | @@ -104,10 +104,10 @@ |
499 | if (button != Qt::LeftButton) |
500 | return true; |
501 | |
502 | - QPoint windowPos(pressEvent->event_x, pressEvent->event_y); |
503 | - |
504 | QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(pressEvent->event)); |
505 | |
506 | + QPoint windowPos(pressEvent->event_x / targetWindow->devicePixelRatio(), pressEvent->event_y / targetWindow->devicePixelRatio()); |
507 | + |
508 | QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice, |
509 | false /* autoCommit */); |
510 | touchEvent.press(0 /* touchId */, windowPos); |
511 | @@ -125,10 +125,10 @@ |
512 | if (button != Qt::LeftButton) |
513 | return true; |
514 | |
515 | - QPoint windowPos(releaseEvent->event_x, releaseEvent->event_y); |
516 | - |
517 | QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(releaseEvent->event)); |
518 | |
519 | + QPoint windowPos(releaseEvent->event_x / targetWindow->devicePixelRatio(), releaseEvent->event_y / targetWindow->devicePixelRatio()); |
520 | + |
521 | QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice, |
522 | false /* autoCommit */); |
523 | touchEvent.release(0 /* touchId */, windowPos); |
524 | @@ -144,10 +144,10 @@ |
525 | return true; |
526 | } |
527 | |
528 | - QPoint windowPos(event->event_x, event->event_y); |
529 | - |
530 | QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(event->event)); |
531 | |
532 | + QPoint windowPos(event->event_x / targetWindow->devicePixelRatio(), event->event_y / targetWindow->devicePixelRatio()); |
533 | + |
534 | QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice, |
535 | false /* autoCommit */); |
536 | touchEvent.move(0 /* touchId */, windowPos); |
537 | |
538 | === modified file 'tests/mocks/Unity/Launcher/MockLauncherItem.cpp' |
539 | --- tests/mocks/Unity/Launcher/MockLauncherItem.cpp 2014-09-03 13:30:52 +0000 |
540 | +++ tests/mocks/Unity/Launcher/MockLauncherItem.cpp 2015-06-18 11:46:46 +0000 |
541 | @@ -39,7 +39,10 @@ |
542 | m_focused(false), |
543 | m_quickList(new MockQuickListModel(this)) |
544 | { |
545 | +} |
546 | |
547 | +MockLauncherItem::~MockLauncherItem() |
548 | +{ |
549 | } |
550 | |
551 | QString MockLauncherItem::appId() const |
552 | |
553 | === modified file 'tests/mocks/Unity/Launcher/MockLauncherItem.h' |
554 | --- tests/mocks/Unity/Launcher/MockLauncherItem.h 2015-04-30 09:31:51 +0000 |
555 | +++ tests/mocks/Unity/Launcher/MockLauncherItem.h 2015-06-18 11:46:46 +0000 |
556 | @@ -31,6 +31,7 @@ |
557 | Q_OBJECT |
558 | public: |
559 | MockLauncherItem(const QString &appId, const QString& desktopFile, const QString& name, const QString& icon, QObject* parent = 0); |
560 | + ~MockLauncherItem(); |
561 | |
562 | QString appId() const override; |
563 | QString desktopFile() const; |
564 | |
565 | === modified file 'tests/mocks/Unity/Launcher/MockLauncherModel.cpp' |
566 | --- tests/mocks/Unity/Launcher/MockLauncherModel.cpp 2015-03-06 04:44:11 +0000 |
567 | +++ tests/mocks/Unity/Launcher/MockLauncherModel.cpp 2015-06-18 11:46:46 +0000 |
568 | @@ -43,6 +43,7 @@ |
569 | item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this); |
570 | item->setCount(1); |
571 | item->setCountVisible(true); |
572 | + item->setRunning(true); |
573 | m_list.append(item); |
574 | item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this); |
575 | item->setCount(12); |
576 | @@ -174,7 +175,9 @@ |
577 | int index = findApp(appId); |
578 | if (index >= 0) { |
579 | beginRemoveRows(QModelIndex(), index, 0); |
580 | - m_list.takeAt(index)->deleteLater(); |
581 | + MockLauncherItem * item = m_list.takeAt(index); |
582 | + item->setRunning(false); |
583 | + item->deleteLater(); |
584 | endRemoveRows(); |
585 | } |
586 | } |
587 | |
588 | === modified file 'tests/plugins/Unity/Launcher/launchermodeltest.cpp' |
589 | --- tests/plugins/Unity/Launcher/launchermodeltest.cpp 2015-06-12 16:07:43 +0000 |
590 | +++ tests/plugins/Unity/Launcher/launchermodeltest.cpp 2015-06-18 11:46:46 +0000 |
591 | @@ -68,6 +68,7 @@ |
592 | Q_OBJECT |
593 | public: |
594 | MockAppManager(QObject *parent = 0): ApplicationManagerInterface(parent) {} |
595 | + ~MockAppManager() {} |
596 | int rowCount(const QModelIndex &) const override { return m_list.count(); } |
597 | QVariant data(const QModelIndex &, int ) const override { return QVariant(); } |
598 | QString focusedApplicationId() const override { |
599 | @@ -325,6 +326,46 @@ |
600 | QCOMPARE(launcherModel->get(0)->appId(), QLatin1String("abs-icon")); |
601 | } |
602 | |
603 | + void testQuitMenuItem() { |
604 | + // we have 2 apps running, both should have the Quit action in its quick list |
605 | + QCOMPARE(launcherModel->rowCount(), 2); |
606 | + |
607 | + // stop the second one keeping it pinned so that it doesn't go away |
608 | + launcherModel->pin("no-icon"); |
609 | + appManager->stopApplication("no-icon"); |
610 | + |
611 | + // find the first Quit item, should be there |
612 | + QuickListModel *model = qobject_cast<QuickListModel*>(launcherModel->get(0)->quickList()); |
613 | + int quitActionIndex = -1; |
614 | + for (int i = 0; i < model->rowCount(); ++i) { |
615 | + if (model->get(i).actionId() == "stop_item") { |
616 | + quitActionIndex = i; |
617 | + break; |
618 | + } |
619 | + } |
620 | + QVERIFY(quitActionIndex >= 0); |
621 | + |
622 | + // find the second Quit item, should NOT be there, the app is stopped |
623 | + QuickListModel *model2 = qobject_cast<QuickListModel*>(launcherModel->get(1)->quickList()); |
624 | + int quitActionIndex2 = -1; |
625 | + for (int i = 0; i < model2->rowCount(); ++i) { |
626 | + if (model2->get(i).actionId() == "stop_item") { |
627 | + quitActionIndex2 = i; |
628 | + break; |
629 | + } |
630 | + } |
631 | + QVERIFY(quitActionIndex2 == -1); |
632 | + |
633 | + // trigger the first quit item quicklist action |
634 | + launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), quitActionIndex); |
635 | + // first app should be gone... |
636 | + QCOMPARE(launcherModel->rowCount(QModelIndex()), 1); |
637 | + // ... the second app (now at index 0) should still be there, pinned and stopped |
638 | + QCOMPARE(launcherModel->get(0)->appId(), QStringLiteral("no-icon")); |
639 | + QCOMPARE(launcherModel->get(0)->pinned(), true); |
640 | + QCOMPARE(launcherModel->get(0)->running(), false); |
641 | + } |
642 | + |
643 | void testGetUrlForAppId() { |
644 | QCOMPARE(launcherModel->getUrlForAppId(QString()), QString()); |
645 | QCOMPARE(launcherModel->getUrlForAppId(""), QString()); |
646 | |
647 | === modified file 'tests/qmltests/Launcher/tst_Launcher.qml' |
648 | --- tests/qmltests/Launcher/tst_Launcher.qml 2015-04-24 09:53:37 +0000 |
649 | +++ tests/qmltests/Launcher/tst_Launcher.qml 2015-06-18 11:46:46 +0000 |
650 | @@ -146,7 +146,7 @@ |
651 | startX+units.gu(8), startY); |
652 | |
653 | var panel = findChild(launcher, "launcherPanel"); |
654 | - verify(panel != undefined); |
655 | + verify(!!panel); |
656 | |
657 | // wait until it gets fully extended |
658 | tryCompare(panel, "x", 0); |
659 | @@ -157,7 +157,7 @@ |
660 | mouseMove(root, 1, root.height / 2); |
661 | |
662 | var panel = findChild(launcher, "launcherPanel"); |
663 | - verify(panel != undefined); |
664 | + verify(!!panel); |
665 | |
666 | // wait until it gets fully extended |
667 | tryCompare(panel, "x", 0); |
668 | @@ -187,7 +187,7 @@ |
669 | waitUntilLauncherDisappears(); |
670 | |
671 | var panel = findChild(launcher, "launcherPanel") |
672 | - verify(panel != undefined) |
673 | + verify(!!panel) |
674 | |
675 | // it starts out hidden just left of the left launcher edge |
676 | compare(panel.x, -panel.width) |
677 | @@ -225,7 +225,7 @@ |
678 | |
679 | var appIcon = findChild(launcher, "launcherDelegate0"); |
680 | |
681 | - verify(appIcon != undefined); |
682 | + verify(!!appIcon); |
683 | |
684 | if (data.mouse) { |
685 | mouseClick(appIcon); |
686 | @@ -248,7 +248,7 @@ |
687 | dragLauncherIntoView() |
688 | |
689 | var dashIcon = findChild(launcher, "dashItem") |
690 | - verify(dashIcon != undefined) |
691 | + verify(!!dashIcon) |
692 | |
693 | mouseClick(dashIcon) |
694 | |
695 | @@ -538,7 +538,7 @@ |
696 | |
697 | // Doing longpress |
698 | mousePress(draggedItem); |
699 | - tryCompare(quickListShape, "opacity", 0.96); |
700 | + tryCompare(quickListShape, "opacity", 0.8); |
701 | mouseRelease(draggedItem); |
702 | |
703 | verify(quickList.y >= units.gu(1)); |
704 | @@ -623,9 +623,31 @@ |
705 | tryCompare(quickList, "state", ""); |
706 | } |
707 | |
708 | + function test_quickListMenuOnRMB() { |
709 | + dragLauncherIntoView(); |
710 | + var clickedItem = findChild(launcher, "launcherDelegate5") |
711 | + var quickList = findChild(launcher, "quickList") |
712 | + var quickListShape = findChild(launcher, "quickListShape") |
713 | + var dndArea = findChild(launcher, "dndArea"); |
714 | + |
715 | + // Initial state |
716 | + tryCompare(quickListShape, "visible", false) |
717 | + |
718 | + // Doing RMB click |
719 | + mouseClick(clickedItem, clickedItem.width / 2, clickedItem.height / 2, Qt.RightButton) |
720 | + tryCompare(quickListShape, "visible", true) |
721 | + verify(quickList, "state", "open") |
722 | + verify(dndArea, "dragging", false) |
723 | + |
724 | + // Click somewhere in the empty space to dismiss the quicklist |
725 | + mouseClick(launcher, launcher.width - units.gu(1), units.gu(1)); |
726 | + tryCompare(quickListShape, "visible", false); |
727 | + verify(quickList, "state", "") |
728 | + } |
729 | + |
730 | function test_revealByHover() { |
731 | var panel = findChild(launcher, "launcherPanel"); |
732 | - verify(panel != undefined); |
733 | + verify(!!panel); |
734 | |
735 | revealByHover(); |
736 | tryCompare(launcher, "state", "visibleTemporary"); |
PASSED: Continuous integration, rev:1810 jenkins. qa.ubuntu. com/job/ unity8- ci/5803/ jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- wily-touch/ 94 jenkins. qa.ubuntu. com/job/ unity8- wily-amd64- ci/81 jenkins. qa.ubuntu. com/job/ unity8- wily-i386- ci/81 jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- runner- wily-mako/ 72 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- wily-armhf/ 94 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- wily-armhf/ 94/artifact/ work/output/ *zip*/output. zip s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 21099
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/unity8- ci/5803/ rebuild
http://