Merge lp:~tpeeters/ubuntu-ui-toolkit/10-headAnimate into lp:ubuntu-ui-toolkit/staging
- 10-headAnimate
- Merge into staging
Status: | Merged |
---|---|
Approved by: | Zsombor Egri |
Approved revision: | 1213 |
Merged at revision: | 1218 |
Proposed branch: | lp:~tpeeters/ubuntu-ui-toolkit/10-headAnimate |
Merge into: | lp:ubuntu-ui-toolkit/staging |
Prerequisite: | lp:~ubuntu-sdk-team/ubuntu-ui-toolkit/sourceOverflow |
Diff against target: |
1044 lines (+391/-124) 16 files modified
modules/Ubuntu/Components/AppHeader.qml (+5/-0) modules/Ubuntu/Components/Icon10.qml (+1/-1) modules/Ubuntu/Components/MainView.qml (+1/-0) modules/Ubuntu/Components/PageStack.qml (+7/-6) modules/Ubuntu/Components/PageWrapperUtils.js (+3/-1) modules/Ubuntu/Components/Styles/PageHeadStyle.qml (+2/-0) modules/Ubuntu/Components/Themes/Ambiance/PageHeadStyle.qml (+166/-29) tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_header.py (+19/-0) tests/autopilot/ubuntuuitoolkit/tests/components/test_header.py (+2/-0) tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_tabs.TabsTestCase.deprecated_TabBar.qml (+61/-0) tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_tabs.TabsTestCase.new_header.qml (+43/-0) tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_tabs.py (+14/-56) tests/resources/header/header.qml (+4/-6) tests/resources/navigation/MyCustomPage.qml (+17/-25) tests/unit_x11/tst_components/tst_headActions.qml (+13/-0) tests/unit_x11/tst_components/tst_pagestack_new_header.qml (+33/-0) |
To merge this branch: | bzr merge lp:~tpeeters/ubuntu-ui-toolkit/10-headAnimate |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Approve | |
Zsombor Egri | Pending | ||
Review via email: mp+232201@code.launchpad.net |
This proposal supersedes a proposal from 2014-08-22.
Commit message
Description of the change
Add header animations
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
Zsombor Egri (zsombi) wrote : Posted in a previous version of this proposal | # |
Looks good, can go in.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:1212
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) : | # |
Preview Diff
1 | === modified file 'modules/Ubuntu/Components/AppHeader.qml' | |||
2 | --- modules/Ubuntu/Components/AppHeader.qml 2014-07-17 10:59:18 +0000 | |||
3 | +++ modules/Ubuntu/Components/AppHeader.qml 2014-08-26 11:42:43 +0000 | |||
4 | @@ -37,6 +37,11 @@ | |||
5 | 37 | */ | 37 | */ |
6 | 38 | property bool animate: true | 38 | property bool animate: true |
7 | 39 | 39 | ||
8 | 40 | /*! | ||
9 | 41 | Animate changing to new title/actions inside the header. | ||
10 | 42 | */ | ||
11 | 43 | property bool animateContents: false | ||
12 | 44 | |||
13 | 40 | Behavior on y { | 45 | Behavior on y { |
14 | 41 | enabled: animate && !(header.flickable && header.flickable.moving) | 46 | enabled: animate && !(header.flickable && header.flickable.moving) |
15 | 42 | SmoothedAnimation { | 47 | SmoothedAnimation { |
16 | 43 | 48 | ||
17 | === modified file 'modules/Ubuntu/Components/Icon10.qml' | |||
18 | --- modules/Ubuntu/Components/Icon10.qml 2014-07-31 14:14:56 +0000 | |||
19 | +++ modules/Ubuntu/Components/Icon10.qml 2014-08-26 11:42:43 +0000 | |||
20 | @@ -83,7 +83,7 @@ | |||
21 | 83 | visible: active | 83 | visible: active |
22 | 84 | 84 | ||
23 | 85 | // Whether or not a color has been set. | 85 | // Whether or not a color has been set. |
25 | 86 | property bool active: keyColorOut != Qt.rgba(0.0, 0.0, 0.0, 0.0) | 86 | property bool active: keyColorOut != Qt.rgba(0.0, 0.0, 0.0, 0.0) && source |
26 | 87 | 87 | ||
27 | 88 | property Image source: active && image.status == Image.Ready ? image : null | 88 | property Image source: active && image.status == Image.Ready ? image : null |
28 | 89 | property color keyColorOut: Qt.rgba(0.0, 0.0, 0.0, 0.0) | 89 | property color keyColorOut: Qt.rgba(0.0, 0.0, 0.0, 0.0) |
29 | 90 | 90 | ||
30 | === modified file 'modules/Ubuntu/Components/MainView.qml' | |||
31 | --- modules/Ubuntu/Components/MainView.qml 2014-08-18 13:34:29 +0000 | |||
32 | +++ modules/Ubuntu/Components/MainView.qml 2014-08-26 11:42:43 +0000 | |||
33 | @@ -341,6 +341,7 @@ | |||
34 | 341 | id: headerItem | 341 | id: headerItem |
35 | 342 | property real bottomY: headerItem.y + headerItem.height | 342 | property real bottomY: headerItem.y + headerItem.height |
36 | 343 | animate: canvas.animate | 343 | animate: canvas.animate |
37 | 344 | animateContents: canvas.animate | ||
38 | 344 | 345 | ||
39 | 345 | title: internal.activePage ? internal.activePage.title : "" | 346 | title: internal.activePage ? internal.activePage.title : "" |
40 | 346 | flickable: internal.activePage ? internal.activePage.flickable : null | 347 | flickable: internal.activePage ? internal.activePage.flickable : null |
41 | 347 | 348 | ||
42 | === modified file 'modules/Ubuntu/Components/PageStack.qml' | |||
43 | --- modules/Ubuntu/Components/PageStack.qml 2014-08-12 10:03:13 +0000 | |||
44 | +++ modules/Ubuntu/Components/PageStack.qml 2014-08-26 11:42:43 +0000 | |||
45 | @@ -163,7 +163,6 @@ | |||
46 | 163 | function push(page, properties) { | 163 | function push(page, properties) { |
47 | 164 | if (internal.stack.size() > 0) internal.stack.top().active = false; | 164 | if (internal.stack.size() > 0) internal.stack.top().active = false; |
48 | 165 | internal.stack.push(internal.createWrapper(page, properties)); | 165 | internal.stack.push(internal.createWrapper(page, properties)); |
49 | 166 | internal.stack.top().active = true; | ||
50 | 167 | internal.stackUpdated(); | 166 | internal.stackUpdated(); |
51 | 168 | } | 167 | } |
52 | 169 | 168 | ||
53 | @@ -181,8 +180,6 @@ | |||
54 | 181 | if (internal.stack.top().canDestroy) internal.stack.top().destroyObject(); | 180 | if (internal.stack.top().canDestroy) internal.stack.top().destroyObject(); |
55 | 182 | internal.stack.pop(); | 181 | internal.stack.pop(); |
56 | 183 | internal.stackUpdated(); | 182 | internal.stackUpdated(); |
57 | 184 | |||
58 | 185 | if (internal.stack.size() > 0) internal.stack.top().active = true; | ||
59 | 186 | } | 183 | } |
60 | 187 | 184 | ||
61 | 188 | /*! | 185 | /*! |
62 | @@ -216,9 +213,13 @@ | |||
63 | 216 | } | 213 | } |
64 | 217 | 214 | ||
65 | 218 | function stackUpdated() { | 215 | function stackUpdated() { |
69 | 219 | pageStack.depth =+ stack.size(); | 216 | pageStack.depth = stack.size(); |
70 | 220 | if (pageStack.depth > 0) currentPage = stack.top().object; | 217 | if (pageStack.depth > 0) { |
71 | 221 | else currentPage = null; | 218 | internal.stack.top().active = true; |
72 | 219 | currentPage = stack.top().object; | ||
73 | 220 | } else { | ||
74 | 221 | currentPage = null; | ||
75 | 222 | } | ||
76 | 222 | } | 223 | } |
77 | 223 | } | 224 | } |
78 | 224 | 225 | ||
79 | 225 | 226 | ||
80 | === modified file 'modules/Ubuntu/Components/PageWrapperUtils.js' | |||
81 | --- modules/Ubuntu/Components/PageWrapperUtils.js 2013-05-24 13:51:12 +0000 | |||
82 | +++ modules/Ubuntu/Components/PageWrapperUtils.js 2014-08-26 11:42:43 +0000 | |||
83 | @@ -101,8 +101,10 @@ | |||
84 | 101 | */ | 101 | */ |
85 | 102 | function destroyObject(pageWrapper) { | 102 | function destroyObject(pageWrapper) { |
86 | 103 | if (pageWrapper.canDestroy) { | 103 | if (pageWrapper.canDestroy) { |
87 | 104 | pageWrapper.object.destroy(); | ||
88 | 105 | pageWrapper.object = null; | 104 | pageWrapper.object = null; |
89 | 105 | // Rely on garbage collector to destroy the object after all | ||
90 | 106 | // (other) references are gone. PageHeadStyle uses actions etc. | ||
91 | 107 | // of the page to show a fade-out animation after it was popped. | ||
92 | 106 | pageWrapper.canDestroy = false; | 108 | pageWrapper.canDestroy = false; |
93 | 107 | } | 109 | } |
94 | 108 | } | 110 | } |
95 | 109 | 111 | ||
96 | === modified file 'modules/Ubuntu/Components/Styles/PageHeadStyle.qml' | |||
97 | --- modules/Ubuntu/Components/Styles/PageHeadStyle.qml 2014-06-26 14:24:41 +0000 | |||
98 | +++ modules/Ubuntu/Components/Styles/PageHeadStyle.qml 2014-08-26 11:42:43 +0000 | |||
99 | @@ -53,7 +53,9 @@ | |||
100 | 53 | property int fontWeight | 53 | property int fontWeight |
101 | 54 | 54 | ||
102 | 55 | /*! | 55 | /*! |
103 | 56 | \deprecated | ||
104 | 56 | The color of the title text. | 57 | The color of the title text. |
105 | 58 | Use \l Page.head.foregroundColor instead. | ||
106 | 57 | */ | 59 | */ |
107 | 58 | property color textColor | 60 | property color textColor |
108 | 59 | 61 | ||
109 | 60 | 62 | ||
110 | === modified file 'modules/Ubuntu/Components/Themes/Ambiance/PageHeadStyle.qml' | |||
111 | --- modules/Ubuntu/Components/Themes/Ambiance/PageHeadStyle.qml 2014-08-26 11:42:42 +0000 | |||
112 | +++ modules/Ubuntu/Components/Themes/Ambiance/PageHeadStyle.qml 2014-08-26 11:42:43 +0000 | |||
113 | @@ -29,9 +29,127 @@ | |||
114 | 29 | textColor: styledItem.config.foregroundColor | 29 | textColor: styledItem.config.foregroundColor |
115 | 30 | textLeftMargin: units.gu(2) | 30 | textLeftMargin: units.gu(2) |
116 | 31 | maximumNumberOfActions: 3 | 31 | maximumNumberOfActions: 3 |
117 | 32 | objectName: "PageHeadStyle" | ||
118 | 33 | |||
119 | 34 | // workaround because autopilot cannot select the SequentalAnimation | ||
120 | 35 | // Needed in AppHeader.wait_for_animation() autopilot proxy object and | ||
121 | 36 | // in tst_pagestack_new_header.qml unit test. | ||
122 | 37 | property bool animating: changeAnimation.running | ||
123 | 32 | 38 | ||
124 | 33 | implicitHeight: headerStyle.contentHeight + separator.height + separatorBottom.height | 39 | implicitHeight: headerStyle.contentHeight + separator.height + separatorBottom.height |
125 | 34 | 40 | ||
126 | 41 | Component.onCompleted: buffer.update() | ||
127 | 42 | |||
128 | 43 | Object { | ||
129 | 44 | id: buffer | ||
130 | 45 | |||
131 | 46 | property PageHeadConfiguration config | ||
132 | 47 | property string title | ||
133 | 48 | property Item pageStack: null | ||
134 | 49 | property int pageStackDepth: 0 | ||
135 | 50 | property var tabsModel: null | ||
136 | 51 | |||
137 | 52 | function update() { | ||
138 | 53 | buffer.config = styledItem.config; | ||
139 | 54 | buffer.title = styledItem.title; | ||
140 | 55 | buffer.pageStack = styledItem.pageStack; | ||
141 | 56 | buffer.pageStackDepth = buffer.pageStack ? buffer.pageStack.depth : 0; | ||
142 | 57 | buffer.tabsModel = styledItem.tabsModel ? styledItem.tabsModel : null; | ||
143 | 58 | } | ||
144 | 59 | |||
145 | 60 | // Calling changeAnimation.start() a second time has no effect, | ||
146 | 61 | // so below we can call it whenever something changes. | ||
147 | 62 | Connections { | ||
148 | 63 | target: styledItem | ||
149 | 64 | onConfigChanged: buffer.updateConfigAndTitle() | ||
150 | 65 | onTitleChanged: buffer.updateConfigAndTitle() | ||
151 | 66 | onPageStackChanged: buffer.updateConfigAndTitle() | ||
152 | 67 | onTabsModelChanged: buffer.updateConfigAndTitle() | ||
153 | 68 | } | ||
154 | 69 | |||
155 | 70 | function updateConfigAndTitle() { | ||
156 | 71 | if (styledItem.animateContents) { | ||
157 | 72 | changeAnimation.start(); | ||
158 | 73 | } else { | ||
159 | 74 | buffer.update(); | ||
160 | 75 | } | ||
161 | 76 | } | ||
162 | 77 | |||
163 | 78 | SequentialAnimation { | ||
164 | 79 | id: changeAnimation | ||
165 | 80 | objectName: "changeAnimation" | ||
166 | 81 | ParallelAnimation { | ||
167 | 82 | UbuntuNumberAnimation { | ||
168 | 83 | target: foreground | ||
169 | 84 | property: "opacity" | ||
170 | 85 | from: 1.0 | ||
171 | 86 | to: 0.0 | ||
172 | 87 | } | ||
173 | 88 | UbuntuNumberAnimation { | ||
174 | 89 | target: leftButtonContainer | ||
175 | 90 | property: "opacity" | ||
176 | 91 | from: 1.0 | ||
177 | 92 | to: 0.0 | ||
178 | 93 | } | ||
179 | 94 | UbuntuNumberAnimation { | ||
180 | 95 | target: actionsContainer | ||
181 | 96 | property: "opacity" | ||
182 | 97 | from: 1.0 | ||
183 | 98 | to: 0.0 | ||
184 | 99 | } | ||
185 | 100 | UbuntuNumberAnimation { | ||
186 | 101 | target: leftAnchor | ||
187 | 102 | properties: "anchors.leftMargin" | ||
188 | 103 | from: 0.0 | ||
189 | 104 | to: -units.gu(5) | ||
190 | 105 | } | ||
191 | 106 | UbuntuNumberAnimation { | ||
192 | 107 | target: rightAnchor | ||
193 | 108 | properties: "anchors.rightMargin" | ||
194 | 109 | from: 0 | ||
195 | 110 | to: -units.gu(5) | ||
196 | 111 | } | ||
197 | 112 | } | ||
198 | 113 | ScriptAction { | ||
199 | 114 | script: { | ||
200 | 115 | buffer.update(); | ||
201 | 116 | } | ||
202 | 117 | } | ||
203 | 118 | ParallelAnimation { | ||
204 | 119 | UbuntuNumberAnimation { | ||
205 | 120 | target: foreground | ||
206 | 121 | property: "opacity" | ||
207 | 122 | from: 0.0 | ||
208 | 123 | to: 1.0 | ||
209 | 124 | } | ||
210 | 125 | UbuntuNumberAnimation { | ||
211 | 126 | target: leftButtonContainer | ||
212 | 127 | property: "opacity" | ||
213 | 128 | from: 0.0 | ||
214 | 129 | to: 1.0 | ||
215 | 130 | } | ||
216 | 131 | UbuntuNumberAnimation { | ||
217 | 132 | target: actionsContainer | ||
218 | 133 | property: "opacity" | ||
219 | 134 | from: 0.0 | ||
220 | 135 | to: 1.0 | ||
221 | 136 | } | ||
222 | 137 | UbuntuNumberAnimation { | ||
223 | 138 | target: leftAnchor | ||
224 | 139 | properties: "anchors.leftMargin" | ||
225 | 140 | from: -units.gu(5) | ||
226 | 141 | to: 0 | ||
227 | 142 | } | ||
228 | 143 | UbuntuNumberAnimation { | ||
229 | 144 | target: rightAnchor | ||
230 | 145 | properties: "anchors.rightMargin" | ||
231 | 146 | from: -units.gu(5) | ||
232 | 147 | to: 0 | ||
233 | 148 | } | ||
234 | 149 | } | ||
235 | 150 | } | ||
236 | 151 | } | ||
237 | 152 | |||
238 | 35 | // FIXME: Workaround to get sectionsRepeater.count in autopilot tests, | 153 | // FIXME: Workaround to get sectionsRepeater.count in autopilot tests, |
239 | 36 | // see also FIXME in AppHeader where this property is used. | 154 | // see also FIXME in AppHeader where this property is used. |
240 | 37 | property alias __sections_repeater_for_autopilot: sectionsRepeater | 155 | property alias __sections_repeater_for_autopilot: sectionsRepeater |
241 | @@ -46,7 +164,7 @@ | |||
242 | 46 | source: headerStyle.separatorSource | 164 | source: headerStyle.separatorSource |
243 | 47 | height: sectionsRow.visible ? units.gu(3) : units.gu(2) | 165 | height: sectionsRow.visible ? units.gu(3) : units.gu(2) |
244 | 48 | 166 | ||
246 | 49 | property PageHeadSections sections: styledItem.config.sections | 167 | property PageHeadSections sections: buffer.config.sections |
247 | 50 | 168 | ||
248 | 51 | Row { | 169 | Row { |
249 | 52 | id: sectionsRow | 170 | id: sectionsRow |
250 | @@ -121,9 +239,30 @@ | |||
251 | 121 | } | 239 | } |
252 | 122 | 240 | ||
253 | 123 | Item { | 241 | Item { |
254 | 242 | id: leftAnchor | ||
255 | 243 | anchors { | ||
256 | 244 | top: parent.top | ||
257 | 245 | bottom: parent.bottom | ||
258 | 246 | left: parent.left | ||
259 | 247 | leftMargin: 0 | ||
260 | 248 | } | ||
261 | 249 | width: 0 | ||
262 | 250 | } | ||
263 | 251 | Item { | ||
264 | 252 | id: rightAnchor | ||
265 | 253 | anchors { | ||
266 | 254 | top: parent.top | ||
267 | 255 | bottom: parent.bottom | ||
268 | 256 | right: parent.right | ||
269 | 257 | rightMargin: 0 | ||
270 | 258 | } | ||
271 | 259 | width: 0 | ||
272 | 260 | } | ||
273 | 261 | |||
274 | 262 | Item { | ||
275 | 124 | id: leftButtonContainer | 263 | id: leftButtonContainer |
276 | 125 | anchors { | 264 | anchors { |
278 | 126 | left: parent.left | 265 | left: leftAnchor.right |
279 | 127 | top: parent.top | 266 | top: parent.top |
280 | 128 | leftMargin: width > 0 ? units.gu(1) : 0 | 267 | leftMargin: width > 0 ? units.gu(1) : 0 |
281 | 129 | } | 268 | } |
282 | @@ -133,10 +272,10 @@ | |||
283 | 133 | PageHeadButton { | 272 | PageHeadButton { |
284 | 134 | id: customBackButton | 273 | id: customBackButton |
285 | 135 | objectName: "customBackButton" | 274 | objectName: "customBackButton" |
290 | 136 | action: styledItem.config.backAction | 275 | action: buffer.config.backAction |
291 | 137 | visible: null !== styledItem.config.backAction && | 276 | visible: null !== buffer.config.backAction && |
292 | 138 | styledItem.config.backAction.visible | 277 | buffer.config.backAction.visible |
293 | 139 | color: styledItem.config.foregroundColor | 278 | color: buffer.config.foregroundColor |
294 | 140 | } | 279 | } |
295 | 141 | 280 | ||
296 | 142 | PageHeadButton { | 281 | PageHeadButton { |
297 | @@ -144,16 +283,14 @@ | |||
298 | 144 | objectName: "backButton" | 283 | objectName: "backButton" |
299 | 145 | 284 | ||
300 | 146 | iconName: "back" | 285 | iconName: "back" |
305 | 147 | visible: styledItem.pageStack !== null && | 286 | visible: buffer.pageStackDepth > 1 && |
306 | 148 | styledItem.pageStack !== undefined && | 287 | !buffer.config.backAction |
303 | 149 | styledItem.pageStack.depth > 1 && | ||
304 | 150 | !styledItem.config.backAction | ||
307 | 151 | 288 | ||
308 | 152 | text: "back" | 289 | text: "back" |
310 | 153 | color: styledItem.config.foregroundColor | 290 | color: buffer.config.foregroundColor |
311 | 154 | 291 | ||
312 | 155 | onTriggered: { | 292 | onTriggered: { |
314 | 156 | styledItem.pageStack.pop(); | 293 | buffer.pageStack.pop(); |
315 | 157 | } | 294 | } |
316 | 158 | } | 295 | } |
317 | 159 | 296 | ||
318 | @@ -162,12 +299,11 @@ | |||
319 | 162 | objectName: "tabsButton" | 299 | objectName: "tabsButton" |
320 | 163 | 300 | ||
321 | 164 | iconName: "navigation-menu" | 301 | iconName: "navigation-menu" |
324 | 165 | visible: styledItem.tabsModel !== null && | 302 | visible: buffer.tabsModel !== null && |
323 | 166 | styledItem.tabsModel !== undefined && | ||
325 | 167 | !backButton.visible && | 303 | !backButton.visible && |
326 | 168 | !customBackButton.visible | 304 | !customBackButton.visible |
329 | 169 | text: visible ? styledItem.tabsModel.count + " tabs" : "" | 305 | text: visible ? buffer.tabsModel.count + " tabs" : "" |
330 | 170 | color: styledItem.config.foregroundColor | 306 | color: buffer.config.foregroundColor |
331 | 171 | 307 | ||
332 | 172 | onTriggered: PopupUtils.open(tabsPopoverComponent, tabsButton) | 308 | onTriggered: PopupUtils.open(tabsPopoverComponent, tabsButton) |
333 | 173 | 309 | ||
334 | @@ -187,11 +323,11 @@ | |||
335 | 187 | right: parent.right | 323 | right: parent.right |
336 | 188 | } | 324 | } |
337 | 189 | Repeater { | 325 | Repeater { |
339 | 190 | model: styledItem.tabsModel | 326 | model: buffer.tabsModel |
340 | 191 | AbstractButton { | 327 | AbstractButton { |
341 | 192 | objectName: "tabButton" + index | 328 | objectName: "tabButton" + index |
342 | 193 | onClicked: { | 329 | onClicked: { |
344 | 194 | styledItem.tabsModel.selectedIndex = index; | 330 | buffer.tabsModel.selectedIndex = index; |
345 | 195 | tabsPopover.hide(); | 331 | tabsPopover.hide(); |
346 | 196 | } | 332 | } |
347 | 197 | implicitHeight: units.gu(6) + bottomDividerLine.height | 333 | implicitHeight: units.gu(6) + bottomDividerLine.height |
348 | @@ -224,7 +360,7 @@ | |||
349 | 224 | ListItem.ThinDivider { | 360 | ListItem.ThinDivider { |
350 | 225 | id: bottomDividerLine | 361 | id: bottomDividerLine |
351 | 226 | anchors.bottom: parent.bottom | 362 | anchors.bottom: parent.bottom |
353 | 227 | visible: index < styledItem.tabsModel.count - 1 | 363 | visible: index < buffer.tabsModel.count - 1 |
354 | 228 | } | 364 | } |
355 | 229 | } | 365 | } |
356 | 230 | } | 366 | } |
357 | @@ -238,12 +374,12 @@ | |||
358 | 238 | id: foreground | 374 | id: foreground |
359 | 239 | anchors { | 375 | anchors { |
360 | 240 | left: leftButtonContainer.right | 376 | left: leftButtonContainer.right |
361 | 241 | right: actionsContainer.left | ||
362 | 242 | top: parent.top | 377 | top: parent.top |
363 | 243 | // don't keep a margin if there is already a button with spacing | 378 | // don't keep a margin if there is already a button with spacing |
364 | 244 | leftMargin: leftButtonContainer.width > 0 ? 0 : headerStyle.textLeftMargin | 379 | leftMargin: leftButtonContainer.width > 0 ? 0 : headerStyle.textLeftMargin |
365 | 245 | } | 380 | } |
366 | 246 | height: headerStyle.contentHeight | 381 | height: headerStyle.contentHeight |
367 | 382 | width: parent.width - leftButtonContainer.width - actionsContainer.width | ||
368 | 247 | 383 | ||
369 | 248 | Label { | 384 | Label { |
370 | 249 | objectName: "header_title_label" | 385 | objectName: "header_title_label" |
371 | @@ -254,10 +390,10 @@ | |||
372 | 254 | right: parent.right | 390 | right: parent.right |
373 | 255 | verticalCenter: parent.verticalCenter | 391 | verticalCenter: parent.verticalCenter |
374 | 256 | } | 392 | } |
376 | 257 | text: styledItem.title | 393 | text: buffer.title |
377 | 258 | font.weight: headerStyle.fontWeight | 394 | font.weight: headerStyle.fontWeight |
378 | 259 | fontSize: headerStyle.fontSize | 395 | fontSize: headerStyle.fontSize |
380 | 260 | color: headerStyle.textColor | 396 | color: buffer.config.foregroundColor |
381 | 261 | elide: Text.ElideRight | 397 | elide: Text.ElideRight |
382 | 262 | } | 398 | } |
383 | 263 | 399 | ||
384 | @@ -267,7 +403,7 @@ | |||
385 | 267 | // when the bindings below is no longer active | 403 | // when the bindings below is no longer active |
386 | 268 | id: contentsContainer | 404 | id: contentsContainer |
387 | 269 | anchors.fill: parent | 405 | anchors.fill: parent |
389 | 270 | visible: styledItem.contents || styledItem.config.contents | 406 | visible: styledItem.contents || buffer.config.contents |
390 | 271 | } | 407 | } |
391 | 272 | Binding { | 408 | Binding { |
392 | 273 | target: styledItem.contents | 409 | target: styledItem.contents |
393 | @@ -282,17 +418,17 @@ | |||
394 | 282 | when: styledItem.contents | 418 | when: styledItem.contents |
395 | 283 | } | 419 | } |
396 | 284 | Binding { | 420 | Binding { |
398 | 285 | target: styledItem.config.contents | 421 | target: buffer.config.contents |
399 | 286 | property: "parent" | 422 | property: "parent" |
400 | 287 | value: contentsContainer | 423 | value: contentsContainer |
402 | 288 | when: styledItem.config.contents && !styledItem.contents | 424 | when: buffer.config.contents && !styledItem.contents |
403 | 289 | } | 425 | } |
404 | 290 | } | 426 | } |
405 | 291 | 427 | ||
406 | 292 | Row { | 428 | Row { |
407 | 293 | id: actionsContainer | 429 | id: actionsContainer |
408 | 294 | 430 | ||
410 | 295 | property var visibleActions: getVisibleActions(styledItem.config.actions) | 431 | property var visibleActions: getVisibleActions(buffer.config.actions) |
411 | 296 | function getVisibleActions(actions) { | 432 | function getVisibleActions(actions) { |
412 | 297 | var visibleActionList = []; | 433 | var visibleActionList = []; |
413 | 298 | for (var i in actions) { | 434 | for (var i in actions) { |
414 | @@ -316,7 +452,7 @@ | |||
415 | 316 | 452 | ||
416 | 317 | anchors { | 453 | anchors { |
417 | 318 | top: parent.top | 454 | top: parent.top |
419 | 319 | right: parent.right | 455 | right: rightAnchor.left |
420 | 320 | rightMargin: units.gu(1) | 456 | rightMargin: units.gu(1) |
421 | 321 | } | 457 | } |
422 | 322 | width: childrenRect.width | 458 | width: childrenRect.width |
423 | @@ -328,7 +464,7 @@ | |||
424 | 328 | id: actionButton | 464 | id: actionButton |
425 | 329 | objectName: action.objectName + "_header_button" | 465 | objectName: action.objectName + "_header_button" |
426 | 330 | action: actionsContainer.visibleActions[index] | 466 | action: actionsContainer.visibleActions[index] |
428 | 331 | color: styledItem.config.foregroundColor | 467 | color: buffer.config.foregroundColor |
429 | 332 | } | 468 | } |
430 | 333 | } | 469 | } |
431 | 334 | 470 | ||
432 | @@ -337,8 +473,9 @@ | |||
433 | 337 | objectName: "actions_overflow_button" | 473 | objectName: "actions_overflow_button" |
434 | 338 | visible: numberOfSlots.requested > numberOfSlots.right | 474 | visible: numberOfSlots.requested > numberOfSlots.right |
435 | 339 | iconName: "contextual-menu" | 475 | iconName: "contextual-menu" |
437 | 340 | color: styledItem.config.foregroundColor | 476 | color: buffer.config.foregroundColor |
438 | 341 | height: actionsContainer.height | 477 | height: actionsContainer.height |
439 | 478 | |||
440 | 342 | onTriggered: PopupUtils.open(actionsOverflowPopoverComponent, actionsOverflowButton) | 479 | onTriggered: PopupUtils.open(actionsOverflowPopoverComponent, actionsOverflowButton) |
441 | 343 | 480 | ||
442 | 344 | Component { | 481 | Component { |
443 | 345 | 482 | ||
444 | === modified file 'tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_header.py' | |||
445 | --- tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_header.py 2014-08-20 06:45:28 +0000 | |||
446 | +++ tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_header.py 2014-08-26 11:42:43 +0000 | |||
447 | @@ -67,6 +67,7 @@ | |||
448 | 67 | raise _common.ToolkitException('Old header has no sections') | 67 | raise _common.ToolkitException('Old header has no sections') |
449 | 68 | 68 | ||
450 | 69 | try: | 69 | try: |
451 | 70 | self.wait_for_animation() | ||
452 | 70 | object_name = "section_button_" + str(index) | 71 | object_name = "section_button_" + str(index) |
453 | 71 | button = self.select_single(objectName=object_name) | 72 | button = self.select_single(objectName=object_name) |
454 | 72 | except dbus.StateNotFoundError: | 73 | except dbus.StateNotFoundError: |
455 | @@ -79,6 +80,7 @@ | |||
456 | 79 | if self.useDeprecatedToolbar: | 80 | if self.useDeprecatedToolbar: |
457 | 80 | raise _common.ToolkitException('Old header has no sections') | 81 | raise _common.ToolkitException('Old header has no sections') |
458 | 81 | 82 | ||
459 | 83 | self.wait_for_animation() | ||
460 | 82 | sectionsProperties = self.select_single( | 84 | sectionsProperties = self.select_single( |
461 | 83 | 'QQuickItem', objectName='sectionsProperties') | 85 | 'QQuickItem', objectName='sectionsProperties') |
462 | 84 | return sectionsProperties.selectedIndex | 86 | return sectionsProperties.selectedIndex |
463 | @@ -89,12 +91,14 @@ | |||
464 | 89 | if self.useDeprecatedToolbar: | 91 | if self.useDeprecatedToolbar: |
465 | 90 | raise _common.ToolkitException('Old header has no back button') | 92 | raise _common.ToolkitException('Old header has no back button') |
466 | 91 | try: | 93 | try: |
467 | 94 | self.wait_for_animation() | ||
468 | 92 | back_button = self.select_single(objectName='backButton') | 95 | back_button = self.select_single(objectName='backButton') |
469 | 93 | except dbus.StateNotFoundError: | 96 | except dbus.StateNotFoundError: |
470 | 94 | raise _common.ToolkitException('Missing back button in header') | 97 | raise _common.ToolkitException('Missing back button in header') |
471 | 95 | if not back_button.visible: | 98 | if not back_button.visible: |
472 | 96 | raise _common.ToolkitException('Back button in header not visible') | 99 | raise _common.ToolkitException('Back button in header not visible') |
473 | 97 | self.pointing_device.click_object(back_button) | 100 | self.pointing_device.click_object(back_button) |
474 | 101 | self.wait_for_animation() | ||
475 | 98 | 102 | ||
476 | 99 | def click_custom_back_button(self): | 103 | def click_custom_back_button(self): |
477 | 100 | self._show_if_not_visible() | 104 | self._show_if_not_visible() |
478 | @@ -103,6 +107,7 @@ | |||
479 | 103 | raise _common.ToolkitException( | 107 | raise _common.ToolkitException( |
480 | 104 | 'Old header has no custom back button') | 108 | 'Old header has no custom back button') |
481 | 105 | try: | 109 | try: |
482 | 110 | self.wait_for_animation() | ||
483 | 106 | custom_back_button = self.select_single( | 111 | custom_back_button = self.select_single( |
484 | 107 | objectName='customBackButton') | 112 | objectName='customBackButton') |
485 | 108 | except dbus.StateNotFoundError: | 113 | except dbus.StateNotFoundError: |
486 | @@ -112,6 +117,7 @@ | |||
487 | 112 | raise _common.ToolkitException( | 117 | raise _common.ToolkitException( |
488 | 113 | 'Custom back button in header not visible') | 118 | 'Custom back button in header not visible') |
489 | 114 | self.pointing_device.click_object(custom_back_button) | 119 | self.pointing_device.click_object(custom_back_button) |
490 | 120 | self.wait_for_animation() | ||
491 | 115 | 121 | ||
492 | 116 | def _get_animating(self): | 122 | def _get_animating(self): |
493 | 117 | if self.useDeprecatedToolbar: | 123 | if self.useDeprecatedToolbar: |
494 | @@ -120,6 +126,14 @@ | |||
495 | 120 | else: | 126 | else: |
496 | 121 | return False | 127 | return False |
497 | 122 | 128 | ||
498 | 129 | def wait_for_animation(self): | ||
499 | 130 | try: | ||
500 | 131 | style = self.select_single(objectName='PageHeadStyle') | ||
501 | 132 | style.animating.wait_for(False) | ||
502 | 133 | except dbus.StateNotFoundError: | ||
503 | 134 | raise _common.ToolkitException( | ||
504 | 135 | 'AppHeader is not using the new PageHeadStyle') | ||
505 | 136 | |||
506 | 123 | @autopilot_logging.log_action(logger.info) | 137 | @autopilot_logging.log_action(logger.info) |
507 | 124 | def switch_to_next_tab(self): | 138 | def switch_to_next_tab(self): |
508 | 125 | """Open the next tab. | 139 | """Open the next tab. |
509 | @@ -143,6 +157,7 @@ | |||
510 | 143 | self._get_animating().wait_for(False) | 157 | self._get_animating().wait_for(False) |
511 | 144 | 158 | ||
512 | 145 | def _switch_to_next_tab_in_drawer(self): | 159 | def _switch_to_next_tab_in_drawer(self): |
513 | 160 | self.wait_for_animation() | ||
514 | 146 | tabs_model_properties = self.select_single( | 161 | tabs_model_properties = self.select_single( |
515 | 147 | 'QQuickItem', objectName='tabsModelProperties') | 162 | 'QQuickItem', objectName='tabsModelProperties') |
516 | 148 | next_tab_index = (tabs_model_properties.selectedIndex | 163 | next_tab_index = (tabs_model_properties.selectedIndex |
517 | @@ -167,6 +182,7 @@ | |||
518 | 167 | self._switch_to_tab_in_drawer_by_index(index) | 182 | self._switch_to_tab_in_drawer_by_index(index) |
519 | 168 | 183 | ||
520 | 169 | def _switch_to_tab_in_drawer_by_index(self, index): | 184 | def _switch_to_tab_in_drawer_by_index(self, index): |
521 | 185 | self.wait_for_animation() | ||
522 | 170 | try: | 186 | try: |
523 | 171 | tabs_drawer_button = self.select_single(objectName='tabsButton') | 187 | tabs_drawer_button = self.select_single(objectName='tabsButton') |
524 | 172 | except dbus.StateNotFoundError: | 188 | except dbus.StateNotFoundError: |
525 | @@ -187,6 +203,7 @@ | |||
526 | 187 | "Tab button {0} not found.".format(index)) | 203 | "Tab button {0} not found.".format(index)) |
527 | 188 | 204 | ||
528 | 189 | self.pointing_device.click_object(tab_button) | 205 | self.pointing_device.click_object(tab_button) |
529 | 206 | self.wait_for_animation() | ||
530 | 190 | 207 | ||
531 | 191 | def click_action_button(self, action_object_name): | 208 | def click_action_button(self, action_object_name): |
532 | 192 | """Click an action button of the header. | 209 | """Click an action button of the header. |
533 | @@ -200,8 +217,10 @@ | |||
534 | 200 | 217 | ||
535 | 201 | button = self._get_action_button(action_object_name) | 218 | button = self._get_action_button(action_object_name) |
536 | 202 | self.pointing_device.click_object(button) | 219 | self.pointing_device.click_object(button) |
537 | 220 | self.wait_for_animation() | ||
538 | 203 | 221 | ||
539 | 204 | def _get_action_button(self, action_object_name): | 222 | def _get_action_button(self, action_object_name): |
540 | 223 | self.wait_for_animation() | ||
541 | 205 | try: | 224 | try: |
542 | 206 | object_name = action_object_name + "_header_button" | 225 | object_name = action_object_name + "_header_button" |
543 | 207 | button = self.select_single(objectName=object_name) | 226 | button = self.select_single(objectName=object_name) |
544 | 208 | 227 | ||
545 | === modified file 'tests/autopilot/ubuntuuitoolkit/tests/components/test_header.py' | |||
546 | --- tests/autopilot/ubuntuuitoolkit/tests/components/test_header.py 2014-07-31 08:21:14 +0000 | |||
547 | +++ tests/autopilot/ubuntuuitoolkit/tests/components/test_header.py 2014-08-26 11:42:43 +0000 | |||
548 | @@ -81,6 +81,7 @@ | |||
549 | 81 | objectName='push_button') | 81 | objectName='push_button') |
550 | 82 | self.pointing_device.move_to_object(pushButton) | 82 | self.pointing_device.move_to_object(pushButton) |
551 | 83 | self.pointing_device.click() | 83 | self.pointing_device.click() |
552 | 84 | self.header.wait_for_animation() | ||
553 | 84 | 85 | ||
554 | 85 | self.assertEqual(label.visible, False) | 86 | self.assertEqual(label.visible, False) |
555 | 86 | headerContents = self.header.select_single( | 87 | headerContents = self.header.select_single( |
556 | @@ -94,6 +95,7 @@ | |||
557 | 94 | objectName='push_button') | 95 | objectName='push_button') |
558 | 95 | self.pointing_device.move_to_object(pushButton) | 96 | self.pointing_device.move_to_object(pushButton) |
559 | 96 | self.pointing_device.click() | 97 | self.pointing_device.click() |
560 | 98 | self.header.wait_for_animation() | ||
561 | 97 | 99 | ||
562 | 98 | headerContents = self.header.select_single( | 100 | headerContents = self.header.select_single( |
563 | 99 | objectName='orange_header_contents') | 101 | objectName='orange_header_contents') |
564 | 100 | 102 | ||
565 | === added file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_tabs.TabsTestCase.deprecated_TabBar.qml' | |||
566 | --- tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_tabs.TabsTestCase.deprecated_TabBar.qml 1970-01-01 00:00:00 +0000 | |||
567 | +++ tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_tabs.TabsTestCase.deprecated_TabBar.qml 2014-08-26 11:42:43 +0000 | |||
568 | @@ -0,0 +1,61 @@ | |||
569 | 1 | /* | ||
570 | 2 | * Copyright 2014 Canonical Ltd. | ||
571 | 3 | * | ||
572 | 4 | * This program is free software; you can redistribute it and/or modify | ||
573 | 5 | * it under the terms of the GNU Lesser General Public License as published by | ||
574 | 6 | * the Free Software Foundation; version 3. | ||
575 | 7 | * | ||
576 | 8 | * This program is distributed in the hope that it will be useful, | ||
577 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
578 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
579 | 11 | * GNU Lesser General Public License for more details. | ||
580 | 12 | * | ||
581 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
582 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
583 | 15 | */ | ||
584 | 16 | |||
585 | 17 | import QtQuick 2.2 | ||
586 | 18 | import Ubuntu.Components 1.1 | ||
587 | 19 | |||
588 | 20 | MainView { | ||
589 | 21 | width: units.gu(70) | ||
590 | 22 | height: units.gu(60) | ||
591 | 23 | useDeprecatedToolbar: true | ||
592 | 24 | |||
593 | 25 | Tabs { | ||
594 | 26 | id: tabs | ||
595 | 27 | Tab { | ||
596 | 28 | objectName: "tab1" | ||
597 | 29 | title: "Tab1" | ||
598 | 30 | Page { | ||
599 | 31 | tools: ToolbarItems { | ||
600 | 32 | ToolbarButton { | ||
601 | 33 | text: "Test1" | ||
602 | 34 | } | ||
603 | 35 | } | ||
604 | 36 | } | ||
605 | 37 | } | ||
606 | 38 | Tab { | ||
607 | 39 | objectName: "tab2" | ||
608 | 40 | title: "Tab2" | ||
609 | 41 | Page { | ||
610 | 42 | tools: ToolbarItems { | ||
611 | 43 | ToolbarButton { | ||
612 | 44 | text: "Test2" | ||
613 | 45 | } | ||
614 | 46 | } | ||
615 | 47 | } | ||
616 | 48 | } | ||
617 | 49 | Tab { | ||
618 | 50 | objectName: "tab3" | ||
619 | 51 | title: "Tab3" | ||
620 | 52 | Page { | ||
621 | 53 | tools: ToolbarItems { | ||
622 | 54 | ToolbarButton { | ||
623 | 55 | text: "Test3" | ||
624 | 56 | } | ||
625 | 57 | } | ||
626 | 58 | } | ||
627 | 59 | } | ||
628 | 60 | } | ||
629 | 61 | } | ||
630 | 0 | 62 | ||
631 | === added file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_tabs.TabsTestCase.new_header.qml' | |||
632 | --- tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_tabs.TabsTestCase.new_header.qml 1970-01-01 00:00:00 +0000 | |||
633 | +++ tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_tabs.TabsTestCase.new_header.qml 2014-08-26 11:42:43 +0000 | |||
634 | @@ -0,0 +1,43 @@ | |||
635 | 1 | /* | ||
636 | 2 | * Copyright 2014 Canonical Ltd. | ||
637 | 3 | * | ||
638 | 4 | * This program is free software; you can redistribute it and/or modify | ||
639 | 5 | * it under the terms of the GNU Lesser General Public License as published by | ||
640 | 6 | * the Free Software Foundation; version 3. | ||
641 | 7 | * | ||
642 | 8 | * This program is distributed in the hope that it will be useful, | ||
643 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
644 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
645 | 11 | * GNU Lesser General Public License for more details. | ||
646 | 12 | * | ||
647 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
648 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
649 | 15 | */ | ||
650 | 16 | |||
651 | 17 | import QtQuick 2.2 | ||
652 | 18 | import Ubuntu.Components 1.1 | ||
653 | 19 | |||
654 | 20 | MainView { | ||
655 | 21 | width: units.gu(70) | ||
656 | 22 | height: units.gu(60) | ||
657 | 23 | useDeprecatedToolbar: false | ||
658 | 24 | |||
659 | 25 | Tabs { | ||
660 | 26 | id: tabs | ||
661 | 27 | Tab { | ||
662 | 28 | objectName: "tab1" | ||
663 | 29 | title: "Tab1" | ||
664 | 30 | Page { } | ||
665 | 31 | } | ||
666 | 32 | Tab { | ||
667 | 33 | objectName: "tab2" | ||
668 | 34 | title: "Tab2" | ||
669 | 35 | Page { } | ||
670 | 36 | } | ||
671 | 37 | Tab { | ||
672 | 38 | objectName: "tab3" | ||
673 | 39 | title: "Tab3" | ||
674 | 40 | Page { } | ||
675 | 41 | } | ||
676 | 42 | } | ||
677 | 43 | } | ||
678 | 0 | 44 | ||
679 | === modified file 'tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_tabs.py' | |||
680 | --- tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_tabs.py 2014-06-02 23:21:48 +0000 | |||
681 | +++ tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_tabs.py 2014-08-26 11:42:43 +0000 | |||
682 | @@ -14,6 +14,8 @@ | |||
683 | 14 | # You should have received a copy of the GNU Lesser General Public License | 14 | # You should have received a copy of the GNU Lesser General Public License |
684 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
685 | 16 | 16 | ||
686 | 17 | import os | ||
687 | 18 | |||
688 | 17 | try: | 19 | try: |
689 | 18 | from unittest import mock | 20 | from unittest import mock |
690 | 19 | except ImportError: | 21 | except ImportError: |
691 | @@ -23,64 +25,20 @@ | |||
692 | 23 | from ubuntuuitoolkit import tests | 25 | from ubuntuuitoolkit import tests |
693 | 24 | 26 | ||
694 | 25 | 27 | ||
745 | 26 | TEST_TABS_QML_FORMAT = (""" | 28 | class TabsTestCase(tests.QMLFileAppTestCase): |
746 | 27 | import QtQuick 2.0 | 29 | |
747 | 28 | import Ubuntu.Components 1.0 | 30 | path = os.path.abspath(__file__) |
748 | 29 | 31 | dir_path = os.path.dirname(path) | |
749 | 30 | MainView {{ | 32 | deprecated_tabbar_test_qml_file_path = os.path.join( |
750 | 31 | width: units.gu(70) | 33 | dir_path, 'test_tabs.TabsTestCase.deprecated_TabBar.qml') |
751 | 32 | height: units.gu(60) | 34 | new_header_test_qml_file_path = os.path.join( |
752 | 33 | useDeprecatedToolbar: {use_deprecated_toolbar} | 35 | dir_path, 'test_tabs.TabsTestCase.new_header.qml') |
703 | 34 | |||
704 | 35 | Tabs {{ | ||
705 | 36 | id: tabs | ||
706 | 37 | Tab {{ | ||
707 | 38 | objectName: "tab1" | ||
708 | 39 | title: "Tab1" | ||
709 | 40 | Page {{ | ||
710 | 41 | tools: ToolbarItems {{ | ||
711 | 42 | ToolbarButton {{ | ||
712 | 43 | text: "Test1" | ||
713 | 44 | }} | ||
714 | 45 | }} | ||
715 | 46 | }} | ||
716 | 47 | }} | ||
717 | 48 | Tab {{ | ||
718 | 49 | objectName: "tab2" | ||
719 | 50 | title: "Tab2" | ||
720 | 51 | Page {{ | ||
721 | 52 | tools: ToolbarItems {{ | ||
722 | 53 | ToolbarButton {{ | ||
723 | 54 | text: "Test2" | ||
724 | 55 | }} | ||
725 | 56 | }} | ||
726 | 57 | }} | ||
727 | 58 | }} | ||
728 | 59 | Tab {{ | ||
729 | 60 | objectName: "tab3" | ||
730 | 61 | title: "Tab3" | ||
731 | 62 | Page {{ | ||
732 | 63 | tools: ToolbarItems {{ | ||
733 | 64 | ToolbarButton {{ | ||
734 | 65 | text: "Test3" | ||
735 | 66 | }} | ||
736 | 67 | }} | ||
737 | 68 | }} | ||
738 | 69 | }} | ||
739 | 70 | }} | ||
740 | 71 | }} | ||
741 | 72 | """) | ||
742 | 73 | |||
743 | 74 | |||
744 | 75 | class TabsTestCase(tests.QMLStringAppTestCase): | ||
753 | 76 | 36 | ||
754 | 77 | scenarios = [ | 37 | scenarios = [ |
761 | 78 | ('deprecated tabs', dict( | 38 | ('deprecated TabBar', |
762 | 79 | test_qml=TEST_TABS_QML_FORMAT.format( | 39 | dict(test_qml_file_path=deprecated_tabbar_test_qml_file_path)), |
763 | 80 | use_deprecated_toolbar='true'))), | 40 | ('new header', |
764 | 81 | ('drawer tabs', dict( | 41 | dict(test_qml_file_path=new_header_test_qml_file_path)) |
759 | 82 | test_qml=TEST_TABS_QML_FORMAT.format( | ||
760 | 83 | use_deprecated_toolbar='false'))) | ||
765 | 84 | ] | 42 | ] |
766 | 85 | 43 | ||
767 | 86 | def test_tabs_custom_proxy_object(self): | 44 | def test_tabs_custom_proxy_object(self): |
768 | 87 | 45 | ||
769 | === modified file 'tests/resources/header/header.qml' | |||
770 | --- tests/resources/header/header.qml 2014-07-31 14:09:45 +0000 | |||
771 | +++ tests/resources/header/header.qml 2014-08-26 11:42:43 +0000 | |||
772 | @@ -206,12 +206,10 @@ | |||
773 | 206 | anchors.centerIn: parent | 206 | anchors.centerIn: parent |
774 | 207 | text: "Use back button to return" | 207 | text: "Use back button to return" |
775 | 208 | } | 208 | } |
782 | 209 | tools: ToolbarItems { | 209 | head { |
783 | 210 | ToolbarButton { | 210 | actions: Action { |
784 | 211 | action: Action { | 211 | iconName: "settings" |
785 | 212 | iconName: "settings" | 212 | text: "settings" |
780 | 213 | text: "settings" | ||
781 | 214 | } | ||
786 | 215 | } | 213 | } |
787 | 216 | } | 214 | } |
788 | 217 | } | 215 | } |
789 | 218 | 216 | ||
790 | === modified file 'tests/resources/navigation/MyCustomPage.qml' | |||
791 | --- tests/resources/navigation/MyCustomPage.qml 2014-08-26 11:42:42 +0000 | |||
792 | +++ tests/resources/navigation/MyCustomPage.qml 2014-08-26 11:42:43 +0000 | |||
793 | @@ -15,7 +15,7 @@ | |||
794 | 15 | */ | 15 | */ |
795 | 16 | 16 | ||
796 | 17 | import QtQuick 2.0 | 17 | import QtQuick 2.0 |
798 | 18 | import Ubuntu.Components 1.0 | 18 | import Ubuntu.Components 1.1 |
799 | 19 | 19 | ||
800 | 20 | Page { | 20 | Page { |
801 | 21 | title: i18n.tr("My custom page") | 21 | title: i18n.tr("My custom page") |
802 | @@ -30,32 +30,24 @@ | |||
803 | 30 | horizontalCenter: parent.horizontalCenter | 30 | horizontalCenter: parent.horizontalCenter |
804 | 31 | } | 31 | } |
805 | 32 | 32 | ||
807 | 33 | text: i18n.tr("This is an external page\nwith a locked toolbar.") | 33 | text: i18n.tr("This is an external page.") |
808 | 34 | color: "#757373" | 34 | color: "#757373" |
809 | 35 | } | 35 | } |
810 | 36 | } | 36 | } |
811 | 37 | 37 | ||
835 | 38 | tools: ToolbarItems { | 38 | head.actions: [ |
836 | 39 | ToolbarButton { | 39 | Action { |
837 | 40 | action: Action { | 40 | text: "action 1" |
838 | 41 | text: "action 1" | 41 | iconName: "outgoing-call" |
839 | 42 | iconName: "outgoing-call" | 42 | }, |
840 | 43 | } | 43 | Action { |
841 | 44 | } | 44 | text: "action 2" |
842 | 45 | ToolbarButton { | 45 | iconSource: "call_icon.png" |
843 | 46 | action: Action { | 46 | iconName: "missed-call" |
844 | 47 | text: "action 2" | 47 | }, |
845 | 48 | iconName: "missed-call" | 48 | Action { |
846 | 49 | } | 49 | text: "another one" |
847 | 50 | } | 50 | iconName: "contact" |
848 | 51 | ToolbarButton { | 51 | } |
849 | 52 | action: Action { | 52 | ] |
827 | 53 | text: "another one" | ||
828 | 54 | iconSource: "call_icon.png" | ||
829 | 55 | } | ||
830 | 56 | } | ||
831 | 57 | |||
832 | 58 | opened: true | ||
833 | 59 | locked: true | ||
834 | 60 | } | ||
850 | 61 | } | 53 | } |
851 | 62 | 54 | ||
852 | === modified file 'tests/unit_x11/tst_components/tst_headActions.qml' | |||
853 | --- tests/unit_x11/tst_components/tst_headActions.qml 2014-07-28 18:41:55 +0000 | |||
854 | +++ tests/unit_x11/tst_components/tst_headActions.qml 2014-08-26 11:42:43 +0000 | |||
855 | @@ -64,39 +64,52 @@ | |||
856 | 64 | property var app_header | 64 | property var app_header |
857 | 65 | property var back_button | 65 | property var back_button |
858 | 66 | property var custom_back_button | 66 | property var custom_back_button |
859 | 67 | property var head_animation | ||
860 | 67 | 68 | ||
861 | 68 | function initTestCase() { | 69 | function initTestCase() { |
862 | 69 | testCase.app_header = findChild(mainView, "MainView_Header"); | 70 | testCase.app_header = findChild(mainView, "MainView_Header"); |
863 | 70 | testCase.back_button = findChild(app_header, "backButton"); | 71 | testCase.back_button = findChild(app_header, "backButton"); |
864 | 71 | testCase.custom_back_button = findChild(app_header, "customBackButton"); | 72 | testCase.custom_back_button = findChild(app_header, "customBackButton"); |
865 | 73 | testCase.head_animation = findChild(app_header, "PageHeadStyle"); | ||
866 | 72 | 74 | ||
867 | 75 | waitHeadAnimation(); | ||
868 | 73 | compare(page2.head.backAction, null, "Back action set by default."); | 76 | compare(page2.head.backAction, null, "Back action set by default."); |
869 | 74 | compare(back_button.visible, false, "Back button visible with only 1 page on the stack."); | 77 | compare(back_button.visible, false, "Back button visible with only 1 page on the stack."); |
870 | 75 | compare(custom_back_button.visible, false, "Custom back button visible without custom back action.") | 78 | compare(custom_back_button.visible, false, "Custom back button visible without custom back action.") |
871 | 76 | } | 79 | } |
872 | 77 | 80 | ||
873 | 81 | function waitHeadAnimation() { | ||
874 | 82 | tryCompareFunction(function(){return testCase.head_animation.animating}, false); | ||
875 | 83 | } | ||
876 | 84 | |||
877 | 78 | function test_default_back_button() { | 85 | function test_default_back_button() { |
878 | 79 | pageStack.push(page2); | 86 | pageStack.push(page2); |
879 | 87 | waitHeadAnimation(); | ||
880 | 80 | compare(back_button.visible, true, "Back button not visible with 2 pages on stack."); | 88 | compare(back_button.visible, true, "Back button not visible with 2 pages on stack."); |
881 | 81 | compare(custom_back_button.visible, false, "Showing custom back button without custom back action."); | 89 | compare(custom_back_button.visible, false, "Showing custom back button without custom back action."); |
882 | 82 | pageStack.pop(); | 90 | pageStack.pop(); |
883 | 91 | waitHeadAnimation(); | ||
884 | 83 | } | 92 | } |
885 | 84 | 93 | ||
886 | 85 | function test_custom_back_button() { | 94 | function test_custom_back_button() { |
887 | 86 | page2.head.backAction = customBackAction; | 95 | page2.head.backAction = customBackAction; |
888 | 87 | pageStack.push(page2); | 96 | pageStack.push(page2); |
889 | 97 | waitHeadAnimation(); | ||
890 | 88 | compare(back_button.visible, false, "Default back button visible with custom back action."); | 98 | compare(back_button.visible, false, "Default back button visible with custom back action."); |
891 | 89 | compare(custom_back_button.visible, true, "Custom back button invisible with back action."); | 99 | compare(custom_back_button.visible, true, "Custom back button invisible with back action."); |
892 | 90 | pageStack.pop(); | 100 | pageStack.pop(); |
893 | 101 | waitHeadAnimation(); | ||
894 | 91 | page2.head.backAction = null; | 102 | page2.head.backAction = null; |
895 | 92 | } | 103 | } |
896 | 93 | 104 | ||
897 | 94 | function test_no_back_button() { | 105 | function test_no_back_button() { |
898 | 95 | page2.head.backAction = invisibleAction; | 106 | page2.head.backAction = invisibleAction; |
899 | 96 | pageStack.push(page2); | 107 | pageStack.push(page2); |
900 | 108 | waitHeadAnimation(); | ||
901 | 97 | compare(back_button.visible, false, "Default back button visible with invisible custom back action."); | 109 | compare(back_button.visible, false, "Default back button visible with invisible custom back action."); |
902 | 98 | compare(custom_back_button.visible, false, "Custom back button visible with invisible custom back action."); | 110 | compare(custom_back_button.visible, false, "Custom back button visible with invisible custom back action."); |
903 | 99 | pageStack.pop(); | 111 | pageStack.pop(); |
904 | 112 | waitHeadAnimation(); | ||
905 | 100 | page2.head.backAction = null; | 113 | page2.head.backAction = null; |
906 | 101 | } | 114 | } |
907 | 102 | } | 115 | } |
908 | 103 | 116 | ||
909 | === modified file 'tests/unit_x11/tst_components/tst_pagestack_new_header.qml' | |||
910 | --- tests/unit_x11/tst_components/tst_pagestack_new_header.qml 2014-08-18 10:40:45 +0000 | |||
911 | +++ tests/unit_x11/tst_components/tst_pagestack_new_header.qml 2014-08-26 11:42:43 +0000 | |||
912 | @@ -27,6 +27,7 @@ | |||
913 | 27 | 27 | ||
914 | 28 | MainView { | 28 | MainView { |
915 | 29 | id: mainView | 29 | id: mainView |
916 | 30 | anchors.fill: parent | ||
917 | 30 | useDeprecatedToolbar: false | 31 | useDeprecatedToolbar: false |
918 | 31 | PageStack { | 32 | PageStack { |
919 | 32 | id: pageStack | 33 | id: pageStack |
920 | @@ -65,7 +66,13 @@ | |||
921 | 65 | when: windowShown | 66 | when: windowShown |
922 | 66 | id: testCase | 67 | id: testCase |
923 | 67 | 68 | ||
924 | 69 | function waitPageHeadAnimation() { | ||
925 | 70 | var pageHeadStyle = findChild(mainView, "PageHeadStyle"); | ||
926 | 71 | tryCompare(pageHeadStyle, "animating", false); | ||
927 | 72 | } | ||
928 | 73 | |||
929 | 68 | function initTestCase() { | 74 | function initTestCase() { |
930 | 75 | waitPageHeadAnimation(); | ||
931 | 69 | compare(pageStack.currentPage, null, "is not set by default"); | 76 | compare(pageStack.currentPage, null, "is not set by default"); |
932 | 70 | compare(pageStack.__propagated, mainView.__propagated, "propagated property of pageStack equals mainView.__propagated") | 77 | compare(pageStack.__propagated, mainView.__propagated, "propagated property of pageStack equals mainView.__propagated") |
933 | 71 | compare(mainView.__propagated.header.title, "", "empty title by default"); | 78 | compare(mainView.__propagated.header.title, "", "empty title by default"); |
934 | @@ -74,55 +81,73 @@ | |||
935 | 74 | function test_depth() { | 81 | function test_depth() { |
936 | 75 | compare(pageStack.depth, 0, "depth is 0 by default"); | 82 | compare(pageStack.depth, 0, "depth is 0 by default"); |
937 | 76 | pageStack.push(page1); | 83 | pageStack.push(page1); |
938 | 84 | waitPageHeadAnimation(); | ||
939 | 77 | compare(pageStack.depth, 1, "depth is correctly increased when pushing a page"); | 85 | compare(pageStack.depth, 1, "depth is correctly increased when pushing a page"); |
940 | 78 | pageStack.push(page2); | 86 | pageStack.push(page2); |
941 | 87 | waitPageHeadAnimation(); | ||
942 | 79 | compare(pageStack.depth, 2, "depth is correctly updated when pushing a page"); | 88 | compare(pageStack.depth, 2, "depth is correctly updated when pushing a page"); |
943 | 80 | pageStack.pop(); | 89 | pageStack.pop(); |
944 | 90 | waitPageHeadAnimation(); | ||
945 | 81 | compare(pageStack.depth, 1, "depth is correctly decreased when popping a page"); | 91 | compare(pageStack.depth, 1, "depth is correctly decreased when popping a page"); |
946 | 82 | pageStack.clear(); | 92 | pageStack.clear(); |
947 | 93 | waitPageHeadAnimation(); | ||
948 | 83 | compare(pageStack.depth, 0, "depth is after clearing"); | 94 | compare(pageStack.depth, 0, "depth is after clearing"); |
949 | 84 | } | 95 | } |
950 | 85 | 96 | ||
951 | 86 | function test_currentPage() { | 97 | function test_currentPage() { |
952 | 87 | compare(pageStack.currentPage, null, "currentPage is null by default"); | 98 | compare(pageStack.currentPage, null, "currentPage is null by default"); |
953 | 88 | pageStack.push(page1); | 99 | pageStack.push(page1); |
954 | 100 | waitPageHeadAnimation(); | ||
955 | 89 | compare(pageStack.currentPage, page1, "currentPage properly updated"); | 101 | compare(pageStack.currentPage, page1, "currentPage properly updated"); |
956 | 90 | pageStack.clear(); | 102 | pageStack.clear(); |
957 | 103 | waitPageHeadAnimation(); | ||
958 | 91 | compare(pageStack.currentPage, null, "currentPage properly reset"); | 104 | compare(pageStack.currentPage, null, "currentPage properly reset"); |
959 | 92 | } | 105 | } |
960 | 93 | 106 | ||
961 | 94 | function test_active_bug1260116() { | 107 | function test_active_bug1260116() { |
962 | 95 | pageStack.push(page1); | 108 | pageStack.push(page1); |
963 | 109 | waitPageHeadAnimation(); | ||
964 | 96 | compare(page1.active, true, "Page is active after pushing"); | 110 | compare(page1.active, true, "Page is active after pushing"); |
965 | 97 | pageStack.push(page2); | 111 | pageStack.push(page2); |
966 | 112 | waitPageHeadAnimation(); | ||
967 | 98 | compare(page1.active, false, "Page no longer active after pushing a new page"); | 113 | compare(page1.active, false, "Page no longer active after pushing a new page"); |
968 | 99 | compare(page2.active, true, "New page is active after pushing"); | 114 | compare(page2.active, true, "New page is active after pushing"); |
969 | 100 | pageStack.pop(); | 115 | pageStack.pop(); |
970 | 116 | waitPageHeadAnimation(); | ||
971 | 101 | compare(page1.active, true, "Page re-activated when on top of the stack"); | 117 | compare(page1.active, true, "Page re-activated when on top of the stack"); |
972 | 102 | compare(page2.active, false, "Page no longer active after being popped"); | 118 | compare(page2.active, false, "Page no longer active after being popped"); |
973 | 103 | pageStack.clear(); | 119 | pageStack.clear(); |
974 | 120 | waitPageHeadAnimation(); | ||
975 | 104 | 121 | ||
976 | 105 | compare(pageInStack.active, false, "Page defined inside PageStack is not active by default"); | 122 | compare(pageInStack.active, false, "Page defined inside PageStack is not active by default"); |
977 | 106 | pageStack.push(pageInStack); | 123 | pageStack.push(pageInStack); |
978 | 124 | waitPageHeadAnimation(); | ||
979 | 107 | compare(pageInStack.active, true, "Pushing a page on PageStack makes it active"); | 125 | compare(pageInStack.active, true, "Pushing a page on PageStack makes it active"); |
980 | 108 | pageStack.pop(); | 126 | pageStack.pop(); |
981 | 127 | waitPageHeadAnimation(); | ||
982 | 109 | compare(pageInStack.active, false, "Popping a page from PageStack makes it inactive"); | 128 | compare(pageInStack.active, false, "Popping a page from PageStack makes it inactive"); |
983 | 110 | pageStack.clear(); | 129 | pageStack.clear(); |
984 | 130 | waitPageHeadAnimation(); | ||
985 | 111 | } | 131 | } |
986 | 112 | 132 | ||
987 | 113 | function test_title_bug1143345_bug1317902() { | 133 | function test_title_bug1143345_bug1317902() { |
988 | 114 | pageStack.push(page1); | 134 | pageStack.push(page1); |
989 | 135 | waitPageHeadAnimation(); | ||
990 | 115 | compare(mainView.__propagated.header.title, "Title 1", "Header title is correctly set by page"); | 136 | compare(mainView.__propagated.header.title, "Title 1", "Header title is correctly set by page"); |
991 | 116 | page1.title = "New title"; | 137 | page1.title = "New title"; |
992 | 117 | compare(mainView.__propagated.header.title, "New title", "Header title correctly updated by page"); | 138 | compare(mainView.__propagated.header.title, "New title", "Header title correctly updated by page"); |
993 | 118 | pageStack.push(page2); | 139 | pageStack.push(page2); |
994 | 140 | waitPageHeadAnimation(); | ||
995 | 119 | compare(mainView.__propagated.header.title, "Title 2", "Header title is correctly set by page"); | 141 | compare(mainView.__propagated.header.title, "Title 2", "Header title is correctly set by page"); |
996 | 120 | pageStack.clear(); | 142 | pageStack.clear(); |
997 | 143 | waitPageHeadAnimation(); | ||
998 | 121 | page1.title = "Title 1"; | 144 | page1.title = "Title 1"; |
999 | 122 | 145 | ||
1000 | 123 | pageStack.push(pageWithPage); | 146 | pageStack.push(pageWithPage); |
1001 | 147 | waitPageHeadAnimation(); | ||
1002 | 124 | compare(mainView.__propagated.header.title, pageWithPage.title, "Embedded page sets title of outer page"); | 148 | compare(mainView.__propagated.header.title, pageWithPage.title, "Embedded page sets title of outer page"); |
1003 | 125 | pageStack.clear(); | 149 | pageStack.clear(); |
1004 | 150 | waitPageHeadAnimation(); | ||
1005 | 126 | } | 151 | } |
1006 | 127 | 152 | ||
1007 | 128 | function get_tabs_button() { | 153 | function get_tabs_button() { |
1008 | @@ -134,28 +159,36 @@ | |||
1009 | 134 | function test_tabs_inside_stack_bug1187850() { | 159 | function test_tabs_inside_stack_bug1187850() { |
1010 | 135 | compare(get_tabs_button(), null, "Without tabs there is no visible tabs button."); | 160 | compare(get_tabs_button(), null, "Without tabs there is no visible tabs button."); |
1011 | 136 | pageStack.push(tabs); | 161 | pageStack.push(tabs); |
1012 | 162 | waitPageHeadAnimation(); | ||
1013 | 137 | compare(pageStack.currentPage, tabs, "Tabs can be pushed on a PageStack"); | 163 | compare(pageStack.currentPage, tabs, "Tabs can be pushed on a PageStack"); |
1014 | 138 | compare(tabs.active, true, "Tabs on top of a PageStack are active"); | 164 | compare(tabs.active, true, "Tabs on top of a PageStack are active"); |
1015 | 139 | compare(get_tabs_button().visible, true, "Pushing tabs on pagestack enables the tabs button"); | 165 | compare(get_tabs_button().visible, true, "Pushing tabs on pagestack enables the tabs button"); |
1016 | 140 | pageStack.push(page1); | 166 | pageStack.push(page1); |
1017 | 167 | waitPageHeadAnimation(); | ||
1018 | 141 | compare(pageStack.currentPage, page1, "A page can be pushed on top of a Tabs"); | 168 | compare(pageStack.currentPage, page1, "A page can be pushed on top of a Tabs"); |
1019 | 142 | compare(tabs.active, false, "Tabs on a PageStack, but not on top, are inactive"); | 169 | compare(tabs.active, false, "Tabs on a PageStack, but not on top, are inactive"); |
1020 | 143 | compare(get_tabs_button(), null, "Contents of inactive Tabs is not applied to header"); | 170 | compare(get_tabs_button(), null, "Contents of inactive Tabs is not applied to header"); |
1021 | 144 | pageStack.pop(); | 171 | pageStack.pop(); |
1022 | 172 | waitPageHeadAnimation(); | ||
1023 | 145 | compare(tabs.active, true, "Tabs on top of PageStack is active"); | 173 | compare(tabs.active, true, "Tabs on top of PageStack is active"); |
1024 | 146 | compare(get_tabs_button().visible, true, "Active Tabs controls header contents"); | 174 | compare(get_tabs_button().visible, true, "Active Tabs controls header contents"); |
1025 | 147 | pageStack.clear(); | 175 | pageStack.clear(); |
1026 | 176 | waitPageHeadAnimation(); | ||
1027 | 148 | } | 177 | } |
1028 | 149 | 178 | ||
1029 | 150 | function test_pop_to_tabs_bug1316736() { | 179 | function test_pop_to_tabs_bug1316736() { |
1030 | 151 | pageStack.push(tabs); | 180 | pageStack.push(tabs); |
1031 | 181 | waitPageHeadAnimation(); | ||
1032 | 152 | tabs.selectedTabIndex = 1; | 182 | tabs.selectedTabIndex = 1; |
1033 | 153 | pageStack.push(page1); | 183 | pageStack.push(page1); |
1034 | 184 | waitPageHeadAnimation(); | ||
1035 | 154 | compare(tabs.active, false, "Tabs on a PageStack, but not on top, are inactive"); | 185 | compare(tabs.active, false, "Tabs on a PageStack, but not on top, are inactive"); |
1036 | 155 | pageStack.pop(); | 186 | pageStack.pop(); |
1037 | 187 | waitPageHeadAnimation(); | ||
1038 | 156 | compare(tabs.active, true, "Tabs on top of PageStack is active"); | 188 | compare(tabs.active, true, "Tabs on top of PageStack is active"); |
1039 | 157 | compare(tabs.selectedTabIndex, 1, "Pushing and popping another page on top of Tabs does not change selectedTabsIndex"); | 189 | compare(tabs.selectedTabIndex, 1, "Pushing and popping another page on top of Tabs does not change selectedTabsIndex"); |
1040 | 158 | pageStack.clear(); | 190 | pageStack.clear(); |
1041 | 191 | waitPageHeadAnimation(); | ||
1042 | 159 | } | 192 | } |
1043 | 160 | } | 193 | } |
1044 | 161 | } | 194 | } |
PASSED: Continuous integration, rev:1210 jenkins. qa.ubuntu. com/job/ ubuntu- sdk-team- ubuntu- ui-toolkit- staging- ci/889/ jenkins. qa.ubuntu. com/job/ ubuntu- sdk-team- ubuntu- ui-toolkit- staging- utopic- amd64-ci/ 721 jenkins. qa.ubuntu. com/job/ ubuntu- sdk-team- ubuntu- ui-toolkit- staging- utopic- armhf-ci/ 721 jenkins. qa.ubuntu. com/job/ ubuntu- sdk-team- ubuntu- ui-toolkit- staging- utopic- armhf-ci/ 721/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ ubuntu- sdk-team- ubuntu- ui-toolkit- staging- utopic- i386-ci/ 721
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/ubuntu- sdk-team- ubuntu- ui-toolkit- staging- ci/889/ rebuild
http://