Merge lp:~tpeeters/ubuntu-ui-toolkit/back into lp:~ubuntu-sdk-team/ubuntu-ui-toolkit/multiColumnView

Proposed by Tim Peeters on 2015-07-15
Status: Merged
Approved by: Christian Dywan on 2015-07-20
Approved revision: 1578
Merged at revision: 1578
Proposed branch: lp:~tpeeters/ubuntu-ui-toolkit/back
Merge into: lp:~ubuntu-sdk-team/ubuntu-ui-toolkit/multiColumnView
Diff against target: 770 lines (+413/-119)
7 files modified
examples/ubuntu-ui-toolkit-gallery/Template.qml (+0/-5)
modules/Ubuntu/Components/1.2/PageWrapperUtils.js (+1/-2)
modules/Ubuntu/Components/1.3/MultiColumnView.qml (+45/-10)
modules/Ubuntu/Components/1.3/tree.js (+84/-23)
modules/Ubuntu/Components/Themes/Ambiance/1.3/PageHeadStyle.qml (+15/-5)
tests/unit_x11/tst_components/tst_multicolumnheader.qml (+172/-13)
tests/unit_x11/tst_components/tst_multicolumnview.qml (+96/-61)
To merge this branch: bzr merge lp:~tpeeters/ubuntu-ui-toolkit/back
Reviewer Review Type Date Requested Status
Christian Dywan 2015-07-15 Approve on 2015-07-20
Review via email: mp+264839@code.launchpad.net

Commit Message

Automatic back button.

To post a comment you must log in.
lp:~tpeeters/ubuntu-ui-toolkit/back updated on 2015-07-20
1569. By Tim Peeters on 2015-07-16

clean showcase

1570. By Tim Peeters on 2015-07-16

don't add page that was already added

1571. By Tim Peeters on 2015-07-16

update tests

1572. By Tim Peeters on 2015-07-17

clean

1573. By Tim Peeters on 2015-07-19

clean

1574. By Tim Peeters on 2015-07-20

add unit tests for single column on phone

1575. By Tim Peeters on 2015-07-20

tst_multicolumnview to work in single column

1576. By Tim Peeters on 2015-07-20

clean

Christian Dywan (kalikiana) wrote :

I appreciate the extensive back button test, it probably wasn't easy to get sorted, but it's looking good. And you added a check for adding the same Page with tests, which I like a lot.

400 + function test_number_of_headers_equals_number_of_columns_wide() {
401 + if (root.columns !== 2) {
402 + skip("Only for wide view.");
403 + }

Can we resize the window on an environment where there's no enforced fullscreen and run both narrow and wide tests that way? Otherwise you could only run all tests by changing the window size by hand.

review: Needs Information
lp:~tpeeters/ubuntu-ui-toolkit/back updated on 2015-07-20
1577. By Tim Peeters on 2015-07-20

review comment

1578. By Tim Peeters on 2015-07-20

tweak

Christian Dywan (kalikiana) wrote :

Nice. Thanks for the update!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'examples/ubuntu-ui-toolkit-gallery/Template.qml'
2--- examples/ubuntu-ui-toolkit-gallery/Template.qml 2015-07-02 14:51:16 +0000
3+++ examples/ubuntu-ui-toolkit-gallery/Template.qml 2015-07-20 22:43:59 +0000
4@@ -20,11 +20,6 @@
5 Page {
6 id: template
7
8- head.backAction: Action {
9- iconName: 'back'
10- onTriggered: columns.removePages(template)
11- }
12-
13 default property alias content: layout.children
14 property alias spacing: layout.spacing
15
16
17=== modified file 'modules/Ubuntu/Components/1.2/PageWrapperUtils.js'
18--- modules/Ubuntu/Components/1.2/PageWrapperUtils.js 2015-04-30 08:32:44 +0000
19+++ modules/Ubuntu/Components/1.2/PageWrapperUtils.js 2015-07-20 22:43:59 +0000
20@@ -30,8 +30,7 @@
21 if (pageWrapper.reference.createObject) {
22 // page reference is a component
23 pageComponent = pageWrapper.reference;
24- }
25- else if (typeof pageWrapper.reference == "string") {
26+ } else if (typeof pageWrapper.reference == "string") {
27 // page reference is a string (url)
28 pageComponent = Qt.createComponent(pageWrapper.reference);
29 }
30
31=== modified file 'modules/Ubuntu/Components/1.3/MultiColumnView.qml'
32--- modules/Ubuntu/Components/1.3/MultiColumnView.qml 2015-07-13 15:14:03 +0000
33+++ modules/Ubuntu/Components/1.3/MultiColumnView.qml 2015-07-20 22:43:59 +0000
34@@ -175,13 +175,18 @@
35
36 /*!
37 \qmlmethod Item addPageToNextColumn(Item sourcePage, var page[, var properties])
38- Same as \l addPageToCurrentColumn except that the \c page is added to the column
39- next to the one the \c sourcePage resides. If \c sourcePage is null, the new
40- page will be added to the leftmost column. If \c sourcePage is located in the
41+ Remove all previous pages from the next column (relative to the column that
42+ holds \c sourcePage) and all following columns, and then add \c page to the next column.
43+ If \c sourcePage is located in the
44 rightmost column, the new page will be pushed to the same column as \c sourcePage.
45 */
46 function addPageToNextColumn(sourcePage, page, properties) {
47- return d.addPageToColumn(d.columnForPage(sourcePage) + 1, sourcePage, page, properties);
48+ var nextColumn = d.columnForPage(sourcePage) + 1;
49+ d.tree.prune(nextColumn);
50+ for (var i = nextColumn; i < d.columns; i++) {
51+ d.updatePageForColumn(i);
52+ }
53+ return d.addPageToColumn(nextColumn, sourcePage, page, properties);
54 }
55
56 /*!
57@@ -263,7 +268,7 @@
58 function getWrapper(page) {
59 if (page && page.hasOwnProperty("parentNode")) {
60 var w = page.parentNode;
61- if (w.hasOwnProperty("object") && w.hasOwnProperty("reference")) {
62+ if (w && w.hasOwnProperty("object") && w.hasOwnProperty("reference")) {
63 if (w.object == page) {
64 return w;
65 } else {
66@@ -300,11 +305,22 @@
67 return;
68 }
69
70- var wrapper = d.createWrapper(page, properties);
71- wrapper.parentPage = sourcePage;
72- wrapper.column = column;
73- d.addWrappedPage(wrapper);
74- return wrapper.object;
75+ // Check that the Page was not already added.
76+ if (typeof page !== "string" && !page.createObject) {
77+ // page is neither a url or a Component so it must be a Page object.
78+
79+ var oldWrapper = getWrapper(page);
80+ if (oldWrapper && d.tree.index(oldWrapper) !== -1) {
81+ console.warn("Cannot add a Page that was already added.");
82+ return null;
83+ }
84+ }
85+
86+ var newWrapper = d.createWrapper(page, properties);
87+ newWrapper.parentPage = sourcePage;
88+ newWrapper.column = column;
89+ d.addWrappedPage(newWrapper);
90+ return newWrapper.object;
91 }
92
93 // update the page for the specified column
94@@ -455,6 +471,25 @@
95 property color panelColor: multiColumnView.__propagated.header.panelColor
96
97 visible: holder.pageWrapper && holder.pageWrapper.active
98+
99+ // The multiColumn, page and showBackButton properties are used in
100+ // PageHeadStyle to show/hide the back button.
101+ property var multiColumn: multiColumnView
102+ property var page: holder.pageWrapper ? holder.pageWrapper.object : null
103+ property bool showBackButton: {
104+ if (!page) {
105+ return false;
106+ }
107+ var parentWrapper;
108+ try {
109+ parentWrapper = d.tree.parent(holder.pageWrapper);
110+ } catch(err) {
111+ // Root node has no parent node.
112+ return false;
113+ }
114+ var nextInColumn = d.tree.top(holder.column, holder.column < d.columns - 1, 1);
115+ return parentWrapper === nextInColumn;
116+ }
117 }
118
119 Rectangle {
120
121=== modified file 'modules/Ubuntu/Components/1.3/tree.js'
122--- modules/Ubuntu/Components/1.3/tree.js 2015-07-14 09:51:34 +0000
123+++ modules/Ubuntu/Components/1.3/tree.js 2015-07-20 22:43:59 +0000
124@@ -42,7 +42,6 @@
125 if (this.index(newNode) !== -1) {
126 throw "Cannot add the same node twice to a tree.";
127 }
128- var parentIndex = this.index(parentNode);
129 if (size === 0) {
130 // adding root node
131 if (parentNode !== null) {
132@@ -53,17 +52,43 @@
133 if (parentNode === null) {
134 throw "Only root node has parentNode null."
135 }
136- if (parentIndex === -1) {
137+ if (this.index(parentNode) === -1) {
138 throw "Cannot add non-root node if parentNode is not in the tree.";
139 }
140 }
141 nodes.push(newNode);
142 stems.push(stem);
143- parents.push(parentIndex);
144+ parents.push(parentNode);
145 size++;
146 }
147
148- // Chops all nodes with an index higher than the given node.
149+ // Remove all nodes from the specified stem and higher stems.
150+ //
151+ // Returns the removed nodes.
152+ this.prune = function(stem) {
153+ var newNodes = [];
154+ var newStems = [];
155+ var newParents = [];
156+ var removedNodes = [];
157+ for (var i = 0; i < nodes.length; i++) {
158+ if (stems[i] < stem) {
159+ newNodes.push(nodes[i]);
160+ newStems.push(stems[i]);
161+ newParents.push(parents[i]);
162+ } else {
163+ removedNodes.push(nodes[i]);
164+ }
165+ }
166+ nodes = newNodes;
167+ stems = newStems;
168+ parents = newParents;
169+ size = nodes.length;
170+ return removedNodes;
171+ }
172+
173+ // Chops all nodes with an index higher than the given node which
174+ // are in the same stem or a higher stem.
175+ //
176 // If, and only if, (inclusive) then also chop the given node.
177 //
178 // Default values for node and inclusive are top() and true.
179@@ -72,38 +97,72 @@
180 node = typeof node !== 'undefined' ? node : this.top();
181 inclusive = typeof inclusive !== 'undefined' ? inclusive : true
182 var nodeIndex = this.index(node);
183- if (nodeIndex >= 0) {
184- if (inclusive) {
185- size = nodeIndex;
186- } else {
187- size = nodeIndex + 1;
188- }
189- var oldNodes = nodes;
190- nodes = nodes.slice(0, size);
191- stems = stems.slice(0, size);
192- parents = parents.slice(0, size);
193- return oldNodes.slice(size);
194- } else {
195- // given node is not in the tree
196+ if (nodeIndex < 0) {
197+ // given node is not in the tree.
198 return [];
199 }
200+ if (inclusive) {
201+ size = nodeIndex;
202+ } else {
203+ size = nodeIndex + 1;
204+ }
205+
206+ // Nodes with index(node) >= nodeIndex && stem >= stems[nodeIndex];
207+ var badNodes = []; // to fill below
208+
209+ // Nodes with index(node) >= nodeIndex (any stem).
210+ // Potential bad nodes to be removed.
211+ var uglyNodes = nodes.slice(size);
212+ var uglyStems = stems.slice(size); // to check below
213+ var uglyParents = parents.slice(size);
214+
215+ var stem = stems[nodeIndex];
216+ // Good nodes, with index(node) < nodeIndex && stem < stems[nodeIndex]:
217+ nodes = nodes.slice(0, size);
218+ stems = stems.slice(0, size);
219+ parents = parents.slice(0, size);
220+
221+ // Add nodes with index(node) > nodeIndex && stem < stems[nodeIndex] back:
222+ for (var i = 0; i < uglyNodes.length; i++) {
223+ if (uglyStems[i] < stem) {
224+ // Because the stem of the parentNode <= stem of the node,
225+ // the node that is added back has the parentNode in nodes.
226+ nodes.push(uglyNodes[i]);
227+ stems.push(uglyStems[i]);
228+ parents.push(uglyParents[i]);
229+ size++;
230+ } else {
231+ badNodes.push(uglyNodes[i]);
232+ }
233+ }
234+ return badNodes;
235 }
236
237- // If exactMatch, return the node on top of the specified stem.
238- // If !exactMatch, return the node with the highest index for stem <= the returned node stem
239+ // Returns the n'th node when traversing one or more stems from the
240+ // top down. When exactMatch, only the specified stem is traversed, and
241+ // when !exactMatch the specified stem and all higher stems are traversed.
242+ //
243+ // Returns null if no matching node was found.
244 //
245 // Default value for stem: 0
246 // Default value for exactMatch: false
247+ // Default value for n: 0 (first node)
248 //
249- // Returns null if no matching node was found.
250- this.top = function(stem, exactMatch) {
251+ // Calling top() with no parameters returns top(0, false, 0) which is the
252+ // last node that was added to the tree.
253+ this.top = function(stem, exactMatch, n) {
254 stem = typeof stem !== 'undefined' ? stem : 0
255 exactMatch = typeof exactMatch !== 'undefined' ? exactMatch : false
256+ n = typeof n !== 'undefined' ? n : 0
257
258 var st;
259- for (var i = size-1; i >= 0; i--) {
260+ var count = n;
261+ for (var i = size - 1; i >= 0; i--) {
262 st = stems[i];
263 if ((exactMatch && st === stem) || (!exactMatch && st >= stem)) {
264+ count--;
265+ }
266+ if (count < 0) {
267 return nodes[i];
268 }
269 }
270@@ -115,7 +174,9 @@
271 var i = nodes.indexOf(node);
272 if (i === -1) {
273 throw "Specified node not found in tree.";
274+ } else if (i === 0) {
275+ throw "Root node has no parent node.";
276 }
277- return nodes[parents[i]];
278+ return parents[i];
279 }
280 }
281
282=== modified file 'modules/Ubuntu/Components/Themes/Ambiance/1.3/PageHeadStyle.qml'
283--- modules/Ubuntu/Components/Themes/Ambiance/1.3/PageHeadStyle.qml 2015-06-25 23:49:33 +0000
284+++ modules/Ubuntu/Components/Themes/Ambiance/1.3/PageHeadStyle.qml 2015-07-20 22:43:59 +0000
285@@ -268,16 +268,26 @@
286 objectName: "backButton"
287
288 iconName: "back"
289- visible: styledItem.pageStack !== null &&
290- styledItem.pageStack !== undefined &&
291- styledItem.pageStack.depth > 1 &&
292- !headerStyle.config.backAction
293+ property bool stackBack: styledItem.pageStack !== null &&
294+ styledItem.pageStack !== undefined &&
295+ styledItem.pageStack.depth > 1
296+
297+ // MultiColumnView adds the following properties: multiColumn, page, showBackButton.
298+ property bool treeBack: styledItem.hasOwnProperty("multiColumn") &&
299+ styledItem.showBackButton
300+
301+ visible: !headerStyle.config.backAction && (stackBack || treeBack)
302
303 text: "back"
304 color: headerStyle.config.foregroundColor
305
306 onTriggered: {
307- styledItem.pageStack.pop();
308+ if (stackBack) {
309+ styledItem.pageStack.pop();
310+ } else {
311+ // treeBack
312+ styledItem.multiColumn.removePages(styledItem.page);
313+ }
314 }
315 }
316
317
318=== modified file 'tests/unit_x11/tst_components/tst_multicolumnheader.qml'
319--- tests/unit_x11/tst_components/tst_multicolumnheader.qml 2015-07-07 14:21:26 +0000
320+++ tests/unit_x11/tst_components/tst_multicolumnheader.qml 2015-07-20 22:43:59 +0000
321@@ -23,6 +23,9 @@
322 width: units.gu(120)
323 height: units.gu(71)
324
325+ // 2 on desktop, 1 on phone.
326+ property int columns: width >= units.gu(80) ? 2 : 1
327+
328 MultiColumnView {
329 id: multiColumnView
330 width: parent.width
331@@ -69,6 +72,11 @@
332 margins: units.gu(2)
333 }
334 color: "orange"
335+ Button {
336+ anchors.centerIn: parent
337+ text: "right"
338+ onTriggered: multiColumnView.addPageToNextColumn(leftPage, rightPage)
339+ }
340 }
341 }
342 Page {
343@@ -80,6 +88,11 @@
344 margins: units.gu(2)
345 }
346 color: "green"
347+ Button {
348+ anchors.centerIn: parent
349+ text: "Another page!"
350+ onTriggered: multiColumnView.addPageToCurrentColumn(rightPage, sectionsPage)
351+ }
352 }
353 }
354 Page {
355@@ -96,23 +109,33 @@
356 }
357 }
358 }
359+
360 UbuntuTestCase {
361 when: windowShown
362
363+ function resize_single_column_width() {
364+ multiColumnView.width = units.gu(40);
365+ }
366+
367+ // resize to use the full window width
368+ function resize_full_width() {
369+ multiColumnView.width = root.width;
370+ }
371+
372 function get_number_of_columns() {
373 var body = findChild(multiColumnView, "body");
374 return body.children.length;
375 }
376
377- function get_header(index) {
378- return findChild(multiColumnView, "Header" + index);
379+ function get_header(column) {
380+ return findChild(multiColumnView, "Header" + column);
381 }
382
383 function get_number_of_headers() {
384 // FIXME: With only one column, revert to using the AppHeader
385- // so multiColumnView sill not include any headers.
386+ // so multiColumnView will not include any headers.
387 var numHeaders = 0;
388- var header = findChild(multiColumnView, "Header0");
389+ var header = get_header(0);
390 verify(header !== null, "No header found!");
391 while (header !== null) {
392 numHeaders++;
393@@ -121,22 +144,37 @@
394 return numHeaders;
395 }
396
397+ function get_back_button_visible(column) {
398+ var header = get_header(column);
399+ var back_button = findChild(header, "backButton");
400+ return back_button.visible;
401+ }
402+
403 function cleanup() {
404- multiColumnView.width = root.width;
405- multiColumnView.height = root.height;
406 multiColumnView.removePages(rootPage);
407+ resize_full_width();
408 }
409
410- function test_number_of_headers_equals_number_of_columns() {
411- multiColumnView.width = units.gu(40);
412- compare(get_number_of_columns(), 1, "Number of columns is not 1.");
413- compare(get_number_of_headers(), 1, "Number of headers is not 1.");
414- multiColumnView.width = root.width;
415+ function test_number_of_headers_equals_number_of_columns_wide() {
416+ if (root.columns !== 2) {
417+ skip("Only for wide view.");
418+ }
419 compare(get_number_of_columns(), 2, "Number of columns is not 2.");
420 compare(get_number_of_headers(), 2, "Number of headers is not 2.");
421 }
422
423- function test_header_configuration_equals_column_page_configuration() {
424+ function test_number_of_headers_equals_number_of_columns_narrow() {
425+ if (root.columns !== 1) {
426+ resize_single_column_width();
427+ }
428+ compare(get_number_of_columns(), 1, "Number of columns is not 1 on narrow screen.");
429+ compare(get_number_of_headers(), 1, "Number of headers is not 1 on narrow screen.");
430+ }
431+
432+ function test_header_configuration_equals_column_page_configuration_wide() {
433+ if (root.columns !== 2) {
434+ skip("Only for wide view.");
435+ }
436 compare(get_number_of_headers(), 2, "Number of headers is not 2 initially.");
437 compare(get_header(0).config, rootPage.head,
438 "First column header is not initialized with primaryPage header config.");
439@@ -162,9 +200,33 @@
440 "Second column header is not reverted properly.");
441 }
442
443+ function test_header_configuration_equals_column_page_configuration_narrow() {
444+ if (root.columns !== 1) {
445+ resize_single_column_width();
446+ }
447+ compare(get_number_of_headers(), 1, "Number of headers is not 1.");
448+ compare(get_header(0).config, rootPage.head,
449+ "First column header is not initialized with primaryPage header config.");
450+
451+ multiColumnView.addPageToCurrentColumn(rootPage, leftPage);
452+ compare(get_header(0).config, leftPage.head,
453+ "Single column header is not updated properly.");
454+ multiColumnView.removePages(leftPage);
455+ compare(get_header(0).config, rootPage.head,
456+ "Single column header is not reverted properly.");
457+
458+ multiColumnView.addPageToNextColumn(rootPage, rightPage);
459+ compare(get_header(0).config, rightPage.head,
460+ "Single column header is not updated properly when adding to next column.");
461+ multiColumnView.removePages(rightPage);
462+ compare(get_header(0).config, rootPage.head,
463+ "Single column header is not reverted properly after adding to next column.");
464+ }
465+
466 function test_header_title_for_external_page() {
467 multiColumnView.addPageToNextColumn(rootPage, Qt.resolvedUrl("MyExternalPage.qml"));
468- compare(get_header(1).config.title, "Page from QML file",
469+ var n = root.columns === 2 ? 1 : 0
470+ compare(get_header(n).config.title, "Page from QML file",
471 "Adding external Page does not update the header title.");
472 }
473
474@@ -196,5 +258,102 @@
475 " height is not correctly reverted after removing Page with sections.");
476 }
477 }
478+
479+ function test_back_button_wide() {
480+ if (root.columns !== 2) {
481+ skip("Only for wide view.");
482+ }
483+ // A is the first column, B is the second column.
484+ // A:i, B:j = i pages in A, j pages in B.
485+
486+ // primary page has no back button
487+ // A:1, B:0
488+ compare(get_back_button_visible(0), false,
489+ "Back button is visible for primary page.");
490+ multiColumnView.addPageToCurrentColumn(rootPage, leftPage);
491+ // A:2, B:0
492+ compare(get_back_button_visible(0), true,
493+ "Adding page 2 to column A does not show back button.");
494+
495+ multiColumnView.removePages(leftPage);
496+ // A:1, B:0
497+ compare(get_back_button_visible(0), false,
498+ "Removing page 2 from column A does not hide back button.");
499+
500+ multiColumnView.addPageToNextColumn(rootPage, rightPage);
501+ // A:1, B:1
502+ compare(get_back_button_visible(0), false,
503+ "Adding page 1 to column B shows back button in column A.");
504+ compare(get_back_button_visible(1), false,
505+ "Adding page 1 to column B shows back button in column B.");
506+
507+ multiColumnView.addPageToCurrentColumn(rootPage, leftPage);
508+ // A:2, B:1
509+ compare(get_back_button_visible(0), true,
510+ "Adding page 2 to column A not show back button when column B has a page.");
511+ compare(get_back_button_visible(1), false,
512+ "Adding page 2 to column A shows back button in column B.");
513+ multiColumnView.removePages(leftPage);
514+ // A:1, B:1
515+
516+ multiColumnView.addPageToCurrentColumn(rightPage, sectionsPage);
517+ // A:1, B:2
518+ compare(get_back_button_visible(0), false,
519+ "Adding page 2 to column B shows back button in column A.");
520+ compare(get_back_button_visible(1), true,
521+ "Adding page 2 to column B does not show back button in column B.");
522+
523+ multiColumnView.addPageToCurrentColumn(rootPage, leftPage);
524+ // A:2, B:2
525+ compare(get_back_button_visible(0), true,
526+ "Adding page 2 to column A does not show back button in column A when column B has 2 pages.");
527+ compare(get_back_button_visible(1), true,
528+ "Adding page 2 to column A hides back button in column B.");
529+
530+ multiColumnView.removePages(sectionsPage);
531+ // A:2, B:1
532+ compare(get_back_button_visible(0), true,
533+ "Removing page 2 from column B hides back button in column A.");
534+ compare(get_back_button_visible(1), false,
535+ "Removing page 2 from column B does not hide back button when column A has 2 pages.");
536+
537+ // A weird case that I encountered with manual testing:
538+ multiColumnView.removePages(rootPage);
539+ // A:1, B:0
540+ multiColumnView.addPageToNextColumn(rootPage, Qt.resolvedUrl("MyExternalPage.qml"));
541+ // A:1, B:1
542+ multiColumnView.addPageToCurrentColumn(rootPage, leftPage);
543+ // A:2, B:1
544+ multiColumnView.addPageToNextColumn(rootPage, rightPage);
545+ // A:2, B:2
546+ multiColumnView.addPageToCurrentColumn(rightPage, sectionsPage);
547+ // A:2, B:3
548+ compare(get_back_button_visible(0), true,
549+ "No back button on the left with multiple pages per column.");
550+ compare(get_back_button_visible(1), true,
551+ "No back button on the right with multiple pages per column.");
552+ }
553+
554+ function test_back_button_narrow() {
555+ if (root.columns !== 1) {
556+ resize_single_column_width();
557+ }
558+
559+ compare(get_back_button_visible(0), false,
560+ "Back button is visible for primary page.");
561+ multiColumnView.addPageToCurrentColumn(rootPage, leftPage);
562+ compare(get_back_button_visible(0), true,
563+ "No back button visible with two pages in single column.");
564+ multiColumnView.removePages(leftPage);
565+ compare(get_back_button_visible(0), false,
566+ "Back button remains visible after removing second page from column.");
567+
568+ multiColumnView.addPageToNextColumn(rootPage, rightPage);
569+ compare(get_back_button_visible(0), true,
570+ "No back button visible after pushing to next column when viewing single column.");
571+ multiColumnView.removePages(rightPage);
572+ compare(get_back_button_visible(0), false,
573+ "Back button remains visible after removing page from following column.");
574+ }
575 }
576 }
577
578=== modified file 'tests/unit_x11/tst_components/tst_multicolumnview.qml'
579--- tests/unit_x11/tst_components/tst_multicolumnview.qml 2015-07-14 09:42:53 +0000
580+++ tests/unit_x11/tst_components/tst_multicolumnview.qml 2015-07-20 22:43:59 +0000
581@@ -20,12 +20,15 @@
582 import Ubuntu.Components 1.3
583
584 MainView {
585- id: test
586+ id: root
587 width: units.gu(120)
588 height: units.gu(71)
589
590+ // 2 on desktop, 1 on phone.
591+ property int columns: width >= units.gu(80) ? 2 : 1
592+
593 MultiColumnView {
594- id: testView
595+ id: mcv
596 width: parent.width
597 height: parent.height
598
599@@ -34,6 +37,18 @@
600 Page {
601 id: page1
602 title: "Page1"
603+ Button {
604+ anchors.centerIn: parent
605+ text: "Page 2 left"
606+ }
607+
608+ Column {
609+ width: parent.width
610+ Button {
611+ text: "Page 2"
612+ onTriggered: mcv.addPageToCurrentColumn(page1, page2);
613+ }
614+ }
615 }
616 Page {
617 id: page2
618@@ -56,73 +71,93 @@
619 UbuntuTestCase {
620 when: windowShown
621
622+ function resize_single_column() {
623+ mcv.width = units.gu(40);
624+ }
625+
626+ // resize to use the full window width
627+ function resize_multiple_columns() {
628+ mcv.width = root.width;
629+ }
630+
631 function cleanup() {
632-// testView.columns = Qt.binding(function() {
633-// return test.width > units.gu(100) ? 3 : (test.width > units.gu(80) ? 2 : 1);
634-// });
635- testView.width = test.width;
636- testView.height = test.height;
637- // remove allpages
638- testView.removePages(page1);
639+ resize_multiple_columns();
640+ mcv.removePages(page1);
641 }
642
643 function test_0_API() {
644 compare(defaults.primaryPage, undefined, "primaryPage not undefined by default");
645 }
646
647- function test_add_to_first_column_data() {
648- return [
649- {tag: "null sourcePage, fail", sourcePage: null, page: page2, failMsg: "No sourcePage specified. Page will not be added."},
650- {tag: "valid sourcePage, pass", sourcePage: page1, page: page2, failMsg: ""},
651- ]
652- }
653- function test_add_to_first_column(data) {
654- if (data.failMsg != "") {
655- ignoreWarning(data.failMsg);
656- }
657-
658- testView.addPageToCurrentColumn(data.sourcePage, data.page);
659- var firstColumn = findChild(testView, "ColumnHolder0");
660- verify(firstColumn);
661- if (data.failMsg != "") {
662- expectFail(data.tag, "Fail");
663- }
664- compare(firstColumn.pageWrapper.object, data.page);
665- }
666-
667- function test_add_to_next_column_data() {
668- return [
669- {tag: "null sourcePage, fail", sourcePage: null, page: page2, failMsg: "No sourcePage specified. Page will not be added."},
670- {tag: "valid sourcePage, pass", sourcePage: page1, page: page2, failMsg: ""},
671- ]
672- }
673- function test_add_to_next_column(data) {
674- if (data.failMsg != "") {
675- ignoreWarning(data.failMsg);
676- }
677-
678- testView.addPageToNextColumn(data.sourcePage, data.page);
679- var secondColumn = findChild(testView, "ColumnHolder1");
680- verify(secondColumn);
681- if (data.failMsg != "") {
682- expectFail(data.tag, "Fail");
683- }
684- verify(secondColumn.pageWrapper);
685- }
686-
687- function test_change_primaryPage() {
688+ function test_zzz_change_primaryPage() {
689+ // this prints the warning but still changes the primary page,
690+ // so the test must be executed last not to mess up the other tests.
691 ignoreWarning("Cannot change primaryPage after completion.");
692- testView.primaryPage = page3;
693- }
694-
695- function test_add_to_same_column_when_source_page_not_in_stack() {
696- ignoreWarning("sourcePage must be added to the view to add new page.");
697- testView.addPageToCurrentColumn(page2, page3);
698- }
699-
700- function test_add_to_next_column_when_source_page_not_in_stack() {
701- ignoreWarning("sourcePage must be added to the view to add new page.");
702- testView.addPageToNextColumn(page2, page3);
703+ mcv.primaryPage = page3;
704+ }
705+
706+ function test_add_page_when_source_page_not_in_stack() {
707+ ignoreWarning("sourcePage must be added to the view to add new page.");
708+ mcv.addPageToCurrentColumn(page2, page3);
709+ ignoreWarning("sourcePage must be added to the view to add new page.");
710+ mcv.addPageToNextColumn(page2, page3);
711+ }
712+
713+ function test_add_page_with_null_sourcePage() {
714+ ignoreWarning("No sourcePage specified. Page will not be added.")
715+ mcv.addPageToCurrentColumn(null, page1);
716+ ignoreWarning("No sourcePage specified. Page will not be added.")
717+ mcv.addPageToNextColumn(null, page2);
718+ }
719+
720+ function test_add_same_page_twice() {
721+ mcv.addPageToCurrentColumn(page1, page2);
722+ mcv.addPageToCurrentColumn(page2, page3);
723+ ignoreWarning("Cannot add a Page that was already added.");
724+ mcv.addPageToCurrentColumn(page3, page2);
725+ ignoreWarning("Cannot add a Page that was already added.");
726+ mcv.addPageToNextColumn(page3, page2);
727+ }
728+
729+ function test_page_visible() {
730+ // Two columns on desktop, one on phone
731+ compare(page1.visible, true, "Primary page not initially visible.");
732+ compare(page2.visible, false, "Page 2 visible before it was added.");
733+ compare(page3.visible, false, "Page 3 visible before it was added.");
734+
735+ mcv.addPageToCurrentColumn(page1, page2);
736+ compare(page1.visible, false, "Page still visible after adding new page in current column.");
737+ compare(page2.visible, true, "Page invisible after adding it to current column.");
738+ mcv.addPageToNextColumn(page2, page3);
739+ if (root.columns === 2) {
740+ compare(page2.visible, true, "Page in first column became invisible after adding to next column.");
741+ } else { // root.columns === 1
742+ compare(page2.visible, false, "Page in single column still visible after adding page to next column.");
743+ }
744+ compare(page3.visible, true, "Page invisible after adding it to next column.");
745+
746+ // One column
747+ resize_single_column();
748+ compare(page3.visible, true, "Top page in last column invisible when resizing to one column.");
749+ compare(page2.visible, false, "Top page in first column visible when resizing to one column.");
750+
751+ mcv.removePages(page3);
752+ compare(page3.visible, false, "Page 3 visible after it was removed.");
753+ compare(page2.visible, true, "New top page in single column not visible.");
754+
755+ mcv.removePages(page1);
756+ compare(page1.visible, true, "Primary page not visible in single column.");
757+ compare(page2.visible, false, "Page 2 visible while it is not added.");
758+ mcv.addPageToNextColumn(page1, page4);
759+ compare(page1.visible, false, "Page remains visible after adding to next column in single column view.");
760+ compare(page4.visible, true, "Page added to next column with single column view is not visible.");
761+
762+ // Two columns on desktop, one on phone
763+ resize_multiple_columns();
764+ if (root.columns === 2) {
765+ compare(page1.visible, true, "Page in left column did not become visible when switching to multi-column view.");
766+ compare(page4.visible, true, "Page in right column became invisible when switching to multi-column view.");
767+ }
768 }
769 }
770 }

Subscribers

People subscribed via source and target branches

to all changes: