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