Merge lp:~mzanetti/unity8/quicklists into lp:unity8

Proposed by Michael Zanetti
Status: Merged
Approved by: Michał Sawicz
Approved revision: 142
Merged at revision: 217
Proposed branch: lp:~mzanetti/unity8/quicklists
Merge into: lp:unity8
Diff against target: 341 lines (+157/-6)
11 files modified
Launcher/Launcher.qml (+2/-2)
Launcher/LauncherPanel.qml (+55/-1)
plugins/Unity/Launcher/common/quicklistentry.cpp (+5/-0)
plugins/Unity/Launcher/common/quicklistentry.h (+2/-0)
plugins/Unity/Launcher/launcheritem.cpp (+3/-0)
plugins/Unity/Launcher/quicklistmodel.cpp (+2/-0)
tests/mocks/Unity/Launcher/MockLauncherModel.cpp (+1/-3)
tests/mocks/Unity/Launcher/MockLauncherModel.h (+3/-0)
tests/mocks/Unity/Launcher/MockQuickListModel.cpp (+2/-0)
tests/mocks/Unity/Launcher/plugin.cpp (+3/-0)
tests/qmltests/Launcher/tst_Launcher.qml (+79/-0)
To merge this branch: bzr merge lp:~mzanetti/unity8/quicklists
Reviewer Review Type Date Requested Status
Michał Sawicz Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+180894@code.launchpad.net

Commit message

initial support for quicklists

For now they support pinning and removing of items

To post a comment you must log in.
lp:~mzanetti/unity8/quicklists updated
140. By Michael Zanetti

remove obsolete check for non-clickable item

141. By Michael Zanetti

hasAction -> clickable to reflect changes in unity-api

142. By Michael Zanetti

remove unrelated whitespace change

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Launcher/Launcher.qml'
2--- Launcher/Launcher.qml 2013-08-15 22:41:41 +0000
3+++ Launcher/Launcher.qml 2013-08-19 15:20:54 +0000
4@@ -73,7 +73,7 @@
5 id: dismissTimer
6 interval: 5000
7 onTriggered: {
8- if (!panel.moving) {
9+ if (!panel.preventHiding) {
10 root.state = ""
11 } else {
12 dismissTimer.restart()
13@@ -162,7 +162,7 @@
14 root.dashItemSelected(index)
15 }
16
17- onMovingChanged: {
18+ onPreventHidingChanged: {
19 if (dismissTimer.running) {
20 dismissTimer.restart();
21 }
22
23=== modified file 'Launcher/LauncherPanel.qml'
24--- Launcher/LauncherPanel.qml 2013-08-19 11:47:29 +0000
25+++ Launcher/LauncherPanel.qml 2013-08-19 15:20:54 +0000
26@@ -19,6 +19,7 @@
27 import Ubuntu.Components.ListItems 0.1 as ListItems
28 import Unity 0.1
29 import Unity.Launcher 0.1
30+import Ubuntu.Components.Popups 0.1
31 import "../Components/ListItems"
32
33 Item {
34@@ -29,7 +30,8 @@
35 property var model
36 property bool inverted: true
37 property bool dragging: false
38- property bool moving: launcherListView.moving || launcherListView.flicking || dndArea.draggedIndex >= 0
39+ property bool moving: launcherListView.moving || launcherListView.flicking
40+ property bool preventHiding: moving || dndArea.draggedIndex >= 0 || dndArea.quickListPopover !== null || dndArea.pressed
41 property int highlightIndex: -1
42
43 signal applicationSelected(string desktopFile)
44@@ -219,6 +221,7 @@
45
46 MouseArea {
47 id: dndArea
48+ objectName: "dndArea"
49 anchors {
50 fill: parent
51 topMargin: launcherListView.topMargin
52@@ -234,6 +237,7 @@
53 property bool postDragging: false
54 property int startX
55 property int startY
56+ property var quickListPopover: null
57
58 onPressed: {
59 selectedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
60@@ -302,6 +306,13 @@
61
62 draggedIndex = Math.floor((mouseY + launcherListView.realContentY) / launcherListView.realItemHeight);
63
64+ // Opening QuickList
65+ var quickListModel = launcherListView.model.get(draggedIndex).quickList
66+ var quickListAppId = launcherListView.model.get(draggedIndex).appId
67+
68+ quickListPopover = PopupUtils.open(popoverComponent, selectedItem,
69+ {model: quickListModel, appId: quickListAppId})
70+
71 launcherListView.interactive = false
72
73 var yOffset = draggedIndex > 0 ? (mouseY + launcherListView.realContentY) % (draggedIndex * launcherListView.realItemHeight) : mouseY + launcherListView.realContentY
74@@ -324,6 +335,7 @@
75 var distance = Math.max(Math.abs(mouseX - startX), Math.abs(mouseY - startY))
76 if (!preDragging && distance > units.gu(1.5)) {
77 preDragging = true;
78+ PopupUtils.close(quickListPopover)
79 }
80 if (distance > launcherListView.itemHeight) {
81 selectedItem.dragging = true
82@@ -414,4 +426,46 @@
83 }
84 }
85 }
86+
87+ Component {
88+ id: popoverComponent
89+
90+ Popover {
91+ id: popover
92+ property var model
93+ property string appId
94+ contentWidth: quickListColumn.width
95+
96+ // FIXME: There's a bug in the Popover positioning that it covers the item in case it is rotated.
97+ // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1204470
98+ // For now, let's move the Popover around with callerMargin.
99+ // Remove popupMargin once the bug is fixed.
100+ property int popupMargin: root.inverted ? launcherListView.itemHeight : 0;
101+
102+ callerMargin: units.gu(1) + popupMargin
103+
104+ Column {
105+ id: quickListColumn
106+ width: units.gu(30)
107+ height: childrenRect.height
108+
109+ Repeater {
110+ model: popover.model
111+
112+ ListItems.Standard {
113+ objectName: "quickListEntry" + index
114+ text: (model.clickable ? "" : "<b>") + model.label + (model.clickable ? "" : "</b>")
115+ highlightWhenPressed: model.clickable
116+ onClicked: {
117+ if (!model.clickable) {
118+ return;
119+ }
120+ LauncherModel.quickListActionInvoked(appId, index);
121+ PopupUtils.close(popover);
122+ }
123+ }
124+ }
125+ }
126+ }
127+ }
128 }
129
130=== modified file 'plugins/Unity/Launcher/common/quicklistentry.cpp'
131--- plugins/Unity/Launcher/common/quicklistentry.cpp 2013-07-05 11:25:54 +0000
132+++ plugins/Unity/Launcher/common/quicklistentry.cpp 2013-08-19 15:20:54 +0000
133@@ -52,3 +52,8 @@
134 {
135 m_icon = icon;
136 }
137+
138+bool QuickListEntry::clickable() const
139+{
140+ return !m_actionId.isEmpty();
141+}
142
143=== modified file 'plugins/Unity/Launcher/common/quicklistentry.h'
144--- plugins/Unity/Launcher/common/quicklistentry.h 2013-07-05 11:25:54 +0000
145+++ plugins/Unity/Launcher/common/quicklistentry.h 2013-08-19 15:20:54 +0000
146@@ -35,6 +35,8 @@
147 QString icon() const;
148 void setIcon(const QString &icon);
149
150+ bool clickable() const;
151+
152 private:
153 QString m_actionId;
154 QString m_text;
155
156=== modified file 'plugins/Unity/Launcher/launcheritem.cpp'
157--- plugins/Unity/Launcher/launcheritem.cpp 2013-07-24 10:42:00 +0000
158+++ plugins/Unity/Launcher/launcheritem.cpp 2013-08-19 15:20:54 +0000
159@@ -33,6 +33,9 @@
160 m_count(0),
161 m_quickList(new QuickListModel(this))
162 {
163+ QuickListEntry nameAction;
164+ nameAction.setText(m_name);
165+ m_quickList->appendAction(nameAction);
166 QuickListEntry pinningAction;
167 pinningAction.setActionId("pin_item");
168 pinningAction.setText("Pin to Launcher");
169
170=== modified file 'plugins/Unity/Launcher/quicklistmodel.cpp'
171--- plugins/Unity/Launcher/quicklistmodel.cpp 2013-07-24 10:42:00 +0000
172+++ plugins/Unity/Launcher/quicklistmodel.cpp 2013-08-19 15:20:54 +0000
173@@ -66,6 +66,8 @@
174 return m_list.at(index.row()).text();
175 case RoleIcon:
176 return m_list.at(index.row()).icon();
177+ case RoleClickable:
178+ return m_list.at(index.row()).clickable();
179 }
180 return QVariant();
181 }
182
183=== modified file 'tests/mocks/Unity/Launcher/MockLauncherModel.cpp'
184--- tests/mocks/Unity/Launcher/MockLauncherModel.cpp 2013-08-14 23:00:28 +0000
185+++ tests/mocks/Unity/Launcher/MockLauncherModel.cpp 2013-08-19 15:20:54 +0000
186@@ -131,9 +131,7 @@
187
188 void MockLauncherModel::quickListActionInvoked(const QString &appId, int actionIndex)
189 {
190- Q_UNUSED(appId)
191- Q_UNUSED(actionIndex)
192- // Nothing to mock yet...
193+ Q_EMIT quickListTriggered(appId, actionIndex);
194 }
195
196 int MockLauncherModel::findApp(const QString &appId)
197
198=== modified file 'tests/mocks/Unity/Launcher/MockLauncherModel.h'
199--- tests/mocks/Unity/Launcher/MockLauncherModel.h 2013-07-08 12:57:39 +0000
200+++ tests/mocks/Unity/Launcher/MockLauncherModel.h 2013-08-19 15:20:54 +0000
201@@ -44,6 +44,9 @@
202 Q_INVOKABLE void requestRemove(const QString &appId);
203 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
204
205+Q_SIGNALS:
206+ void quickListTriggered(const QString &appId, int index);
207+
208 private:
209 int findApp(const QString &appId);
210
211
212=== modified file 'tests/mocks/Unity/Launcher/MockQuickListModel.cpp'
213--- tests/mocks/Unity/Launcher/MockQuickListModel.cpp 2013-07-05 11:25:54 +0000
214+++ tests/mocks/Unity/Launcher/MockQuickListModel.cpp 2013-08-19 15:20:54 +0000
215@@ -35,6 +35,8 @@
216 return QLatin1String("test menu entry ") + QString::number(index.row());
217 case RoleIcon:
218 return QLatin1String("copy.png");
219+ case RoleClickable:
220+ return index.row() == 1 ? false : true;
221 }
222 return QVariant();
223 }
224
225=== modified file 'tests/mocks/Unity/Launcher/plugin.cpp'
226--- tests/mocks/Unity/Launcher/plugin.cpp 2013-07-05 11:25:54 +0000
227+++ tests/mocks/Unity/Launcher/plugin.cpp 2013-08-19 15:20:54 +0000
228@@ -20,6 +20,7 @@
229 #include "plugin.h"
230 #include "MockLauncherModel.h"
231 #include "MockLauncherItem.h"
232+#include "MockQuickListModel.h"
233
234 #include <unity/shell/launcher/LauncherModelInterface.h>
235 #include <unity/shell/launcher/LauncherItemInterface.h>
236@@ -39,7 +40,9 @@
237 // @uri Unity.Launcher
238 qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
239 qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
240+ qmlRegisterUncreatableType<QuickListModelInterface>(uri, 0, 1, "QuickListModelInterface", "Abstract Interface. Cannot be instantiated.");
241
242 qmlRegisterSingletonType<MockLauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
243 qmlRegisterUncreatableType<MockLauncherItem>(uri, 0, 1, "LauncherItem", "Can't create LauncherItems in QML. Get them from the LauncherModel");
244+ qmlRegisterUncreatableType<MockQuickListModel>(uri, 0, 1, "QuickListModel", "Can't create QuickLists in QML. Get them from the LauncherItems");
245 }
246
247=== modified file 'tests/qmltests/Launcher/tst_Launcher.qml'
248--- tests/qmltests/Launcher/tst_Launcher.qml 2013-08-16 21:57:18 +0000
249+++ tests/qmltests/Launcher/tst_Launcher.qml 2013-08-19 15:20:54 +0000
250@@ -49,6 +49,11 @@
251 property int maxPanelX: 0
252 }
253
254+ SignalSpy {
255+ id: signalSpy
256+ target: LauncherModel
257+ }
258+
259 Connections {
260 target: testCase.findChild(launcher, "launcherPanel")
261
262@@ -306,5 +311,79 @@
263 tryCompare(draggedItem, "itemOpacity", 1)
264 tryCompare(fakeDragItem, "visible", false)
265 }
266+
267+ function test_quicklist_dismiss() {
268+ revealer.dragLauncherIntoView();
269+ var draggedItem = findChild(launcher, "launcherDelegate5")
270+ var item0 = findChild(launcher, "launcherDelegate0")
271+ var fakeDragItem = findChild(launcher, "fakeDragItem")
272+ var dndArea = findChild(launcher, "dndArea")
273+
274+ // Initial state
275+ compare(dndArea.quickListPopover, null)
276+
277+ // Doing longpress
278+ mousePress(draggedItem, draggedItem.width / 2, draggedItem.height / 2)
279+ tryCompare(fakeDragItem, "visible", true) // Wait longpress happening
280+ verify(dndArea.quickListPopover != null)
281+
282+ // Dragging a bit (> 1.5 gu)
283+ mouseMove(draggedItem, -units.gu(2), draggedItem.height / 2)
284+
285+ // QuickList needs to be closed when a drag operation starts
286+ tryCompare(dndArea, "quickListPopover", null)
287+
288+ mouseRelease(draggedItem);
289+ }
290+
291+ function test_quicklist_click_data() {
292+ return [
293+ {tag: "non-clickable", index: 1, clickable: false },
294+ {tag: "clickable", index: 2, clickable: true },
295+ ];
296+ }
297+
298+ function test_quicklist_click(data) {
299+ revealer.dragLauncherIntoView();
300+ var clickedItem = findChild(launcher, "launcherDelegate5")
301+ var dndArea = findChild(launcher, "dndArea")
302+
303+ // Initial state
304+ compare(dndArea.quickListPopover, null)
305+
306+ // Doing longpress
307+ mousePress(clickedItem, clickedItem.width / 2, clickedItem.height / 2)
308+ tryCompare(clickedItem, "itemOpacity", 0) // Wait for longpress to happen
309+ verify(dndArea.quickListPopover != null)
310+
311+ mouseRelease(clickedItem);
312+
313+ var quickListEntry = findChild(dndArea.quickListPopover, "quickListEntry" + data.index)
314+
315+ signalSpy.clear();
316+ signalSpy.signalName = "quickListTriggered"
317+
318+ mouseClick(quickListEntry, quickListEntry.width / 2, quickListEntry.height / 2)
319+
320+ if (data.clickable) {
321+ // QuickList needs to be closed when some clickable item is clicked
322+ tryCompare(dndArea, "quickListPopover", null)
323+
324+ compare(signalSpy.count, 1, "Quicklist signal wasn't triggered")
325+ compare(signalSpy.signalArguments[0][0], LauncherModel.get(5).appId)
326+ compare(signalSpy.signalArguments[0][1], 2)
327+
328+ } else {
329+
330+ // QuickList must not be closed when a non-clickable item is clicked
331+ verify(dndArea.quickListPopover != null)
332+
333+ compare(signalSpy.count, 0, "Quicklist signal must NOT be triggered when clicking a non-clickable item")
334+
335+ // Click somewhere in the empty space to dismiss the quicklist
336+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
337+ tryCompare(dndArea, "quickListPopover", null)
338+ }
339+ }
340 }
341 }

Subscribers

People subscribed via source and target branches