on xenial help sign in
Merge lp:~saviq/unity8/category-header-links
commit9554b771df203a4d0966621e221eaf51b6c107aa authorAlbert Astals <albert.astals@canonical.com> dateMon 21 Jul 2014, 16:13 (12 years ago) parentc1822217abda69
29 files changed+395−436
tests/autopilot/.project → .project+1−1 tests/autopilot/.pydevproject → .pydevproject+3−1 .settings/Autopilot List.launch+14 .settings/Autopilot Run.launch+14 debian/control+1−1 po/unity8.pot+34−14 qml/Components/FilterGrid.qml → qml/Components/FilterGrid.qml.THIS qml/Components/ListItems/Header.qml+17−26 qml/Components/ResponsiveGridView.qml+4−3 qml/Dash/CardCarousel.qml+2−7 qml/Dash/CardFilterGrid.qml → qml/Dash/CardGrid.qml+26−26 qml/Dash/CardVerticalJournal.qml+2−21 qml/Dash/DashRenderer.qml+3−10 qml/Dash/GenericScopeView.qml+109−46 qml/Dash/ScopesOverviewAll.qml+8−11 tests/autopilot/unity8/shell/emulators/dash.py+6−3 tests/autopilot/unity8/shell/tests/test_emulators.py+5−5 tests/mocks/Ubuntu/Telephony/CMakeLists.txt+1−9 tests/mocks/Unity/fake_categories.cpp+13−1 tests/mocks/Unity/fake_categories.h+2 tests/mocks/Unity/fake_scope.cpp+2−7 tests/mocks/Unity/fake_scope.h+2−2 tests/mocks/Unity/fake_scopes.cpp+1−1 tests/mocks/Unity/fake_scopesoverview.cpp+4 tests/qmltests/CMakeLists.txt−1 tests/qmltests/Components/tst_FilterGrid.qml−190 tests/qmltests/Dash/tst_Dash.qml+1−1 tests/qmltests/Dash/tst_DashContent.qml+8−4 tests/qmltests/Dash/tst_GenericScopeView.qml+112−45
tests/autopilot/.project → .projectrenamed+1−1
@@ -1,6 +1,6 @@
11<?xml version="1.0" encoding="UTF-8"?>
22<projectDescription>
3− <name>Unity8 Autopilot</name>
3+ <name>Unity8</name>
44 <comment></comment>
55 <projects>
66 </projects>
tests/autopilot/.pydevproject → .pydevprojectrenamed+3−1
@@ -1,7 +1,9 @@
11<?xml version="1.0" encoding="UTF-8" standalone="no"?>
22<?eclipse-pydev version="1.0"?><pydev_project>
33<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
4−<path>/Unity8 Autopilot</path>
4+<path>/Unity8/tests/autopilot</path>
5+<path>/Unity8/.bazaar</path>
6+<path>/Unity8/tests/whitespace</path>
57</pydev_pathproperty>
68<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
79<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
.settings/Autopilot List.launchnew file+14
@@ -0,0 +1,14 @@
1+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+<launchConfiguration type="org.python.pydev.debug.regularLaunchConfigurationType">
3+<stringAttribute key="bad_container_name" value="/Unity8/.pyd"/>
4+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"/>
5+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"/>
6+<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
7+<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
8+</listAttribute>
9+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="/usr/bin/autopilot"/>
10+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_OTHER_WORKING_DIRECTORY" value=""/>
11+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="list unity8"/>
12+<stringAttribute key="org.python.pydev.debug.ATTR_INTERPRETER" value="__default"/>
13+<stringAttribute key="org.python.pydev.debug.ATTR_PROJECT" value="Unity8"/>
14+</launchConfiguration>
.settings/Autopilot Run.launchnew file+14
@@ -0,0 +1,14 @@
1+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+<launchConfiguration type="org.python.pydev.debug.regularLaunchConfigurationType">
3+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"/>
4+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"/>
5+<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
6+<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
7+<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
8+</listAttribute>
9+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="/usr/bin/autopilot"/>
10+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_OTHER_WORKING_DIRECTORY" value=""/>
11+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="run ${string_prompt:the test suite to run:unity8}"/>
12+<stringAttribute key="org.python.pydev.debug.ATTR_INTERPRETER" value="__default"/>
13+<stringAttribute key="org.python.pydev.debug.ATTR_PROJECT" value="Unity8"/>
14+</launchConfiguration>
debian/control+1−1
@@ -108,7 +108,7 @@ Depends: qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
108108 unity-application-impl-2,
109109 unity-notifications-impl-3,
110110 unity-plugin-scopes | unity-scopes-impl,
111− unity-scopes-impl-0,
111+ unity-scopes-impl-2,
112112 unity8-fake-env | unity-application-impl,
113113 ${misc:Depends},
114114Breaks: unity8 (<< 7.86),
po/unity8.pot+34−14
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2014-07-16 17:27+0200\n"
11+"POT-Creation-Date: 2014-07-21 15:41+0200\n"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -101,7 +101,7 @@ msgstr ""
101101msgid "See more"
102102msgstr ""
103103
104−#: qml/Components/SeeMore.qml:58
104+#: qml/Components/SeeMore.qml:58 qml/Dash/GenericScopeView.qml:356
105105msgid "See less"
106106msgstr ""
107107
@@ -109,6 +109,10 @@ msgstr ""
109109msgid "Recent"
110110msgstr ""
111111
112+#: qml/Dash/GenericScopeView.qml:356
113+msgid "See all"
114+msgstr ""
115+
112116#: qml/Dash/Previews/PreviewActionCombo.qml:34
113117msgid "More..."
114118msgstr ""
@@ -129,11 +133,11 @@ msgstr ""
129133msgid "Send"
130134msgstr ""
131135
132−#: qml/Dash/ScopesOverview.qml:114
136+#: qml/Dash/ScopesOverview.qml:185
133137msgid "Manage Dash"
134138msgstr ""
135139
136−#: qml/Dash/ScopesOverview.qml:291
140+#: qml/Dash/ScopesOverview.qml:390
137141msgid "Done"
138142msgstr ""
139143
@@ -141,7 +145,7 @@ msgstr ""
141145msgid "Favorites"
142146msgstr ""
143147
144−#: qml/Dash/ScopesOverviewTab.qml:53
148+#: qml/Dash/ScopesOverviewTab.qml:54
145149msgid "All"
146150msgstr ""
147151
@@ -169,11 +173,11 @@ msgstr ""
169173msgid "Speaking..."
170174msgstr ""
171175
172−#: qml/Notifications/NotificationMenuItemFactory.qml:91
176+#: qml/Notifications/NotificationMenuItemFactory.qml:97
173177msgid "Show password"
174178msgstr ""
175179
176−#: qml/Notifications/NotificationMenuItemFactory.qml:103
180+#: qml/Notifications/NotificationMenuItemFactory.qml:112
177181msgid "Please enter SIM PIN"
178182msgstr ""
179183
@@ -185,27 +189,27 @@ msgstr ""
185189msgid "Conference"
186190msgstr ""
187191
188−#: qml/Panel/Indicators/MenuItemFactory.qml:583
192+#: qml/Panel/Indicators/MenuItemFactory.qml:577
189193msgid "In queue…"
190194msgstr ""
191195
192−#: qml/Panel/Indicators/MenuItemFactory.qml:587
196+#: qml/Panel/Indicators/MenuItemFactory.qml:581
193197msgid "Downloading"
194198msgstr ""
195199
196−#: qml/Panel/Indicators/MenuItemFactory.qml:589
200+#: qml/Panel/Indicators/MenuItemFactory.qml:583
197201msgid "Paused, tap to resume"
198202msgstr ""
199203
200−#: qml/Panel/Indicators/MenuItemFactory.qml:591
204+#: qml/Panel/Indicators/MenuItemFactory.qml:585
201205msgid "Canceled"
202206msgstr ""
203207
204−#: qml/Panel/Indicators/MenuItemFactory.qml:593
208+#: qml/Panel/Indicators/MenuItemFactory.qml:587
205209msgid "Finished"
206210msgstr ""
207211
208−#: qml/Panel/Indicators/MenuItemFactory.qml:595
212+#: qml/Panel/Indicators/MenuItemFactory.qml:589
209213msgid "Failed, tap to retry"
210214msgstr ""
211215
@@ -213,7 +217,23 @@ msgstr ""
213217msgid "Search"
214218msgstr ""
215219
216−#: qml/Shell.qml:353
220+#: qml/Shell.qml:347
221+msgid "Are you sure you would like to turn power off?"
222+msgstr ""
223+
224+#: qml/Shell.qml:349
225+msgid "Power off"
226+msgstr ""
227+
228+#: qml/Shell.qml:360
229+msgid "Restart"
230+msgstr ""
231+
232+#: qml/Shell.qml:369
233+msgid "Cancel"
234+msgstr ""
235+
236+#: qml/Shell.qml:496
217237#, qt-format
218238msgid "Please enter %1"
219239msgstr ""
qml/Components/FilterGrid.qml → qml/Components/FilterGrid.qml.THISrenamed

The content is the same.

qml/Components/ListItems/Header.qml+17−26
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2014 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -15,54 +15,45 @@
1515 */
1616
1717import QtQuick 2.0
18+import QtQuick.Layouts 1.1
1819import Ubuntu.Components 0.1
1920
2021Item {
21− /*!
22− \preliminary
23− The text that is shown as the header text.
24− \qmlproperty string text
25− */
2622 property alias text: label.text
27− property alias textColor: label.color
28− property alias image: image.source
23+ property alias color: label.color
24+ property alias iconName: icon.name
2925
3026 signal clicked(variant mouse)
3127
3228 height: units.gu(5)
3329
34− Item {
30+ RowLayout {
31+ spacing: units.gu(.5)
3532 anchors {
3633 left: parent.left
3734 right: parent.right
38− rightMargin: units.gu(0.5)
39− top: parent.top
40− bottom: parent.bottom
35+ verticalCenter: parent.verticalCenter
36+ margins: units.gu(1)
4137 }
4238
4339 Label {
4440 id: label
45− anchors {
46− left: parent.left
47− leftMargin: units.gu(2)
48− verticalCenter: parent.verticalCenter
49− }
41+ Layout.fillWidth: true
5042
51− color: "grey" // TODO karni: Update Ubuntu.Compoonents.Themes.Palette.
5243 font.family: "Ubuntu"
5344 fontSize: "medium"
5445 elide: Text.ElideRight
46+ maximumLineCount: 1
5547 textFormat: Text.PlainText
56− width: parent.width - image.width - image.leftMargin - anchors.leftMargin
48+ width: Math.min(parent.width - units.gu(4), implicitWidth)
5749 }
5850
59− Image {
60− id: image
61− readonly property double leftMargin: units.gu(1)
62− x: label.x + label.contentWidth + leftMargin
63− anchors {
64− verticalCenter: parent.verticalCenter
65− }
51+ Icon {
52+ id: icon
53+ visible: name != ""
54+ height: units.gu(2)
55+ width: height
56+ color: label.color
6657 }
6758 }
6859
qml/Components/ResponsiveGridView.qml+4−3
@@ -34,7 +34,7 @@ Item {
3434 readonly property int cellWidth: gridView.cellWidth
3535 readonly property int cellHeight: gridView.cellHeight
3636 readonly property int totalContentHeight: {
37− return contentHeightForRows(Math.ceil(gridView.model.count / columns), cellHeight, verticalSpacing)
37+ return contentHeightForRows(Math.ceil(gridView.model.count / columns), cellHeight)
3838 }
3939 property alias interactive: gridView.interactive
4040 readonly property alias flicking: gridView.flicking
@@ -44,10 +44,11 @@ Item {
4444 property alias displayMarginBeginning: gridView.displayMarginBeginning
4545 property alias displayMarginEnd: gridView.displayMarginEnd
4646 property alias highlightIndex: gridView.highlightIndex
47+ property alias cacheBuffer: gridView.cacheBuffer
4748 readonly property alias currentItem: gridView.currentItem
4849
49− function contentHeightForRows(rows, height, vSpacing) {
50− return rows * height + vSpacing
50+ function contentHeightForRows(rows, height) {
51+ return rows * height
5152 }
5253
5354 GridView {
qml/Dash/CardCarousel.qml+2−7
@@ -21,13 +21,8 @@ import "../Components"
2121DashRenderer {
2222 id: cardCarousel
2323
24− property alias cacheBuffer: carousel.cacheBuffer
25− property alias itemComponent: carousel.itemComponent
26− property alias minimumTileWidth: carousel.minimumTileWidth
27− property alias selectedItemScaleFactor: carousel.selectedItemScaleFactor
28− property alias tileAspectRatio: carousel.tileAspectRatio
29−
30− height: carousel.implicitHeight + units.gu(6)
24+ expandedHeight: carousel.implicitHeight + units.gu(6)
25+ collapsedHeight: expandedHeight
3126
3227 Carousel {
3328 id: carousel
qml/Dash/CardFilterGrid.qml → qml/Dash/CardGrid.qmlrenamed+26−26
@@ -18,43 +18,43 @@ import QtQuick 2.0
1818import "../Components"
1919
2020DashRenderer {
21− id: genericFilterGrid
21+ id: root
2222
23− expandable: filterGrid.expandable
24− collapsedHeight: filterGrid.collapsedHeight
25− uncollapsedHeight: filterGrid.uncollapsedHeight
26− originY: filterGrid.originY
27− height: filterGrid.height
28− filtered: filterGrid.filtered
29−
30− function setFilter(filter, animate) {
31− filterGrid.setFilter(filter, animate)
23+ readonly property int collapsedRows: {
24+ if (!cardTool || !cardTool.template || typeof cardTool.template["collapsed-rows"] != "number") return 2;
25+ return cardTool.template["collapsed-rows"];
3226 }
3327
28+ expandedHeight: grid.totalContentHeight
29+ collapsedHeight: Math.min(grid.contentHeightForRows(collapsedRows, grid.cellHeight), expandedHeight)
30+ collapsedItemCount: collapsedRows * grid.columns
31+ originY: grid.originY
32+
3433 function cardPosition(index) {
3534 var pos = {};
36− var row = Math.floor(index / filterGrid.columns);
37− var column = index % filterGrid.columns;
35+ var row = Math.floor(index / grid.columns);
36+ var column = index % grid.columns;
3837 // Bit sad this is not symmetrical
39− pos.x = column * filterGrid.cellWidth + filterGrid.margins;
40− pos.y = row * filterGrid.cellHeight;
38+ pos.x = column * grid.cellWidth + grid.margins;
39+ pos.y = row * grid.cellHeight;
4140 return pos;
4241 }
4342
44− FilterGrid {
45− id: filterGrid
46− width: genericFilterGrid.width
43+ ResponsiveGridView {
44+ id: grid
45+ anchors.fill: parent
4746 minimumHorizontalSpacing: units.gu(1)
4847 delegateWidth: cardTool.cardWidth
4948 delegateHeight: cardTool.cardHeight
5049 verticalSpacing: units.gu(1)
51− model: genericFilterGrid.model
52− collapsedRowCount: Math.min(2, cardTool && cardTool.template && cardTool.template["collapsed-rows"] || 2)
53− displayMarginBeginning: genericFilterGrid.displayMarginBeginning
54− displayMarginEnd: genericFilterGrid.displayMarginEnd
50+ model: root.model
51+ displayMarginBeginning: root.displayMarginBeginning
52+ displayMarginEnd: root.displayMarginEnd
53+ cacheBuffer: 0
54+ interactive: false
5555 delegate: Item {
56− width: filterGrid.cellWidth
57− height: filterGrid.cellHeight
56+ width: grid.cellWidth
57+ height: grid.cellHeight
5858 Loader {
5959 id: loader
6060 sourceComponent: cardTool.cardComponent
@@ -69,12 +69,12 @@ DashRenderer {
6969 item.template = Qt.binding(function() { return cardTool.template; });
7070 item.components = Qt.binding(function() { return cardTool.components; });
7171 item.headerAlignment = Qt.binding(function() { return cardTool.headerAlignment; });
72− item.scopeStyle = genericFilterGrid.scopeStyle;
72+ item.scopeStyle = root.scopeStyle;
7373 }
7474 Connections {
7575 target: loader.item
76− onClicked: genericFilterGrid.clicked(index, result, loader.item, model)
77− onPressAndHold: genericFilterGrid.pressAndHold(index)
76+ onClicked: root.clicked(index, result, loader.item, model)
77+ onPressAndHold: root.pressAndHold(index)
7878 }
7979 }
8080 }
qml/Dash/CardVerticalJournal.qml+2−21
@@ -23,33 +23,14 @@ DashRenderer {
2323
2424 readonly property double collapseLimit: units.gu(35)
2525
26− uncollapsedHeight: cardVerticalJournal.implicitHeight
27− collapsedHeight: Math.min(collapseLimit, cardVerticalJournal.implicitHeight)
28− expandable: uncollapsedHeight > collapseLimit
26+ expandedHeight: Math.max(cardVerticalJournal.implicitHeight, minHeight)
27+ collapsedHeight: Math.max(Math.min(collapseLimit, cardVerticalJournal.implicitHeight), minHeight)
2928
3029 // This minHeight is used as bootstrapper for the height. Vertical Journal
3130 // is special by the fact that it doesn't know how to calculate its implicit height unless we give it
3231 // enough height that it can start creating its children so we make sure it has enough height for that
3332 // in case the model is non empty
3433 readonly property double minHeight: root.model.count >= 1 ? cardVerticalJournal.rowSpacing + 1 : 0
35− height: filtered ? Math.max(collapsedHeight, minHeight) : uncollapsedHeight
36−
37− Behavior on height {
38− id: heightBehaviour
39− enabled: false
40− animation: UbuntuNumberAnimation {
41− onRunningChanged: {
42− if (!running) {
43− heightBehaviour.enabled = false
44− }
45− }
46− }
47− }
48−
49− function setFilter(filter, animate) {
50− heightBehaviour.enabled = animate;
51− filtered = filter;
52− }
5334
5435 ResponsiveVerticalJournal {
5536 id: cardVerticalJournal
qml/Dash/DashRenderer.qml+3−10
@@ -17,15 +17,11 @@
1717import QtQuick 2.0
1818
1919Item {
20− // Can the item be expanded?
21− property bool expandable: false
20+ property int collapsedHeight
2221
23− // In case it can be expanded, is it filtered
24− property bool filtered: true
22+ property int expandedHeight
2523
26− property int collapsedHeight: height
27−
28− property int uncollapsedHeight: height
24+ property int collapsedItemCount: 0
2925
3026 property int displayMarginBeginning: 0
3127
@@ -52,7 +48,4 @@ Item {
5248 /// Emitted when the user pressed and held on an item
5349 /// @param index is the index of the held item
5450 signal pressAndHold(int index)
55−
56− function setFilter(filter, animate) {
57− }
5851}
qml/Dash/GenericScopeView.qml+109−46
@@ -134,11 +134,22 @@ FocusScope {
134134 highlightWhenPressed: false
135135 showDivider: false
136136
137− readonly property bool expandable: rendererLoader.item ? rendererLoader.item.expandable : false
138− readonly property bool filtered: rendererLoader.item ? rendererLoader.item.filtered : true
137+ readonly property bool expandable: {
138+ if (categoryView.model.count === 1) return false;
139+ if (cardTool.template && cardTool.template["collapsed-rows"] === 0) return false;
140+ if (item && item.expandedHeight > item.collapsedHeight) return true;
141+ return false;
142+ }
143+ property bool expanded: false
139144 readonly property string category: categoryId
145+ readonly property string headerLink: model.headerLink
140146 readonly property var item: rendererLoader.item
141147
148+ function expand(expand, animate) {
149+ heightBehaviour.enabled = animate;
150+ expanded = expand;
151+ }
152+
142153 CardTool {
143154 id: cardTool
144155 objectName: "cardTool"
@@ -148,6 +159,18 @@ FocusScope {
148159 viewWidth: parent.width
149160 }
150161
162+ onExpandableChanged: {
163+ // This can happen with the VJ that doesn't know how height it will be on creation
164+ // so doesn't set expandable until a bit too late for onLoaded
165+ if (expandable) {
166+ var shouldExpand = baseItem.category === categoryView.expandedCategoryId;
167+ baseItem.expand(shouldExpand, false /*animate*/);
168+ }
169+ }
170+
171+ onHeightChanged: rendererLoader.updateDelegateCreationRange();
172+ onYChanged: rendererLoader.updateDelegateCreationRange();
173+
151174 Loader {
152175 id: rendererLoader
153176 anchors {
@@ -157,13 +180,28 @@ FocusScope {
157180 topMargin: hasSectionHeader ? 0 : units.gu(2)
158181 }
159182
183+ Behavior on height {
184+ id: heightBehaviour
185+ enabled: false
186+ animation: UbuntuNumberAnimation {
187+ onRunningChanged: {
188+ if (!running) {
189+ heightBehaviour.enabled = false
190+ }
191+ }
192+ }
193+ }
194+
195+ readonly property bool expanded: baseItem.expanded || !baseItem.expandable
196+ height: expanded ? item.expandedHeight : item.collapsedHeight
197+
160198 source: {
161199 switch (cardTool.categoryLayout) {
162200 case "carousel": return "CardCarousel.qml";
163201 case "vertical-journal": return "CardVerticalJournal.qml";
164202 case "running-apps": return "Apps/RunningApplicationsGrid.qml";
165203 case "grid":
166− default: return "CardFilterGrid.qml";
204+ default: return "CardGrid.qml";
167205 }
168206 }
169207
@@ -182,9 +220,9 @@ FocusScope {
182220 }
183221 item.objectName = Qt.binding(function() { return categoryId })
184222 item.scopeStyle = scopeView.scopeStyle;
185− if (item.expandable) {
186− var shouldFilter = categoryId != categoryView.expandedCategoryId;
187− item.setFilter(shouldFilter, false /*animate*/);
223+ if (baseItem.expandable) {
224+ var shouldExpand = categoryId === categoryView.expandedCategoryId;
225+ baseItem.expand(shouldExpand, false /*animate*/);
188226 }
189227 updateDelegateCreationRange();
190228 item.cardTool = cardTool;
@@ -210,30 +248,28 @@ FocusScope {
210248 // so it's not implemented
211249 scopeView.scope.activate(result)
212250 } else {
213− previewListView.model = target.model;
214− previewListView.currentIndex = -1
215− previewListView.currentIndex = index;
216− previewListView.open = true
251+ openPreview(index);
217252 }
218253 }
219254 onPressAndHold: {
220255 if (scopeView.pressAndHoldOverride) {
221256 scopeView.pressAndHoldOverride(index);
222− return;
257+ } else {
258+ openPreview(index)
223259 }
224−
225− previewListView.model = target.model;
226− previewListView.currentIndex = -1
227− previewListView.currentIndex = index;
228− previewListView.open = true
229260 }
230− onExpandableChanged: {
231− // This can happen with the VJ that doesn't know how height it will be on creation
232− // so doesn't set expandable until a bit too late for onLoaded
233− if (rendererLoader.item.expandable) {
234− var shouldFilter = baseItem.category != categoryView.expandedCategoryId;
235− rendererLoader.item.setFilter(shouldFilter, false /*animate*/);
261+
262+ function openPreview(index) {
263+ if (!rendererLoader.expanded && !seeAllLabel.visible && target.collapsedItemCount > 0) {
264+ previewLimitModel.model = target.model;
265+ previewLimitModel.limit = target.collapsedItemCount;
266+ previewListView.model = previewLimitModel;
267+ } else {
268+ previewListView.model = target.model;
236269 }
270+ previewListView.currentIndex = -1;
271+ previewListView.currentIndex = index;
272+ previewListView.open = true;
237273 }
238274 }
239275 Connections {
@@ -243,17 +279,17 @@ FocusScope {
243279 }
244280 function collapseAllButExpandedCategory() {
245281 var item = rendererLoader.item;
246− if (item.expandable) {
247− var shouldFilter = categoryId != categoryView.expandedCategoryId;
248− if (shouldFilter != item.filter) {
282+ if (baseItem.expandable) {
283+ var shouldExpand = categoryId === categoryView.expandedCategoryId;
284+ if (shouldExpand != baseItem.expanded) {
249285 // If the filter animation will be seen start it, otherwise, just flip the switch
250− var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
251− var growingVisible = !shouldFilter && y + height < categoryView.height;
252− if (!previewListView.open || !shouldFilter) {
286+ var shrinkingVisible = !shouldExpand && y + item.collapsedHeight + seeAll.height < categoryView.height;
287+ var growingVisible = shouldExpand && y + height < categoryView.height;
288+ if (!previewListView.open || shouldExpand) {
253289 var animate = shrinkingVisible || growingVisible;
254− item.setFilter(shouldFilter, animate)
255− if (!shouldFilter && !previewListView.open) {
256− categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
290+ baseItem.expand(shouldExpand, animate)
291+ if (shouldExpand && !previewListView.open) {
292+ categoryView.maximizeVisibleArea(index, item.expandedHeight + seeAll.height);
257293 }
258294 }
259295 }
@@ -290,10 +326,43 @@ FocusScope {
290326 item.displayMarginEnd = -baseItem.height;
291327 } else {
292328 item.displayMarginBeginning = -Math.max(-baseItem.y, 0);
293− item.displayMarginEnd = -Math.max(baseItem.height - categoryView.height + baseItem.y, 0)
329+ item.displayMarginEnd = -Math.max(baseItem.height - seeAll.height
330+ - categoryView.height + baseItem.y, 0)
294331 }
295332 }
296333 }
334+ }
335+
336+ AbstractButton {
337+ id: seeAll
338+ objectName: "seeAll"
339+ anchors {
340+ top: rendererLoader.bottom
341+ left: parent.left
342+ right: parent.right
343+ }
344+ height: seeAllLabel.visible ? seeAllLabel.font.pixelSize + units.gu(6) : 0
345+
346+ onClicked: {
347+ if (categoryView.expandedCategoryId != baseItem.category) {
348+ categoryView.expandedCategoryId = baseItem.category;
349+ } else {
350+ categoryView.expandedCategoryId = "";
351+ }
352+ }
353+
354+ Label {
355+ id: seeAllLabel
356+ text: baseItem.expanded ? i18n.tr("See less") : i18n.tr("See all")
357+ anchors {
358+ centerIn: parent
359+ verticalCenterOffset: units.gu(-0.5)
360+ }
361+ fontSize: "small"
362+ font.weight: Font.Bold
363+ color: scopeStyle ? scopeStyle.foreground : "grey"
364+ visible: baseItem.expandable && !baseItem.headerLink
365+ }
297366
298367 Image {
299368 visible: index != 0
@@ -320,28 +389,18 @@ FocusScope {
320389 z: -1
321390 }
322391 }
323−
324− onHeightChanged: rendererLoader.updateDelegateCreationRange();
325− onYChanged: rendererLoader.updateDelegateCreationRange();
326392 }
327393
328394 sectionProperty: "name"
329395 sectionDelegate: ListItems.Header {
330396 objectName: "dashSectionHeader" + (delegate ? delegate.category : "")
331− property var delegate: categoryView.item(delegateIndex)
397+ readonly property var delegate: categoryView.item(delegateIndex)
332398 width: categoryView.width
333399 text: section
334− textColor: scopeStyle ? scopeStyle.foreground : "grey"
335− image: {
336− if (delegate && delegate.expandable)
337− return delegate.filtered ? "graphics/header_handlearrow.png" : "graphics/header_handlearrow2.png"
338− return "";
339− }
400+ color: scopeStyle ? scopeStyle.foreground : "grey"
401+ iconName: delegate && delegate.headerLink ? "go-next" : ""
340402 onClicked: {
341− if (categoryView.expandedCategoryId != delegate.category)
342− categoryView.expandedCategoryId = delegate.category;
343− else
344− categoryView.expandedCategoryId = "";
403+ if (delegate.headerLink) scopeView.scope.performQuery(delegate.headerLink);
345404 }
346405 }
347406
@@ -376,6 +435,10 @@ FocusScope {
376435 }
377436 }
378437
438+ LimitProxyModel {
439+ id: previewLimitModel
440+ }
441+
379442 PreviewListView {
380443 id: previewListView
381444 objectName: "previewListView"
qml/Dash/ScopesOverviewAll.qml+8−11
@@ -20,31 +20,28 @@ import Ubuntu.Components 0.1
2020Flickable {
2121 id: root
2222
23− property alias model: cardFilterGrid.model
24− property alias cardTool: cardFilterGrid.cardTool
23+ property alias model: cardGrid.model
24+ property alias cardTool: cardGrid.cardTool
2525
2626 property real extraHeight: 0
2727
2828 signal clicked(int index, var result, var item, var itemModel)
2929 signal pressAndHold(int index)
3030
31− contentHeight: cardFilterGrid.height + extraHeight
32− contentWidth: cardFilterGrid.width
31+ contentHeight: cardGrid.expandedHeight + extraHeight
32+ contentWidth: cardGrid.width
3333
3434 function scopeCardPosition(scopeId) {
3535 var index = model.scopeIndex(scopeId);
36− var pos = cardFilterGrid.cardPosition(index);
36+ var pos = cardGrid.cardPosition(index);
3737 pos.y = pos.y - root.contentY;
3838 return pos;
3939 }
4040
41− CardFilterGrid {
42− id: cardFilterGrid
41+ CardGrid {
42+ id: cardGrid
4343 width: parent.width
44−
45− Component.onCompleted: {
46− setFilter(false, false);
47− }
44+ height: parent.height
4845
4946 onClicked: {
5047 root.clicked(index, result, item, itemModel);
tests/autopilot/unity8/shell/emulators/dash.py+6−3
@@ -41,7 +41,7 @@ class Dash(emulators.UnityEmulatorBase):
4141
4242 def get_applications_grid(self):
4343 get_grid = self.get_scope('clickscope').wait_select_single(
44− 'CardFilterGrid', objectName='local')
44+ 'CardGrid', objectName='local')
4545 return get_grid
4646
4747 def get_application_icon(self, text):
@@ -205,9 +205,12 @@ class DashApps(GenericScopeView):
205205 category_element = self._get_category_element(category)
206206 application_cards = category_element.select_many('AbstractButton')
207207
208− # sort by y, x
208+ # sort by y, x, filter those out of view
209+ categoryBottom = category_element.globalRect.y \
210+ + category_element.height
209211 application_cards = sorted(
210− application_cards,
212+ (card for card in application_cards
213+ if card.globalRect.y < categoryBottom),
211214 key=lambda card: (card.globalRect.y, card.globalRect.x))
212215
213216 result = []
tests/autopilot/unity8/shell/tests/test_emulators.py+5−5
@@ -191,9 +191,9 @@ class DashAppsEmulatorTestCase(DashBaseTestCase):
191191 def _get_number_of_application_slots(self, category):
192192 category_element = self.applications_scope._get_category_element(
193193 category)
194− grid = category_element.select_single('CardFilterGrid')
195− filtergrid = grid.select_single('FilterGrid')
196− if (grid.filtered):
197− return filtergrid.collapsedRowCount * filtergrid.columns
194+ cardgrid = category_element.select_single('CardGrid')
195+ if (category_element.expanded):
196+ return cardgrid.select_single('QQuickGridView').count
198197 else:
199− return filtergrid.uncollapsedRowCount * filtergrid.columns
198+ return cardgrid.collapsedRows \
199+ * cardgrid.select_single('ResponsiveGridView').columns
tests/mocks/Ubuntu/Telephony/CMakeLists.txt+1−9
@@ -11,12 +11,4 @@ add_library(FakeUbuntuTelephonyQml MODULE ${FakeUbuntuTelephonyQml_SOURCES})
1111
1212qt5_use_modules(FakeUbuntuTelephonyQml Core Quick)
1313
14−# copy files into build directory for shadow builds
15−add_custom_target(UbuntuTelephonyQmlDirFile ALL
16− COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/qmldir" ${CMAKE_CURRENT_BINARY_DIR}
17− DEPENDS qmldir
18−)
19−
20−install(TARGETS FakeUbuntuTelephonyQml
21− DESTINATION ${SHELL_INSTALL_QML}/mocks/Ubuntu/Telephony
22− )
14+add_unity8_mock(Ubuntu.Telephony 0.1 Ubuntu/Telephony TARGETS FakeUbuntuTelephonyQml)
tests/mocks/Unity/fake_categories.cpp+13−1
@@ -145,9 +145,12 @@ Categories::data(const QModelIndex& index, int role) const
145145 } else {
146146 map["category-layout"] = "carousel";
147147 map["card-size"] = "medium";
148− map["collapsed-rows"] = 2;
149148 map["overlay"] = true;
150149 }
150+ if (index.row() == 19) {
151+ map["category-layout"] = "grid";
152+ map["collapsed-rows"] = 0;
153+ }
151154 map["card-size"] = "small";
152155 return map;
153156 }
@@ -166,6 +169,9 @@ Categories::data(const QModelIndex& index, int role) const
166169 return map;
167170 }
168171 case RoleHeaderLink:
172+ if (index.row() == 1 || index.row() == 4) {
173+ return QString("scope://query/1");
174+ }
169175 return QString();
170176 case RoleResults:
171177 return QVariant::fromValue(resultsModel);
@@ -177,3 +183,9 @@ Categories::data(const QModelIndex& index, int role) const
177183 }
178184 }
179185}
186+
187+QVariant
188+Categories::data(int row, int role) const
189+{
190+ return data(index(row, 0), role);
191+}
tests/mocks/Unity/fake_categories.h+2
@@ -37,6 +37,8 @@ public:
3737 Q_INVOKABLE void addSpecialCategory(QString const& categoryId, QString const& name, QString const& icon, QString const& rawTemplate, QObject* countObject) override;
3838 Q_INVOKABLE bool overrideCategoryJson(QString const& categoryId, QString const& json) override;
3939
40+ Q_INVOKABLE QVariant data(int row, int role) const;
41+
4042private Q_SLOTS:
4143 void countChanged();
4244
tests/mocks/Unity/fake_scope.cpp+2−7
@@ -27,7 +27,7 @@ Scope::Scope(Scopes* parent) : Scope(QString(), QString(), false, parent)
2727{
2828}
2929
30−Scope::Scope(QString const& id, QString const& name, bool visible, Scopes* parent)
30+Scope::Scope(QString const& id, QString const& name, bool visible, Scopes* parent, int categories)
3131 : unity::shell::scopes::ScopeInterface(parent)
3232 , m_id(id)
3333 , m_name(name)
@@ -36,7 +36,7 @@ Scope::Scope(QString const& id, QString const& name, bool visible, Scopes* paren
3636 , m_isActive(false)
3737 , m_currentDeparment("root")
3838 , m_previewRendererName("preview-generic")
39− , m_categories(new Categories(20, this))
39+ , m_categories(new Categories(categories, this))
4040 , m_openScope(nullptr)
4141{
4242}
@@ -229,8 +229,3 @@ void Scope::loadDepartment(const QString& id)
229229 m_currentDeparment = id;
230230 Q_EMIT currentDepartmentIdChanged();
231231}
232−
233−void Scope::performQuery(const QString& query)
234−{
235− Q_UNUSED(query);
236−}
tests/mocks/Unity/fake_scope.h+2−2
@@ -32,7 +32,7 @@ class Scope : public unity::shell::scopes::ScopeInterface
3232
3333public:
3434 Scope(Scopes* parent = 0);
35− Scope(QString const& id, QString const& name, bool visible, Scopes* parent = 0);
35+ Scope(QString const& id, QString const& name, bool visible, Scopes* parent = 0, int categories = 20);
3636
3737 /* getters */
3838 QString id() const override;
@@ -66,7 +66,7 @@ public:
6666 bool hasDepartments() const override;
6767 Q_INVOKABLE unity::shell::scopes::DepartmentInterface* getDepartment(const QString& id) override;
6868 Q_INVOKABLE void loadDepartment(const QString& id) override;
69− Q_INVOKABLE void performQuery(const QString& query) override;
69+ Q_SIGNAL void performQuery(const QString& query) override;
7070
7171 QVariantMap customizations() const override;
7272
tests/mocks/Unity/fake_scopes.cpp+1−1
@@ -48,7 +48,7 @@ void Scopes::updateScopes()
4848 addScope(new Scope("MockScope2", "Music", false, this));
4949 addScope(new Scope("clickscope", "Apps", true, this));
5050 addScope(new Scope("MockScope5", "Videos", true, this));
51− addScope(new Scope("MockScope3", "MS3", true, this));
51+ addScope(new Scope("SingleCategoryScope", "Single", true, this, 1));
5252 addScope(new Scope("MockScope4", "MS4", true, this));
5353 addScope(new Scope("MockScope6", "MS6", true, this));
5454 addScope(new Scope("MockScope7", "MS7", false, this));
tests/mocks/Unity/fake_scopesoverview.cpp+4
@@ -115,6 +115,8 @@ ScopesOverviewCategories::data(const QModelIndex& index, int role) const
115115 return QVariant::fromValue(resultsModel);
116116 case RoleCount:
117117 return resultsModel->rowCount();
118+ case RoleHeaderLink:
119+ return QString();
118120 default:
119121 qFatal("Using un-implemented Categories role");
120122 return QVariant();
@@ -196,6 +198,8 @@ ScopesOverviewSearchCategories::data(const QModelIndex& index, int role) const
196198 return QVariant::fromValue(resultsModel);
197199 case RoleCount:
198200 return resultsModel->rowCount();
201+ case RoleHeaderLink:
202+ return QString();
199203 default:
200204 qFatal("Using un-implemented Categories role");
201205 return QVariant();
tests/qmltests/CMakeLists.txt−1
@@ -24,7 +24,6 @@ add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_
2424add_qml_test(Components Carousel)
2525add_qml_test(Components DraggingArea)
2626add_qml_test(Components EdgeDemoOverlay)
27−add_qml_test(Components FilterGrid)
2827add_qml_test(Components LazyImage)
2928add_qml_test(Components Rating)
3029add_qml_test(Components ResponsiveFlowView)
tests/qmltests/Components/tst_FilterGrid.qmldeleted−190
@@ -1,190 +0,0 @@
1−/*
2− * Copyright 2013 Canonical Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import QtTest 1.0
19−import "../../../qml/Components"
20−import Ubuntu.Components.ListItems 0.1 as ListItem
21−import Ubuntu.Components 0.1
22−import Unity.Test 0.1 as UT
23−import Utils 0.1
24−
25−/*
26− You should see 6 green squares (from "A" to "F") and a button "View all (12)".
27− Once you press that button other 6 green squares (from "G" to "L") should show up.
28−*/
29−Rectangle {
30− width: gridRect.width + controls.width
31− height: units.gu(50)
32− color: "white"
33−
34− Column {
35− id: controls
36− width: units.gu(30)
37− height: parent.height
38− anchors.top: parent.top
39− anchors.right: parent.right
40− ListItem.ValueSelector {
41− id: collapsedRowCountSelector
42− text: "collapsedRowCount"
43− values: [1,2,3,4]
44− selectedIndex: 1
45− }
46− Row {
47− spacing: units.gu(1)
48− Label { anchors.verticalCenter: parent.verticalCenter
49− text: "Filter" }
50− CheckBox {
51− id: filterCheckBox
52− checked: true
53− onCheckedChanged: filterGrid.setFilter(checked, false /*animate*/)
54− }
55− }
56− }
57−
58− ListModel {
59− id: fakeModel
60− ListElement { name: "A" }
61− ListElement { name: "B" }
62− ListElement { name: "C" }
63− ListElement { name: "D" }
64− ListElement { name: "E" }
65− ListElement { name: "F" }
66− ListElement { name: "G" }
67− ListElement { name: "H" }
68− ListElement { name: "I" }
69− ListElement { name: "J" }
70− ListElement { name: "K" }
71− ListElement { name: "L" }
72− }
73−
74− ListModel {
75− id: fakeModelTwoItems
76− ListElement { name: "A" }
77− ListElement { name: "B" }
78− }
79−
80− ListModel {
81− id: fakeModelFourItems
82− ListElement { name: "A" }
83− ListElement { name: "B" }
84− ListElement { name: "C" }
85− ListElement { name: "D" }
86− }
87−
88− Rectangle {
89− id: gridRect
90− width: units.gu(30)
91− height: parent.height
92− color: "grey"
93− anchors.top: parent.top
94− anchors.left: parent.left
95−
96− FilterGrid {
97− id: filterGrid
98− anchors.fill: parent
99− model: fakeModel
100− maximumNumberOfColumns: 3
101− collapsedRowCount:
102− collapsedRowCountSelector.values[collapsedRowCountSelector.selectedIndex]
103− minimumHorizontalSpacing: units.gu(1)
104− delegateWidth: units.gu(6)
105− delegateHeight: units.gu(6)
106− verticalSpacing: units.gu(1)
107−
108− delegate: Rectangle {
109− // So that it can be identified by countVisibleDelegates()
110− property bool isGridDelegate: true
111−
112− color: "green"
113− width: units.gu(6)
114− height: units.gu(6)
115− Text {
116− anchors.centerIn: parent
117− text: name
118− }
119− }
120− }
121− }
122−
123− UT.UnityTestCase {
124− name: "FilterGrid"
125− when: windowShown
126−
127− function init() {
128− filterGrid.model = fakeModel
129− }
130−
131− function cleanup() {
132− filterCheckBox.checked = true
133− collapsedRowCountSelector.selectedIndex = 1
134− }
135−
136− function test_turningFilterOffShowsAllElements() {
137− tryCompareFunction(countVisibleDelegates, 6)
138−
139− filterCheckBox.checked = false
140−
141− tryCompareFunction(countVisibleDelegates, 12)
142− }
143−
144− function test_collapsedRowCount() {
145− for (var i = 0; i < 4; ++i) {
146− collapsedRowCountSelector.selectedIndex = i
147− // We have 3 elements per row.
148− // row count == index + 1
149− tryCompareFunction(countVisibleDelegates, 3*(i+1))
150− }
151− }
152−
153− function test_modelSizeAffectsCollapsedRowCount_data() {
154− return [
155− { tag: "2 items, collapsedRows 1", model: fakeModelTwoItems, collapsedRowCountIndex: 0,
156− rowsWhenCollapsed: 1, visibleDelegates: 2 },
157− { tag: "2 items, collapsedRows 2", model: fakeModelTwoItems, collapsedRowCountIndex: 1,
158− rowsWhenCollapsed: 1, visibleDelegates: 2 },
159− { tag: "4 items, collapsedRows 1", model: fakeModelFourItems, collapsedRowCountIndex: 0,
160− rowsWhenCollapsed: 1, visibleDelegates: 3 },
161− { tag: "4 items, collapsedRows 2", model: fakeModelFourItems, collapsedRowCountIndex: 1,
162− rowsWhenCollapsed: 2, visibleDelegates: 4 },
163− ]
164− }
165−
166− function test_modelSizeAffectsCollapsedRowCount(data) {
167− filterGrid.model = data.model
168− collapsedRowCountSelector.selectedIndex = data.collapsedRowCountIndex
169−
170− tryCompare(filterGrid, "rowsWhenCollapsed", data.rowsWhenCollapsed)
171− tryCompareFunction(countVisibleDelegates, data.visibleDelegates)
172− }
173−
174− function countVisibleDelegates() {
175− return __countVisibleDelegates(filterGrid.visibleChildren, 0)
176− }
177−
178− function __countVisibleDelegates(objList, total) {
179− for (var i = 0; i < objList.length; ++i) {
180− var child = objList[i];
181− if (child.isGridDelegate !== undefined) {
182− ++total;
183− } else {
184− total = __countVisibleDelegates(child.visibleChildren, total)
185− }
186− }
187− return total
188− }
189− }
190−}
tests/qmltests/Dash/tst_Dash.qml+1−1
@@ -234,7 +234,7 @@ Item {
234234 var dashCategory2 = findChild(dashContentList.currentItem, "dashCategory2");
235235 var card2 = findChild(dashCategory2, "delegate2");
236236 waitForRendering(card2);
237− mouseClick(card2, 0, 0);
237+ mouseClick(card2, card2.width / 2, card2.height / 2);
238238
239239 // Wait for temp scope to be there
240240 var dashTempScopeItem = findChild(dash, "dashTempScopeItem");
tests/qmltests/Dash/tst_DashContent.qml+8−4
@@ -117,10 +117,11 @@ Item {
117117
118118 function test_show_header_on_list_movement() {
119119 var dashContentList = findChild(dashContent, "dashContentList");
120− verify(dashContentList != undefined);
121− var categoryListView = findChild(dashContentList, "categoryListView");
122− verify(categoryListView != undefined);
120+ verify(dashContentList !== null);
121+ var scope = findChild(dashContent, "MockScope1 loader");
122+ waitForRendering(scope);
123123
124+ var categoryListView = findChild(scope, "categoryListView");
124125 waitForRendering(categoryListView);
125126
126127 categoryListView.contentY = units.gu(15);
@@ -135,8 +136,11 @@ Item {
135136 function test_set_current_scope_reset() {
136137 var dashContentList = findChild(dashContent, "dashContentList");
137138 verify(dashContentList != undefined);
139+ var scope = findChild(dashContent, "MockScope1 loader");
140+ waitForRendering(scope);
141+
138142 var categoryListView = findChild(dashContentList, "categoryListView");
139− verify(categoryListView != undefined);
143+ waitForRendering(categoryListView);
140144
141145 categoryListView.contentY = units.gu(10);
142146
tests/qmltests/Dash/tst_GenericScopeView.qml+112−45
@@ -39,6 +39,10 @@ Item {
3939 id: scopes
4040 }
4141
42+ SignalSpy {
43+ id: spy
44+ }
45+
4246 property Item applicationManager: Item {
4347 signal sideStageFocusedApplicationChanged()
4448 signal mainStageFocusedApplicationChanged()
@@ -59,7 +63,14 @@ Item {
5963 genericScopeView.scope = scopes.getScope(1)
6064 shell.width = units.gu(120)
6165 genericScopeView.categoryView.positionAtBeginning();
62− tryCompare(genericScopeView.categoryView, "contentY", 0)
66+ waitForRendering(genericScopeView.categoryView);
67+ }
68+
69+ function cleanup() {
70+ genericScopeView.scope = null;
71+ spy.clear();
72+ spy.target = null;
73+ spy.signalName = "";
6374 }
6475
6576 function test_isActive() {
@@ -104,90 +115,146 @@ Item {
104115 tryCompare(genericScopeView.scope, "searchQuery", "test")
105116 }
106117
107− function test_filter_expand_collapse() {
108− // wait for the item to be there
109− waitForRendering(genericScopeView);
118+ function test_expand_collapse() {
110119 tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader0") != null; }, true);
111120
112− var header = findChild(genericScopeView, "dashSectionHeader0")
113121 var category = findChild(genericScopeView, "dashCategory0")
122+ var seeAll = findChild(category, "seeAll")
114123
115− waitForRendering(header);
124+ waitForRendering(seeAll);
116125 verify(category.expandable);
117− verify(category.filtered);
126+ verify(!category.expanded);
118127
119128 var initialHeight = category.height;
120− var middleHeight;
121− mouseClick(header, header.width / 2, header.height / 2);
122− tryCompareFunction(function() { middleHeight = category.height; return category.height > initialHeight; }, true);
123− tryCompare(category, "filtered", false);
124− tryCompareFunction(function() { return category.height > middleHeight; }, true);
129+ mouseClick(seeAll, seeAll.width / 2, seeAll.height / 2);
130+ verify(category.expanded);
131+ tryCompareFunction(function() { return category.height > initialHeight; }, true);
125132
126− mouseClick(header, header.width / 2, header.height / 2);
127− verify(category.expandable);
128− tryCompare(category, "filtered", true);
133+ mouseClick(seeAll, seeAll.width / 2, seeAll.height / 2);
134+ verify(!category.expanded);
129135 }
130136
131− function test_filter_expand_expand_collapse() {
137+ function test_expand_expand_collapse() {
132138 // wait for the item to be there
133139 tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader2") != null; }, true);
134140
135141 var categoryListView = findChild(genericScopeView, "categoryListView");
136142 categoryListView.contentY = categoryListView.height;
137143
138− var header2 = findChild(genericScopeView, "dashSectionHeader2")
139144 var category2 = findChild(genericScopeView, "dashCategory2")
140− var category2FilterGrid = category2.children[1].children[2];
141− verify(UT.Util.isInstanceOf(category2FilterGrid, "CardFilterGrid"));
145+ var seeAll2 = findChild(category2, "seeAll")
142146
143− waitForRendering(header2);
147+ waitForRendering(seeAll2);
144148 verify(category2.expandable);
145− verify(category2.filtered);
149+ verify(!category2.expanded);
146150
147− mouseClick(header2, header2.width / 2, header2.height / 2);
148− tryCompare(category2, "filtered", false);
149− tryCompare(category2FilterGrid, "filtered", false);
151+ mouseClick(seeAll2, seeAll2.width / 2, seeAll2.height / 2);
152+ tryCompare(category2, "expanded", true);
150153
151154 categoryListView.positionAtBeginning();
152155
153− var header0 = findChild(genericScopeView, "dashSectionHeader0")
154156 var category0 = findChild(genericScopeView, "dashCategory0")
155− mouseClick(header0, header0.width / 2, header0.height / 2);
156− tryCompare(category0, "filtered", false);
157− tryCompare(category2, "filtered", true);
158− tryCompare(category2FilterGrid, "filtered", true);
159− mouseClick(header0, header0.width / 2, header0.height / 2);
160− tryCompare(category0, "filtered", true);
161− tryCompare(category2, "filtered", true);
157+ var seeAll0 = findChild(category0, "seeAll")
158+ mouseClick(seeAll0, seeAll0.width / 2, seeAll0.height / 2);
159+ tryCompare(category0, "expanded", true);
160+ tryCompare(category2, "expanded", false);
161+ mouseClick(seeAll0, seeAll0.width / 2, seeAll0.height / 2);
162+ tryCompare(category0, "expanded", false);
163+ tryCompare(category2, "expanded", false);
164+ }
165+
166+ function test_headerLink() {
167+ tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader1") != null; }, true);
168+ var header = findChild(genericScopeView, "dashSectionHeader1");
169+
170+ spy.target = genericScopeView.scope;
171+ spy.signalName = "performQuery";
172+
173+ mouseClick(header, header.width / 2, header.height / 2);
174+
175+ spy.wait();
176+ compare(spy.signalArguments[0][0], genericScopeView.scope.categories.data(1, Categories.RoleHeaderLink));
177+ }
178+
179+ function test_headerLink_disable_expansion() {
180+ var categoryListView = findChild(genericScopeView, "categoryListView");
181+ waitForRendering(categoryListView);
182+
183+ categoryListView.contentY = categoryListView.height * 2;
184+
185+ // wait for the item to be there
186+ tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader4") != null; }, true);
187+
188+ var categoryView = findChild(genericScopeView, "dashCategory4");
189+ verify(categoryView, "Can't find the category view.");
190+
191+ var seeAll = findChild(categoryView, "seeAll");
192+ verify(seeAll, "Can't find the seeAll element");
193+
194+ compare(seeAll.height, 0, "SeeAll should be 0-height.");
195+
196+ openPreview(4, 0);
197+
198+ compare(testCase.previewListView.count, 12, "There should only be 12 items in preview.");
162199 }
163200
164201 function test_narrow_delegate_ranges_expand() {
165− tryCompareFunction(function() { return findChild(genericScopeView, "dashCategory0") != undefined; }, true);
202+ tryCompareFunction(function() { return findChild(genericScopeView, "dashCategory0") !== null; }, true);
166203 var category = findChild(genericScopeView, "dashCategory0")
167− tryCompare(category, "filtered", true);
204+ tryCompare(category, "expanded", false);
168205
169206 shell.width = units.gu(20)
170207 var categoryListView = findChild(genericScopeView, "categoryListView");
171208 categoryListView.contentY = units.gu(20);
172− var header0 = findChild(genericScopeView, "dashSectionHeader0")
173− mouseClick(header0, header0.width / 2, header0.height / 2);
174− tryCompare(category, "filtered", false);
209+ var seeAll = findChild(category, "seeAll")
210+ mouseClick(seeAll, seeAll.width / 2, seeAll.height / 2);
211+ tryCompare(category, "expanded", true);
175212 tryCompareFunction(function() { return category.item.height == genericScopeView.height - category.item.displayMarginBeginning - category.item.displayMarginEnd; }, true);
176− mouseClick(header0, header0.width / 2, header0.height / 2);
177− tryCompare(category, "filtered", true);
213+ mouseClick(seeAll, seeAll.width / 2, seeAll.height / 2);
214+ tryCompare(category, "expanded", false);
215+ }
216+
217+ function test_forced_category_expansion() {
218+ tryCompareFunction(function() {
219+ mouseFlick(genericScopeView, genericScopeView.width/2, genericScopeView.height,
220+ genericScopeView.width/2, genericScopeView.y)
221+ return findChild(genericScopeView, "dashCategory19") !== null;
222+ }, true);
223+ var category = findChild(genericScopeView, "dashCategory19")
224+ compare(category.expandable, false, "Category with collapsed-rows: 0 should not be expandable");
225+
226+ var grid = findChild(category, "19");
227+ verify(grid, "Could not find the category renderer.");
228+
229+ compare(grid.height, grid.expandedHeight, "Category with collapsed-rows: 0 should always be expanded.");
230+ }
231+
232+ function test_single_category_expansion() {
233+ genericScopeView.scope = scopes.getScope(4);
234+
235+ tryCompareFunction(function() { return findChild(genericScopeView, "dashCategory0") != undefined; }, true);
236+ var category = findChild(genericScopeView, "dashCategory0")
237+ compare(category.expandable, false, "Only category should not be expandable.");
238+
239+ var grid = findChild(category, "0");
240+ verify(grid, "Could not find the category renderer.");
241+
242+ compare(grid.height, grid.expandedHeight, "Only category should always be expanded");
178243 }
179244
180− function openPreview() {
245+ function openPreview(category, delegate) {
246+ if (category === undefined) category = 0;
247+ if (delegate === undefined) delegate = 0;
181248 tryCompareFunction(function() {
182− var filterGrid = findChild(genericScopeView, "0");
183− if (filterGrid != null) {
184− var tile = findChild(filterGrid, "delegate0");
249+ var cardGrid = findChild(genericScopeView, category);
250+ if (cardGrid != null) {
251+ var tile = findChild(cardGrid, "delegate"+delegate);
185252 return tile != null;
186253 }
187254 return false;
188255 },
189256 true);
190− var tile = findChild(findChild(genericScopeView, "0"), "delegate0");
257+ var tile = findChild(findChild(genericScopeView, category), "delegate"+delegate);
191258 mouseClick(tile, tile.width / 2, tile.height / 2);
192259 tryCompare(testCase.previewListView, "open", true);
193260 tryCompare(testCase.previewListView, "x", 0);
@@ -257,7 +324,7 @@ Item {
257324 closePreview();
258325 }
259326
260− function test_haeder_logo() {
327+ function test_header_logo() {
261328 genericScopeView.scope = scopes.getScope(3);
262329
263330 var image = findChild(genericScopeView, "titleImage");