Merge lp:~aacid/unity8/dash_overview into lp:unity8

Proposed by Albert Astals Cid
Status: Superseded
Proposed branch: lp:~aacid/unity8/dash_overview
Merge into: lp:unity8
Prerequisite: lp:~saviq/unity8/category-header-links
Diff against target: 2668 lines (+1767/-106)
35 files modified
data/unity8.conf (+1/-1)
debian/control (+1/-1)
plugins/Dash/CardCreator.js (+2/-1)
plugins/Dash/ScopeStyle.qml (+2/-2)
plugins/Dash/listviewwithpageheader.cpp (+2/-2)
po/unity8.pot (+44/-12)
qml/Components/PageHeader.qml (+26/-8)
qml/Components/ResponsiveGridView.qml (+3/-3)
qml/Dash/CardCarousel.qml (+2/-2)
qml/Dash/CardGrid.qml (+13/-2)
qml/Dash/CardVerticalJournal.qml (+1/-1)
qml/Dash/Dash.qml (+138/-7)
qml/Dash/DashBackground.qml (+24/-0)
qml/Dash/DashContent.qml (+7/-4)
qml/Dash/DashRenderer.qml (+3/-1)
qml/Dash/GenericScopeView.qml (+53/-25)
qml/Dash/PreviewListView.qml (+2/-2)
qml/Dash/ScopesOverview.qml (+518/-0)
qml/Dash/ScopesOverviewAll.qml (+54/-0)
qml/Dash/ScopesOverviewFavorites.qml (+73/-0)
qml/Dash/ScopesOverviewTab.qml (+74/-0)
qml/Shell.qml (+2/-8)
tests/autopilot/unity8/shell/emulators/dash.py (+9/-0)
tests/mocks/Unity/CMakeLists.txt (+2/-1)
tests/mocks/Unity/fake_resultsmodel.cpp (+2/-1)
tests/mocks/Unity/fake_scope.cpp (+18/-5)
tests/mocks/Unity/fake_scope.h (+6/-3)
tests/mocks/Unity/fake_scopes.cpp (+43/-2)
tests/mocks/Unity/fake_scopes.h (+6/-0)
tests/mocks/Unity/fake_scopesoverview.cpp (+280/-0)
tests/mocks/Unity/fake_scopesoverview.h (+104/-0)
tests/plugins/Dash/cardcreator/5.res (+1/-0)
tests/qmltests/Components/tst_PageHeader.qml (+36/-6)
tests/qmltests/Dash/tst_Dash.qml (+211/-3)
tests/qmltests/Dash/tst_DashContent.qml (+4/-3)
To merge this branch: bzr merge lp:~aacid/unity8/dash_overview
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Unity Team Pending
Review via email: mp+227562@code.launchpad.net

This proposal has been superseded by a proposal from 2014-07-29.

Commit message

Dash Overview

Description of the change

* Are there any related MPs required for this MP to build/function as expected?
https://code.launchpad.net/~unity-team/unity-scopes-shell/overview/+merge/227745
Also we probably want to get the fix for https://bugs.launchpad.net/ubuntu/+source/qtdeclarative-opensource-src/+bug/1339828 AKA https://bugreports.qt-project.org/browse/QTBUG-40385

 * Did you perform an exploratory manual test run of your code change and any related functionality?
Yes, all via make tryDash right now since real scopes backend is not there yet

 * 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?
Still needs to happen

To post a comment you must log in.
lp:~aacid/unity8/dash_overview updated
1112. By Albert Astals Cid

Only vertical flick

1113. By Albert Astals Cid

Wrong merge

1114. By Albert Astals Cid

deleteLater this

For some reason i'm now getting lots of crashes in this branch when showing the overview for the second time

1115. By Albert Astals Cid

Use the root.scopeStyle.threshold if it's there

1116. By Albert Astals Cid

Merge lp:~saviq/unity8/category-header-links

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)
lp:~aacid/unity8/dash_overview updated
1117. By Albert Astals Cid

Fixes for the phone

Make sure we bind instead of assign since some sizes get updated later on
Force the art shape size since to be what we want

1118. By Albert Astals Cid

Merge

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)
lp:~aacid/unity8/dash_overview updated
1119. By Albert Astals Cid

Fix threshold calculation as discussed with Saviq

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~aacid/unity8/dash_overview updated
1120. By Albert Astals Cid

Bind scope query back

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~aacid/unity8/dash_overview updated
1121. By Albert Astals Cid

Use root and not parent since parent is a hidden item the flickable uses

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~aacid/unity8/dash_overview updated
1122. By Albert Astals Cid

App store button

1123. By Albert Astals Cid

Dark background for the overview

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~aacid/unity8/dash_overview updated
1124. By Albert Astals Cid

Don't show the scopes scope, it's conflicting with the dash overview

1125. By Albert Astals Cid

Merge

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~aacid/unity8/dash_overview updated
1126. By Albert Astals Cid

Height actually needs to be parent and not root

found by mhr3

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~aacid/unity8/dash_overview updated
1127. By Albert Astals Cid

Merge

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~aacid/unity8/dash_overview updated
1128. By Albert Astals Cid

Merge

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)
lp:~aacid/unity8/dash_overview updated
1129. By Albert Astals Cid

Somehow this code ended up repeated, remove

1130. By Albert Astals Cid

pressAndHold has only 1 param in DashREnderer

1131. By Albert Astals Cid

Merge lp:~aacid/unity8/compile-scopes-v3

1132. By Albert Astals Cid

Merge lp:~aacid/unity8/showSearchOnSearchQueryChange

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~aacid/unity8/dash_overview updated
1133. By Albert Astals Cid

Merge

1134. By Albert Astals Cid

Merge

1135. By Albert Astals Cid

Fix issue with the first time fading in to favorites not working

1136. By Albert Astals Cid

Use a ShaderEffectSource instead of changing Dash parent

Much faster rendering

1137. By Albert Astals Cid

Split into two lines

1138. By Albert Astals Cid

Some people have ridiculously narrow screens and make me lose vertical space

1139. By Albert Astals Cid

Make this totally harder to read by splitting in two lines as requested on the review

1140. By Albert Astals Cid

Add ;

1141. By Albert Astals Cid

Merge

1142. By Albert Astals Cid

require new scopes impl

1143. By Albert Astals Cid

Small catches from review

1144. By Albert Astals Cid

Convert the overrides to functions as Nick suggested

1145. By Albert Astals Cid

Merge

1146. By Albert Astals Cid

Merge lp:~unity-team/unity8/dash-as-app

1147. By Albert Astals Cid

Merge lp:~canonical-platform-qa/unity8/dash-as-app-autopilot

1148. By Albert Astals Cid

Merge

1149. By Albert Astals Cid

pot_file

1150. By Albert Astals Cid

Don't remember why this is here, remove

1151. By Albert Astals Cid

Add an option for desktop_file_hint since the upstart file is using it

1152. By Albert Astals Cid

I did not want to remove this

1153. By Albert Astals Cid

We changed the formula

1154. By Albert Astals Cid

scopes scope is not there by default anymore so use another one

1155. By Albert Astals Cid

make sure the delegate is created (scroll to it)

1156. By Albert Astals Cid

bottom bar should be non opaque and taller

1157. By Albert Astals Cid

set scope active when overview is open

1158. By Albert Astals Cid

search results viewer is always current

1159. By Albert Astals Cid

Merge

1160. By Albert Astals Cid

Merge

1161. By Albert Astals Cid

Merge

1162. By Albert Astals Cid

Make the goto store button work if store is a favorite

1163. By Albert Astals Cid

"fake" dash should not be visible if we have a temp scope

1164. By Albert Astals Cid

Use icon instead of own image

1165. By Albert Astals Cid

Dash overview handle

1166. By Albert Astals Cid

enable drag handle only if overviewDragHandle.enabled

1167. By Albert Astals Cid

Only use dashContent.pageHeaderTotallyVisible when there's no temp scope

1168. By Albert Astals Cid

overview handle: opacity because of header pos, y because of opening

1169. By Albert Astals Cid

More update

1170. By Albert Astals Cid

hook up the search indicator

1171. By Albert Astals Cid

hook up temp scope processing

1172. By Albert Astals Cid

unfocus++

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/unity8.conf'
2--- data/unity8.conf 2014-07-16 15:51:56 +0000
3+++ data/unity8.conf 2014-07-29 11:05:57 +0000
4@@ -36,7 +36,7 @@
5
6 if [ -z "$UNITY_SCOPES_LIST" ]; then
7 # FIXME: remove once we have this in dconf
8- initctl set-env UNITY_SCOPES_LIST="scopes;clickscope;musicaggregator;videoaggregator"
9+ initctl set-env UNITY_SCOPES_LIST="clickscope;musicaggregator;videoaggregator"
10 fi
11
12 # Remove the socket if still there
13
14=== modified file 'debian/control'
15--- debian/control 2014-07-24 20:40:44 +0000
16+++ debian/control 2014-07-29 11:05:57 +0000
17@@ -23,7 +23,7 @@
18 libpulse-dev,
19 libqmenumodel-dev (>= 0.2.8),
20 libqt5xmlpatterns5-dev,
21- libunity-api-dev (>= 7.85),
22+ libunity-api-dev (>= 7.87),
23 libunity-mir-dev,
24 libusermetricsoutput1-dev,
25 libxcb1-dev,
26
27=== modified file 'plugins/Dash/CardCreator.js'
28--- plugins/Dash/CardCreator.js 2014-07-09 19:44:44 +0000
29+++ plugins/Dash/CardCreator.js 2014-07-29 11:05:57 +0000
30@@ -408,7 +408,7 @@
31 mascotCode = kMascotImageCode.arg(anchors).arg(mascotImageVisible);
32 }
33
34- var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey")';
35+ var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : "grey")';
36
37 var titleSubtitleCode = "";
38 if (hasTitle) {
39@@ -438,6 +438,7 @@
40 titleAnchors = 'left: parent.left; \n\
41 leftMargin: units.gu(1); \n\
42 right: parent.right; \n\
43+ rightMargin: units.gu(1); \n\
44 top: overlayLoader.top; \n\
45 topMargin: units.gu(1);\n';
46 } else {
47
48=== modified file 'plugins/Dash/ScopeStyle.qml'
49--- plugins/Dash/ScopeStyle.qml 2014-07-18 18:09:13 +0000
50+++ plugins/Dash/ScopeStyle.qml 2014-07-29 11:05:57 +0000
51@@ -43,9 +43,9 @@
52
53 /*! \brief Luminance threshold for switching between fore and background color
54
55- \note If background colour is not fully opaque, it's not taken into account.
56+ \note If background colour is not fully opaque, the defaultLightLuminance it's taken into account instead of it.
57 */
58- readonly property real threshold: background.a !== 1.0 ? d.foregroundLuminance : (d.foregroundLuminance + d.backgroundLuminance) / 2
59+ readonly property real threshold: background.a !== 1.0 ? (d.foregroundLuminance + d.defaultLightLuminance) / 2: (d.foregroundLuminance + d.backgroundLuminance) / 2
60
61 /*! \brief The lighter of foreground and background colors
62
63
64=== modified file 'plugins/Dash/listviewwithpageheader.cpp'
65--- plugins/Dash/listviewwithpageheader.cpp 2014-07-25 10:47:08 +0000
66+++ plugins/Dash/listviewwithpageheader.cpp 2014-07-29 11:05:57 +0000
67@@ -822,8 +822,8 @@
68 ListItem *nextItem = itemAtIndex(modelIndex + 1);
69 if (nextItem) {
70 listItem->setY(nextItem->y() - listItem->height());
71- } else if (modelIndex == 0 && m_headerItem) {
72- listItem->setY(m_headerItem->height());
73+ } else if (modelIndex == 0) {
74+ listItem->setY(m_headerItem ? m_headerItem->height() : 0);
75 } else if (!m_visibleItems.isEmpty()) {
76 lostItem = true;
77 }
78
79=== modified file 'po/unity8.pot'
80--- po/unity8.pot 2014-07-11 14:20:12 +0000
81+++ po/unity8.pot 2014-07-29 11:05:57 +0000
82@@ -8,7 +8,7 @@
83 msgstr ""
84 "Project-Id-Version: unity8\n"
85 "Report-Msgid-Bugs-To: \n"
86-"POT-Creation-Date: 2014-07-11 14:59+0200\n"
87+"POT-Creation-Date: 2014-07-21 15:41+0200\n"
88 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
89 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
90 "Language-Team: LANGUAGE <LL@li.org>\n"
91@@ -101,7 +101,7 @@
92 msgid "See more"
93 msgstr ""
94
95-#: qml/Components/SeeMore.qml:58 qml/Dash/GenericScopeView.qml:330
96+#: qml/Components/SeeMore.qml:58 qml/Dash/GenericScopeView.qml:356
97 msgid "See less"
98 msgstr ""
99
100@@ -109,7 +109,7 @@
101 msgid "Recent"
102 msgstr ""
103
104-#: qml/Dash/GenericScopeView.qml:330
105+#: qml/Dash/GenericScopeView.qml:356
106 msgid "See all"
107 msgstr ""
108
109@@ -133,6 +133,22 @@
110 msgid "Send"
111 msgstr ""
112
113+#: qml/Dash/ScopesOverview.qml:185
114+msgid "Manage Dash"
115+msgstr ""
116+
117+#: qml/Dash/ScopesOverview.qml:390
118+msgid "Done"
119+msgstr ""
120+
121+#: qml/Dash/ScopesOverviewTab.qml:36
122+msgid "Favorites"
123+msgstr ""
124+
125+#: qml/Dash/ScopesOverviewTab.qml:54
126+msgid "All"
127+msgstr ""
128+
129 #: qml/Greeter/Greeter.qml:157
130 msgid "Swipe to unlock"
131 msgstr ""
132@@ -157,11 +173,11 @@
133 msgid "Speaking..."
134 msgstr ""
135
136-#: qml/Notifications/NotificationMenuItemFactory.qml:91
137+#: qml/Notifications/NotificationMenuItemFactory.qml:97
138 msgid "Show password"
139 msgstr ""
140
141-#: qml/Notifications/NotificationMenuItemFactory.qml:103
142+#: qml/Notifications/NotificationMenuItemFactory.qml:112
143 msgid "Please enter SIM PIN"
144 msgstr ""
145
146@@ -173,27 +189,27 @@
147 msgid "Conference"
148 msgstr ""
149
150-#: qml/Panel/Indicators/MenuItemFactory.qml:583
151+#: qml/Panel/Indicators/MenuItemFactory.qml:577
152 msgid "In queue…"
153 msgstr ""
154
155-#: qml/Panel/Indicators/MenuItemFactory.qml:587
156+#: qml/Panel/Indicators/MenuItemFactory.qml:581
157 msgid "Downloading"
158 msgstr ""
159
160-#: qml/Panel/Indicators/MenuItemFactory.qml:589
161+#: qml/Panel/Indicators/MenuItemFactory.qml:583
162 msgid "Paused, tap to resume"
163 msgstr ""
164
165-#: qml/Panel/Indicators/MenuItemFactory.qml:591
166+#: qml/Panel/Indicators/MenuItemFactory.qml:585
167 msgid "Canceled"
168 msgstr ""
169
170-#: qml/Panel/Indicators/MenuItemFactory.qml:593
171+#: qml/Panel/Indicators/MenuItemFactory.qml:587
172 msgid "Finished"
173 msgstr ""
174
175-#: qml/Panel/Indicators/MenuItemFactory.qml:595
176+#: qml/Panel/Indicators/MenuItemFactory.qml:589
177 msgid "Failed, tap to retry"
178 msgstr ""
179
180@@ -201,7 +217,23 @@
181 msgid "Search"
182 msgstr ""
183
184-#: qml/Shell.qml:359
185+#: qml/Shell.qml:347
186+msgid "Are you sure you would like to turn power off?"
187+msgstr ""
188+
189+#: qml/Shell.qml:349
190+msgid "Power off"
191+msgstr ""
192+
193+#: qml/Shell.qml:360
194+msgid "Restart"
195+msgstr ""
196+
197+#: qml/Shell.qml:369
198+msgid "Cancel"
199+msgstr ""
200+
201+#: qml/Shell.qml:496
202 #, qt-format
203 msgid "Please enter %1"
204 msgstr ""
205
206=== modified file 'qml/Components/PageHeader.qml'
207--- qml/Components/PageHeader.qml 2014-07-21 09:49:41 +0000
208+++ qml/Components/PageHeader.qml 2014-07-29 11:05:57 +0000
209@@ -24,7 +24,7 @@
210 Item {
211 id: root
212 objectName: "pageHeader"
213- implicitHeight: headerContainer.height + units.gu(2) + bottomContainer.height
214+ implicitHeight: headerContainer.height + bottomContainer.height + (showSignatureLine ? units.gu(2) : 0)
215
216 property bool showBackButton: false
217 property string title
218@@ -33,6 +33,7 @@
219 property ListModel searchHistory: SearchHistoryModel
220 property alias searchQuery: searchTextField.text
221 property bool searchInProgress: false
222+ property alias showSignatureLine: bottomBorder.visible
223
224 property alias bottomItem: bottomContainer.children
225
226@@ -44,6 +45,12 @@
227 signal backClicked()
228
229 onScopeStyleChanged: refreshLogo()
230+ onSearchQueryChanged: {
231+ // Make sure we are at the search page if the search query changes behind our feet
232+ if (searchQuery) {
233+ headerContainer.showSearch = true;
234+ }
235+ }
236
237 function triggerSearch() {
238 if (searchEntryEnabled) {
239@@ -52,6 +59,12 @@
240 }
241 }
242
243+ function closePopup() {
244+ if (headerContainer.popover != null) {
245+ PopupUtils.close(headerContainer.popover);
246+ }
247+ }
248+
249 function resetSearch(keepFocus) {
250 if (searchHistory) {
251 searchHistory.addQuery(searchTextField.text);
252@@ -60,9 +73,7 @@
253 unfocus();
254 }
255 searchTextField.text = "";
256- if (headerContainer.popover != null) {
257- PopupUtils.close(headerContainer.popover);
258- }
259+ closePopup();
260 }
261
262 function unfocus() {
263@@ -103,9 +114,7 @@
264 anchors { fill: parent; margins: units.gu(1); bottomMargin: units.gu(3) + bottomContainer.height }
265 visible: headerContainer.showSearch
266 onPressed: {
267- if (headerContainer.popover) {
268- PopupUtils.close(headerContainer.popover);
269- }
270+ closePopup();
271 if (!searchTextField.text) {
272 headerContainer.showSearch = false;
273 }
274@@ -220,6 +229,12 @@
275 root.openSearchHistory();
276 }
277 }
278+
279+ onTextChanged: {
280+ if (text != "") {
281+ closePopup();
282+ }
283+ }
284 }
285 }
286
287@@ -244,6 +259,7 @@
288
289 actions: [
290 Action {
291+ objectName: "search"
292 iconName: "search"
293 visible: root.searchEntryEnabled
294 onTriggered: {
295@@ -296,6 +312,7 @@
296
297 Repeater {
298 id: recentSearches
299+ objectName: "recentSearches"
300 model: searchHistory
301
302 delegate: Standard {
303@@ -304,7 +321,8 @@
304 onClicked: {
305 searchHistory.addQuery(text);
306 searchTextField.text = text;
307- PopupUtils.close(popover);
308+ closePopup();
309+ unfocus();
310 }
311 }
312 }
313
314=== modified file 'qml/Components/ResponsiveGridView.qml'
315--- qml/Components/ResponsiveGridView.qml 2014-07-11 12:12:50 +0000
316+++ qml/Components/ResponsiveGridView.qml 2014-07-29 11:05:57 +0000
317@@ -34,7 +34,7 @@
318 readonly property int cellWidth: gridView.cellWidth
319 readonly property int cellHeight: gridView.cellHeight
320 readonly property int totalContentHeight: {
321- return contentHeightForRows(Math.ceil(gridView.model.count / columns))
322+ return contentHeightForRows(Math.ceil(gridView.model.count / columns), cellHeight)
323 }
324 property alias interactive: gridView.interactive
325 readonly property alias flicking: gridView.flicking
326@@ -47,8 +47,8 @@
327 property alias cacheBuffer: gridView.cacheBuffer
328 readonly property alias currentItem: gridView.currentItem
329
330- function contentHeightForRows(rows) {
331- return rows * cellHeight;
332+ function contentHeightForRows(rows, height) {
333+ return rows * height
334 }
335
336 GridView {
337
338=== modified file 'qml/Dash/CardCarousel.qml'
339--- qml/Dash/CardCarousel.qml 2014-07-11 12:13:08 +0000
340+++ qml/Dash/CardCarousel.qml 2014-07-29 11:05:57 +0000
341@@ -48,8 +48,8 @@
342
343 objectName: "carouselDelegate" + index
344
345- function clicked() { cardCarousel.clicked(index, model.result) }
346- function pressAndHold() { cardCarousel.pressAndHold(index, model.result) }
347+ function clicked() { cardCarousel.clicked(index, model.result, loader.item, model) }
348+ function pressAndHold() { cardCarousel.pressAndHold(index) }
349
350 sourceComponent: cardTool.cardComponent
351 onLoaded: {
352
353=== modified file 'qml/Dash/CardGrid.qml'
354--- qml/Dash/CardGrid.qml 2014-07-18 11:35:47 +0000
355+++ qml/Dash/CardGrid.qml 2014-07-29 11:05:57 +0000
356@@ -26,10 +26,20 @@
357 }
358
359 expandedHeight: grid.totalContentHeight
360- collapsedHeight: Math.min(grid.contentHeightForRows(collapsedRows), expandedHeight)
361+ collapsedHeight: Math.min(grid.contentHeightForRows(collapsedRows, grid.cellHeight), expandedHeight)
362 collapsedItemCount: collapsedRows * grid.columns
363 originY: grid.originY
364
365+ function cardPosition(index) {
366+ var pos = {};
367+ var row = Math.floor(index / grid.columns);
368+ var column = index % grid.columns;
369+ // Bit sad this is not symmetrical
370+ pos.x = column * grid.cellWidth + grid.margins;
371+ pos.y = row * grid.cellHeight;
372+ return pos;
373+ }
374+
375 ResponsiveGridView {
376 id: grid
377 anchors.fill: parent
378@@ -53,6 +63,7 @@
379 item.objectName = "delegate" + index;
380 item.width = Qt.binding(function() { return cardTool.cardWidth; });
381 item.height = Qt.binding(function() { return cardTool.cardHeight; });
382+ item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight; });
383 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
384 item.cardData = Qt.binding(function() { return model; });
385 item.template = Qt.binding(function() { return cardTool.template; });
386@@ -62,7 +73,7 @@
387 }
388 Connections {
389 target: loader.item
390- onClicked: root.clicked(index, result)
391+ onClicked: root.clicked(index, result, loader.item, model)
392 onPressAndHold: root.pressAndHold(index)
393 }
394 }
395
396=== modified file 'qml/Dash/CardVerticalJournal.qml'
397--- qml/Dash/CardVerticalJournal.qml 2014-07-22 12:17:34 +0000
398+++ qml/Dash/CardVerticalJournal.qml 2014-07-29 11:05:57 +0000
399@@ -61,7 +61,7 @@
400 }
401 Connections {
402 target: loader.item
403- onClicked: root.clicked(index, result)
404+ onClicked: root.clicked(index, result, loader.item, model)
405 onPressAndHold: root.pressAndHold(index)
406 }
407 }
408
409=== modified file 'qml/Dash/Dash.qml'
410--- qml/Dash/Dash.qml 2014-07-07 08:20:04 +0000
411+++ qml/Dash/Dash.qml 2014-07-29 11:05:57 +0000
412@@ -16,6 +16,7 @@
413
414 import QtQuick 2.0
415 import Ubuntu.Components 0.1
416+import Ubuntu.Gestures 0.1
417 import Unity 0.2
418 import Utils 0.1
419 import "../Components"
420@@ -29,6 +30,8 @@
421 property string showScopeOnLoaded: "clickscope"
422 property real contentScale: 1.0
423
424+ property alias overviewHandleHeight: overviewDragHandle.height
425+
426 function setCurrentScope(scopeId, animate, reset) {
427 var scopeIndex = filteredScopes.findFirst(Scopes.RoleId, scopeId)
428
429@@ -66,19 +69,85 @@
430 filterRegExp: RegExp("^true$")
431 }
432
433+ QtObject {
434+ id: overviewController
435+ objectName: "overviewController"
436+
437+ property alias enableAnimation: progressAnimation.enabled
438+ property real progress: 0
439+ Behavior on progress {
440+ id: progressAnimation
441+ UbuntuNumberAnimation { }
442+ }
443+ }
444+
445+ ScopesOverview {
446+ id: scopesOverview
447+ objectName: "scopesOverview"
448+ anchors.fill: parent
449+ scope: scopes.overviewScope
450+ progress: overviewController.progress
451+ scopeScale: scopeItem.scope ? 0.4 : (1 - overviewController.progress * 0.6)
452+ visible: scopeScale != 1
453+ currentIndex: dashContent.currentIndex
454+ onDone: {
455+ if (currentTab == 1) {
456+ animateDashFromAll(dashContent.currentScopeId);
457+ }
458+ hide();
459+ }
460+ onFavoriteSelected: {
461+ setCurrentScope(scopeId, false, false);
462+ hide();
463+ }
464+ onAllFavoriteSelected: {
465+ setCurrentScope(scopeId, false, false);
466+ animateDashFromAll(dashContent.currentScopeId);
467+ hide();
468+ }
469+ onSearchSelected: {
470+ var scopeIndex = filteredScopes.findFirst(Scopes.RoleId, scopeId);
471+ if (scopeIndex >= 0) {
472+ // Is a favorite one
473+ setCurrentScope(scopeId, false, false);
474+ showDashFromPos(pos, size);
475+ hide();
476+ } else {
477+ // Is not a favorite one, activate and get openScope
478+ scope.activate(result);
479+ }
480+ }
481+ function hide() {
482+ overviewController.enableAnimation = true;
483+ overviewController.progress = 0;
484+ }
485+ onProgressChanged: {
486+ if (progress == 0) {
487+ currentTab = scopeItem.scope ? 1 : 0;
488+ }
489+ }
490+ }
491+
492 DashContent {
493 id: dashContent
494+
495+ property var scopeThatOpenedScope: null
496+
497+ parent: overviewController.progress == 0 ? dash : scopesOverview.dashItemEater
498 objectName: "dashContent"
499- width: parent.width
500- height: parent.height
501+ width: dash.width
502+ height: dash.height
503 model: filteredScopes
504 scopes: scopes
505- visible: x != -width
506+ visible: !scopesOverview.showingNonFavoriteScope && x != -width
507 onGotoScope: {
508 dash.setCurrentScope(scopeId, true, false);
509 }
510 onOpenScope: {
511+ scopeThatOpenedScope = currentScope;
512 scopeItem.scope = scope;
513+ scopesOverview.currentTab = 1;
514+ scopesOverview.ensureAllScopeVisible(scope.id);
515 x = -width;
516 }
517 onScopeLoaded: {
518@@ -88,25 +157,63 @@
519 }
520 }
521 scale: dash.contentScale
522- clip: scale != 1.0 || scopeItem.visible
523+ clip: scale != 1.0 || scopeItem.visible || overviewController.progress != 0
524 Behavior on x {
525 UbuntuNumberAnimation {
526+ duration: overviewController.progress != 0 ? 0 : UbuntuAnimation.FastDuration
527 onRunningChanged: {
528 if (!running && dashContent.x == 0) {
529- dashContent.closeScope(scopeItem.scope);
530+ dashContent.scopeThatOpenedScope.closeScope(scopeItem.scope);
531 scopeItem.scope = null;
532+ if (overviewController.progress == 0) {
533+ // Set tab to Favorites only if we are not showing the overview
534+ scopesOverview.currentTab = 0;
535+ }
536 }
537 }
538 }
539 }
540+
541+ enabled: opacity == 1
542+ opacity: scopesOverview.growingDashFromPos ? 1 : 1 - overviewController.progress
543+ }
544+
545+ DashBackground
546+ {
547+ anchors.fill: scopeItem
548+ visible: scopeItem.visible
549+ parent: scopeItem.parent
550+ scale: scopeItem.scale
551+ opacity: scopeItem.opacity
552 }
553
554 GenericScopeView {
555 id: scopeItem
556- anchors.left: dashContent.right
557+ objectName: "dashTempScopeItem"
558+
559+ readonly property real targetOverviewScale: {
560+ if (scopesOverview.currentTab == 0) {
561+ return 0.4;
562+ } else {
563+ return scopesOverview.allCardSize.width / scopeItem.width;
564+ }
565+ }
566+ readonly property real overviewProgressScale: (1 - overviewController.progress * (1 - targetOverviewScale))
567+ readonly property var targetOverviewPosition: scope ? scopesOverview.allScopeCardPosition(scope.id) : null
568+ readonly property real overviewProgressX: scope && scopesOverview.currentTab == 1 && targetOverviewPosition ?
569+ overviewController.progress * (targetOverviewPosition.x - (width - scopesOverview.allCardSize.width) / 2)
570+ : 0
571+ readonly property real overviewProgressY: scope && scopesOverview.currentTab == 1 && targetOverviewPosition ?
572+ overviewController.progress * (targetOverviewPosition.y - (height - scopesOverview.allCardSize.height) / 2)
573+ : 0
574+
575+ x: overviewController.progress == 0 ? dashContent.x + width : overviewProgressX
576+ y: overviewController.progress == 0 ? dashContent.y : overviewProgressY
577 width: parent.width
578 height: parent.height
579- scale: dash.contentScale
580+ scale: dash.contentScale * overviewProgressScale
581+ enabled: opacity == 1
582+ opacity: 1 - overviewController.progress
583 clip: scale != 1.0
584 visible: scope != null
585 hasBackAction: true
586@@ -126,4 +233,28 @@
587 }
588 }
589 }
590+
591+ EdgeDragArea {
592+ id: overviewDragHandle
593+ objectName: "overviewDragHandle"
594+ z: 1
595+ direction: Direction.Upwards
596+ enabled: !dashContent.previewShown && dashContent.currentScope && dashContent.currentScope.searchQuery == "" && (overviewController.progress == 0 || dragging)
597+
598+ readonly property real fullMovement: units.gu(20)
599+
600+ anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
601+ height: units.gu(2)
602+
603+ onSceneDistanceChanged: {
604+ overviewController.enableAnimation = false;
605+ overviewController.progress = Math.max(0, Math.min(1, sceneDistance / fullMovement));
606+ }
607+
608+ onDraggingChanged: {
609+ overviewController.enableAnimation = true;
610+ overviewController.progress = (overviewController.progress > 0.7) ? 1 : 0;
611+ }
612+ }
613+
614 }
615
616=== added file 'qml/Dash/DashBackground.qml'
617--- qml/Dash/DashBackground.qml 1970-01-01 00:00:00 +0000
618+++ qml/Dash/DashBackground.qml 2014-07-29 11:05:57 +0000
619@@ -0,0 +1,24 @@
620+/*
621+ * Copyright (C) 2013, 2014 Canonical, Ltd.
622+ *
623+ * This program is free software; you can redistribute it and/or modify
624+ * it under the terms of the GNU General Public License as published by
625+ * the Free Software Foundation; version 3.
626+ *
627+ * This program is distributed in the hope that it will be useful,
628+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
629+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
630+ * GNU General Public License for more details.
631+ *
632+ * You should have received a copy of the GNU General Public License
633+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
634+ */
635+
636+import QtQuick 2.0
637+
638+Image {
639+ source: anchors.fill.width > anchors.fill.height ? "graphics/paper_landscape.png" : "graphics/paper_portrait.png"
640+ fillMode: Image.PreserveAspectCrop
641+ horizontalAlignment: Image.AlignRight
642+ verticalAlignment: Image.AlignTop
643+}
644
645=== modified file 'qml/Dash/DashContent.qml'
646--- qml/Dash/DashContent.qml 2014-07-08 19:35:59 +0000
647+++ qml/Dash/DashContent.qml 2014-07-29 11:05:57 +0000
648@@ -26,6 +26,9 @@
649 property var model: null
650 property var scopes: null
651 readonly property alias currentIndex: dashContentList.currentIndex
652+ readonly property string currentScopeId: dashContentList.currentItem ? dashContentList.currentItem.scopeId : ""
653+ readonly property var currentScope: dashContentList.currentItem ? dashContentList.currentItem.theScope : null
654+ readonly property bool previewShown: dashContentList.currentItem && dashContentList.currentItem.item ? dashContentList.currentItem.item.previewShown : false
655
656 signal scopeLoaded(string scopeId)
657 signal gotoScope(string scopeId)
658@@ -76,15 +79,15 @@
659 }
660 }
661
662- function closeScope(scope) {
663- dashContentList.currentItem.theScope.closeScope(scope)
664- }
665-
666 Item {
667 id: dashContentListHolder
668
669 anchors.fill: parent
670
671+ DashBackground {
672+ anchors.fill: parent
673+ }
674+
675 ListView {
676 id: dashContentList
677 objectName: "dashContentList"
678
679=== modified file 'qml/Dash/DashRenderer.qml'
680--- qml/Dash/DashRenderer.qml 2014-07-22 12:17:34 +0000
681+++ qml/Dash/DashRenderer.qml 2014-07-29 11:05:57 +0000
682@@ -41,7 +41,9 @@
683 /// Emitted when the user clicked on an item
684 /// @param index is the index of the clicked item
685 /// @param result result model of the cliked item, used for activation
686- signal clicked(int index, var result)
687+ /// @param item item that has been clicked
688+ /// @param itemModel model of the item
689+ signal clicked(int index, var result, var item, var itemModel)
690
691 /// Emitted when the user pressed and held on an item
692 /// @param index is the index of the held item
693
694=== modified file 'qml/Dash/GenericScopeView.qml'
695--- qml/Dash/GenericScopeView.qml 2014-07-24 20:40:44 +0000
696+++ qml/Dash/GenericScopeView.qml 2014-07-29 11:05:57 +0000
697@@ -33,6 +33,10 @@
698 property bool hasBackAction: false
699 property bool enableHeightBehaviorOnNextCreation: false
700 property var categoryView: categoryView
701+ property bool showPageHeader: true
702+ property var clickOverride: null
703+ property var pressAndHoldOverride: null
704+ readonly property alias previewShown: previewListView.open
705
706 property var scopeStyle: ScopeStyle {
707 style: scope ? scope.customizations : {}
708@@ -78,22 +82,24 @@
709 }
710
711 onIsCurrentChanged: {
712- pageHeader.resetSearch();
713+ if (showPageHeader) {
714+ pageHeaderLoader.item.resetSearch();
715+ }
716 previewListView.open = false;
717 }
718
719 Binding {
720 target: scopeView.scope
721 property: "searchQuery"
722- value: pageHeader.searchQuery
723- when: isCurrent
724+ value: pageHeaderLoader.item ? pageHeaderLoader.item.searchQuery : ""
725+ when: isCurrent && showPageHeader
726 }
727
728 Binding {
729- target: pageHeader
730+ target: pageHeaderLoader.item
731 property: "searchQuery"
732 value: scopeView.scope ? scopeView.scope.searchQuery : ""
733- when: isCurrent
734+ when: isCurrent && showPageHeader
735 }
736
737 Connections {
738@@ -232,6 +238,11 @@
739 Connections {
740 target: rendererLoader.item
741 onClicked: {
742+ if (scopeView.clickOverride) {
743+ scopeView.clickOverride(index, result, item, itemModel);
744+ return;
745+ }
746+
747 if (scopeView.scope.id === "scopes" || scopeView.scope.id == "clickscope") {
748 // TODO Technically it is possible that calling activate() will make the scope emit
749 // previewRequested so that we show a preview but there's no scope that does that yet
750@@ -241,7 +252,14 @@
751 openPreview(index);
752 }
753 }
754- onPressAndHold: openPreview(index)
755+
756+ onPressAndHold: {
757+ if (scopeView.pressAndHoldOverride) {
758+ scopeView.pressAndHoldOverride(index);
759+ } else {
760+ openPreview(index)
761+ }
762+ }
763
764 function openPreview(index) {
765 if (!rendererLoader.expanded && !seeAllLabel.visible && target.collapsedItemCount > 0) {
766@@ -389,26 +407,34 @@
767 }
768 }
769
770- pageHeader: PageHeader {
771- id: pageHeader
772- objectName: "scopePageHeader"
773+ pageHeader: scopeView.showPageHeader ? pageHeaderLoader : null
774+ Loader {
775+ id: pageHeaderLoader
776 width: parent.width
777- title: scopeView.scope ? scopeView.scope.name : ""
778- showBackButton: scopeView.hasBackAction
779- searchEntryEnabled: true
780- searchInProgress: scopeView.scope ? scopeView.scope.searchInProgress : false
781- scopeStyle: scopeView.scopeStyle
782-
783- bottomItem: DashDepartments {
784- scope: scopeView.scope
785- width: parent.width <= units.gu(60) ? parent.width : units.gu(40)
786- anchors.right: parent.right
787- windowHeight: scopeView.height
788- windowWidth: scopeView.width
789- scopeStyle: scopeView.scopeStyle
790+ sourceComponent: scopeView.showPageHeader ? pageHeaderComponent : undefined
791+ Component {
792+ id: pageHeaderComponent
793+ PageHeader {
794+ objectName: "scopePageHeader"
795+ width: parent.width
796+ title: scopeView.scope ? scopeView.scope.name : ""
797+ showBackButton: scopeView.hasBackAction
798+ searchEntryEnabled: true
799+ searchInProgress: scopeView.scope ? scopeView.scope.searchInProgress : false
800+ scopeStyle: scopeView.scopeStyle
801+
802+ bottomItem: DashDepartments {
803+ scope: scopeView.scope
804+ width: parent.width <= units.gu(60) ? parent.width : units.gu(40)
805+ anchors.right: parent.right
806+ windowHeight: scopeView.height
807+ windowWidth: scopeView.width
808+ scopeStyle: scopeView.scopeStyle
809+ }
810+
811+ onBackClicked: scopeView.backClicked()
812+ }
813 }
814-
815- onBackClicked: scopeView.backClicked()
816 }
817 }
818
819@@ -427,7 +453,9 @@
820 anchors.left: categoryView.right
821
822 onOpenChanged: {
823- pageHeader.unfocus();
824+ if (showPageHeader) {
825+ pageHeaderLoader.item.unfocus();
826+ }
827 }
828 }
829
830
831=== modified file 'qml/Dash/PreviewListView.qml'
832--- qml/Dash/PreviewListView.qml 2014-07-17 13:10:45 +0000
833+++ qml/Dash/PreviewListView.qml 2014-07-29 11:05:57 +0000
834@@ -88,7 +88,7 @@
835 height: previewListView.height
836 width: previewListView.width
837
838- readonly property bool ready: preview.previewModel.loaded
839+ readonly property bool ready: preview.previewModel && preview.previewModel.loaded
840
841 Previews.Preview {
842 id: preview
843@@ -108,7 +108,7 @@
844 id: processingMouseArea
845 objectName: "processingMouseArea"
846 anchors.fill: parent
847- enabled: !preview.previewModel.loaded || preview.previewModel.processingAction
848+ enabled: preview.previewModel && (!preview.previewModel.loaded || preview.previewModel.processingAction)
849
850 ActivityIndicator {
851 anchors.centerIn: parent
852
853=== added file 'qml/Dash/ScopesOverview.qml'
854--- qml/Dash/ScopesOverview.qml 1970-01-01 00:00:00 +0000
855+++ qml/Dash/ScopesOverview.qml 2014-07-29 11:05:57 +0000
856@@ -0,0 +1,518 @@
857+/*
858+ * Copyright (C) 2014 Canonical, Ltd.
859+ *
860+ * This program is free software; you can redistribute it and/or modify
861+ * it under the terms of the GNU General Public License as published by
862+ * the Free Software Foundation; version 3.
863+ *
864+ * This program is distributed in the hope that it will be useful,
865+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
866+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
867+ * GNU General Public License for more details.
868+ *
869+ * You should have received a copy of the GNU General Public License
870+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
871+ */
872+
873+import QtQuick 2.0
874+import Dash 0.1
875+import Ubuntu.Components 0.1
876+import "../Components"
877+
878+Item {
879+ id: root
880+
881+ // Properties set by parent
882+ property real progress: 0
883+ property var scope: null
884+ property int currentIndex: 0
885+ property real scopeScale: 1
886+
887+ // Properties set and used by parent
888+ property alias currentTab: tabBar.currentTab
889+
890+ // Properties used by parent
891+ property bool growingDashFromPos: false
892+ readonly property bool searching: scope && scope.searchQuery == ""
893+ readonly property bool showingNonFavoriteScope: tempScopeItem.scope != null
894+ readonly property var dashItemEater: {
895+ if (!forceXYScalerEater && tabBar.currentTab == 0 && middleItems.count > 0) {
896+ var loaderItem = middleItems.itemAt(0).item;
897+ return loaderItem && loaderItem.currentItem ? loaderItem.currentItem : null;
898+ }
899+ return scopesOverviewXYScaler;
900+ }
901+ readonly property size allCardSize: {
902+ if (middleItems.count > 1) {
903+ var loaderItem = middleItems.itemAt(1).item;
904+ if (loaderItem) {
905+ var cardTool = loaderItem.cardTool;
906+ return Qt.size(cardTool.cardWidth, cardTool.cardHeight);
907+ }
908+ }
909+ return Qt.size(0, 0);
910+ }
911+
912+ // Internal properties
913+ property bool forceXYScalerEater: false
914+
915+ signal done()
916+ signal favoriteSelected(var scopeId)
917+ signal allFavoriteSelected(var scopeId)
918+ signal searchSelected(var scopeId, var result, var pos, var size)
919+
920+ Connections {
921+ target: scope
922+ onOpenScope: {
923+ var itemPos = scopesOverviewXYScaler.restorePosition;
924+ var itemSize = scopesOverviewXYScaler.restoreSize;
925+ scopesOverviewXYScaler.scale = itemSize.width / scopesOverviewXYScaler.width;
926+ if (itemPos) {
927+ scopesOverviewXYScaler.x = itemPos.x -(scopesOverviewXYScaler.width - scopesOverviewXYScaler.width * scopesOverviewXYScaler.scale) / 2;
928+ scopesOverviewXYScaler.y = itemPos.y -(scopesOverviewXYScaler.height - scopesOverviewXYScaler.height * scopesOverviewXYScaler.scale) / 2;
929+ } else {
930+ scopesOverviewXYScaler.x = 0;
931+ scopesOverviewXYScaler.y = 0;
932+ }
933+ scopesOverviewXYScaler.opacity = 0;
934+ tempScopeItem.scope = scope;
935+ middleItems.overrideOpacity = 0;
936+ scopesOverviewXYScaler.scale = 1;
937+ scopesOverviewXYScaler.x = 0;
938+ scopesOverviewXYScaler.y = 0;
939+ scopesOverviewXYScaler.opacity = 1;
940+ }
941+ }
942+
943+ function animateDashFromAll(scopeId) {
944+ var currentScopePos = allScopeCardPosition(scopeId);
945+ if (currentScopePos) {
946+ showDashFromPos(currentScopePos, allCardSize);
947+ } else {
948+ console.log("Warning: Could not find Dash OverView All card position for scope", dashContent.currentScopeId);
949+ }
950+ }
951+
952+ function showDashFromPos(itemPos, itemSize) {
953+ scopesOverviewXYScaler.scale = itemSize.width / scopesOverviewXYScaler.width;
954+ scopesOverviewXYScaler.x = itemPos.x -(scopesOverviewXYScaler.width - scopesOverviewXYScaler.width * scopesOverviewXYScaler.scale) / 2;
955+ scopesOverviewXYScaler.y = itemPos.y -(scopesOverviewXYScaler.height - scopesOverviewXYScaler.height * scopesOverviewXYScaler.scale) / 2;
956+ scopesOverviewXYScaler.opacity = 0;
957+ root.growingDashFromPos = true;
958+ scopesOverviewXYScaler.scale = 1;
959+ scopesOverviewXYScaler.x = 0;
960+ scopesOverviewXYScaler.y = 0;
961+ scopesOverviewXYScaler.opacity = 1;
962+ }
963+
964+ function allScopeCardPosition(scopeId) {
965+ if (middleItems.count > 1) {
966+ var loaderItem = middleItems.itemAt(1).item;
967+ if (loaderItem) {
968+ var pos = loaderItem.scopeCardPosition(scopeId);
969+ return loaderItem.mapToItem(null, pos.x, pos.y);
970+ }
971+ }
972+ }
973+
974+ function ensureAllScopeVisible(scopeId) {
975+ if (middleItems.count > 1) {
976+ var loaderItem = middleItems.itemAt(1).item;
977+ if (loaderItem) {
978+ var pos = loaderItem.scopeCardPosition(scopeId);
979+ loaderItem.contentY = Math.min(pos.y, loaderItem.contentHeight - loaderItem.height);
980+ }
981+ }
982+ }
983+
984+ onProgressChanged: {
985+ if (progress == 0) {
986+ pageHeader.resetSearch();
987+ pageHeader.unfocus(); // Shouldn't the previous call do this too?
988+ }
989+ }
990+
991+ ScopeStyle {
992+ id: overviewScopeStyle
993+ style: { "foreground-color" : "white", "background-color" : "transparent" }
994+ }
995+
996+ DashBackground {
997+ anchors.fill: parent
998+ source: "graphics/dark_background.jpg"
999+ }
1000+
1001+ Connections {
1002+ target: pageHeader
1003+ onSearchQueryChanged: {
1004+ // Need this in order, otherwise something gets unhappy in rendering
1005+ // of the overlay in carousels because the parent of the dash dies for
1006+ // a moment, this way we make sure it's reparented first
1007+ // by forceXYScalerEater making dashItemEater return scopesOverviewXYScaler
1008+ // before we kill the previous parent by scope.searchQuery
1009+ root.forceXYScalerEater = true;
1010+ root.scope.searchQuery = pageHeader.searchQuery;
1011+ root.forceXYScalerEater = false;
1012+ }
1013+ }
1014+
1015+ Binding {
1016+ target: pageHeader
1017+ property: "searchQuery"
1018+ value: scope ? scope.searchQuery : ""
1019+ }
1020+
1021+ Item {
1022+ id: scopesOverviewContent
1023+ x: previewListView.open ? -width : 0
1024+ Behavior on x { UbuntuNumberAnimation { } }
1025+ width: parent.width
1026+ height: parent.height
1027+
1028+ PageHeader {
1029+ id: pageHeader
1030+ objectName: "scopesOverviewPageHeader"
1031+
1032+ readonly property real yDisplacement: pageHeader.height + tabBar.height + tabBar.anchors.margins
1033+
1034+ y: {
1035+ if (root.progress < 0.5) {
1036+ return -yDisplacement;
1037+ } else {
1038+ return -yDisplacement + (root.progress - 0.5) * yDisplacement * 2;
1039+ }
1040+ }
1041+ width: parent.width
1042+ clip: true
1043+ title: i18n.tr("Manage Dash")
1044+ scopeStyle: overviewScopeStyle
1045+ showSignatureLine: false
1046+ searchEntryEnabled: true
1047+ searchInProgress: root.scope ? root.scope.searchInProgress : false
1048+ }
1049+
1050+ ScopesOverviewTab {
1051+ id: tabBar
1052+ anchors {
1053+ left: parent.left
1054+ right: parent.right
1055+ top: pageHeader.bottom
1056+ margins: units.gu(2)
1057+ }
1058+ height: units.gu(4)
1059+
1060+ enabled: opacity == 1
1061+ opacity: !scope || scope.searchQuery == "" ? 1 : 0
1062+ Behavior on opacity { UbuntuNumberAnimation { } }
1063+ }
1064+
1065+ Repeater {
1066+ id: middleItems
1067+ objectName: "scopesOverviewRepeater"
1068+ property real overrideOpacity: -1
1069+ model: scope && scope.searchQuery == "" ? scope.categories : null
1070+ delegate: Loader {
1071+ id: loader
1072+ objectName: "scopesOverviewRepeaterChild" + index
1073+
1074+ height: {
1075+ if (index == 0) {
1076+ return root.height;
1077+ } else {
1078+ return root.height - pageHeader.height - tabBar.height - tabBar.anchors.margins - units.gu(2)
1079+ }
1080+ }
1081+ width: {
1082+ if (index == 0) {
1083+ return root.width / scopeScale
1084+ } else {
1085+ return root.width
1086+ }
1087+ }
1088+ x: {
1089+ if (index == 0) {
1090+ return (root.width - width) / 2;
1091+ } else {
1092+ return 0;
1093+ }
1094+ }
1095+ anchors {
1096+ bottom: scopesOverviewContent.bottom
1097+ }
1098+
1099+ scale: index == 0 ? scopeScale : 1
1100+
1101+ opacity: {
1102+ if (middleItems.overrideOpacity >= 0)
1103+ return middleItems.overrideOpacity;
1104+
1105+ if (tabBar.currentTab != index)
1106+ return 0;
1107+
1108+ return index == 0 ? 1 : root.progress;
1109+ }
1110+ Behavior on opacity {
1111+ enabled: root.progress == 1
1112+ UbuntuNumberAnimation { }
1113+ }
1114+ enabled: opacity == 1
1115+
1116+ clip: index == 1
1117+
1118+ CardTool {
1119+ id: cardTool
1120+ objectName: "cardTool"
1121+ count: results.count
1122+ template: model.renderer
1123+ components: model.components
1124+ viewWidth: parent.width
1125+ }
1126+
1127+ source: {
1128+ if (index == 0 && categoryId == "favorites") return "ScopesOverviewFavorites.qml";
1129+ else if (index == 1 && categoryId == "all") return "ScopesOverviewAll.qml";
1130+ else return "";
1131+ }
1132+
1133+ onLoaded: {
1134+ item.model = Qt.binding(function() { return results })
1135+ item.cardTool = cardTool;
1136+ if (index == 0) {
1137+ item.scopeWidth = Qt.binding(function() { return root.width; });
1138+ item.scopeHeight = Qt.binding(function() { return root.height; });
1139+ item.appliedScale = Qt.binding(function() { return loader.scale });
1140+ item.currentIndex = Qt.binding(function() { return root.currentIndex });
1141+ } else if (index == 1) {
1142+ item.extraHeight = bottomBar.height;
1143+ }
1144+ }
1145+
1146+ Connections {
1147+ target: loader.item
1148+ onClicked: {
1149+ if (tabBar.currentTab == 0) {
1150+ root.favoriteSelected(itemModel.scopeId)
1151+ } else {
1152+ var favoriteScopesItem = middleItems.itemAt(0).item;
1153+ var scopeIndex = favoriteScopesItem.model.scopeIndex(itemModel.scopeId);
1154+ if (scopeIndex >= 0) {
1155+ root.allFavoriteSelected(itemModel.scopeId);
1156+ } else {
1157+ // Will result in an openScope from root.scope
1158+ scopesOverviewXYScaler.restorePosition = item.mapToItem(null, 0, 0);
1159+ scopesOverviewXYScaler.restoreSize = allCardSize;
1160+ root.scope.activate(result);
1161+ }
1162+ }
1163+ }
1164+ onPressAndHold: {
1165+ // Preview can call openScope so make sure restorePosition and restoreSize are set
1166+ scopesOverviewXYScaler.restorePosition = undefined;
1167+ scopesOverviewXYScaler.restoreSize = allCardSize;
1168+
1169+ previewListView.model = target.model;
1170+ previewListView.currentIndex = -1
1171+ previewListView.currentIndex = index;
1172+ previewListView.open = true
1173+ }
1174+ }
1175+ }
1176+ }
1177+
1178+ GenericScopeView {
1179+ id: searchResultsViewer
1180+ objectName: "searchResultsViewer"
1181+ anchors {
1182+ top: pageHeader.bottom
1183+ right: parent.right
1184+ left: parent.left
1185+ bottom: parent.bottom
1186+ }
1187+ scope: root.scope && root.scope.searchQuery != "" ? root.scope : null
1188+ scopeStyle: overviewScopeStyle
1189+ enabled: opacity == 1
1190+ showPageHeader: false
1191+ clip: true
1192+ opacity: searchResultsViewer.scope ? 1 : 0
1193+ Behavior on opacity { UbuntuNumberAnimation { } }
1194+
1195+ clickOverride: function (index, result, item, itemModel) {
1196+ pageHeader.closePopup();
1197+ if (itemModel.scopeId) {
1198+ // This can end up in openScope so save restorePosition and restoreSize
1199+ scopesOverviewXYScaler.restorePosition = item.mapToItem(null, 0, 0);
1200+ scopesOverviewXYScaler.restoreSize = Qt.size(item.width, item.height);
1201+ root.searchSelected(itemModel.scopeId, result, item.mapToItem(null, 0, 0), Qt.size(item.width, item.height));
1202+ } else {
1203+ // Not a scope, just activate it
1204+ searchResultsViewer.scope.activate(result);
1205+ }
1206+ }
1207+
1208+ pressAndHoldOverride: function (index) {
1209+ // Do nothing
1210+ }
1211+ }
1212+
1213+ Rectangle {
1214+ id: bottomBar
1215+ color: "black"
1216+ height: units.gu(6)
1217+ width: parent.width
1218+ enabled: opacity == 0.4
1219+ opacity: scope && scope.searchQuery == "" ? 0.4 : 0
1220+ Behavior on opacity { UbuntuNumberAnimation { } }
1221+ y: {
1222+ if (root.progress < 0.5) {
1223+ return parent.height;
1224+ } else {
1225+ return parent.height - (root.progress - 0.5) * height * 2;
1226+ }
1227+ }
1228+
1229+ AbstractButton {
1230+ objectName: "scopesOverviewDoneButton"
1231+ width: Math.max(label.width + units.gu(2), units.gu(10))
1232+ height: units.gu(4)
1233+ anchors {
1234+ left: parent.left
1235+ leftMargin: units.gu(2)
1236+ verticalCenter: parent.verticalCenter
1237+ }
1238+ Rectangle {
1239+ anchors.fill: parent
1240+ border.color: "white"
1241+ border.width: units.dp(1)
1242+ radius: units.dp(10)
1243+ color: parent.pressed ? "gray" : "transparent"
1244+ }
1245+ Label {
1246+ id: label
1247+ anchors.centerIn: parent
1248+ text: i18n.tr("Done")
1249+ color: parent.pressed ? "black" : "white"
1250+ }
1251+ onClicked: root.done();
1252+ }
1253+
1254+ AbstractButton {
1255+ objectName: "scopesOverviewStoreButton"
1256+ width: Math.max(storeLabel.width, units.gu(10))
1257+ height: units.gu(4)
1258+ anchors {
1259+ right: parent.right
1260+ verticalCenter: parent.verticalCenter
1261+ }
1262+ Image {
1263+ id: storeImage
1264+ source: "graphics/apps-scope.png"
1265+ anchors.horizontalCenter: parent.horizontalCenter
1266+ width: units.gu(2)
1267+ height: units.gu(2)
1268+ fillMode: Image.Stretch
1269+ }
1270+ Label {
1271+ id: storeLabel
1272+ anchors.horizontalCenter: parent.horizontalCenter
1273+ anchors.top: storeImage.bottom
1274+ text: i18n.tr("Store")
1275+ color: "white"
1276+ }
1277+ onClicked: {
1278+ // Just zoom from the middle
1279+ scopesOverviewXYScaler.restorePosition = undefined;
1280+ scopesOverviewXYScaler.restoreSize = allCardSize;
1281+ scope.performQuery("scope://com.canonical.scopes.clickstore");
1282+ }
1283+ }
1284+ }
1285+ }
1286+
1287+ PreviewListView {
1288+ id: previewListView
1289+ objectName: "scopesOverviewPreviewListView"
1290+ scope: root.scope
1291+ scopeStyle: overviewScopeStyle
1292+ visible: x != width
1293+ width: parent.width
1294+ height: parent.height
1295+ anchors.left: scopesOverviewContent.right
1296+ }
1297+
1298+
1299+
1300+ Item {
1301+ id: scopesOverviewXYScaler
1302+ width: parent.width
1303+ height: parent.height
1304+
1305+ clip: scale != 1.0
1306+ enabled: scale == 1
1307+
1308+ property bool animationsEnabled: root.showingNonFavoriteScope || root.growingDashFromPos
1309+
1310+ property var restorePosition
1311+ property var restoreSize
1312+
1313+ Behavior on x {
1314+ enabled: scopesOverviewXYScaler.animationsEnabled
1315+ UbuntuNumberAnimation { }
1316+ }
1317+ Behavior on y {
1318+ enabled: scopesOverviewXYScaler.animationsEnabled
1319+ UbuntuNumberAnimation { }
1320+ }
1321+ Behavior on opacity {
1322+ enabled: scopesOverviewXYScaler.animationsEnabled
1323+ UbuntuNumberAnimation { }
1324+ }
1325+ Behavior on scale {
1326+ enabled: scopesOverviewXYScaler.animationsEnabled
1327+ UbuntuNumberAnimation {
1328+ onRunningChanged: {
1329+ if (!running) {
1330+ if (root.showingNonFavoriteScope && scopesOverviewXYScaler.scale != 1) {
1331+ root.scope.closeScope(tempScopeItem.scope);
1332+ tempScopeItem.scope = null;
1333+ } else if (root.growingDashFromPos) {
1334+ root.growingDashFromPos = false;
1335+ }
1336+ }
1337+ }
1338+ }
1339+ }
1340+
1341+ DashBackground
1342+ {
1343+ anchors.fill: tempScopeItem
1344+ visible: tempScopeItem.visible
1345+ parent: tempScopeItem.parent
1346+ }
1347+
1348+ GenericScopeView {
1349+ id: tempScopeItem
1350+ objectName: "scopesOverviewTempScopeItem"
1351+
1352+ width: parent.width
1353+ height: parent.height
1354+ scale: dash.contentScale
1355+ clip: scale != 1.0
1356+ visible: scope != null
1357+ hasBackAction: true
1358+ isCurrent: visible
1359+ onBackClicked: {
1360+ var v = scopesOverviewXYScaler.restoreSize.width / tempScopeItem.width;
1361+ scopesOverviewXYScaler.scale = v;
1362+ if (scopesOverviewXYScaler.restorePosition) {
1363+ scopesOverviewXYScaler.x = scopesOverviewXYScaler.restorePosition.x -(tempScopeItem.width - tempScopeItem.width * v) / 2;
1364+ scopesOverviewXYScaler.y = scopesOverviewXYScaler.restorePosition.y -(tempScopeItem.height - tempScopeItem.height * v) / 2;
1365+ } else {
1366+ scopesOverviewXYScaler.x = 0;
1367+ scopesOverviewXYScaler.y = 0;
1368+ }
1369+ scopesOverviewXYScaler.opacity = 0;
1370+ middleItems.overrideOpacity = -1;
1371+ }
1372+ }
1373+ }
1374+}
1375
1376=== added file 'qml/Dash/ScopesOverviewAll.qml'
1377--- qml/Dash/ScopesOverviewAll.qml 1970-01-01 00:00:00 +0000
1378+++ qml/Dash/ScopesOverviewAll.qml 2014-07-29 11:05:57 +0000
1379@@ -0,0 +1,54 @@
1380+/*
1381+ * Copyright (C) 2014 Canonical, Ltd.
1382+ *
1383+ * This program is free software; you can redistribute it and/or modify
1384+ * it under the terms of the GNU General Public License as published by
1385+ * the Free Software Foundation; version 3.
1386+ *
1387+ * This program is distributed in the hope that it will be useful,
1388+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1389+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1390+ * GNU General Public License for more details.
1391+ *
1392+ * You should have received a copy of the GNU General Public License
1393+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1394+ */
1395+
1396+import QtQuick 2.0
1397+import Ubuntu.Components 0.1
1398+
1399+Flickable {
1400+ id: root
1401+
1402+ property alias model: cardGrid.model
1403+ property alias cardTool: cardGrid.cardTool
1404+
1405+ property real extraHeight: 0
1406+
1407+ signal clicked(int index, var result, var item, var itemModel)
1408+ signal pressAndHold(int index)
1409+
1410+ contentHeight: cardGrid.expandedHeight + extraHeight
1411+ contentWidth: cardGrid.width
1412+ flickableDirection: Flickable.VerticalFlick
1413+
1414+ function scopeCardPosition(scopeId) {
1415+ var index = model.scopeIndex(scopeId);
1416+ var pos = cardGrid.cardPosition(index);
1417+ pos.y = pos.y - root.contentY;
1418+ return pos;
1419+ }
1420+
1421+ CardGrid {
1422+ id: cardGrid
1423+ width: root.width
1424+ height: parent.height
1425+
1426+ onClicked: {
1427+ root.clicked(index, result, item, itemModel);
1428+ }
1429+ onPressAndHold: {
1430+ root.pressAndHold(index);
1431+ }
1432+ }
1433+}
1434
1435=== added file 'qml/Dash/ScopesOverviewFavorites.qml'
1436--- qml/Dash/ScopesOverviewFavorites.qml 1970-01-01 00:00:00 +0000
1437+++ qml/Dash/ScopesOverviewFavorites.qml 2014-07-29 11:05:57 +0000
1438@@ -0,0 +1,73 @@
1439+/*
1440+ * Copyright (C) 2014 Canonical, Ltd.
1441+ *
1442+ * This program is free software; you can redistribute it and/or modify
1443+ * it under the terms of the GNU General Public License as published by
1444+ * the Free Software Foundation; version 3.
1445+ *
1446+ * This program is distributed in the hope that it will be useful,
1447+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1448+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1449+ * GNU General Public License for more details.
1450+ *
1451+ * You should have received a copy of the GNU General Public License
1452+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1453+ */
1454+
1455+import QtQuick 2.0
1456+
1457+Flickable {
1458+ id: root
1459+
1460+ signal clicked(int index, var result, var itemModel)
1461+ signal pressAndHold(int index)
1462+
1463+ property var cardTool: null
1464+ property real scopeHeight: 0
1465+ property real scopeWidth: 0
1466+ property real appliedScale: 1
1467+ property int currentIndex: -1
1468+ property var currentItem: repeater.itemAt(currentIndex);
1469+
1470+ property alias model: repeater.model
1471+
1472+ contentHeight: height
1473+ contentWidth: repeater.count * root.scopeWidth + units.gu(2) / appliedScale * (repeater.count - 1)
1474+
1475+ contentX: {
1476+ var indexX = currentIndex * scopeWidth + units.gu(2) / appliedScale * currentIndex;
1477+ var newContentX = indexX - (width - scopeWidth) / 2;
1478+ newContentX = Math.min(Math.max(newContentX, 0), contentWidth - width);
1479+ return newContentX;
1480+ }
1481+
1482+ Repeater {
1483+ id: repeater
1484+ objectName: "scopesOverviewFavoritesRepeater"
1485+
1486+ delegate: Loader {
1487+ id: loader
1488+
1489+ x: index * root.scopeWidth + units.gu(2) / appliedScale * index
1490+ asynchronous: true
1491+
1492+ sourceComponent: cardTool.cardComponent
1493+ onLoaded: {
1494+ item.fixedArtShapeSize = Qt.binding(function() { return Qt.size(root.scopeWidth, root.scopeHeight); });
1495+ item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight / appliedScale; });
1496+ item.fontScale = Qt.binding(function() { return 1 / appliedScale; });
1497+ item.height = Qt.binding(function() { return root.scopeHeight; });
1498+ item.width = Qt.binding(function() { return root.scopeWidth; });
1499+ item.cardData = Qt.binding(function() { return model; });
1500+ item.template = Qt.binding(function() { return cardTool.template; });
1501+ item.components = Qt.binding(function() { return cardTool.components; });
1502+ item.headerAlignment = Qt.binding(function() { return cardTool.headerAlignment; });
1503+ }
1504+
1505+ Connections {
1506+ target: loader.item
1507+ onClicked: root.clicked(index, result, model)
1508+ }
1509+ }
1510+ }
1511+}
1512
1513=== added file 'qml/Dash/ScopesOverviewTab.qml'
1514--- qml/Dash/ScopesOverviewTab.qml 1970-01-01 00:00:00 +0000
1515+++ qml/Dash/ScopesOverviewTab.qml 2014-07-29 11:05:57 +0000
1516@@ -0,0 +1,74 @@
1517+/*
1518+ * Copyright (C) 2014 Canonical, Ltd.
1519+ *
1520+ * This program is free software; you can redistribute it and/or modify
1521+ * it under the terms of the GNU General Public License as published by
1522+ * the Free Software Foundation; version 3.
1523+ *
1524+ * This program is distributed in the hope that it will be useful,
1525+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1526+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1527+ * GNU General Public License for more details.
1528+ *
1529+ * You should have received a copy of the GNU General Public License
1530+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1531+ */
1532+
1533+import QtQuick 2.3
1534+import Ubuntu.Components 0.1
1535+
1536+Item {
1537+ id: root
1538+
1539+ property int currentTab: 0
1540+
1541+ AbstractButton {
1542+ id: tab1
1543+ height: parent.height
1544+ width: parent.width / 2
1545+ Rectangle {
1546+ anchors.fill: parent
1547+ color: root.currentTab == 0 && root.enabled ? "white" : "transparent"
1548+ radius: units.dp(10)
1549+ }
1550+ Label {
1551+ anchors.centerIn: parent
1552+ text: i18n.tr("Favorites")
1553+ color: root.currentTab == 0 && root.enabled ? "black" : "white"
1554+ }
1555+ onClicked: root.currentTab = 0
1556+ }
1557+ AbstractButton {
1558+ id: tab2
1559+ objectName: "scopesOverviewAllTabButton"
1560+ x: width
1561+ height: parent.height
1562+ width: parent.width / 2
1563+ Rectangle {
1564+ anchors.fill: parent
1565+ color: root.currentTab == 1 && root.enabled ? "white" : "transparent"
1566+ radius: units.dp(10)
1567+ }
1568+ Label {
1569+ anchors.centerIn: parent
1570+ text: i18n.tr("All")
1571+ color: root.currentTab == 1 && root.enabled ? "black" : "white"
1572+ }
1573+ onClicked: root.currentTab = 1
1574+ }
1575+ Rectangle {
1576+ id: centerPiece
1577+ width: root.enabled ? units.dp(10) : units.dp(1)
1578+ height: parent.height
1579+ color: "white"
1580+ x: root.currentTab == 1 ? tab2.x : tab2.x - width
1581+ }
1582+ Rectangle {
1583+ id: border
1584+ anchors.fill: parent
1585+ radius: units.dp(10)
1586+ color: "transparent"
1587+ border.color: centerPiece.color
1588+ border.width: units.dp(1)
1589+ }
1590+}
1591
1592=== added file 'qml/Dash/graphics/apps-scope.png'
1593Binary files qml/Dash/graphics/apps-scope.png 1970-01-01 00:00:00 +0000 and qml/Dash/graphics/apps-scope.png 2014-07-29 11:05:57 +0000 differ
1594=== added file 'qml/Dash/graphics/dark_background.jpg'
1595Binary files qml/Dash/graphics/dark_background.jpg 1970-01-01 00:00:00 +0000 and qml/Dash/graphics/dark_background.jpg 2014-07-29 11:05:57 +0000 differ
1596=== modified file 'qml/Shell.qml'
1597--- qml/Shell.qml 2014-07-18 15:13:35 +0000
1598+++ qml/Shell.qml 2014-07-29 11:05:57 +0000
1599@@ -125,14 +125,6 @@
1600 opacity: dash.disappearingAnimationProgress
1601 }
1602
1603- Image {
1604- anchors.fill: dash
1605- source: shell.width > shell.height ? "Dash/graphics/paper_landscape.png" : "Dash/graphics/paper_portrait.png"
1606- fillMode: Image.PreserveAspectCrop
1607- horizontalAlignment: Image.AlignRight
1608- verticalAlignment: Image.AlignTop
1609- }
1610-
1611 Dash {
1612 id: dash
1613 objectName: "dash"
1614@@ -142,6 +134,8 @@
1615 shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0
1616 enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
1617
1618+ overviewHandleHeight: shell.edgeSize
1619+
1620 anchors {
1621 fill: parent
1622 topMargin: panel.panelHeight
1623
1624=== modified file 'tests/autopilot/unity8/shell/emulators/dash.py'
1625--- tests/autopilot/unity8/shell/emulators/dash.py 2014-07-25 10:47:19 +0000
1626+++ tests/autopilot/unity8/shell/emulators/dash.py 2014-07-29 11:05:57 +0000
1627@@ -18,6 +18,7 @@
1628 #
1629
1630 import logging
1631+import time
1632 import ubuntuuitoolkit
1633
1634 from unity8.shell import emulators
1635@@ -68,7 +69,9 @@
1636
1637 """
1638 scope_loader = self._get_scope_loader(scope_id)
1639+ print ("open_scope", scope_id, scope_loader)
1640 if scope_loader.isCurrent:
1641+ print ("scope_loader.isCurrent")
1642 logger.info('The scope is already open.')
1643 return self._get_scope_from_loader(scope_loader)
1644 else:
1645@@ -83,6 +86,12 @@
1646 'No scope found with id {0}'.format(scope_id))
1647
1648 def _get_scope_from_loader(self, loader):
1649+ print ("_get_scope_from_loader", loader, loader.isCurrent, loader.isLoaded, loader.get_children())
1650+ i = 0
1651+ time.sleep(1)
1652+ while len(loader.get_children()) < 1 and i < 5:
1653+ time.sleep(1)
1654+ i = i + 1
1655 return loader.get_children()[0]
1656
1657 def _open_scope_scrolling(self, scope_loader):
1658
1659=== modified file 'tests/mocks/Unity/CMakeLists.txt'
1660--- tests/mocks/Unity/CMakeLists.txt 2014-07-08 09:23:14 +0000
1661+++ tests/mocks/Unity/CMakeLists.txt 2014-07-29 11:05:57 +0000
1662@@ -6,7 +6,7 @@
1663 pkg_search_module(DEE dee-1.0 REQUIRED)
1664 pkg_search_module(GOBJECT gobject-2.0 REQUIRED)
1665 pkg_search_module(DEEQT libdee-qt5 REQUIRED)
1666-pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=2)
1667+pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=3)
1668
1669 include_directories(
1670 ${CMAKE_CURRENT_BINARY_DIR}
1671@@ -21,6 +21,7 @@
1672 set(UnityQML_SOURCES
1673 fake_scope.cpp
1674 fake_scopes.cpp
1675+ fake_scopesoverview.cpp
1676 fake_categories.cpp
1677 fake_department.cpp
1678 fake_resultsmodel.cpp
1679
1680=== modified file 'tests/mocks/Unity/fake_resultsmodel.cpp'
1681--- tests/mocks/Unity/fake_resultsmodel.cpp 2014-05-20 10:29:20 +0000
1682+++ tests/mocks/Unity/fake_resultsmodel.cpp 2014-07-29 11:05:57 +0000
1683@@ -58,8 +58,9 @@
1684 case RoleUri:
1685 case RoleCategoryId:
1686 case RoleDndUri:
1687+ return QString();
1688 case RoleResult:
1689- return QString();
1690+ return QString("Result.%1.%2").arg(m_categoryId).arg(index.row());
1691 case RoleTitle:
1692 return QString("Title.%1.%2").arg(m_categoryId).arg(index.row());
1693 case RoleArt:
1694
1695=== modified file 'tests/mocks/Unity/fake_scope.cpp'
1696--- tests/mocks/Unity/fake_scope.cpp 2014-07-24 20:40:57 +0000
1697+++ tests/mocks/Unity/fake_scope.cpp 2014-07-29 11:05:57 +0000
1698@@ -14,17 +14,20 @@
1699 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1700 */
1701
1702+#include <QDebug>
1703 #include <QUrl>
1704
1705 #include "fake_scope.h"
1706+
1707 #include "fake_department.h"
1708 #include "fake_resultsmodel.h"
1709+#include "fake_scopes.h"
1710
1711-Scope::Scope(QObject* parent) : Scope(QString(), QString(), false, parent)
1712+Scope::Scope(Scopes* parent) : Scope(QString(), QString(), false, parent)
1713 {
1714 }
1715
1716-Scope::Scope(QString const& id, QString const& name, bool visible, QObject* parent, int categories)
1717+Scope::Scope(QString const& id, QString const& name, bool visible, Scopes* parent, int categories)
1718 : unity::shell::scopes::ScopeInterface(parent)
1719 , m_id(id)
1720 , m_name(name)
1721@@ -34,6 +37,7 @@
1722 , m_currentDeparment("root")
1723 , m_previewRendererName("preview-generic")
1724 , m_categories(new Categories(categories, this))
1725+ , m_openScope(nullptr)
1726 {
1727 }
1728
1729@@ -149,7 +153,12 @@
1730
1731 void Scope::activate(QVariant const& result)
1732 {
1733- Q_UNUSED(result);
1734+ qDebug() << "Called activate on scope" << m_id << "with result" << result;
1735+ if (result.toString() == "Result.2.2") {
1736+ Scopes *scopes = dynamic_cast<Scopes*>(parent());
1737+ m_openScope = scopes->getScopeFromAll("MockScope9");
1738+ Q_EMIT openScope(m_openScope);
1739+ }
1740 }
1741
1742 PreviewStack* Scope::preview(QVariant const& result)
1743@@ -165,9 +174,13 @@
1744 {
1745 }
1746
1747-void Scope::closeScope(unity::shell::scopes::ScopeInterface* /*scope*/)
1748+void Scope::closeScope(unity::shell::scopes::ScopeInterface* scope)
1749 {
1750- qFatal("Scope::closeScope is not implemented");
1751+ if (scope != m_openScope) {
1752+ qDebug() << scope << m_openScope;
1753+ qFatal("Scope::closeScope got wrong scope in closeScope");
1754+ }
1755+ m_openScope = nullptr;
1756 }
1757
1758 QString Scope::currentDepartmentId() const
1759
1760=== modified file 'tests/mocks/Unity/fake_scope.h'
1761--- tests/mocks/Unity/fake_scope.h 2014-07-11 16:39:33 +0000
1762+++ tests/mocks/Unity/fake_scope.h 2014-07-29 11:05:57 +0000
1763@@ -24,13 +24,15 @@
1764
1765 #include <QTimer>
1766
1767+class Scopes;
1768+
1769 class Scope : public unity::shell::scopes::ScopeInterface
1770 {
1771 Q_OBJECT
1772
1773 public:
1774- Scope(QObject* parent = 0);
1775- Scope(QString const& id, QString const& name, bool visible, QObject* parent = 0, int categories = 20);
1776+ Scope(Scopes* parent = 0);
1777+ Scope(QString const& id, QString const& name, bool visible, Scopes* parent = 0, int categories = 20);
1778
1779 /* getters */
1780 QString id() const override;
1781@@ -84,7 +86,8 @@
1782
1783 QString m_previewRendererName;
1784
1785- Categories* m_categories;
1786+ unity::shell::scopes::CategoriesInterface* m_categories;
1787+ unity::shell::scopes::ScopeInterface* m_openScope;
1788 };
1789
1790 #endif // FAKE_SCOPE_H
1791
1792=== modified file 'tests/mocks/Unity/fake_scopes.cpp'
1793--- tests/mocks/Unity/fake_scopes.cpp 2014-07-11 11:45:45 +0000
1794+++ tests/mocks/Unity/fake_scopes.cpp 2014-07-29 11:05:57 +0000
1795@@ -18,6 +18,7 @@
1796
1797 // Self
1798 #include "fake_scopes.h"
1799+#include "fake_scopesoverview.h"
1800
1801 // TODO: Implement remaining pieces, like Categories (i.e. LensView now gives warnings)
1802
1803@@ -26,6 +27,7 @@
1804
1805 Scopes::Scopes(QObject *parent)
1806 : unity::shell::scopes::ScopesInterface(parent)
1807+ , m_scopesOverview(nullptr)
1808 , m_loaded(false)
1809 , timer(this)
1810 {
1811@@ -47,10 +49,17 @@
1812 addScope(new Scope("clickscope", "Apps", true, this));
1813 addScope(new Scope("MockScope5", "Videos", true, this));
1814 addScope(new Scope("SingleCategoryScope", "Single", true, this, 1));
1815+ addScope(new Scope("MockScope4", "MS4", true, this));
1816+ addScope(new Scope("MockScope6", "MS6", true, this));
1817+ addScope(new Scope("MockScope7", "MS7", false, this));
1818+ addScope(new Scope("MockScope8", "MS8", false, this));
1819+ addScope(new Scope("MockScope9", "MS9", false, this));
1820+ m_scopesOverview = new ScopesOverview(this);
1821
1822 if (!m_loaded) {
1823 m_loaded = true;
1824 Q_EMIT loadedChanged();
1825+ Q_EMIT overviewScopeChanged();
1826 }
1827 }
1828
1829@@ -63,6 +72,8 @@
1830 m_scopes.clear();
1831 endRemoveRows();
1832 }
1833+ delete m_scopesOverview;
1834+ m_scopesOverview = nullptr;
1835
1836 if (m_loaded) {
1837 m_loaded = false;
1838@@ -110,8 +121,22 @@
1839 return m_scopes[row];
1840 }
1841
1842-unity::shell::scopes::ScopeInterface* Scopes::getScope(QString const&) const
1843-{
1844+unity::shell::scopes::ScopeInterface* Scopes::getScope(QString const &scope_id) const
1845+{
1846+ for (Scope *scope : m_scopes) {
1847+ // According to mh3 Scopes::getScope should only return non null for visible scopes
1848+ if (scope->id() == scope_id && scope->visible())
1849+ return scope;
1850+ }
1851+ return nullptr;
1852+}
1853+
1854+unity::shell::scopes::ScopeInterface* Scopes::getScopeFromAll(const QString& scope_id) const
1855+{
1856+ for (Scope *scope : m_scopes) {
1857+ if (scope->id() == scope_id)
1858+ return scope;
1859+ }
1860 return nullptr;
1861 }
1862
1863@@ -125,6 +150,22 @@
1864 return m_loaded;
1865 }
1866
1867+unity::shell::scopes::ScopeInterface* Scopes::overviewScope() const
1868+{
1869+ return m_scopesOverview;
1870+}
1871+
1872+QList<unity::shell::scopes::ScopeInterface *> Scopes::scopes(bool onlyVisible) const
1873+{
1874+ QList<unity::shell::scopes::ScopeInterface *> res;
1875+ for (Scope *scope : m_scopes) {
1876+ if (!onlyVisible || scope->visible()) {
1877+ res << scope;
1878+ }
1879+ }
1880+ return res;
1881+}
1882+
1883 void Scopes::addScope(Scope* scope)
1884 {
1885 int index = rowCount();
1886
1887=== modified file 'tests/mocks/Unity/fake_scopes.h'
1888--- tests/mocks/Unity/fake_scopes.h 2014-05-20 14:48:08 +0000
1889+++ tests/mocks/Unity/fake_scopes.h 2014-07-29 11:05:57 +0000
1890@@ -50,12 +50,18 @@
1891 QModelIndex parent ( const QModelIndex & index ) const;
1892
1893 bool loaded() const override;
1894+ unity::shell::scopes::ScopeInterface* overviewScope() const override;
1895+
1896+ // This is used as part of implementation of the other C++ code, not API
1897+ QList<unity::shell::scopes::ScopeInterface *> scopes(bool onlyVisible) const;
1898+ unity::shell::scopes::ScopeInterface* getScopeFromAll(const QString& scope_id) const;
1899
1900 private Q_SLOTS:
1901 void updateScopes();
1902
1903 private:
1904 QList<Scope*> m_scopes;
1905+ Scope *m_scopesOverview;
1906 bool m_loaded;
1907 QTimer timer;
1908 };
1909
1910=== added file 'tests/mocks/Unity/fake_scopesoverview.cpp'
1911--- tests/mocks/Unity/fake_scopesoverview.cpp 1970-01-01 00:00:00 +0000
1912+++ tests/mocks/Unity/fake_scopesoverview.cpp 2014-07-29 11:05:57 +0000
1913@@ -0,0 +1,280 @@
1914+/*
1915+ * Copyright (C) 2014 Canonical, Ltd.
1916+ *
1917+ * This program is free software; you can redistribute it and/or modify
1918+ * it under the terms of the GNU General Public License as published by
1919+ * the Free Software Foundation; version 3.
1920+ *
1921+ * This program is distributed in the hope that it will be useful,
1922+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1923+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1924+ * GNU General Public License for more details.
1925+ *
1926+ * You should have received a copy of the GNU General Public License
1927+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1928+ */
1929+
1930+#include "fake_scopesoverview.h"
1931+
1932+#include "fake_scopes.h"
1933+
1934+#include <paths.h>
1935+
1936+ScopesOverview::ScopesOverview(Scopes* parent)
1937+ : Scope("scopesOverview", "Scopes Overview", false, parent)
1938+{
1939+ delete m_categories; // delete the usual categories, we're not going to use it
1940+ m_scopesOverviewCategories = new ScopesOverviewCategories(parent, this);
1941+ m_searchCategories = new ScopesOverviewSearchCategories(parent, this);
1942+ m_categories = m_scopesOverviewCategories;
1943+}
1944+
1945+void ScopesOverview::setSearchQuery(const QString& search_query)
1946+{
1947+ Scope::setSearchQuery(search_query);
1948+
1949+ auto origCategories = m_categories;
1950+ if (search_query.isEmpty()) m_categories = m_scopesOverviewCategories;
1951+ else m_categories = m_searchCategories;
1952+
1953+ if (m_categories != origCategories)
1954+ Q_EMIT categoriesChanged();
1955+}
1956+
1957+Q_INVOKABLE void ScopesOverview::activate(QVariant const& result)
1958+{
1959+ Scopes *scopes = dynamic_cast<Scopes*>(parent());
1960+ m_openScope = scopes->getScopeFromAll(result.toString());
1961+ Q_EMIT openScope(m_openScope);
1962+}
1963+
1964+ScopesOverviewCategories::ScopesOverviewCategories(Scopes *scopes, QObject* parent)
1965+ : unity::shell::scopes::CategoriesInterface(parent)
1966+ , m_scopes(scopes)
1967+{
1968+}
1969+
1970+int ScopesOverviewCategories::rowCount(const QModelIndex& /*parent*/) const
1971+{
1972+ return 2;
1973+}
1974+
1975+void ScopesOverviewCategories::addSpecialCategory(QString const&, QString const&, QString const&, QString const&, QObject*)
1976+{
1977+ qFatal("Using un-implemented ScopesOverviewCategories::addSpecialCategory");
1978+}
1979+
1980+bool ScopesOverviewCategories::overrideCategoryJson(QString const& /* categoryId */, QString const& /* json */)
1981+{
1982+ qFatal("Using un-implemented ScopesOverviewCategories::overrideCategoryJson");
1983+}
1984+
1985+QVariant
1986+ScopesOverviewCategories::data(const QModelIndex& index, int role) const
1987+{
1988+ if (!index.isValid()) {
1989+ return QVariant();
1990+ }
1991+
1992+ const QString categoryId = index.row() == 0 ? "favorites" : "all";
1993+
1994+ unity::shell::scopes::ResultsModelInterface *resultsModel = m_resultsModels[index.row()];
1995+ if (!resultsModel) {
1996+ QObject *that = const_cast<ScopesOverviewCategories*>(this);
1997+ resultsModel = new ScopesOverviewResultsModel(m_scopes->scopes(index.row() == 0), categoryId, that);
1998+ m_resultsModels[index.row()] = resultsModel;
1999+ }
2000+ switch (role) {
2001+ case RoleCategoryId:
2002+ return categoryId;
2003+ case RoleName:
2004+ return index.row() == 0 ? "Favorites" : "All";
2005+ case RoleIcon:
2006+ return QVariant();
2007+ case RoleRawRendererTemplate:
2008+ qFatal("Using un-implemented RoleRawRendererTemplate Categories role");
2009+ return QVariant();
2010+ case RoleRenderer:
2011+ {
2012+ QVariantMap map;
2013+ map["category-layout"] = "grid";
2014+ map["card-size"] = "small";
2015+ map["overlay"] = true;
2016+ return map;
2017+ }
2018+ case RoleComponents:
2019+ {
2020+ QVariantMap map, artMap;
2021+ artMap["aspect-ratio"] = "0.5";
2022+ artMap["field"] = "art";
2023+ map["art"] = artMap;
2024+ map["title"] = "HOLA";
2025+ return map;
2026+ }
2027+ case RoleResults:
2028+ return QVariant::fromValue(resultsModel);
2029+ case RoleCount:
2030+ return resultsModel->rowCount();
2031+ case RoleHeaderLink:
2032+ return QString();
2033+ default:
2034+ qFatal("Using un-implemented Categories role");
2035+ return QVariant();
2036+ }
2037+}
2038+
2039+
2040+
2041+ScopesOverviewSearchCategories::ScopesOverviewSearchCategories(Scopes *scopes, QObject* parent)
2042+ : unity::shell::scopes::CategoriesInterface(parent)
2043+ , m_scopes(scopes)
2044+{
2045+}
2046+
2047+int ScopesOverviewSearchCategories::rowCount(const QModelIndex& /*parent*/) const
2048+{
2049+ return 2;
2050+}
2051+
2052+void ScopesOverviewSearchCategories::addSpecialCategory(QString const&, QString const&, QString const&, QString const&, QObject*)
2053+{
2054+ qFatal("Using un-implemented ScopesOverviewSearchCategories::addSpecialCategory");
2055+}
2056+
2057+bool ScopesOverviewSearchCategories::overrideCategoryJson(QString const& /* categoryId */, QString const& /* json */)
2058+{
2059+ qFatal("Using un-implemented ScopesOverviewSearchCategories::overrideCategoryJson");
2060+}
2061+
2062+QVariant
2063+ScopesOverviewSearchCategories::data(const QModelIndex& index, int role) const
2064+{
2065+ if (!index.isValid()) {
2066+ return QVariant();
2067+ }
2068+
2069+ const QString categoryId = index.row() == 0 ? "searchA" : "searchB";
2070+
2071+ unity::shell::scopes::ResultsModelInterface *resultsModel = m_resultsModels[index.row()];
2072+ if (!resultsModel) {
2073+ QObject *that = const_cast<ScopesOverviewSearchCategories*>(this);
2074+ QList<unity::shell::scopes::ScopeInterface *> scopes;
2075+ if (index.row() == 0) {
2076+ scopes << m_scopes->getScopeFromAll("clickscope") << nullptr << m_scopes->getScopeFromAll("MockScope2");
2077+ } else {
2078+ scopes << nullptr << m_scopes->getScopeFromAll("MockScope7") << nullptr << m_scopes->getScopeFromAll("MockScope1");
2079+ }
2080+ resultsModel = new ScopesOverviewResultsModel(scopes, categoryId, that);
2081+ m_resultsModels[index.row()] = resultsModel;
2082+ }
2083+ switch (role) {
2084+ case RoleCategoryId:
2085+ return categoryId;
2086+ case RoleName:
2087+ return index.row() == 0 ? "SearchA" : "SearchB";
2088+ case RoleIcon:
2089+ return QVariant();
2090+ case RoleRawRendererTemplate:
2091+ qFatal("Using un-implemented RoleRawRendererTemplate Categories role");
2092+ return QVariant();
2093+ case RoleRenderer:
2094+ {
2095+ QVariantMap map;
2096+ map["category-layout"] = "grid";
2097+ map["card-size"] = "small";
2098+ map["overlay"] = true;
2099+ return map;
2100+ }
2101+ case RoleComponents:
2102+ {
2103+ QVariantMap map, artMap;
2104+ artMap["aspect-ratio"] = "1";
2105+ artMap["field"] = "art";
2106+ map["art"] = artMap;
2107+ map["title"] = "HOLA";
2108+ return map;
2109+ }
2110+ case RoleResults:
2111+ return QVariant::fromValue(resultsModel);
2112+ case RoleCount:
2113+ return resultsModel->rowCount();
2114+ case RoleHeaderLink:
2115+ return QString();
2116+ default:
2117+ qFatal("Using un-implemented Categories role");
2118+ return QVariant();
2119+ }
2120+}
2121+
2122+
2123+ScopesOverviewResultsModel::ScopesOverviewResultsModel(const QList<unity::shell::scopes::ScopeInterface *> &scopes, const QString &categoryId, QObject* parent)
2124+ : unity::shell::scopes::ResultsModelInterface(parent)
2125+ , m_scopes(scopes)
2126+ , m_categoryId(categoryId)
2127+{
2128+}
2129+
2130+QString ScopesOverviewResultsModel::categoryId() const
2131+{
2132+ return m_categoryId;
2133+}
2134+
2135+void ScopesOverviewResultsModel::setCategoryId(QString const& /*id*/)
2136+{
2137+ qFatal("Calling un-implemented ScopesOverviewResultsModel::setCategoryId");
2138+}
2139+
2140+int ScopesOverviewResultsModel::scopeIndex(QString const& id) const
2141+{
2142+ const int scopeCount = count();
2143+ for (int i = 0; i < scopeCount; ++i) {
2144+ if (m_scopes[i]->id() == id)
2145+ return i;
2146+ }
2147+ return -1;
2148+}
2149+
2150+QHash<int, QByteArray> ScopesOverviewResultsModel::roleNames() const
2151+{
2152+ QHash<int, QByteArray> roles = unity::shell::scopes::ResultsModelInterface::roleNames();
2153+ roles[RoleBackground + 1] = "scopeId";
2154+ return roles;
2155+}
2156+
2157+int ScopesOverviewResultsModel::rowCount(const QModelIndex& parent) const
2158+{
2159+ Q_UNUSED(parent);
2160+
2161+ return m_scopes.count();
2162+}
2163+
2164+int ScopesOverviewResultsModel::count() const
2165+{
2166+ return rowCount();
2167+}
2168+
2169+QVariant
2170+ScopesOverviewResultsModel::data(const QModelIndex& index, int role) const
2171+{
2172+ unity::shell::scopes::ScopeInterface *scope = m_scopes[index.row()];
2173+ switch (role) {
2174+ case RoleUri:
2175+ case RoleCategoryId:
2176+ case RoleDndUri:
2177+ return QString();
2178+ case RoleResult:
2179+ return scope ? scope->id() : QString("Result.%1.%2").arg(categoryId()).arg(index.row());
2180+ case RoleTitle:
2181+ return scope ? scope->name() : QString("Title.%1.%2").arg(categoryId()).arg(index.row());
2182+ case RoleArt:
2183+ return qmlDirectory() + "graphics/applicationIcons/dash.png";
2184+ case RoleMascot:
2185+ case RoleEmblem:
2186+ case RoleSummary:
2187+ case RoleBackground + 1: // scopeId
2188+ return scope ? scope->id() : nullptr;
2189+ break;
2190+ default:
2191+ return QVariant();
2192+ }
2193+}
2194
2195=== added file 'tests/mocks/Unity/fake_scopesoverview.h'
2196--- tests/mocks/Unity/fake_scopesoverview.h 1970-01-01 00:00:00 +0000
2197+++ tests/mocks/Unity/fake_scopesoverview.h 2014-07-29 11:05:57 +0000
2198@@ -0,0 +1,104 @@
2199+/*
2200+ * Copyright (C) 2014 Canonical, Ltd.
2201+ *
2202+ * This program is free software; you can redistribute it and/or modify
2203+ * it under the terms of the GNU General Public License as published by
2204+ * the Free Software Foundation; version 3.
2205+ *
2206+ * This program is distributed in the hope that it will be useful,
2207+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2208+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2209+ * GNU General Public License for more details.
2210+ *
2211+ * You should have received a copy of the GNU General Public License
2212+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2213+ */
2214+
2215+#ifndef FAKE_SCOPESOVERVIEW_H
2216+#define FAKE_SCOPESOVERVIEW_H
2217+
2218+#include "fake_scope.h"
2219+#include <unity/shell/scopes/ResultsModelInterface.h>
2220+
2221+class Scopes;
2222+
2223+class ScopesOverview : public Scope
2224+{
2225+ Q_OBJECT
2226+
2227+public:
2228+ ScopesOverview(Scopes* parent = 0);
2229+
2230+ void setSearchQuery(const QString& search_query) override;
2231+ Q_INVOKABLE void activate(QVariant const& result) override;
2232+
2233+private:
2234+ unity::shell::scopes::CategoriesInterface *m_scopesOverviewCategories;
2235+ unity::shell::scopes::CategoriesInterface *m_searchCategories;
2236+};
2237+
2238+class ScopesOverviewCategories : public unity::shell::scopes::CategoriesInterface
2239+{
2240+ Q_OBJECT
2241+
2242+public:
2243+ ScopesOverviewCategories(Scopes *scopes, QObject* parent = 0);
2244+
2245+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
2246+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
2247+
2248+ Q_INVOKABLE void addSpecialCategory(QString const& categoryId, QString const& name, QString const& icon, QString const& rawTemplate, QObject* countObject) override;
2249+ Q_INVOKABLE bool overrideCategoryJson(QString const& categoryId, QString const& json) override;
2250+
2251+private:
2252+ mutable QHash<int, unity::shell::scopes::ResultsModelInterface*> m_resultsModels;
2253+
2254+ Scopes *m_scopes;
2255+};
2256+
2257+class ScopesOverviewSearchCategories : public unity::shell::scopes::CategoriesInterface
2258+{
2259+ Q_OBJECT
2260+
2261+public:
2262+ ScopesOverviewSearchCategories(Scopes *scopes, QObject* parent = 0);
2263+
2264+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
2265+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
2266+
2267+ Q_INVOKABLE void addSpecialCategory(QString const& categoryId, QString const& name, QString const& icon, QString const& rawTemplate, QObject* countObject) override;
2268+ Q_INVOKABLE bool overrideCategoryJson(QString const& categoryId, QString const& json) override;
2269+
2270+private:
2271+ mutable QHash<int, unity::shell::scopes::ResultsModelInterface*> m_resultsModels;
2272+
2273+ Scopes *m_scopes;
2274+};
2275+
2276+class ScopesOverviewResultsModel : public unity::shell::scopes::ResultsModelInterface
2277+{
2278+ Q_OBJECT
2279+
2280+public:
2281+ explicit ScopesOverviewResultsModel(const QList<unity::shell::scopes::ScopeInterface *> &scopes, const QString &categoryId, QObject* parent = 0);
2282+
2283+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
2284+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
2285+
2286+ /* getters */
2287+ QString categoryId() const override;
2288+ int count() const override;
2289+
2290+ /* setters */
2291+ void setCategoryId(QString const& id) override;
2292+
2293+ /* Special API */
2294+ Q_INVOKABLE int scopeIndex(QString const& id) const;
2295+ QHash<int, QByteArray> roleNames() const override;
2296+
2297+private:
2298+ QList<unity::shell::scopes::ScopeInterface *> m_scopes;
2299+ QString m_categoryId;
2300+};
2301+
2302+#endif // FAKE_SCOPESOVERVIEW_H
2303
2304=== modified file 'tests/plugins/Dash/cardcreator/5.res'
2305--- tests/plugins/Dash/cardcreator/5.res 2014-07-08 12:37:34 +0000
2306+++ tests/plugins/Dash/cardcreator/5.res 2014-07-29 11:05:57 +0000
2307@@ -109,6 +109,7 @@
2308 anchors { left: parent.left;
2309 leftMargin: units.gu(1);
2310 right: parent.right;
2311+ rightMargin: units.gu(1);
2312 top: overlayLoader.top;
2313 topMargin: units.gu(1);
2314 }
2315
2316=== modified file 'tests/qmltests/Components/tst_PageHeader.qml'
2317--- tests/qmltests/Components/tst_PageHeader.qml 2014-07-08 08:30:46 +0000
2318+++ tests/qmltests/Components/tst_PageHeader.qml 2014-07-29 11:05:57 +0000
2319@@ -36,6 +36,16 @@
2320
2321 function init() {
2322 searchEnabled = true;
2323+
2324+ // Reset to initial state
2325+ pageHeader.searchQuery = "";
2326+ pageHeader.closePopup();
2327+ pageHeader.searchHistory.clear();
2328+
2329+ // Check initial state
2330+ var headerContainer = findChild(pageHeader, "headerContainer");
2331+ tryCompareFunction(function() { return headerContainer.popover === null; }, true);
2332+ compare(pageHeader.searchHistory.count, 0);
2333 }
2334
2335 function test_search_disabled() {
2336@@ -80,9 +90,6 @@
2337 }
2338
2339 function test_history() {
2340- pageHeader.searchHistory.clear()
2341- compare(pageHeader.searchHistory.count, 0)
2342-
2343 pageHeader.triggerSearch()
2344 typeString("humppa1")
2345 pageHeader.resetSearch()
2346@@ -166,16 +173,26 @@
2347 tryCompareFunction(function() { return headerContainer.popover !== null; }, true);
2348
2349 tryCompare(headerContainer.popover, "visible", true);
2350+
2351+ var searchTextField = findChild(pageHeader, "searchTextField");
2352+ compare(searchTextField.focus, true);
2353+
2354+ var recentSearches = findChild(headerContainer.popover, "recentSearches");
2355+ mouseClick(recentSearches.itemAt(0), 0, 0);
2356+
2357+ compare(pageHeader.searchQuery, "Search2");
2358+ tryCompareFunction(function() { return headerContainer.popover === null; }, true);
2359+ compare(searchTextField.focus, false);
2360 }
2361
2362- function test_tap_outside_closes_popup_data() {
2363+ function test_popup_closing_data() {
2364 return [
2365 { tag: "with search text", searchText: "foobar", hideSearch: false },
2366 { tag: "without search text", searchText: "", hideSearch: true }
2367 ];
2368 }
2369
2370- function test_tap_outside_closes_popup(data) {
2371+ function test_popup_closing(data) {
2372 searchEnabled = true;
2373 pageHeader.searchHistory.clear();
2374
2375@@ -191,13 +208,26 @@
2376
2377 pageHeader.searchQuery = data.searchText;
2378
2379- mouseClick(root, root.width / 2, root.height - 1);
2380+ if (data.searchText == "") {
2381+ // When the text is empty the user can also close the
2382+ // popup by clicking outside the header instead of by starting a search
2383+ mouseClick(root, root.width / 2, root.height - 1);
2384+ }
2385
2386 tryCompare(headerContainer, "showSearch", !data.hideSearch);
2387 tryCompareFunction(function() { return headerContainer.popover === null; }, true);
2388
2389 pageHeader.resetSearch();
2390 }
2391+
2392+ function test_search_change_shows_search() {
2393+ var headerContainer = findChild(pageHeader, "headerContainer");
2394+ compare(headerContainer.showSearch, false);
2395+ compare(searchQuery, "");
2396+
2397+ searchQuery = "H";
2398+ compare(headerContainer.showSearch, true);
2399+ }
2400 }
2401
2402 Column {
2403
2404=== modified file 'tests/qmltests/Dash/tst_Dash.qml'
2405--- tests/qmltests/Dash/tst_Dash.qml 2014-07-11 11:45:45 +0000
2406+++ tests/qmltests/Dash/tst_Dash.qml 2014-07-29 11:05:57 +0000
2407@@ -20,8 +20,6 @@
2408 import Ubuntu.Components 0.1
2409 import Unity.Test 0.1 as UT
2410
2411-// TODO We don't have any tests for the overlay scope functionality.
2412-
2413 Item {
2414 id: shell
2415 width: units.gu(40)
2416@@ -86,10 +84,220 @@
2417 tryCompare(dashContentList, "count", 0);
2418 scopes.load();
2419 tryCompare(scopes, "loaded", true);
2420- tryCompare(dashContentList, "count", 4);
2421+ tryCompare(dashContentList, "count", 6);
2422
2423 verify(dashContentList != undefined);
2424 tryCompare(dashContentList, "currentIndex", data.visualIndex);
2425 }
2426+
2427+ function test_dash_overview_show_select_same_favorite() {
2428+ // Wait for stuff to be loaded
2429+ tryCompare(scopes, "loaded", true);
2430+ var dashContentList = findChild(dash, "dashContentList");
2431+ tryCompare(dashContentList, "count", 6);
2432+ var mockScope1Loader = findChild(dash, "MockScope1 loader");
2433+ tryCompareFunction(function() { return mockScope1Loader.item != null; }, true);
2434+
2435+ // Show the overview
2436+ touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, dash.height - units.gu(18));
2437+ var overviewController = findInvisibleChild(dash, "overviewController");
2438+ tryCompare(overviewController, "progress", 1);
2439+
2440+ // Make sure tab is where it should
2441+ var scopesOverview = findChild(dash, "scopesOverview");
2442+ compare(scopesOverview.currentTab, 0);
2443+
2444+ // Make sure stuff is loaded
2445+ var scopesOverviewFavoritesRepeater = findChild(dash, "scopesOverviewFavoritesRepeater");
2446+ tryCompare(scopesOverviewFavoritesRepeater, "count", 6);
2447+ tryCompareFunction(function() { return scopesOverviewFavoritesRepeater.itemAt(0).item != null; }, true);
2448+ waitForRendering(scopesOverviewFavoritesRepeater.itemAt(0).item);
2449+
2450+ // Click in first item
2451+ mouseClick(scopesOverviewFavoritesRepeater.itemAt(0).item, 0, 0);
2452+
2453+ // Make sure animation went back
2454+ tryCompare(overviewController, "progress", 0);
2455+ compare(dashContentList.currentIndex, 0);
2456+ }
2457+
2458+ function test_dash_overview_show_select_different_favorite() {
2459+ // Wait for stuff to be loaded
2460+ tryCompare(scopes, "loaded", true);
2461+ var dashContentList = findChild(dash, "dashContentList");
2462+ tryCompare(dashContentList, "count", 6);
2463+ var mockScope1Loader = findChild(dash, "MockScope1 loader");
2464+ tryCompareFunction(function() { return mockScope1Loader.item != null; }, true);
2465+
2466+ // Show the overview
2467+ touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, dash.height - units.gu(18));
2468+ var overviewController = findInvisibleChild(dash, "overviewController");
2469+ tryCompare(overviewController, "progress", 1);
2470+
2471+ // Make sure tab is where it should
2472+ var scopesOverview = findChild(dash, "scopesOverview");
2473+ compare(scopesOverview.currentTab, 0);
2474+
2475+ // Make sure stuff is loaded
2476+ var scopesOverviewFavoritesRepeater = findChild(dash, "scopesOverviewFavoritesRepeater");
2477+ tryCompare(scopesOverviewFavoritesRepeater, "count", 6);
2478+ tryCompareFunction(function() { return scopesOverviewFavoritesRepeater.itemAt(0).item != null; }, true);
2479+ waitForRendering(scopesOverviewFavoritesRepeater.itemAt(1).item);
2480+
2481+ // Click in first item
2482+ mouseClick(scopesOverviewFavoritesRepeater.itemAt(1).item, 0, 0);
2483+
2484+ // Make sure animation went back
2485+ tryCompare(overviewController, "progress", 0);
2486+ compare(dashContentList.currentIndex, 1);
2487+ }
2488+
2489+ function test_dash_overview_all_temp_scope_done_from_all() {
2490+ // Wait for stuff to be loaded
2491+ tryCompare(scopes, "loaded", true);
2492+ var dashContentList = findChild(dash, "dashContentList");
2493+ tryCompare(dashContentList, "count", 6);
2494+ var mockScope1Loader = findChild(dash, "MockScope1 loader");
2495+ tryCompareFunction(function() { return mockScope1Loader.item != null; }, true);
2496+
2497+ // Show the overview
2498+ touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, dash.height - units.gu(18));
2499+ var overviewController = findInvisibleChild(dash, "overviewController");
2500+ tryCompare(overviewController, "progress", 1);
2501+
2502+ // Make sure tab is where it should
2503+ var scopesOverview = findChild(dash, "scopesOverview");
2504+ compare(scopesOverview.currentTab, 0);
2505+
2506+ // Make sure stuff is loaded
2507+ var scopesOverviewFavoritesRepeater = findChild(dash, "scopesOverviewFavoritesRepeater");
2508+ tryCompare(scopesOverviewFavoritesRepeater, "count", 6);
2509+ tryCompareFunction(function() { return scopesOverviewFavoritesRepeater.itemAt(0).item != null; }, true);
2510+ waitForRendering(scopesOverviewFavoritesRepeater.itemAt(1).item);
2511+
2512+ // Click on the all tab
2513+ var scopesOverviewAllTabButton = findChild(dash, "scopesOverviewAllTabButton");
2514+ mouseClick(scopesOverviewAllTabButton, 0, 0);
2515+
2516+ // Wait for all tab to be enabled (animation finish)
2517+ var scopesOverviewAllView = findChild(dash, "scopesOverviewRepeaterChild1");
2518+ tryCompare(scopesOverviewAllView, "enabled", true);
2519+
2520+ // Click on a temp scope
2521+ var tempScopeCard = findChild(scopesOverviewAllView, "delegate1");
2522+ mouseClick(tempScopeCard, 0, 0);
2523+
2524+ // Check the bottom edge (overview) is disabled from temp scope
2525+ var overviewDragHandle = findChild(dash, "overviewDragHandle");
2526+ compare(overviewDragHandle.enabled, false);
2527+
2528+ // Check temp scope is there
2529+ var scopesOverviewTempScopeItem = findChild(dash, "scopesOverviewTempScopeItem");
2530+ tryCompareFunction( function() { return scopesOverviewTempScopeItem.scope != null; }, true);
2531+ tryCompare(scopesOverviewTempScopeItem, "enabled", true);
2532+
2533+ // Go back
2534+ var scopesOverviewTempScopeItemHeader = findChild(scopesOverviewTempScopeItem, "scopePageHeader");
2535+ var backButton = findChild(findChild(scopesOverviewTempScopeItemHeader, "innerPageHeader"), "backButton");
2536+ mouseClick(backButton, 0, 0);
2537+
2538+ // Check temp scope is gone
2539+ var scopesOverviewTempScopeItem = findChild(dash, "scopesOverviewTempScopeItem");
2540+ tryCompareFunction( function() { return scopesOverviewTempScopeItem.scope == null; }, true);
2541+ tryCompare(scopesOverviewTempScopeItem, "enabled", false);
2542+
2543+ // Press on done
2544+ var scopesOverviewDoneButton = findChild(scopesOverview, "scopesOverviewDoneButton");
2545+ mouseClick(scopesOverviewDoneButton, 0, 0);
2546+
2547+ // Check the dash overview is gone
2548+ tryCompare(overviewController, "progress", 0);
2549+
2550+ // Original list is still on 0
2551+ compare(dashContentList.currentIndex, 0);
2552+ }
2553+
2554+ function test_temp_scope_dash_overview_all_search_temp_scope_favorite_from_all() {
2555+ // Wait for stuff to be loaded
2556+ tryCompare(scopes, "loaded", true);
2557+ var dashContentList = findChild(dash, "dashContentList");
2558+ tryCompare(dashContentList, "count", 6);
2559+ var mockScope1Loader = findChild(dash, "MockScope1 loader");
2560+ tryCompareFunction(function() { return mockScope1Loader.item != null; }, true);
2561+
2562+ // Swipe right to Apps scope
2563+ touchFlick(dash, dash.width - 1, units.gu(1), dash.width - units.gu(10), units.gu(1));
2564+ tryCompare(dashContentList, "contentX", dashContentList.width);
2565+ tryCompare(dashContentList, "currentIndex", 1);
2566+
2567+ // Click on card that opens temp scope
2568+ var dashCategory2 = findChild(dashContentList.currentItem, "dashCategory2");
2569+ var card2 = findChild(dashCategory2, "delegate2");
2570+ waitForRendering(card2);
2571+ mouseClick(card2, card2.width / 2, card2.height / 2);
2572+
2573+ // Wait for temp scope to be there
2574+ var dashTempScopeItem = findChild(dash, "dashTempScopeItem");
2575+ tryCompare(dashTempScopeItem, "x", 0);
2576+
2577+ // Show the overview
2578+ touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, dash.height - units.gu(18));
2579+ var overviewController = findInvisibleChild(dash, "overviewController");
2580+ tryCompare(overviewController, "progress", 1);
2581+
2582+ // Make sure tab is where it should
2583+ var scopesOverview = findChild(dash, "scopesOverview");
2584+ compare(scopesOverview.currentTab, 1);
2585+
2586+ // Do a search
2587+ var scopesOverviewPageHeader = findChild(scopesOverview, "scopesOverviewPageHeader");
2588+ var searchButton = findChild(scopesOverviewPageHeader, "search_header_button");
2589+ mouseClick(searchButton, 0, 0);
2590+
2591+ // Type something
2592+ keyClick(Qt.Key_H);
2593+
2594+ // Check results grid is there and the other lists are not
2595+ var searchResultsViewer = findChild(scopesOverview, "searchResultsViewer");
2596+ var scopesOverviewRepeater = findChild(dash, "scopesOverviewRepeater");
2597+ tryCompare(searchResultsViewer, "opacity", 1);
2598+ tryCompare(scopesOverviewRepeater, "count", 0);
2599+
2600+ // Click on a temp scope in the search
2601+ var dashCategorysearchA = findChild(searchResultsViewer, "dashCategorysearchA");
2602+ var cardTempScope = findChild(dashCategorysearchA, "delegate2");
2603+ waitForRendering(cardTempScope);
2604+ mouseClick(cardTempScope, cardTempScope.width / 2, cardTempScope.height / 2);
2605+
2606+ // Check the bottom edge (overview) is disabled from temp scope
2607+ var overviewDragHandle = findChild(dash, "overviewDragHandle");
2608+ compare(overviewDragHandle.enabled, false);
2609+
2610+ // Check temp scope is there
2611+ var scopesOverviewTempScopeItem = findChild(dash, "scopesOverviewTempScopeItem");
2612+ tryCompareFunction( function() { return scopesOverviewTempScopeItem.scope != null; }, true);
2613+ tryCompare(scopesOverviewTempScopeItem, "enabled", true);
2614+
2615+ // Go back
2616+ var scopesOverviewTempScopeItemHeader = findChild(scopesOverviewTempScopeItem, "scopePageHeader");
2617+ var backButton = findChild(findChild(scopesOverviewTempScopeItemHeader, "innerPageHeader"), "backButton");
2618+ mouseClick(backButton, 0, 0);
2619+
2620+ // Check temp scope is gone
2621+ var scopesOverviewTempScopeItem = findChild(dash, "scopesOverviewTempScopeItem");
2622+ tryCompareFunction( function() { return scopesOverviewTempScopeItem.scope == null; }, true);
2623+ tryCompare(scopesOverviewTempScopeItem, "enabled", false);
2624+
2625+ // Press on a favorite
2626+ var dashCategorysearchB = findChild(searchResultsViewer, "dashCategorysearchB");
2627+ var cardFavSearch = findChild(dashCategorysearchB, "delegate3");
2628+ mouseClick(cardFavSearch, 0, 0);
2629+
2630+ // Check the dash overview is gone
2631+ tryCompare(overviewController, "progress", 0);
2632+
2633+ // Original list went to the favorite
2634+ compare(dashContentList.currentIndex, 0);
2635+ }
2636 }
2637 }
2638
2639=== modified file 'tests/qmltests/Dash/tst_DashContent.qml'
2640--- tests/qmltests/Dash/tst_DashContent.qml 2014-07-21 13:28:35 +0000
2641+++ tests/qmltests/Dash/tst_DashContent.qml 2014-07-29 11:05:57 +0000
2642@@ -73,7 +73,7 @@
2643 function loadScopes() {
2644 scopeLoadedSpy.clear();
2645 scopesModel.load();
2646- tryCompare(scopeLoadedSpy, "count", 4);
2647+ tryCompare(scopeLoadedSpy, "count", 10);
2648 }
2649
2650 function init() {
2651@@ -117,7 +117,8 @@
2652
2653 loadScopes();
2654
2655- verify(dashContentList.currentIndex >= 0 && dashContentList.currentIndex < 5);
2656+ compare(dashContentList.count, 10);
2657+ verify(dashContentList.currentIndex >= 0 && dashContentList.currentIndex < dashContentList.count);
2658 }
2659
2660 function test_show_header_on_list_movement() {
2661@@ -173,7 +174,7 @@
2662
2663 // test greater than scope count.
2664 var currentScopeIndex = dashContent.currentIndex;
2665- dashContent.setCurrentScopeAtIndex(8, true, false);
2666+ dashContent.setCurrentScopeAtIndex(18, true, false);
2667 compare(dashContent.currentIndex, currentScopeIndex, "Scope should not change if changing to greater index than count");
2668 }
2669

Subscribers

People subscribed via source and target branches