Merge lp:~unity-team/unity/8.carousel-listview into lp:unity/8.0
- 8.carousel-listview
- Merge into 8.0
Status: | Merged |
---|---|
Approved by: | Albert Astals Cid |
Approved revision: | no longer in the source branch. |
Merged at revision: | 9 |
Proposed branch: | lp:~unity-team/unity/8.carousel-listview |
Merge into: | lp:unity/8.0 |
Diff against target: |
603 lines (+244/-173) 3 files modified
Components/Carousel.qml (+159/-128) Components/carousel.js (+47/-11) tests/qmltests/Components/tst_Carousel.qml (+38/-34) |
To merge this branch: | bzr merge lp:~unity-team/unity/8.carousel-listview |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Albert Astals Cid (community) | Approve | ||
Andrea Cimitan | Pending | ||
Review via email: mp+167713@code.launchpad.net |
This proposal supersedes a proposal from 2013-05-22.
Commit message
Bring back ListView'ed Carousel now that Qt is fixed.
Description of the change
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
Andrea Cimitan (cimi) wrote : Posted in a previous version of this proposal | # |
Since we are here, shall we add some extra tests? (in case, push as unity-team so I can contribute)
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal | # |
> Since we are here, shall we add some extra tests? (in case, push as unity-team so I can contribute)
Done. Test away :)
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:682
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2
http://
Executed test runs:
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Albert Astals Cid (aacid) wrote : | # |
Exactly the same code i have in the branch i had for testing the listview-carousel crashes
PS Jenkins bot (ps-jenkins) : | # |
Preview Diff
1 | === modified file 'Components/Carousel.qml' | |||
2 | --- Components/Carousel.qml 2013-06-05 22:03:08 +0000 | |||
3 | +++ Components/Carousel.qml 2013-06-06 08:03:28 +0000 | |||
4 | @@ -18,26 +18,50 @@ | |||
5 | 18 | import Ubuntu.Components 0.1 | 18 | import Ubuntu.Components 0.1 |
6 | 19 | import "carousel.js" as CarouselJS | 19 | import "carousel.js" as CarouselJS |
7 | 20 | 20 | ||
8 | 21 | /*! The Carousel component presents the items of a model in a carousel view. It's similar to a | ||
9 | 22 | cover flow. But it stops at it's boundaries (therefore no PathView is used). | ||
10 | 23 | */ | ||
11 | 21 | Item { | 24 | Item { |
12 | 22 | id: carousel | 25 | id: carousel |
13 | 23 | 26 | ||
14 | 27 | /// The component to be used as delegate. This component has to be derived from BaseCarouselDelegate | ||
15 | 24 | property Component itemComponent | 28 | property Component itemComponent |
21 | 25 | property var model | 29 | /// Model for the Carousel, which has to be a model usable by a ListView |
22 | 26 | property alias minimumTileWidth: flickable.minimumTileWidth | 30 | property alias model: listView.model |
23 | 27 | property alias pathItemCount: flickable.pathItemCount | 31 | /// A minimal width of a tile can be set here. Per default a best fit will be calculated |
24 | 28 | property alias tileAspectRatio: flickable.tileAspectRatio | 32 | property alias minimumTileWidth: listView.minimumTileWidth |
25 | 29 | property int cacheBuffer: 0 | 33 | /// Sets the number of tiles that are visible |
26 | 34 | property alias pathItemCount: listView.pathItemCount | ||
27 | 35 | /// Aspect ratio of the tiles width/height | ||
28 | 36 | property alias tileAspectRatio: listView.tileAspectRatio | ||
29 | 37 | /// Used to cache some delegates for performance reasons. See the ListView documentation for details | ||
30 | 38 | property alias cacheBuffer: listView.cacheBuffer | ||
31 | 39 | /// Width of the "draw buffer" in pixel. The drawBuffer is an additional area at start/end where | ||
32 | 40 | /// items drawn, even if it is not in the visible area. | ||
33 | 41 | /// cacheBuffer controls only the to retain delegates outside the visible area (and is used on top of the drawBuffer) | ||
34 | 42 | /// see https://bugreports.qt-project.org/browse/QTBUG-29173 | ||
35 | 43 | property int drawBuffer: width / pathItemCount // an "ok" value - but values used from the listView cause loops | ||
36 | 44 | /// The selected item can be shown in a different size controlled by selectedItemScaleFactor | ||
37 | 30 | property real selectedItemScaleFactor: 1.1 | 45 | property real selectedItemScaleFactor: 1.1 |
38 | 31 | 46 | ||
39 | 47 | /// Emitted when the user clicked on an item | ||
40 | 48 | /// @param index is the index of the clicked item | ||
41 | 49 | /// @param delegateItem is the clicked component/delegate itself | ||
42 | 50 | /// @param itemY is y of the clicked delegate | ||
43 | 32 | signal clicked(int index, var delegateItem, real itemY) | 51 | signal clicked(int index, var delegateItem, real itemY) |
44 | 33 | 52 | ||
52 | 34 | implicitHeight: flickable.tileHeight * selectedItemScaleFactor | 53 | implicitHeight: listView.tileHeight * selectedItemScaleFactor |
53 | 35 | 54 | ||
54 | 36 | /* TODO: evaluate if the component could be more efficient with a ListView, | 55 | /* Basic idea behind the carousel effect is to move the items of the delegates (compacting /stuffing them). |
55 | 37 | using this technique https://bugreports.qt-project.org/browse/QTBUG-29173 */ | 56 | One drawback is, that more delegates have to be drawn than usually. As some items are moved from the |
56 | 38 | 57 | invisible to the visible area. Setting the cacheBuffer does not fix this. | |
57 | 39 | Flickable { | 58 | See https://bugreports.qt-project.org/browse/QTBUG-29173 |
58 | 40 | id: flickable | 59 | Therefore the ListView has negative left and right anchors margins, and in addition a header |
59 | 60 | and footer item to compensate that. | ||
60 | 61 | |||
61 | 62 | The scaling of the items is controlled by the variable continuousIndex, described below. */ | ||
62 | 63 | ListView { | ||
63 | 64 | id: listView | ||
64 | 41 | 65 | ||
65 | 42 | property real minimumTileWidth: 0 | 66 | property real minimumTileWidth: 0 |
66 | 43 | property real newContentX: -1 | 67 | property real newContentX: -1 |
67 | @@ -57,20 +81,27 @@ | |||
68 | 57 | - 'gapToEndPhase' gap in pixels between middle and end phase | 81 | - 'gapToEndPhase' gap in pixels between middle and end phase |
69 | 58 | - 'kGapEnd' constant used to calculate 'continuousIndex' in end phase | 82 | - 'kGapEnd' constant used to calculate 'continuousIndex' in end phase |
70 | 59 | - 'kMiddleIndex' constant used to calculate 'continuousIndex' in middle phase | 83 | - 'kMiddleIndex' constant used to calculate 'continuousIndex' in middle phase |
72 | 60 | - 'kXBeginningEnd' constant used to calculate 'continuousIndex' in beginning and end phase. */ | 84 | - 'kXBeginningEnd' constant used to calculate 'continuousIndex' in beginning and end phase |
73 | 85 | - 'realContentWidth' the width of all the delegates only (without header/footer) | ||
74 | 86 | - 'realContentX' the 'contentX' of the listview ignoring the 'drawBuffer' | ||
75 | 87 | - 'realWidth' the 'width' of the listview, as it is used as component. */ | ||
76 | 61 | 88 | ||
79 | 62 | readonly property real gapToMiddlePhase: Math.min(width / 2 - tileWidth / 2, (contentWidth - width) / 2) | 89 | readonly property real gapToMiddlePhase: Math.min(realWidth / 2 - tileWidth / 2, (realContentWidth - realWidth) / 2) |
80 | 63 | readonly property real gapToEndPhase: contentWidth - width - gapToMiddlePhase | 90 | readonly property real gapToEndPhase: realContentWidth - realWidth - gapToMiddlePhase |
81 | 64 | readonly property real kGapEnd: kMiddleIndex * (1 - gapToEndPhase / gapToMiddlePhase) | 91 | readonly property real kGapEnd: kMiddleIndex * (1 - gapToEndPhase / gapToMiddlePhase) |
83 | 65 | readonly property real kMiddleIndex: (width / 2) / tileWidth - 0.5 | 92 | readonly property real kMiddleIndex: (realWidth / 2) / tileWidth - 0.5 |
84 | 66 | readonly property real kXBeginningEnd: 1 / tileWidth + kMiddleIndex / gapToMiddlePhase | 93 | readonly property real kXBeginningEnd: 1 / tileWidth + kMiddleIndex / gapToMiddlePhase |
87 | 67 | readonly property real realPathItemCount: Math.min(width / tileWidth, pathItemCount) | 94 | readonly property real maximumItemTranslation: (listView.tileWidth * 3) / listView.scaleFactor |
88 | 68 | readonly property real referenceGapToMiddlePhase: width / 2 - tileWidth / 2 | 95 | readonly property real realContentWidth: contentWidth - 2 * carousel.drawBuffer |
89 | 96 | readonly property real realContentX: contentX + carousel.drawBuffer | ||
90 | 97 | readonly property real realPathItemCount: Math.min(realWidth / tileWidth, pathItemCount) | ||
91 | 98 | readonly property real realWidth: carousel.width | ||
92 | 99 | readonly property real referenceGapToMiddlePhase: realWidth / 2 - tileWidth / 2 | ||
93 | 69 | readonly property real referencePathItemCount: referenceWidth / referenceTileWidth | 100 | readonly property real referencePathItemCount: referenceWidth / referenceTileWidth |
94 | 70 | readonly property real referenceWidth: 848 | 101 | readonly property real referenceWidth: 848 |
95 | 71 | readonly property real referenceTileWidth: 175 | 102 | readonly property real referenceTileWidth: 175 |
96 | 72 | readonly property real scaleFactor: tileWidth / referenceTileWidth | 103 | readonly property real scaleFactor: tileWidth / referenceTileWidth |
98 | 73 | readonly property real tileWidth: Math.max(width / pathItemCount, minimumTileWidth) | 104 | readonly property real tileWidth: Math.max(realWidth / pathItemCount, minimumTileWidth) |
99 | 74 | readonly property real tileHeight: tileWidth / tileAspectRatio | 105 | readonly property real tileHeight: tileWidth / tileAspectRatio |
100 | 75 | readonly property real translationXViewFactor: 0.2 * (referenceGapToMiddlePhase / gapToMiddlePhase) | 106 | readonly property real translationXViewFactor: 0.2 * (referenceGapToMiddlePhase / gapToMiddlePhase) |
101 | 76 | readonly property real verticalMargin: (parent.height - tileHeight) / 2 | 107 | readonly property real verticalMargin: (parent.height - tileHeight) / 2 |
102 | @@ -80,20 +111,36 @@ | |||
103 | 80 | fill: parent | 111 | fill: parent |
104 | 81 | topMargin: verticalMargin | 112 | topMargin: verticalMargin |
105 | 82 | bottomMargin: verticalMargin | 113 | bottomMargin: verticalMargin |
109 | 83 | } | 114 | // extending the "drawing area" |
110 | 84 | contentWidth: view.width | 115 | leftMargin: -carousel.drawBuffer |
111 | 85 | contentHeight: height | 116 | rightMargin: -carousel.drawBuffer |
112 | 117 | } | ||
113 | 118 | |||
114 | 119 | /* The header and footer help to "extend" the area, the listview draws items. | ||
115 | 120 | This together with anchors.leftMargin and anchors.rightMargin. */ | ||
116 | 121 | header: Item { | ||
117 | 122 | width: carousel.drawBuffer | ||
118 | 123 | height: listView.tileHeight | ||
119 | 124 | } | ||
120 | 125 | footer: Item { | ||
121 | 126 | width: carousel.drawBuffer | ||
122 | 127 | height: listView.tileHeight | ||
123 | 128 | } | ||
124 | 129 | |||
125 | 86 | boundsBehavior: Flickable.StopAtBounds | 130 | boundsBehavior: Flickable.StopAtBounds |
128 | 87 | flickDeceleration: Math.max(1500 * Math.pow(width / referenceWidth, 1.5), 1500) // 1500 is platform default | 131 | cacheBuffer: carousel.cacheBuffer |
129 | 88 | maximumFlickVelocity: Math.max(2500 * Math.pow(width / referenceWidth, 1.5), 2500) // 2500 is platform default | 132 | flickDeceleration: Math.max(1500 * Math.pow(realWidth / referenceWidth, 1.5), 1500) // 1500 is platform default |
130 | 133 | maximumFlickVelocity: Math.max(2500 * Math.pow(realWidth / referenceWidth, 1.5), 2500) // 2500 is platform default | ||
131 | 134 | orientation: ListView.Horizontal | ||
132 | 89 | 135 | ||
133 | 90 | function itemClicked(index, delegateItem) { | 136 | function itemClicked(index, delegateItem) { |
134 | 91 | var x = CarouselJS.getXFromContinuousIndex(index, | 137 | var x = CarouselJS.getXFromContinuousIndex(index, |
137 | 92 | width, | 138 | realWidth, |
138 | 93 | contentWidth, | 139 | realContentWidth, |
139 | 94 | tileWidth, | 140 | tileWidth, |
140 | 95 | gapToMiddlePhase, | 141 | gapToMiddlePhase, |
142 | 96 | gapToEndPhase) | 142 | gapToEndPhase, |
143 | 143 | carousel.drawBuffer) | ||
144 | 97 | 144 | ||
145 | 98 | if (Math.abs(x - contentX) < 1) { | 145 | if (Math.abs(x - contentX) < 1) { |
146 | 99 | /* We're clicking the selected item and | 146 | /* We're clicking the selected item and |
147 | @@ -116,22 +163,22 @@ | |||
148 | 116 | newContentX = -1 | 163 | newContentX = -1 |
149 | 117 | } | 164 | } |
150 | 118 | onMovementEnded: { | 165 | onMovementEnded: { |
152 | 119 | if (contentX > 0 && contentX < contentWidth - width) | 166 | if (realContentX > 0 && realContentX < realContentWidth - realWidth) |
153 | 120 | stepAnimation.start() | 167 | stepAnimation.start() |
154 | 121 | } | 168 | } |
155 | 122 | 169 | ||
156 | 123 | SmoothedAnimation { | 170 | SmoothedAnimation { |
157 | 124 | id: stepAnimation | 171 | id: stepAnimation |
158 | 125 | 172 | ||
160 | 126 | target: flickable | 173 | target: listView |
161 | 127 | property: "contentX" | 174 | property: "contentX" |
169 | 128 | from: flickable.contentX | 175 | to: CarouselJS.getXFromContinuousIndex(listView.selectedIndex, |
170 | 129 | to: CarouselJS.getXFromContinuousIndex(view.selectedIndex, | 176 | listView.realWidth, |
171 | 130 | flickable.width, | 177 | listView.realContentWidth, |
172 | 131 | flickable.contentWidth, | 178 | listView.tileWidth, |
173 | 132 | flickable.tileWidth, | 179 | listView.gapToMiddlePhase, |
174 | 133 | flickable.gapToMiddlePhase, | 180 | listView.gapToEndPhase, |
175 | 134 | flickable.gapToEndPhase) | 181 | carousel.drawBuffer) |
176 | 135 | duration: 450 | 182 | duration: 450 |
177 | 136 | velocity: 200 | 183 | velocity: 200 |
178 | 137 | easing.type: Easing.InOutQuad | 184 | easing.type: Easing.InOutQuad |
179 | @@ -141,113 +188,97 @@ | |||
180 | 141 | id: newContentXAnimation | 188 | id: newContentXAnimation |
181 | 142 | 189 | ||
182 | 143 | NumberAnimation { | 190 | NumberAnimation { |
184 | 144 | target: flickable | 191 | target: listView |
185 | 145 | property: "contentX" | 192 | property: "contentX" |
188 | 146 | from: flickable.contentX | 193 | from: listView.contentX |
189 | 147 | to: flickable.newContentX | 194 | to: listView.newContentX |
190 | 148 | duration: 300 | 195 | duration: 300 |
191 | 149 | easing.type: Easing.InOutQuad | 196 | easing.type: Easing.InOutQuad |
192 | 150 | } | 197 | } |
193 | 151 | ScriptAction { | 198 | ScriptAction { |
195 | 152 | script: flickable.newContentX = -1 | 199 | script: listView.newContentX = -1 |
196 | 153 | } | 200 | } |
197 | 154 | } | 201 | } |
198 | 155 | 202 | ||
213 | 156 | Row { | 203 | readonly property int selectedIndex: Math.round(continuousIndex) |
214 | 157 | id: view | 204 | readonly property real continuousIndex: CarouselJS.getContinuousIndex(listView.realContentX, |
215 | 158 | 205 | listView.tileWidth, | |
216 | 159 | readonly property int selectedIndex: Math.round(continuousIndex) | 206 | listView.gapToMiddlePhase, |
217 | 160 | readonly property real continuousIndex: CarouselJS.getContinuousIndex(flickable.contentX, | 207 | listView.gapToEndPhase, |
218 | 161 | flickable.tileWidth, | 208 | listView.kGapEnd, |
219 | 162 | flickable.gapToMiddlePhase, | 209 | listView.kMiddleIndex, |
220 | 163 | flickable.gapToEndPhase, | 210 | listView.kXBeginningEnd) |
221 | 164 | flickable.kGapEnd, | 211 | |
222 | 165 | flickable.kMiddleIndex, | 212 | property real viewTranslation: CarouselJS.getViewTranslation(listView.realContentX, |
223 | 166 | flickable.kXBeginningEnd) | 213 | listView.tileWidth, |
224 | 167 | 214 | listView.gapToMiddlePhase, | |
225 | 168 | height: parent.height | 215 | listView.gapToEndPhase, |
226 | 169 | anchors.verticalCenter: parent.verticalCenter | 216 | listView.translationXViewFactor) |
227 | 217 | |||
228 | 218 | delegate: Loader { | ||
229 | 219 | property bool explicitlyScaled: explicitScaleFactor == carousel.selectedItemScaleFactor | ||
230 | 220 | property real explicitScaleFactor: explicitScale ? carousel.selectedItemScaleFactor : 1.0 | ||
231 | 221 | readonly property bool explicitScale: (!listView.moving || | ||
232 | 222 | listView.realContentX <= 0 || | ||
233 | 223 | listView.realContentX >= listView.realContentWidth - listView.realWidth) && | ||
234 | 224 | listView.newContentX < 0 && | ||
235 | 225 | index === listView.selectedIndex | ||
236 | 226 | readonly property real cachedTiles: listView.realPathItemCount + carousel.drawBuffer / listView.tileWidth | ||
237 | 227 | readonly property real distance: listView.continuousIndex - index | ||
238 | 228 | readonly property real itemTranslationScale: CarouselJS.getItemScale(0.5, | ||
239 | 229 | (index + 0.5), // good approximation of scale while changing selected item | ||
240 | 230 | listView.count, | ||
241 | 231 | listView.visibleTilesScaleFactor) | ||
242 | 232 | readonly property real itemScale: CarouselJS.getItemScale(distance, | ||
243 | 233 | listView.continuousIndex, | ||
244 | 234 | listView.count, | ||
245 | 235 | listView.visibleTilesScaleFactor) | ||
246 | 236 | readonly property real translationX: CarouselJS.getItemTranslation(index, | ||
247 | 237 | listView.selectedIndex, | ||
248 | 238 | distance, | ||
249 | 239 | itemScale, | ||
250 | 240 | itemTranslationScale, | ||
251 | 241 | listView.maximumItemTranslation) | ||
252 | 242 | |||
253 | 243 | width: listView.tileWidth | ||
254 | 244 | height: listView.tileHeight | ||
255 | 245 | scale: itemScale * explicitScaleFactor | ||
256 | 246 | sourceComponent: itemComponent | ||
257 | 247 | z: cachedTiles - Math.abs(index - listView.selectedIndex) | ||
258 | 170 | 248 | ||
259 | 171 | transform: Translate { | 249 | transform: Translate { |
265 | 172 | x: CarouselJS.getViewTranslation(flickable.contentX, | 250 | x: listView.viewTranslation + translationX * listView.scaleFactor |
261 | 173 | flickable.tileWidth, | ||
262 | 174 | flickable.gapToMiddlePhase, | ||
263 | 175 | flickable.gapToEndPhase, | ||
264 | 176 | flickable.translationXViewFactor) | ||
266 | 177 | } | 251 | } |
267 | 178 | 252 | ||
337 | 179 | Repeater { | 253 | Behavior on explicitScaleFactor { |
338 | 180 | id: repeater | 254 | SequentialAnimation { |
339 | 181 | 255 | ScriptAction { | |
340 | 182 | model: carousel.model | 256 | script: if (!explicitScale) |
341 | 183 | 257 | explicitlyScaled = false | |
342 | 184 | Loader { | 258 | } |
343 | 185 | property bool explicitlyScaled: explicitScaleFactor == carousel.selectedItemScaleFactor | 259 | NumberAnimation { |
344 | 186 | property real explicitScaleFactor: explicitScale ? carousel.selectedItemScaleFactor : 1.0 | 260 | duration: explicitScaleFactor === 1.0 ? 250 : 150 |
345 | 187 | readonly property bool explicitScale: (!flickable.moving || | 261 | easing.type: Easing.InOutQuad |
346 | 188 | flickable.contentX <= 0 || | 262 | } |
347 | 189 | flickable.contentX >= flickable.contentWidth - flickable.width) && | 263 | ScriptAction { |
348 | 190 | flickable.newContentX < 0 && | 264 | script: if (explicitScale) |
349 | 191 | index === view.selectedIndex | 265 | explicitlyScaled = true |
281 | 192 | readonly property real cachedTiles: flickable.realPathItemCount + carousel.cacheBuffer / flickable.tileWidth | ||
282 | 193 | readonly property real distance: view.continuousIndex - index | ||
283 | 194 | readonly property real itemTranslationScale: CarouselJS.getItemScale(0.5, | ||
284 | 195 | (index + 0.5), // good approximation of scale while changing selected item | ||
285 | 196 | repeater.count, | ||
286 | 197 | flickable.visibleTilesScaleFactor) | ||
287 | 198 | readonly property real itemScale: CarouselJS.getItemScale(distance, | ||
288 | 199 | view.continuousIndex, | ||
289 | 200 | repeater.count, | ||
290 | 201 | flickable.visibleTilesScaleFactor) | ||
291 | 202 | readonly property real translationFactor: (flickable.tileWidth * 3) / flickable.scaleFactor | ||
292 | 203 | readonly property real translationX: index === view.selectedIndex ? 0 : | ||
293 | 204 | CarouselJS.getItemTranslation(distance, | ||
294 | 205 | itemScale, | ||
295 | 206 | itemTranslationScale, | ||
296 | 207 | translationFactor) | ||
297 | 208 | |||
298 | 209 | width: flickable.tileWidth | ||
299 | 210 | height: flickable.tileHeight | ||
300 | 211 | scale: itemScale * explicitScaleFactor | ||
301 | 212 | opacity: scale > 0.02 ? 1 : 0 | ||
302 | 213 | sourceComponent: z > 0 ? itemComponent : undefined | ||
303 | 214 | z: cachedTiles - Math.abs(index - view.selectedIndex) | ||
304 | 215 | |||
305 | 216 | transform: Translate { | ||
306 | 217 | x: translationX * flickable.scaleFactor | ||
307 | 218 | } | ||
308 | 219 | |||
309 | 220 | Behavior on explicitScaleFactor { | ||
310 | 221 | SequentialAnimation { | ||
311 | 222 | ScriptAction { | ||
312 | 223 | script: if (!explicitScale) | ||
313 | 224 | explicitlyScaled = false | ||
314 | 225 | } | ||
315 | 226 | NumberAnimation { | ||
316 | 227 | duration: explicitScaleFactor === 1.0 ? 250 : 150 | ||
317 | 228 | easing.type: Easing.InOutQuad | ||
318 | 229 | } | ||
319 | 230 | ScriptAction { | ||
320 | 231 | script: if (explicitScale) | ||
321 | 232 | explicitlyScaled = true | ||
322 | 233 | } | ||
323 | 234 | } | ||
324 | 235 | } | ||
325 | 236 | |||
326 | 237 | onLoaded: { | ||
327 | 238 | item.explicitlyScaled = Qt.binding(function() { return explicitlyScaled; }) | ||
328 | 239 | item.model = Qt.binding(function() { return model; }) | ||
329 | 240 | } | ||
330 | 241 | |||
331 | 242 | MouseArea { | ||
332 | 243 | id: mouseArea | ||
333 | 244 | |||
334 | 245 | anchors.fill: parent | ||
335 | 246 | |||
336 | 247 | onClicked: flickable.itemClicked(index, item) | ||
350 | 248 | } | 266 | } |
351 | 249 | } | 267 | } |
352 | 250 | } | 268 | } |
353 | 269 | |||
354 | 270 | onLoaded: { | ||
355 | 271 | item.explicitlyScaled = Qt.binding(function() { return explicitlyScaled; }) | ||
356 | 272 | item.model = Qt.binding(function() { return model; }) | ||
357 | 273 | } | ||
358 | 274 | |||
359 | 275 | MouseArea { | ||
360 | 276 | id: mouseArea | ||
361 | 277 | |||
362 | 278 | anchors.fill: parent | ||
363 | 279 | |||
364 | 280 | onClicked: listView.itemClicked(index, item) | ||
365 | 281 | } | ||
366 | 251 | } | 282 | } |
367 | 252 | } | 283 | } |
368 | 253 | } | 284 | } |
369 | 254 | 285 | ||
370 | === modified file 'Components/carousel.js' | |||
371 | --- Components/carousel.js 2013-06-05 22:03:08 +0000 | |||
372 | +++ Components/carousel.js 2013-06-06 08:03:28 +0000 | |||
373 | @@ -16,7 +16,15 @@ | |||
374 | 16 | 16 | ||
375 | 17 | .pragma library | 17 | .pragma library |
376 | 18 | 18 | ||
378 | 19 | // get the element which is selected accordingly to x, the hero of the view | 19 | /*! get the element which is selected accordingly to x, the hero of the view |
379 | 20 | @param x contentX of the ListView | ||
380 | 21 | @param tileWidth width of a single tile | ||
381 | 22 | @param gapToMiddlePhase gap in pixels between beginning and middle phase | ||
382 | 23 | @param gapToEndPhase gap in pixels between middle and end phase | ||
383 | 24 | @param kGapEnd | ||
384 | 25 | @param kMiddleIndex | ||
385 | 26 | @param kXBeginningEnd | ||
386 | 27 | */ | ||
387 | 20 | function getContinuousIndex(x, tileWidth, gapToMiddlePhase, gapToEndPhase, kGapEnd, kMiddleIndex, kXBeginningEnd) { | 28 | function getContinuousIndex(x, tileWidth, gapToMiddlePhase, gapToEndPhase, kGapEnd, kMiddleIndex, kXBeginningEnd) { |
388 | 21 | if (x < gapToMiddlePhase) { | 29 | if (x < gapToMiddlePhase) { |
389 | 22 | // beginning | 30 | // beginning |
390 | @@ -30,8 +38,16 @@ | |||
391 | 30 | return x / tileWidth + kMiddleIndex | 38 | return x / tileWidth + kMiddleIndex |
392 | 31 | } | 39 | } |
393 | 32 | 40 | ||
396 | 33 | // obtain x position relative to an index, essentially an inverse of getContinuousIndex() | 41 | /*! obtain x position relative to an index, essentially an inverse of getContinuousIndex() |
397 | 34 | function getXFromContinuousIndex(index, viewWidth, contentWidth, tileWidth, gapToMiddlePhase, gapToEndPhase) { | 42 | @param index index of the item to calcualte the proper X value for |
398 | 43 | @param viewWidth visible width of the view | ||
399 | 44 | @param contentWidth width off all items in the view | ||
400 | 45 | @param tileWidth width of one item | ||
401 | 46 | @param gapToMiddlePhase | ||
402 | 47 | @param gapToEndPhase | ||
403 | 48 | @param drawBuffer width of the drawBuffer | ||
404 | 49 | */ | ||
405 | 50 | function getXFromContinuousIndex(index, viewWidth, contentWidth, tileWidth, gapToMiddlePhase, gapToEndPhase, drawBuffer) { | ||
406 | 35 | var middleX = (index + 0.5) * tileWidth - viewWidth / 2 | 51 | var middleX = (index + 0.5) * tileWidth - viewWidth / 2 |
407 | 36 | 52 | ||
408 | 37 | if (middleX < gapToMiddlePhase) { | 53 | if (middleX < gapToMiddlePhase) { |
409 | @@ -40,6 +56,7 @@ | |||
410 | 40 | ((1 / tileWidth) + | 56 | ((1 / tileWidth) + |
411 | 41 | (viewWidth / (2 * tileWidth * gapToMiddlePhase)) - | 57 | (viewWidth / (2 * tileWidth * gapToMiddlePhase)) - |
412 | 42 | 1 / (2 * gapToMiddlePhase)) | 58 | 1 / (2 * gapToMiddlePhase)) |
413 | 59 | - drawBuffer | ||
414 | 43 | } else if (middleX > gapToEndPhase) { | 60 | } else if (middleX > gapToEndPhase) { |
415 | 44 | // inverse of 'middleIndex + kGap' of getContinuousIndex() | 61 | // inverse of 'middleIndex + kGap' of getContinuousIndex() |
416 | 45 | return (index + | 62 | return (index + |
417 | @@ -50,13 +67,19 @@ | |||
418 | 50 | (1 / tileWidth + | 67 | (1 / tileWidth + |
419 | 51 | viewWidth / (2 * tileWidth * gapToMiddlePhase) - | 68 | viewWidth / (2 * tileWidth * gapToMiddlePhase) - |
420 | 52 | 1 / (2 * gapToMiddlePhase)) | 69 | 1 / (2 * gapToMiddlePhase)) |
421 | 70 | - drawBuffer | ||
422 | 53 | } | 71 | } |
423 | 54 | 72 | ||
424 | 55 | // inverse of 'middleIndex' of getContinuousIndex() | 73 | // inverse of 'middleIndex' of getContinuousIndex() |
426 | 56 | return middleX | 74 | return middleX - drawBuffer |
427 | 57 | } | 75 | } |
428 | 58 | 76 | ||
430 | 59 | // get translation of the whole view, adds gaps on sides | 77 | /*! get translation of the whole view, adds gaps on sides |
431 | 78 | @param x contentX of the ListView | ||
432 | 79 | @param gapToMiddlePhase | ||
433 | 80 | @param gapToEndPhase | ||
434 | 81 | @param translationXViewFactor | ||
435 | 82 | */ | ||
436 | 60 | function getViewTranslation(x, tileWidth, gapToMiddlePhase, gapToEndPhase, translationXViewFactor) { | 83 | function getViewTranslation(x, tileWidth, gapToMiddlePhase, gapToEndPhase, translationXViewFactor) { |
437 | 61 | if (x < gapToMiddlePhase) { | 84 | if (x < gapToMiddlePhase) { |
438 | 62 | // beginning | 85 | // beginning |
439 | @@ -70,16 +93,29 @@ | |||
440 | 70 | return 0 | 93 | return 0 |
441 | 71 | } | 94 | } |
442 | 72 | 95 | ||
445 | 73 | // item scale | 96 | /*! item scale |
446 | 74 | function getItemScale(distance, continuousIndex, end, scaleFactor) { | 97 | @param distance is the difference of the item's index to the continuousIndex |
447 | 98 | @param continuousIndex the current index in real number | ||
448 | 99 | @param numberOfItems the total number of items in the model | ||
449 | 100 | @param scaleFactor if bigger than 1, the scaling is done slower (more distance needed) | ||
450 | 101 | */ | ||
451 | 102 | function getItemScale(distance, continuousIndex, numberOfItems, scaleFactor) { | ||
452 | 75 | var distanceAbs = Math.abs(distance) | 103 | var distanceAbs = Math.abs(distance) |
454 | 76 | var distanceToBounds = Math.min(continuousIndex, end - continuousIndex) | 104 | var distanceToBounds = Math.min(continuousIndex, numberOfItems - continuousIndex) |
455 | 77 | var k = Math.max(200 + 100 * (-distanceToBounds / (3 * scaleFactor)), 50) | 105 | var k = Math.max(200 + 100 * (-distanceToBounds / (3 * scaleFactor)), 50) |
456 | 78 | return Math.max(0.01, 1 - Math.pow(distanceAbs, 2.5) / (k * scaleFactor)) | 106 | return Math.max(0.01, 1 - Math.pow(distanceAbs, 2.5) / (k * scaleFactor)) |
457 | 79 | } | 107 | } |
458 | 80 | 108 | ||
461 | 81 | // item translation | 109 | /*! item translation |
462 | 82 | function getItemTranslation(distance, scale, maxScale, translationFactor) { | 110 | @param index index of the current item |
463 | 111 | @param selectedIndex index of the selected item | ||
464 | 112 | @param distance controls the direction wich is left/negative and right/positive | ||
465 | 113 | @param scale is the current scale factor of the item | ||
466 | 114 | @param maxScale the maximum scale factor (the one used when the index is on that item | ||
467 | 115 | @param maxTranslation the maximum translation length in pixel | ||
468 | 116 | */ | ||
469 | 117 | function getItemTranslation(index, selectedIndex, distance, scale, maxScale, maxTranslation) { | ||
470 | 118 | if (index === selectedIndex) return 0 | ||
471 | 83 | var sign = distance > 0 ? 1 : -1 | 119 | var sign = distance > 0 ? 1 : -1 |
473 | 84 | return sign * (maxScale - scale) * translationFactor | 120 | return sign * (maxScale - scale) * maxTranslation |
474 | 85 | } | 121 | } |
475 | 86 | 122 | ||
476 | === modified file 'tests/qmltests/Components/tst_Carousel.qml' | |||
477 | --- tests/qmltests/Components/tst_Carousel.qml 2013-06-05 22:03:08 +0000 | |||
478 | +++ tests/qmltests/Components/tst_Carousel.qml 2013-06-06 08:03:28 +0000 | |||
479 | @@ -19,6 +19,7 @@ | |||
480 | 19 | import "../../../Components/carousel.js" as Carousel | 19 | import "../../../Components/carousel.js" as Carousel |
481 | 20 | 20 | ||
482 | 21 | TestCase { | 21 | TestCase { |
483 | 22 | id: root | ||
484 | 22 | name: "Carousel" | 23 | name: "Carousel" |
485 | 23 | 24 | ||
486 | 24 | property real carouselWidth | 25 | property real carouselWidth |
487 | @@ -49,9 +50,9 @@ | |||
488 | 49 | } | 50 | } |
489 | 50 | 51 | ||
490 | 51 | function test_getContinuousIndex(data) { | 52 | function test_getContinuousIndex(data) { |
494 | 52 | carouselWidth = data.carouselWidth | 53 | root.carouselWidth = data.carouselWidth |
495 | 53 | tileWidth = data.tileWidth | 54 | root.tileWidth = data.tileWidth |
496 | 54 | itemCount = data.itemCount | 55 | root.itemCount = data.itemCount |
497 | 55 | 56 | ||
498 | 56 | var index = Carousel.getContinuousIndex(data.x, | 57 | var index = Carousel.getContinuousIndex(data.x, |
499 | 57 | data.tileWidth, | 58 | data.tileWidth, |
500 | @@ -69,23 +70,23 @@ | |||
501 | 69 | index: 0, carouselWidth:400, tileWidth:100, itemCount:10, drawBuffer:0, result: 0}, | 70 | index: 0, carouselWidth:400, tileWidth:100, itemCount:10, drawBuffer:0, result: 0}, |
502 | 70 | {tag:"in startup", | 71 | {tag:"in startup", |
503 | 71 | index: 2, carouselWidth:400, tileWidth:100, itemCount:10, drawBuffer:0, result: 100}, | 72 | index: 2, carouselWidth:400, tileWidth:100, itemCount:10, drawBuffer:0, result: 100}, |
506 | 72 | // {tag:"in startup with drawBuffer", | 73 | {tag:"in startup with drawBuffer", |
507 | 73 | // index: 2, carouselWidth:400, tileWidth:100, itemCount:10, drawBuffer:100, result: 0}, | 74 | index: 2, carouselWidth:400, tileWidth:100, itemCount:10, drawBuffer:100, result: 0}, |
508 | 74 | {tag:"in the middle", | 75 | {tag:"in the middle", |
509 | 75 | index: 5, carouselWidth:400, tileWidth:100, itemCount:10, drawBuffer:0, result: 350}, | 76 | index: 5, carouselWidth:400, tileWidth:100, itemCount:10, drawBuffer:0, result: 350}, |
512 | 76 | // {tag:"in the middle with drawBuffer", | 77 | {tag:"in the middle with drawBuffer", |
513 | 77 | // index: 5, carouselWidth:400, tileWidth:100, itemCount:10, drawBuffer:100, result: 250}, | 78 | index: 5, carouselWidth:400, tileWidth:100, itemCount:10, drawBuffer:100, result: 250}, |
514 | 78 | {tag:"at end", | 79 | {tag:"at end", |
515 | 79 | index: 9, carouselWidth:400, tileWidth:100, itemCount:10, drawBuffer:0, result: 600}, | 80 | index: 9, carouselWidth:400, tileWidth:100, itemCount:10, drawBuffer:0, result: 600}, |
518 | 80 | // {tag:"at end with drawBuffer", | 81 | {tag:"at end with drawBuffer", |
519 | 81 | // index: 9, carouselWidth:400, tileWidth:100, itemCount:10, drawBuffer:100, result: 500}, | 82 | index: 9, carouselWidth:400, tileWidth:100, itemCount:10, drawBuffer:100, result: 500}, |
520 | 82 | ] | 83 | ] |
521 | 83 | } | 84 | } |
522 | 84 | 85 | ||
523 | 85 | function test_getXFromContinuousIndex(data) { | 86 | function test_getXFromContinuousIndex(data) { |
527 | 86 | carouselWidth = data.carouselWidth | 87 | root.carouselWidth = data.carouselWidth |
528 | 87 | tileWidth = data.tileWidth | 88 | root.tileWidth = data.tileWidth |
529 | 88 | itemCount = data.itemCount | 89 | root.itemCount = data.itemCount |
530 | 89 | 90 | ||
531 | 90 | var x = Carousel.getXFromContinuousIndex(data.index, | 91 | var x = Carousel.getXFromContinuousIndex(data.index, |
532 | 91 | data.carouselWidth, | 92 | data.carouselWidth, |
533 | @@ -113,14 +114,14 @@ | |||
534 | 113 | } | 114 | } |
535 | 114 | 115 | ||
536 | 115 | function test_getViewTranslation(data) { | 116 | function test_getViewTranslation(data) { |
540 | 116 | carouselWidth = data.carouselWidth | 117 | root.carouselWidth = data.carouselWidth |
541 | 117 | tileWidth = data.tileWidth | 118 | root.tileWidth = data.tileWidth |
542 | 118 | itemCount = data.itemCount | 119 | root.itemCount = data.itemCount |
543 | 119 | 120 | ||
544 | 120 | var x = Carousel.getViewTranslation(data.x, | 121 | var x = Carousel.getViewTranslation(data.x, |
545 | 121 | data.tileWidth, | 122 | data.tileWidth, |
548 | 122 | gapToMiddlePhase, | 123 | root.gapToMiddlePhase, |
549 | 123 | gapToEndPhase, | 124 | root.gapToEndPhase, |
550 | 124 | data.translationXViewFactor) | 125 | data.translationXViewFactor) |
551 | 125 | compare(x, data.result) | 126 | compare(x, data.result) |
552 | 126 | } | 127 | } |
553 | @@ -158,30 +159,33 @@ | |||
554 | 158 | 159 | ||
555 | 159 | // test for the getItemTranslation() function | 160 | // test for the getItemTranslation() function |
556 | 160 | function test_getItemTranslation_data() { | 161 | function test_getItemTranslation_data() { |
562 | 161 | return [ // tests if distance only affects the sign | 162 | return [ // tests for index and selectedIndex |
563 | 162 | {distance: 1, scale: 0, maxScale: 1, maxTranslation: 10, result: 10}, | 163 | {index: 1, selectedIndex: 1, distance: 1, scale: 1, maxScale: 1, maxTranslation: 10, result: 0}, |
564 | 163 | // {distance: 99, scale: 0, maxScale: 1, maxTranslation: 10, result: 10}, | 164 | // tests if distance only affects the sign |
565 | 164 | {distance: 0, scale: 0, maxScale: 1, maxTranslation: 10, result: -10}, | 165 | {index: 1, selectedIndex: 2, distance: 1, scale: 0, maxScale: 1, maxTranslation: 10, result: 10}, |
566 | 165 | {distance: -1, scale: 0, maxScale: 1, maxTranslation: 10, result: -10}, | 166 | {index: 1, selectedIndex: 2, distance: 99, scale: 0, maxScale: 1, maxTranslation: 10, result: 10}, |
567 | 167 | {index: 1, selectedIndex: 2, distance: -1, scale: 0, maxScale: 1, maxTranslation: 10, result: -10}, | ||
568 | 168 | {index: 1, selectedIndex: 2, distance: -99, scale: 0, maxScale: 1, maxTranslation: 10, result: -10}, | ||
569 | 166 | // tests for the scale | 169 | // tests for the scale |
573 | 167 | {distance: 1, scale: 1, maxScale: 1, maxTranslation: 10, result: 0}, | 170 | {index: 1, selectedIndex: 2, distance: 1, scale: 1, maxScale: 1, maxTranslation: 10, result: 0}, |
574 | 168 | {distance: 1, scale: 0, maxScale: 1, maxTranslation: 10, result: 10}, | 171 | {index: 1, selectedIndex: 2, distance: 1, scale: 0, maxScale: 1, maxTranslation: 10, result: 10}, |
575 | 169 | {distance: 1, scale: 0.5, maxScale: 1, maxTranslation: 10, result: 5}, | 172 | {index: 1, selectedIndex: 2, distance: 1, scale: 0.5, maxScale: 1, maxTranslation: 10, result: 5}, |
576 | 170 | // tests for maxScale | 173 | // tests for maxScale |
581 | 171 | {distance: 1, scale: 1, maxScale: 1, maxTranslation: 10, result: 0}, | 174 | {index: 1, selectedIndex: 2, distance: 1, scale: 0, maxScale: 0.98, maxTranslation: 10, result: 9.8}, |
582 | 172 | {distance: 1, scale: 1, maxScale: 2, maxTranslation: 10, result: 10}, | 175 | {index: 1, selectedIndex: 2, distance: 1, scale: 0.5, maxScale: 0.95, maxTranslation: 10, result: 4.5}, |
583 | 173 | // {distance: 1, scale: 1, maxScale: 0, maxTranslation: 10, result: 0}, | 176 | {index: 1, selectedIndex: 2, distance: 1, scale: 0.8, maxScale: 0.93, maxTranslation: 10, result: 1.3}, |
580 | 174 | // {distance: 1, scale: 1, maxScale: 99, maxTranslation: 10, result: 10}, | ||
584 | 175 | // test for maxTranslation | 177 | // test for maxTranslation |
589 | 176 | {distance: 1, scale: 1, maxScale: 1, maxTranslation: 1, result: 0}, | 178 | {index: 1, selectedIndex: 2, distance: 1, scale: 1, maxScale: 1, maxTranslation: 1, result: 0}, |
590 | 177 | {distance: 1, scale: 0, maxScale: 1, maxTranslation: 1, result: 1}, | 179 | {index: 1, selectedIndex: 2, distance: 1, scale: 0, maxScale: 1, maxTranslation: 1, result: 1}, |
591 | 178 | {distance: 1, scale: 0, maxScale: 1, maxTranslation: 10, result: 10}, | 180 | {index: 1, selectedIndex: 2, distance: 1, scale: 0, maxScale: 1, maxTranslation: 10, result: 10}, |
592 | 179 | {distance: 1, scale: 0, maxScale: 1, maxTranslation: 0, result: 0}, | 181 | {index: 1, selectedIndex: 2, distance: 1, scale: 0, maxScale: 1, maxTranslation: 0, result: 0}, |
593 | 180 | ] | 182 | ] |
594 | 181 | } | 183 | } |
595 | 182 | 184 | ||
596 | 183 | function test_getItemTranslation(data) { | 185 | function test_getItemTranslation(data) { |
598 | 184 | var scale = Carousel.getItemTranslation(data.distance, | 186 | var scale = Carousel.getItemTranslation(data.index, |
599 | 187 | data.selectedIndex, | ||
600 | 188 | data.distance, | ||
601 | 185 | data.scale, | 189 | data.scale, |
602 | 186 | data.maxScale, | 190 | data.maxScale, |
603 | 187 | data.maxTranslation) | 191 | data.maxTranslation) |
FAILED: Continuous integration, rev:682 jenkins. qa.ubuntu. com/job/ unity-phablet- ci/991/ s-jenkins: 8080/job/ unity-phablet- qmluitests/ 963 jenkins. qa.ubuntu. com/job/ unity-phablet- raring- armhf-ci/ 867 jenkins. qa.ubuntu. com/job/ unity-phablet- raring- armhf-ci/ 867/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ unity-phablet- raring- i386-ci/ 871 jenkins. qa.ubuntu. com/job/ generic- mediumtests- runner/ 1581
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ unity-phablet- ci/991/ rebuild
http://