Merge lp:~faenil/ubuntu-ui-toolkit/ContactsAdaptive_UIToolkit into lp:ubuntu-ui-toolkit/staging
- ContactsAdaptive_UIToolkit
- Merge into staging
Status: | Rejected |
---|---|
Rejected by: | Zsombor Egri |
Proposed branch: | lp:~faenil/ubuntu-ui-toolkit/ContactsAdaptive_UIToolkit |
Merge into: | lp:ubuntu-ui-toolkit/staging |
Diff against target: |
2335 lines (+1712/-203) 27 files modified
components.api (+7/-0) examples/ubuntu-ui-toolkit-gallery/ListItemWithLabel.qml (+1/-1) examples/ubuntu-ui-toolkit-gallery/Popover.qml (+4/-7) examples/ubuntu-ui-toolkit-gallery/Sections.qml (+1/-1) examples/ubuntu-ui-toolkit-gallery/Template.qml (+7/-7) examples/ubuntu-ui-toolkit-gallery/ubuntu-ui-toolkit-gallery.qml (+45/-125) modules/Ubuntu/Components/1.2/stack.js (+1/-0) modules/Ubuntu/Components/1.3/ColumnMetrics.qml (+53/-0) modules/Ubuntu/Components/1.3/MultiColumnView.qml (+553/-0) modules/Ubuntu/Components/1.3/OrientationHelper.qml (+1/-1) 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/IconButtonStyle.qml (+1/-1) modules/Ubuntu/Components/Themes/Ambiance/1.3/PageHeadStyle.qml (+51/-22) modules/Ubuntu/Components/Themes/Ambiance/1.3/Palette.qml (+1/-1) modules/Ubuntu/Components/Themes/Ambiance/1.3/SectionsStyle.qml (+362/-28) modules/Ubuntu/Components/qmldir (+1/-0) tests/autopilot/ubuntuuitoolkit/tests/__init__.py (+3/-4) tests/resources/navigation/MyCustomPage.qml (+1/-1) tests/resources/navigation/SplitViewStack.qml (+137/-0) tests/unit_x11/tst_components/ListItemWithLabel.qml (+30/-0) tests/unit_x11/tst_components/MyExternalPage.qml (+7/-3) tests/unit_x11/tst_components/tst_components.pro (+2/-1) tests/unit_x11/tst_components/tst_multicolumnheader.qml (+200/-0) tests/unit_x11/tst_components/tst_multicolumnview.qml (+128/-0) |
To merge this branch: | bzr merge lp:~faenil/ubuntu-ui-toolkit/ContactsAdaptive_UIToolkit |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Zsombor Egri | Disapprove | ||
PS Jenkins bot | continuous-integration | Needs Fixing | |
Review via email: mp+264424@code.launchpad.net |
Commit message
Description of the change
Changes needed for UI Convergency prototype
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1566
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Zsombor Egri (zsombi) wrote : | # |
This was a demo MR to convert AddressBook to use AdaptivePageLayout. No need to land.
Unmerged revisions
- 1566. By Andrea Bernabei
-
merge multiColumnView branch
- 1565. By Andrea Bernabei
-
OrientationHelper: check if a valid inputMethod exists before evaluating its props
- 1564. By Andrea Bernabei
-
merge
- 1563. By Andrea Bernabei
-
change default color of IconButtonStyle
- 1562. By Andrea Bernabei
-
change backgroundText in theme Palette
- 1561. By Andrea Bernabei
-
add white background to header
- 1560. By Andrea Bernabei
-
update backgroundText palette value
- 1559. By Andrea Bernabei
-
PageHeadStyle: take number of actions available into account when computing numberOfSlots
- 1558. By Andrea Bernabei
-
merge tims branch
- 1557. By Andrea Bernabei
-
Implement dynamic number of slots in PageHeadStyle
Preview Diff
1 | === modified file 'components.api' | |||
2 | --- components.api 2015-07-10 13:05:26 +0000 | |||
3 | +++ components.api 2015-07-10 13:56:40 +0000 | |||
4 | @@ -522,6 +522,12 @@ | |||
5 | 522 | Ubuntu.Components.Mouse.Priority: Enum | 522 | Ubuntu.Components.Mouse.Priority: Enum |
6 | 523 | AfterItem | 523 | AfterItem |
7 | 524 | BeforeItem | 524 | BeforeItem |
8 | 525 | Ubuntu.Components.MultiColumnView 1.3: PageTreeNode | ||
9 | 526 | default readonly property QtObject data | ||
10 | 527 | function var addPageToCurrentColumn(var sourcePage, var page, var properties) | ||
11 | 528 | function var addPageToNextColumn(var sourcePage, var page, var properties) | ||
12 | 529 | function var removePages(var page) | ||
13 | 530 | property var primaryPage | ||
14 | 525 | Ubuntu.Components.ListItems.MultiValue 1.0 0.1: Base | 531 | Ubuntu.Components.ListItems.MultiValue 1.0 0.1: Base |
15 | 526 | property var values | 532 | property var values |
16 | 527 | Ubuntu.Components.ListItems.MultiValue 1.3: Base | 533 | Ubuntu.Components.ListItems.MultiValue 1.3: Base |
17 | @@ -606,6 +612,7 @@ | |||
18 | 606 | property bool locked | 612 | property bool locked |
19 | 607 | property string preset | 613 | property string preset |
20 | 608 | readonly property PageHeadSections sections | 614 | readonly property PageHeadSections sections |
21 | 615 | property string title | ||
22 | 609 | property bool visible | 616 | property bool visible |
23 | 610 | Ubuntu.Components.PageHeadSections 1.1: QtObject | 617 | Ubuntu.Components.PageHeadSections 1.1: QtObject |
24 | 611 | property bool enabled | 618 | property bool enabled |
25 | 612 | 619 | ||
26 | === modified file 'examples/ubuntu-ui-toolkit-gallery/ListItemWithLabel.qml' | |||
27 | --- examples/ubuntu-ui-toolkit-gallery/ListItemWithLabel.qml 2015-04-25 08:18:45 +0000 | |||
28 | +++ examples/ubuntu-ui-toolkit-gallery/ListItemWithLabel.qml 2015-07-10 13:56:40 +0000 | |||
29 | @@ -14,7 +14,7 @@ | |||
30 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
31 | 15 | */ | 15 | */ |
32 | 16 | 16 | ||
34 | 17 | import QtQuick 2.2 | 17 | import QtQuick 2.4 |
35 | 18 | import Ubuntu.Components 1.3 | 18 | import Ubuntu.Components 1.3 |
36 | 19 | 19 | ||
37 | 20 | ListItem { | 20 | ListItem { |
38 | 21 | 21 | ||
39 | === modified file 'examples/ubuntu-ui-toolkit-gallery/Popover.qml' | |||
40 | --- examples/ubuntu-ui-toolkit-gallery/Popover.qml 2015-04-29 08:55:31 +0000 | |||
41 | +++ examples/ubuntu-ui-toolkit-gallery/Popover.qml 2015-07-10 13:56:40 +0000 | |||
42 | @@ -21,17 +21,14 @@ | |||
43 | 21 | Template { | 21 | Template { |
44 | 22 | objectName: "popoversTemplate" | 22 | objectName: "popoversTemplate" |
45 | 23 | 23 | ||
49 | 24 | tools: ToolbarItems { | 24 | head.actions: [ |
50 | 25 | ToolbarButton { | 25 | Action { |
48 | 26 | id: actionsButton | ||
51 | 27 | text: "Actions" | 26 | text: "Actions" |
52 | 28 | iconSource: "call_icon.png" | 27 | iconSource: "call_icon.png" |
54 | 29 | onTriggered: PopupUtils.open(actionSelectionPopover, actionsButton) | 28 | onTriggered: PopupUtils.open(actionSelectionPopover) |
55 | 30 | visible: true | 29 | visible: true |
56 | 31 | } | 30 | } |
60 | 32 | locked: true | 31 | ] |
58 | 33 | opened: true | ||
59 | 34 | } | ||
61 | 35 | 32 | ||
62 | 36 | TemplateSection { | 33 | TemplateSection { |
63 | 37 | className: "Popover" | 34 | className: "Popover" |
64 | 38 | 35 | ||
65 | === modified file 'examples/ubuntu-ui-toolkit-gallery/Sections.qml' | |||
66 | --- examples/ubuntu-ui-toolkit-gallery/Sections.qml 2015-06-17 12:13:40 +0000 | |||
67 | +++ examples/ubuntu-ui-toolkit-gallery/Sections.qml 2015-07-10 13:56:40 +0000 | |||
68 | @@ -19,7 +19,7 @@ | |||
69 | 19 | 19 | ||
70 | 20 | Template { | 20 | Template { |
71 | 21 | objectName: "sectionsTemplate" | 21 | objectName: "sectionsTemplate" |
73 | 22 | 22 | head.sections.model: ["first", "second", "third"] | |
74 | 23 | TemplateSection { | 23 | TemplateSection { |
75 | 24 | title: "Sections" | 24 | title: "Sections" |
76 | 25 | className: "Sections" | 25 | className: "Sections" |
77 | 26 | 26 | ||
78 | === modified file 'examples/ubuntu-ui-toolkit-gallery/Template.qml' | |||
79 | --- examples/ubuntu-ui-toolkit-gallery/Template.qml 2015-04-25 08:18:45 +0000 | |||
80 | +++ examples/ubuntu-ui-toolkit-gallery/Template.qml 2015-07-10 13:56:40 +0000 | |||
81 | @@ -1,5 +1,5 @@ | |||
82 | 1 | /* | 1 | /* |
84 | 2 | * Copyright 2013 Canonical Ltd. | 2 | * Copyright 2015 Canonical Ltd. |
85 | 3 | * | 3 | * |
86 | 4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
87 | 5 | * it under the terms of the GNU Lesser General Public License as published by | 5 | * it under the terms of the GNU Lesser General Public License as published by |
88 | @@ -14,19 +14,19 @@ | |||
89 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
90 | 15 | */ | 15 | */ |
91 | 16 | 16 | ||
93 | 17 | import QtQuick 2.0 | 17 | import QtQuick 2.4 |
94 | 18 | import Ubuntu.Components 1.3 | 18 | import Ubuntu.Components 1.3 |
95 | 19 | 19 | ||
97 | 20 | Item { | 20 | Page { |
98 | 21 | id: template | 21 | id: template |
99 | 22 | 22 | ||
102 | 23 | width: units.gu(40) | 23 | head.backAction: Action { |
103 | 24 | height: units.gu(75) | 24 | iconName: 'back' |
104 | 25 | onTriggered: columns.removePages(template) | ||
105 | 26 | } | ||
106 | 25 | 27 | ||
107 | 26 | default property alias content: layout.children | 28 | default property alias content: layout.children |
108 | 27 | property alias spacing: layout.spacing | 29 | property alias spacing: layout.spacing |
109 | 28 | property Item tools: null | ||
110 | 29 | property Flickable flickable: flickable | ||
111 | 30 | 30 | ||
112 | 31 | Flickable { | 31 | Flickable { |
113 | 32 | id: flickable | 32 | id: flickable |
114 | 33 | 33 | ||
115 | === modified file 'examples/ubuntu-ui-toolkit-gallery/ubuntu-ui-toolkit-gallery.qml' | |||
116 | --- examples/ubuntu-ui-toolkit-gallery/ubuntu-ui-toolkit-gallery.qml 2015-06-23 17:40:21 +0000 | |||
117 | +++ examples/ubuntu-ui-toolkit-gallery/ubuntu-ui-toolkit-gallery.qml 2015-07-10 13:56:40 +0000 | |||
118 | @@ -1,5 +1,5 @@ | |||
119 | 1 | /* | 1 | /* |
121 | 2 | * Copyright 2013 Canonical Ltd. | 2 | * Copyright 2015 Canonical Ltd. |
122 | 3 | * | 3 | * |
123 | 4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
124 | 5 | * it under the terms of the GNU Lesser General Public License as published by | 5 | * it under the terms of the GNU Lesser General Public License as published by |
125 | @@ -38,107 +38,51 @@ | |||
126 | 38 | LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft | 38 | LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft |
127 | 39 | LayoutMirroring.childrenInherit: true | 39 | LayoutMirroring.childrenInherit: true |
128 | 40 | 40 | ||
218 | 41 | state: width >= units.gu(80) ? "wide" : "narrow" | 41 | MultiColumnView { |
219 | 42 | states: [ | 42 | id: columns |
220 | 43 | State { | 43 | anchors.fill: parent |
221 | 44 | name: "narrow" | 44 | primaryPage: mainPage |
222 | 45 | StateChangeScript { | 45 | |
223 | 46 | script: { | 46 | Page { |
224 | 47 | pageStack.push(mainPage); | 47 | id: mainPage |
225 | 48 | if (selectedWidget) { | 48 | title: "Ubuntu UI Toolkit" |
226 | 49 | pageStack.push(contentPage); | 49 | |
227 | 50 | } | 50 | head.actions: [ |
228 | 51 | } | 51 | Action { |
229 | 52 | } | 52 | text: i18n.tr('Use dark theme') |
230 | 53 | PropertyChanges { | 53 | iconName: 'torch-on' |
231 | 54 | target: mainPage | 54 | visible: theme.name == 'Ubuntu.Components.Themes.Ambiance' |
232 | 55 | flickable: widgetList | 55 | onTriggered: theme.name = 'Ubuntu.Components.Themes.SuruDark' |
233 | 56 | } | 56 | }, |
234 | 57 | PropertyChanges { | 57 | Action { |
235 | 58 | target: contentPage | 58 | text: i18n.tr('Use light theme') |
236 | 59 | flickable: contentLoader.item ? contentLoader.item.flickable : null | 59 | iconName: 'torch-off' |
237 | 60 | } | 60 | visible: theme.name == 'Ubuntu.Components.Themes.SuruDark' |
238 | 61 | }, | 61 | onTriggered: theme.name = 'Ubuntu.Components.Themes.Ambiance' |
239 | 62 | State { | 62 | } |
240 | 63 | name: "wide" | 63 | ] |
241 | 64 | StateChangeScript { | 64 | |
242 | 65 | script: { | 65 | Rectangle { |
243 | 66 | pageStack.clear(); | 66 | color: Qt.rgba(0.0, 0.0, 0.0, 0.01) |
155 | 67 | |||
156 | 68 | /* When pushing Pages into a PageStack they are reparented | ||
157 | 69 | to internally created PageWrappers. This undoes it as to | ||
158 | 70 | allow us to anchor the Pages freely again. | ||
159 | 71 | */ | ||
160 | 72 | mainPage.parent = gallery; | ||
161 | 73 | contentPage.parent = gallery; | ||
162 | 74 | } | ||
163 | 75 | } | ||
164 | 76 | PropertyChanges { | ||
165 | 77 | target: mainPage | ||
166 | 78 | width: units.gu(40) | ||
167 | 79 | clip: true | ||
168 | 80 | } | ||
169 | 81 | AnchorChanges { | ||
170 | 82 | target: mainPage | ||
171 | 83 | anchors.right: undefined | ||
172 | 84 | } | ||
173 | 85 | PropertyChanges { | ||
174 | 86 | target: contentPage | ||
175 | 87 | clip: true | ||
176 | 88 | } | ||
177 | 89 | AnchorChanges { | ||
178 | 90 | target: contentPage | ||
179 | 91 | anchors.left: mainPage.right | ||
180 | 92 | } | ||
181 | 93 | } | ||
182 | 94 | ] | ||
183 | 95 | |||
184 | 96 | property var selectedWidget: null | ||
185 | 97 | |||
186 | 98 | Page { | ||
187 | 99 | id: mainPage | ||
188 | 100 | active: selectedWidget == null | ||
189 | 101 | |||
190 | 102 | title: "Ubuntu UI Toolkit" | ||
191 | 103 | /* Page internally sets the topMargin of its flickable to account for | ||
192 | 104 | the height of the header. Undo it when unsetting the flickable. | ||
193 | 105 | */ | ||
194 | 106 | onFlickableChanged: if (!flickable) widgetList.topMargin = 0; | ||
195 | 107 | |||
196 | 108 | head.actions: [ | ||
197 | 109 | Action { | ||
198 | 110 | text: i18n.tr('Use dark theme') | ||
199 | 111 | iconName: 'torch-on' | ||
200 | 112 | visible: theme.name == 'Ubuntu.Components.Themes.Ambiance' | ||
201 | 113 | onTriggered: theme.name = 'Ubuntu.Components.Themes.SuruDark' | ||
202 | 114 | }, | ||
203 | 115 | Action { | ||
204 | 116 | text: i18n.tr('Use light theme') | ||
205 | 117 | iconName: 'torch-off' | ||
206 | 118 | visible: theme.name == 'Ubuntu.Components.Themes.SuruDark' | ||
207 | 119 | onTriggered: theme.name = 'Ubuntu.Components.Themes.Ambiance' | ||
208 | 120 | } | ||
209 | 121 | ] | ||
210 | 122 | |||
211 | 123 | Rectangle { | ||
212 | 124 | color: Qt.rgba(0.0, 0.0, 0.0, 0.01) | ||
213 | 125 | anchors.fill: parent | ||
214 | 126 | |||
215 | 127 | ListView { | ||
216 | 128 | id: widgetList | ||
217 | 129 | objectName: "widgetList" | ||
244 | 130 | anchors.fill: parent | 67 | anchors.fill: parent |
256 | 131 | model: widgetsModel | 68 | |
257 | 132 | delegate: ListItem.Standard { | 69 | ListView { |
258 | 133 | text: model.label | 70 | id: widgetList |
259 | 134 | objectName: model.objectName | 71 | objectName: "widgetList" |
260 | 135 | enabled: model.source != "" | 72 | anchors.fill: parent |
261 | 136 | progression: true | 73 | model: widgetsModel |
262 | 137 | selected: enabled && selectedWidget == model | 74 | currentIndex: -1 |
263 | 138 | onClicked: { | 75 | delegate: ListItem.Standard { |
264 | 139 | selectedWidget = model; | 76 | text: model.label |
265 | 140 | if (gallery.state == "narrow") { | 77 | objectName: model.objectName |
266 | 141 | pageStack.push(contentPage); | 78 | enabled: model.source != "" |
267 | 79 | progression: true | ||
268 | 80 | selected: index === widgetList.currentIndex | ||
269 | 81 | onClicked: { | ||
270 | 82 | var source = Qt.resolvedUrl(model.source); | ||
271 | 83 | var newPage = columns.addPageToNextColumn(mainPage, source); | ||
272 | 84 | newPage.title = model.label; | ||
273 | 85 | widgetList.currentIndex = index; | ||
274 | 142 | } | 86 | } |
275 | 143 | } | 87 | } |
276 | 144 | } | 88 | } |
277 | @@ -146,30 +90,6 @@ | |||
278 | 146 | } | 90 | } |
279 | 147 | } | 91 | } |
280 | 148 | 92 | ||
281 | 149 | Page { | ||
282 | 150 | id: contentPage | ||
283 | 151 | active: selectedWidget != null | ||
284 | 152 | title: selectedWidget ? selectedWidget.label : "" | ||
285 | 153 | /* Page internally sets the topMargin of its flickable to account for | ||
286 | 154 | the height of the header. Undo it when unsetting the flickable. | ||
287 | 155 | */ | ||
288 | 156 | onFlickableChanged: if (!flickable && contentLoader.item) contentLoader.item.flickable.topMargin = 0; | ||
289 | 157 | onActiveChanged: if (gallery.state == "narrow" && !active) { | ||
290 | 158 | selectedWidget = null; | ||
291 | 159 | } | ||
292 | 160 | |||
293 | 161 | Loader { | ||
294 | 162 | id: contentLoader | ||
295 | 163 | objectName: "contentLoader" | ||
296 | 164 | anchors.fill: parent | ||
297 | 165 | source: selectedWidget ? selectedWidget.source : "" | ||
298 | 166 | } | ||
299 | 167 | } | ||
300 | 168 | |||
301 | 169 | PageStack { | ||
302 | 170 | id: pageStack | ||
303 | 171 | } | ||
304 | 172 | |||
305 | 173 | WidgetsModel { | 93 | WidgetsModel { |
306 | 174 | id: widgetsModel | 94 | id: widgetsModel |
307 | 175 | } | 95 | } |
308 | 176 | 96 | ||
309 | === modified file 'modules/Ubuntu/Components/1.2/stack.js' | |||
310 | --- modules/Ubuntu/Components/1.2/stack.js 2015-04-30 08:32:44 +0000 | |||
311 | +++ modules/Ubuntu/Components/1.2/stack.js 2015-07-10 13:56:40 +0000 | |||
312 | @@ -41,4 +41,5 @@ | |||
313 | 41 | if (this.size() < 1) return undefined; | 41 | if (this.size() < 1) return undefined; |
314 | 42 | return elements[elements.length-1]; | 42 | return elements[elements.length-1]; |
315 | 43 | } | 43 | } |
316 | 44 | |||
317 | 44 | } | 45 | } |
318 | 45 | 46 | ||
319 | === added file 'modules/Ubuntu/Components/1.3/ColumnMetrics.qml' | |||
320 | --- modules/Ubuntu/Components/1.3/ColumnMetrics.qml 1970-01-01 00:00:00 +0000 | |||
321 | +++ modules/Ubuntu/Components/1.3/ColumnMetrics.qml 2015-07-10 13:56:40 +0000 | |||
322 | @@ -0,0 +1,53 @@ | |||
323 | 1 | /* | ||
324 | 2 | * Copyright 2015 Canonical Ltd. | ||
325 | 3 | * | ||
326 | 4 | * This program is free software; you can redistribute it and/or modify | ||
327 | 5 | * it under the terms of the GNU Lesser General Public License as published by | ||
328 | 6 | * the Free Software Foundation; version 3. | ||
329 | 7 | * | ||
330 | 8 | * This program is distributed in the hope that it will be useful, | ||
331 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
332 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
333 | 11 | * GNU Lesser General Public License for more details. | ||
334 | 12 | * | ||
335 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
336 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
337 | 15 | */ | ||
338 | 16 | |||
339 | 17 | import QtQuick 2.4 | ||
340 | 18 | |||
341 | 19 | /*! | ||
342 | 20 | \qmltype ColumnMetrics | ||
343 | 21 | \inqmlmodule Ubuntu.Components 1.3 | ||
344 | 22 | \since Ubuntu.Components 1.3 | ||
345 | 23 | \ingroup ubuntu | ||
346 | 24 | \brief Component configuring the metrics of a column in MultiColumnView. | ||
347 | 25 | \internal | ||
348 | 26 | |||
349 | 27 | */ | ||
350 | 28 | QtObject { | ||
351 | 29 | /*! | ||
352 | 30 | 1-based value identifying the column the metrics to be applied to. | ||
353 | 31 | */ | ||
354 | 32 | property int column | ||
355 | 33 | |||
356 | 34 | /*! | ||
357 | 35 | Specifies whether the width of the column should fill the available space | ||
358 | 36 | of the MultiColumnView column or not. Defaults to \a false. | ||
359 | 37 | */ | ||
360 | 38 | property bool fillWidth: false | ||
361 | 39 | |||
362 | 40 | /*! | ||
363 | 41 | Specifies the minimum width of the column. If the value is greater than | ||
364 | 42 | \b MultiColumnView::defaultColumnWidth, the value will be set as width for | ||
365 | 43 | the column. | ||
366 | 44 | */ | ||
367 | 45 | property real minimumWidth: 0 | ||
368 | 46 | |||
369 | 47 | /*! | ||
370 | 48 | Specifies the maximum width of the column. If the value is smaller than | ||
371 | 49 | \b MultiColumnView::defaultColumnWidth, the value will be set as width for | ||
372 | 50 | the column. A maximum value of 0 will be ignored. | ||
373 | 51 | */ | ||
374 | 52 | property real maximumWidth: Number.POSITIVE_INFINITY | ||
375 | 53 | } | ||
376 | 0 | 54 | ||
377 | === added file 'modules/Ubuntu/Components/1.3/MultiColumnView.qml' | |||
378 | --- modules/Ubuntu/Components/1.3/MultiColumnView.qml 1970-01-01 00:00:00 +0000 | |||
379 | +++ modules/Ubuntu/Components/1.3/MultiColumnView.qml 2015-07-10 13:56:40 +0000 | |||
380 | @@ -0,0 +1,553 @@ | |||
381 | 1 | /* | ||
382 | 2 | * Copyright 2015 Canonical Ltd. | ||
383 | 3 | * | ||
384 | 4 | * This program is free software; you can redistribute it and/or modify | ||
385 | 5 | * it under the terms of the GNU Lesser General Public License as published by | ||
386 | 6 | * the Free Software Foundation; version 3. | ||
387 | 7 | * | ||
388 | 8 | * This program is distributed in the hope that it will be useful, | ||
389 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
390 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
391 | 11 | * GNU Lesser General Public License for more details. | ||
392 | 12 | * | ||
393 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
394 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
395 | 15 | */ | ||
396 | 16 | |||
397 | 17 | import QtQuick 2.4 | ||
398 | 18 | import QtQuick.Layouts 1.1 | ||
399 | 19 | import Ubuntu.Components 1.3 | ||
400 | 20 | import "stack.js" as Stack | ||
401 | 21 | |||
402 | 22 | /*! | ||
403 | 23 | \qmltype MultiColumnView | ||
404 | 24 | \inqmlmodule Ubuntu.Components 1.3 | ||
405 | 25 | \since Ubuntu.Components 1.3 | ||
406 | 26 | \ingroup ubuntu | ||
407 | 27 | \brief View with multiple columns of Pages. | ||
408 | 28 | |||
409 | 29 | The component provides a flexible way of viewing a stack of pages in one or | ||
410 | 30 | more columns. Unlike in PageStack, there can be more than one Page active at | ||
411 | 31 | a time, depending on the number of the columns in the view. | ||
412 | 32 | |||
413 | 33 | MultiColumnView stores pages added in a tree. Pages are added relative to a | ||
414 | 34 | given page, either as sibling (\l addPageToCurrentColumn) or as child | ||
415 | 35 | (\l addPageToNextColumn). This means that removing a non-leaf page from the Page | ||
416 | 36 | tree will remove all its children from the page tree. | ||
417 | 37 | |||
418 | 38 | The columns are populated from left to right. The column a page is added to is | ||
419 | 39 | detected based on the source page that is given to the functions adding the page. | ||
420 | 40 | The pages can be added either to the same column the source page resides or to | ||
421 | 41 | the column next to the source page. Giving a null value to the source page will | ||
422 | 42 | add the page to the leftmost column of the view. | ||
423 | 43 | |||
424 | 44 | The primary page, the very first page must be specified through the \l primaryPage | ||
425 | 45 | property. The property cannot be changed after component completion and can hold | ||
426 | 46 | a Page instance, a Component or a url to a document defining a Page. The page | ||
427 | 47 | cannot be removed from the view. | ||
428 | 48 | |||
429 | 49 | \note Unlike PageStack or Page the component does not fill its parent by default. | ||
430 | 50 | |||
431 | 51 | \qml | ||
432 | 52 | import QtQuick 2.4 | ||
433 | 53 | import Ubuntu.Components 1.3 | ||
434 | 54 | |||
435 | 55 | MainView { | ||
436 | 56 | width: units.gu(80) | ||
437 | 57 | height: units.gu(71) | ||
438 | 58 | |||
439 | 59 | MultiColumnView { | ||
440 | 60 | anchors.fill: parent | ||
441 | 61 | primaryPage: page1 | ||
442 | 62 | Page { | ||
443 | 63 | id: page1 | ||
444 | 64 | title: "Main page" | ||
445 | 65 | Column { | ||
446 | 66 | Button { | ||
447 | 67 | text: "Add Page2 above " + title | ||
448 | 68 | onClicked: page1.pageStack.addPageToCurrentColumn(page1, page2) | ||
449 | 69 | } | ||
450 | 70 | Button { | ||
451 | 71 | text: "Add Page3 next to " + title | ||
452 | 72 | onClicked: page1.pageStack.addPageToNextColumn(page1, page3) | ||
453 | 73 | } | ||
454 | 74 | } | ||
455 | 75 | } | ||
456 | 76 | Page { | ||
457 | 77 | id: page2 | ||
458 | 78 | title: "Page #2" | ||
459 | 79 | } | ||
460 | 80 | Page { | ||
461 | 81 | id: page3 | ||
462 | 82 | title: "Page #3" | ||
463 | 83 | } | ||
464 | 84 | } | ||
465 | 85 | } | ||
466 | 86 | \endqml | ||
467 | 87 | |||
468 | 88 | MultiColumnView supports adaptive column handling. When the number of columns changes at | ||
469 | 89 | runtime the pages are automatically rearranged. To understand it better, let's take the following example: | ||
470 | 90 | \qml | ||
471 | 91 | import QtQuick 2.4 | ||
472 | 92 | import Ubuntu.Components 1.3 | ||
473 | 93 | |||
474 | 94 | MainView { | ||
475 | 95 | width: units.gu(120) | ||
476 | 96 | height: units.gu(71) | ||
477 | 97 | |||
478 | 98 | MultiColumnView { | ||
479 | 99 | anchors.fill: parent | ||
480 | 100 | primaryPage: page1 | ||
481 | 101 | Page { | ||
482 | 102 | id: page1 | ||
483 | 103 | title: "Main page" | ||
484 | 104 | Button { | ||
485 | 105 | text: "Add Page2 next to " + title | ||
486 | 106 | onClicked: page1.pageStack.addPageToNextColumn(page1, page2) | ||
487 | 107 | } | ||
488 | 108 | } | ||
489 | 109 | Page { | ||
490 | 110 | id: page2 | ||
491 | 111 | title: "Page #2" | ||
492 | 112 | Button { | ||
493 | 113 | text: "Add Page3 next to " + title | ||
494 | 114 | onClicked: page2.pageStack.addPageToNextColumn(page2, page3) | ||
495 | 115 | } | ||
496 | 116 | } | ||
497 | 117 | Page { | ||
498 | 118 | id: page3 | ||
499 | 119 | title: "Page #3" | ||
500 | 120 | } | ||
501 | 121 | } | ||
502 | 122 | } | ||
503 | 123 | \endqml | ||
504 | 124 | |||
505 | 125 | When the code is run on sufficiently wide screen, like a desktop or TV, | ||
506 | 126 | it will launch with multiple columns. | ||
507 | 127 | |||
508 | 128 | \c page1 is set to be the primary page, \c page2 will be added to column next to | ||
509 | 129 | \c page1 (to column 2) and \c page3 next to \c page2 (column 3). When the window | ||
510 | 130 | is resized to have its size below 80 GU, the component will switch to 1 column | ||
511 | 131 | mode, and \c page3 will be placed in the last column, and the header for \c page2 | ||
512 | 132 | will have a back button, indicating that there is a page below it. If the window | ||
513 | 133 | is resized to contain only one column, all pages will be shown in that column, so | ||
514 | 134 | the component will act as PageStack. Resizing the window back to 2 columns will place the pages side-by-side. | ||
515 | 135 | |||
516 | 136 | \note In the above example if \c page2 is removed, that will remove all its child | ||
517 | 137 | pages, meaning \c page3 will also be removed. | ||
518 | 138 | |||
519 | 139 | \sa PageStack | ||
520 | 140 | */ | ||
521 | 141 | |||
522 | 142 | PageTreeNode { | ||
523 | 143 | id: multiColumnView | ||
524 | 144 | |||
525 | 145 | Page { | ||
526 | 146 | // MultiColumnView has its own split headers, so | ||
527 | 147 | // disable the application header. | ||
528 | 148 | id: appHeaderControlPage | ||
529 | 149 | head { | ||
530 | 150 | locked: true | ||
531 | 151 | visible: false | ||
532 | 152 | } | ||
533 | 153 | // title is set in attachPage() when the attached Page.column === 0 | ||
534 | 154 | } | ||
535 | 155 | |||
536 | 156 | /*! | ||
537 | 157 | The property holds the first Page which will be added to the view. If the | ||
538 | 158 | view has more than one column, the page will be added to the leftmost column. | ||
539 | 159 | The property can hold either a Page instance, a component holding a Page | ||
540 | 160 | or a QML document defining the Page. The property cannot be changed after | ||
541 | 161 | component completion. | ||
542 | 162 | */ | ||
543 | 163 | property var primaryPage | ||
544 | 164 | |||
545 | 165 | /*! | ||
546 | 166 | \qmlmethod Item addPageToCurrentColumn(Item sourcePage, var page[, var properties]) | ||
547 | 167 | Adds a \c page to the column the \c sourcePage resides in. \c page can be a | ||
548 | 168 | Component or a file. \c properties is a JSON object containing properties | ||
549 | 169 | to be set when page is created. \c sourcePage must be active. Returns the | ||
550 | 170 | instance of the page created. | ||
551 | 171 | */ | ||
552 | 172 | function addPageToCurrentColumn(sourcePage, page, properties) { | ||
553 | 173 | return d.addPageToColumn(d.columnForPage(sourcePage), sourcePage, page, properties); | ||
554 | 174 | } | ||
555 | 175 | |||
556 | 176 | /*! | ||
557 | 177 | \qmlmethod Item addPageToNextColumn(Item sourcePage, var page[, var properties]) | ||
558 | 178 | Same as \l addPageToCurrentColumn except that the \c page is added to the column | ||
559 | 179 | next to the one the \c sourcePage resides. If \c sourcePage is null, the new | ||
560 | 180 | page will be added to the leftmost column. If \c sourcePage is located in the | ||
561 | 181 | rightmost column, the new page will be pushed to the same column as \c sourcePage. | ||
562 | 182 | */ | ||
563 | 183 | function addPageToNextColumn(sourcePage, page, properties) { | ||
564 | 184 | return d.addPageToColumn(d.columnForPage(sourcePage) + 1, sourcePage, page, properties); | ||
565 | 185 | } | ||
566 | 186 | |||
567 | 187 | /*! | ||
568 | 188 | \qmlmethod void removePages(Item page) | ||
569 | 189 | The function removes and deletes all pages up to and including \c page | ||
570 | 190 | is reached. If the \a page is the same as the \l primaryPage, only its child | ||
571 | 191 | pages will be removed. | ||
572 | 192 | */ | ||
573 | 193 | function removePages(page) { | ||
574 | 194 | // remove nodes which have page as ascendant | ||
575 | 195 | var node = d.stack.top(); | ||
576 | 196 | while (node && node.childOf(page)) { | ||
577 | 197 | d.popAndSetPageForColumn(node); | ||
578 | 198 | node = d.stack.top(); | ||
579 | 199 | } | ||
580 | 200 | while (node && node.object == page && node != d.stack.first()) { | ||
581 | 201 | d.popAndSetPageForColumn(node); | ||
582 | 202 | node = d.stack.top(); | ||
583 | 203 | } | ||
584 | 204 | } | ||
585 | 205 | |||
586 | 206 | /* | ||
587 | 207 | internals | ||
588 | 208 | */ | ||
589 | 209 | |||
590 | 210 | Component.onCompleted: { | ||
591 | 211 | d.relayout(); | ||
592 | 212 | d.completed = true; | ||
593 | 213 | if (primaryPage) { | ||
594 | 214 | var wrapper = d.createWrapper(primaryPage); | ||
595 | 215 | d.addWrappedPage(wrapper); | ||
596 | 216 | } else { | ||
597 | 217 | console.warn("No primary page set. No pages can be added without a primary page."); | ||
598 | 218 | } | ||
599 | 219 | } | ||
600 | 220 | onPrimaryPageChanged: { | ||
601 | 221 | if (d.completed) { | ||
602 | 222 | console.warn("Cannot change primaryPage after completion."); | ||
603 | 223 | return; | ||
604 | 224 | } | ||
605 | 225 | } | ||
606 | 226 | |||
607 | 227 | QtObject { | ||
608 | 228 | id: d | ||
609 | 229 | |||
610 | 230 | property bool completed: false | ||
611 | 231 | property var stack: new Stack.Stack() | ||
612 | 232 | |||
613 | 233 | property int columns: multiColumnView.width >= units.gu(80) ? 2 : 1 | ||
614 | 234 | /*! internal */ | ||
615 | 235 | onColumnsChanged: { | ||
616 | 236 | if (columns <= 0) { | ||
617 | 237 | console.warn("There must be a minimum of one column set."); | ||
618 | 238 | columns = 1; | ||
619 | 239 | } | ||
620 | 240 | d.relayout(); | ||
621 | 241 | } | ||
622 | 242 | property real defaultColumnWidth: units.gu(40) | ||
623 | 243 | onDefaultColumnWidthChanged: body.applyMetrics() | ||
624 | 244 | property list<ColumnMetrics> columnMetrics | ||
625 | 245 | |||
626 | 246 | function createWrapper(page, properties) { | ||
627 | 247 | var wrapperComponent = Qt.createComponent("PageWrapper.qml"); | ||
628 | 248 | var wrapperObject = wrapperComponent.createObject(hiddenPages); | ||
629 | 249 | wrapperObject.pageStack = multiColumnView; | ||
630 | 250 | wrapperObject.properties = properties; | ||
631 | 251 | // set reference last because it will trigger creation of the object | ||
632 | 252 | // with specified properties. | ||
633 | 253 | wrapperObject.reference = page; | ||
634 | 254 | return wrapperObject; | ||
635 | 255 | } | ||
636 | 256 | |||
637 | 257 | function addWrappedPage(pageWrapper) { | ||
638 | 258 | stack.push(pageWrapper); | ||
639 | 259 | pageWrapper.parentWrapper = stack.find(pageWrapper.parentPage); | ||
640 | 260 | var targetColumn = MathUtils.clamp(pageWrapper.column, 0, d.columns - 1); | ||
641 | 261 | // replace page holder's child | ||
642 | 262 | var holder = body.children[targetColumn]; | ||
643 | 263 | holder.detachCurrentPage(); | ||
644 | 264 | holder.attachPage(pageWrapper) | ||
645 | 265 | } | ||
646 | 266 | |||
647 | 267 | function columnForPage(page) { | ||
648 | 268 | var wrapper = stack.find(page); | ||
649 | 269 | return wrapper ? wrapper.column : 0; | ||
650 | 270 | } | ||
651 | 271 | |||
652 | 272 | function addPageToColumn(column, sourcePage, page, properties) { | ||
653 | 273 | if (column < 0) { | ||
654 | 274 | console.warn("Column must be >= 0."); | ||
655 | 275 | return; | ||
656 | 276 | } | ||
657 | 277 | if (!sourcePage) { | ||
658 | 278 | console.warn("No sourcePage specified. Page will not be added."); | ||
659 | 279 | return; | ||
660 | 280 | } | ||
661 | 281 | if (!d.stack.find(sourcePage)) { | ||
662 | 282 | console.warn("sourcePage must be added to the view to add new page."); | ||
663 | 283 | return; | ||
664 | 284 | } | ||
665 | 285 | |||
666 | 286 | var wrapper = d.createWrapper(page, properties); | ||
667 | 287 | wrapper.parentPage = sourcePage; | ||
668 | 288 | wrapper.column = column; | ||
669 | 289 | d.addWrappedPage(wrapper); | ||
670 | 290 | return wrapper.object; | ||
671 | 291 | } | ||
672 | 292 | |||
673 | 293 | // node is a triplet of {page, column, parentPage} | ||
674 | 294 | function popAndSetPageForColumn(node) { | ||
675 | 295 | stack.pop(); | ||
676 | 296 | var effectiveColumn = MathUtils.clamp(node.column, 0, d.columns - 1); | ||
677 | 297 | var columnHolder = body.children[effectiveColumn]; | ||
678 | 298 | // is the page in a column? | ||
679 | 299 | if (node == columnHolder.pageWrapper) { | ||
680 | 300 | // detach page from column | ||
681 | 301 | columnHolder.detachCurrentPage(); | ||
682 | 302 | } | ||
683 | 303 | node.parent = null; | ||
684 | 304 | var prevPage = stack.topForColumn(effectiveColumn, effectiveColumn < d.columns - 1); | ||
685 | 305 | if (prevPage) { | ||
686 | 306 | columnHolder.attachPage(prevPage); | ||
687 | 307 | } | ||
688 | 308 | if (node.canDestroy) { | ||
689 | 309 | node.destroyObject(); | ||
690 | 310 | } | ||
691 | 311 | } | ||
692 | 312 | |||
693 | 313 | // relayouts when column count changes | ||
694 | 314 | function relayout() { | ||
695 | 315 | if (body.children.length == d.columns) return; | ||
696 | 316 | if (body.children.length > d.columns) { | ||
697 | 317 | // need to remove few columns, the last ones | ||
698 | 318 | while (body.children.length > d.columns) { | ||
699 | 319 | var holder = body.children[body.children.length - 1]; | ||
700 | 320 | holder.detachCurrentPage(); | ||
701 | 321 | holder.parent = null; | ||
702 | 322 | holder.destroy(); | ||
703 | 323 | } | ||
704 | 324 | } else { | ||
705 | 325 | var prevColumns = body.children.length; | ||
706 | 326 | |||
707 | 327 | // add columns | ||
708 | 328 | for (var i = 0; i < d.columns - prevColumns; i++) { | ||
709 | 329 | pageHolderComponent.createObject(body); | ||
710 | 330 | } | ||
711 | 331 | } | ||
712 | 332 | rearrangePages(); | ||
713 | 333 | } | ||
714 | 334 | |||
715 | 335 | function rearrangePages() { | ||
716 | 336 | for (var column = d.columns - 1; column >= 0; column--) { | ||
717 | 337 | var holder = body.children[column]; | ||
718 | 338 | var pageWrapper = stack.topForColumn(column, column < (d.columns - 1)); | ||
719 | 339 | if (!pageWrapper) { | ||
720 | 340 | continue; | ||
721 | 341 | } | ||
722 | 342 | if (!pageWrapper.parent) { | ||
723 | 343 | // this should never happen, so if it does, we have a bug! | ||
724 | 344 | console.error("Found a page which wasn't parented anywhere!", pageWrapper.object.title); | ||
725 | 345 | continue; | ||
726 | 346 | } | ||
727 | 347 | // detach current page from holder if differs | ||
728 | 348 | if (holder.pageWrapper != pageWrapper) { | ||
729 | 349 | holder.detachCurrentPage(); | ||
730 | 350 | } | ||
731 | 351 | if (pageWrapper.parent == hiddenPages) { | ||
732 | 352 | // add the page to the column | ||
733 | 353 | holder.attachPage(pageWrapper); | ||
734 | 354 | } else if (pageWrapper.pageHolder != holder) { | ||
735 | 355 | // detach the pageWrapper from its holder | ||
736 | 356 | if (pageWrapper.pageHolder) { | ||
737 | 357 | pageWrapper.pageHolder.detachCurrentPage(); | ||
738 | 358 | } | ||
739 | 359 | // then attach to this holder | ||
740 | 360 | holder.attachPage(pageWrapper); | ||
741 | 361 | } | ||
742 | 362 | } | ||
743 | 363 | } | ||
744 | 364 | } | ||
745 | 365 | |||
746 | 366 | // default metrics | ||
747 | 367 | Component { | ||
748 | 368 | id: defaultMetrics | ||
749 | 369 | ColumnMetrics { | ||
750 | 370 | fillWidth: column == d.columns | ||
751 | 371 | minimumWidth: d.defaultColumnWidth | ||
752 | 372 | } | ||
753 | 373 | } | ||
754 | 374 | |||
755 | 375 | // Page holder component, can have only one Page as child at a time, all stacked pages | ||
756 | 376 | // will be parented into hiddenPages | ||
757 | 377 | Component { | ||
758 | 378 | id: pageHolderComponent | ||
759 | 379 | // Page uses the height of the parentNode for its height, so make | ||
760 | 380 | // the holder a PageTreeNode that determines the Page height. | ||
761 | 381 | PageTreeNode { | ||
762 | 382 | id: holder | ||
763 | 383 | active: false | ||
764 | 384 | objectName: "ColumnHolder" + column | ||
765 | 385 | property PageWrapper pageWrapper | ||
766 | 386 | property int column | ||
767 | 387 | property alias config: subHeader.config | ||
768 | 388 | property ColumnMetrics metrics: setDefaultMetrics() | ||
769 | 389 | |||
770 | 390 | Layout.fillWidth: metrics.fillWidth | ||
771 | 391 | Layout.fillHeight: true | ||
772 | 392 | Layout.preferredWidth: metrics.maximumWidth > 0 ? | ||
773 | 393 | MathUtils.clamp(d.defaultColumnWidth, metrics.minimumWidth, metrics.maximumWidth) : | ||
774 | 394 | d.defaultColumnWidth | ||
775 | 395 | Layout.minimumWidth: metrics.minimumWidth | ||
776 | 396 | Layout.maximumWidth: metrics.maximumWidth | ||
777 | 397 | |||
778 | 398 | // prevent the pages from taking the app header height into account. | ||
779 | 399 | __propagated: null | ||
780 | 400 | Item { | ||
781 | 401 | id: holderBody | ||
782 | 402 | objectName: parent.objectName + "Body" | ||
783 | 403 | anchors { | ||
784 | 404 | top: subHeader.bottom | ||
785 | 405 | bottom: parent.bottom | ||
786 | 406 | left: parent.left | ||
787 | 407 | right: parent.right | ||
788 | 408 | rightMargin: verticalDivider.width | ||
789 | 409 | } | ||
790 | 410 | // we need to clip because the header does not have a background | ||
791 | 411 | clip: true | ||
792 | 412 | } | ||
793 | 413 | |||
794 | 414 | property alias head: subHeader | ||
795 | 415 | StyledItem { | ||
796 | 416 | id: subHeader | ||
797 | 417 | anchors { | ||
798 | 418 | left: parent.left | ||
799 | 419 | top: parent.top | ||
800 | 420 | right: parent.right | ||
801 | 421 | } | ||
802 | 422 | height: body.headerHeight | ||
803 | 423 | |||
804 | 424 | styleName: config ? "PageHeadStyle" : "" | ||
805 | 425 | theme.version: Ubuntu.toolkitVersion | ||
806 | 426 | objectName: "Header" + column | ||
807 | 427 | |||
808 | 428 | property real preferredHeight: subHeader.__styleInstance ? | ||
809 | 429 | subHeader.__styleInstance.implicitHeight : | ||
810 | 430 | 0 | ||
811 | 431 | onPreferredHeightChanged: { | ||
812 | 432 | body.updateHeaderHeight(preferredHeight); | ||
813 | 433 | } | ||
814 | 434 | |||
815 | 435 | property PageHeadConfiguration config: null | ||
816 | 436 | property Item contents: null | ||
817 | 437 | |||
818 | 438 | property color dividerColor: multiColumnView.__propagated.header.dividerColor | ||
819 | 439 | property color panelColor: multiColumnView.__propagated.header.panelColor | ||
820 | 440 | |||
821 | 441 | visible: holder.pageWrapper && holder.pageWrapper.active | ||
822 | 442 | } | ||
823 | 443 | |||
824 | 444 | Rectangle { | ||
825 | 445 | id: verticalDivider | ||
826 | 446 | anchors { | ||
827 | 447 | top: parent.top | ||
828 | 448 | bottom: parent.bottom | ||
829 | 449 | right: parent.right | ||
830 | 450 | } | ||
831 | 451 | width: (column == (d.columns - 1)) || !pageWrapper ? 0 : units.dp(1) | ||
832 | 452 | color: subHeader.dividerColor | ||
833 | 453 | } | ||
834 | 454 | |||
835 | 455 | function attachPage(page) { | ||
836 | 456 | pageWrapper = page; | ||
837 | 457 | pageWrapper.parent = holderBody; | ||
838 | 458 | pageWrapper.pageHolder = holder; | ||
839 | 459 | pageWrapper.active = true; | ||
840 | 460 | |||
841 | 461 | if (pageWrapper.object.hasOwnProperty("head")) { | ||
842 | 462 | subHeader.config = pageWrapper.object.head; | ||
843 | 463 | } | ||
844 | 464 | if (pageWrapper.column === 0 && pageWrapper.object.hasOwnProperty("title")) { | ||
845 | 465 | // set the application title | ||
846 | 466 | appHeaderControlPage.title = pageWrapper.object.title; | ||
847 | 467 | } | ||
848 | 468 | } | ||
849 | 469 | function detachCurrentPage() { | ||
850 | 470 | if (!pageWrapper) return undefined; | ||
851 | 471 | var wrapper = pageWrapper; | ||
852 | 472 | // remove header | ||
853 | 473 | wrapper.active = false; | ||
854 | 474 | subHeader.config = null; | ||
855 | 475 | pageWrapper = null; | ||
856 | 476 | wrapper.parent = hiddenPages; | ||
857 | 477 | wrapper.pageHolder = null; | ||
858 | 478 | return wrapper; | ||
859 | 479 | } | ||
860 | 480 | |||
861 | 481 | function setDefaultMetrics() { | ||
862 | 482 | var result = defaultMetrics.createObject(holder); | ||
863 | 483 | result.column = Qt.binding(function() { return holder.column + 1; }); | ||
864 | 484 | return result; | ||
865 | 485 | } | ||
866 | 486 | } | ||
867 | 487 | } | ||
868 | 488 | |||
869 | 489 | /*! \internal */ | ||
870 | 490 | // Pages declared as children will be placed directly into hiddenPages | ||
871 | 491 | default property alias data: hiddenPages.data | ||
872 | 492 | Item { | ||
873 | 493 | id: hiddenPages | ||
874 | 494 | objectName: "HiddenPagePool" | ||
875 | 495 | visible: false | ||
876 | 496 | // make sure nothing is shown eventually | ||
877 | 497 | clip: true | ||
878 | 498 | } | ||
879 | 499 | |||
880 | 500 | // Holds the columns holding the pages visible. Each column has only one page | ||
881 | 501 | // as child, the invisible stacked ones are all stored in the hiddenPages | ||
882 | 502 | // component. The stack keeps the column index onto which those should be moved | ||
883 | 503 | // once they become visible. | ||
884 | 504 | RowLayout { | ||
885 | 505 | id: body | ||
886 | 506 | objectName: "body" | ||
887 | 507 | anchors.fill: parent | ||
888 | 508 | spacing: 0 | ||
889 | 509 | |||
890 | 510 | property real headerHeight: 0 | ||
891 | 511 | |||
892 | 512 | function updateHeaderHeight(newHeight) { | ||
893 | 513 | if (newHeight > body.headerHeight) { | ||
894 | 514 | body.headerHeight = newHeight; | ||
895 | 515 | } else { | ||
896 | 516 | var h = 0; | ||
897 | 517 | var subHeight = 0; | ||
898 | 518 | for (var i = 0; i < children.length; i++) { | ||
899 | 519 | subHeight = children[i].head.preferredHeight; | ||
900 | 520 | if (subHeight > h) h = subHeight; | ||
901 | 521 | } | ||
902 | 522 | body.headerHeight = h; | ||
903 | 523 | } | ||
904 | 524 | } | ||
905 | 525 | |||
906 | 526 | onChildrenChanged: { | ||
907 | 527 | // all children should have Layout.fillWidth false, except the last one | ||
908 | 528 | for (var i = 0; i < children.length; i++) { | ||
909 | 529 | children[i].column = i; | ||
910 | 530 | } | ||
911 | 531 | applyMetrics(); | ||
912 | 532 | } | ||
913 | 533 | |||
914 | 534 | function applyMetrics() { | ||
915 | 535 | for (var i = 0; i < children.length; i++) { | ||
916 | 536 | var holder = children[i]; | ||
917 | 537 | // search for the column metrics | ||
918 | 538 | var metrics = null; | ||
919 | 539 | for (var j = 0; j < d.columnMetrics.length; j++) { | ||
920 | 540 | if (d.columnMetrics[j].column == (i + 1)) { | ||
921 | 541 | metrics = d.columnMetrics[j]; | ||
922 | 542 | break; | ||
923 | 543 | } | ||
924 | 544 | } | ||
925 | 545 | if (!metrics) { | ||
926 | 546 | metrics = holder.setDefaultMetrics(); | ||
927 | 547 | } | ||
928 | 548 | holder.metrics = metrics; | ||
929 | 549 | updateHeaderHeight(0); | ||
930 | 550 | } | ||
931 | 551 | } | ||
932 | 552 | } | ||
933 | 553 | } | ||
934 | 0 | 554 | ||
935 | === modified file 'modules/Ubuntu/Components/1.3/OrientationHelper.qml' | |||
936 | --- modules/Ubuntu/Components/1.3/OrientationHelper.qml 2015-07-02 17:39:03 +0000 | |||
937 | +++ modules/Ubuntu/Components/1.3/OrientationHelper.qml 2015-07-10 13:56:40 +0000 | |||
938 | @@ -117,7 +117,7 @@ | |||
939 | 117 | return 0; | 117 | return 0; |
940 | 118 | 118 | ||
941 | 119 | var availableHeight = orientationHelper.parent.height; | 119 | var availableHeight = orientationHelper.parent.height; |
943 | 120 | if (d.stateAngle === 0 && UbuntuApplication.inputMethod.visible && anchorToKeyboard) | 120 | if (d.stateAngle === 0 && UbuntuApplication.inputMethod && UbuntuApplication.inputMethod.visible && anchorToKeyboard) |
944 | 121 | availableHeight -= UbuntuApplication.inputMethod.keyboardRectangle.height; | 121 | availableHeight -= UbuntuApplication.inputMethod.keyboardRectangle.height; |
945 | 122 | return availableHeight; | 122 | return availableHeight; |
946 | 123 | } | 123 | } |
947 | 124 | 124 | ||
948 | === modified file 'modules/Ubuntu/Components/1.3/Page.qml' | |||
949 | --- modules/Ubuntu/Components/1.3/Page.qml 2015-07-02 20:40:01 +0000 | |||
950 | +++ modules/Ubuntu/Components/1.3/Page.qml 2015-07-10 13:56:40 +0000 | |||
951 | @@ -43,6 +43,7 @@ | |||
952 | 43 | readonly property alias head: headerConfig | 43 | readonly property alias head: headerConfig |
953 | 44 | Toolkit13.PageHeadConfiguration { | 44 | Toolkit13.PageHeadConfiguration { |
954 | 45 | id: headerConfig | 45 | id: headerConfig |
955 | 46 | title: page.title | ||
956 | 46 | } | 47 | } |
957 | 47 | 48 | ||
958 | 48 | Toolkit13.Object { | 49 | Toolkit13.Object { |
959 | 49 | 50 | ||
960 | === modified file 'modules/Ubuntu/Components/1.3/PageHeadConfiguration.qml' | |||
961 | --- modules/Ubuntu/Components/1.3/PageHeadConfiguration.qml 2015-04-30 08:32:44 +0000 | |||
962 | +++ modules/Ubuntu/Components/1.3/PageHeadConfiguration.qml 2015-07-10 13:56:40 +0000 | |||
963 | @@ -56,4 +56,6 @@ | |||
964 | 56 | 56 | ||
965 | 57 | // auto-updated by AppHeader, but may be set by the developer | 57 | // auto-updated by AppHeader, but may be set by the developer |
966 | 58 | property bool visible | 58 | property bool visible |
967 | 59 | |||
968 | 60 | property string title | ||
969 | 59 | } | 61 | } |
970 | 60 | 62 | ||
971 | === modified file 'modules/Ubuntu/Components/1.3/PageWrapper.qml' | |||
972 | --- modules/Ubuntu/Components/1.3/PageWrapper.qml 2015-05-05 16:23:29 +0000 | |||
973 | +++ modules/Ubuntu/Components/1.3/PageWrapper.qml 2015-07-10 13:56:40 +0000 | |||
974 | @@ -46,6 +46,43 @@ | |||
975 | 46 | property bool canDestroy: false | 46 | property bool canDestroy: false |
976 | 47 | 47 | ||
977 | 48 | /*! | 48 | /*! |
978 | 49 | Column number in MultiColumnView. | ||
979 | 50 | */ | ||
980 | 51 | property int column: 0 | ||
981 | 52 | |||
982 | 53 | /*! | ||
983 | 54 | Parent page. | ||
984 | 55 | */ | ||
985 | 56 | property Item parentPage | ||
986 | 57 | |||
987 | 58 | /*! | ||
988 | 59 | Parent PageWrapper or the parentPage. | ||
989 | 60 | */ | ||
990 | 61 | property Item parentWrapper | ||
991 | 62 | |||
992 | 63 | /*! | ||
993 | 64 | Page holder in MultiColumnView | ||
994 | 65 | */ | ||
995 | 66 | property Item pageHolder | ||
996 | 67 | |||
997 | 68 | /*! | ||
998 | 69 | Returns true if the current PageWrapper is a child of the given page | ||
999 | 70 | */ | ||
1000 | 71 | function childOf(page) { | ||
1001 | 72 | if (parentPage == page) return true; | ||
1002 | 73 | if (page && parentWrapper) { | ||
1003 | 74 | var wrapper = parentWrapper; | ||
1004 | 75 | while (wrapper) { | ||
1005 | 76 | if (wrapper.object == page) { | ||
1006 | 77 | return true; | ||
1007 | 78 | } | ||
1008 | 79 | wrapper = wrapper.parentWrapper; | ||
1009 | 80 | } | ||
1010 | 81 | } | ||
1011 | 82 | return false; | ||
1012 | 83 | } | ||
1013 | 84 | |||
1014 | 85 | /*! | ||
1015 | 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. |
1016 | 50 | */ | 87 | */ |
1017 | 51 | active: false | 88 | active: false |
1018 | 52 | 89 | ||
1019 | === added file 'modules/Ubuntu/Components/1.3/stack.js' | |||
1020 | --- modules/Ubuntu/Components/1.3/stack.js 1970-01-01 00:00:00 +0000 | |||
1021 | +++ modules/Ubuntu/Components/1.3/stack.js 2015-07-10 13:56:40 +0000 | |||
1022 | @@ -0,0 +1,75 @@ | |||
1023 | 1 | /* | ||
1024 | 2 | * Copyright 2015 Canonical Ltd. | ||
1025 | 3 | * | ||
1026 | 4 | * This program is free software; you can redistribute it and/or modify | ||
1027 | 5 | * it under the terms of the GNU Lesser General Public License as published by | ||
1028 | 6 | * the Free Software Foundation; version 3. | ||
1029 | 7 | * | ||
1030 | 8 | * This program is distributed in the hope that it will be useful, | ||
1031 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1032 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1033 | 11 | * GNU Lesser General Public License for more details. | ||
1034 | 12 | * | ||
1035 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
1036 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1037 | 15 | */ | ||
1038 | 16 | |||
1039 | 17 | .pragma library | ||
1040 | 18 | // By defining Stack as a function, we can make its variables private, | ||
1041 | 19 | // and force calls to Stack to make use of the functions we define. | ||
1042 | 20 | function Stack() { | ||
1043 | 21 | var elements; | ||
1044 | 22 | this.clear = function() { | ||
1045 | 23 | elements = []; | ||
1046 | 24 | } | ||
1047 | 25 | |||
1048 | 26 | this.clear(); | ||
1049 | 27 | |||
1050 | 28 | this.push = function(element) { | ||
1051 | 29 | elements.push(element); | ||
1052 | 30 | }; | ||
1053 | 31 | |||
1054 | 32 | this.pop = function() { | ||
1055 | 33 | elements.pop(); | ||
1056 | 34 | }; | ||
1057 | 35 | |||
1058 | 36 | this.size = function() { | ||
1059 | 37 | return elements.length; | ||
1060 | 38 | } | ||
1061 | 39 | |||
1062 | 40 | this.first = function() { | ||
1063 | 41 | return this.size() > 0 ? elements[0] : null; | ||
1064 | 42 | } | ||
1065 | 43 | |||
1066 | 44 | this.top = function() { | ||
1067 | 45 | if (this.size() < 1) return undefined; | ||
1068 | 46 | return elements[elements.length-1]; | ||
1069 | 47 | } | ||
1070 | 48 | |||
1071 | 49 | // returns the topmost element for the column, using exact column match | ||
1072 | 50 | // in case the column to be extracted is less than the columns available | ||
1073 | 51 | this.topForColumn = function(column, exactMatch) { | ||
1074 | 52 | if (exactMatch == undefined) { | ||
1075 | 53 | exactMatch = false; | ||
1076 | 54 | } | ||
1077 | 55 | |||
1078 | 56 | for (var i = elements.length - 1; i >= 0; i--) { | ||
1079 | 57 | var node = elements[i]; | ||
1080 | 58 | if ((exactMatch && elements[i].column == column) || (!exactMatch && elements[i].column >= column)) { | ||
1081 | 59 | return elements[i]; | ||
1082 | 60 | } | ||
1083 | 61 | } | ||
1084 | 62 | return undefined; | ||
1085 | 63 | } | ||
1086 | 64 | |||
1087 | 65 | // returns the node the Page is stored; undefined if not found | ||
1088 | 66 | this.find = function(page) { | ||
1089 | 67 | if (!page) return null; | ||
1090 | 68 | for (var i = elements.length - 1; i >= 0; i--) { | ||
1091 | 69 | if (elements[i].object == page) { | ||
1092 | 70 | return elements[i]; | ||
1093 | 71 | } | ||
1094 | 72 | } | ||
1095 | 73 | return null; | ||
1096 | 74 | } | ||
1097 | 75 | } | ||
1098 | 0 | 76 | ||
1099 | === modified file 'modules/Ubuntu/Components/Themes/Ambiance/1.3/IconButtonStyle.qml' | |||
1100 | --- modules/Ubuntu/Components/Themes/Ambiance/1.3/IconButtonStyle.qml 2015-05-26 10:54:14 +0000 | |||
1101 | +++ modules/Ubuntu/Components/Themes/Ambiance/1.3/IconButtonStyle.qml 2015-07-10 13:56:40 +0000 | |||
1102 | @@ -26,7 +26,7 @@ | |||
1103 | 26 | /*! | 26 | /*! |
1104 | 27 | The color of the icons. | 27 | The color of the icons. |
1105 | 28 | */ | 28 | */ |
1107 | 29 | property color foregroundColor: "#808080" | 29 | property color foregroundColor: "#333333" |
1108 | 30 | 30 | ||
1109 | 31 | /*! | 31 | /*! |
1110 | 32 | The background color of the button. | 32 | The background color of the button. |
1111 | 33 | 33 | ||
1112 | === modified file 'modules/Ubuntu/Components/Themes/Ambiance/1.3/PageHeadStyle.qml' | |||
1113 | --- modules/Ubuntu/Components/Themes/Ambiance/1.3/PageHeadStyle.qml 2015-06-23 13:22:10 +0000 | |||
1114 | +++ modules/Ubuntu/Components/Themes/Ambiance/1.3/PageHeadStyle.qml 2015-07-10 13:56:40 +0000 | |||
1115 | @@ -27,15 +27,22 @@ | |||
1116 | 27 | textLeftMargin: units.gu(2) | 27 | textLeftMargin: units.gu(2) |
1117 | 28 | maximumNumberOfActions: 3 | 28 | maximumNumberOfActions: 3 |
1118 | 29 | 29 | ||
1119 | 30 | PageHeadConfiguration { | ||
1120 | 31 | id: defaultConfig | ||
1121 | 32 | } | ||
1122 | 33 | |||
1123 | 34 | property PageHeadConfiguration config: styledItem.config ? | ||
1124 | 35 | styledItem.config : | ||
1125 | 36 | defaultConfig | ||
1126 | 30 | /*! | 37 | /*! |
1127 | 31 | The color of the buttons in the header. | 38 | The color of the buttons in the header. |
1128 | 32 | */ | 39 | */ |
1130 | 33 | property color buttonColor: styledItem.config.foregroundColor | 40 | property color buttonColor: headerStyle.config.foregroundColor |
1131 | 34 | 41 | ||
1132 | 35 | /*! | 42 | /*! |
1133 | 36 | The color of the title text. | 43 | The color of the title text. |
1134 | 37 | */ | 44 | */ |
1136 | 38 | property color titleColor: styledItem.config.foregroundColor | 45 | property color titleColor: headerStyle.config.foregroundColor |
1137 | 39 | 46 | ||
1138 | 40 | // FIXME: When the three panel color properties below are removed, | 47 | // FIXME: When the three panel color properties below are removed, |
1139 | 41 | // update unity8/Dash/PageHeader to use the new theming (currently | 48 | // update unity8/Dash/PageHeader to use the new theming (currently |
1140 | @@ -84,6 +91,12 @@ | |||
1141 | 84 | // have a separator. | 91 | // have a separator. |
1142 | 85 | property alias __separator_visible: divider.visible | 92 | property alias __separator_visible: divider.visible |
1143 | 86 | 93 | ||
1144 | 94 | //Default background for the header is white according to latest visuals | ||
1145 | 95 | Rectangle { | ||
1146 | 96 | anchors.fill: parent | ||
1147 | 97 | color: "white" | ||
1148 | 98 | } | ||
1149 | 99 | |||
1150 | 87 | Rectangle { | 100 | Rectangle { |
1151 | 88 | id: divider | 101 | id: divider |
1152 | 89 | anchors { | 102 | anchors { |
1153 | @@ -100,16 +113,23 @@ | |||
1154 | 100 | objectName: "headerSectionsItem" | 113 | objectName: "headerSectionsItem" |
1155 | 101 | anchors { | 114 | anchors { |
1156 | 102 | left: parent.left | 115 | left: parent.left |
1158 | 103 | leftMargin: units.gu(2) | 116 | right: parent.right |
1159 | 117 | //leftMargin: units.gu(2) | ||
1160 | 104 | bottom: divider.top | 118 | bottom: divider.top |
1161 | 105 | } | 119 | } |
1162 | 120 | visible: model && model.length > 0 | ||
1163 | 106 | enabled: sections.enabled | 121 | enabled: sections.enabled |
1170 | 107 | height: model && model.length > 0 ? implicitHeight : 0 | 122 | height: visible ? implicitHeight : 0 |
1171 | 108 | 123 | ||
1172 | 109 | property PageHeadSections sections: styledItem.config.sections | 124 | property PageHeadSections sections: headerStyle.config.sections |
1173 | 110 | model: sections.model | 125 | model: sections ? sections.model : null |
1174 | 111 | 126 | ||
1175 | 112 | onSelectedIndexChanged: sections.selectedIndex = sectionsItem.selectedIndex | 127 | onSelectedIndexChanged: { |
1176 | 128 | if (sections) { | ||
1177 | 129 | sections.selectedIndex = sectionsItem.selectedIndex; | ||
1178 | 130 | } | ||
1179 | 131 | } | ||
1180 | 132 | |||
1181 | 113 | Connections { | 133 | Connections { |
1182 | 114 | target: sectionsItem.sections | 134 | target: sectionsItem.sections |
1183 | 115 | onSelectedIndexChanged: sectionsItem.selectedIndex = sectionsItem.sections.selectedIndex | 135 | onSelectedIndexChanged: sectionsItem.selectedIndex = sectionsItem.sections.selectedIndex |
1184 | @@ -244,10 +264,10 @@ | |||
1185 | 244 | PageHeadButton { | 264 | PageHeadButton { |
1186 | 245 | id: customBackButton | 265 | id: customBackButton |
1187 | 246 | objectName: "customBackButton" | 266 | objectName: "customBackButton" |
1192 | 247 | action: styledItem.config.backAction | 267 | action: headerStyle.config.backAction |
1193 | 248 | visible: null !== styledItem.config.backAction && | 268 | visible: null !== headerStyle.config.backAction && |
1194 | 249 | styledItem.config.backAction.visible | 269 | headerStyle.config.backAction.visible |
1195 | 250 | color: styledItem.config.foregroundColor | 270 | color: headerStyle.config.foregroundColor |
1196 | 251 | } | 271 | } |
1197 | 252 | 272 | ||
1198 | 253 | PageHeadButton { | 273 | PageHeadButton { |
1199 | @@ -258,10 +278,10 @@ | |||
1200 | 258 | visible: styledItem.pageStack !== null && | 278 | visible: styledItem.pageStack !== null && |
1201 | 259 | styledItem.pageStack !== undefined && | 279 | styledItem.pageStack !== undefined && |
1202 | 260 | styledItem.pageStack.depth > 1 && | 280 | styledItem.pageStack.depth > 1 && |
1204 | 261 | !styledItem.config.backAction | 281 | !headerStyle.config.backAction |
1205 | 262 | 282 | ||
1206 | 263 | text: "back" | 283 | text: "back" |
1208 | 264 | color: styledItem.config.foregroundColor | 284 | color: headerStyle.config.foregroundColor |
1209 | 265 | 285 | ||
1210 | 266 | onTriggered: { | 286 | onTriggered: { |
1211 | 267 | styledItem.pageStack.pop(); | 287 | styledItem.pageStack.pop(); |
1212 | @@ -327,13 +347,13 @@ | |||
1213 | 327 | Label { | 347 | Label { |
1214 | 328 | objectName: "header_title_label" | 348 | objectName: "header_title_label" |
1215 | 329 | LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft | 349 | LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft |
1217 | 330 | visible: !contentsContainer.visible && styledItem.config.preset === "" | 350 | visible: !contentsContainer.visible && headerStyle.config.preset === "" |
1218 | 331 | anchors { | 351 | anchors { |
1219 | 332 | left: parent.left | 352 | left: parent.left |
1220 | 333 | right: parent.right | 353 | right: parent.right |
1221 | 334 | verticalCenter: parent.verticalCenter | 354 | verticalCenter: parent.verticalCenter |
1222 | 335 | } | 355 | } |
1224 | 336 | text: styledItem.title | 356 | text: headerStyle.config.title |
1225 | 337 | font.weight: headerStyle.fontWeight | 357 | font.weight: headerStyle.fontWeight |
1226 | 338 | fontSize: headerStyle.fontSize | 358 | fontSize: headerStyle.fontSize |
1227 | 339 | color: headerStyle.titleColor | 359 | color: headerStyle.titleColor |
1228 | @@ -346,7 +366,7 @@ | |||
1229 | 346 | // when the bindings below is no longer active | 366 | // when the bindings below is no longer active |
1230 | 347 | id: contentsContainer | 367 | id: contentsContainer |
1231 | 348 | anchors.fill: parent | 368 | anchors.fill: parent |
1233 | 349 | visible: styledItem.contents || styledItem.config.contents | 369 | visible: styledItem.contents || headerStyle.config.contents |
1234 | 350 | } | 370 | } |
1235 | 351 | Binding { | 371 | Binding { |
1236 | 352 | target: styledItem.contents | 372 | target: styledItem.contents |
1237 | @@ -361,16 +381,23 @@ | |||
1238 | 361 | when: styledItem.contents | 381 | when: styledItem.contents |
1239 | 362 | } | 382 | } |
1240 | 363 | Binding { | 383 | Binding { |
1242 | 364 | target: styledItem.config.contents | 384 | target: headerStyle.config.contents |
1243 | 365 | property: "parent" | 385 | property: "parent" |
1244 | 366 | value: contentsContainer | 386 | value: contentsContainer |
1246 | 367 | when: styledItem.config.contents && !styledItem.contents | 387 | when: headerStyle.config.contents && !styledItem.contents |
1247 | 368 | } | 388 | } |
1248 | 369 | } | 389 | } |
1249 | 370 | 390 | ||
1250 | 371 | ActionBar { | 391 | ActionBar { |
1251 | 372 | id: actionsContainer | 392 | id: actionsContainer |
1252 | 373 | objectName: "headerActionBar" | 393 | objectName: "headerActionBar" |
1253 | 394 | |||
1254 | 395 | //currently hardcoded maximum number of actions we want to show in the header | ||
1255 | 396 | property int maxActions: 6 | ||
1256 | 397 | |||
1257 | 398 | //UX: we reserver max 30% of the header width for actions | ||
1258 | 399 | property real headerPortionDedicatedToActions: 0.3 | ||
1259 | 400 | |||
1260 | 374 | anchors { | 401 | anchors { |
1261 | 375 | top: parent.top | 402 | top: parent.top |
1262 | 376 | right: rightAnchor.left | 403 | right: rightAnchor.left |
1263 | @@ -378,8 +405,10 @@ | |||
1264 | 378 | } | 405 | } |
1265 | 379 | height: headerStyle.contentHeight | 406 | height: headerStyle.contentHeight |
1266 | 380 | 407 | ||
1269 | 381 | actions: styledItem.config.actions | 408 | actions: headerStyle.config.actions |
1270 | 382 | numberOfSlots: 3 | 409 | |
1271 | 410 | //FIXME: currently hardcoded icon width! | ||
1272 | 411 | numberOfSlots: Math.min(Math.min(Math.floor(headerStyle.width * headerPortionDedicatedToActions / units.gu(4)), actions.length), maxActions) | ||
1273 | 383 | } | 412 | } |
1274 | 384 | } | 413 | } |
1275 | 385 | } | 414 | } |
1276 | 386 | 415 | ||
1277 | === modified file 'modules/Ubuntu/Components/Themes/Ambiance/1.3/Palette.qml' | |||
1278 | --- modules/Ubuntu/Components/Themes/Ambiance/1.3/Palette.qml 2015-04-27 07:19:22 +0000 | |||
1279 | +++ modules/Ubuntu/Components/Themes/Ambiance/1.3/Palette.qml 2015-07-10 13:56:40 +0000 | |||
1280 | @@ -33,7 +33,7 @@ | |||
1281 | 33 | } | 33 | } |
1282 | 34 | selected { | 34 | selected { |
1283 | 35 | background: Qt.rgba(0, 0, 0, 0.05) | 35 | background: Qt.rgba(0, 0, 0, 0.05) |
1285 | 36 | backgroundText: UbuntuColors.darkGrey | 36 | backgroundText: "#333333"//"#5D5D5D"//UbuntuColors.darkGrey |
1286 | 37 | selection: selected.foreground | 37 | selection: selected.foreground |
1287 | 38 | foreground: Qt.rgba(UbuntuColors.blue.r, UbuntuColors.blue.g, UbuntuColors.blue.b, 0.2) | 38 | foreground: Qt.rgba(UbuntuColors.blue.r, UbuntuColors.blue.g, UbuntuColors.blue.b, 0.2) |
1288 | 39 | foregroundText: UbuntuColors.darkGrey | 39 | foregroundText: UbuntuColors.darkGrey |
1289 | 40 | 40 | ||
1290 | === modified file 'modules/Ubuntu/Components/Themes/Ambiance/1.3/SectionsStyle.qml' | |||
1291 | --- modules/Ubuntu/Components/Themes/Ambiance/1.3/SectionsStyle.qml 2015-06-16 23:18:45 +0000 | |||
1292 | +++ modules/Ubuntu/Components/Themes/Ambiance/1.3/SectionsStyle.qml 2015-07-10 13:56:40 +0000 | |||
1293 | @@ -1,3 +1,20 @@ | |||
1294 | 1 | /* !!!!!!!!!! THIS IS A WIP SECTION HEADER !!!!!!!!!!!!!!! | ||
1295 | 2 | |||
1296 | 3 | * Copyright 2015 Canonical Ltd. | ||
1297 | 4 | * | ||
1298 | 5 | * This program is free software; you can redistribute it and/or modify | ||
1299 | 6 | * it under the terms of the GNU Lesser General Public License as published by | ||
1300 | 7 | * the Free Software Foundation; version 3. | ||
1301 | 8 | * | ||
1302 | 9 | * This program is distributed in the hope that it will be useful, | ||
1303 | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1304 | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1305 | 12 | * GNU Lesser General Public License for more details. | ||
1306 | 13 | * | ||
1307 | 14 | * You should have received a copy of the GNU Lesser General Public License | ||
1308 | 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1309 | 16 | */ | ||
1310 | 17 | |||
1311 | 1 | /* | 18 | /* |
1312 | 2 | * Copyright 2015 Canonical Ltd. | 19 | * Copyright 2015 Canonical Ltd. |
1313 | 3 | * | 20 | * |
1314 | @@ -15,20 +32,33 @@ | |||
1315 | 15 | */ | 32 | */ |
1316 | 16 | import QtQuick 2.4 | 33 | import QtQuick 2.4 |
1317 | 17 | import Ubuntu.Components 1.3 | 34 | import Ubuntu.Components 1.3 |
1318 | 35 | import QtGraphicalEffects 1.0 | ||
1319 | 18 | 36 | ||
1320 | 19 | Item { | 37 | Item { |
1321 | 20 | id: sectionsStyle | 38 | id: sectionsStyle |
1322 | 21 | 39 | ||
1324 | 22 | implicitWidth: sectionsRow.width | 40 | //VISUAL CHANGES: |
1325 | 41 | //- sectionColor | ||
1326 | 42 | //- added underlineColor | ||
1327 | 43 | //- fontsize to medium | ||
1328 | 44 | //- font weight to Light | ||
1329 | 45 | |||
1330 | 46 | implicitWidth: units.gu(50) //sectionsRow.width | ||
1331 | 23 | implicitHeight: units.gu(4) | 47 | implicitHeight: units.gu(4) |
1332 | 24 | 48 | ||
1333 | 25 | enabled: styledItem.enabled | 49 | enabled: styledItem.enabled |
1334 | 26 | opacity: enabled ? 1.0 : 0.5 | 50 | opacity: enabled ? 1.0 : 0.5 |
1335 | 27 | |||
1336 | 28 | /*! | 51 | /*! |
1337 | 29 | The foreground color of unselected sections. | 52 | The foreground color of unselected sections. |
1338 | 30 | */ | 53 | */ |
1340 | 31 | property color sectionColor: theme.palette.selected.backgroundText | 54 | //FIXME: hardcoded color |
1341 | 55 | property color sectionColor: "#888888"//theme.palette.selected.backgroundText | ||
1342 | 56 | |||
1343 | 57 | /*! | ||
1344 | 58 | The foreground color of underline rectangle of unselected sections. | ||
1345 | 59 | */ | ||
1346 | 60 | //FIXME: hardcoded color | ||
1347 | 61 | property color underlineColor: Qt.rgba(0,0,0,0.2)//theme.palette.selected.backgroundText | ||
1348 | 32 | 62 | ||
1349 | 33 | /*! | 63 | /*! |
1350 | 34 | The foreground color of the selected section. | 64 | The foreground color of the selected section. |
1351 | @@ -43,7 +73,7 @@ | |||
1352 | 43 | /*! | 73 | /*! |
1353 | 44 | The font size for the text in the buttons. | 74 | The font size for the text in the buttons. |
1354 | 45 | */ | 75 | */ |
1356 | 46 | property string fontSize: "small" | 76 | property string fontSize: "medium" |
1357 | 47 | 77 | ||
1358 | 48 | /*! | 78 | /*! |
1359 | 49 | The spacing on the left and right sides of the label | 79 | The spacing on the left and right sides of the label |
1360 | @@ -56,30 +86,174 @@ | |||
1361 | 56 | */ | 86 | */ |
1362 | 57 | property real underlineHeight: units.dp(2) | 87 | property real underlineHeight: units.dp(2) |
1363 | 58 | 88 | ||
1375 | 59 | Row { | 89 | property real __listViewMargin: units.gu(3) |
1376 | 60 | id: sectionsRow | 90 | property bool __hoveringLeft: false |
1377 | 61 | anchors { | 91 | property bool __hoveringRight: false |
1378 | 62 | top: parent.top | 92 | |
1379 | 63 | bottom: parent.bottom | 93 | onStateChanged: console.log(state) |
1380 | 64 | horizontalCenter: parent.horizontalCenter | 94 | |
1381 | 65 | } | 95 | clip: true |
1382 | 66 | width: childrenRect.width | 96 | |
1383 | 67 | 97 | //This item is needed for the OpacityMask feature. It is needed to make sure that when we | |
1384 | 68 | Repeater { | 98 | //bring a list element into view, that element won't be covered by the opacity mask. So we |
1385 | 69 | id: sectionsRepeater | 99 | //disable clipping on the list but we give it margins. This way when an item is repositioned |
1386 | 100 | //to be within the listview, that item will not be positioned under the opacity mask. (which is | ||
1387 | 101 | //what would have happened if the listview were filling the parent) | ||
1388 | 102 | Item { | ||
1389 | 103 | id: listviewcontainer | ||
1390 | 104 | anchors.fill: parent | ||
1391 | 105 | |||
1392 | 106 | //We need this because OpacityMask will draw this listview for us, but at the | ||
1393 | 107 | //same time we still want to get the input events! | ||
1394 | 108 | opacity: 0 | ||
1395 | 109 | |||
1396 | 110 | ListView { | ||
1397 | 111 | id: sectionsListView | ||
1398 | 112 | objectName: "section_listview" | ||
1399 | 113 | |||
1400 | 114 | property bool animateContentX: false | ||
1401 | 115 | |||
1402 | 116 | //this is just to disable keyboard navigation to avoid messing with contentX/contentWidth while | ||
1403 | 117 | //the view is moving | ||
1404 | 118 | focus: !moving | ||
1405 | 119 | |||
1406 | 120 | SmoothedAnimation { | ||
1407 | 121 | id: contentXAnim | ||
1408 | 122 | target: sectionsListView | ||
1409 | 123 | property: "contentX" | ||
1410 | 124 | duration: UbuntuAnimation.FastDuration | ||
1411 | 125 | velocity: units.gu(10) | ||
1412 | 126 | //alwaysRunToEnd: true | ||
1413 | 127 | } | ||
1414 | 128 | |||
1415 | 129 | function ensureItemIsInTheMiddle(currentItem) { | ||
1416 | 130 | if (currentItem !== null) { | ||
1417 | 131 | //stop the flick before doing computations | ||
1418 | 132 | if (moving) { | ||
1419 | 133 | return | ||
1420 | 134 | } | ||
1421 | 135 | if (dragging || flicking) { | ||
1422 | 136 | cancelFlick() | ||
1423 | 137 | } | ||
1424 | 138 | |||
1425 | 139 | console.log("CONTENTX", contentX) | ||
1426 | 140 | if (contentXAnim.running) { contentXAnim.stop() } | ||
1427 | 141 | console.log("CONTENTXAFTER", contentX) | ||
1428 | 142 | |||
1429 | 143 | var pos = currentItem.mapToItem(sectionsListView.contentItem, 0, 0) | ||
1430 | 144 | |||
1431 | 145 | //In the case where we're moving to an item which is outside of the screen (this | ||
1432 | 146 | //happens when using Keyboard navigation after scrolling the view), we | ||
1433 | 147 | //try working around the fact that contentWidth/X keeps changing | ||
1434 | 148 | //by using listview's functions (WHICH DON'T HAVE ANIMATIONS though) just to position the | ||
1435 | 149 | //view more or less at the right place. After that, we'll fake an animation from one side to the middle of the | ||
1436 | 150 | //screen. | ||
1437 | 151 | /*if (pos.x + pos.width <= sectionsListView.x) { | ||
1438 | 152 | positionViewAtIndex(currentIndex, ListView.Beginning) | ||
1439 | 153 | //refresh the mapping as we've moved the view | ||
1440 | 154 | pos = currentItem.mapToItem(sectionsListView, 0, 0) | ||
1441 | 155 | |||
1442 | 156 | } else if (pos.x >= sectionsListView.x + sectionsListView.width) { | ||
1443 | 157 | positionViewAtIndex(currentIndex, ListView.End) | ||
1444 | 158 | //refresh the mapping as we've moved the view | ||
1445 | 159 | pos = currentItem.mapToItem(sectionsListView, 0, 0) | ||
1446 | 160 | }*/ | ||
1447 | 161 | |||
1448 | 162 | var newContentX = pos.x - sectionsListView.width/2 + currentItem.width/2 | ||
1449 | 163 | contentXAnim.from = contentX | ||
1450 | 164 | //make sure we don't overshoot bounds | ||
1451 | 165 | contentXAnim.to = Math.max(originX, Math.min(newContentX, originX + contentWidth - width)) | ||
1452 | 166 | contentXAnim.start() | ||
1453 | 167 | |||
1454 | 168 | console.log("OUR VALUES", contentX, originX, contentWidth, contentXAnim.to) | ||
1455 | 169 | //FIXME: due to listview internal implementation and the fact that delegates don't have a fixed size, | ||
1456 | 170 | //this breaks in some cases, due to originX changing *after* we start the | ||
1457 | 171 | //animation. In those cases the current item doesn't align to the horizontal center because we compute | ||
1458 | 172 | //contentXAnim.to based on an originX which is then changed by the internals after we have already | ||
1459 | 173 | //started the animation. We could fixup the position at the end of the animation, | ||
1460 | 174 | //but that would just be a workaround. | ||
1461 | 175 | } | ||
1462 | 176 | } | ||
1463 | 177 | |||
1464 | 178 | onContentXChanged: console.log(contentX, originX, contentWidth) | ||
1465 | 179 | |||
1466 | 180 | anchors.fill: parent | ||
1467 | 181 | anchors.leftMargin: __listViewMargin | ||
1468 | 182 | anchors.rightMargin: __listViewMargin | ||
1469 | 183 | |||
1470 | 184 | //FIXME: this means when we resize the window it will refocus on the current item even if it's outside of the view! | ||
1471 | 185 | //NOTE: removing this also breaks the alignment when the sections are initialized, because of contentX/contentWidth changing | ||
1472 | 186 | onWidthChanged: ensureItemIsInTheMiddle(currentItem) | ||
1473 | 187 | |||
1474 | 188 | orientation: ListView.Horizontal | ||
1475 | 189 | |||
1476 | 190 | //We want to block dragging but at the same time we want the keyboard to work!!! | ||
1477 | 191 | //interactive: contentWidth > width | ||
1478 | 192 | boundsBehavior: Flickable.StopAtBounds | ||
1479 | 193 | |||
1480 | 70 | model: styledItem.model | 194 | model: styledItem.model |
1483 | 71 | objectName: "sections_repeater" | 195 | |
1484 | 72 | AbstractButton { | 196 | //We need this to make sure that we have delegates for the whole width, since we have |
1485 | 197 | //clip disabled. Read more | ||
1486 | 198 | displayMarginBeginning: __listViewMargin | ||
1487 | 199 | displayMarginEnd: __listViewMargin | ||
1488 | 200 | |||
1489 | 201 | //make sure that the currentItem is in the middle when everything is initialized | ||
1490 | 202 | Component.onCompleted: ensureItemIsInTheMiddle(currentItem) | ||
1491 | 203 | |||
1492 | 204 | //FIXME: keyboard navigation offered by ListView will break this, won't it? | ||
1493 | 205 | currentIndex: styledItem.selectedIndex | ||
1494 | 206 | onCurrentIndexChanged: { | ||
1495 | 207 | styledItem.selectedIndex = currentIndex | ||
1496 | 208 | } | ||
1497 | 209 | onCurrentItemChanged: { | ||
1498 | 210 | //adjust contentX so that the item is kept in the middle | ||
1499 | 211 | //don't use ListView.ApplyRange because that does an awkward animation when you select an item | ||
1500 | 212 | //*while* the current item is outside of screen | ||
1501 | 213 | ensureItemIsInTheMiddle(currentItem) | ||
1502 | 214 | } | ||
1503 | 215 | |||
1504 | 216 | //highlightMoveDuration: UbuntuAnimation.BriskDuration | ||
1505 | 217 | //highlightResizeDuration: UbuntuAnimation.BriskDuration | ||
1506 | 218 | |||
1507 | 219 | highlightFollowsCurrentItem: false | ||
1508 | 220 | //DON'T use ApplyRange, because it will cause a weird animation when you select an item which is on screen | ||
1509 | 221 | //with the previously selected item being out of screen | ||
1510 | 222 | //highlightRangeMode: ListView.ApplyRange | ||
1511 | 223 | //preferredHighlightEnd: width/2 + currentItem.width/2 | ||
1512 | 224 | //preferredHighlightBegin: width/2 - currentItem.width/2 | ||
1513 | 225 | |||
1514 | 226 | //highlightFollowsCurrentItem will resize the highlight element to fill the delegate, | ||
1515 | 227 | //so we need an Item in the middle to set a height for the highlight rectangle different | ||
1516 | 228 | //from the delegate size | ||
1517 | 229 | highlight: Item { | ||
1518 | 230 | //show the highlight on top of the delegate | ||
1519 | 231 | z: 2 | ||
1520 | 232 | |||
1521 | 233 | x: sectionsListView.currentItem ? sectionsListView.currentItem.x : -width | ||
1522 | 234 | width: sectionsListView.currentItem ? sectionsListView.currentItem.width : 0 | ||
1523 | 235 | height: sectionsListView.currentItem ? sectionsListView.currentItem.height : 0 | ||
1524 | 236 | |||
1525 | 237 | Rectangle { | ||
1526 | 238 | anchors { | ||
1527 | 239 | bottom: parent.bottom | ||
1528 | 240 | left: parent.left | ||
1529 | 241 | right: parent.right | ||
1530 | 242 | } | ||
1531 | 243 | height: sectionsStyle.underlineHeight | ||
1532 | 244 | color: sectionsStyle.selectedSectionColor | ||
1533 | 245 | } | ||
1534 | 246 | |||
1535 | 247 | Behavior on x { UbuntuNumberAnimation {} } | ||
1536 | 248 | } | ||
1537 | 249 | |||
1538 | 250 | delegate: AbstractButton { | ||
1539 | 73 | id: sectionButton | 251 | id: sectionButton |
1540 | 74 | anchors { | ||
1541 | 75 | top: parent ? parent.top : undefined | ||
1542 | 76 | bottom: parent ? parent.bottom : undefined | ||
1543 | 77 | } | ||
1544 | 78 | objectName: "section_button_" + index | 252 | objectName: "section_button_" + index |
1545 | 79 | width: label.width + 2 * sectionsStyle.horizontalLabelSpacing | 253 | width: label.width + 2 * sectionsStyle.horizontalLabelSpacing |
1547 | 80 | height: sectionsRow.height | 254 | height: sectionsStyle.height |
1548 | 81 | property bool selected: index === styledItem.selectedIndex | 255 | property bool selected: index === styledItem.selectedIndex |
1550 | 82 | onClicked: styledItem.selectedIndex = index | 256 | onClicked: { styledItem.selectedIndex = index; sectionsListView.forceActiveFocus() } |
1551 | 83 | 257 | ||
1552 | 84 | // Background pressed highlight | 258 | // Background pressed highlight |
1553 | 85 | Rectangle { | 259 | Rectangle { |
1554 | @@ -95,10 +269,14 @@ | |||
1555 | 95 | // modelData may be either a string, or an Action | 269 | // modelData may be either a string, or an Action |
1556 | 96 | text: modelData.hasOwnProperty("text") ? modelData.text : modelData | 270 | text: modelData.hasOwnProperty("text") ? modelData.text : modelData |
1557 | 97 | fontSize: sectionsStyle.fontSize | 271 | fontSize: sectionsStyle.fontSize |
1558 | 272 | font.weight: Font.Light | ||
1559 | 98 | anchors.centerIn: parent | 273 | anchors.centerIn: parent |
1560 | 99 | color: sectionButton.selected ? | 274 | color: sectionButton.selected ? |
1561 | 100 | sectionsStyle.selectedSectionColor : | 275 | sectionsStyle.selectedSectionColor : |
1562 | 101 | sectionsStyle.sectionColor | 276 | sectionsStyle.sectionColor |
1563 | 277 | |||
1564 | 278 | //FIXME: color animation? is that ok to design? what's the duration? | ||
1565 | 279 | Behavior on color { ColorAnimation { duration: 500 } } | ||
1566 | 102 | } | 280 | } |
1567 | 103 | 281 | ||
1568 | 104 | // Section title underline | 282 | // Section title underline |
1569 | @@ -109,11 +287,167 @@ | |||
1570 | 109 | right: parent.right | 287 | right: parent.right |
1571 | 110 | } | 288 | } |
1572 | 111 | height: sectionsStyle.underlineHeight | 289 | height: sectionsStyle.underlineHeight |
1576 | 112 | color: sectionButton.selected ? | 290 | color: sectionsStyle.underlineColor |
1574 | 113 | sectionsStyle.selectedSectionColor : | ||
1575 | 114 | sectionsStyle.sectionColor | ||
1577 | 115 | } | 291 | } |
1578 | 116 | } | 292 | } |
1581 | 117 | } | 293 | |
1582 | 118 | } | 294 | //Behavior on contentX { SmoothedAnimation { duration: UbuntuAnimation.FastDuration; velocity: units.gu(10)} } |
1583 | 295 | |||
1584 | 296 | } | ||
1585 | 297 | } | ||
1586 | 298 | |||
1587 | 299 | MouseArea { | ||
1588 | 300 | id: hoveringArea | ||
1589 | 301 | |||
1590 | 302 | function checkHovering(mouse) { | ||
1591 | 303 | if (mouse.x < __listViewMargin) { | ||
1592 | 304 | if (!__hoveringLeft) __hoveringLeft = true | ||
1593 | 305 | } else if (mouse.x > width - __listViewMargin ) { | ||
1594 | 306 | if (!__hoveringRight) __hoveringRight = true | ||
1595 | 307 | } else { | ||
1596 | 308 | __hoveringLeft = false | ||
1597 | 309 | __hoveringRight = false | ||
1598 | 310 | } | ||
1599 | 311 | } | ||
1600 | 312 | |||
1601 | 313 | onContainsMouseChanged: console.log(containsMouse) | ||
1602 | 314 | anchors.fill: parent | ||
1603 | 315 | hoverEnabled: true | ||
1604 | 316 | |||
1605 | 317 | onPositionChanged: checkHovering(mouse) | ||
1606 | 318 | onExited: { | ||
1607 | 319 | __hoveringLeft = false | ||
1608 | 320 | __hoveringRight = false | ||
1609 | 321 | } | ||
1610 | 322 | onPressed: if (!__hoveringLeft && !__hoveringRight) { | ||
1611 | 323 | mouse.accepted = false | ||
1612 | 324 | } | ||
1613 | 325 | onClicked: { | ||
1614 | 326 | //scroll the list to bring the element which is under the cursor into the view | ||
1615 | 327 | //var item = sectionsListView.itemAt(mouse.x + sectionsListView.contentX - __listViewMargin, mouse.y) | ||
1616 | 328 | //if (item !== null) { | ||
1617 | 329 | //We could use positionViewAtIndex(...) here but it wouldn't provide animation | ||
1618 | 330 | |||
1619 | 331 | if (contentXAnim.running) contentXAnim.stop() | ||
1620 | 332 | |||
1621 | 333 | //Scroll one item at a time with animation | ||
1622 | 334 | //sectionsListView.contentX = __hoveringLeft ? item.mapToItem(sectionsListView.contentItem, 0,0).x : item.mapToItem(sectionsListView.contentItem, 0,0).x - sectionsListView.width + item.width | ||
1623 | 335 | var newContentX = sectionsListView.contentX + (sectionsListView.width * (__hoveringLeft ? -1 : 1)) | ||
1624 | 336 | |||
1625 | 337 | contentXAnim.from = sectionsListView.contentX | ||
1626 | 338 | //make sure we don't overshoot bounds | ||
1627 | 339 | contentXAnim.to = Math.max(sectionsListView.originX, Math.min(newContentX, sectionsListView.originX + sectionsListView.contentWidth - sectionsListView.width)) | ||
1628 | 340 | |||
1629 | 341 | contentXAnim.start() | ||
1630 | 342 | |||
1631 | 343 | //} | ||
1632 | 344 | } | ||
1633 | 345 | |||
1634 | 346 | property real iconsDisabledOpacity: 0.3 | ||
1635 | 347 | |||
1636 | 348 | Icon { | ||
1637 | 349 | id: leftHoveringIcon | ||
1638 | 350 | anchors.left: parent.left | ||
1639 | 351 | anchors.leftMargin: units.gu(1) | ||
1640 | 352 | anchors.verticalCenter: parent.verticalCenter | ||
1641 | 353 | width: units.gu(1) | ||
1642 | 354 | height: units.gu(1) | ||
1643 | 355 | rotation: 180 | ||
1644 | 356 | opacity: sectionsListView.atXBeginning ? hoveringArea.iconsDisabledOpacity : 1.0 | ||
1645 | 357 | name: "chevron" | ||
1646 | 358 | } | ||
1647 | 359 | |||
1648 | 360 | Icon { | ||
1649 | 361 | id: rightHoveringIcon | ||
1650 | 362 | anchors.right: parent.right | ||
1651 | 363 | anchors.rightMargin: units.gu(1) | ||
1652 | 364 | anchors.verticalCenter: parent.verticalCenter | ||
1653 | 365 | width: units.gu(1) | ||
1654 | 366 | height: units.gu(1) | ||
1655 | 367 | opacity: sectionsListView.atXEnd ? hoveringArea.iconsDisabledOpacity : 1.0 | ||
1656 | 368 | name: "chevron" | ||
1657 | 369 | } | ||
1658 | 370 | } | ||
1659 | 371 | |||
1660 | 372 | Rectangle { | ||
1661 | 373 | anchors.left: parent.left | ||
1662 | 374 | anchors.right: parent.right | ||
1663 | 375 | anchors.bottom: parent.bottom | ||
1664 | 376 | height: units.dp(1) | ||
1665 | 377 | color: sectionsStyle.underlineColor | ||
1666 | 378 | } | ||
1667 | 379 | |||
1668 | 380 | LinearGradient { | ||
1669 | 381 | id: gradient | ||
1670 | 382 | |||
1671 | 383 | visible: false | ||
1672 | 384 | anchors.fill: parent | ||
1673 | 385 | start: Qt.point(0,0) | ||
1674 | 386 | end: Qt.point(width,0) | ||
1675 | 387 | |||
1676 | 388 | property real __gradientWidth: units.gu(3) / gradient.width | ||
1677 | 389 | //the width is __gradientWidth, but we want the gradient to actually start/finish at __gradientSplitPosition | ||
1678 | 390 | //just to leave some margin. | ||
1679 | 391 | property real __gradientSplitPosition: 3/4 * __gradientWidth | ||
1680 | 392 | |||
1681 | 393 | gradient: Gradient { | ||
1682 | 394 | //left gradient | ||
1683 | 395 | GradientStop { position: 0.0 ; color: Qt.rgba(1,1,1,0) } | ||
1684 | 396 | GradientStop { position: gradient.__gradientSplitPosition ; color: Qt.rgba(1,1,1,0) } | ||
1685 | 397 | GradientStop { position: gradient.__gradientWidth; color: Qt.rgba(1,1,1,1) } | ||
1686 | 398 | //right gradient | ||
1687 | 399 | GradientStop { position: 1.0 - gradient.__gradientWidth; color: Qt.rgba(1,1,1,1) } | ||
1688 | 400 | GradientStop { position: 1.0 - gradient.__gradientSplitPosition; color: Qt.rgba(1,1,1,0) } | ||
1689 | 401 | GradientStop { position: 1.0; color: Qt.rgba(1,1,1,0) } | ||
1690 | 402 | } | ||
1691 | 403 | |||
1692 | 404 | } | ||
1693 | 405 | |||
1694 | 406 | OpacityMask { | ||
1695 | 407 | id: mask | ||
1696 | 408 | anchors.fill: parent | ||
1697 | 409 | |||
1698 | 410 | source: listviewcontainer | ||
1699 | 411 | maskSource: gradient | ||
1700 | 412 | } | ||
1701 | 413 | |||
1702 | 414 | |||
1703 | 415 | //Since we only show one arrow at a time, let's reuse the same item and handle the property changes with states | ||
1704 | 416 | states: [ | ||
1705 | 417 | State { | ||
1706 | 418 | name: "" | ||
1707 | 419 | PropertyChanges { target: mask; visible: false } | ||
1708 | 420 | PropertyChanges { target: listviewcontainer; opacity: 1.0 } | ||
1709 | 421 | PropertyChanges { target: leftHoveringIcon; visible: false; } | ||
1710 | 422 | PropertyChanges { target: rightHoveringIcon; visible: false; } | ||
1711 | 423 | }, | ||
1712 | 424 | State { | ||
1713 | 425 | name: "hovering" | ||
1714 | 426 | when: hoveringArea.containsMouse | ||
1715 | 427 | PropertyChanges { target: mask; visible: true } | ||
1716 | 428 | PropertyChanges { target: listviewcontainer; opacity: 0.0 } | ||
1717 | 429 | PropertyChanges { target: leftHoveringIcon; visible: true; } | ||
1718 | 430 | PropertyChanges { target: rightHoveringIcon; visible: true; } | ||
1719 | 431 | } | ||
1720 | 432 | //one of the experiments was to only show one arrow at a time on hover | ||
1721 | 433 | /*State { | ||
1722 | 434 | name: "hoveringLeft" | ||
1723 | 435 | when: __hoveringLeft | ||
1724 | 436 | PropertyChanges { target: mask; visible: true } | ||
1725 | 437 | PropertyChanges { target: gradient; mirrored: true } | ||
1726 | 438 | PropertyChanges { target: listviewcontainer; opacity: 0.0 } | ||
1727 | 439 | PropertyChanges { target: hoveringIcon; visible: true; rotation: 180; anchors.leftMargin: units.gu(1) } | ||
1728 | 440 | AnchorChanges { target: hoveringIcon; anchors.left: hoveringArea.left; anchors.right: undefined } | ||
1729 | 441 | }, | ||
1730 | 442 | State { | ||
1731 | 443 | name: "hoveringRight" | ||
1732 | 444 | when: __hoveringRight | ||
1733 | 445 | PropertyChanges { target: mask; visible: true } | ||
1734 | 446 | PropertyChanges { target: gradient; mirrored: false } | ||
1735 | 447 | PropertyChanges { target: listviewcontainer; opacity: 0.0 } | ||
1736 | 448 | PropertyChanges { target: hoveringIcon; visible: true; rotation: 0; anchors.rightMargin: units.gu(1) } | ||
1737 | 449 | AnchorChanges { target: hoveringIcon; anchors.right: hoveringArea.right; anchors.left: undefined } | ||
1738 | 450 | }*/ | ||
1739 | 451 | ] | ||
1740 | 119 | } | 452 | } |
1741 | 453 | |||
1742 | 120 | 454 | ||
1743 | === modified file 'modules/Ubuntu/Components/qmldir' | |||
1744 | --- modules/Ubuntu/Components/qmldir 2015-05-24 14:14:36 +0000 | |||
1745 | +++ modules/Ubuntu/Components/qmldir 2015-07-10 13:56:40 +0000 | |||
1746 | @@ -141,3 +141,4 @@ | |||
1747 | 141 | PullToRefresh 1.3 1.3/PullToRefresh.qml | 141 | PullToRefresh 1.3 1.3/PullToRefresh.qml |
1748 | 142 | UbuntuListView 1.3 1.3/UbuntuListView11.qml | 142 | UbuntuListView 1.3 1.3/UbuntuListView11.qml |
1749 | 143 | Captions 1.3 1.3/Captions.qml | 143 | Captions 1.3 1.3/Captions.qml |
1750 | 144 | MultiColumnView 1.3 1.3/MultiColumnView.qml | ||
1751 | 144 | 145 | ||
1752 | === modified file 'tests/autopilot/ubuntuuitoolkit/tests/__init__.py' | |||
1753 | --- tests/autopilot/ubuntuuitoolkit/tests/__init__.py 2015-04-14 21:02:06 +0000 | |||
1754 | +++ tests/autopilot/ubuntuuitoolkit/tests/__init__.py 2015-07-10 13:56:40 +0000 | |||
1755 | @@ -205,10 +205,9 @@ | |||
1756 | 205 | 205 | ||
1757 | 206 | def checkPageHeader(self, pageTitle): | 206 | def checkPageHeader(self, pageTitle): |
1758 | 207 | orientationHelper = self.getOrientationHelper() | 207 | orientationHelper = self.getOrientationHelper() |
1763 | 208 | header = orientationHelper.select_single("AppHeader", title=pageTitle) | 208 | header_label = orientationHelper.select_single(objectName="header_title_label", text=pageTitle) |
1764 | 209 | self.assertThat(header, Not(Is(None))) | 209 | self.assertThat(header_label, Not(Is(None))) |
1765 | 210 | self.assertThat(header.visible, Eventually(Equals(True))) | 210 | self.assertThat(header_label.visible, Eventually(Equals(True))) |
1762 | 211 | return header | ||
1766 | 212 | 211 | ||
1767 | 213 | def getObject(self, objectName): | 212 | def getObject(self, objectName): |
1768 | 214 | obj = self.app.select_single(objectName=objectName) | 213 | obj = self.app.select_single(objectName=objectName) |
1769 | 215 | 214 | ||
1770 | === modified file 'tests/resources/navigation/MyCustomPage.qml' | |||
1771 | --- tests/resources/navigation/MyCustomPage.qml 2015-03-03 13:20:06 +0000 | |||
1772 | +++ tests/resources/navigation/MyCustomPage.qml 2015-07-10 13:56:40 +0000 | |||
1773 | @@ -15,7 +15,7 @@ | |||
1774 | 15 | */ | 15 | */ |
1775 | 16 | 16 | ||
1776 | 17 | import QtQuick 2.2 | 17 | import QtQuick 2.2 |
1778 | 18 | import Ubuntu.Components 1.1 | 18 | import Ubuntu.Components 1.3 |
1779 | 19 | 19 | ||
1780 | 20 | Page { | 20 | Page { |
1781 | 21 | title: i18n.tr("My custom page") | 21 | title: i18n.tr("My custom page") |
1782 | 22 | 22 | ||
1783 | === added file 'tests/resources/navigation/SplitViewStack.qml' | |||
1784 | --- tests/resources/navigation/SplitViewStack.qml 1970-01-01 00:00:00 +0000 | |||
1785 | +++ tests/resources/navigation/SplitViewStack.qml 2015-07-10 13:56:40 +0000 | |||
1786 | @@ -0,0 +1,137 @@ | |||
1787 | 1 | /* | ||
1788 | 2 | * Copyright 2015 Canonical Ltd. | ||
1789 | 3 | * | ||
1790 | 4 | * This program is free software; you can redistribute it and/or modify | ||
1791 | 5 | * it under the terms of the GNU Lesser General Public License as published by | ||
1792 | 6 | * the Free Software Foundation; version 3. | ||
1793 | 7 | * | ||
1794 | 8 | * This program is distributed in the hope that it will be useful, | ||
1795 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1796 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1797 | 11 | * GNU Lesser General Public License for more details. | ||
1798 | 12 | * | ||
1799 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
1800 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1801 | 15 | */ | ||
1802 | 16 | |||
1803 | 17 | import QtQuick 2.4 | ||
1804 | 18 | import Ubuntu.Components 1.3 | ||
1805 | 19 | import QtQuick.Layouts 1.1 | ||
1806 | 20 | |||
1807 | 21 | MainView { | ||
1808 | 22 | id: main | ||
1809 | 23 | width: units.gu(100) | ||
1810 | 24 | height: units.gu(71) | ||
1811 | 25 | |||
1812 | 26 | MultiColumnView { | ||
1813 | 27 | id: view | ||
1814 | 28 | anchors.fill: parent | ||
1815 | 29 | columns: { | ||
1816 | 30 | if (width > units.gu(120)) return 3; | ||
1817 | 31 | if (width > units.gu(80)) return 2; | ||
1818 | 32 | return 1; | ||
1819 | 33 | } | ||
1820 | 34 | columnMetrics: [ | ||
1821 | 35 | ColumnMetrics { | ||
1822 | 36 | column: 2 | ||
1823 | 37 | fillWidth: true | ||
1824 | 38 | }, | ||
1825 | 39 | ColumnMetrics { | ||
1826 | 40 | column: 3 | ||
1827 | 41 | fillWidth: false | ||
1828 | 42 | minimumWidth: units.gu(50) | ||
1829 | 43 | } | ||
1830 | 44 | ] | ||
1831 | 45 | primaryPage: page1 | ||
1832 | 46 | |||
1833 | 47 | Page { | ||
1834 | 48 | id: page1 | ||
1835 | 49 | title: "Page #1" | ||
1836 | 50 | |||
1837 | 51 | Rectangle { | ||
1838 | 52 | anchors.fill: parent | ||
1839 | 53 | color: "red" | ||
1840 | 54 | } | ||
1841 | 55 | Column { | ||
1842 | 56 | Button { | ||
1843 | 57 | text: "Add page2 next" | ||
1844 | 58 | onClicked: page1.pageStack.addPageToNextColumn(page1, page2) | ||
1845 | 59 | } | ||
1846 | 60 | Button { | ||
1847 | 61 | text: "Add page4 above" | ||
1848 | 62 | onClicked: page1.pageStack.addPageToCurrentColumn(page1, page4) | ||
1849 | 63 | } | ||
1850 | 64 | } | ||
1851 | 65 | } | ||
1852 | 66 | |||
1853 | 67 | Page { | ||
1854 | 68 | id: page2 | ||
1855 | 69 | title: "Page #2" | ||
1856 | 70 | |||
1857 | 71 | Rectangle { | ||
1858 | 72 | anchors.fill: parent | ||
1859 | 73 | color: "green" | ||
1860 | 74 | } | ||
1861 | 75 | Column { | ||
1862 | 76 | Button { | ||
1863 | 77 | text: "Back..." | ||
1864 | 78 | onClicked: page2.pageStack.removePages(page2) | ||
1865 | 79 | } | ||
1866 | 80 | Button { | ||
1867 | 81 | text: "Add page3 next" | ||
1868 | 82 | onClicked: page2.pageStack.addPageToNextColumn(page2, page3) | ||
1869 | 83 | } | ||
1870 | 84 | } | ||
1871 | 85 | } | ||
1872 | 86 | Page { | ||
1873 | 87 | id: page3 | ||
1874 | 88 | title: "Page #3" | ||
1875 | 89 | |||
1876 | 90 | Rectangle { | ||
1877 | 91 | anchors.fill: parent | ||
1878 | 92 | color: "blue" | ||
1879 | 93 | } | ||
1880 | 94 | Button { | ||
1881 | 95 | text: "Back..." | ||
1882 | 96 | onClicked: page3.pageStack.removePages(page3) | ||
1883 | 97 | } | ||
1884 | 98 | } | ||
1885 | 99 | Page { | ||
1886 | 100 | id: page4 | ||
1887 | 101 | title: "Page #4" | ||
1888 | 102 | |||
1889 | 103 | Rectangle { | ||
1890 | 104 | anchors.fill: parent | ||
1891 | 105 | color: "teal" | ||
1892 | 106 | } | ||
1893 | 107 | Column { | ||
1894 | 108 | Button { | ||
1895 | 109 | text: "Back..." | ||
1896 | 110 | onClicked: page4.pageStack.removePages(page4) | ||
1897 | 111 | } | ||
1898 | 112 | Button { | ||
1899 | 113 | text: "Add page5 next" | ||
1900 | 114 | onClicked: page4.pageStack.addPageToNextColumn(page4, page5) | ||
1901 | 115 | } | ||
1902 | 116 | } | ||
1903 | 117 | } | ||
1904 | 118 | Page { | ||
1905 | 119 | id: page5 | ||
1906 | 120 | title: "Page #5" | ||
1907 | 121 | Rectangle { | ||
1908 | 122 | anchors.fill: parent | ||
1909 | 123 | color: "tan" | ||
1910 | 124 | } | ||
1911 | 125 | Column { | ||
1912 | 126 | Button { | ||
1913 | 127 | text: "Back..." | ||
1914 | 128 | onClicked: page5.pageStack.removePages(page5) | ||
1915 | 129 | } | ||
1916 | 130 | Button { | ||
1917 | 131 | text: "Custom page on same column" | ||
1918 | 132 | onClicked: page5.pageStack.addPageToCurrentColumn(page5, Qt.resolvedUrl("MyCustomPage.qml")) | ||
1919 | 133 | } | ||
1920 | 134 | } | ||
1921 | 135 | } | ||
1922 | 136 | } | ||
1923 | 137 | } | ||
1924 | 0 | 138 | ||
1925 | === added file 'tests/unit_x11/tst_components/ListItemWithLabel.qml' | |||
1926 | --- tests/unit_x11/tst_components/ListItemWithLabel.qml 1970-01-01 00:00:00 +0000 | |||
1927 | +++ tests/unit_x11/tst_components/ListItemWithLabel.qml 2015-07-10 13:56:40 +0000 | |||
1928 | @@ -0,0 +1,30 @@ | |||
1929 | 1 | /* | ||
1930 | 2 | * Copyright 2015 Canonical Ltd. | ||
1931 | 3 | * | ||
1932 | 4 | * This program is free software; you can redistribute it and/or modify | ||
1933 | 5 | * it under the terms of the GNU Lesser General Public License as published by | ||
1934 | 6 | * the Free Software Foundation; version 3. | ||
1935 | 7 | * | ||
1936 | 8 | * This program is distributed in the hope that it will be useful, | ||
1937 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1938 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1939 | 11 | * GNU Lesser General Public License for more details. | ||
1940 | 12 | * | ||
1941 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
1942 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1943 | 15 | */ | ||
1944 | 16 | |||
1945 | 17 | import QtQuick 2.4 | ||
1946 | 18 | import Ubuntu.Components 1.3 | ||
1947 | 19 | |||
1948 | 20 | ListItem { | ||
1949 | 21 | property alias text: label.text | ||
1950 | 22 | Label { | ||
1951 | 23 | id: label | ||
1952 | 24 | anchors { | ||
1953 | 25 | left: parent.left | ||
1954 | 26 | leftMargin: units.gu(2) | ||
1955 | 27 | verticalCenter: parent.verticalCenter | ||
1956 | 28 | } | ||
1957 | 29 | } | ||
1958 | 30 | } | ||
1959 | 0 | 31 | ||
1960 | === modified file 'tests/unit_x11/tst_components/MyExternalPage.qml' | |||
1961 | --- tests/unit_x11/tst_components/MyExternalPage.qml 2015-03-03 13:20:06 +0000 | |||
1962 | +++ tests/unit_x11/tst_components/MyExternalPage.qml 2015-07-10 13:56:40 +0000 | |||
1963 | @@ -1,5 +1,5 @@ | |||
1964 | 1 | /* | 1 | /* |
1966 | 2 | * Copyright 2012-2014 Canonical Ltd. | 2 | * Copyright 2012-2015 Canonical Ltd. |
1967 | 3 | * | 3 | * |
1968 | 4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
1969 | 5 | * it under the terms of the GNU Lesser General Public License as published by | 5 | * it under the terms of the GNU Lesser General Public License as published by |
1970 | @@ -14,9 +14,13 @@ | |||
1971 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1972 | 15 | */ | 15 | */ |
1973 | 16 | 16 | ||
1976 | 17 | import QtQuick 2.2 | 17 | import QtQuick 2.4 |
1977 | 18 | import Ubuntu.Components 1.1 | 18 | import Ubuntu.Components 1.3 |
1978 | 19 | 19 | ||
1979 | 20 | Page { | 20 | Page { |
1980 | 21 | title: "Page from QML file" | 21 | title: "Page from QML file" |
1981 | 22 | Label { | ||
1982 | 23 | anchors.centerIn: parent | ||
1983 | 24 | text: "This page was created from MyExternalPage.qml." | ||
1984 | 25 | } | ||
1985 | 22 | } | 26 | } |
1986 | 23 | 27 | ||
1987 | === modified file 'tests/unit_x11/tst_components/tst_components.pro' | |||
1988 | --- tests/unit_x11/tst_components/tst_components.pro 2015-04-25 07:10:57 +0000 | |||
1989 | +++ tests/unit_x11/tst_components/tst_components.pro 2015-07-10 13:56:40 +0000 | |||
1990 | @@ -7,5 +7,6 @@ | |||
1991 | 7 | SOURCES += tst_components.cpp tabsmodel.cpp | 7 | SOURCES += tst_components.cpp tabsmodel.cpp |
1992 | 8 | HEADERS += tabsmodel.h | 8 | HEADERS += tabsmodel.h |
1993 | 9 | 9 | ||
1995 | 10 | OTHER_FILES += $$system(ls *.qml) | 10 | OTHER_FILES += $$system(ls *.qml) \ |
1996 | 11 | tst_multicolumnview.qml | ||
1997 | 11 | OTHER_FILES += $$system(ls AppTheme/*) | 12 | OTHER_FILES += $$system(ls AppTheme/*) |
1998 | 12 | 13 | ||
1999 | === added file 'tests/unit_x11/tst_components/tst_multicolumnheader.qml' | |||
2000 | --- tests/unit_x11/tst_components/tst_multicolumnheader.qml 1970-01-01 00:00:00 +0000 | |||
2001 | +++ tests/unit_x11/tst_components/tst_multicolumnheader.qml 2015-07-10 13:56:40 +0000 | |||
2002 | @@ -0,0 +1,200 @@ | |||
2003 | 1 | /* | ||
2004 | 2 | * Copyright 2015 Canonical Ltd. | ||
2005 | 3 | * | ||
2006 | 4 | * This program is free software; you can redistribute it and/or modify | ||
2007 | 5 | * it under the terms of the GNU Lesser General Public License as published by | ||
2008 | 6 | * the Free Software Foundation; version 3. | ||
2009 | 7 | * | ||
2010 | 8 | * This program is distributed in the hope that it will be useful, | ||
2011 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2012 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2013 | 11 | * GNU Lesser General Public License for more details. | ||
2014 | 12 | * | ||
2015 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
2016 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2017 | 15 | */ | ||
2018 | 16 | |||
2019 | 17 | import QtQuick 2.4 | ||
2020 | 18 | import Ubuntu.Test 1.0 | ||
2021 | 19 | import Ubuntu.Components 1.3 | ||
2022 | 20 | |||
2023 | 21 | MainView { | ||
2024 | 22 | id: root | ||
2025 | 23 | width: units.gu(120) | ||
2026 | 24 | height: units.gu(71) | ||
2027 | 25 | |||
2028 | 26 | MultiColumnView { | ||
2029 | 27 | id: multiColumnView | ||
2030 | 28 | width: parent.width | ||
2031 | 29 | height: parent.height | ||
2032 | 30 | primaryPage: rootPage | ||
2033 | 31 | |||
2034 | 32 | Page { | ||
2035 | 33 | id: rootPage | ||
2036 | 34 | title: "Root" | ||
2037 | 35 | |||
2038 | 36 | Column { | ||
2039 | 37 | anchors { | ||
2040 | 38 | top: parent.top | ||
2041 | 39 | left: parent.left | ||
2042 | 40 | right: parent.right | ||
2043 | 41 | } | ||
2044 | 42 | height: childrenRect.height | ||
2045 | 43 | |||
2046 | 44 | ListItemWithLabel { | ||
2047 | 45 | text: "Add page left" | ||
2048 | 46 | onClicked: multiColumnView.addPageToCurrentColumn(rootPage, leftPage) | ||
2049 | 47 | } | ||
2050 | 48 | ListItemWithLabel { | ||
2051 | 49 | text: "Add page right" | ||
2052 | 50 | onClicked: multiColumnView.addPageToNextColumn(rootPage, rightPage) | ||
2053 | 51 | } | ||
2054 | 52 | ListItemWithLabel { | ||
2055 | 53 | text: "Add sections page right" | ||
2056 | 54 | onClicked: multiColumnView.addPageToNextColumn(rootPage, sectionsPage) | ||
2057 | 55 | } | ||
2058 | 56 | ListItemWithLabel { | ||
2059 | 57 | text: "Add external page right" | ||
2060 | 58 | onClicked: multiColumnView.addPageToNextColumn( | ||
2061 | 59 | rootPage, Qt.resolvedUrl("MyExternalPage.qml")) | ||
2062 | 60 | } | ||
2063 | 61 | } | ||
2064 | 62 | } | ||
2065 | 63 | Page { | ||
2066 | 64 | id: leftPage | ||
2067 | 65 | title: "First column" | ||
2068 | 66 | Rectangle { | ||
2069 | 67 | anchors { | ||
2070 | 68 | fill: parent | ||
2071 | 69 | margins: units.gu(2) | ||
2072 | 70 | } | ||
2073 | 71 | color: "orange" | ||
2074 | 72 | } | ||
2075 | 73 | } | ||
2076 | 74 | Page { | ||
2077 | 75 | id: rightPage | ||
2078 | 76 | title: "Second column" | ||
2079 | 77 | Rectangle { | ||
2080 | 78 | anchors { | ||
2081 | 79 | fill: parent | ||
2082 | 80 | margins: units.gu(2) | ||
2083 | 81 | } | ||
2084 | 82 | color: "green" | ||
2085 | 83 | } | ||
2086 | 84 | } | ||
2087 | 85 | Page { | ||
2088 | 86 | id: sectionsPage | ||
2089 | 87 | title: "Page with sections" | ||
2090 | 88 | head.sections.model: ["uno", "dos", "tres"] | ||
2091 | 89 | |||
2092 | 90 | Rectangle { | ||
2093 | 91 | anchors { | ||
2094 | 92 | fill: parent | ||
2095 | 93 | margins: units.gu(2) | ||
2096 | 94 | } | ||
2097 | 95 | color: "blue" | ||
2098 | 96 | } | ||
2099 | 97 | } | ||
2100 | 98 | } | ||
2101 | 99 | UbuntuTestCase { | ||
2102 | 100 | when: windowShown | ||
2103 | 101 | |||
2104 | 102 | function get_number_of_columns() { | ||
2105 | 103 | var body = findChild(multiColumnView, "body"); | ||
2106 | 104 | return body.children.length; | ||
2107 | 105 | } | ||
2108 | 106 | |||
2109 | 107 | function get_header(index) { | ||
2110 | 108 | return findChild(multiColumnView, "Header" + index); | ||
2111 | 109 | } | ||
2112 | 110 | |||
2113 | 111 | function get_number_of_headers() { | ||
2114 | 112 | // FIXME: With only one column, revert to using the AppHeader | ||
2115 | 113 | // so multiColumnView sill not include any headers. | ||
2116 | 114 | var numHeaders = 0; | ||
2117 | 115 | var header = findChild(multiColumnView, "Header0"); | ||
2118 | 116 | verify(header !== null, "No header found!"); | ||
2119 | 117 | while (header !== null) { | ||
2120 | 118 | numHeaders++; | ||
2121 | 119 | header = get_header(numHeaders); | ||
2122 | 120 | } | ||
2123 | 121 | return numHeaders; | ||
2124 | 122 | } | ||
2125 | 123 | |||
2126 | 124 | function cleanup() { | ||
2127 | 125 | multiColumnView.width = root.width; | ||
2128 | 126 | multiColumnView.height = root.height; | ||
2129 | 127 | multiColumnView.removePages(rootPage); | ||
2130 | 128 | } | ||
2131 | 129 | |||
2132 | 130 | function test_number_of_headers_equals_number_of_columns() { | ||
2133 | 131 | multiColumnView.width = units.gu(40); | ||
2134 | 132 | compare(get_number_of_columns(), 1, "Number of columns is not 1."); | ||
2135 | 133 | compare(get_number_of_headers(), 1, "Number of headers is not 1."); | ||
2136 | 134 | multiColumnView.width = root.width; | ||
2137 | 135 | compare(get_number_of_columns(), 2, "Number of columns is not 2."); | ||
2138 | 136 | compare(get_number_of_headers(), 2, "Number of headers is not 2."); | ||
2139 | 137 | } | ||
2140 | 138 | |||
2141 | 139 | function test_header_configuration_equals_column_page_configuration() { | ||
2142 | 140 | compare(get_number_of_headers(), 2, "Number of headers is not 2 initially."); | ||
2143 | 141 | compare(get_header(0).config, rootPage.head, | ||
2144 | 142 | "First column header is not initialized with primaryPage header config."); | ||
2145 | 143 | compare(get_header(1).config, null, | ||
2146 | 144 | "Second column header is not initalized with null."); | ||
2147 | 145 | |||
2148 | 146 | multiColumnView.addPageToCurrentColumn(rootPage, leftPage); | ||
2149 | 147 | compare(get_header(0).config, leftPage.head, | ||
2150 | 148 | "First column header is not updated properly."); | ||
2151 | 149 | compare(get_header(1).config, null, | ||
2152 | 150 | "Second column header is updated when it should not be."); | ||
2153 | 151 | multiColumnView.removePages(leftPage); | ||
2154 | 152 | compare(get_header(0).config, rootPage.head, | ||
2155 | 153 | "First column header is not reverted properly."); | ||
2156 | 154 | |||
2157 | 155 | multiColumnView.addPageToNextColumn(rootPage, rightPage); | ||
2158 | 156 | compare(get_header(0).config, rootPage.head, | ||
2159 | 157 | "First column header is updated when it should not be."); | ||
2160 | 158 | compare(get_header(1).config, rightPage.head, | ||
2161 | 159 | "Second column header is not updated properly."); | ||
2162 | 160 | multiColumnView.removePages(rightPage); | ||
2163 | 161 | compare(get_header(1).config, null, | ||
2164 | 162 | "Second column header is not reverted properly."); | ||
2165 | 163 | } | ||
2166 | 164 | |||
2167 | 165 | function test_header_title_for_external_page() { | ||
2168 | 166 | multiColumnView.addPageToNextColumn(rootPage, Qt.resolvedUrl("MyExternalPage.qml")); | ||
2169 | 167 | compare(get_header(1).config.title, "Page from QML file", | ||
2170 | 168 | "Adding external Page does not update the header title."); | ||
2171 | 169 | } | ||
2172 | 170 | |||
2173 | 171 | function test_header_height() { | ||
2174 | 172 | // contentHeight + divider height | ||
2175 | 173 | var baseHeight = units.gu(6) + units.dp(1); | ||
2176 | 174 | var withSectionsHeight = baseHeight + units.gu(4); | ||
2177 | 175 | var n = get_number_of_headers(); | ||
2178 | 176 | var i; | ||
2179 | 177 | for (i = 0; i < n; i++) { | ||
2180 | 178 | compare(get_header(i).height, baseHeight, | ||
2181 | 179 | "Header " + i + " height is not initialized correctly."); | ||
2182 | 180 | } | ||
2183 | 181 | multiColumnView.addPageToNextColumn(rootPage, rightPage); | ||
2184 | 182 | for (i = 0; i < n; i++) { | ||
2185 | 183 | compare(get_header(i).height, baseHeight, | ||
2186 | 184 | "Header " + i + " height is incorrect after adding Page."); | ||
2187 | 185 | } | ||
2188 | 186 | multiColumnView.removePages(rightPage); | ||
2189 | 187 | multiColumnView.addPageToNextColumn(rootPage, sectionsPage); | ||
2190 | 188 | for (i = 0; i < n; i++) { | ||
2191 | 189 | compare(get_header(i).height, withSectionsHeight, | ||
2192 | 190 | "Header " + i + " height is incorrect after adding single Page with sections."); | ||
2193 | 191 | } | ||
2194 | 192 | multiColumnView.removePages(sectionsPage); | ||
2195 | 193 | for (i = 0; i < n; i++) { | ||
2196 | 194 | compare(get_header(i).height, baseHeight, | ||
2197 | 195 | "Header " + i + | ||
2198 | 196 | " height is not correctly reverted after removing Page with sections."); | ||
2199 | 197 | } | ||
2200 | 198 | } | ||
2201 | 199 | } | ||
2202 | 200 | } | ||
2203 | 0 | 201 | ||
2204 | === added file 'tests/unit_x11/tst_components/tst_multicolumnview.qml' | |||
2205 | --- tests/unit_x11/tst_components/tst_multicolumnview.qml 1970-01-01 00:00:00 +0000 | |||
2206 | +++ tests/unit_x11/tst_components/tst_multicolumnview.qml 2015-07-10 13:56:40 +0000 | |||
2207 | @@ -0,0 +1,128 @@ | |||
2208 | 1 | /* | ||
2209 | 2 | * Copyright 2015 Canonical Ltd. | ||
2210 | 3 | * | ||
2211 | 4 | * This program is free software; you can redistribute it and/or modify | ||
2212 | 5 | * it under the terms of the GNU Lesser General Public License as published by | ||
2213 | 6 | * the Free Software Foundation; version 3. | ||
2214 | 7 | * | ||
2215 | 8 | * This program is distributed in the hope that it will be useful, | ||
2216 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2217 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2218 | 11 | * GNU Lesser General Public License for more details. | ||
2219 | 12 | * | ||
2220 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
2221 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2222 | 15 | */ | ||
2223 | 16 | |||
2224 | 17 | import QtQuick 2.4 | ||
2225 | 18 | import QtTest 1.0 | ||
2226 | 19 | import Ubuntu.Test 1.0 | ||
2227 | 20 | import Ubuntu.Components 1.3 | ||
2228 | 21 | |||
2229 | 22 | Item { | ||
2230 | 23 | id: test | ||
2231 | 24 | width: units.gu(120) | ||
2232 | 25 | height: units.gu(71) | ||
2233 | 26 | |||
2234 | 27 | MultiColumnView { | ||
2235 | 28 | id: testView | ||
2236 | 29 | width: parent.width | ||
2237 | 30 | height: parent.height | ||
2238 | 31 | |||
2239 | 32 | primaryPage: page1 | ||
2240 | 33 | |||
2241 | 34 | Page { | ||
2242 | 35 | id: page1 | ||
2243 | 36 | title: "Page1" | ||
2244 | 37 | } | ||
2245 | 38 | Page { | ||
2246 | 39 | id: page2 | ||
2247 | 40 | title: "Page2" | ||
2248 | 41 | } | ||
2249 | 42 | Page { | ||
2250 | 43 | id: page3 | ||
2251 | 44 | title: "Page3" | ||
2252 | 45 | } | ||
2253 | 46 | Page { | ||
2254 | 47 | id: page4 | ||
2255 | 48 | title: "Page4" | ||
2256 | 49 | } | ||
2257 | 50 | } | ||
2258 | 51 | |||
2259 | 52 | MultiColumnView { | ||
2260 | 53 | id: defaults | ||
2261 | 54 | } | ||
2262 | 55 | |||
2263 | 56 | UbuntuTestCase { | ||
2264 | 57 | when: windowShown | ||
2265 | 58 | |||
2266 | 59 | function cleanup() { | ||
2267 | 60 | // testView.columns = Qt.binding(function() { | ||
2268 | 61 | // return test.width > units.gu(100) ? 3 : (test.width > units.gu(80) ? 2 : 1); | ||
2269 | 62 | // }); | ||
2270 | 63 | testView.width = test.width; | ||
2271 | 64 | testView.height = test.height; | ||
2272 | 65 | // remove allpages | ||
2273 | 66 | testView.removePages(page1); | ||
2274 | 67 | } | ||
2275 | 68 | |||
2276 | 69 | function test_0_API() { | ||
2277 | 70 | compare(defaults.primaryPage, undefined, "primaryPage not undefined by default"); | ||
2278 | 71 | } | ||
2279 | 72 | |||
2280 | 73 | function test_add_to_first_column_data() { | ||
2281 | 74 | return [ | ||
2282 | 75 | {tag: "null sourcePage, fail", sourcePage: null, page: page2, failMsg: "No sourcePage specified. Page will not be added."}, | ||
2283 | 76 | {tag: "valid sourcePage, pass", sourcePage: page1, page: page2, failMsg: ""}, | ||
2284 | 77 | ] | ||
2285 | 78 | } | ||
2286 | 79 | function test_add_to_first_column(data) { | ||
2287 | 80 | if (data.failMsg != "") { | ||
2288 | 81 | ignoreWarning(data.failMsg); | ||
2289 | 82 | } | ||
2290 | 83 | |||
2291 | 84 | testView.addPageToCurrentColumn(data.sourcePage, data.page); | ||
2292 | 85 | var firstColumn = findChild(testView, "ColumnHolder0"); | ||
2293 | 86 | verify(firstColumn); | ||
2294 | 87 | if (data.failMsg != "") { | ||
2295 | 88 | expectFail(data.tag, "Fail"); | ||
2296 | 89 | } | ||
2297 | 90 | compare(firstColumn.pageWrapper.object, data.page); | ||
2298 | 91 | } | ||
2299 | 92 | |||
2300 | 93 | function test_add_to_next_column_data() { | ||
2301 | 94 | return [ | ||
2302 | 95 | {tag: "null sourcePage, fail", sourcePage: null, page: page2, failMsg: "No sourcePage specified. Page will not be added."}, | ||
2303 | 96 | {tag: "valid sourcePage, pass", sourcePage: page1, page: page2, failMsg: ""}, | ||
2304 | 97 | ] | ||
2305 | 98 | } | ||
2306 | 99 | function test_add_to_next_column(data) { | ||
2307 | 100 | if (data.failMsg != "") { | ||
2308 | 101 | ignoreWarning(data.failMsg); | ||
2309 | 102 | } | ||
2310 | 103 | |||
2311 | 104 | testView.addPageToNextColumn(data.sourcePage, data.page); | ||
2312 | 105 | var secondColumn = findChild(testView, "ColumnHolder1"); | ||
2313 | 106 | verify(secondColumn); | ||
2314 | 107 | if (data.failMsg != "") { | ||
2315 | 108 | expectFail(data.tag, "Fail"); | ||
2316 | 109 | } | ||
2317 | 110 | verify(secondColumn.pageWrapper); | ||
2318 | 111 | } | ||
2319 | 112 | |||
2320 | 113 | function test_change_primaryPage() { | ||
2321 | 114 | ignoreWarning("Cannot change primaryPage after completion."); | ||
2322 | 115 | testView.primaryPage = page3; | ||
2323 | 116 | } | ||
2324 | 117 | |||
2325 | 118 | function test_add_to_same_column_when_source_page_not_in_stack() { | ||
2326 | 119 | ignoreWarning("sourcePage must be added to the view to add new page."); | ||
2327 | 120 | testView.addPageToCurrentColumn(page2, page3); | ||
2328 | 121 | } | ||
2329 | 122 | |||
2330 | 123 | function test_add_to_next_column_when_source_page_not_in_stack() { | ||
2331 | 124 | ignoreWarning("sourcePage must be added to the view to add new page."); | ||
2332 | 125 | testView.addPageToNextColumn(page2, page3); | ||
2333 | 126 | } | ||
2334 | 127 | } | ||
2335 | 128 | } |
FAILED: Continuous integration, rev:1566 jenkins. qa.ubuntu. com/job/ ubuntu- ui-toolkit- ci/2218/ jenkins. qa.ubuntu. com/job/ ubuntu- ui-toolkit- vivid-amd64- ci/42/console jenkins. qa.ubuntu. com/job/ ubuntu- ui-toolkit- vivid-armhf- ci/42/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/ubuntu- ui-toolkit- ci/2218/ rebuild
http://