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

Proposed by Albert Astals Cid on 2014-07-25
Status: Superseded
Proposed branch: lp:~aacid/unity8/scopeSearchHintText
Merge into: lp:unity8
Diff against target: 2711 lines (+1789/-111)
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 (+27/-8)
qml/Components/ResponsiveGridView.qml (+3/-3)
qml/Dash/CardCarousel.qml (+2/-2)
qml/Dash/CardGrid.qml (+14/-3)
qml/Dash/CardVerticalJournal.qml (+2/-2)
qml/Dash/Dash.qml (+138/-7)
qml/Dash/DashBackground.qml (+24/-0)
qml/Dash/DashContent.qml (+7/-4)
qml/Dash/DashRenderer.qml (+5/-2)
qml/Dash/GenericScopeView.qml (+57/-26)
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 (+19/-6)
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 (+16/-3)
To merge this branch: bzr merge lp:~aacid/unity8/scopeSearchHintText
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing on 2014-07-28
Michael Zanetti (community) 2014-07-25 Approve on 2014-07-25
Review via email: mp+228279@code.launchpad.net

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

Commit message

Pass the scope search hint up to the search line

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/catch-no-search-hint/+merge/228288
No, but there is a crash somewhere in scopes plugin that makes it crash for some scopes and also we have a white on white text problem, so it eihter needs the SDK team to fix the color on SuruDark or the Dash being a separate app so we can not use SuruDark theme

 * Did you perform an exploratory manual test run of your code change and any related functionality?
Yes, changed hint color of /usr/lib/x86_64-linux-gnu/qt5/qml/Ubuntu/Components/TextField.qml to be red manually and it is showing up fine

 * 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?
This is requested by design

To post a comment you must log in.
Michael Zanetti (mzanetti) wrote :

I think we could add a simple test here to make sure the value from the scope ends up in the placeholderText.

review: Needs Fixing
Michael Zanetti (mzanetti) wrote :

Otherwise it works fine except the above mentioned issues. I tried to merge it into the dash-as-app branch but that makes me hit the mentioned crash.

lp:~aacid/unity8/scopeSearchHintText updated on 2014-07-25
1082. By Albert Astals Cid on 2014-07-25

Check the searchhint travels up correcly through the layers

Albert Astals Cid (aacid) wrote :

> I think we could add a simple test here to make sure the value from the scope
> ends up in the placeholderText.

Added.

lp:~aacid/unity8/scopeSearchHintText updated on 2014-07-25
1083. By Albert Astals Cid on 2014-07-25

Merge

Michael Zanetti (mzanetti) wrote :

thanks. looks good.

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

yeah man!

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

CI? what's that?

review: Approve
lp:~aacid/unity8/scopeSearchHintText updated on 2014-08-08
1084. By Albert Astals Cid on 2014-07-28

Merge

1085. By Albert Astals Cid on 2014-07-29

Merge lp:~aacid/unity8/scopeActivatePreview

1086. By Albert Astals Cid on 2014-07-30

Merge

1087. By Albert Astals Cid on 2014-07-30

Merge

1088. By Albert Astals Cid on 2014-07-31

Merge

1089. By Albert Astals Cid on 2014-08-01

Merge

1090. By Albert Astals Cid on 2014-08-05

Merge

1091. By Albert Astals Cid on 2014-08-06

Merge

1092. By Albert Astals Cid on 2014-08-06

Merge

1093. By Albert Astals Cid on 2014-08-07

Merge

1094. By Albert Astals Cid on 2014-08-07

Merge

1095. By Albert Astals Cid on 2014-08-08

Merge

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

Subscribers

People subscribed via source and target branches