Merge lp:~zsombi/ubuntu-ui-toolkit/aplReplacePrimaryPage into lp:ubuntu-ui-toolkit/staging

Proposed by Zsombor Egri
Status: Merged
Approved by: Cris Dywan
Approved revision: 1668
Merged at revision: 1673
Proposed branch: lp:~zsombi/ubuntu-ui-toolkit/aplReplacePrimaryPage
Merge into: lp:ubuntu-ui-toolkit/staging
Prerequisite: lp:~zsombi/ubuntu-ui-toolkit/aplPrimaryPageAsync
Diff against target: 466 lines (+179/-138)
2 files modified
src/Ubuntu/Components/1.3/AdaptivePageLayout.qml (+52/-30)
tests/unit_x11/tst_components/tst_adaptivepagelayout.qml (+127/-108)
To merge this branch: bzr merge lp:~zsombi/ubuntu-ui-toolkit/aplReplacePrimaryPage
Reviewer Review Type Date Requested Status
Cris Dywan Approve
PS Jenkins bot continuous-integration Approve
Review via email: mp+272868@code.launchpad.net

Commit message

Make primaryPage and primaryPageSource replaceable after component completion.

Description of the change

Make primaryPage and primaryPageSource replaceable after component completion.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1666. By Zsombor Egri

documentation fixed

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Cris Dywan (kalikiana) wrote :

+ The property can only hold a Page instance. When changed runtime (not by the

...changed at runtime...

+ be cerated asynchronously and the instance will be reported through

...be created asynchronously...

+ if (source === null) {
+ if (source === undefined) {

These conditionals are hard to make sense of for me even after checking the two places the function can be called from. I'm thinking it might make more sense to dissolve the function so it's easier to understand given there is very little code to be shared.

+ function findPageFromLayoutWithProperty(apl, property, value) {

How about making this objectName only? That'd be more consistent and there's no chance of killing time by retroactively investigating localization-related failures because of labels.
Also, may be worth adding to UbuntuTestCase?

review: Needs Fixing
Revision history for this message
Zsombor Egri (zsombi) wrote :

> + The property can only hold a Page instance. When changed runtime (not by the
>
> ...changed at runtime...
>
> + be cerated asynchronously and the instance will be reported through
>
> ...be created asynchronously...
>
> + if (source === null) {
> + if (source === undefined) {
>
> These conditionals are hard to make sense of for me even after checking the
> two places the function can be called from. I'm thinking it might make more
> sense to dissolve the function so it's easier to understand given there is
> very little code to be shared.
>
> + function findPageFromLayoutWithProperty(apl, property, value) {
>
> How about making this objectName only? That'd be more consistent and there's
> no chance of killing time by retroactively investigating localization-related
> failures because of labels.

Yep, makes sense.

> Also, may be worth adding to UbuntuTestCase?

Well... should we have APL specific function in UbuntuTestCase? The function itself looks in the body of the APL and not in the APL children itself. Pages can be added as child to APL (which are then forwarded to the internal hiddenPages) so we are not interested of those...

1667. By Zsombor Egri

review comments applied

1668. By Zsombor Egri

findPageFromLayoutWithProperty renamed into findPageFromLayout

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Cris Dywan (kalikiana) wrote :

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 'src/Ubuntu/Components/1.3/AdaptivePageLayout.qml'
2--- src/Ubuntu/Components/1.3/AdaptivePageLayout.qml 2015-09-30 09:46:03 +0000
3+++ src/Ubuntu/Components/1.3/AdaptivePageLayout.qml 2015-10-02 05:22:37 +0000
4@@ -42,13 +42,11 @@
5 add the page to the leftmost column of the view.
6
7 The primary page, the very first page must be specified either through the
8- \l primaryPage or \l primaryPageSource properties. The properties cannot be
9- changed after component completion. \l primaryPage can only hold a Page instance,
10- \l primaryPageSource can either be a Component or a url to a document defining
11- a Page. This page cannot be removed from the view. \l primaryPageSource has
12- precedence over \l primaryPage and will create the Page asynchronously. The
13- page instance will be reported through \l primaryPage property and will replace
14- any previous value set to that property.
15+ \l primaryPage or \l primaryPageSource properties. \l primaryPage can only
16+ hold a Page instance, \l primaryPageSource can either be a Component or a
17+ url to a document defining a Page. \l primaryPageSource has precedence over
18+ \l primaryPage, and when set it will report the loaded Page through \l primaryPage
19+ property, and will replace any value set into that property.
20
21 \qml
22 import QtQuick 2.4
23@@ -188,17 +186,18 @@
24 /*!
25 The property holds the first Page which will be added to the view. If the
26 view has more than one column, the page will be added to the leftmost column.
27- The property can hold only a Page instance. The property cannot be changed after
28- component completion.
29+ The property can only hold a Page instance. When changed runtime (not by the
30+ AdaptivePageLayout component itself), the \l primaryPageSource property
31+ will be reset.
32 */
33 property Page primaryPage
34
35 /*!
36 The property specifies the source of the primaryPage in case the primary
37 page is created from a Component or loaded from an external document. It
38- has precedence over \l primaryPage and cannot be changed after component
39- completion. The page specified in this way will be cerated asynchronously
40- and the instance will be reported through \l primaryPage property.
41+ has precedence over \l primaryPage. The page specified in this way will
42+ be cerated asynchronously and the instance will be reported through
43+ \l primaryPage property.
44 */
45 property var primaryPageSource
46
47@@ -322,12 +321,7 @@
48 pages will be removed.
49 */
50 function removePages(page) {
51- var nodeToRemove = d.getWrapper(page);
52- var removedNodes = d.tree.chop(nodeToRemove, page != layout.primaryPage);
53- for (var i = removedNodes.length-1; i >= 0; i--) {
54- var node = removedNodes[i];
55- d.updatePageForColumn(node.column);
56- }
57+ d.removeAllPages(page, page != layout.primaryPage);
58 }
59
60 /*
61@@ -345,26 +339,34 @@
62 d.createPrimaryPage(primaryPageSource);
63 } else if (primaryPage) {
64 d.createPrimaryPage(primaryPage);
65- } else {
66- console.warn("No primary page set. No pages can be added without a primary page.");
67 }
68 d.completed = true;
69 }
70 onPrimaryPageChanged: {
71- if (d.completed && !d.internalUpdate) {
72- console.warn("Cannot change primaryPage after completion.");
73- d.internalPropertyUpdate("primaryPage", d.lastPrimaryPage);
74+ if (!d.completed || d.internalUpdate) {
75 return;
76 }
77- d.lastPrimaryPage = primaryPage;
78+ // reset the primaryPageSource
79+ d.internalPropertyUpdate("primaryPageSource", undefined);
80+ // clear the layout
81+ d.purgeLayout();
82+ // add the new page if valid
83+ if (primaryPage !== null) {
84+ d.createPrimaryPage(primaryPage);
85+ }
86 }
87 onPrimaryPageSourceChanged: {
88- if (d.completed && !d.internalUpdate) {
89- console.warn("Cannot change primaryPageSource after completion.");
90- d.internalPropertyUpdate("primaryPageSource", d.lastPrimaryPageSource);
91+ if (!d.completed || d.internalUpdate) {
92 return;
93 }
94- d.lastPrimaryPageSource = primaryPageSource;
95+ // remove all pages first
96+ d.purgeLayout();
97+ // create the new primary page if a valid component is specified
98+ if (primaryPageSource) {
99+ d.createPrimaryPage(primaryPageSource);
100+ } else {
101+ d.internalPropertyUpdate("primaryPage", null);
102+ }
103 }
104
105 onLayoutsChanged: {
106@@ -389,8 +391,7 @@
107 (activeLayout ? activeLayout.data.length : 1)
108 property PageColumnsLayout activeLayout: null
109 property list<PageColumnsLayout> prevLayouts
110- property Page lastPrimaryPage
111- property var lastPrimaryPageSource
112+ property Page prevPrimaryPage
113
114 /*! internal */
115 onColumnsChanged: {
116@@ -416,10 +417,20 @@
117 wrapper.incubator.onStatusChanged = function (status) {
118 if (status == Component.Ready) {
119 internalPropertyUpdate("primaryPage", wrapper.incubator.object);
120+ prevPrimaryPage = wrapper.incubator.object;
121 }
122 }
123 } else {
124 finalizeAddingPage(wrapper);
125+ prevPrimaryPage = wrapper.object;
126+ }
127+ }
128+
129+ // remove all pages, including primaryPage
130+ function purgeLayout() {
131+ if (prevPrimaryPage) {
132+ removeAllPages(prevPrimaryPage, true);
133+ prevPrimaryPage = null;
134 }
135 }
136
137@@ -514,6 +525,17 @@
138 return newWrapper.incubator;
139 }
140
141+ // removes all pages from the layout, and may include the page itself
142+ function removeAllPages(page, inclusive) {
143+ inclusive = typeof inclusive !== 'undefined' ? inclusive : true;
144+ var nodeToRemove = d.getWrapper(page);
145+ var removedNodes = d.tree.chop(nodeToRemove, inclusive);
146+ for (var i = removedNodes.length-1; i >= 0; i--) {
147+ var node = removedNodes[i];
148+ updatePageForColumn(node.column);
149+ }
150+ }
151+
152 // update the page for the specified column
153 function updatePageForColumn(column) {
154 var effectiveColumn = MathUtils.clamp(column, 0, d.columns - 1);
155
156=== modified file 'tests/unit_x11/tst_components/tst_adaptivepagelayout.qml'
157--- tests/unit_x11/tst_components/tst_adaptivepagelayout.qml 2015-09-29 10:37:46 +0000
158+++ tests/unit_x11/tst_components/tst_adaptivepagelayout.qml 2015-10-02 05:22:37 +0000
159@@ -27,83 +27,79 @@
160 // 2 on desktop, 1 on phone.
161 property int columns: width >= units.gu(80) ? 2 : 1
162
163- AdaptivePageLayout {
164- id: layout
165- width: parent.width
166- height: parent.height
167-
168- primaryPage: page1
169-
170- Page {
171- id: page1
172- title: "Page1"
173-
174- Column {
175- anchors.centerIn: parent
176- width: childrenRect.width
177- Button {
178- text: "Page 2 left"
179- onTriggered: layout.addPageToCurrentColumn(page1, page2)
180- }
181- Button {
182- text: "Page 3 right"
183- onTriggered: layout.addPageToNextColumn(page1, page3);
184- }
185- }
186- }
187- Page {
188- id: page2
189- title: "Page2"
190- }
191- Page {
192- id: page3
193- title: "Page3"
194- }
195- Page {
196- id: page4
197- title: "Page4"
198- }
199- }
200-
201- AdaptivePageLayout {
202- id: defaults
203+ Column {
204+ anchors.fill: parent
205+ AdaptivePageLayout {
206+ id: layout
207+ width: parent.width
208+ height: parent.height / 2
209+
210+ primaryPage: page1
211+
212+ Page {
213+ id: page1
214+ objectName: title
215+ title: "Page1"
216+
217+ Column {
218+ anchors.centerIn: parent
219+ width: childrenRect.width
220+ Button {
221+ text: "Page 2 left"
222+ onTriggered: layout.addPageToCurrentColumn(page1, page2)
223+ }
224+ Button {
225+ text: "Page 3 right"
226+ onTriggered: layout.addPageToNextColumn(page1, page3);
227+ }
228+ }
229+ }
230+ Page {
231+ id: page2
232+ objectName: title
233+ title: "Page2"
234+ }
235+ Page {
236+ id: page3
237+ objectName: title
238+ title: "Page3"
239+ }
240+ Page {
241+ id: page4
242+ objectName: title
243+ title: "Page4"
244+ }
245+ }
246+ AdaptivePageLayout {
247+ id: defaults
248+ width: parent.width
249+ height: parent.height / 2
250+ Page {
251+ id: otherPage1
252+ objectName: title
253+ title: "Page1"
254+ }
255+ Page {
256+ id: otherPage2
257+ objectName: title
258+ title: "Page2"
259+ }
260+ Page {
261+ id: otherPage3
262+ objectName: title
263+ title: "Page3"
264+ }
265+ }
266 }
267
268 Component {
269 id: pageComponent
270 Page {
271+ objectName: title
272 title: "DynamicPage"
273 }
274 }
275
276- Component {
277- id: aplComponent
278- AdaptivePageLayout {
279- width: units.gu(40)
280- height: units.gu(50)
281- primaryPageSource: pageComponent
282- }
283- }
284-
285- Component {
286- id: aplDocument
287- AdaptivePageLayout {
288- width: units.gu(40)
289- height: units.gu(50)
290- primaryPageSource: Qt.resolvedUrl("MyExternalPage.qml")
291- }
292- }
293-
294- Component {
295- id: aplPrecedence
296- AdaptivePageLayout {
297- width: units.gu(40)
298- height: units.gu(50)
299- primaryPage: page1
300- primaryPageSource: Qt.resolvedUrl("MyExternalPage.qml")
301- }
302- }
303-
304 UbuntuTestCase {
305 id: testCase
306 when: windowShown
307@@ -115,6 +111,10 @@
308 target: testCase
309 signalName: "pageLoaded"
310 }
311+ SignalSpy {
312+ id: primaryPageSpy
313+ signalName: "primaryPageChanged"
314+ }
315
316 function resize_single_column() {
317 layout.width = units.gu(40);
318@@ -135,29 +135,38 @@
319 return apl.__d.getWrapper(page);
320 }
321
322+ function findPageFromLayout(apl, objectName) {
323+ var body = findChild(apl, "body");
324+ verify(body);
325+ return findChild(body, objectName);
326+ }
327+
328 function cleanup() {
329 page1.title = "Page1";
330 page2.title = "Page2";
331 page3.title = "Page3";
332 page4.title = "Page4";
333 loadedSpy.clear();
334+ primaryPageSpy.clear();
335+ primaryPageSpy.target = null;
336 resize_multiple_columns();
337- layout.removePages(page1);
338+ layout.removePages(layout.primaryPage);
339+ defaults.primaryPage = null;
340+ wait(200);
341 }
342
343 function initTestCase() {
344 compare(defaults.primaryPage, null, "primaryPage not null by default");
345 compare(defaults.primaryPageSource, undefined, "primaryPageSource not set by default");
346 compare(defaults.layouts.length, 0, "no layouts by default");
347- compare(defaults.columns, 1, "1 column as default");
348+ compare(defaults.columns, columns, columns + " column(s) as default");
349 }
350
351 function test_change_primaryPage() {
352- // this prints the warning but still changes the primary page,
353- // so the test must be executed last not to mess up the other tests.
354- ignoreWarning("Cannot change primaryPage after completion.");
355- layout.primaryPage = page3;
356- verify(layout.primaryPage != page3, "primaryPage value was changed");
357+ defaults.primaryPage = otherPage1;
358+ defaults.addPageToCurrentColumn(defaults.primaryPage, otherPage2);
359+ defaults.primaryPage = otherPage3;
360+ verify(!findPageFromLayout(defaults, "Page2"), "Page2 still in the view!");
361 }
362
363 function test_add_page_when_source_page_not_in_stack() {
364@@ -333,10 +342,10 @@
365 }
366
367 var testPage = testHolder.pageWrapper.object;
368- var prevPageActive = false
369+ var prevPageActive = false;
370 var incubator = data.nextColumn
371 ? layout.addPageToNextColumn(data.sourcePage, data.page)
372- : layout.addPageToCurrentColumn(data.sourcePage, data.page);;
373+ : layout.addPageToCurrentColumn(data.sourcePage, data.page);
374 verify(incubator);
375 compare(testHolder.pageWrapper.object, testPage);
376 incubator.onStatusChanged = function (status) {
377@@ -351,48 +360,58 @@
378
379 function test_primaryPageSource_bug1499179_data() {
380 return [
381- {tag: "Component", test: aplComponent},
382- {tag: "Document", test: aplDocument},
383+ {tag: "Component", test: pageComponent},
384+ {tag: "Document", test: Qt.resolvedUrl("MyExternalPage.qml")},
385 ];
386 }
387 function test_primaryPageSource_bug1499179(data) {
388- var apl = data.test.createObject(root);
389- verify(apl);
390- tryCompareFunction(function () { return apl.primaryPage != null }, true, 1500);
391- apl.visible = false;
392- apl = null;
393+ primaryPageSpy.target = defaults;
394+ defaults.primaryPageSource = data.test;
395+ primaryPageSpy.wait();
396 }
397
398- function test_primaryPageSource_not_set_twice_data() {
399+ function test_change_primaryPageSource_data() {
400 return [
401- {tag: "Component", test: aplComponent, nextValue: null},
402- {tag: "Document", test: aplDocument, nextValue: null},
403+ {tag: "Component", test: pageComponent, nextValue: Qt.resolvedUrl("MyExternalPage.qml")},
404+ {tag: "Document", test: Qt.resolvedUrl("MyExternalPage.qml"), nextValue: pageComponent},
405 ];
406 }
407- function test_primaryPageSource_not_set_twice(data) {
408- var apl = data.test.createObject(root);
409- verify(apl);
410- tryCompareFunction(function () { return apl.primaryPage != null }, true, 1500);
411- ignoreWarning("Cannot change primaryPageSource after completion.");
412- apl.primaryPageSource = data.nextValue;
413- verify(apl.primaryPageSource != data.nextValue, "property value changed!");
414- apl.visible = false;
415- apl = null;
416- }
417-
418- SignalSpy {
419- id: primaryPageSpy
420- signalName: "primaryPageChanged"
421+ function test_change_primaryPageSource(data) {
422+ primaryPageSpy.target = defaults;
423+ verify(defaults.primaryPage == null);
424+ verify(defaults.primaryPageSource == undefined);
425+ defaults.primaryPageSource = data.test;
426+ primaryPageSpy.wait(400);
427+ // add some pages
428+ defaults.addPageToCurrentColumn(defaults.primaryPage, otherPage2);
429+ // then replace the primaryPageSource
430+ primaryPageSpy.clear();
431+ defaults.primaryPageSource = data.nextValue;
432+ primaryPageSpy.wait(400);
433+ // look after page2
434+ verify(!findPageFromLayout(defaults, "Page2"), "Page2 still in the view!");
435 }
436
437 function test_primaryPageSource_precedence_over_primaryPage() {
438- var apl = aplPrecedence.createObject(root);
439- primaryPageSpy.target = apl;
440- verify(apl);
441- primaryPageSpy.wait();
442- verify(apl.primaryPage.title != page1.title, "primaryPage has not been overloaded by primaryPageSource");
443- apl.visible = false;
444- apl = null;
445+ primaryPageSpy.target = defaults;
446+ defaults.primaryPage = otherPage1;
447+ primaryPageSpy.wait(400);
448+ // now set a value to primaryPageSource
449+ primaryPageSpy.clear();
450+ defaults.primaryPageSource = pageComponent;
451+ primaryPageSpy.wait(400);
452+ }
453+
454+ function test_primaryPage_change_clears_primaryPageSource() {
455+ primaryPageSpy.target = defaults;
456+ defaults.primaryPageSource = pageComponent;
457+ primaryPageSpy.wait(400);
458+ compare(defaults.primaryPage.title, "DynamicPage", "DynamicPage not set as primaryPage");
459+ // now set a value to primaryPage
460+ primaryPageSpy.clear();
461+ defaults.primaryPage = otherPage1;
462+ primaryPageSpy.wait(400);
463+ compare(defaults.primaryPageSource, undefined, "primaryPageSource must be cleared");
464 }
465 }
466 }

Subscribers

People subscribed via source and target branches