Merge lp:~aacid/unity8/filtergrid_bindingloop into lp:unity8
- filtergrid_bindingloop
- Merge into trunk
Proposed by
Albert Astals Cid
Status: | Superseded |
---|---|
Proposed branch: | lp:~aacid/unity8/filtergrid_bindingloop |
Merge into: | lp:unity8 |
Diff against target: |
887 lines (+328/-182) 13 files modified
cmake/modules/QmlTest.cmake (+16/-1) qml/Components/FilterGrid.qml (+40/-23) qml/Dash/Card.qml (+109/-89) qml/Dash/CardCarousel.qml (+1/-1) qml/Dash/CardFilterGrid.qml (+5/-10) qml/Dash/CardHeader.qml (+42/-32) qml/Dash/DashRenderer.qml (+1/-4) qml/Dash/GenericScopeView.qml (+5/-10) tests/qmltests/CMakeLists.txt (+1/-0) tests/qmltests/Components/tst_FilterGrid.qml (+1/-1) tests/qmltests/Dash/tst_Card.qml (+12/-10) tests/qmltests/Dash/tst_CardBenchmark.qml (+94/-0) tests/qmltests/Dash/tst_CardHeader.qml (+1/-1) |
To merge this branch: | bzr merge lp:~aacid/unity8/filtergrid_bindingloop |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Unity Team | Pending | ||
Review via email: mp+216145@code.launchpad.net |
This proposal has been superseded by a proposal from 2014-04-16.
Commit message
Fix binding loop in FilterGrid height
Description of the change
To post a comment you must log in.
- 832. By Albert Astals Cid
-
A bit more of reog
- 833. By Albert Astals Cid
-
reog++
- 834. By Albert Astals Cid
-
reorg++
- 835. By Albert Astals Cid
-
don't need the id anymroe
- 836. By Albert Astals Cid
-
property rename
- 837. By Albert Astals Cid
-
Merge
Unmerged revisions
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'cmake/modules/QmlTest.cmake' |
2 | --- cmake/modules/QmlTest.cmake 2014-04-02 12:08:51 +0000 |
3 | +++ cmake/modules/QmlTest.cmake 2014-04-16 15:47:25 +0000 |
4 | @@ -58,7 +58,15 @@ |
5 | |
6 | endmacro(add_manual_qml_test) |
7 | |
8 | +macro(add_qml_benchmark SUBPATH COMPONENT_NAME ITERATIONS) |
9 | + add_qml_test_internal(${SUBPATH} ${COMPONENT_NAME} ${ITERATIONS} ${ARGN}) |
10 | +endmacro(add_qml_benchmark) |
11 | + |
12 | macro(add_qml_test SUBPATH COMPONENT_NAME) |
13 | + add_qml_test_internal(${SUBPATH} ${COMPONENT_NAME} 0 ${ARGN}) |
14 | +endmacro(add_qml_test) |
15 | + |
16 | +macro(add_qml_test_internal SUBPATH COMPONENT_NAME ITERATIONS) |
17 | set(options NO_ADD_TEST NO_TARGETS) |
18 | set(multi_value_keywords IMPORT_PATHS TARGETS PROPERTIES ENVIRONMENT) |
19 | |
20 | @@ -88,10 +96,17 @@ |
21 | set(function_ARGS "") |
22 | endif() |
23 | |
24 | + if (${ITERATIONS} GREATER 0) |
25 | + set(ITERATIONS_STRING "-iterations" ${ITERATIONS}) |
26 | + else() |
27 | + set(ITERATIONS_STRING "") |
28 | + endif() |
29 | + |
30 | set(qmltest_command |
31 | env ${qmltest_ENVIRONMENT} |
32 | ${qmltestrunner_exe} -input ${CMAKE_CURRENT_SOURCE_DIR}/${qmltest_FILE}.qml |
33 | ${qmltestrunner_imports} |
34 | + ${ITERATIONS_STRING} |
35 | -o ${CMAKE_BINARY_DIR}/${qmltest_TARGET}.xml,xunitxml |
36 | -o -,txt |
37 | ${function_ARGS} |
38 | @@ -114,7 +129,7 @@ |
39 | add_qmltest_target(${qmltest_TARGET} "${qmltest_command}" TRUE ${qmltest_NO_ADD_TEST}) |
40 | add_qmltest_target(${qmltest_xvfb_TARGET} "${qmltest_xvfb_command}" ${qmltest_NO_TARGETS} TRUE) |
41 | add_manual_qml_test(${SUBPATH} ${COMPONENT_NAME} ${ARGN}) |
42 | -endmacro(add_qml_test) |
43 | +endmacro(add_qml_test_internal) |
44 | |
45 | macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS) |
46 | set(testCommand |
47 | |
48 | === modified file 'qml/Components/FilterGrid.qml' |
49 | --- qml/Components/FilterGrid.qml 2014-04-08 13:32:59 +0000 |
50 | +++ qml/Components/FilterGrid.qml 2014-04-16 15:47:25 +0000 |
51 | @@ -28,10 +28,13 @@ |
52 | Item { |
53 | id: root |
54 | |
55 | - /* If true, the number of elements displayed will be limited by collapsedRowCount. |
56 | - If false, all elements will be displayed, effectively looking the same as a regular |
57 | - ResponsiveGridView. */ |
58 | - property bool filter: true |
59 | + QtObject { |
60 | + id: d |
61 | + /* If true, the number of elements displayed will be limited by collapsedRowCount. |
62 | + If false, all elements will be displayed, effectively looking the same as a regular |
63 | + ResponsiveGridView. Manipulate this property through setFilter */ |
64 | + property bool filter: true |
65 | + } |
66 | |
67 | /* Whether, when collapsed, a button should be displayed enabling the user to expand |
68 | the grid to its full size. */ |
69 | @@ -66,29 +69,35 @@ |
70 | property alias highlightIndex: iconTileGrid.highlightIndex |
71 | readonly property alias currentItem: iconTileGrid.currentItem |
72 | |
73 | - height: !filterAnimation.running ? childrenRect.height : height |
74 | + height: filter ? root.collapsedHeight : root.uncollapsedHeight |
75 | clip: filterAnimation.running |
76 | |
77 | - NumberAnimation { |
78 | - property bool filterEndValue |
79 | - id: filterAnimation |
80 | - target: root |
81 | - property: "height" |
82 | - to: filterEndValue ? root.collapsedHeight : root.uncollapsedHeight |
83 | - // Duration and easing here match the ListViewWithPageHeader::m_contentYAnimation |
84 | - // otherwise since both animations can run at the same time you'll get |
85 | - // some visual weirdness. |
86 | - duration: 200 |
87 | - easing.type: Easing.InOutQuad |
88 | - onStopped: { |
89 | - root.filter = filterEndValue; |
90 | + Behavior on height { |
91 | + id: heightBehaviour |
92 | + enabled: false |
93 | + NumberAnimation { |
94 | + id: filterAnimation |
95 | + // Duration and easing here match the ListViewWithPageHeader::m_contentYAnimation |
96 | + // otherwise since both animations can run at the same time you'll get |
97 | + // some visual weirdness. |
98 | + duration: 200 |
99 | + easing.type: Easing.InOutQuad |
100 | + onRunningChanged: { |
101 | + if (!running) { |
102 | + limitModel.filter = d.filter; |
103 | + } |
104 | + heightBehaviour.enabled = false; |
105 | + } |
106 | } |
107 | } |
108 | |
109 | - function startFilterAnimation(filter) { |
110 | - filterAnimation.filterEndValue = filter |
111 | - filterAnimation.start(); |
112 | - } |
113 | + function setFilter(filter, animate) { |
114 | + heightBehaviour.enabled = animate; |
115 | + d.filter = filter; |
116 | + if (!animate || !filter) { |
117 | + limitModel.filter = filter; |
118 | + } |
119 | + } |
120 | |
121 | ResponsiveGridView { |
122 | id: iconTileGrid |
123 | @@ -104,8 +113,16 @@ |
124 | verticalSpacing: units.gu(2) |
125 | |
126 | model: LimitProxyModel { |
127 | + id: limitModel |
128 | + // We do have another filter property here because |
129 | + // we can't directly use filter from the root since we need to decouple |
130 | + // the real filtering with the animation since the closing animation |
131 | + // i.e. filter=false we still need to not be filtering until |
132 | + // t he animation finishes otherwise we hide the items when the animation |
133 | + // is still running |
134 | + property bool filter: true |
135 | model: root.model |
136 | - limit: (filter && !filterAnimation.running) ? rowsWhenCollapsed * iconTileGrid.columns : -1 |
137 | + limit: filter ? rowsWhenCollapsed * iconTileGrid.columns : -1 |
138 | } |
139 | } |
140 | } |
141 | |
142 | === modified file 'qml/Dash/Card.qml' |
143 | --- qml/Dash/Card.qml 2014-04-01 22:56:38 +0000 |
144 | +++ qml/Dash/Card.qml 2014-04-16 15:47:25 +0000 |
145 | @@ -25,47 +25,53 @@ |
146 | property var components |
147 | property var cardData |
148 | |
149 | - property alias fontScale: header.fontScale |
150 | - property alias headerAlignment: header.headerAlignment |
151 | - property alias headerHeight: header.height |
152 | - readonly property alias title: header.title |
153 | + property real fontScale: 1.0 |
154 | + property int headerAlignment: Text.AlignLeft |
155 | + readonly property int headerHeight: headerLoader.item ? headerLoader.item.height : 0 |
156 | + property int fixedHeaderHeight: -1 |
157 | + readonly property string title: cardData && cardData["title"] || "" |
158 | |
159 | property bool showHeader: true |
160 | |
161 | implicitWidth: childrenRect.width |
162 | - implicitHeight: summary.y + summary.height + (summary.text && background.visible ? units.gu(1) : 0) |
163 | - |
164 | - UbuntuShape { |
165 | - id: background |
166 | - objectName: "background" |
167 | - radius: "medium" |
168 | - visible: template["card-layout"] !== "horizontal" && (template["card-background"] || components["background"] |
169 | - || artAndSummary) |
170 | - property bool artAndSummary: components["art"]["field"] && components["summary"] || false |
171 | - color: getColor(0) || "white" |
172 | - gradientColor: getColor(1) || color |
173 | + implicitHeight: summary.y + summary.height + (summary.text && backgroundLoader.active ? units.gu(1) : 0) |
174 | + |
175 | + Loader { |
176 | + id: backgroundLoader |
177 | + objectName: "backgroundLoader" |
178 | + |
179 | + readonly property bool artAndSummary: components["art"]["field"] && components["summary"] || false |
180 | + active: template["card-layout"] !== "horizontal" && (template["card-background"] || components["background"] || artAndSummary) |
181 | anchors.fill: parent |
182 | - image: backgroundImage.source ? backgroundImage : null |
183 | - |
184 | - property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b |
185 | - |
186 | - property Image backgroundImage: Image { |
187 | - objectName: "backgroundImage" |
188 | - source: { |
189 | - if (cardData && typeof cardData["background"] === "string") return cardData["background"] |
190 | - else if (template && typeof template["card-background"] === "string") return template["card-background"] |
191 | - else return "" |
192 | - } |
193 | - } |
194 | - |
195 | - function getColor(index) { |
196 | - if (cardData && typeof cardData["background"] === "object" |
197 | - && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) { |
198 | - return cardData["background"]["elements"][index]; |
199 | - } else if (template && typeof template["card-background"] === "object" |
200 | - && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) { |
201 | - return template["card-background"]["elements"][index]; |
202 | - } else return undefined; |
203 | + |
204 | + sourceComponent: UbuntuShape { |
205 | + objectName: "background" |
206 | + radius: "medium" |
207 | + color: getColor(0) || "white" |
208 | + gradientColor: getColor(1) || color |
209 | + anchors.fill: parent |
210 | + image: backgroundImage.source ? backgroundImage : null |
211 | + |
212 | + property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b |
213 | + |
214 | + property Image backgroundImage: Image { |
215 | + objectName: "backgroundImage" |
216 | + source: { |
217 | + if (cardData && typeof cardData["background"] === "string") return cardData["background"] |
218 | + else if (template && typeof template["card-background"] === "string") return template["card-background"] |
219 | + else return "" |
220 | + } |
221 | + } |
222 | + |
223 | + function getColor(index) { |
224 | + if (cardData && typeof cardData["background"] === "object" |
225 | + && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) { |
226 | + return cardData["background"]["elements"][index]; |
227 | + } else if (template && typeof template["card-background"] === "object" |
228 | + && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) { |
229 | + return template["card-background"]["elements"][index]; |
230 | + } else return undefined; |
231 | + } |
232 | } |
233 | } |
234 | |
235 | @@ -111,7 +117,7 @@ |
236 | function updateWidthHeightBindings() { |
237 | if (isHorizontal) { |
238 | width = Qt.binding(function() { return height * artShape.aspect }); |
239 | - height = Qt.binding(function() { return header.height }); |
240 | + height = Qt.binding(function() { return root.headerHeight }); |
241 | } else { |
242 | width = Qt.binding(function() { return root.width }); |
243 | height = Qt.binding(function() { return width / artShape.aspect }); |
244 | @@ -120,53 +126,58 @@ |
245 | } |
246 | } |
247 | |
248 | - ShaderEffect { |
249 | - id: overlay |
250 | + Loader { |
251 | + id: overlayLoader |
252 | anchors { |
253 | left: artShape.left |
254 | right: artShape.right |
255 | bottom: artShape.bottom |
256 | } |
257 | - |
258 | - height: header.height |
259 | - opacity: header.opacity * 0.6 |
260 | - visible: template && template["overlay"] && artShape.visible && artShape.image.status === Image.Ready || false |
261 | - |
262 | - property var source: ShaderEffectSource { |
263 | - id: shaderSource |
264 | - sourceItem: artShape |
265 | - onVisibleChanged: if (visible) scheduleUpdate() |
266 | - live: false |
267 | - sourceRect: Qt.rect(0, artShape.height - overlay.height, artShape.width, overlay.height) |
268 | + active: template && template["overlay"] && artShape.visible && artShape.image.status === Image.Ready || false |
269 | + |
270 | + sourceComponent: ShaderEffect { |
271 | + id: overlay |
272 | + |
273 | + height: headerLoader.item ? headerLoader.item.height : 0 |
274 | + opacity: headerLoader.item ? headerLoader.item.opacity * 0.6 : 0 |
275 | + |
276 | + property var source: ShaderEffectSource { |
277 | + id: shaderSource |
278 | + sourceItem: artShape |
279 | + onVisibleChanged: if (visible) scheduleUpdate() |
280 | + live: false |
281 | + sourceRect: Qt.rect(0, artShape.height - overlay.height, artShape.width, overlay.height) |
282 | + } |
283 | + |
284 | + vertexShader: " |
285 | + uniform highp mat4 qt_Matrix; |
286 | + attribute highp vec4 qt_Vertex; |
287 | + attribute highp vec2 qt_MultiTexCoord0; |
288 | + varying highp vec2 coord; |
289 | + void main() { |
290 | + coord = qt_MultiTexCoord0; |
291 | + gl_Position = qt_Matrix * qt_Vertex; |
292 | + }" |
293 | + |
294 | + fragmentShader: " |
295 | + varying highp vec2 coord; |
296 | + uniform sampler2D source; |
297 | + uniform lowp float qt_Opacity; |
298 | + void main() { |
299 | + lowp vec4 tex = texture2D(source, coord); |
300 | + gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity; |
301 | + }" |
302 | } |
303 | - |
304 | - vertexShader: " |
305 | - uniform highp mat4 qt_Matrix; |
306 | - attribute highp vec4 qt_Vertex; |
307 | - attribute highp vec2 qt_MultiTexCoord0; |
308 | - varying highp vec2 coord; |
309 | - void main() { |
310 | - coord = qt_MultiTexCoord0; |
311 | - gl_Position = qt_Matrix * qt_Vertex; |
312 | - }" |
313 | - |
314 | - fragmentShader: " |
315 | - varying highp vec2 coord; |
316 | - uniform sampler2D source; |
317 | - uniform lowp float qt_Opacity; |
318 | - void main() { |
319 | - lowp vec4 tex = texture2D(source, coord); |
320 | - gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity; |
321 | - }" |
322 | } |
323 | |
324 | - CardHeader { |
325 | - id: header |
326 | - objectName: "cardHeader" |
327 | + Loader { |
328 | + id: headerLoader |
329 | + objectName: "cardHeaderLoader" |
330 | + |
331 | anchors { |
332 | top: { |
333 | if (template) { |
334 | - if (template["overlay"]) return overlay.top; |
335 | + if (template["overlay"]) return overlayLoader.top; |
336 | if (template["card-layout"] === "horizontal") return artShape.top; |
337 | } |
338 | return artShape.bottom; |
339 | @@ -179,29 +190,38 @@ |
340 | } |
341 | right: parent.right |
342 | } |
343 | - |
344 | - mascot: cardData && cardData["mascot"] || "" |
345 | - title: cardData && cardData["title"] || "" |
346 | - subtitle: cardData && cardData["subtitle"] || "" |
347 | - |
348 | - titleWeight: components && components["subtitle"] ? Font.DemiBold : Font.Normal |
349 | - |
350 | - opacity: showHeader ? 1 : 0 |
351 | - inOverlay: root.template && root.template["overlay"] === true |
352 | - fontColor: inOverlay ? "white" : summary.color |
353 | - useMascotShape: !background.visible && !inOverlay |
354 | - |
355 | - Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } } |
356 | + active: cardData && cardData["title"] || cardData && cardData["mascot"] || false |
357 | + |
358 | + sourceComponent: CardHeader { |
359 | + id: header |
360 | + objectName: "cardHeader" |
361 | + |
362 | + mascot: cardData && cardData["mascot"] || "" |
363 | + title: root.title |
364 | + subtitle: cardData && cardData["subtitle"] || "" |
365 | + |
366 | + titleWeight: components && components["subtitle"] ? Font.DemiBold : Font.Normal |
367 | + |
368 | + opacity: showHeader ? 1 : 0 |
369 | + inOverlay: root.template && root.template["overlay"] === true |
370 | + fontColor: inOverlay ? "white" : summary.color |
371 | + useMascotShape: !backgroundLoader.active && !inOverlay |
372 | + headerAlignment: root.headerAlignment |
373 | + height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight |
374 | + fontScale: root.fontScale |
375 | + |
376 | + Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } } |
377 | + } |
378 | } |
379 | |
380 | Label { |
381 | id: summary |
382 | objectName: "summaryLabel" |
383 | anchors { |
384 | - top: header.visible ? header.bottom : artShape.bottom |
385 | + top: headerLoader.active ? headerLoader.bottom : artShape.bottom |
386 | left: parent.left |
387 | right: parent.right |
388 | - margins: background.visible ? units.gu(1) : 0 |
389 | + margins: backgroundLoader.active ? units.gu(1) : 0 |
390 | topMargin: 0 |
391 | } |
392 | wrapMode: Text.Wrap |
393 | @@ -211,6 +231,6 @@ |
394 | height: text ? implicitHeight : 0 |
395 | fontSize: "small" |
396 | // TODO karni: Change "grey" to Ubuntu.Components.Palette color once updated. |
397 | - color: background.visible && background.luminance < 0.7 ? "white" : "grey" |
398 | + color: backgroundLoader.active && backgroundLoader.item.luminance < 0.7 ? "white" : "grey" |
399 | } |
400 | } |
401 | |
402 | === modified file 'qml/Dash/CardCarousel.qml' |
403 | --- qml/Dash/CardCarousel.qml 2014-03-17 10:38:03 +0000 |
404 | +++ qml/Dash/CardCarousel.qml 2014-04-16 15:47:25 +0000 |
405 | @@ -52,7 +52,7 @@ |
406 | itemComponent: Card { |
407 | id: card |
408 | objectName: "carouselDelegate" + index |
409 | - headerHeight: carousel.headerHeight |
410 | + fixedHeaderHeight: carousel.headerHeight |
411 | cardData: model |
412 | template: cardTool.template |
413 | components: cardTool.components |
414 | |
415 | === modified file 'qml/Dash/CardFilterGrid.qml' |
416 | --- qml/Dash/CardFilterGrid.qml 2014-04-08 13:32:59 +0000 |
417 | +++ qml/Dash/CardFilterGrid.qml 2014-04-16 15:47:25 +0000 |
418 | @@ -29,8 +29,8 @@ |
419 | currentItem: filterGrid.currentItem |
420 | height: filterGrid.height |
421 | |
422 | - function startFilterAnimation(filter) { |
423 | - filterGrid.startFilterAnimation(filter) |
424 | + function setFilter(filter, animate) { |
425 | + filterGrid.setFilter(filter, animate) |
426 | } |
427 | |
428 | FilterGrid { |
429 | @@ -41,18 +41,18 @@ |
430 | delegateHeight: cardTool.cardHeight |
431 | verticalSpacing: genericFilterGrid.verticalSpacing |
432 | model: genericFilterGrid.model |
433 | - filter: genericFilterGrid.filter |
434 | collapsedRowCount: Math.min(2, cardTool && cardTool.template && cardTool.template["collapsed-rows"] || 2) |
435 | delegateCreationBegin: genericFilterGrid.delegateCreationBegin |
436 | delegateCreationEnd: genericFilterGrid.delegateCreationEnd |
437 | - delegate: Item { |
438 | + delegate: Loader { |
439 | + asynchronous: true |
440 | width: filterGrid.cellWidth |
441 | height: filterGrid.cellHeight |
442 | Card { |
443 | id: card |
444 | width: cardTool.cardWidth |
445 | height: cardTool.cardHeight |
446 | - headerHeight: cardTool.headerHeight |
447 | + fixedHeaderHeight: cardTool.headerHeight |
448 | anchors.horizontalCenter: parent.horizontalCenter |
449 | objectName: "delegate" + index |
450 | cardData: model |
451 | @@ -65,10 +65,5 @@ |
452 | onPressAndHold: genericFilterGrid.pressAndHold(index, card.y) |
453 | } |
454 | } |
455 | - |
456 | - onFilterChanged: { |
457 | - genericFilterGrid.filter = filter |
458 | - filter = Qt.binding(function() { return genericFilterGrid.filter }) |
459 | - } |
460 | } |
461 | } |
462 | |
463 | === modified file 'qml/Dash/CardHeader.qml' |
464 | --- qml/Dash/CardHeader.qml 2014-03-17 11:42:38 +0000 |
465 | +++ qml/Dash/CardHeader.qml 2014-04-16 15:47:25 +0000 |
466 | @@ -19,9 +19,9 @@ |
467 | |
468 | Item { |
469 | id: root |
470 | - property alias mascot: mascotImage.source |
471 | + property url mascot: "" |
472 | property alias title: titleLabel.text |
473 | - property alias subtitle: subtitleLabel.text |
474 | + property var subtitle |
475 | |
476 | property alias titleWeight: titleLabel.font.weight |
477 | property alias titleSize: titleLabel.fontSize |
478 | @@ -35,8 +35,8 @@ |
479 | property bool useMascotShape: true |
480 | property color fontColor: Theme.palette.selected.backgroundText |
481 | |
482 | - visible: mascotImage.status === Image.Ready || title |
483 | - height: row.height > 0 ? row.height + row.margins * 2 : 0 |
484 | + visible: mascot != "" || title |
485 | + implicitHeight: row.height > 0 ? row.height + row.margins * 2 : 0 |
486 | |
487 | Row { |
488 | id: row |
489 | @@ -44,7 +44,7 @@ |
490 | |
491 | property real margins: units.gu(1) |
492 | |
493 | - spacing: mascotShape.visible || mascotImage.visible || inOverlay ? margins : 0 |
494 | + spacing: mascotShapeLoader.active || mascotImageLoader.active || inOverlay ? margins : 0 |
495 | anchors { |
496 | top: parent.top; left: parent.left; right: parent.right |
497 | margins: margins |
498 | @@ -52,33 +52,40 @@ |
499 | rightMargin: spacing |
500 | } |
501 | |
502 | - UbuntuShape { |
503 | - id: mascotShape |
504 | - objectName: "mascotShape" |
505 | + Loader { |
506 | + id: mascotShapeLoader |
507 | + objectName: "mascotShapeLoader" |
508 | |
509 | + active: useMascotShape && mascotImageLoader.item && mascotImageLoader.item.status === Image.Ready |
510 | + visible: active |
511 | + anchors.verticalCenter: parent.verticalCenter |
512 | // TODO karni: Icon aspect-ratio is 8:7.5. Revisit these values to avoid fraction of pixels. |
513 | width: units.gu(6) |
514 | height: units.gu(5.625) |
515 | - anchors.verticalCenter: parent.verticalCenter |
516 | - visible: useMascotShape && image && image.status === Image.Ready |
517 | readonly property int maxSize: Math.max(width, height) * 4 |
518 | |
519 | - image: useMascotShape ? mascotImage : null |
520 | + sourceComponent: UbuntuShape { |
521 | + image: mascotImageLoader.item |
522 | + } |
523 | } |
524 | |
525 | - Image { |
526 | - id: mascotImage |
527 | - objectName: "mascotImage" |
528 | - |
529 | - width: source ? mascotShape.width : 0 |
530 | - height: mascotShape.height |
531 | + Loader { |
532 | + id: mascotImageLoader |
533 | + active: root.mascot != "" |
534 | + visible: active && !useMascotShape && item.status === Image.Ready |
535 | anchors.verticalCenter: parent.verticalCenter |
536 | - visible: !useMascotShape && status === Image.Ready |
537 | - |
538 | - sourceSize { width: mascotShape.maxSize; height: mascotShape.maxSize } |
539 | - fillMode: Image.PreserveAspectCrop |
540 | - horizontalAlignment: Image.AlignHCenter |
541 | - verticalAlignment: Image.AlignVCenter |
542 | + sourceComponent: Image { |
543 | + objectName: "mascotImage" |
544 | + |
545 | + source: root.mascot |
546 | + width: source ? mascotShapeLoader.width : 0 |
547 | + height: mascotShapeLoader.height |
548 | + |
549 | + sourceSize { width: mascotShapeLoader.maxSize; height: mascotShapeLoader.maxSize } |
550 | + fillMode: Image.PreserveAspectCrop |
551 | + horizontalAlignment: Image.AlignHCenter |
552 | + verticalAlignment: Image.AlignVCenter |
553 | + } |
554 | } |
555 | |
556 | Column { |
557 | @@ -100,16 +107,19 @@ |
558 | color: fontColor |
559 | } |
560 | |
561 | - Label { |
562 | - id: subtitleLabel |
563 | - objectName: "subtitleLabel" |
564 | + Loader { |
565 | + active: titleLabel.text && root.subtitle |
566 | anchors { left: parent.left; right: parent.right } |
567 | - elide: Text.ElideRight |
568 | - fontSize: "small" |
569 | - font.weight: Font.Light |
570 | - visible: titleLabel.text && text |
571 | - font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale) |
572 | - color: fontColor |
573 | + sourceComponent: Label { |
574 | + id: subtitleLabel |
575 | + objectName: "subtitleLabel" |
576 | + elide: Text.ElideRight |
577 | + fontSize: "small" |
578 | + font.weight: Font.Light |
579 | + font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale) |
580 | + color: fontColor |
581 | + text: root.subtitle |
582 | + } |
583 | } |
584 | } |
585 | } |
586 | |
587 | === modified file 'qml/Dash/DashRenderer.qml' |
588 | --- qml/Dash/DashRenderer.qml 2014-04-08 13:32:59 +0000 |
589 | +++ qml/Dash/DashRenderer.qml 2014-04-16 15:47:25 +0000 |
590 | @@ -20,9 +20,6 @@ |
591 | // Can the item be expanded? |
592 | property bool expandable: false |
593 | |
594 | - // In case it can be expanded, should we filter it |
595 | - property bool filter: true |
596 | - |
597 | property int collapsedHeight: height |
598 | |
599 | property int margins: 0 |
600 | @@ -58,6 +55,6 @@ |
601 | /// @param itemY is y of the held delegate |
602 | signal pressAndHold(int index, real itemY) |
603 | |
604 | - function startFilterAnimation(filter) { |
605 | + function setFilter(filter, animate) { |
606 | } |
607 | } |
608 | |
609 | === modified file 'qml/Dash/GenericScopeView.qml' |
610 | --- qml/Dash/GenericScopeView.qml 2014-04-08 13:32:59 +0000 |
611 | +++ qml/Dash/GenericScopeView.qml 2014-04-16 15:47:25 +0000 |
612 | @@ -121,7 +121,7 @@ |
613 | showDivider: false |
614 | |
615 | readonly property bool expandable: rendererLoader.item ? rendererLoader.item.expandable : false |
616 | - readonly property bool filtered: rendererLoader.item ? rendererLoader.item.filter : true |
617 | + readonly property bool filtered: categoryView.expandedCategoryId != categoryId |
618 | readonly property string category: categoryId |
619 | readonly property var item: rendererLoader.item |
620 | |
621 | @@ -167,9 +167,7 @@ |
622 | item.objectName = Qt.binding(function() { return categoryId }) |
623 | if (item.expandable) { |
624 | var shouldFilter = categoryId != categoryView.expandedCategoryId; |
625 | - if (shouldFilter != item.filter) { |
626 | - item.filter = shouldFilter; |
627 | - } |
628 | + item.setFilter(shouldFilter, false /*animate*/); |
629 | } |
630 | updateDelegateCreationRange(); |
631 | item.cardTool = cardTool; |
632 | @@ -218,11 +216,8 @@ |
633 | var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height; |
634 | var growingVisible = !shouldFilter && y + height < categoryView.height; |
635 | if (!previewListView.open || !shouldFilter) { |
636 | - if (shrinkingVisible || growingVisible) { |
637 | - item.startFilterAnimation(shouldFilter) |
638 | - } else { |
639 | - item.filter = shouldFilter; |
640 | - } |
641 | + var animate = shrinkingVisible || growingVisible; |
642 | + item.setFilter(shouldFilter, animate) |
643 | if (!shouldFilter && !previewListView.open) { |
644 | categoryView.maximizeVisibleArea(index, item.uncollapsedHeight); |
645 | } |
646 | @@ -241,7 +236,7 @@ |
647 | // to the stable position and delete/create items without any reason |
648 | if (categoryView.contentY < categoryView.originY) { |
649 | return; |
650 | - } else if (categoryView.contentY + categoryView.height > categoryView.contentHeight) { |
651 | + } else if (categoryView.contentHeight > categoryView.height && categoryView.contentY + categoryView.height > categoryView.contentHeight) { |
652 | return; |
653 | } |
654 | |
655 | |
656 | === modified file 'tests/qmltests/CMakeLists.txt' |
657 | --- tests/qmltests/CMakeLists.txt 2014-04-03 10:38:06 +0000 |
658 | +++ tests/qmltests/CMakeLists.txt 2014-04-16 15:47:25 +0000 |
659 | @@ -36,6 +36,7 @@ |
660 | add_qml_test(Dash Dash IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}) |
661 | add_qml_test(Dash DashContent IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}) |
662 | add_qml_test(Dash Card) |
663 | +add_qml_benchmark(Dash CardBenchmark 1000) |
664 | add_qml_test(Dash CardHeader) |
665 | add_qml_test(Dash CardTool) |
666 | add_qml_test(Dash GenericScopeView IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}) |
667 | |
668 | === modified file 'tests/qmltests/Components/tst_FilterGrid.qml' |
669 | --- tests/qmltests/Components/tst_FilterGrid.qml 2014-01-29 13:37:23 +0000 |
670 | +++ tests/qmltests/Components/tst_FilterGrid.qml 2014-04-16 15:47:25 +0000 |
671 | @@ -50,6 +50,7 @@ |
672 | CheckBox { |
673 | id: filterCheckBox |
674 | checked: true |
675 | + onCheckedChanged: filterGrid.setFilter(checked, false /*animate*/) |
676 | } |
677 | } |
678 | } |
679 | @@ -99,7 +100,6 @@ |
680 | maximumNumberOfColumns: 3 |
681 | collapsedRowCount: |
682 | collapsedRowCountSelector.values[collapsedRowCountSelector.selectedIndex] |
683 | - filter: filterCheckBox.checked |
684 | minimumHorizontalSpacing: units.gu(1) |
685 | delegateWidth: units.gu(6) |
686 | delegateHeight: units.gu(6) |
687 | |
688 | === modified file 'tests/qmltests/Dash/tst_Card.qml' |
689 | --- tests/qmltests/Dash/tst_Card.qml 2014-03-12 10:55:37 +0000 |
690 | +++ tests/qmltests/Dash/tst_Card.qml 2014-04-16 15:47:25 +0000 |
691 | @@ -182,11 +182,13 @@ |
692 | when: windowShown |
693 | |
694 | property Item header: findChild(card, "cardHeader") |
695 | + property Item headerLoader: findChild(card, "cardHeaderLoader") |
696 | property Item title: findChild(header, "titleLabel") |
697 | property Item art: findChild(card, "artShape") |
698 | property Item artImage: findChild(card, "artImage") |
699 | property Item summary: findChild(card, "summaryLabel") |
700 | property Item background: findChild(card, "background") |
701 | + property Item backgroundLoader: findChild(card, "backgroundLoader") |
702 | property Item backgroundImage: findChild(card, "backgroundImage") |
703 | |
704 | function initTestCase() { |
705 | @@ -244,8 +246,8 @@ |
706 | { tag: "Wide", width: units.gu(18.5), aspect: 0.5, index: 0 }, |
707 | { tag: "Horizontal", width: units.gu(38), index: 5 }, |
708 | // Make sure card ends with header when there's no summary |
709 | - { tag: "NoSummary", height: function() { return header.y + header.height }, index: 6 }, |
710 | - { tag: "HorizontalNoSummary", height: function() { return header.height }, card_layout: "horizontal", index: 6 }, |
711 | + { tag: "NoSummary", height: function() { return headerLoader.y + headerLoader.height }, index: 6 }, |
712 | + { tag: "HorizontalNoSummary", height: function() { return headerLoader.height }, card_layout: "horizontal", index: 6 }, |
713 | ] |
714 | } |
715 | |
716 | @@ -288,7 +290,7 @@ |
717 | { tag: "Fit", height: units.gu(38), size: "large", width: units.gu(19), index: 4 }, |
718 | { tag: "VerticalWidth", width: function() { return header.width }, index: 0 }, |
719 | { tag: "HorizontalHeight", height: function() { return header.height }, index: 5 }, |
720 | - { tag: "HorizontalWidth", width: function() { return header.x }, index: 5 }, |
721 | + { tag: "HorizontalWidth", width: function() { return headerLoader.x }, index: 5 }, |
722 | ] |
723 | } |
724 | |
725 | @@ -332,7 +334,7 @@ |
726 | function test_art_layout(data) { |
727 | selector.selectedIndex = data.index; |
728 | |
729 | - tryCompare(testCase.header, "x", data.left()); |
730 | + tryCompare(testCase.headerLoader, "x", data.left()); |
731 | } |
732 | |
733 | function test_header_layout_data() { |
734 | @@ -349,13 +351,13 @@ |
735 | function test_header_layout(data) { |
736 | selector.selectedIndex = data.index; |
737 | |
738 | - tryCompareFunction(function() { return testCase.header.y === data.top() }, true); |
739 | - tryCompareFunction(function() { return testCase.header.x === data.left() }, true); |
740 | + tryCompareFunction(function() { return testCase.headerLoader.y === data.top() }, true); |
741 | + tryCompareFunction(function() { return testCase.headerLoader.x === data.left() }, true); |
742 | } |
743 | |
744 | function test_summary_layout_data() { |
745 | return [ |
746 | - { tag: "With header", top: function() { return header.y + header.height }, index: 0 }, |
747 | + { tag: "With header", top: function() { return headerLoader.y + headerLoader.height }, index: 0 }, |
748 | { tag: "Without header", top: function() { return art.y + art.height }, index: 7 }, |
749 | ] |
750 | } |
751 | @@ -400,7 +402,7 @@ |
752 | |
753 | waitForRendering(card); |
754 | |
755 | - tryCompare(background, "visible", data.visible); |
756 | + tryCompare(backgroundLoader, "active", data.visible); |
757 | |
758 | if (data.hasOwnProperty("color")) { |
759 | tryCompare(background, "color", data.color); |
760 | @@ -467,13 +469,13 @@ |
761 | function test_mascotShape(data) { |
762 | selector.selectedIndex = data.index; |
763 | |
764 | - var shape = findChild(card, "mascotShape"); |
765 | + var shape = findChild(card, "mascotShapeLoader"); |
766 | var image = findChild(card, "mascotImage"); |
767 | |
768 | verify(shape, "Could not find shape."); |
769 | verify(image, "Could not find image."); |
770 | |
771 | - tryCompare(shape, "visible", data.shape); |
772 | + tryCompare(shape, "active", data.shape); |
773 | tryCompare(image, "visible", !data.shape); |
774 | } |
775 | } |
776 | |
777 | === added file 'tests/qmltests/Dash/tst_CardBenchmark.qml' |
778 | --- tests/qmltests/Dash/tst_CardBenchmark.qml 1970-01-01 00:00:00 +0000 |
779 | +++ tests/qmltests/Dash/tst_CardBenchmark.qml 2014-04-16 15:47:25 +0000 |
780 | @@ -0,0 +1,94 @@ |
781 | +/* |
782 | + * Copyright (C) 2014 Canonical, Ltd. |
783 | + * |
784 | + * This program is free software; you can redistribute it and/or modify |
785 | + * it under the terms of the GNU General Public License as published by |
786 | + * the Free Software Foundation; version 3. |
787 | + * |
788 | + * This program is distributed in the hope that it will be useful, |
789 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
790 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
791 | + * GNU General Public License for more details. |
792 | + * |
793 | + * You should have received a copy of the GNU General Public License |
794 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
795 | + */ |
796 | + |
797 | +import QtQuick 2.0 |
798 | +import QtTest 1.0 |
799 | +import Ubuntu.Components 0.1 |
800 | +import Unity.Test 0.1 as UT |
801 | +import "../../../qml/Dash" |
802 | +import "CardHelpers.js" as Helpers |
803 | + |
804 | +Rectangle { |
805 | + id: root |
806 | + width: units.gu(80) |
807 | + height: units.gu(72) |
808 | + color: "#88FFFFFF" |
809 | + |
810 | + property string cardData: ' |
811 | + { |
812 | + "art": "../../tests/qmltests/Dash/artwork/music-player-design.png", |
813 | + "mascot": "../../tests/qmltests/Dash/artwork/avatar.png", |
814 | + "title": "foo", |
815 | + "subtitle": "bar", |
816 | + "summary": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." |
817 | + }' |
818 | + |
819 | + property string currentModel: '{}' |
820 | + property string cardTitleArtSubtitleMascotSummaryModel: '{ "components": { "title": "title", "art": "art", "subtitle": "subtitle", "mascot": "mascot", "summary": "summary" } }' |
821 | + property string cardTitleArtSubtitleMascotModel: '{ "components": { "title": "title", "art": "art", "subtitle": "subtitle", "mascot": "mascot" } }' |
822 | + property string cardTitleArtSubtitleModel: '{ "components": { "title": "title", "art": "art", "subtitle": "subtitle" } }' |
823 | + property string cardTitleArtModel: '{ "components": { "title": "title", "art": "art" } }' |
824 | + property string cardArtModel: '{ "components": { "art": "art" } }' |
825 | + property string cardTitleModel: '{ "components": { "title": "title" } }' |
826 | + |
827 | + CardTool { |
828 | + id: cardTool |
829 | + template: Helpers.update(JSON.parse(Helpers.defaultLayout), JSON.parse(currentModel))['template']; |
830 | + components: Helpers.update(JSON.parse(Helpers.defaultLayout), JSON.parse(currentModel))['components']; |
831 | + viewWidth: units.gu(48) |
832 | + } |
833 | + |
834 | + Repeater { |
835 | + id: cardRepeater |
836 | + model: 0 |
837 | + Card { |
838 | + width: cardTool.cardWidth || implicitWidth |
839 | + height: cardTool.cardHeight || implicitHeight |
840 | + |
841 | + template: cardTool.template |
842 | + components: cardTool.components |
843 | + cardData: Helpers.mapData(root.cardData, components) |
844 | + } |
845 | + } |
846 | + |
847 | + UT.UnityTestCase { |
848 | + id: testCase |
849 | + name: "CardBenchmark" |
850 | + |
851 | + when: windowShown |
852 | + |
853 | + function init() { |
854 | + wait(1); |
855 | + } |
856 | + |
857 | + function benchmark_time_data() { |
858 | + return [ |
859 | + { tag: "cardTitleArtSubtitleMascotSummaryModel", model: cardTitleArtSubtitleMascotSummaryModel }, |
860 | + { tag: "cardTitleArtSubtitleMascotModel", model: cardTitleArtSubtitleMascotModel }, |
861 | + { tag: "cardTitleArtSubtitleModel", model: cardTitleArtSubtitleModel }, |
862 | + { tag: "cardTitleArtModel", model: cardTitleArtModel }, |
863 | + { tag: "cardArtModel", model: cardArtModel }, |
864 | + { tag: "cardTitleModel", model: cardTitleModel }, |
865 | + ]; |
866 | + } |
867 | + |
868 | + function benchmark_time(data) { |
869 | + currentModel = data.model; |
870 | + cardRepeater.model = 1; |
871 | + cardRepeater.model = 0; |
872 | + } |
873 | + } |
874 | +} |
875 | |
876 | === modified file 'tests/qmltests/Dash/tst_CardHeader.qml' |
877 | --- tests/qmltests/Dash/tst_CardHeader.qml 2014-03-17 11:44:05 +0000 |
878 | +++ tests/qmltests/Dash/tst_CardHeader.qml 2014-04-16 15:47:25 +0000 |
879 | @@ -43,7 +43,7 @@ |
880 | |
881 | when: windowShown |
882 | |
883 | - property Item mascot: findChild(cardHeader, "mascotShape") |
884 | + property Item mascot: findChild(cardHeader, "mascotShapeLoader") |
885 | property Item titleLabel: findChild(cardHeader, "titleLabel") |
886 | property Item subtitleLabel: findChild(cardHeader, "subtitleLabel") |
887 | property Item oldPriceLabel: findChild(cardHeader, "oldPriceLabel") |