Merge lp:~unity-team/unity8/two_see_more_bugfix into lp:unity8

Proposed by Michał Sawicz
Status: Merged
Approved by: Andrea Cimitan
Approved revision: 1193
Merged at revision: 1272
Proposed branch: lp:~unity-team/unity8/two_see_more_bugfix
Merge into: lp:unity8
Diff against target: 738 lines (+322/-184)
10 files modified
plugins/Dash/listviewwithpageheader.cpp (+11/-1)
plugins/Dash/listviewwithpageheader.h (+2/-0)
qml/Dash/GenericScopeView.qml (+74/-29)
tests/mocks/Unity/fake_categories.cpp (+100/-141)
tests/mocks/Unity/fake_categories.h (+7/-12)
tests/mocks/Unity/fake_resultsmodel.cpp (+9/-0)
tests/mocks/Unity/fake_resultsmodel.h (+3/-0)
tests/mocks/Unity/fake_scope.cpp (+17/-0)
tests/mocks/Unity/fake_scope.h (+2/-0)
tests/qmltests/Dash/tst_GenericScopeView.qml (+97/-1)
To merge this branch: bzr merge lp:~unity-team/unity8/two_see_more_bugfix
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Andrea Cimitan (community) Approve
Albert Astals Cid (community) contains my code Abstain
Michael Zanetti (community) Needs Fixing
Review via email: mp+234340@code.launchpad.net

This proposal supersedes a proposal from 2014-08-19.

Commit message

Dash: Fix issue when expanding a category and collapsing another one at the same time.

We need to first run the shrink+grow animations in the two category items, and later, if needed run the maximizeVisibleArea in the one we expanded.

It does not fix *everything* related to seeAll growing/shrinking + maximization, it may need some rethinking/rearchitecture to fix all issues, so let's just get these in that fix some very broken behaviour

Description of the change

* Are there any related MPs required for this MP to build/function as expected?
No

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

* Did you make sure that your branch does not contain spurious tags?
Yes

* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A

* If you changed the UI, has there been a design review?
N/A

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

Some minor things inline, seems to work fine.

review: Needs Fixing
Revision history for this message
Michał Sawicz (saviq) wrote :

Fixeded.

1188. By Michał Sawicz

Drop Q_PROPERTY

1189. By Michał Sawicz

We already have a seeAllButton property.

1190. By Michał Sawicz

I said we have it already.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

seems to fail a test now :/

review: Needs Fixing
Revision history for this message
Michał Sawicz (saviq) wrote :

> seems to fail a test now :/

Yeah, and I can't reproduce locally. Albert, back to you...

Revision history for this message
Albert Astals Cid (aacid) :
review: Abstain (contains my code)
1191. By Albert Astals Cid

Try to make LVWPH to behave consistently

doubles are not fun

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Andrea Cimitan (cimi) wrote :

Minor things around spacing

A question more for the backend: is it possible to have same categoryId? (in case someone badly implements a scope) We are checking for the id string now, rather the obj

1192. By Albert Astals Cid

spacing and (C) year

Revision history for this message
Albert Astals Cid (aacid) wrote :

> A question more for the backend: is it possible to have same categoryId? (in
> case someone badly implements a scope) We are checking for the id string now,
> rather the obj

Yes, we used to check for id not too long ago, but was [wrongly] changed in r1212 http://bazaar.launchpad.net/~unity-team/unity8/trunk/revision/1212

I say wrognly changed because keeping the pointer to the delegate means that if you scroll a lot in the list and the delegate gets destroyed, when you come back it'll be a different delegate pointer.

1193. By Albert Astals Cid

Typo

Revision history for this message
Andrea Cimitan (cimi) wrote :

 * Did you perform an exploratory manual test run of the code change and any related functionality?
Tested this branch through other dependants, works fine
 * Did CI run pass? If not, please explain why.
Auto.. ehm. :)

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'plugins/Dash/listviewwithpageheader.cpp'
2--- plugins/Dash/listviewwithpageheader.cpp 2014-09-01 10:45:25 +0000
3+++ plugins/Dash/listviewwithpageheader.cpp 2014-09-18 10:05:43 +0000
4@@ -409,6 +409,16 @@
5 }
6 }
7
8+int ListViewWithPageHeader::firstCreatedIndex() const
9+{
10+ return m_firstVisibleIndex;
11+}
12+
13+int ListViewWithPageHeader::createdItemCount() const
14+{
15+ return m_visibleItems.count();
16+}
17+
18 QQuickItem *ListViewWithPageHeader::item(int modelIndex) const
19 {
20 ListItem *item = itemAtIndex(modelIndex);
21@@ -520,7 +530,7 @@
22 // (but the header was not shown by it's own position)
23 // or the header is partially shown and we are not doing a maximizeVisibleArea either
24 const bool scrolledUp = m_previousContentY > contentY();
25- const bool notRebounding = contentY() + height() < contentHeight();
26+ const bool notRebounding = qRound(contentY() + height()) < qRound(contentHeight());
27 const bool notShownByItsOwn = contentY() + diff >= m_headerItem->y() + m_headerItem->height();
28 const bool maximizeVisibleAreaRunning = m_contentYAnimation->isRunning() && contentYAnimationType == ContentYAnimationMaximizeVisibleArea;
29
30
31=== modified file 'plugins/Dash/listviewwithpageheader.h'
32--- plugins/Dash/listviewwithpageheader.h 2014-09-01 10:45:25 +0000
33+++ plugins/Dash/listviewwithpageheader.h 2014-09-18 10:05:43 +0000
34@@ -86,6 +86,8 @@
35
36 Q_INVOKABLE void positionAtBeginning();
37 Q_INVOKABLE void showHeader();
38+ Q_INVOKABLE int firstCreatedIndex() const;
39+ Q_INVOKABLE int createdItemCount() const;
40 Q_INVOKABLE QQuickItem *item(int modelIndex) const;
41
42 // The index has to be created for this to try to do something
43
44=== modified file 'qml/Dash/GenericScopeView.qml'
45--- qml/Dash/GenericScopeView.qml 2014-09-08 14:15:01 +0000
46+++ qml/Dash/GenericScopeView.qml 2014-09-18 10:05:43 +0000
47@@ -47,6 +47,10 @@
48
49 signal backClicked()
50
51+ onScopeChanged: {
52+ floatingSeeLess.companionBase = null;
53+ }
54+
55 function positionAtBeginning() {
56 categoryView.positionAtBeginning()
57 }
58@@ -152,11 +156,54 @@
59 forceNoClip: subPageLoader.open
60 pixelAligned: true
61
62- property Item expandedCategoryItem: null
63+ property string expandedCategoryId: ""
64+ property int runMaximizeAfterSizeChanges: 0
65
66 readonly property bool pageHeaderTotallyVisible: scopeView.showPageHeader &&
67 ((headerItemShownHeight == 0 && categoryView.contentY <= categoryView.originY) || (headerItemShownHeight == pageHeaderLoader.item.height))
68
69+ onExpandedCategoryIdChanged: {
70+ var firstCreated = firstCreatedIndex();
71+ var shrinkingAny = false;
72+ var shrinkHeightDifference = 0;
73+ for (var i = 0; i < createdItemCount(); ++i) {
74+ var baseItem = item(firstCreated + i);
75+ if (baseItem.expandable) {
76+ var shouldExpand = baseItem.category === expandedCategoryId;
77+ if (shouldExpand != baseItem.expanded) {
78+ var animate = false;
79+ if (!subPageLoader.open) {
80+ var animateShrinking = !shouldExpand && baseItem.y + baseItem.item.collapsedHeight + baseItem.seeAllButton.height < categoryView.height;
81+ var animateGrowing = shouldExpand && baseItem.y + baseItem.height < categoryView.height;
82+ animate = shrinkingAny || animateShrinking || animateGrowing;
83+ }
84+
85+ if (!shouldExpand) {
86+ shrinkingAny = true;
87+ shrinkHeightDifference = baseItem.item.expandedHeight - baseItem.item.collapsedHeight;
88+ }
89+
90+ if (shouldExpand && !subPageLoader.open) {
91+ if (!shrinkingAny) {
92+ categoryView.maximizeVisibleArea(firstCreated + i, baseItem.item.expandedHeight + baseItem.seeAllButton.height);
93+ } else {
94+ // If the space that shrinking is smaller than the one we need to grow we'll call maximizeVisibleArea
95+ // after the shrink/grow animation ends
96+ var growHeightDifference = baseItem.item.expandedHeight - baseItem.item.collapsedHeight;
97+ if (growHeightDifference > shrinkHeightDifference) {
98+ runMaximizeAfterSizeChanges = 2;
99+ } else {
100+ runMaximizeAfterSizeChanges = 0;
101+ }
102+ }
103+ }
104+
105+ baseItem.expand(shouldExpand, animate);
106+ }
107+ }
108+ }
109+ }
110+
111 delegate: ListItems.Base {
112 id: baseItem
113 objectName: "dashCategory" + category
114@@ -194,7 +241,7 @@
115 // This can happen with the VJ that doesn't know how height it will be on creation
116 // so doesn't set expandable until a bit too late for onLoaded
117 if (expandable) {
118- var shouldExpand = baseItem === categoryView.expandedCategoryItem;
119+ var shouldExpand = baseItem.category === categoryView.expandedCategoryId;
120 baseItem.expand(shouldExpand, false /*animate*/);
121 }
122 }
123@@ -215,9 +262,23 @@
124 id: heightBehaviour
125 enabled: false
126 animation: UbuntuNumberAnimation {
127+ duration: UbuntuAnimation.FastDuration
128 onRunningChanged: {
129 if (!running) {
130 heightBehaviour.enabled = false
131+ if (categoryView.runMaximizeAfterSizeChanges > 0) {
132+ categoryView.runMaximizeAfterSizeChanges--;
133+ if (categoryView.runMaximizeAfterSizeChanges == 0) {
134+ var firstCreated = categoryView.firstCreatedIndex();
135+ for (var i = 0; i < categoryView.createdItemCount(); ++i) {
136+ var baseItem = categoryView.item(firstCreated + i);
137+ if (baseItem.category === categoryView.expandedCategoryId) {
138+ categoryView.maximizeVisibleArea(firstCreated + i, baseItem.item.expandedHeight + baseItem.seeAllButton.height);
139+ break;
140+ }
141+ }
142+ }
143+ }
144 }
145 }
146 }
147@@ -245,7 +306,7 @@
148 item.objectName = Qt.binding(function() { return categoryId })
149 item.scopeStyle = scopeView.scopeStyle;
150 if (baseItem.expandable) {
151- var shouldExpand = baseItem === categoryView.expandedCategoryItem;
152+ var shouldExpand = baseItem.category === categoryView.expandedCategoryId;
153 baseItem.expand(shouldExpand, false /*animate*/);
154 }
155 updateDelegateCreationRange();
156@@ -282,27 +343,6 @@
157 }
158 Connections {
159 target: categoryView
160- onExpandedCategoryItemChanged: {
161- collapseAllButExpandedCategory();
162- }
163- function collapseAllButExpandedCategory() {
164- var item = rendererLoader.item;
165- if (baseItem.expandable) {
166- var shouldExpand = baseItem === categoryView.expandedCategoryItem;
167- if (shouldExpand != baseItem.expanded) {
168- // If the filter animation will be seen start it, otherwise, just flip the switch
169- var shrinkingVisible = !shouldExpand && y + item.collapsedHeight + seeAll.height < categoryView.height;
170- var growingVisible = shouldExpand && y + height < categoryView.height;
171- if (!subPageLoader.open || shouldExpand) {
172- var animate = shrinkingVisible || growingVisible;
173- baseItem.expand(shouldExpand, animate)
174- if (shouldExpand && !subPageLoader.open) {
175- categoryView.maximizeVisibleArea(index, item.expandedHeight + seeAll.height);
176- }
177- }
178- }
179- }
180- }
181 onOriginYChanged: rendererLoader.updateDelegateCreationRange();
182 onContentYChanged: rendererLoader.updateDelegateCreationRange();
183 onHeightChanged: rendererLoader.updateDelegateCreationRange();
184@@ -352,10 +392,11 @@
185 height: seeAllLabel.visible ? seeAllLabel.font.pixelSize + units.gu(4) : 0
186
187 onClicked: {
188- if (categoryView.expandedCategoryItem !== baseItem) {
189- categoryView.expandedCategoryItem = baseItem;
190+ if (categoryView.expandedCategoryId !== baseItem.category) {
191+ categoryView.expandedCategoryId = baseItem.category;
192+ floatingSeeLess.companionBase = baseItem;
193 } else {
194- categoryView.expandedCategoryItem = null;
195+ categoryView.expandedCategoryId = "";
196 }
197 }
198
199@@ -455,7 +496,7 @@
200 objectName: "floatingSeeLess"
201
202 property Item companionTo: companionBase ? companionBase.seeAllButton : null
203- property Item companionBase: categoryView.expandedCategoryItem
204+ property Item companionBase: null
205 property bool showBecausePosition: false
206 property real yOffset: 0
207
208@@ -467,7 +508,7 @@
209 height: seeLessLabel.font.pixelSize + units.gu(4)
210 visible: companionTo && showBecausePosition
211
212- onClicked: categoryView.expandedCategoryItem = null;
213+ onClicked: categoryView.expandedCategoryId = "";
214
215 function updateVisibility() {
216 var companionPos = companionTo.mapToItem(floatingSeeLess, 0, 0);
217@@ -476,6 +517,10 @@
218 var posToBase = floatingSeeLess.mapToItem(companionBase, 0, -yOffset).y;
219 yOffset = Math.max(0, companionBase.item.collapsedHeight - posToBase);
220 yOffset = Math.min(yOffset, height);
221+
222+ if (!showBecausePosition && categoryView.expandedCategoryId === "") {
223+ companionBase = null;
224+ }
225 }
226
227 Label {
228
229=== modified file 'tests/mocks/Unity/fake_categories.cpp'
230--- tests/mocks/Unity/fake_categories.cpp 2014-08-15 14:21:42 +0000
231+++ tests/mocks/Unity/fake_categories.cpp 2014-09-18 10:05:43 +0000
232@@ -29,25 +29,12 @@
233
234 int Categories::rowCount(const QModelIndex& /*parent*/) const
235 {
236- return m_category_count + m_specialCategories.count();
237+ return m_category_count;
238 }
239
240-void Categories::addSpecialCategory(QString const& categoryId, QString const& name, QString const& icon, QString const& rawTemplate, QObject* countObject)
241+void Categories::addSpecialCategory(QString const& /*categoryId*/, QString const& /*name*/, QString const& /*icon*/, QString const& /*rawTemplate*/, QObject* /*countObject*/)
242 {
243- CategoryData catData;
244- catData.categoryId = categoryId;
245- catData.name = name;
246- catData.icon = icon;
247- catData.rawTemplate = rawTemplate;
248- catData.countObject = countObject;
249-
250- beginInsertRows(QModelIndex(), 0, 0);
251- m_specialCategories.prepend(catData);
252- endInsertRows();
253-
254- if (countObject) {
255- connect(countObject, SIGNAL(countChanged()), this, SLOT(countChanged()));
256- }
257+ qFatal("Using un-implemented Categories::addSpecialCategory");
258 }
259
260 bool Categories::overrideCategoryJson(QString const& /* categoryId */, QString const& /* json */)
261@@ -55,23 +42,6 @@
262 qFatal("Using un-implemented Categories::overrideCategoryJson");
263 }
264
265-void Categories::countChanged()
266-{
267- QObject* countObject = sender();
268-
269- for (int i = 0; i < m_specialCategories.count(); ++i) {
270- const CategoryData &catData = m_specialCategories.at(i);
271- if (catData.countObject == countObject) {
272- QVector<int> roles;
273- roles.append(RoleCount);
274-
275- QModelIndex changedIndex(index(i));
276- dataChanged(changedIndex, changedIndex, roles);
277- break;
278- }
279- }
280-}
281-
282 QVariant
283 Categories::data(const QModelIndex& index, int role) const
284 {
285@@ -79,115 +49,74 @@
286 return QVariant();
287 }
288
289- ResultsModel *resultsModel = m_resultsModels[index.row()];
290- if (!resultsModel) {
291- resultsModel = new ResultsModel(15, index.row());
292- m_resultsModels[index.row()] = resultsModel;
293- }
294- if (index.row() < m_specialCategories.count()) {
295- const CategoryData &catData = m_specialCategories.at(index.row());
296- switch (role) {
297- case RoleCategoryId:
298- return catData.categoryId;
299- case RoleName:
300- return catData.name;
301- case RoleIcon:
302- return catData.icon;
303- case RoleRawRendererTemplate:
304- return catData.rawTemplate;
305- case RoleRenderer:
306- {
307- QVariantMap map;
308- if (index.row() % 2 == 0) {
309- map["category-layout"] = "grid";
310- } else {
311- map["category-layout"] = "carousel";
312- map["overlay"] = true;
313- }
314- map["card-size"] = "small";
315- return map;
316- }
317- case RoleComponents:
318- {
319- QVariantMap map, artMap, attributeMap;
320+ ResultsModel *resultsModel = resultModel(index.row());
321+ switch (role) {
322+ case RoleCategoryId:
323+ return QString("%1").arg(index.row());
324+ case RoleName:
325+ return QString("Category %1").arg(index.row());
326+ case RoleIcon:
327+ return "gtk-apply";
328+ case RoleRawRendererTemplate:
329+ qFatal("Using un-implemented RoleRawRendererTemplate Categories role");
330+ return QVariant();
331+ case RoleRenderer:
332+ {
333+ QVariantMap map;
334+ if (index.row() % 2 == 0) {
335+ map["category-layout"] = "grid";
336+ } else {
337+ map["category-layout"] = "carousel";
338+ }
339+ if (index.row() == 18) {
340+ map["category-layout"] = "horizontal-list";
341+ }
342+ if (index.row() == 19) {
343+ map["category-layout"] = "grid";
344+ map["collapsed-rows"] = 0;
345+ }
346+ map["card-size"] = "small";
347+
348+ map["category-layout"] = m_layouts.value(index.row(), map["category-layout"].toString());
349+
350+ if (map["category-layout"] == "carousel") {
351+ map["overlay"] = true;
352+ }
353+
354+ return map;
355+ }
356+ case RoleComponents:
357+ {
358+ QVariantMap map, artMap, attributeMap;
359+ if (index.row() % 2 != 0) {
360+ artMap["aspect-ratio"] = QString("1.%1").arg(index.row());
361+ } else {
362 artMap["aspect-ratio"] = "1.0";
363- artMap["field"] = "art";
364- map["art"] = artMap;
365- map["title"] = "HOLA";
366- attributeMap["field"] = "attribute";
367- map["attributes"] = attributeMap;
368- return map;
369- }
370- case RoleHeaderLink:
371- return QString();
372- case RoleResults:
373- return QVariant();
374- case RoleCount:
375- return catData.countObject->property("count");
376- default:
377- qFatal("Using un-implemented Categories role");
378- return QVariant();
379- }
380- } else {
381- switch (role) {
382- case RoleCategoryId:
383- return QString("%1").arg(index.row());
384- case RoleName:
385- return QString("Category %1").arg(index.row());
386- case RoleIcon:
387- return "gtk-apply";
388- case RoleRawRendererTemplate:
389- qFatal("Using un-implemented RoleRawRendererTemplate Categories role");
390- return QVariant();
391- case RoleRenderer:
392- {
393- QVariantMap map;
394- if (index.row() % 2 == 0) {
395- map["category-layout"] = "grid";
396- } else {
397- map["category-layout"] = "carousel";
398- map["card-size"] = "medium";
399- map["overlay"] = true;
400- }
401- if (index.row() == 18) {
402- map["category-layout"] = "horizontal-list";
403- }
404- if (index.row() == 19) {
405- map["category-layout"] = "grid";
406- map["collapsed-rows"] = 0;
407- }
408- map["card-size"] = "small";
409- return map;
410- }
411- case RoleComponents:
412- {
413- QVariantMap map, artMap, attributeMap;
414- if (index.row() % 2 != 0) {
415- artMap["aspect-ratio"] = QString("1.%1").arg(index.row());
416- } else {
417- artMap["aspect-ratio"] = "1.0";
418- }
419- artMap["field"] = "art";
420- map["art"] = artMap;
421- map["title"] = "HOLA";
422- map["subtitle"] = "HOLA";
423- attributeMap["field"] = "attribute";
424- map["attributes"] = attributeMap;
425- return map;
426- }
427- case RoleHeaderLink:
428- if (index.row() == 1 || index.row() == 4) {
429- return QString("scope://query/1");
430- }
431- return QString();
432- case RoleResults:
433- return QVariant::fromValue(resultsModel);
434- case RoleCount:
435- return resultsModel->rowCount();
436- default:
437- qFatal("Using un-implemented Categories role");
438- return QVariant();
439- }
440+ }
441+ artMap["field"] = "art";
442+ map["art"] = artMap;
443+ map["title"] = "HOLA";
444+ map["subtitle"] = "HOLA";
445+ attributeMap["field"] = "attribute";
446+ map["attributes"] = attributeMap;
447+ return map;
448+ }
449+ case RoleHeaderLink:
450+ {
451+ QString res;
452+ if (index.row() == 1 || index.row() == 4) {
453+ res = QString("scope://query/1");
454+ }
455+ res = m_headerLinks.value(index.row(), res);
456+ return res;
457+ }
458+ case RoleResults:
459+ return QVariant::fromValue(resultsModel);
460+ case RoleCount:
461+ return resultsModel->rowCount();
462+ default:
463+ qFatal("Using un-implemented Categories role");
464+ return QVariant();
465 }
466 }
467
468@@ -196,3 +125,33 @@
469 {
470 return data(index(row, 0), role);
471 }
472+
473+void Categories::setCount(int count)
474+{
475+ if (m_category_count != count) {
476+ beginResetModel(); // This is just for test setup so we can be lazy and reset
477+ m_category_count = count;
478+ endResetModel();
479+ }
480+}
481+
482+ResultsModel* Categories::resultModel(int row) const
483+{
484+ ResultsModel *result = m_resultsModels[row];
485+ if (!result) {
486+ Categories *that = const_cast<Categories*>(this);
487+ result = new ResultsModel(15, row, that);
488+ m_resultsModels[row] = result;
489+ }
490+ return result;
491+}
492+
493+void Categories::setLayout(int row, const QString &layout)
494+{
495+ m_layouts[row] = layout;
496+}
497+
498+void Categories::setHeaderLink(int row, const QString &headerLink)
499+{
500+ m_headerLinks[row] = headerLink;
501+}
502
503=== modified file 'tests/mocks/Unity/fake_categories.h'
504--- tests/mocks/Unity/fake_categories.h 2014-07-11 16:39:33 +0000
505+++ tests/mocks/Unity/fake_categories.h 2014-09-18 10:05:43 +0000
506@@ -39,22 +39,17 @@
507
508 Q_INVOKABLE QVariant data(int row, int role) const;
509
510-private Q_SLOTS:
511- void countChanged();
512+ // For testing purposes
513+ Q_INVOKABLE void setCount(int count);
514+ Q_INVOKABLE ResultsModel* resultModel(int row) const;
515+ Q_INVOKABLE void setLayout(int row, const QString &layout);
516+ Q_INVOKABLE void setHeaderLink(int row, const QString &headerLink);
517
518 private:
519 mutable QHash<int, ResultsModel*> m_resultsModels;
520+ QHash<int, QString> m_layouts;
521+ QHash<int, QString> m_headerLinks;
522 int m_category_count;
523-
524- struct CategoryData {
525- QString categoryId;
526- QString name;
527- QString icon;
528- QString rawTemplate;
529- QObject* countObject;
530- };
531-
532- QList<CategoryData> m_specialCategories;
533 };
534
535 #endif // FAKE_CATEGORIES_H
536
537=== modified file 'tests/mocks/Unity/fake_resultsmodel.cpp'
538--- tests/mocks/Unity/fake_resultsmodel.cpp 2014-08-13 16:06:40 +0000
539+++ tests/mocks/Unity/fake_resultsmodel.cpp 2014-09-18 10:05:43 +0000
540@@ -44,6 +44,15 @@
541 qFatal("Calling un-implemented ResultsModel::setCategoryId");
542 }
543
544+void ResultsModel::setResultCount(int result_count)
545+{
546+ if (m_result_count != result_count) {
547+ beginResetModel(); // This is just for test setup so we can be lazy and reset
548+ m_result_count = result_count;
549+ endResetModel();
550+ }
551+}
552+
553 int ResultsModel::rowCount(const QModelIndex& parent) const
554 {
555 Q_UNUSED(parent);
556
557=== modified file 'tests/mocks/Unity/fake_resultsmodel.h'
558--- tests/mocks/Unity/fake_resultsmodel.h 2014-08-11 09:57:29 +0000
559+++ tests/mocks/Unity/fake_resultsmodel.h 2014-09-18 10:05:43 +0000
560@@ -41,6 +41,9 @@
561 /* setters */
562 void setCategoryId(QString const& id) override;
563
564+ // For testing purposes
565+ Q_INVOKABLE void setResultCount(int result_count);
566+
567 private:
568 int m_result_count;
569 int m_categoryId;
570
571=== modified file 'tests/mocks/Unity/fake_scope.cpp'
572--- tests/mocks/Unity/fake_scope.cpp 2014-08-27 08:01:40 +0000
573+++ tests/mocks/Unity/fake_scope.cpp 2014-09-18 10:05:43 +0000
574@@ -145,6 +145,23 @@
575 Q_EMIT favoriteChanged();
576 }
577 }
578+
579+void Scope::setId(const QString &id)
580+{
581+ if (id != m_id) {
582+ m_id = id;
583+ Q_EMIT idChanged();
584+ }
585+}
586+
587+void Scope::setName(const QString &name)
588+{
589+ if (name != m_name) {
590+ m_name = name;
591+ Q_EMIT nameChanged();
592+ }
593+}
594+
595 void Scope::setSearchInProgress(const bool inProg)
596 {
597 if (inProg != m_searching) {
598
599=== modified file 'tests/mocks/Unity/fake_scope.h'
600--- tests/mocks/Unity/fake_scope.h 2014-08-12 12:09:49 +0000
601+++ tests/mocks/Unity/fake_scope.h 2014-09-18 10:05:43 +0000
602@@ -56,6 +56,8 @@
603 void setFormFactor(const QString& form_factor) override;
604 void setActive(const bool) override;
605 void setFavorite(const bool) override;
606+ Q_INVOKABLE void setId(const QString &id); // This is not invokable in the Interface, here for testing benefits
607+ Q_INVOKABLE void setName(const QString &name); // This is not invokable in the Interface, here for testing benefits
608 Q_INVOKABLE void setSearchInProgress(const bool inProg); // This is not invokable in the Interface, here for testing benefits
609
610 Q_INVOKABLE void activate(QVariant const& result) override;
611
612=== modified file 'tests/qmltests/Dash/tst_GenericScopeView.qml'
613--- tests/qmltests/Dash/tst_GenericScopeView.qml 2014-08-26 19:25:41 +0000
614+++ tests/qmltests/Dash/tst_GenericScopeView.qml 2014-09-18 10:05:43 +0000
615@@ -1,5 +1,5 @@
616 /*
617- * Copyright 2013 Canonical Ltd.
618+ * Copyright 2014 Canonical Ltd.
619 *
620 * This program is free software; you can redistribute it and/or modify
621 * it under the terms of the GNU General Public License as published by
622@@ -43,6 +43,10 @@
623 onLoadedChanged: if (loaded) genericScopeView.scope = scopes.getScope(2);
624 }
625
626+ MockScope {
627+ id: mockScope
628+ }
629+
630 SignalSpy {
631 id: spy
632 }
633@@ -92,6 +96,17 @@
634 return findChild(genericScopeView, category);
635 }
636
637+ function scrollToEnd()
638+ {
639+ var categoryListView = findChild(genericScopeView, "categoryListView");
640+ waitForRendering(categoryListView);
641+ while (!categoryListView.atYEnd) {
642+ mouseFlick(genericScopeView, genericScopeView.width/2, genericScopeView.height - units.gu(8),
643+ genericScopeView.width/2, genericScopeView.y)
644+ tryCompare(categoryListView, "moving", false);
645+ }
646+ }
647+
648 function test_isActive() {
649 tryCompare(genericScopeView.scope, "isActive", false)
650 genericScopeView.isCurrent = true
651@@ -426,6 +441,87 @@
652 compare(image.source, data.logo, "Title image has the wrong source");
653 }
654
655+ function test_seeAllTwoCategoriesScenario1() {
656+ mockScope.setId("mockScope");
657+ mockScope.setName("Mock Scope");
658+ mockScope.categories.setCount(2);
659+ mockScope.categories.resultModel(0).setResultCount(50);
660+ mockScope.categories.resultModel(1).setResultCount(15);
661+ mockScope.categories.setLayout(0, "grid");
662+ mockScope.categories.setLayout(1, "grid");
663+ mockScope.categories.setHeaderLink(0, "");
664+ mockScope.categories.setHeaderLink(1, "");
665+ genericScopeView.scope = mockScope;
666+ waitForRendering(genericScopeView.categoryView);
667+
668+ var category0 = findChild(genericScopeView, "dashCategory0")
669+ var seeAll0 = findChild(category0, "seeAll")
670+
671+ waitForRendering(seeAll0);
672+ verify(category0.expandable);
673+ verify(!category0.expanded);
674+
675+ mouseClick(seeAll0, seeAll0.width / 2, seeAll0.height / 2);
676+ verify(category0.expanded);
677+ tryCompare(category0, "height", category0.item.expandedHeight + seeAll0.height);
678+ tryCompare(genericScopeView.categoryView, "contentY", units.gu(13.5));
679+
680+ scrollToEnd();
681+
682+ tryCompareFunction(function() { return findChild(genericScopeView, "dashCategory1") !== null; }, true);
683+ var category1 = findChild(genericScopeView, "dashCategory1")
684+ var seeAll1 = findChild(category1, "seeAll")
685+ verify(category1.expandable);
686+ verify(!category1.expanded);
687+
688+ mouseClick(seeAll1, seeAll1.width / 2, seeAll1.height / 2);
689+ verify(!category0.expanded);
690+ verify(category1.expanded);
691+ tryCompare(category1, "height", category1.item.expandedHeight + seeAll1.height);
692+ tryCompareFunction(function() {
693+ return genericScopeView.categoryView.contentY + category1.y + category1.height
694+ == genericScopeView.categoryView.contentHeight;}
695+ , true);
696+ }
697+
698+ function test_seeAllTwoCategoriesScenario2() {
699+ mockScope.setId("mockScope");
700+ mockScope.setName("Mock Scope");
701+ mockScope.categories.setCount(2);
702+ mockScope.categories.resultModel(0).setResultCount(15);
703+ mockScope.categories.resultModel(1).setResultCount(50);
704+ mockScope.categories.setLayout(0, "grid");
705+ mockScope.categories.setLayout(1, "grid");
706+ mockScope.categories.setHeaderLink(0, "");
707+ mockScope.categories.setHeaderLink(1, "");
708+ genericScopeView.scope = mockScope;
709+ waitForRendering(genericScopeView.categoryView);
710+
711+ var category0 = findChild(genericScopeView, "dashCategory0")
712+ var seeAll0 = findChild(category0, "seeAll")
713+
714+ waitForRendering(seeAll0);
715+ verify(category0.expandable);
716+ verify(!category0.expanded);
717+
718+ mouseClick(seeAll0, seeAll0.width / 2, seeAll0.height / 2);
719+ verify(category0.expanded);
720+ tryCompare(category0, "height", category0.item.expandedHeight + seeAll0.height);
721+
722+ scrollToEnd();
723+
724+ var category1 = findChild(genericScopeView, "dashCategory1")
725+ var seeAll1 = findChild(category1, "seeAll")
726+ verify(category1.expandable);
727+ verify(!category1.expanded);
728+
729+ mouseClick(seeAll1, seeAll1.width / 2, seeAll1.height / 2);
730+ verify(!category0.expanded);
731+ verify(category1.expanded);
732+ tryCompare(category1, "height", category1.item.expandedHeight + seeAll1.height);
733+ tryCompare(category1, "y", units.gu(5));
734+ }
735+
736 function test_favorite_data() {
737 return [
738 { tag: "People", id: "MockScope1", favorite: true },

Subscribers

People subscribed via source and target branches