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