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

Proposed by Zsombor Egri
Status: Merged
Merge reported by: Cris Dywan
Merged at revision: not available
Proposed branch: lp:~zsombi/ubuntu-ui-toolkit/multicolumnview
Merge into: lp:ubuntu-ui-toolkit/staging
Diff against target: 1172 lines (+1033/-4)
14 files modified
components.api (+15/-0)
modules/Ubuntu/Components/1.2/stack.js (+1/-0)
modules/Ubuntu/Components/1.3/ColumnMetrics.qml (+52/-0)
modules/Ubuntu/Components/1.3/MultiColumnView.qml (+569/-0)
modules/Ubuntu/Components/1.3/Page.qml (+1/-0)
modules/Ubuntu/Components/1.3/PageHeadConfiguration.qml (+2/-0)
modules/Ubuntu/Components/1.3/PageWrapper.qml (+37/-0)
modules/Ubuntu/Components/1.3/stack.js (+75/-0)
modules/Ubuntu/Components/Themes/Ambiance/1.3/PageHeadStyle.qml (+2/-2)
modules/Ubuntu/Components/qmldir (+2/-0)
tests/resources/navigation/MyCustomPage.qml (+1/-1)
tests/resources/navigation/SplitViewStack.qml (+137/-0)
tests/unit_x11/tst_components/tst_components.pro (+2/-1)
tests/unit_x11/tst_components/tst_multicolumnview.qml (+137/-0)
To merge this branch: bzr merge lp:~zsombi/ubuntu-ui-toolkit/multicolumnview
Reviewer Review Type Date Requested Status
Tim Peeters Needs Fixing
PS Jenkins bot continuous-integration Approve
Review via email: mp+261703@code.launchpad.net

Commit message

MultiColumnView component

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
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
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Tim Peeters (tpeeters) wrote :

23 + default readonly property QtObject data

why?

Revision history for this message
Tim Peeters (tpeeters) wrote :

41 === modified file 'modules/Ubuntu/Components/1.2/stack.js'
42 --- modules/Ubuntu/Components/1.2/stack.js 2015-04-30 08:32:44 +0000
43 +++ modules/Ubuntu/Components/1.2/stack.js 2015-06-17 14:25:30 +0000
44 @@ -41,4 +41,5 @@
45 if (this.size() < 1) return undefined;
46 return elements[elements.length-1];
47 }
48 +
49 }

undo

Revision history for this message
Tim Peeters (tpeeters) wrote :

82 + /*!
83 + 1-based value identifying the column the metrics to be applied to.
84 + */
85 + property int column

Why not start at 0, as is common?

Revision history for this message
Tim Peeters (tpeeters) wrote :

87 + /*!
88 + Specifies whether the width of the column should fill the available space
89 + of the MultiColumnView column or not. Defaults to \a false.
90 + */
91 + property bool fillWidth: false

if fillWidth = true, then defaultColumnWidth will be used (if that is within the borders of the min and max width for the colum)? Can you describe that explicitly here?

Revision history for this message
Tim Peeters (tpeeters) wrote :

144 + MultiColumnView stores pages added in a tree. Pages are added relative to a
145 + given page, either as sibling (\l addPageToCurrentColumn) or as child
146 + (\l addPageToNextColumn). This means that removing a non-leaf page from the Page
147 + tree will remove all its children from the page tree.

I don't agree with the terminology here. They are both children, in either of the columns.

Revision history for this message
Tim Peeters (tpeeters) wrote :

152 + the column next to the source page. Giving a null value to the source page will
153 + add the page to the leftmost column of the view.

No, adding a null value is forbidden.

Revision history for this message
Tim Peeters (tpeeters) wrote :

160 + \note Unlike PageStack, the component does not fill its parent content.

why not?

review: Needs Information
Revision history for this message
Tim Peeters (tpeeters) wrote :

I'm reading this - http://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html and they have GridLayout, RowLayout and ColumnLayout. I'm wondering whether we should name our component MultiColumnView (similar to our MainView), or we adapt naming similar to that used in Layouts, so MultiColumnLayout?

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

> 23 + default readonly property QtObject data
>
> why?
That wasn't me, it is the qmlapicheck, sorry.

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

> 41 === modified file 'modules/Ubuntu/Components/1.2/stack.js'
> 42 --- modules/Ubuntu/Components/1.2/stack.js 2015-04-30 08:32:44
> +0000
> 43 +++ modules/Ubuntu/Components/1.2/stack.js 2015-06-17 14:25:30
> +0000
> 44 @@ -41,4 +41,5 @@
> 45 if (this.size() < 1) return undefined;
> 46 return elements[elements.length-1];
> 47 }
> 48 +
> 49 }
>
> undo

ehh... will undo soon.

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

> 82 + /*!
> 83 + 1-based value identifying the column the metrics to be applied
> to.
> 84 + */
> 85 + property int column
>
> Why not start at 0, as is common?

Well, doesn't matter, we can start from 0 then.

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

> 87 + /*!
> 88 + Specifies whether the width of the column should fill the
> available space
> 89 + of the MultiColumnView column or not. Defaults to \a false.
> 90 + */
> 91 + property bool fillWidth: false
>
> if fillWidth = true, then defaultColumnWidth will be used (if that is within
> the borders of the min and max width for the colum)? Can you describe that
> explicitly here?

if fillWidth == true => every value is omitted, just like in Layout.

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

> 160 + \note Unlike PageStack, the component does not fill its parent
> content.
>
> why not?

This was something people complained a lot in PageStack. if you want different aching, you have to set anchor.fill: undefined first, then change the anchors.

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

> I'm reading this - http://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html and
> they have GridLayout, RowLayout and ColumnLayout. I'm wondering whether we
> should name our component MultiColumnView (similar to our MainView), or we
> adapt naming similar to that used in Layouts, so MultiColumnLayout?

Not a bad idea!!!!!

Revision history for this message
Tim Peeters (tpeeters) wrote :

293 + The proeprty can hold either a Page instance, a component holding a Page

proeprty

review: Needs Fixing
Revision history for this message
Tim Peeters (tpeeters) wrote :

293 + The proeprty can hold either a Page instance, a component holding a Page
294 + or a QML document defining the Page. The property cannot be changed after
295 + component completion.
296 + */
297 + property var primaryPage

Why? What is the use case for this flexibility? So we just make it property Item primaryPage or even property Page primaryPage.

Revision history for this message
Tim Peeters (tpeeters) wrote :

300 + Specifies the number of columns in the view. A condition must be set to
301 + control the number of columns depending on the space available.

It is not a requirement that a condition is set.

Revision history for this message
Tim Peeters (tpeeters) wrote :

311 + /*!
312 + The property specifies the default width of each column. The property is
313 + applied on each column. If the \a minimumWidth specified for the column is

Not on the last column. That will use full width.

314 + bigger than this value, the minimum width will be applied.
315 + */
316 + property real defaultColumnWidth: units.gu(40)

Revision history for this message
Tim Peeters (tpeeters) wrote :

323 + is empty. Only columns requiring special handling than the default should
324 + be specified.
325 + */

+different from

Revision history for this message
Tim Peeters (tpeeters) wrote :

329 + \qmlmethod Item addPageToCurrentColumn(Item sourcePage, var page[, var properties])

this doesn't seem to do anything in the generated docs.

Revision history for this message
Tim Peeters (tpeeters) wrote :

330 + Adds a \c page to the column the \c sourcePage resides in. If \c sourcePage
331 + is null, the \c page will be added to the leftmost column. \c page can be a
332

Null is not allowed. Update docs.

Revision history for this message
Tim Peeters (tpeeters) wrote :

336 + function addPageToCurrentColumn(sourcePage, page, properties) {
363 + function addPageToNextColumn(sourcePage, page, properties) {

These share a lot of implementation. Add an internal addPageToColumn(i, sourcePage, page, properties) function that they call.

Revision history for this message
Tim Peeters (tpeeters) wrote :

409 + console.warn("There must me a minimum of one column set.");

BE

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'components.api'
2--- components.api 2015-06-02 12:37:41 +0000
3+++ components.api 2015-06-17 14:25:30 +0000
4@@ -204,6 +204,11 @@
5 function clear()
6 function QQuickMimeData* newData()
7 Ubuntu.Components.ColorUtils 0.1 1.0
8+Ubuntu.Components.ColumnMetrics 1.3: QtObject
9+ property int column
10+ property bool fillWidth
11+ property double maximumWidth
12+ property double minimumWidth
13 Ubuntu.Components.ComboButton 1.1: Button
14 property double collapsedHeight
15 default readonly property QtObject comboList
16@@ -521,6 +526,15 @@
17 Ubuntu.Components.Mouse.Priority: Enum
18 AfterItem
19 BeforeItem
20+Ubuntu.Components.MultiColumnView 1.3: PageTreeNode
21+ readonly property ColumnMetrics columnMetrics
22+ property int columns
23+ default readonly property QtObject data
24+ property double defaultColumnWidth
25+ function var addPageToCurrentColumn(var sourcePage, var page, var properties)
26+ function var addPageToNextColumn(var sourcePage, var page, var properties)
27+ function var removePages(var page)
28+ property var primaryPage
29 Ubuntu.Components.ListItems.MultiValue 1.0 0.1: Base
30 property var values
31 Ubuntu.Components.ListItems.MultiValue 1.3: Base
32@@ -605,6 +619,7 @@
33 property bool locked
34 property string preset
35 readonly property PageHeadSections sections
36+ property string title
37 property bool visible
38 Ubuntu.Components.PageHeadSections 1.1: QtObject
39 property bool enabled
40
41=== modified file 'modules/Ubuntu/Components/1.2/stack.js'
42--- modules/Ubuntu/Components/1.2/stack.js 2015-04-30 08:32:44 +0000
43+++ modules/Ubuntu/Components/1.2/stack.js 2015-06-17 14:25:30 +0000
44@@ -41,4 +41,5 @@
45 if (this.size() < 1) return undefined;
46 return elements[elements.length-1];
47 }
48+
49 }
50
51=== added file 'modules/Ubuntu/Components/1.3/ColumnMetrics.qml'
52--- modules/Ubuntu/Components/1.3/ColumnMetrics.qml 1970-01-01 00:00:00 +0000
53+++ modules/Ubuntu/Components/1.3/ColumnMetrics.qml 2015-06-17 14:25:30 +0000
54@@ -0,0 +1,52 @@
55+/*
56+ * Copyright 2015 Canonical Ltd.
57+ *
58+ * This program is free software; you can redistribute it and/or modify
59+ * it under the terms of the GNU Lesser General Public License as published by
60+ * the Free Software Foundation; version 3.
61+ *
62+ * This program is distributed in the hope that it will be useful,
63+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
64+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
65+ * GNU Lesser General Public License for more details.
66+ *
67+ * You should have received a copy of the GNU Lesser General Public License
68+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
69+ */
70+
71+import QtQuick 2.4
72+
73+/*!
74+ \qmltype ColumnMetrics
75+ \inqmlmodule Ubuntu.Components 1.3
76+ \since Ubuntu.Components 1.3
77+ \ingroup ubuntu
78+ \brief Component configuring the metrics of a column in MultiColumnView.
79+
80+ */
81+QtObject {
82+ /*!
83+ 1-based value identifying the column the metrics to be applied to.
84+ */
85+ property int column
86+
87+ /*!
88+ Specifies whether the width of the column should fill the available space
89+ of the MultiColumnView column or not. Defaults to \a false.
90+ */
91+ property bool fillWidth: false
92+
93+ /*!
94+ Specifies the minimum width of the column. If the value is greater than
95+ \l MultiColumnView::defaultColumnWidth, the value will be set as width for
96+ the column.
97+ */
98+ property real minimumWidth: 0
99+
100+ /*!
101+ Specifies the maximum width of the column. If the value is smaller than
102+ \l MultiColumnView::defaultColumnWidth, the value will be set as width for
103+ the column. A maximum value of 0 will be ignored.
104+ */
105+ property real maximumWidth: Number.POSITIVE_INFINITY
106+}
107
108=== added file 'modules/Ubuntu/Components/1.3/MultiColumnView.qml'
109--- modules/Ubuntu/Components/1.3/MultiColumnView.qml 1970-01-01 00:00:00 +0000
110+++ modules/Ubuntu/Components/1.3/MultiColumnView.qml 2015-06-17 14:25:30 +0000
111@@ -0,0 +1,569 @@
112+/*
113+ * Copyright 2015 Canonical Ltd.
114+ *
115+ * This program is free software; you can redistribute it and/or modify
116+ * it under the terms of the GNU Lesser General Public License as published by
117+ * the Free Software Foundation; version 3.
118+ *
119+ * This program is distributed in the hope that it will be useful,
120+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
121+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
122+ * GNU Lesser General Public License for more details.
123+ *
124+ * You should have received a copy of the GNU Lesser General Public License
125+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
126+ */
127+
128+import QtQuick 2.4
129+import QtQuick.Layouts 1.1
130+import Ubuntu.Components 1.3
131+import "stack.js" as Stack
132+
133+/*!
134+ \qmltype MultiColumnView
135+ \inqmlmodule Ubuntu.Components 1.3
136+ \since Ubuntu.Components 1.3
137+ \ingroup ubuntu
138+ \brief View with multiple columns of Pages.
139+
140+ The component provides a flexible way of viewing a stack of pages in one or
141+ more columns. Unlike in PageStack, there can be more than one Page active at
142+ a time, depending on the number of the columns in the view.
143+
144+ MultiColumnView stores pages added in a tree. Pages are added relative to a
145+ given page, either as sibling (\l addPageToCurrentColumn) or as child
146+ (\l addPageToNextColumn). This means that removing a non-leaf page from the Page
147+ tree will remove all its children from the page tree.
148+
149+ The columns are populated from left to right. The column a page is added to is
150+ detected based on the source page that is given to the functions adding the page.
151+ The pages can be added either to the same column the source page resides or to
152+ the column next to the source page. Giving a null value to the source page will
153+ add the page to the leftmost column of the view.
154+
155+ The primary page, the very first page must be specified through the \l primaryPage
156+ property. The property cannot be changed after component completion and can hold
157+ a Page instance, a Component or a url to a document defining a Page. The page
158+ cannot be removed from the view.
159+
160+ \note Unlike PageStack, the component does not fill its parent content.
161+
162+ \qml
163+ import QtQuick 2.4
164+ import Ubuntu.Components 1.3
165+
166+ MainView {
167+ width: units.gu(80)
168+ height: units.gu(71)
169+
170+ MultiColumnView {
171+ anchors.fill: parent
172+ columns: 2
173+ primaryPage: page1
174+ Page {
175+ id: page1
176+ title: "Main page"
177+ Column {
178+ Button {
179+ text: "Add Page2 above " + title
180+ onClicked: page1.pageStack.addPageToCurrentColumn(page1, page2)
181+ }
182+ Button {
183+ text: "Add Page3 next to " + title
184+ onClicked: page1.pageStack.addPageToNextColumn(page1, page3)
185+ }
186+ }
187+ }
188+ Page {
189+ id: page2
190+ title: "Page #2"
191+ }
192+ Page {
193+ id: page3
194+ title: "Page #3"
195+ }
196+ }
197+ }
198+ \endqml
199+
200+ Column widths are controlled by the \l defaultColumnWidth property and the \l columnMetrics
201+ properties. The \l columnMetrics contains a list of metrics configuring a specific
202+ column. If no metrics are set, the component will use \l defaultColumnWidth on
203+ each column. If the component is set to have one column only, the content will
204+ be stretched to the entire component area no matter of the metrics specified.
205+ When multiple columns are set, the last column is set to fill the available
206+ width and the rest are configured with the \l defaultColumnWidth. This behavior
207+ can be changed by specifying the \l ColumnMetrics::fillWidth for the column that
208+ needs to fill the available width. There can be more columns filling the available
209+ width at a time.
210+
211+ Let's modify the example above, to have 3 columns, where columns 1 and 3
212+ should have fixed widths of 40 GU and column 2 should fill to the space
213+ available. The code handling this would look as follows:
214+ \qml
215+ MultiColumnView {
216+ columns: 3
217+ defaultColumnWidth: units.gu(40)
218+ columnMetrics: [
219+ ColumnMetrics {
220+ column: 2
221+ fillWidth: true
222+ },
223+ ColumnMetrics {
224+ column: 3
225+ fillWidth: false
226+ }
227+ ]
228+ }
229+ \endqml
230+
231+ MultiColumnView supports adaptive column handling. When columns number changes
232+ runtime, the pages are automatically rearranged to the closest columns they were
233+ added to. To understand it better, let's take the following example:
234+ \qml
235+ import QtQuick 2.4
236+ import Ubuntu.Components 1.3
237+
238+ MainView {
239+ width: units.gu(120)
240+ height: units.gu(71)
241+
242+ MultiColumnView {
243+ anchors.fill: parent
244+ columns: width > units.gu(100) ? 3 :
245+ (width >= units.gu(80) ? 2 : 1)
246+ primaryPage: page1
247+ Page {
248+ id: page1
249+ title: "Main page"
250+ Button {
251+ text: "Add Page2 next to " + title
252+ onClicked: page1.pageStack.addPageToNextColumn(page1, page2)
253+ }
254+ }
255+ Page {
256+ id: page2
257+ title: "Page #2"
258+ Button {
259+ text: "Add Page3 next to " + title
260+ onClicked: page2.pageStack.addPageToNextColumn(page2, page3)
261+ }
262+ }
263+ Page {
264+ id: page3
265+ title: "Page #3"
266+ }
267+ }
268+ }
269+ \endqml
270+
271+ When the code is run on desktop, it will launch with a space for three columns.
272+ \c page1 is set to be the primary page, \c page2 will be added to column next to
273+ \c page1 (to column 2) and \c page3 next to \c page2 (column 3). When the window
274+ is resized to have its size below 100 GU, the component will switch to 2 column
275+ mode, and \c page3 will be placed in the last column, and the header for \c page2
276+ will have a back button, indicating that there is a page below it. If the window
277+ is resized to contain only one column, all pages will be shown in that column, so
278+ the component will act as PageStack. Resizing the window back to 2 respectively
279+ 3 columns will place the pages side-by-side.
280+
281+ \note In the above example if \c page2 is removed, that will remove all its child
282+ pages, meaning \c page3 will also be removed.
283+
284+ \sa PageStack, ColumnMetrics
285+*/
286+
287+PageTreeNode {
288+ id: multiColumnView
289+
290+ /*!
291+ The property holds the first Page which will be added to the view. If the
292+ view has more than one column, the page will be added to the leftmost column.
293+ The proeprty can hold either a Page instance, a component holding a Page
294+ or a QML document defining the Page. The property cannot be changed after
295+ component completion.
296+ */
297+ property var primaryPage
298+
299+ /*!
300+ Specifies the number of columns in the view. A condition must be set to
301+ control the number of columns depending on the space available.
302+ \qml
303+ MultiColumnView {
304+ id: view
305+ columns: view.width > units.gu(50) ? 2 : 1
306+ }
307+ \endqml
308+ */
309+ property int columns: 1
310+
311+ /*!
312+ The property specifies the default width of each column. The property is
313+ applied on each column. If the \a minimumWidth specified for the column is
314+ bigger than this value, the minimum width will be applied.
315+ */
316+ property real defaultColumnWidth: units.gu(40)
317+
318+ /*!
319+ The property configures the size constraints and area filling for columns.
320+ If a column is not specified, the default sizing and filling will be applied.
321+ By default, only the last column is filling the available width, al other
322+ columns are sized to \l defaultColumnWidth as maximum. By default the list
323+ is empty. Only columns requiring special handling than the default should
324+ be specified.
325+ */
326+ property list<ColumnMetrics> columnMetrics
327+
328+ /*!
329+ \qmlmethod Item addPageToCurrentColumn(Item sourcePage, var page[, var properties])
330+ Adds a \c page to the column the \c sourcePage resides in. If \c sourcePage
331+ is null, the \c page will be added to the leftmost column. \c page can be a
332+ Component or a file. \c properties is a JSon object containing properties
333+ to be set when page is created. \c sourcePage must be active. Returns the
334+ instance of the page created.
335+ */
336+ function addPageToCurrentColumn(sourcePage, page, properties) {
337+ if (columns <= 0) {
338+ return;
339+ }
340+ if (!sourcePage) {
341+ console.warn("No sourcePage specified. Page will not be added.");
342+ return;
343+ }
344+ if (!d.stack.find(sourcePage)) {
345+ console.warn("sourcePage must be added to the view to add new page.");
346+ return;
347+ }
348+
349+ var wrapper = d.createWrapper(page, properties);
350+ wrapper.column = d.columnForPage(sourcePage);
351+ wrapper.parentPage = sourcePage;
352+ d.addPage(wrapper);
353+ return wrapper.object;
354+ }
355+
356+ /*!
357+ \qmlmethod Item addPageToNextColumn(Item sourcePage, var page[, var properties])
358+ Same as \l addPageToCurrentColumn except that the \c page is added to the column
359+ next to the one the \c sourcePage resides. If \c sourcePage is null, the new
360+ page will be added to the leftmost column. If \c sourcePage is located in the
361+ rightmost column, the new page will be pushed to the same column as \c sourcePage.
362+ */
363+ function addPageToNextColumn(sourcePage, page, properties) {
364+ if (columns <= 0) {
365+ return;
366+ }
367+ if (!sourcePage) {
368+ console.warn("No sourcePage specified. Page will not be added.");
369+ return;
370+ }
371+ if (!d.stack.find(sourcePage)) {
372+ console.warn("sourcePage must be added to the view to add new page.");
373+ return;
374+ }
375+
376+ var wrapper = d.createWrapper(page, properties);
377+ wrapper.column = (!sourcePage) ? 0 : d.columnForPage(sourcePage) + 1;
378+ wrapper.parentPage = sourcePage;
379+ d.addPage(wrapper);
380+ return wrapper.object;
381+ }
382+
383+ /*!
384+ \qmlmethod void removePages(Item page)
385+ The function removes and deletes all pages from the view columns until \c page
386+ is reached. If the \a page is the same as the \l primaryPage, only its child
387+ pages will be removed.
388+ */
389+ function removePages(page) {
390+ // remove nodes which have page as ascendant
391+ var node = d.stack.top();
392+ while (node && node.childOf(page)) {
393+ d.popAndSetPageForColumn(node);
394+ node = d.stack.top();
395+ }
396+ while (node && node.object == page && node != d.stack.first()) {
397+ d.popAndSetPageForColumn(node);
398+ node = d.stack.top();
399+ }
400+ }
401+
402+ /*
403+ internals
404+ */
405+
406+ /*! internal */
407+ onColumnsChanged: {
408+ if (columns <= 0) {
409+ console.warn("There must me a minimum of one column set.");
410+ }
411+ d.relayout();
412+ }
413+ Component.onCompleted: {
414+ d.relayout();
415+ d.completed = true;
416+ if (primaryPage) {
417+ var wrapper = d.createWrapper(primaryPage);
418+ d.addPage(wrapper);
419+ } else {
420+ console.warn("No primary page set. No pages can be added without a primary page.");
421+ }
422+ }
423+ onPrimaryPageChanged: {
424+ if (d.completed) {
425+ console.warn("Cannot change primaryPage after completion.");
426+ return;
427+ }
428+ }
429+ onDefaultColumnWidthChanged: body.applyMetrics()
430+
431+ QtObject {
432+ id: d
433+
434+ property bool completed: false
435+ property var stack: new Stack.Stack()
436+
437+ function createWrapper(page, properties) {
438+ var wrapperComponent = Qt.createComponent("PageWrapper.qml");
439+ var wrapperObject = wrapperComponent.createObject(hiddenPages);
440+ wrapperObject.pageStack = multiColumnView;
441+ wrapperObject.properties = properties;
442+ // set reference last because it will trigger creation of the object
443+ // with specified properties.
444+ wrapperObject.reference = page;
445+ return wrapperObject;
446+ }
447+
448+ function addPage(pageWrapper) {
449+ stack.push(pageWrapper);
450+ pageWrapper.parentWrapper = stack.find(pageWrapper.parentPage);
451+ var targetColumn = MathUtils.clamp(pageWrapper.column, 0, columns - 1);
452+ // replace page holder's child
453+ var holder = body.children[targetColumn];
454+ holder.detachCurrentPage();
455+ holder.attachPage(pageWrapper)
456+ }
457+
458+ function columnForPage(page) {
459+ var wrapper = stack.find(page);
460+ return wrapper ? wrapper.column : 0;
461+ }
462+
463+ // node is a triplet of {page, column, parentPage}
464+ function popAndSetPageForColumn(node) {
465+ stack.pop();
466+ var effectiveColumn = MathUtils.clamp(node.column, 0, columns - 1);
467+ var columnHolder = body.children[effectiveColumn];
468+ // is the page in a column?
469+ if (node == columnHolder.pageWrapper) {
470+ // detach page from column
471+ columnHolder.detachCurrentPage();
472+ }
473+ node.parent = null;
474+ var prevPage = stack.topForColumn(effectiveColumn, effectiveColumn < columns - 1);
475+ if (prevPage) {
476+ columnHolder.attachPage(prevPage);
477+ }
478+ if (node.canDestroy) {
479+ node.destroyObject();
480+ }
481+ }
482+
483+ // relayouts when column count changes
484+ function relayout() {
485+ if (body.children.length == columns) return;
486+ if (body.children.length > columns) {
487+ // need to remove few columns, the last ones
488+ while (body.children.length > columns) {
489+ var holder = body.children[body.children.length - 1];
490+ holder.detachCurrentPage();
491+ holder.parent = null;
492+ holder.destroy();
493+ }
494+ } else {
495+ var prevColumns = body.children.length;
496+
497+ // add columns
498+ for (var i = 0; i < columns - prevColumns; i++) {
499+ pageHolderComponent.createObject(body);
500+ }
501+ }
502+ rearrangePages();
503+ }
504+
505+ function rearrangePages() {
506+ for (var column = columns - 1; column >= 0; column--) {
507+ var holder = body.children[column];
508+ var pageWrapper = stack.topForColumn(column, column < (columns - 1));
509+ if (!pageWrapper) {
510+ continue;
511+ }
512+ if (!pageWrapper.parent) {
513+ // this should never happen, so if it does, we have a bug!
514+ console.error("Found a page which wasn't parented anywhere!", pageWrapper.object.title);
515+ continue;
516+ }
517+ // detach current page from holder if differs
518+ if (holder.pageWrapper != pageWrapper) {
519+ holder.detachCurrentPage();
520+ }
521+ if (pageWrapper.parent == hiddenPages) {
522+ // add the page to the column
523+ holder.attachPage(pageWrapper);
524+ } else if (pageWrapper.pageHolder != holder) {
525+ // detach the pageWrapper from its holder
526+ if (pageWrapper.pageHolder) {
527+ pageWrapper.pageHolder.detachCurrentPage();
528+ }
529+ // then attach to this holder
530+ holder.attachPage(pageWrapper);
531+ }
532+ }
533+ }
534+ }
535+
536+ // default metrics
537+ Component {
538+ id: defaultMetrics
539+ ColumnMetrics {
540+ fillWidth: column == columns
541+ minimumWidth: defaultColumnWidth
542+ }
543+ }
544+
545+ // Page holder component, can have only one Page as child at a time, all stacked pages
546+ // will be parented into hiddenPages
547+ Component {
548+ id: pageHolderComponent
549+ Item {
550+ id: holder
551+ objectName: "ColumnHolder" + column
552+ property PageWrapper pageWrapper
553+ property int column
554+ property alias config: header.config
555+ property ColumnMetrics metrics: setDefaultMetrics()
556+
557+ Layout.fillWidth: metrics.fillWidth
558+ Layout.fillHeight: true
559+ Layout.preferredWidth: metrics.maximumWidth > 0 ?
560+ MathUtils.clamp(defaultColumnWidth, metrics.minimumWidth, metrics.maximumWidth) :
561+ defaultColumnWidth
562+ Layout.minimumWidth: metrics.minimumWidth
563+ Layout.maximumWidth: metrics.maximumWidth
564+
565+ // header
566+ StyledItem {
567+ id: header
568+ anchors {
569+ left: parent.left
570+ top: parent.top
571+ right: parent.right
572+ }
573+ implicitHeight: units.gu(8)
574+// styleName: config ? "PageHeadStyle" : ""
575+
576+ property PageHeadConfiguration config: null
577+ property Item contents: null
578+
579+ property color dividerColor: theme.palette.normal.background
580+ property color panelColor
581+ }
582+
583+ Rectangle {
584+ id: divider
585+ anchors {
586+ top: parent.top
587+ bottom: parent.bottom
588+ right: parent.right
589+ }
590+ width: (column == (columns - 1)) || !pageWrapper ? 0 : units.dp(2)
591+ color: theme.palette.normal.background
592+ }
593+
594+ Item {
595+ id: holderBody
596+ objectName: parent.objectName + "Body"
597+ anchors {
598+ fill: parent
599+// topMargin: header.height
600+ rightMargin: divider.width
601+ }
602+ }
603+
604+ function attachPage(page) {
605+ if (!page) return;
606+ pageWrapper = page;
607+ pageWrapper.parent = holderBody;
608+ pageWrapper.pageHolder = holder;
609+ pageWrapper.active = true;
610+ if (pageWrapper.object.hasOwnProperty("head")) {
611+ header.config = pageWrapper.object.head;
612+ }
613+ }
614+ function detachCurrentPage() {
615+ if (!pageWrapper) return undefined;
616+ var wrapper = pageWrapper;
617+ // remove header
618+ wrapper.active = false;
619+ header.config = null;
620+ pageWrapper = null;
621+ wrapper.parent = hiddenPages;
622+ wrapper.pageHolder = null;
623+ return wrapper;
624+ }
625+
626+ function setDefaultMetrics() {
627+ var result = defaultMetrics.createObject(holder);
628+ result.column = Qt.binding(function() { return holder.column + 1; });
629+ return result;
630+ }
631+ }
632+ }
633+
634+ /*! \internal */
635+ // Pages declared as children will be placed directly into hiddenPages
636+ default property alias data: hiddenPages.data
637+ Item {
638+ id: hiddenPages
639+ objectName: "HiddenPagePool"
640+ visible: false
641+ // make sure nothing is shown eventually
642+ clip: true
643+ }
644+
645+ // Holds the columns holding the pages visible. Each column has only one page
646+ // as child, the invisible stacked ones are all stored in the hiddenPages
647+ // component. The stack keeps the column index onto which those should be moved
648+ // once they become visible.
649+ RowLayout {
650+ id: body
651+ anchors.fill: parent
652+ spacing: 0
653+
654+ onChildrenChanged: {
655+ // all children should have Layout.fillWidth false, except the last one
656+ for (var i = 0; i < children.length; i++) {
657+ children[i].column = i;
658+ }
659+ applyMetrics();
660+ }
661+
662+ function applyMetrics() {
663+ for (var i = 0; i < children.length; i++) {
664+ var holder = children[i];
665+ // search for the column metrics
666+ var metrics = null;
667+ for (var j = 0; j < columnMetrics.length; j++) {
668+ if (columnMetrics[j].column == (i + 1)) {
669+ metrics = columnMetrics[j];
670+ break;
671+ }
672+ }
673+ if (!metrics) {
674+ metrics = holder.setDefaultMetrics();
675+ }
676+ holder.metrics = metrics;
677+ }
678+ }
679+ }
680+}
681
682=== modified file 'modules/Ubuntu/Components/1.3/Page.qml'
683--- modules/Ubuntu/Components/1.3/Page.qml 2015-04-30 08:32:44 +0000
684+++ modules/Ubuntu/Components/1.3/Page.qml 2015-06-17 14:25:30 +0000
685@@ -43,6 +43,7 @@
686 readonly property alias head: headerConfig
687 Toolkit13.PageHeadConfiguration {
688 id: headerConfig
689+ title: page.title
690 }
691
692 Toolkit13.Object {
693
694=== modified file 'modules/Ubuntu/Components/1.3/PageHeadConfiguration.qml'
695--- modules/Ubuntu/Components/1.3/PageHeadConfiguration.qml 2015-04-30 08:32:44 +0000
696+++ modules/Ubuntu/Components/1.3/PageHeadConfiguration.qml 2015-06-17 14:25:30 +0000
697@@ -56,4 +56,6 @@
698
699 // auto-updated by AppHeader, but may be set by the developer
700 property bool visible
701+
702+ property string title
703 }
704
705=== modified file 'modules/Ubuntu/Components/1.3/PageWrapper.qml'
706--- modules/Ubuntu/Components/1.3/PageWrapper.qml 2015-05-05 16:23:29 +0000
707+++ modules/Ubuntu/Components/1.3/PageWrapper.qml 2015-06-17 14:25:30 +0000
708@@ -46,6 +46,43 @@
709 property bool canDestroy: false
710
711 /*!
712+ Column number in MultiColumnView.
713+ */
714+ property int column: 0
715+
716+ /*!
717+ Parent page.
718+ */
719+ property Item parentPage
720+
721+ /*!
722+ Parent PageWrapper or the parentPage.
723+ */
724+ property Item parentWrapper
725+
726+ /*!
727+ Page holder in MultiColumnView
728+ */
729+ property Item pageHolder
730+
731+ /*!
732+ Returns true if the current PageWrapper is a child of the given page
733+ */
734+ function childOf(page) {
735+ if (parentPage == page) return true;
736+ if (page && parentWrapper) {
737+ var wrapper = parentWrapper;
738+ while (wrapper) {
739+ if (wrapper.object == page) {
740+ return true;
741+ }
742+ wrapper = wrapper.parentWrapper;
743+ }
744+ }
745+ return false;
746+ }
747+
748+ /*!
749 This value is updated when a PageWrapper is pushed to/popped from a PageStack.
750 */
751 active: false
752
753=== added file 'modules/Ubuntu/Components/1.3/stack.js'
754--- modules/Ubuntu/Components/1.3/stack.js 1970-01-01 00:00:00 +0000
755+++ modules/Ubuntu/Components/1.3/stack.js 2015-06-17 14:25:30 +0000
756@@ -0,0 +1,75 @@
757+/*
758+ * Copyright 2015 Canonical Ltd.
759+ *
760+ * This program is free software; you can redistribute it and/or modify
761+ * it under the terms of the GNU Lesser General Public License as published by
762+ * the Free Software Foundation; version 3.
763+ *
764+ * This program is distributed in the hope that it will be useful,
765+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
766+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
767+ * GNU Lesser General Public License for more details.
768+ *
769+ * You should have received a copy of the GNU Lesser General Public License
770+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
771+ */
772+
773+.pragma library
774+// By defining Stack as a function, we can make its variables private,
775+// and force calls to Stack to make use of the functions we define.
776+function Stack() {
777+ var elements;
778+ this.clear = function() {
779+ elements = [];
780+ }
781+
782+ this.clear();
783+
784+ this.push = function(element) {
785+ elements.push(element);
786+ };
787+
788+ this.pop = function() {
789+ elements.pop();
790+ };
791+
792+ this.size = function() {
793+ return elements.length;
794+ }
795+
796+ this.first = function() {
797+ return this.size() > 0 ? elements[0] : null;
798+ }
799+
800+ this.top = function() {
801+ if (this.size() < 1) return undefined;
802+ return elements[elements.length-1];
803+ }
804+
805+ // returns the topmost element for the column, using exact column match
806+ // in case the column to be extracted is less than the columns available
807+ this.topForColumn = function(column, exactMatch) {
808+ if (exactMatch == undefined) {
809+ exactMatch = false;
810+ }
811+
812+ for (var i = elements.length - 1; i >= 0; i--) {
813+ var node = elements[i];
814+ if ((exactMatch && elements[i].column == column) || (!exactMatch && elements[i].column >= column)) {
815+ return elements[i];
816+ }
817+ }
818+ return undefined;
819+ }
820+
821+ // returns the node the Page is stored; undefined if not found
822+ this.find = function(page) {
823+ if (!page) return null;
824+ for (var i = elements.length - 1; i >= 0; i--) {
825+ if (elements[i].object == page) {
826+ return elements[i];
827+ }
828+ }
829+ return null;
830+ }
831+}
832
833=== modified file 'modules/Ubuntu/Components/Themes/Ambiance/1.3/PageHeadStyle.qml'
834--- modules/Ubuntu/Components/Themes/Ambiance/1.3/PageHeadStyle.qml 2015-05-22 13:54:38 +0000
835+++ modules/Ubuntu/Components/Themes/Ambiance/1.3/PageHeadStyle.qml 2015-06-17 14:25:30 +0000
836@@ -23,7 +23,7 @@
837 objectName: "PageHeadStyle" // used in unit tests
838 contentHeight: units.gu(7)
839 fontWeight: Font.Light
840- fontSize: "x-large"
841+ fontSize: "large"
842 textLeftMargin: units.gu(2)
843 maximumNumberOfActions: 3
844
845@@ -394,7 +394,7 @@
846 right: parent.right
847 verticalCenter: parent.verticalCenter
848 }
849- text: styledItem.title
850+ text: styledItem.config.title
851 font.weight: headerStyle.fontWeight
852 fontSize: headerStyle.fontSize
853 color: headerStyle.titleColor
854
855=== modified file 'modules/Ubuntu/Components/qmldir'
856--- modules/Ubuntu/Components/qmldir 2015-05-12 13:41:39 +0000
857+++ modules/Ubuntu/Components/qmldir 2015-06-17 14:25:30 +0000
858@@ -140,3 +140,5 @@
859 PullToRefresh 1.3 1.3/PullToRefresh.qml
860 UbuntuListView 1.3 1.3/UbuntuListView11.qml
861 Captions 1.3 1.3/Captions.qml
862+MultiColumnView 1.3 1.3/MultiColumnView.qml
863+ColumnMetrics 1.3 1.3/ColumnMetrics.qml
864
865=== modified file 'tests/resources/navigation/MyCustomPage.qml'
866--- tests/resources/navigation/MyCustomPage.qml 2015-03-03 13:20:06 +0000
867+++ tests/resources/navigation/MyCustomPage.qml 2015-06-17 14:25:30 +0000
868@@ -15,7 +15,7 @@
869 */
870
871 import QtQuick 2.2
872-import Ubuntu.Components 1.1
873+import Ubuntu.Components 1.3
874
875 Page {
876 title: i18n.tr("My custom page")
877
878=== added file 'tests/resources/navigation/SplitViewStack.qml'
879--- tests/resources/navigation/SplitViewStack.qml 1970-01-01 00:00:00 +0000
880+++ tests/resources/navigation/SplitViewStack.qml 2015-06-17 14:25:30 +0000
881@@ -0,0 +1,137 @@
882+/*
883+ * Copyright 2015 Canonical Ltd.
884+ *
885+ * This program is free software; you can redistribute it and/or modify
886+ * it under the terms of the GNU Lesser General Public License as published by
887+ * the Free Software Foundation; version 3.
888+ *
889+ * This program is distributed in the hope that it will be useful,
890+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
891+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
892+ * GNU Lesser General Public License for more details.
893+ *
894+ * You should have received a copy of the GNU Lesser General Public License
895+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
896+ */
897+
898+import QtQuick 2.4
899+import Ubuntu.Components 1.3
900+import QtQuick.Layouts 1.1
901+
902+MainView {
903+ id: main
904+ width: units.gu(100)
905+ height: units.gu(71)
906+
907+ MultiColumnView {
908+ id: view
909+ anchors.fill: parent
910+ columns: {
911+ if (width > units.gu(120)) return 3;
912+ if (width > units.gu(80)) return 2;
913+ return 1;
914+ }
915+ columnMetrics: [
916+ ColumnMetrics {
917+ column: 2
918+ fillWidth: true
919+ },
920+ ColumnMetrics {
921+ column: 3
922+ fillWidth: false
923+ minimumWidth: units.gu(50)
924+ }
925+ ]
926+ primaryPage: page1
927+
928+ Page {
929+ id: page1
930+ title: "Page #1"
931+
932+ Rectangle {
933+ anchors.fill: parent
934+ color: "red"
935+ }
936+ Column {
937+ Button {
938+ text: "Add page2 next"
939+ onClicked: page1.pageStack.addPageToNextColumn(page1, page2)
940+ }
941+ Button {
942+ text: "Add page4 above"
943+ onClicked: page1.pageStack.addPageToCurrentColumn(page1, page4)
944+ }
945+ }
946+ }
947+
948+ Page {
949+ id: page2
950+ title: "Page #2"
951+
952+ Rectangle {
953+ anchors.fill: parent
954+ color: "green"
955+ }
956+ Column {
957+ Button {
958+ text: "Back..."
959+ onClicked: page2.pageStack.removePages(page2)
960+ }
961+ Button {
962+ text: "Add page3 next"
963+ onClicked: page2.pageStack.addPageToNextColumn(page2, page3)
964+ }
965+ }
966+ }
967+ Page {
968+ id: page3
969+ title: "Page #3"
970+
971+ Rectangle {
972+ anchors.fill: parent
973+ color: "blue"
974+ }
975+ Button {
976+ text: "Back..."
977+ onClicked: page3.pageStack.removePages(page3)
978+ }
979+ }
980+ Page {
981+ id: page4
982+ title: "Page #4"
983+
984+ Rectangle {
985+ anchors.fill: parent
986+ color: "teal"
987+ }
988+ Column {
989+ Button {
990+ text: "Back..."
991+ onClicked: page4.pageStack.removePages(page4)
992+ }
993+ Button {
994+ text: "Add page5 next"
995+ onClicked: page4.pageStack.addPageToNextColumn(page4, page5)
996+ }
997+ }
998+ }
999+ Page {
1000+ id: page5
1001+ title: "Page #5"
1002+ Rectangle {
1003+ anchors.fill: parent
1004+ color: "tan"
1005+ }
1006+ Column {
1007+ Button {
1008+ text: "Back..."
1009+ onClicked: page5.pageStack.removePages(page5)
1010+ }
1011+ Button {
1012+ text: "Custom page on same column"
1013+ onClicked: page5.pageStack.addPageToCurrentColumn(page5, Qt.resolvedUrl("MyCustomPage.qml"))
1014+ }
1015+ }
1016+ }
1017+ }
1018+}
1019
1020=== modified file 'tests/unit_x11/tst_components/tst_components.pro'
1021--- tests/unit_x11/tst_components/tst_components.pro 2015-04-25 07:10:57 +0000
1022+++ tests/unit_x11/tst_components/tst_components.pro 2015-06-17 14:25:30 +0000
1023@@ -7,5 +7,6 @@
1024 SOURCES += tst_components.cpp tabsmodel.cpp
1025 HEADERS += tabsmodel.h
1026
1027-OTHER_FILES += $$system(ls *.qml)
1028+OTHER_FILES += $$system(ls *.qml) \
1029+ tst_multicolumnview.qml
1030 OTHER_FILES += $$system(ls AppTheme/*)
1031
1032=== added file 'tests/unit_x11/tst_components/tst_multicolumnview.qml'
1033--- tests/unit_x11/tst_components/tst_multicolumnview.qml 1970-01-01 00:00:00 +0000
1034+++ tests/unit_x11/tst_components/tst_multicolumnview.qml 2015-06-17 14:25:30 +0000
1035@@ -0,0 +1,137 @@
1036+/*
1037+ * Copyright 2015 Canonical Ltd.
1038+ *
1039+ * This program is free software; you can redistribute it and/or modify
1040+ * it under the terms of the GNU Lesser General Public License as published by
1041+ * the Free Software Foundation; version 3.
1042+ *
1043+ * This program is distributed in the hope that it will be useful,
1044+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1045+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1046+ * GNU Lesser General Public License for more details.
1047+ *
1048+ * You should have received a copy of the GNU Lesser General Public License
1049+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1050+ */
1051+
1052+import QtQuick 2.4
1053+import QtTest 1.0
1054+import Ubuntu.Test 1.0
1055+import Ubuntu.Components 1.3
1056+
1057+Item {
1058+ id: test
1059+ width: units.gu(120)
1060+ height: units.gu(71)
1061+
1062+ MultiColumnView {
1063+ id: testView
1064+ width: parent.width
1065+ height: parent.height
1066+
1067+ columns: width > units.gu(100) ? 3 : (width > units.gu(80) ? 2 : 1)
1068+ primaryPage: page1
1069+
1070+ Page {
1071+ id: page1
1072+ title: "Page1"
1073+ }
1074+ Page {
1075+ id: page2
1076+ title: "Page2"
1077+ }
1078+ Page {
1079+ id: page3
1080+ title: "Page3"
1081+ }
1082+ Page {
1083+ id: page4
1084+ title: "Page4"
1085+ }
1086+ }
1087+
1088+ MultiColumnView {
1089+ id: defaults
1090+ }
1091+
1092+ UbuntuTestCase {
1093+ when: windowShown
1094+
1095+ function cleanup() {
1096+// testView.columns = Qt.binding(function() {
1097+// return test.width > units.gu(100) ? 3 : (test.width > units.gu(80) ? 2 : 1);
1098+// });
1099+ testView.width = test.width;
1100+ testView.height = test.height;
1101+ // remove allpages
1102+ testView.removePages(page1);
1103+ }
1104+
1105+ function test_0_API() {
1106+ compare(defaults.columns, 1, "wrong columns");
1107+ compare(defaults.defaultColumnWidth, units.gu(40), "wrong defaultColumnWidth");
1108+ compare(defaults.columnMetrics.length, 0, "wrong columnMetrics list");
1109+ compare(defaults.primaryPage, undefined, "wrong primaryPage");
1110+ }
1111+
1112+ function test_add_to_first_column_data() {
1113+ return [
1114+ {tag: "null sourcePage, fail", sourcePage: null, page: page2, failMsg: "No sourcePage specified. Page will not be added."},
1115+ {tag: "valid sourcePage, pass", sourcePage: page1, page: page2, failMsg: ""},
1116+ ]
1117+ }
1118+ function test_add_to_first_column(data) {
1119+ if (data.failMsg != "") {
1120+ ignoreWarning(data.failMsg);
1121+ }
1122+
1123+ testView.addPageToCurrentColumn(data.sourcePage, data.page);
1124+ var firstColumn = findChild(testView, "ColumnHolder0");
1125+ verify(firstColumn);
1126+ if (data.failMsg != "") {
1127+ expectFail(data.tag, "Fail");
1128+ }
1129+ compare(firstColumn.pageWrapper.object, data.page);
1130+ }
1131+
1132+ function test_add_to_next_column_data() {
1133+ return [
1134+ {tag: "null sourcePage, fail", sourcePage: null, page: page2, failMsg: "No sourcePage specified. Page will not be added."},
1135+ {tag: "valid sourcePage, pass", sourcePage: page1, page: page2, failMsg: ""},
1136+ ]
1137+ }
1138+ function test_add_to_next_column(data) {
1139+ if (data.failMsg != "") {
1140+ ignoreWarning(data.failMsg);
1141+ }
1142+
1143+ testView.addPageToNextColumn(data.sourcePage, data.page);
1144+ var secondColumn = findChild(testView, "ColumnHolder1");
1145+ verify(secondColumn);
1146+ if (data.failMsg != "") {
1147+ expectFail(data.tag, "Fail");
1148+ }
1149+ verify(secondColumn.pageWrapper);
1150+ }
1151+
1152+ function test_invalid_column() {
1153+ ignoreWarning("There must me a minimum of one column set.");
1154+ defaults.columns = 0;
1155+ }
1156+
1157+ function test_change_primaryPage() {
1158+ ignoreWarning("Cannot change primaryPage after completion.");
1159+ testView.primaryPage = page3;
1160+ }
1161+
1162+ function test_add_to_same_column_when_source_page_not_in_stack() {
1163+ ignoreWarning("sourcePage must be added to the view to add new page.");
1164+ testView.addPageToCurrentColumn(page2, page3);
1165+ }
1166+
1167+ function test_add_to_next_column_when_source_page_not_in_stack() {
1168+ ignoreWarning("sourcePage must be added to the view to add new page.");
1169+ testView.addPageToNextColumn(page2, page3);
1170+ }
1171+ }
1172+}

Subscribers

People subscribed via source and target branches