on xenial help sign in
Merge lp:~unity-team/unity8/scopes_more_things_on_memory
2d8b256Michał Sawicz
@@ -51,6 +51,7 @@ install_dependencies() {
51 qtdeclarative5-ubuntu-settings-components \
51 qtdeclarative5-ubuntu-settings-components \
52 qtdeclarative5-ubuntu-telephony0.1 \
52 qtdeclarative5-ubuntu-telephony0.1 \
53 qtdeclarative5-ubuntu-thumbnailer0.1 \
53 qtdeclarative5-ubuntu-thumbnailer0.1 \
54+ qml-module-ubuntu-connectivity \
54 unity-notifications-impl \
55 unity-notifications-impl \
55 ubuntu-mobile-icons \
56 ubuntu-mobile-icons \
56 unity-scope-home \
57 unity-scope-home \
cmake/modules/QmlTest.cmake+2−1
@@ -131,7 +131,7 @@ macro(add_qml_test_internal SUBPATH COMPONENT_NAME ITERATIONS)
131 add_manual_qml_test(${SUBPATH} ${COMPONENT_NAME} ${ARGN})
131 add_manual_qml_test(${SUBPATH} ${COMPONENT_NAME} ${ARGN})
132endmacro(add_qml_test_internal)
132endmacro(add_qml_test_internal)
133
133
134−macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS)
134+macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS ENVVAR)
135 set(testCommand
135 set(testCommand
136 LD_LIBRARY_PATH=${LD_PATH}
136 LD_LIBRARY_PATH=${LD_PATH}
137 ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
137 ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
@@ -148,6 +148,7 @@ macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS)
148 endif()
148 endif()
149 set(xvfbtestCommand
149 set(xvfbtestCommand
150 ${LD_PRELOAD_PATH}
150 ${LD_PRELOAD_PATH}
151+ ${ENVVAR}
151 LD_LIBRARY_PATH=${LD_PATH}
152 LD_LIBRARY_PATH=${LD_PATH}
152 xvfb-run --server-args "-screen 0 1024x768x24" --auto-servernum
153 xvfb-run --server-args "-screen 0 1024x768x24" --auto-servernum
153 ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
154 ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
debian/control+1
@@ -173,6 +173,7 @@ Depends: accountsservice-ubuntu-schemas (>= 0.0.3),
173 libhardware2,
173 libhardware2,
174 libunity-core-6.0-9,
174 libunity-core-6.0-9,
175 pay-service,
175 pay-service,
176+ qml-module-ubuntu-connectivity,
176 ${misc:Depends},
177 ${misc:Depends},
177 ${shlibs:Depends},
178 ${shlibs:Depends},
178Provides: unity-launcher-impl,
179Provides: unity-launcher-impl,
plugins/Dash/CardCreator.js+5−2
@@ -84,7 +84,8 @@ var kArtShapeHolderCode = 'Item { \n\
84 } \n\
84 } \n\
85 image: Image { \n\
85 image: Image { \n\
86 objectName: "artImage"; \n\
86 objectName: "artImage"; \n\
87+ property bool doLoadSource: !NetworkingStatus.limitedBandwith; \n\
87− source: cardData && cardData["art"] || ""; \n\
88+ source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["art"] || ""; } \n\
88 cache: true; \n\
89 cache: true; \n\
89 asynchronous: root.asynchronous; \n\
90 asynchronous: root.asynchronous; \n\
90 fillMode: Image.PreserveAspectCrop; \n\
91 fillMode: Image.PreserveAspectCrop; \n\
@@ -195,7 +196,8 @@ var kMascotImageCode = 'Image { \n\
195 objectName: "mascotImage"; \n\
196 objectName: "mascotImage"; \n\
196 anchors { %1 } \n\
197 anchors { %1 } \n\
197 readonly property int maxSize: Math.max(width, height) * 4; \n\
198 readonly property int maxSize: Math.max(width, height) * 4; \n\
199+ property bool doLoadSource: !NetworkingStatus.limitedBandwith; \n\
198− source: cardData && cardData["mascot"] || ""; \n\
200+ source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["mascot"] || ""; } \n\
199 width: units.gu(6); \n\
201 width: units.gu(6); \n\
200 height: units.gu(5.625); \n\
202 height: units.gu(5.625); \n\
201 sourceSize { width: maxSize; height: maxSize } \n\
203 sourceSize { width: maxSize; height: maxSize } \n\
@@ -654,6 +656,7 @@ function createCardComponent(parent, template, components) {
654 var imports = 'import QtQuick 2.2; \n\
656 var imports = 'import QtQuick 2.2; \n\
655 import Ubuntu.Components 1.1; \n\
657 import Ubuntu.Components 1.1; \n\
656 import Ubuntu.Settings.Components 0.1; \n\
658 import Ubuntu.Settings.Components 0.1; \n\
659+ import Ubuntu.Connectivity 1.0; \n\
657 import Dash 0.1;\n\
660 import Dash 0.1;\n\
658 import Utils 0.1;\n';
661 import Utils 0.1;\n';
659 var card = cardString(template, components);
662 var card = cardString(template, components);
plugins/Dash/listviewwithpageheader.cpp+28−7
@@ -104,8 +104,6 @@
104#pragma GCC diagnostic pop
104#pragma GCC diagnostic pop
105// #include <private/qquickrectangle_p.h>
105// #include <private/qquickrectangle_p.h>
106
106
107−static const qreal bufferRatio = 0.5;
108−
109qreal ListViewWithPageHeader::ListItem::height() const
107qreal ListViewWithPageHeader::ListItem::height() const
110{
108{
111 return m_item->height() + (m_sectionItem ? m_sectionItem->height() : 0);
109 return m_item->height() + (m_sectionItem ? m_sectionItem->height() : 0);
@@ -158,6 +156,7 @@ ListViewWithPageHeader::ListViewWithPageHeader()
158 , m_forceNoClip(false)
156 , m_forceNoClip(false)
159 , m_inLayout(false)
157 , m_inLayout(false)
160 , m_inContentHeightKeepHeaderShown(false)
158 , m_inContentHeightKeepHeaderShown(false)
159+ , m_cacheBuffer(0)
161{
160{
162 m_clipItem = new QQuickItem(contentItem());
161 m_clipItem = new QQuickItem(contentItem());
163// m_clipItem = new QQuickRectangle(contentItem());
162// m_clipItem = new QQuickRectangle(contentItem());
@@ -335,6 +334,20 @@ qreal ListViewWithPageHeader::headerItemShownHeight() const
335 return m_headerItemShownHeight;
334 return m_headerItemShownHeight;
336}
335}
337
336
337+qreal ListViewWithPageHeader::cacheBuffer() const
338+{
339+ return m_cacheBuffer;
340+}
341+
342+void ListViewWithPageHeader::setCacheBuffer(qreal cacheBuffer)
343+{
344+ if (cacheBuffer != m_cacheBuffer) {
345+ m_cacheBuffer = cacheBuffer;
346+ Q_EMIT cacheBufferChanged();
347+ polish();
348+ }
349+}
350+
338void ListViewWithPageHeader::positionAtBeginning()
351void ListViewWithPageHeader::positionAtBeginning()
339{
352{
340 if (m_delegateModel->count() <= 0)
353 if (m_delegateModel->count() <= 0)
@@ -356,8 +369,7 @@ void ListViewWithPageHeader::positionAtBeginning()
356 // Create the subsequent items
369 // Create the subsequent items
357 int modelIndex = 1;
370 int modelIndex = 1;
358 qreal pos = item->y() + item->height();
371 qreal pos = item->y() + item->height();
359− const qreal buffer = height() * bufferRatio;
360− const qreal bufferTo = height() + buffer;
372+ const qreal bufferTo = height() + m_cacheBuffer;
361 while (modelIndex < m_delegateModel->count() && pos <= bufferTo) {
373 while (modelIndex < m_delegateModel->count() && pos <= bufferTo) {
362 if (!(item = createItem(modelIndex, false)))
374 if (!(item = createItem(modelIndex, false)))
363 break;
375 break;
@@ -409,6 +421,16 @@ void ListViewWithPageHeader::showHeader()
409 }
421 }
410}
422}
411
423
424+Q_INVOKABLE int ListViewWithPageHeader::firstCreatedIndex() const
425+{
426+ return m_firstVisibleIndex;
427+}
428+
429+Q_INVOKABLE int ListViewWithPageHeader::createdItemCount() const
430+{
431+ return m_visibleItems.count();
432+}
433+
412QQuickItem *ListViewWithPageHeader::item(int modelIndex) const
434QQuickItem *ListViewWithPageHeader::item(int modelIndex) const
413{
435{
414 ListItem *item = itemAtIndex(modelIndex);
436 ListItem *item = itemAtIndex(modelIndex);
@@ -593,11 +615,10 @@ void ListViewWithPageHeader::refill()
593 return;
615 return;
594 }
616 }
595
617
596− const qreal buffer = height() * bufferRatio;
597 const qreal from = contentY();
618 const qreal from = contentY();
598 const qreal to = from + height();
619 const qreal to = from + height();
599− const qreal bufferFrom = from - buffer;
620+ const qreal bufferFrom = from - m_cacheBuffer;
600− const qreal bufferTo = to + buffer;
621+ const qreal bufferTo = to + m_cacheBuffer;
601
622
602 bool added = addVisibleItems(from, to, false);
623 bool added = addVisibleItems(from, to, false);
603 bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
624 bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
plugins/Dash/listviewwithpageheader.h+8
@@ -54,6 +54,7 @@ class ListViewWithPageHeader : public QQuickFlickable, public QQuickItemChangeLi
54 Q_PROPERTY(bool forceNoClip READ forceNoClip WRITE setForceNoClip NOTIFY forceNoClipChanged)
54 Q_PROPERTY(bool forceNoClip READ forceNoClip WRITE setForceNoClip NOTIFY forceNoClipChanged)
55 Q_PROPERTY(int stickyHeaderHeight READ stickyHeaderHeight NOTIFY stickyHeaderHeightChanged)
55 Q_PROPERTY(int stickyHeaderHeight READ stickyHeaderHeight NOTIFY stickyHeaderHeightChanged)
56 Q_PROPERTY(qreal headerItemShownHeight READ headerItemShownHeight NOTIFY headerItemShownHeightChanged)
56 Q_PROPERTY(qreal headerItemShownHeight READ headerItemShownHeight NOTIFY headerItemShownHeightChanged)
57+ Q_PROPERTY(qreal cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged)
57
58
58 friend class ListViewWithPageHeaderTest;
59 friend class ListViewWithPageHeaderTest;
59 friend class ListViewWithPageHeaderTestSection;
60 friend class ListViewWithPageHeaderTestSection;
@@ -84,8 +85,13 @@ public:
84 int stickyHeaderHeight() const;
85 int stickyHeaderHeight() const;
85 qreal headerItemShownHeight() const;
86 qreal headerItemShownHeight() const;
86
87
88+ qreal cacheBuffer() const;
89+ void setCacheBuffer(qreal cacheBuffer);
90+
87 Q_INVOKABLE void positionAtBeginning();
91 Q_INVOKABLE void positionAtBeginning();
88 Q_INVOKABLE void showHeader();
92 Q_INVOKABLE void showHeader();
93+ Q_INVOKABLE int firstCreatedIndex() const;
94+ Q_INVOKABLE int createdItemCount() const;
89 Q_INVOKABLE QQuickItem *item(int modelIndex) const;
95 Q_INVOKABLE QQuickItem *item(int modelIndex) const;
90
96
91 // The index has to be created for this to try to do something
97 // The index has to be created for this to try to do something
@@ -103,6 +109,7 @@ Q_SIGNALS:
103 void forceNoClipChanged();
109 void forceNoClipChanged();
104 void stickyHeaderHeightChanged();
110 void stickyHeaderHeightChanged();
105 void headerItemShownHeightChanged();
111 void headerItemShownHeightChanged();
112+ void cacheBufferChanged();
106
113
107protected:
114protected:
108 void componentComplete() override;
115 void componentComplete() override;
@@ -197,6 +204,7 @@ private:
197 bool m_forceNoClip;
204 bool m_forceNoClip;
198 bool m_inLayout;
205 bool m_inLayout;
199 bool m_inContentHeightKeepHeaderShown;
206 bool m_inContentHeightKeepHeaderShown;
207+ qreal m_cacheBuffer;
200
208
201 // Qt 5.0 doesn't like releasing the items just after itemCreated
209 // Qt 5.0 doesn't like releasing the items just after itemCreated
202 // so we delay the releasing until the next updatePolish
210 // so we delay the releasing until the next updatePolish
qml/Dash/CardGrid.qml+1
@@ -55,6 +55,7 @@ DashRenderer {
55 delegate: Item {
55 delegate: Item {
56 width: grid.cellWidth
56 width: grid.cellWidth
57 height: grid.cellHeight
57 height: grid.cellHeight
58+ visible: y + height >= root.visibleRangeBegin && y <= root.visibleRangeEnd
58 Loader {
59 Loader {
59 id: loader
60 id: loader
60 sourceComponent: cardTool.cardComponent
61 sourceComponent: cardTool.cardComponent
qml/Dash/CardTool.qml+8−5
@@ -174,16 +174,19 @@ Item {
174
174
175 Item {
175 Item {
176 id: attributesModel
176 id: attributesModel
177− property int numOfAttributes: {
177+ property int numOfAttributes: 0
178+ property var model: []
179+ property bool hasAttributes: {
178 var attributes = components["attributes"];
180 var attributes = components["attributes"];
179− if ((attributes != undefined) && attributes["field"]) {
181+ var hasAttributesFlag = (attributes != undefined) && attributes["field"];
182+
183+ if (hasAttributesFlag) {
180 if (attributes["max-count"]) {
184 if (attributes["max-count"]) {
181− return attributes["max-count"];
185+ numOfAttributes = attributes["max-count"];
182 }
186 }
183 }
187 }
184− return 0;
188+ return hasAttributesFlag
185 }
189 }
186− property var model: []
187
190
188 onNumOfAttributesChanged: {
191 onNumOfAttributesChanged: {
189 model = []
192 model = []
qml/Dash/CardVerticalJournal.qml+1
@@ -49,6 +49,7 @@ DashRenderer {
49 id: loader
49 id: loader
50 sourceComponent: cardTool.cardComponent
50 sourceComponent: cardTool.cardComponent
51 width: cardTool.cardWidth
51 width: cardTool.cardWidth
52+ visible: y + height >= root.visibleRangeBegin && y <= root.visibleRangeEnd
52 onLoaded: {
53 onLoaded: {
53 item.objectName = "delegate" + index;
54 item.objectName = "delegate" + index;
54 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
55 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
qml/Dash/DashContent.qml+1
@@ -177,6 +177,7 @@ Item {
177 dashContent.scopeLoaded(item.scope.id)
177 dashContent.scopeLoaded(item.scope.id)
178 item.paginationCount = Qt.binding(function() { return dashContentList.count } )
178 item.paginationCount = Qt.binding(function() { return dashContentList.count } )
179 item.paginationIndex = Qt.binding(function() { return dashContentList.currentIndex } )
179 item.paginationIndex = Qt.binding(function() { return dashContentList.currentIndex } )
180+ item.holdingList = dashContentList;
180 }
181 }
181 Connections {
182 Connections {
182 target: isCurrent ? scope : null
183 target: isCurrent ? scope : null
qml/Dash/DashRenderer.qml+4
@@ -27,6 +27,10 @@ Item {
27
27
28 property int displayMarginEnd: 0
28 property int displayMarginEnd: 0
29
29
30+ property int visibleRangeBegin: 0
31+
32+ property int visibleRangeEnd: 0
33+
30 property real originY: 0
34 property real originY: 0
31
35
32 // The model to renderer
36 // The model to renderer
qml/Dash/GenericScopeView.qml+109−20
@@ -38,6 +38,7 @@ FocusScope {
38 property int paginationCount: 0
38 property int paginationCount: 0
39 property int paginationIndex: 0
39 property int paginationIndex: 0
40 property alias pageHeaderTotallyVisible: categoryView.pageHeaderTotallyVisible
40 property alias pageHeaderTotallyVisible: categoryView.pageHeaderTotallyVisible
41+ property var holdingList: null
41
42
42 property var scopeStyle: ScopeStyle {
43 property var scopeStyle: ScopeStyle {
43 style: scope ? scope.customizations : {}
44 style: scope ? scope.customizations : {}
@@ -47,6 +48,10 @@ FocusScope {
47
48
48 signal backClicked()
49 signal backClicked()
49
50
51+ onScopeChanged: {
52+ floatingSeeLess.companionBase = null;
53+ }
54+
50 function positionAtBeginning() {
55 function positionAtBeginning() {
51 categoryView.positionAtBeginning()
56 categoryView.positionAtBeginning()
52 }
57 }
@@ -152,11 +157,55 @@ FocusScope {
152 forceNoClip: subPageLoader.open
157 forceNoClip: subPageLoader.open
153 pixelAligned: true
158 pixelAligned: true
154
159
155− property Item expandedCategoryItem: null
160+ property string expandedCategoryId: ""
161+ property int runMaximizeAfterSizeChanges: 0
156
162
157 readonly property bool pageHeaderTotallyVisible: scopeView.showPageHeader &&
163 readonly property bool pageHeaderTotallyVisible: scopeView.showPageHeader &&
158 ((headerItemShownHeight == 0 && categoryView.contentY <= categoryView.originY) || (headerItemShownHeight == pageHeaderLoader.item.height))
164 ((headerItemShownHeight == 0 && categoryView.contentY <= categoryView.originY) || (headerItemShownHeight == pageHeaderLoader.item.height))
159
165
166+ onExpandedCategoryIdChanged: {
167+ var firstCreated = firstCreatedIndex();
168+ var shrinkingAny = false;
169+ var shrinkHeightDifference = 0;
170+ for (var i = 0; i < createdItemCount(); ++i) {
171+ var baseItem = item(firstCreated + i);
172+ if (baseItem.expandable) {
173+ var shouldExpand = baseItem.category === expandedCategoryId;
174+ if (shouldExpand != baseItem.expanded) {
175+
176+ var animate = false;
177+ if (!subPageLoader.open) {
178+ var animateShrinking = !shouldExpand && baseItem.y + baseItem.item.collapsedHeight + baseItem.seeAll.height < categoryView.height;
179+ var animateGrowing = shouldExpand && baseItem.y + baseItem.height < categoryView.height;
180+ animate = shrinkingAny || animateShrinking || animateGrowing;
181+ }
182+
183+ if (!shouldExpand) {
184+ shrinkingAny = true;
185+ shrinkHeightDifference = baseItem.item.expandedHeight - baseItem.item.collapsedHeight;
186+ }
187+
188+ if (shouldExpand && !subPageLoader.open) {
189+ if (!shrinkingAny) {
190+ categoryView.maximizeVisibleArea(firstCreated + i, baseItem.item.expandedHeight + baseItem.seeAll.height);
191+ } else {
192+ // If the space that shrkinking is smaller than the one we need to grow we'll call maximizeVisibleArea
193+ // after the shrink/grow animation ends
194+ var growHeightDifference = baseItem.item.expandedHeight - baseItem.item.collapsedHeight;
195+ if (growHeightDifference > shrinkHeightDifference) {
196+ runMaximizeAfterSizeChanges = 2;
197+ } else {
198+ runMaximizeAfterSizeChanges = 0;
199+ }
200+ }
201+ }
202+
203+ baseItem.expand(shouldExpand, animate);
204+ }
205+ }
206+ }
207+ }
208+
160 delegate: ListItems.Base {
209 delegate: ListItems.Base {
161 id: baseItem
210 id: baseItem
162 objectName: "dashCategory" + category
211 objectName: "dashCategory" + category
@@ -175,6 +224,7 @@ FocusScope {
175 readonly property string category: categoryId
224 readonly property string category: categoryId
176 readonly property string headerLink: model.headerLink
225 readonly property string headerLink: model.headerLink
177 readonly property var item: rendererLoader.item
226 readonly property var item: rendererLoader.item
227+ readonly property var seeAll: seeAll
178
228
179 function expand(expand, animate) {
229 function expand(expand, animate) {
180 heightBehaviour.enabled = animate;
230 heightBehaviour.enabled = animate;
@@ -194,13 +244,13 @@ FocusScope {
194 // This can happen with the VJ that doesn't know how height it will be on creation
244 // This can happen with the VJ that doesn't know how height it will be on creation
195 // so doesn't set expandable until a bit too late for onLoaded
245 // so doesn't set expandable until a bit too late for onLoaded
196 if (expandable) {
246 if (expandable) {
197− var shouldExpand = baseItem === categoryView.expandedCategoryItem;
247+ var shouldExpand = baseItem.category === categoryView.expandedCategoryId;
198 baseItem.expand(shouldExpand, false /*animate*/);
248 baseItem.expand(shouldExpand, false /*animate*/);
199 }
249 }
200 }
250 }
201
251
202− onHeightChanged: rendererLoader.updateDelegateCreationRange();
252+ onHeightChanged: rendererLoader.updateRanges();
203− onYChanged: rendererLoader.updateDelegateCreationRange();
253+ onYChanged: rendererLoader.updateRanges();
204
254
205 Loader {
255 Loader {
206 id: rendererLoader
256 id: rendererLoader
@@ -215,9 +265,23 @@ FocusScope {
215 id: heightBehaviour
265 id: heightBehaviour
216 enabled: false
266 enabled: false
217 animation: UbuntuNumberAnimation {
267 animation: UbuntuNumberAnimation {
268+ duration: UbuntuAnimation.FastDuration
218 onRunningChanged: {
269 onRunningChanged: {
219 if (!running) {
270 if (!running) {
220 heightBehaviour.enabled = false
271 heightBehaviour.enabled = false
272+ if (categoryView.runMaximizeAfterSizeChanges > 0) {
273+ categoryView.runMaximizeAfterSizeChanges--;
274+ if (categoryView.runMaximizeAfterSizeChanges == 0) {
275+ var firstCreated = categoryView.firstCreatedIndex();
276+ for (var i = 0; i < categoryView.createdItemCount(); ++i) {
277+ var baseItem = categoryView.item(firstCreated + i);
278+ if (baseItem.category === categoryView.expandedCategoryId) {
279+ categoryView.maximizeVisibleArea(firstCreated + i, baseItem.item.expandedHeight + baseItem.seeAll.height);
280+ break;
281+ }
282+ }
283+ }
284+ }
221 }
285 }
222 }
286 }
223 }
287 }
@@ -245,10 +309,10 @@ FocusScope {
245 item.objectName = Qt.binding(function() { return categoryId })
309 item.objectName = Qt.binding(function() { return categoryId })
246 item.scopeStyle = scopeView.scopeStyle;
310 item.scopeStyle = scopeView.scopeStyle;
247 if (baseItem.expandable) {
311 if (baseItem.expandable) {
248− var shouldExpand = baseItem === categoryView.expandedCategoryItem;
312+ var shouldExpand = baseItem.category === categoryView.expandedCategoryId;
249 baseItem.expand(shouldExpand, false /*animate*/);
313 baseItem.expand(shouldExpand, false /*animate*/);
250 }
314 }
251− updateDelegateCreationRange();
315+ updateRanges();
252 if (scope && scope.id === "clickscope" && (categoryId === "predefined" || categoryId === "local")) {
316 if (scope && scope.id === "clickscope" && (categoryId === "predefined" || categoryId === "local")) {
253 // Yeah, hackish :/
317 // Yeah, hackish :/
254 cardTool.artShapeSize = Qt.size(units.gu(8), units.gu(7.5));
318 cardTool.artShapeSize = Qt.size(units.gu(8), units.gu(7.5));
@@ -303,13 +367,25 @@ FocusScope {
303 }
367 }
304 }
368 }
305 }
369 }
306− onOriginYChanged: rendererLoader.updateDelegateCreationRange();
370+ onOriginYChanged: rendererLoader.updateRanges();
307− onContentYChanged: rendererLoader.updateDelegateCreationRange();
371+ onContentYChanged: rendererLoader.updateRanges();
308− onHeightChanged: rendererLoader.updateDelegateCreationRange();
372+ onHeightChanged: rendererLoader.updateRanges();
309− onContentHeightChanged: rendererLoader.updateDelegateCreationRange();
373+ onContentHeightChanged: rendererLoader.updateRanges();
374+ }
375+ Connections {
376+ target: scopeView
377+ onIsCurrentChanged: rendererLoader.updateRanges();
378+ }
379+ Connections {
380+ target: holdingList
381+ onMovingChanged: if (!moving) rendererLoader.updateRanges();
310 }
382 }
311
383
312− function updateDelegateCreationRange() {
384+ function updateRanges() {
385+ if (holdingList && holdingList.moving) {
386+ return;
387+ }
388+
313 if (categoryView.moving) {
389 if (categoryView.moving) {
314 // Do not update the range if we are overshooting up or down, since we'll come back
390 // Do not update the range if we are overshooting up or down, since we'll come back
315 // to the stable position and delete/create items without any reason
391 // to the stable position and delete/create items without any reason
@@ -321,10 +397,18 @@ FocusScope {
321 }
397 }
322 }
398 }
323
399
400+ if (item && item.hasOwnProperty("visibleRangeBegin")) {
401+ item.visibleRangeBegin = Math.max(-baseItem.y, 0)
402+ item.visibleRangeEnd = item.visibleRangeBegin + Math.min(categoryView.height, rendererLoader.height)
403+ }
404+
324 if (item && item.hasOwnProperty("displayMarginBeginning")) {
405 if (item && item.hasOwnProperty("displayMarginBeginning")) {
325 // TODO do we need item.originY here, test 1300302 once we have a silo
406 // TODO do we need item.originY here, test 1300302 once we have a silo
326 // and we can run it on the phone
407 // and we can run it on the phone
408+ if (scopeView.isCurrent) {
409+ item.displayMarginBeginning = 1073741823;
410+ item.displayMarginEnd = 1073741823;
327− if (baseItem.y + baseItem.height <= 0) {
411+ } else if (baseItem.y + baseItem.height <= 0) {
328 // Not visible (item at top of the list viewport)
412 // Not visible (item at top of the list viewport)
329 item.displayMarginBeginning = -baseItem.height;
413 item.displayMarginBeginning = -baseItem.height;
330 item.displayMarginEnd = 0;
414 item.displayMarginEnd = 0;
@@ -333,9 +417,9 @@ FocusScope {
333 item.displayMarginBeginning = 0;
417 item.displayMarginBeginning = 0;
334 item.displayMarginEnd = -baseItem.height;
418 item.displayMarginEnd = -baseItem.height;
335 } else {
419 } else {
336− item.displayMarginBeginning = -Math.max(-baseItem.y, 0);
420+ item.displayMarginBeginning = Math.round(-Math.max(-baseItem.y, 0));
337− item.displayMarginEnd = -Math.max(baseItem.height - seeAll.height
421+ item.displayMarginEnd = -Math.round(Math.max(baseItem.height - seeAll.height
338− - categoryView.height + baseItem.y, 0)
422+ - categoryView.height + baseItem.y, 0));
339 }
423 }
340 }
424 }
341 }
425 }
@@ -352,10 +436,11 @@ FocusScope {
352 height: seeAllLabel.visible ? seeAllLabel.font.pixelSize + units.gu(4) : 0
436 height: seeAllLabel.visible ? seeAllLabel.font.pixelSize + units.gu(4) : 0
353
437
354 onClicked: {
438 onClicked: {
355− if (categoryView.expandedCategoryItem !== baseItem) {
439+ if (categoryView.expandedCategoryId !== baseItem.category) {
356− categoryView.expandedCategoryItem = baseItem;
440+ categoryView.expandedCategoryId = baseItem.category;
441+ floatingSeeLess.companionBase = baseItem;
357 } else {
442 } else {
358− categoryView.expandedCategoryItem = null;
443+ categoryView.expandedCategoryId = "";
359 }
444 }
360 }
445 }
361
446
@@ -455,7 +540,7 @@ FocusScope {
455 objectName: "floatingSeeLess"
540 objectName: "floatingSeeLess"
456
541
457 property Item companionTo: companionBase ? companionBase.seeAllButton : null
542 property Item companionTo: companionBase ? companionBase.seeAllButton : null
458− property Item companionBase: categoryView.expandedCategoryItem
543+ property Item companionBase: null
459 property bool showBecausePosition: false
544 property bool showBecausePosition: false
460 property real yOffset: 0
545 property real yOffset: 0
461
546
@@ -467,7 +552,7 @@ FocusScope {
467 height: seeLessLabel.font.pixelSize + units.gu(4)
552 height: seeLessLabel.font.pixelSize + units.gu(4)
468 visible: companionTo && showBecausePosition
553 visible: companionTo && showBecausePosition
469
554
470− onClicked: categoryView.expandedCategoryItem = null;
555+ onClicked: categoryView.expandedCategoryId = "";
471
556
472 function updateVisibility() {
557 function updateVisibility() {
473 var companionPos = companionTo.mapToItem(floatingSeeLess, 0, 0);
558 var companionPos = companionTo.mapToItem(floatingSeeLess, 0, 0);
@@ -476,6 +561,10 @@ FocusScope {
476 var posToBase = floatingSeeLess.mapToItem(companionBase, 0, -yOffset).y;
561 var posToBase = floatingSeeLess.mapToItem(companionBase, 0, -yOffset).y;
477 yOffset = Math.max(0, companionBase.item.collapsedHeight - posToBase);
562 yOffset = Math.max(0, companionBase.item.collapsedHeight - posToBase);
478 yOffset = Math.min(yOffset, height);
563 yOffset = Math.min(yOffset, height);
564+
565+ if (!showBecausePosition && categoryView.expandedCategoryId === "") {
566+ companionBase = null;
567+ }
479 }
568 }
480
569
481 Label {
570 Label {
qml/Dash/ScopeListView.qml+1
@@ -20,4 +20,5 @@ import Dash 0.1
20ListViewWithPageHeader {
20ListViewWithPageHeader {
21 maximumFlickVelocity: height * 10
21 maximumFlickVelocity: height * 10
22 flickDeceleration: height * 2
22 flickDeceleration: height * 2
23+ cacheBuffer: Number.MAX_VALUE
23}
24}
qml/Dash/ScopesOverviewAll.qml+2
@@ -43,6 +43,8 @@ Flickable {
43 id: cardGrid
43 id: cardGrid
44 width: root.width
44 width: root.width
45 height: parent.height
45 height: parent.height
46+ visibleRangeBegin: root.contentY
47+ visibleRangeEnd: root.contentY + root.height
46
48
47 onClicked: {
49 onClicked: {
48 root.clicked(index, result, item, itemModel);
50 root.clicked(index, result, item, itemModel);
tests/autopilot/unity8/shell/emulators/dash.py+2−2
@@ -93,7 +93,7 @@ class Dash(emulators.UnityEmulatorBase):
93 'No scope found with id {0}'.format(scope_id))
93 'No scope found with id {0}'.format(scope_id))
94
94
95 def _get_scope_from_loader(self, loader):
95 def _get_scope_from_loader(self, loader):
96− return loader.get_children()[0]
96+ return loader.wait_select_single('GenericScopeView');
97
97
98 def _open_scope_scrolling(self, scope_loader):
98 def _open_scope_scrolling(self, scope_loader):
99 scroll = self._get_scroll_direction(scope_loader)
99 scroll = self._get_scroll_direction(scope_loader)
@@ -102,8 +102,8 @@ class Dash(emulators.UnityEmulatorBase):
102 scroll()
102 scroll()
103 self.dash_content_list.moving.wait_for(False)
103 self.dash_content_list.moving.wait_for(False)
104
104
105+ scope_loader.isCurrent.wait_for(True)
105 scope = self._get_scope_from_loader(scope_loader)
106 scope = self._get_scope_from_loader(scope_loader)
106− scope.isCurrent.wait_for(True)
107 return scope
107 return scope
108
108
109 def _get_scroll_direction(self, scope_loader):
109 def _get_scroll_direction(self, scope_loader):
tests/mocks/Ubuntu/CMakeLists.txt+1
@@ -1,3 +1,4 @@
1+add_subdirectory(Connectivity)
1add_subdirectory(DownloadDaemonListener)
2add_subdirectory(DownloadDaemonListener)
2add_subdirectory(Payments)
3add_subdirectory(Payments)
3add_subdirectory(SystemImage)
4add_subdirectory(SystemImage)
tests/mocks/Ubuntu/Connectivity/CMakeLists.txtnew file+10
@@ -0,0 +1,10 @@
1+add_library(connectivity-qml SHARED
2+ networking-status.cpp
3+ plugin.cpp
4+)
5+qt5_use_modules(connectivity-qml Qml)
6+
7+# copy qmldir file into build directory for shadow builds
8+file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/qmldir"
9+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
10+ )
tests/mocks/Ubuntu/Connectivity/networking-status.cppnew file+65
@@ -0,0 +1,65 @@
1+/*
2+ * Copyright © 2013 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU Lesser General Public License version 3,
6+ * as published by the Free Software Foundation.
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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Antti Kaijanmäki <antti.kaijanmaki@canonical.com>
18+ */
19+
20+#include "networking-status.h"
21+
22+NetworkingStatus::NetworkingStatus(QObject *parent)
23+ : QObject(parent)
24+{
25+
26+ qRegisterMetaType<NetworkingStatus::Limitations>();
27+ qRegisterMetaType<QVector<NetworkingStatus::Limitations>>();
28+ qRegisterMetaType<NetworkingStatus::Status>();
29+}
30+
31+NetworkingStatus::~NetworkingStatus()
32+{}
33+
34+QVector<NetworkingStatus::Limitations>
35+NetworkingStatus::limitations() const
36+{
37+ return m_limitations;
38+}
39+
40+NetworkingStatus::Status
41+NetworkingStatus::status() const
42+{
43+ return Status::Online;
44+}
45+
46+bool
47+NetworkingStatus::online() const
48+{
49+ return status() == Status::Online;
50+}
51+
52+bool
53+NetworkingStatus::limitedBandwith() const
54+{
55+ return limitations().contains(Limitations::Bandwith);
56+}
57+
58+void NetworkingStatus::setLimitedBandwidth(bool limited)
59+{
60+ if (limited) {
61+ m_limitations << Limitations::Bandwith;
62+ } else if (limitedBandwith()) {
63+ m_limitations.remove(m_limitations.indexOf(Limitations::Bandwith));
64+ }
65+}
tests/mocks/Ubuntu/Connectivity/networking-status.hnew file+73
@@ -0,0 +1,73 @@
1+/*
2+ * Copyright © 2014 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU Lesser General Public License version 3,
6+ * as published by the Free Software Foundation.
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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ */
17+
18+#ifndef FAKE_NETWORKING_STATUS_H
19+#define FAKE_NETWORKING_STATUS_H
20+
21+#include <QObject>
22+#include <QVector>
23+
24+class Q_DECL_EXPORT NetworkingStatus : public QObject
25+{
26+ Q_OBJECT
27+ Q_DISABLE_COPY(NetworkingStatus)
28+
29+ Q_ENUMS(Limitations)
30+ Q_ENUMS(Status)
31+
32+ Q_PROPERTY(QVector<Limitations> limitations READ limitations NOTIFY limitationsChanged)
33+ Q_PROPERTY(Status status READ status NOTIFY statusChanged)
34+ Q_PROPERTY(bool online READ online NOTIFY onlineChanged)
35+ Q_PROPERTY(bool limitedBandwith READ limitedBandwith WRITE setLimitedBandwidth NOTIFY limitedBandwithChanged)
36+
37+public:
38+ explicit NetworkingStatus(QObject *parent = 0);
39+ virtual ~NetworkingStatus();
40+
41+ enum Limitations {
42+ Bandwith
43+ };
44+
45+ enum Status {
46+ Offline,
47+ Connecting,
48+ Online
49+ };
50+
51+ QVector<Limitations> limitations() const;
52+ Status status() const;
53+ bool online() const;
54+ bool limitedBandwith() const;
55+
56+ // Only in the fake one
57+ void setLimitedBandwidth(bool limited);
58+
59+Q_SIGNALS:
60+ void limitationsChanged();
61+ void statusChanged(Status value);
62+ void onlineChanged(bool value);
63+ void limitedBandwithChanged(bool value);
64+
65+private:
66+ QVector<NetworkingStatus::Limitations> m_limitations;
67+};
68+
69+Q_DECLARE_METATYPE(NetworkingStatus::Limitations)
70+Q_DECLARE_METATYPE(QVector<NetworkingStatus::Limitations>)
71+Q_DECLARE_METATYPE(NetworkingStatus::Status)
72+
73+#endif // FAKE_NETWORKING_STATUS_H
tests/mocks/Ubuntu/Connectivity/plugin.cppnew file+46
@@ -0,0 +1,46 @@
1+/*
2+ * Copyright © 2014 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU Lesser General Public License version 3,
6+ * as published by the Free Software Foundation.
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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Antti Kaijanmäki <antti.kaijanmaki@canonical.com>
18+ */
19+
20+#include "plugin.h"
21+
22+#include <QtQml>
23+
24+#include "networking-status.h"
25+
26+static QObject *
27+networkingStatusSingletonProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
28+{
29+ Q_UNUSED(scriptEngine)
30+
31+ return new NetworkingStatus(engine);
32+}
33+
34+void
35+QmlConnectivityNetworkingPlugin::registerTypes(const char *uri)
36+{
37+ // @uri Ubuntu.Connectivity
38+ qmlRegisterSingletonType<NetworkingStatus>(uri, 1, 0, "NetworkingStatus", networkingStatusSingletonProvider);
39+}
40+
41+void
42+QmlConnectivityNetworkingPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
43+{
44+ Q_UNUSED(uri);
45+ Q_UNUSED(engine);
46+}
tests/mocks/Ubuntu/Connectivity/plugin.hnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright © 2014 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU Lesser General Public License version 3,
6+ * as published by the Free Software Foundation.
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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Antti Kaijanmäki <antti.kaijanmaki@canonical.com>
18+ */
19+
20+#ifndef QML_CONNECTIVITY_PLUGIN_H
21+#define QML_CONNECTIVITY_PLUGIN_H
22+
23+#include <QQmlExtensionPlugin>
24+
25+class QmlConnectivityNetworkingPlugin : public QQmlExtensionPlugin {
26+ Q_OBJECT
27+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28+ Q_INTERFACES(QQmlExtensionInterface)
29+
30+public:
31+ void registerTypes(const char *uri);
32+ void initializeEngine(QQmlEngine *engine, const char *uri);
33+};
34+
35+#endif // QML_CONNECTIVITY_PLUGIN_H
tests/mocks/Ubuntu/Connectivity/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module Ubuntu.Connectivity
2+plugin connectivity-qml
tests/mocks/Unity/fake_categories.cpp+95−136
@@ -29,25 +29,12 @@ Categories::Categories(int category_count, QObject* parent)
29
29
30int Categories::rowCount(const QModelIndex& /*parent*/) const
30int Categories::rowCount(const QModelIndex& /*parent*/) const
31{
31{
32− return m_category_count + m_specialCategories.count();
32+ return m_category_count;
33}
33}
34
34
35−void Categories::addSpecialCategory(QString const& categoryId, QString const& name, QString const& icon, QString const& rawTemplate, QObject* countObject)
35+void Categories::addSpecialCategory(QString const& /*categoryId*/, QString const& /*name*/, QString const& /*icon*/, QString const& /*rawTemplate*/, QObject* /*countObject*/)
36{
36{
37− CategoryData catData;
37+ qFatal("Using un-implemented Categories::addSpecialCategory");
38− catData.categoryId = categoryId;
39− catData.name = name;
40− catData.icon = icon;
41− catData.rawTemplate = rawTemplate;
42− catData.countObject = countObject;
43−
44− beginInsertRows(QModelIndex(), 0, 0);
45− m_specialCategories.prepend(catData);
46− endInsertRows();
47−
48− if (countObject) {
49− connect(countObject, SIGNAL(countChanged()), this, SLOT(countChanged()));
50− }
51}
38}
52
39
53bool Categories::overrideCategoryJson(QString const& /* categoryId */, QString const& /* json */)
40bool Categories::overrideCategoryJson(QString const& /* categoryId */, QString const& /* json */)
@@ -55,23 +42,6 @@ bool Categories::overrideCategoryJson(QString const& /* categoryId */, QString c
55 qFatal("Using un-implemented Categories::overrideCategoryJson");
42 qFatal("Using un-implemented Categories::overrideCategoryJson");
56}
43}
57
44
58−void Categories::countChanged()
59−{
60− QObject* countObject = sender();
61−
62− for (int i = 0; i < m_specialCategories.count(); ++i) {
63− const CategoryData &catData = m_specialCategories.at(i);
64− if (catData.countObject == countObject) {
65− QVector<int> roles;
66− roles.append(RoleCount);
67−
68− QModelIndex changedIndex(index(i));
69− dataChanged(changedIndex, changedIndex, roles);
70− break;
71− }
72− }
73−}
74−
75QVariant
45QVariant
76Categories::data(const QModelIndex& index, int role) const
46Categories::data(const QModelIndex& index, int role) const
77{
47{
@@ -79,115 +49,74 @@ Categories::data(const QModelIndex& index, int role) const
79 return QVariant();
49 return QVariant();
80 }
50 }
81
51
82− ResultsModel *resultsModel = m_resultsModels[index.row()];
52+ ResultsModel *resultsModel = resultModel(index.row());
83− if (!resultsModel) {
84− resultsModel = new ResultsModel(15, index.row());
85− m_resultsModels[index.row()] = resultsModel;
86− }
87− if (index.row() < m_specialCategories.count()) {
88− const CategoryData &catData = m_specialCategories.at(index.row());
89− switch (role) {
53+ switch (role) {
90− case RoleCategoryId:
54+ case RoleCategoryId:
91− return catData.categoryId;
55+ return QString("%1").arg(index.row());
92− case RoleName:
56+ case RoleName:
93− return catData.name;
57+ return QString("Category %1").arg(index.row());
94− case RoleIcon:
58+ case RoleIcon:
95− return catData.icon;
59+ return "gtk-apply";
96− case RoleRawRendererTemplate:
60+ case RoleRawRendererTemplate:
97− return catData.rawTemplate;
61+ qFatal("Using un-implemented RoleRawRendererTemplate Categories role");
62+ return QVariant();
98− case RoleRenderer:
63+ case RoleRenderer:
99− {
64+ {
100− QVariantMap map;
65+ QVariantMap map;
101− if (index.row() % 2 == 0) {
66+ if (index.row() % 2 == 0) {
102− map["category-layout"] = "grid";
67+ map["category-layout"] = "grid";
103− } else {
68+ } else {
104− map["category-layout"] = "carousel";
69+ map["category-layout"] = "carousel";
105− map["overlay"] = true;
106− }
107− map["card-size"] = "small";
108− return map;
109 }
70 }
110− case RoleComponents:
71+ if (index.row() == 18) {
111− {
72+ map["category-layout"] = "horizontal-list";
112− QVariantMap map, artMap, attributeMap;
73+ }
113− artMap["aspect-ratio"] = "1.0";
74+ if (index.row() == 19) {
114− artMap["field"] = "art";
75+ map["category-layout"] = "grid";
115− map["art"] = artMap;
76+ map["collapsed-rows"] = 0;
116− map["title"] = "HOLA";
77+ }
117− attributeMap["field"] = "attribute";
78+ map["card-size"] = "small";
118− map["attributes"] = attributeMap;
79+
119− return map;
80+ map["category-layout"] = m_layouts.value(index.row(), map["category-layout"].toString());
81+
82+ if (map["category-layout"] == "carousel") {
83+ map["overlay"] = true;
120 }
84 }
121− case RoleHeaderLink:
85+
122− return QString();
123− case RoleResults:
124− return QVariant();
86+ return map;
125− case RoleCount:
126− return catData.countObject->property("count");
127− default:
128− qFatal("Using un-implemented Categories role");
129− return QVariant();
130 }
87 }
131− } else {
88+ case RoleComponents:
132− switch (role) {
133− case RoleCategoryId:
134− return QString("%1").arg(index.row());
135− case RoleName:
136− return QString("Category %1").arg(index.row());
137− case RoleIcon:
138− return "gtk-apply";
139− case RoleRawRendererTemplate:
140− qFatal("Using un-implemented RoleRawRendererTemplate Categories role");
141− return QVariant();
142− case RoleRenderer:
143− {
89+ {
144− QVariantMap map;
90+ QVariantMap map, artMap, attributeMap;
145− if (index.row() % 2 == 0) {
91+ if (index.row() % 2 != 0) {
146− map["category-layout"] = "grid";
92+ artMap["aspect-ratio"] = QString("1.%1").arg(index.row());
147− } else {
93+ } else {
148− map["category-layout"] = "carousel";
94+ artMap["aspect-ratio"] = "1.0";
149− map["card-size"] = "medium";
150− map["overlay"] = true;
151− }
152− if (index.row() == 18) {
153− map["category-layout"] = "horizontal-list";
154− }
155− if (index.row() == 19) {
156− map["category-layout"] = "grid";
157− map["collapsed-rows"] = 0;
158− }
159− map["card-size"] = "small";
160− return map;
161 }
95 }
162− case RoleComponents:
163− {
164− QVariantMap map, artMap, attributeMap;
165− if (index.row() % 2 != 0) {
166− artMap["aspect-ratio"] = QString("1.%1").arg(index.row());
167− } else {
168− artMap["aspect-ratio"] = "1.0";
169− }
170− artMap["field"] = "art";
96+ artMap["field"] = "art";
171− map["art"] = artMap;
97+ map["art"] = artMap;
172− map["title"] = "HOLA";
98+ map["title"] = "HOLA";
173− map["subtitle"] = "HOLA";
99+ map["subtitle"] = "HOLA";
174− attributeMap["field"] = "attribute";
100+ attributeMap["field"] = "attribute";
175− map["attributes"] = attributeMap;
101+ map["attributes"] = attributeMap;
176− return map;
102+ return map;
103+ }
104+ case RoleHeaderLink:
105+ {
106+ QString res;
107+ if (index.row() == 1 || index.row() == 4) {
108+ res = QString("scope://query/1");
177 }
109 }
178− case RoleHeaderLink:
110+ res = m_headerLinks.value(index.row(), res);
179− if (index.row() == 1 || index.row() == 4) {
180− return QString("scope://query/1");
181− }
182− return QString();
183− case RoleResults:
184− return QVariant::fromValue(resultsModel);
185− case RoleCount:
186− return resultsModel->rowCount();
187− default:
188− qFatal("Using un-implemented Categories role");
189− return QVariant();
111+ return res;
190 }
112 }
113+ case RoleResults:
114+ return QVariant::fromValue(resultsModel);
115+ case RoleCount:
116+ return resultsModel->rowCount();
117+ default:
118+ qFatal("Using un-implemented Categories role");
119+ return QVariant();
191 }
120 }
192}
121}
193
122
@@ -196,3 +125,33 @@ Categories::data(int row, int role) const
196{
125{
197 return data(index(row, 0), role);
126 return data(index(row, 0), role);
198}
127}
128+
129+void Categories::setCount(int count)
130+{
131+ if (m_category_count != count) {
132+ beginResetModel(); // This is just for test setup so we can be lazy and reset
133+ m_category_count = count;
134+ endResetModel();
135+ }
136+}
137+
138+ResultsModel* Categories::resultModel(int row) const
139+{
140+ ResultsModel *result = m_resultsModels[row];
141+ if (!result) {
142+ Categories *that = const_cast<Categories*>(this);
143+ result = new ResultsModel(15, row, that);
144+ m_resultsModels[row] = result;
145+ }
146+ return result;
147+}
148+
149+void Categories::setLayout(int row, const QString &layout)
150+{
151+ m_layouts[row] = layout;
152+}
153+
154+void Categories::setHeaderLink(int row, const QString &headerLink)
155+{
156+ m_headerLinks[row] = headerLink;
157+}
tests/mocks/Unity/fake_categories.h+7−12
@@ -39,22 +39,17 @@ public:
39
39
40 Q_INVOKABLE QVariant data(int row, int role) const;
40 Q_INVOKABLE QVariant data(int row, int role) const;
41
41
42−private Q_SLOTS:
42+ // For testing purposes
43− void countChanged();
43+ Q_INVOKABLE void setCount(int count);
44+ Q_INVOKABLE ResultsModel* resultModel(int row) const;
45+ Q_INVOKABLE void setLayout(int row, const QString &layout);
46+ Q_INVOKABLE void setHeaderLink(int row, const QString &headerLink);
44
47
45private:
48private:
46 mutable QHash<int, ResultsModel*> m_resultsModels;
49 mutable QHash<int, ResultsModel*> m_resultsModels;
50+ QHash<int, QString> m_layouts;
51+ QHash<int, QString> m_headerLinks;
47 int m_category_count;
52 int m_category_count;
48−
49− struct CategoryData {
50− QString categoryId;
51− QString name;
52− QString icon;
53− QString rawTemplate;
54− QObject* countObject;
55− };
56−
57− QList<CategoryData> m_specialCategories;
58};
53};
59
54
60#endif // FAKE_CATEGORIES_H
55#endif // FAKE_CATEGORIES_H
tests/mocks/Unity/fake_resultsmodel.cpp+9
@@ -44,6 +44,15 @@ void ResultsModel::setCategoryId(QString const& /*id*/)
44 qFatal("Calling un-implemented ResultsModel::setCategoryId");
44 qFatal("Calling un-implemented ResultsModel::setCategoryId");
45}
45}
46
46
47+void ResultsModel::setResultCount(int result_count)
48+{
49+ if (m_result_count != result_count) {
50+ beginResetModel(); // This is just for test setup so we can be lazy and reset
51+ m_result_count = result_count;
52+ endResetModel();
53+ }
54+}
55+
47int ResultsModel::rowCount(const QModelIndex& parent) const
56int ResultsModel::rowCount(const QModelIndex& parent) const
48{
57{
49 Q_UNUSED(parent);
58 Q_UNUSED(parent);
tests/mocks/Unity/fake_resultsmodel.h+3
@@ -41,6 +41,9 @@ public:
41 /* setters */
41 /* setters */
42 void setCategoryId(QString const& id) override;
42 void setCategoryId(QString const& id) override;
43
43
44+ // For testing purposes
45+ Q_INVOKABLE void setResultCount(int result_count);
46+
44private:
47private:
45 int m_result_count;
48 int m_result_count;
46 int m_categoryId;
49 int m_categoryId;
tests/mocks/Unity/fake_scope.cpp+17
@@ -145,6 +145,23 @@ void Scope::setFavorite(const bool favorite)
145 Q_EMIT favoriteChanged();
145 Q_EMIT favoriteChanged();
146 }
146 }
147}
147}
148+
149+void Scope::setId(const QString &id)
150+{
151+ if (id != m_id) {
152+ m_id = id;
153+ Q_EMIT idChanged();
154+ }
155+}
156+
157+void Scope::setName(const QString &name)
158+{
159+ if (name != m_name) {
160+ m_name = name;
161+ Q_EMIT nameChanged();
162+ }
163+}
164+
148void Scope::setSearchInProgress(const bool inProg)
165void Scope::setSearchInProgress(const bool inProg)
149{
166{
150 if (inProg != m_searching) {
167 if (inProg != m_searching) {
tests/mocks/Unity/fake_scope.h+2
@@ -56,6 +56,8 @@ public:
56 void setFormFactor(const QString& form_factor) override;
56 void setFormFactor(const QString& form_factor) override;
57 void setActive(const bool) override;
57 void setActive(const bool) override;
58 void setFavorite(const bool) override;
58 void setFavorite(const bool) override;
59+ Q_INVOKABLE void setId(const QString &id); // This is not invokable in the Interface, here for testing benefits
60+ Q_INVOKABLE void setName(const QString &name); // This is not invokable in the Interface, here for testing benefits
59 Q_INVOKABLE void setSearchInProgress(const bool inProg); // This is not invokable in the Interface, here for testing benefits
61 Q_INVOKABLE void setSearchInProgress(const bool inProg); // This is not invokable in the Interface, here for testing benefits
60
62
61 Q_INVOKABLE void activate(QVariant const& result) override;
63 Q_INVOKABLE void activate(QVariant const& result) override;
tests/plugins/Dash/CMakeLists.txt+3−3
@@ -32,7 +32,7 @@ macro(add_lvwph_test FILENAME TESTNAME)
32 qt5_use_modules(${TESTNAME}TestExec Test Core Qml)
32 qt5_use_modules(${TESTNAME}TestExec Test Core Qml)
33 target_link_libraries(${TESTNAME}TestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
33 target_link_libraries(${TESTNAME}TestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
34
34
35− add_binary_qml_test(${TESTNAME} "" "Dash-qml")
35+ add_binary_qml_test(${TESTNAME} "" "Dash-qml" "")
36endmacro()
36endmacro()
37
37
38add_lvwph_test(listviewwithpageheader ListViewWithPageHeader)
38add_lvwph_test(listviewwithpageheader ListViewWithPageHeader)
@@ -48,7 +48,7 @@ macro(add_dashview_try_test FILENAME TESTNAME)
48 qt5_use_modules(${TESTNAME}TestExec Test Core Qml)
48 qt5_use_modules(${TESTNAME}TestExec Test Core Qml)
49 target_link_libraries(${TESTNAME}TestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
49 target_link_libraries(${TESTNAME}TestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
50
50
51− add_binary_qml_test(${TESTNAME} "" "Dash-qml")
51+ add_binary_qml_test(${TESTNAME} "" "Dash-qml" "")
52
52
53 add_executable(${TESTNAME}tryExec
53 add_executable(${TESTNAME}tryExec
54 ${FILENAME}try.cpp
54 ${FILENAME}try.cpp
@@ -71,7 +71,7 @@ add_dashview_try_test(organicgrid OrganicGrid)
71add_executable(CardCreatorTestExec cardcreatortest.cpp)
71add_executable(CardCreatorTestExec cardcreatortest.cpp)
72qt5_use_modules(CardCreatorTestExec Test Core Qml)
72qt5_use_modules(CardCreatorTestExec Test Core Qml)
73target_link_libraries(CardCreatorTestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
73target_link_libraries(CardCreatorTestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
74−add_binary_qml_test(CardCreator "" "Dash-qml")
74+add_binary_qml_test(CardCreator "" "Dash-qml" "QML2_IMPORT_PATH=${CMAKE_BINARY_DIR}/tests/mocks/")
75
75
76# plain qml test
76# plain qml test
77set(qmltest_DEFAULT_TARGETS qmluitests)
77set(qmltest_DEFAULT_TARGETS qmluitests)
tests/plugins/Dash/cardcreator/1.res+2−1
@@ -46,7 +46,8 @@ Item {
46 }
46 }
47 image: Image {
47 image: Image {
48 objectName: "artImage";
48 objectName: "artImage";
49+ property bool doLoadSource: !NetworkingStatus.limitedBandwith;
49− source: cardData && cardData["art"] || "";
50+ source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["art"] || ""; }
50 cache: true;
51 cache: true;
51 asynchronous: root.asynchronous;
52 asynchronous: root.asynchronous;
52 fillMode: Image.PreserveAspectCrop;
53 fillMode: Image.PreserveAspectCrop;
tests/plugins/Dash/cardcreator/2.res+2−1
@@ -67,7 +67,8 @@ Image {
67 objectName: "mascotImage";
67 objectName: "mascotImage";
68 anchors { verticalCenter: parent.verticalCenter; }
68 anchors { verticalCenter: parent.verticalCenter; }
69 readonly property int maxSize: Math.max(width, height) * 4;
69 readonly property int maxSize: Math.max(width, height) * 4;
70+ property bool doLoadSource: !NetworkingStatus.limitedBandwith;
70− source: cardData && cardData["mascot"] || "";
71+ source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["mascot"] || ""; }
71 width: units.gu(6);
72 width: units.gu(6);
72 height: units.gu(5.625);
73 height: units.gu(5.625);
73 sourceSize { width: maxSize; height: maxSize }
74 sourceSize { width: maxSize; height: maxSize }
tests/plugins/Dash/cardcreator/3.res+3−2
@@ -46,8 +46,9 @@ Item {
46 }
46 }
47 image: Image {
47 image: Image {
48 objectName: "artImage";
48 objectName: "artImage";
49+ property bool doLoadSource: !NetworkingStatus.limitedBandwith;
49− source: cardData && cardData["art"] || "";
50+ source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["art"] || ""; }
50− cache: true;
51+ cache: true;
51 asynchronous: root.asynchronous;
52 asynchronous: root.asynchronous;
52 fillMode: Image.PreserveAspectCrop;
53 fillMode: Image.PreserveAspectCrop;
53 width: root.width;
54 width: root.width;
tests/plugins/Dash/cardcreator/4.res+3−2
@@ -46,8 +46,9 @@ Loader {
46 objectName: "mascotImage";
46 objectName: "mascotImage";
47 anchors { verticalCenter: parent.verticalCenter; }
47 anchors { verticalCenter: parent.verticalCenter; }
48 readonly property int maxSize: Math.max(width, height) * 4;
48 readonly property int maxSize: Math.max(width, height) * 4;
49+ property bool doLoadSource: !NetworkingStatus.limitedBandwith;
49− source: cardData && cardData["mascot"] || "";
50+ source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["mascot"] || ""; }
50− width: units.gu(6);
51+ width: units.gu(6);
51 height: units.gu(5.625);
52 height: units.gu(5.625);
52 sourceSize { width: maxSize; height: maxSize }
53 sourceSize { width: maxSize; height: maxSize }
53 fillMode: Image.PreserveAspectCrop;
54 fillMode: Image.PreserveAspectCrop;
tests/plugins/Dash/cardcreator/5.res+3−2
@@ -46,8 +46,9 @@ Item {
46 }
46 }
47 image: Image {
47 image: Image {
48 objectName: "artImage";
48 objectName: "artImage";
49+ property bool doLoadSource: !NetworkingStatus.limitedBandwith;
49− source: cardData && cardData["art"] || "";
50+ source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["art"] || ""; }
50− cache: true;
51+ cache: true;
51 asynchronous: root.asynchronous;
52 asynchronous: root.asynchronous;
52 fillMode: Image.PreserveAspectCrop;
53 fillMode: Image.PreserveAspectCrop;
53 width: root.width;
54 width: root.width;
tests/plugins/Dash/cardcreator/7.res+3−2
@@ -67,8 +67,9 @@ Image {
67 objectName: "mascotImage";
67 objectName: "mascotImage";
68 anchors { verticalCenter: parent.verticalCenter; }
68 anchors { verticalCenter: parent.verticalCenter; }
69 readonly property int maxSize: Math.max(width, height) * 4;
69 readonly property int maxSize: Math.max(width, height) * 4;
70+ property bool doLoadSource: !NetworkingStatus.limitedBandwith;
70− source: cardData && cardData["mascot"] || "";
71+ source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["mascot"] || ""; }
71− width: units.gu(6);
72+ width: units.gu(6);
72 height: units.gu(5.625);
73 height: units.gu(5.625);
73 sourceSize { width: maxSize; height: maxSize }
74 sourceSize { width: maxSize; height: maxSize }
74 fillMode: Image.PreserveAspectCrop;
75 fillMode: Image.PreserveAspectCrop;
tests/plugins/Dash/listviewwithpageheadertest.cpp+39
@@ -28,6 +28,8 @@
28#include <private/qquickitem_p.h>
28#include <private/qquickitem_p.h>
29#pragma GCC diagnostic pop
29#pragma GCC diagnostic pop
30
30
31+#include <limits>
32+
31// TODO Think on how doing a test for lost items
33// TODO Think on how doing a test for lost items
32// particullary making sure that lost items are culled
34// particullary making sure that lost items are culled
33// and then removed in the next updatePolish cycle
35// and then removed in the next updatePolish cycle
@@ -1899,6 +1901,43 @@ private Q_SLOTS:
1899 QTRY_COMPARE(lvwph->m_firstVisibleIndex, 0);
1901 QTRY_COMPARE(lvwph->m_firstVisibleIndex, 0);
1900 }
1902 }
1901
1903
1904+ void testNoCacheBuffer()
1905+ {
1906+ lvwph->setCacheBuffer(0);
1907+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 3);
1908+ QCOMPARE(lvwph->m_firstVisibleIndex, 0);
1909+ verifyItem(0, 50., 150., false);
1910+ verifyItem(1, 200., 200., false);
1911+ verifyItem(2, 400., 350., false);
1912+ QCOMPARE(lvwph->m_minYExtent, 0.);
1913+ QCOMPARE(lvwph->m_clipItem->y(), 0.);
1914+ QCOMPARE(lvwph->m_clipItem->clip(), false);
1915+ QCOMPARE(lvwph->m_headerItem->y(), 0.);
1916+ QCOMPARE(lvwph->m_headerItem->height(), 50.);
1917+ QCOMPARE(lvwph->contentY(), 0.);
1918+ QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
1919+ }
1920+
1921+ void testAllCacheBuffer()
1922+ {
1923+ lvwph->setCacheBuffer(std::numeric_limits<qreal>::max());
1924+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 6);
1925+ QCOMPARE(lvwph->m_firstVisibleIndex, 0);
1926+ verifyItem(0, 50., 150., false);
1927+ verifyItem(1, 200., 200., false);
1928+ verifyItem(2, 400., 350., false);
1929+ verifyItem(3, 750., 350., true);
1930+ verifyItem(4, 1100., 350., true);
1931+ verifyItem(5, 1450., 350., true);
1932+ QCOMPARE(lvwph->m_minYExtent, 0.);
1933+ QCOMPARE(lvwph->m_clipItem->y(), 0.);
1934+ QCOMPARE(lvwph->m_clipItem->clip(), false);
1935+ QCOMPARE(lvwph->m_headerItem->y(), 0.);
1936+ QCOMPARE(lvwph->m_headerItem->height(), 50.);
1937+ QCOMPARE(lvwph->contentY(), 0.);
1938+ QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
1939+ }
1940+
1902private:
1941private:
1903 QQuickView *view;
1942 QQuickView *view;
1904 ListViewWithPageHeader *lvwph;
1943 ListViewWithPageHeader *lvwph;
tests/plugins/Dash/listviewwithpageheadertest.qml+1
@@ -60,6 +60,7 @@ Rectangle {
60 anchors.top: parent.top
60 anchors.top: parent.top
61 anchors.bottom: parent.bottom
61 anchors.bottom: parent.bottom
62 model: model
62 model: model
63+ cacheBuffer: height * 0.5
63 delegate: Rectangle {
64 delegate: Rectangle {
64 property bool timerDone: false
65 property bool timerDone: false
65 width: parent.width - 20
66 width: parent.width - 20
tests/plugins/Dash/listviewwithpageheadertestsection.qml+1
@@ -60,6 +60,7 @@ Rectangle {
60 anchors.top: parent.top
60 anchors.top: parent.top
61 anchors.bottom: parent.bottom
61 anchors.bottom: parent.bottom
62 model: model
62 model: model
63+ cacheBuffer: height * 0.5
63 delegate: Rectangle {
64 delegate: Rectangle {
64 property bool timerDone: false
65 property bool timerDone: false
65 width: parent.width - 20
66 width: parent.width - 20
tests/plugins/Dash/listviewwithpageheadertestsectionexternalmodel.qml+1
@@ -28,6 +28,7 @@ Rectangle {
28 anchors.top: parent.top
28 anchors.top: parent.top
29 anchors.bottom: parent.bottom
29 anchors.bottom: parent.bottom
30 model: model
30 model: model
31+ cacheBuffer: height * 0.5
31 delegate: Rectangle {
32 delegate: Rectangle {
32 property bool timerDone: false
33 property bool timerDone: false
33 width: parent.width - 20
34 width: parent.width - 20
tests/plugins/Ubuntu/Gestures/CMakeLists.txt+1−1
@@ -19,7 +19,7 @@ macro(add_gesture_ui_test CLASSNAME)
19 qt5_use_modules(${CLASSNAME}TestExec Test Core Qml Gui Quick)
19 qt5_use_modules(${CLASSNAME}TestExec Test Core Qml Gui Quick)
20 target_link_libraries(${CLASSNAME}TestExec UbuntuGestureQml)
20 target_link_libraries(${CLASSNAME}TestExec UbuntuGestureQml)
21
21
22− add_binary_qml_test(${CLASSNAME} ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures UbuntuGesturesTestQmlFiles)
22+ add_binary_qml_test(${CLASSNAME} ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures UbuntuGesturesTestQmlFiles "")
23 add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
23 add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
24endmacro(add_gesture_ui_test)
24endmacro(add_gesture_ui_test)
25
25
tests/qmltests/Components/CMakeLists.txt+1−1
@@ -10,7 +10,7 @@ add_executable(DragHandleTestExec tst_DragHandle.cpp)
10qt5_use_modules(DragHandleTestExec Test Core Qml Gui Quick)
10qt5_use_modules(DragHandleTestExec Test Core Qml Gui Quick)
11target_link_libraries(DragHandleTestExec UbuntuGestureQml)
11target_link_libraries(DragHandleTestExec UbuntuGestureQml)
12
12
13−add_binary_qml_test(DragHandle ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures "")
13+add_binary_qml_test(DragHandle ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures "" "")
14add_manual_qml_test(. DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
14add_manual_qml_test(. DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
15
15
16add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
16add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
tests/qmltests/Dash/tst_Card.qml+21
@@ -17,6 +17,7 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19import Ubuntu.Components 0.1
19import Ubuntu.Components 0.1
20+import Ubuntu.Connectivity 1.0
20import Unity.Test 0.1 as UT
21import Unity.Test 0.1 as UT
21import "../../../qml/Dash"
22import "../../../qml/Dash"
22import "CardHelpers.js" as Helpers
23import "CardHelpers.js" as Helpers
@@ -566,5 +567,25 @@ Rectangle {
566 verify((card.width - titleToCard.x - titleToCard.width) === units.gu(1));
567 verify((card.width - titleToCard.x - titleToCard.width) === units.gu(1));
567 }
568 }
568 }
569 }
570+
571+ function test_load_images_visibility_network_data() {
572+ return [
573+ { tag: "Visible, network", visible: true, limitedBandwith: false },
574+ { tag: "Visible, no network", visible: true, limitedBandwith: true },
575+ { tag: "Not Visible, network", visible: false, limitedBandwith: false },
576+ { tag: "Not Visible, no network", visible: false, limitedBandwith: true }
577+ ];
578+ }
579+
580+ function test_load_images_visibility_network(data) {
581+ loader.visible = data.visible;
582+ NetworkingStatus.limitedBandwith = data.limitedBandwith;
583+
584+ selector.selectedIndex = 0;
585+ waitForRendering(selector);
586+ waitForRendering(card);
587+
588+ verify(data.visible || !data.limitedBandwith || artImage.source == "");
589+ }
569 }
590 }
570}
591}
tests/qmltests/Dash/tst_Dash.qml+1−2
@@ -249,8 +249,7 @@ Item {
249 var categoryListView = findChild(dashContentList.currentItem, "categoryListView");
249 var categoryListView = findChild(dashContentList.currentItem, "categoryListView");
250 var dashCategory2 = findChild(categoryListView, "dashCategory2");
250 var dashCategory2 = findChild(categoryListView, "dashCategory2");
251 tryCompareFunction(function() {
251 tryCompareFunction(function() {
252− var d = findChild(dashCategory2, "delegate2");
253− if (d) return true;
252+ if (dashCategory2.y < 200) return true;
254 categoryListView.contentY += 100;
253 categoryListView.contentY += 100;
255 return false;
254 return false;
256 }, true);
255 }, true);
tests/qmltests/Dash/tst_GenericScopeView.qml+103−7
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013, 2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -43,6 +43,10 @@ Item {
43 onLoadedChanged: if (loaded) genericScopeView.scope = scopes.getScope(2);
43 onLoadedChanged: if (loaded) genericScopeView.scope = scopes.getScope(2);
44 }
44 }
45
45
46+ MockScope {
47+ id: mockScope
48+ }
49+
46 SignalSpy {
50 SignalSpy {
47 id: spy
51 id: spy
48 }
52 }
@@ -78,18 +82,29 @@ Item {
78 spy.signalName = "";
82 spy.signalName = "";
79 }
83 }
80
84
81− function scrollToCategory(category) {
85+ function scrollToCategory(categoryName) {
82 var categoryListView = findChild(genericScopeView, "categoryListView");
86 var categoryListView = findChild(genericScopeView, "categoryListView");
83 tryCompareFunction(function() {
87 tryCompareFunction(function() {
84− if (findChild(genericScopeView, category)) return true;
88+ var category = findChild(genericScopeView, categoryName);
89+ if (category && category.y > 0 && category.y < genericScopeView.height) return true;
85 mouseFlick(genericScopeView, genericScopeView.width/2, genericScopeView.height,
90 mouseFlick(genericScopeView, genericScopeView.width/2, genericScopeView.height,
86 genericScopeView.width/2, genericScopeView.y)
91 genericScopeView.width/2, genericScopeView.y)
87 tryCompare(categoryListView, "moving", false);
92 tryCompare(categoryListView, "moving", false);
88− return findChild(genericScopeView, category) !== null;
93+ return false;
89 }, true);
94 }, true);
90
95
91 tryCompareFunction(function() { return findChild(genericScopeView, "delegate0") !== null; }, true);
96 tryCompareFunction(function() { return findChild(genericScopeView, "delegate0") !== null; }, true);
92− return findChild(genericScopeView, category);
97+ return findChild(genericScopeView, categoryName);
98+ }
99+
100+ function scrollToEnd()
101+ {
102+ var categoryListView = findChild(genericScopeView, "categoryListView");
103+ while (!categoryListView.atYEnd) {
104+ mouseFlick(genericScopeView, genericScopeView.width/2, genericScopeView.height - units.gu(8),
105+ genericScopeView.width/2, genericScopeView.y)
106+ tryCompare(categoryListView, "moving", false);
107+ }
93 }
108 }
94
109
95 function test_isActive() {
110 function test_isActive() {
@@ -268,7 +283,7 @@ Item {
268 if (category === undefined) category = 0;
283 if (category === undefined) category = 0;
269 if (delegate === undefined) delegate = 0;
284 if (delegate === undefined) delegate = 0;
270 tryCompareFunction(function() {
285 tryCompareFunction(function() {
271− var cardGrid = findChild(genericScopeView, category);
286+ var cardGrid = findChild(genericScopeView, "dashCategory"+category);
272 if (cardGrid != null) {
287 if (cardGrid != null) {
273 var tile = findChild(cardGrid, "delegate"+delegate);
288 var tile = findChild(cardGrid, "delegate"+delegate);
274 return tile != null;
289 return tile != null;
@@ -276,7 +291,7 @@ Item {
276 return false;
291 return false;
277 },
292 },
278 true);
293 true);
279− var tile = findChild(findChild(genericScopeView, category), "delegate"+delegate);
294+ var tile = findChild(findChild(genericScopeView, "dashCategory"+category), "delegate"+delegate);
280 mouseClick(tile, tile.width / 2, tile.height / 2);
295 mouseClick(tile, tile.width / 2, tile.height / 2);
281 tryCompare(testCase.subPageLoader, "open", true);
296 tryCompare(testCase.subPageLoader, "open", true);
282 tryCompare(testCase.subPageLoader, "x", 0);
297 tryCompare(testCase.subPageLoader, "x", 0);
@@ -426,6 +441,87 @@ Item {
426 compare(image.source, data.logo, "Title image has the wrong source");
441 compare(image.source, data.logo, "Title image has the wrong source");
427 }
442 }
428
443
444+ function test_seeAllTwoCategoriesScenario1() {
445+ mockScope.setId("mockScope");
446+ mockScope.setName("Mock Scope");
447+ mockScope.categories.setCount(2);
448+ mockScope.categories.resultModel(0).setResultCount(50);
449+ mockScope.categories.resultModel(1).setResultCount(15);
450+ mockScope.categories.setLayout(0, "grid");
451+ mockScope.categories.setLayout(1, "grid");
452+ mockScope.categories.setHeaderLink(0, "");
453+ mockScope.categories.setHeaderLink(1, "");
454+ genericScopeView.scope = mockScope;
455+ waitForRendering(genericScopeView.categoryView);
456+
457+ var category0 = findChild(genericScopeView, "dashCategory0")
458+ var seeAll0 = findChild(category0, "seeAll")
459+
460+ waitForRendering(seeAll0);
461+ verify(category0.expandable);
462+ verify(!category0.expanded);
463+
464+ mouseClick(seeAll0, seeAll0.width / 2, seeAll0.height / 2);
465+ verify(category0.expanded);
466+ tryCompare(category0, "height", category0.item.expandedHeight + seeAll0.height);
467+ tryCompare(genericScopeView.categoryView, "contentY", units.gu(13.5));
468+
469+ scrollToEnd();
470+
471+ tryCompareFunction(function() { return findChild(genericScopeView, "dashCategory1") !== null; }, true);
472+ var category1 = findChild(genericScopeView, "dashCategory1")
473+ var seeAll1 = findChild(category1, "seeAll")
474+ verify(category1.expandable);
475+ verify(!category1.expanded);
476+
477+ mouseClick(seeAll1, seeAll1.width / 2, seeAll1.height / 2);
478+ verify(!category0.expanded);
479+ verify(category1.expanded);
480+ tryCompare(category1, "height", category1.item.expandedHeight + seeAll1.height);
481+ tryCompareFunction(function() {
482+ return genericScopeView.categoryView.contentY + category1.y + category1.height + genericScopeView.categoryView.pageHeader.height
483+ == genericScopeView.categoryView.contentHeight;}
484+ , true);
485+ }
486+
487+ function test_seeAllTwoCategoriesScenario2() {
488+ mockScope.setId("mockScope");
489+ mockScope.setName("Mock Scope");
490+ mockScope.categories.setCount(2);
491+ mockScope.categories.resultModel(0).setResultCount(15);
492+ mockScope.categories.resultModel(1).setResultCount(50);
493+ mockScope.categories.setLayout(0, "grid");
494+ mockScope.categories.setLayout(1, "grid");
495+ mockScope.categories.setHeaderLink(0, "");
496+ mockScope.categories.setHeaderLink(1, "");
497+ genericScopeView.scope = mockScope;
498+ waitForRendering(genericScopeView.categoryView);
499+
500+ var category0 = findChild(genericScopeView, "dashCategory0")
501+ var seeAll0 = findChild(category0, "seeAll")
502+
503+ waitForRendering(seeAll0);
504+ verify(category0.expandable);
505+ verify(!category0.expanded);
506+
507+ mouseClick(seeAll0, seeAll0.width / 2, seeAll0.height / 2);
508+ verify(category0.expanded);
509+ tryCompare(category0, "height", category0.item.expandedHeight + seeAll0.height);
510+
511+ scrollToEnd();
512+
513+ var category1 = findChild(genericScopeView, "dashCategory1")
514+ var seeAll1 = findChild(category1, "seeAll")
515+ verify(category1.expandable);
516+ verify(!category1.expanded);
517+
518+ mouseClick(seeAll1, seeAll1.width / 2, seeAll1.height / 2);
519+ verify(!category0.expanded);
520+ verify(category1.expanded);
521+ tryCompare(category1, "height", category1.item.expandedHeight + seeAll1.height);
522+ tryCompare(category1, "y", units.gu(5));
523+ }
524+
429 function test_favorite_data() {
525 function test_favorite_data() {
430 return [
526 return [
431 { tag: "People", id: "MockScope1", favorite: true },
527 { tag: "People", id: "MockScope1", favorite: true },