2 * Copyright (C) 2013 Canonical, Ltd.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18import Ubuntu.Components 0.1
19import "carousel.js" as CarouselJS
21/*! The Carousel component presents the items of a model in a carousel view. It's similar to a
22 cover flow. But it stops at it's boundaries (therefore no PathView is used).
27 clip: true // Don't leak horizontally to other dashes
29 /// The component to be used as delegate. This component has to be derived from BaseCarouselDelegate
30 property Component itemComponent
31 /// Model for the Carousel, which has to be a model usable by a ListView
32 property alias model: listView.model
33 /// A minimal width of a tile can be set here. Per default a best fit will be calculated
34 property alias minimumTileWidth: listView.minimumTileWidth
35 /// Sets the number of tiles that are visible
36 property alias pathItemCount: listView.pathItemCount
37 /// Aspect ratio of the tiles width/height
38 property alias tileAspectRatio: listView.tileAspectRatio
39 /// Used to cache some delegates for performance reasons. See the ListView documentation for details
40 property alias cacheBuffer: listView.cacheBuffer
41 /// Width of the "draw buffer" in pixel. The drawBuffer is an additional area at start/end where
42 /// items drawn, even if it is not in the visible area.
43 /// cacheBuffer controls only the to retain delegates outside the visible area (and is used on top of the drawBuffer)
44 /// see https://bugreports.qt-project.org/browse/QTBUG-29173
45 property int drawBuffer: width / pathItemCount // an "ok" value - but values used from the listView cause loops
46 /// The selected item can be shown in a different size controlled by selectedItemScaleFactor
47 property real selectedItemScaleFactor: 1.1
49 /// Emitted when the user clicked on an item
50 /// @param index is the index of the clicked item
51 /// @param delegateItem is the clicked component/delegate itself
52 /// @param itemY is y of the clicked delegate
53 signal clicked(int index, var delegateItem, real itemY)
55 /// Emitted when the user pressed and held on an item
56 /// @param index is the index of the held item
57 /// @param delegateItem is the held component/delegate itself
58 /// @param itemY is y of the held delegate
59 signal pressAndHold(int index, var delegateItem, real itemY)
61 implicitHeight: listView.tileHeight * selectedItemScaleFactor
63 /* Basic idea behind the carousel effect is to move the items of the delegates (compacting /stuffing them).
64 One drawback is, that more delegates have to be drawn than usually. As some items are moved from the
65 invisible to the visible area. Setting the cacheBuffer does not fix this.
66 See https://bugreports.qt-project.org/browse/QTBUG-29173
67 Therefore the ListView has negative left and right anchors margins, and in addition a header
68 and footer item to compensate that.
70 The scaling of the items is controlled by the variable continuousIndex, described below. */
74 property real minimumTileWidth: 0
75 property real newContentX: -1
76 property real pathItemCount: referenceWidth / referenceTileWidth
77 property real tileAspectRatio: 1
79 /* The positioning and scaling of the items in the carousel is based on the variable
80 'continuousIndex', a continuous real variable between [0, 'carousel.model.count'],
81 roughly representing the index of the item that is prioritised over the others.
82 'continuousIndex' is not linear, but is weighted depending on if it is close
83 to the beginning of the content (beginning phase), in the middle (middle phase)
84 or at the end (end phase).
85 Each tile is scaled and transformed in proportion to the difference between
86 its own index and continuousIndex.
87 To efficiently calculate continuousIndex, we have these values:
88 - 'gapToMiddlePhase' gap in pixels between beginning and middle phase
89 - 'gapToEndPhase' gap in pixels between middle and end phase
90 - 'kGapEnd' constant used to calculate 'continuousIndex' in end phase
91 - 'kMiddleIndex' constant used to calculate 'continuousIndex' in middle phase
92 - 'kXBeginningEnd' constant used to calculate 'continuousIndex' in beginning and end phase
93 - 'realContentWidth' the width of all the delegates only (without header/footer)
94 - 'realContentX' the 'contentX' of the listview ignoring the 'drawBuffer'
95 - 'realWidth' the 'width' of the listview, as it is used as component. */
97 readonly property real gapToMiddlePhase: Math.min(realWidth / 2 - tileWidth / 2, (realContentWidth - realWidth) / 2)
98 readonly property real gapToEndPhase: realContentWidth - realWidth - gapToMiddlePhase
99 readonly property real kGapEnd: kMiddleIndex * (1 - gapToEndPhase / gapToMiddlePhase)
100 readonly property real kMiddleIndex: (realWidth / 2) / tileWidth - 0.5
101 readonly property real kXBeginningEnd: 1 / tileWidth + kMiddleIndex / gapToMiddlePhase
102 readonly property real maximumItemTranslation: (listView.tileWidth * 3) / listView.scaleFactor
103 readonly property real realContentWidth: contentWidth - 2 * carousel.drawBuffer
104 readonly property real realContentX: contentX + carousel.drawBuffer
105 readonly property real realPathItemCount: Math.min(realWidth / tileWidth, pathItemCount)
106 readonly property real realWidth: carousel.width
107 readonly property real referenceGapToMiddlePhase: realWidth / 2 - tileWidth / 2
108 readonly property real referencePathItemCount: referenceWidth / referenceTileWidth
109 readonly property real referenceWidth: 848
110 readonly property real referenceTileWidth: 175
111 readonly property real scaleFactor: tileWidth / referenceTileWidth
112 readonly property real tileWidth: Math.max(realWidth / pathItemCount, minimumTileWidth)
113 readonly property real tileHeight: tileWidth / tileAspectRatio
114 readonly property real translationXViewFactor: 0.2 * (referenceGapToMiddlePhase / gapToMiddlePhase)
115 readonly property real verticalMargin: (parent.height - tileHeight) / 2
116 readonly property real visibleTilesScaleFactor: realPathItemCount / referencePathItemCount
120 topMargin: verticalMargin
121 bottomMargin: verticalMargin
122 // extending the "drawing area"
123 leftMargin: -carousel.drawBuffer
124 rightMargin: -carousel.drawBuffer
127 /* The header and footer help to "extend" the area, the listview draws items.
128 This together with anchors.leftMargin and anchors.rightMargin. */
130 width: carousel.drawBuffer
131 height: listView.tileHeight
134 width: carousel.drawBuffer
135 height: listView.tileHeight
138 boundsBehavior: Flickable.StopAtBounds
139 cacheBuffer: carousel.cacheBuffer
140 flickDeceleration: Math.max(1500 * Math.pow(realWidth / referenceWidth, 1.5), 1500) // 1500 is platform default
141 maximumFlickVelocity: Math.max(2500 * Math.pow(realWidth / referenceWidth, 1.5), 2500) // 2500 is platform default
142 orientation: ListView.Horizontal
144 function itemClicked(index, delegateItem) {
145 var x = CarouselJS.getXFromContinuousIndex(index,
153 if (Math.abs(x - contentX) < 1) {
154 /* We're clicking the selected item and
155 we're in the neighbourhood of radius 1 pixel from it.
156 Let's emit the clicked signal. */
157 carousel.clicked(index, delegateItem, delegateItem.y)
162 newContentXAnimation.stop()
165 newContentXAnimation.start()
168 function itemPressAndHold(index, delegateItem) {
169 var x = CarouselJS.getXFromContinuousIndex(index,
175 carousel.drawBuffer);
177 if (Math.abs(x - contentX) < 1) {
178 /* We're pressAndHold the selected item and
179 we're in the neighbourhood of radius 1 pixel from it.
180 Let's emit the pressAndHold signal. */
181 carousel.pressAndHold(index, delegateItem, delegateItem.y);
185 stepAnimation.stop();
186 newContentXAnimation.stop();
189 newContentXAnimation.start();
194 newContentXAnimation.stop()
198 if (realContentX > 0 && realContentX < realContentWidth - realWidth)
199 stepAnimation.start()
207 to: CarouselJS.getXFromContinuousIndex(listView.selectedIndex,
209 listView.realContentWidth,
211 listView.gapToMiddlePhase,
212 listView.gapToEndPhase,
216 easing.type: Easing.InOutQuad
219 SequentialAnimation {
220 id: newContentXAnimation
225 from: listView.contentX
226 to: listView.newContentX
228 easing.type: Easing.InOutQuad
231 script: listView.newContentX = -1
235 readonly property int selectedIndex: Math.round(continuousIndex)
236 readonly property real continuousIndex: CarouselJS.getContinuousIndex(listView.realContentX,
238 listView.gapToMiddlePhase,
239 listView.gapToEndPhase,
241 listView.kMiddleIndex,
242 listView.kXBeginningEnd)
244 property real viewTranslation: CarouselJS.getViewTranslation(listView.realContentX,
246 listView.gapToMiddlePhase,
247 listView.gapToEndPhase,
248 listView.translationXViewFactor)
251 property bool explicitlyScaled: explicitScaleFactor == carousel.selectedItemScaleFactor
252 property real explicitScaleFactor: explicitScale ? carousel.selectedItemScaleFactor : 1.0
253 readonly property bool explicitScale: (!listView.moving ||
254 listView.realContentX <= 0 ||
255 listView.realContentX >= listView.realContentWidth - listView.realWidth) &&
256 listView.newContentX < 0 &&
257 index === listView.selectedIndex
258 readonly property real cachedTiles: listView.realPathItemCount + carousel.drawBuffer / listView.tileWidth
259 readonly property real distance: listView.continuousIndex - index
260 readonly property real itemTranslationScale: CarouselJS.getItemScale(0.5,
261 (index + 0.5), // good approximation of scale while changing selected item
263 listView.visibleTilesScaleFactor)
264 readonly property real itemScale: CarouselJS.getItemScale(distance,
265 listView.continuousIndex,
267 listView.visibleTilesScaleFactor)
268 readonly property real translationX: CarouselJS.getItemTranslation(index,
269 listView.selectedIndex,
272 itemTranslationScale,
273 listView.maximumItemTranslation)
275 width: listView.tileWidth
276 height: listView.tileHeight
277 scale: itemScale * explicitScaleFactor
278 sourceComponent: itemComponent
279 z: cachedTiles - Math.abs(index - listView.selectedIndex)
281 transform: Translate {
282 x: listView.viewTranslation + translationX * listView.scaleFactor
285 Behavior on explicitScaleFactor {
286 SequentialAnimation {
288 script: if (!explicitScale)
289 explicitlyScaled = false
292 duration: explicitScaleFactor === 1.0 ? 250 : 150
293 easing.type: Easing.InOutQuad
296 script: if (explicitScale)
297 explicitlyScaled = true
303 item.explicitlyScaled = Qt.binding(function() { return explicitlyScaled; })
304 item.model = Qt.binding(function() { return model; })
313 listView.itemClicked(index, item)
317 listView.itemPressAndHold(index, item)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list