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
48 /// The index of the item that should be highlighted
49 property alias highlightIndex: listView.highlightIndex
50 /// The column count is required for API compatibility with FilterGrids
51 readonly property int columns: listView.count
52 /// Indicates that this component automatically centers the highlighted item
53 readonly property bool highlightCentered: true
55 /// Emitted when the user clicked on an item
56 /// @param index is the index of the clicked item
57 /// @param model is the model of all the items in the carousel
58 /// @param itemY is y of the clicked delegate
59 signal clicked(int index, var model, real itemY)
61 /// Emitted when the user pressed and held on an item
62 /// @param index is the index of the held item
63 /// @param model is the model of all the items in the carousel
64 /// @param itemY is y of the held delegate
65 signal pressAndHold(int index, var model, real itemY)
67 implicitHeight: listView.tileHeight * selectedItemScaleFactor
69 /* Basic idea behind the carousel effect is to move the items of the delegates (compacting /stuffing them).
70 One drawback is, that more delegates have to be drawn than usually. As some items are moved from the
71 invisible to the visible area. Setting the cacheBuffer does not fix this.
72 See https://bugreports.qt-project.org/browse/QTBUG-29173
73 Therefore the ListView has negative left and right anchors margins, and in addition a header
74 and footer item to compensate that.
76 The scaling of the items is controlled by the variable continuousIndex, described below. */
80 property real minimumTileWidth: 0
81 property real newContentX: -1
82 property real pathItemCount: referenceWidth / referenceTileWidth
83 property real tileAspectRatio: 1
84 property int highlightIndex: -1
86 /* The positioning and scaling of the items in the carousel is based on the variable
87 'continuousIndex', a continuous real variable between [0, 'carousel.model.count'],
88 roughly representing the index of the item that is prioritised over the others.
89 'continuousIndex' is not linear, but is weighted depending on if it is close
90 to the beginning of the content (beginning phase), in the middle (middle phase)
91 or at the end (end phase).
92 Each tile is scaled and transformed in proportion to the difference between
93 its own index and continuousIndex.
94 To efficiently calculate continuousIndex, we have these values:
95 - 'gapToMiddlePhase' gap in pixels between beginning and middle phase
96 - 'gapToEndPhase' gap in pixels between middle and end phase
97 - 'kGapEnd' constant used to calculate 'continuousIndex' in end phase
98 - 'kMiddleIndex' constant used to calculate 'continuousIndex' in middle phase
99 - 'kXBeginningEnd' constant used to calculate 'continuousIndex' in beginning and end phase
100 - 'realContentWidth' the width of all the delegates only (without header/footer)
101 - 'realContentX' the 'contentX' of the listview ignoring the 'drawBuffer'
102 - 'realWidth' the 'width' of the listview, as it is used as component. */
104 readonly property real gapToMiddlePhase: Math.min(realWidth / 2 - tileWidth / 2, (realContentWidth - realWidth) / 2)
105 readonly property real gapToEndPhase: realContentWidth - realWidth - gapToMiddlePhase
106 readonly property real kGapEnd: kMiddleIndex * (1 - gapToEndPhase / gapToMiddlePhase)
107 readonly property real kMiddleIndex: (realWidth / 2) / tileWidth - 0.5
108 readonly property real kXBeginningEnd: 1 / tileWidth + kMiddleIndex / gapToMiddlePhase
109 readonly property real maximumItemTranslation: (listView.tileWidth * 3) / listView.scaleFactor
110 readonly property real realContentWidth: contentWidth - 2 * carousel.drawBuffer
111 readonly property real realContentX: contentX + carousel.drawBuffer
112 readonly property real realPathItemCount: Math.min(realWidth / tileWidth, pathItemCount)
113 readonly property real realWidth: carousel.width
114 readonly property real referenceGapToMiddlePhase: realWidth / 2 - tileWidth / 2
115 readonly property real referencePathItemCount: referenceWidth / referenceTileWidth
116 readonly property real referenceWidth: 848
117 readonly property real referenceTileWidth: 175
118 readonly property real scaleFactor: tileWidth / referenceTileWidth
119 readonly property real tileWidth: Math.max(realWidth / pathItemCount, minimumTileWidth)
120 readonly property real tileHeight: tileWidth / tileAspectRatio
121 readonly property real translationXViewFactor: 0.2 * (referenceGapToMiddlePhase / gapToMiddlePhase)
122 readonly property real verticalMargin: (parent.height - tileHeight) / 2
123 readonly property real visibleTilesScaleFactor: realPathItemCount / referencePathItemCount
127 topMargin: verticalMargin
128 bottomMargin: verticalMargin
129 // extending the "drawing area"
130 leftMargin: -carousel.drawBuffer
131 rightMargin: -carousel.drawBuffer
134 /* The header and footer help to "extend" the area, the listview draws items.
135 This together with anchors.leftMargin and anchors.rightMargin. */
137 width: carousel.drawBuffer
138 height: listView.tileHeight
141 width: carousel.drawBuffer
142 height: listView.tileHeight
145 boundsBehavior: Flickable.StopAtBounds
146 cacheBuffer: carousel.cacheBuffer
147 flickDeceleration: Math.max(1500 * Math.pow(realWidth / referenceWidth, 1.5), 1500) // 1500 is platform default
148 maximumFlickVelocity: Math.max(2500 * Math.pow(realWidth / referenceWidth, 1.5), 2500) // 2500 is platform default
149 orientation: ListView.Horizontal
150 currentIndex: highlightIndex
151 highlightFollowsCurrentItem: true
152 preferredHighlightBegin: (width - tileWidth) * 0.5
153 preferredHighlightEnd: (width - tileWidth) * 0.5
154 highlightRangeMode: highlightIndex != -1 ? ListView.StrictlyEnforceRange : ListView.NoHighlightRange
156 function itemClicked(index, delegateItem) {
157 var x = CarouselJS.getXFromContinuousIndex(index,
165 if (Math.abs(x - contentX) < 1) {
166 /* We're clicking the selected item and
167 we're in the neighbourhood of radius 1 pixel from it.
168 Let's emit the clicked signal. */
169 carousel.clicked(index, listView.model, delegateItem.y)
174 newContentXAnimation.stop()
177 newContentXAnimation.start()
180 function itemPressAndHold(index, delegateItem) {
181 var x = CarouselJS.getXFromContinuousIndex(index,
187 carousel.drawBuffer);
189 if (Math.abs(x - contentX) < 1) {
190 /* We're pressAndHold the selected item and
191 we're in the neighbourhood of radius 1 pixel from it.
192 Let's emit the pressAndHold signal. */
193 carousel.pressAndHold(index, listView.model, delegateItem.y);
197 stepAnimation.stop();
198 newContentXAnimation.stop();
201 newContentXAnimation.start();
206 newContentXAnimation.stop()
210 if (realContentX > 0 && realContentX < realContentWidth - realWidth)
211 stepAnimation.start()
219 to: CarouselJS.getXFromContinuousIndex(listView.selectedIndex,
221 listView.realContentWidth,
223 listView.gapToMiddlePhase,
224 listView.gapToEndPhase,
228 easing.type: Easing.InOutQuad
231 SequentialAnimation {
232 id: newContentXAnimation
237 from: listView.contentX
238 to: listView.newContentX
240 easing.type: Easing.InOutQuad
243 script: listView.newContentX = -1
247 readonly property int selectedIndex: Math.round(continuousIndex)
248 readonly property real continuousIndex: CarouselJS.getContinuousIndex(listView.realContentX,
250 listView.gapToMiddlePhase,
251 listView.gapToEndPhase,
253 listView.kMiddleIndex,
254 listView.kXBeginningEnd)
256 property real viewTranslation: CarouselJS.getViewTranslation(listView.realContentX,
258 listView.gapToMiddlePhase,
259 listView.gapToEndPhase,
260 listView.translationXViewFactor)
263 property bool explicitlyScaled: explicitScaleFactor == carousel.selectedItemScaleFactor
264 property real explicitScaleFactor: explicitScale ? carousel.selectedItemScaleFactor : 1.0
265 readonly property bool explicitScale: (!listView.moving ||
266 listView.realContentX <= 0 ||
267 listView.realContentX >= listView.realContentWidth - listView.realWidth) &&
268 listView.newContentX < 0 &&
269 index === listView.selectedIndex
270 readonly property real cachedTiles: listView.realPathItemCount + carousel.drawBuffer / listView.tileWidth
271 readonly property real distance: listView.continuousIndex - index
272 readonly property real itemTranslationScale: CarouselJS.getItemScale(0.5,
273 (index + 0.5), // good approximation of scale while changing selected item
275 listView.visibleTilesScaleFactor)
276 readonly property real itemScale: CarouselJS.getItemScale(distance,
277 listView.continuousIndex,
279 listView.visibleTilesScaleFactor)
280 readonly property real translationX: CarouselJS.getItemTranslation(index,
281 listView.selectedIndex,
284 itemTranslationScale,
285 listView.maximumItemTranslation)
287 width: listView.tileWidth
288 height: listView.tileHeight
289 scale: itemScale * explicitScaleFactor
290 sourceComponent: itemComponent
291 z: cachedTiles - Math.abs(index - listView.selectedIndex)
292 opacity: highlightIndex == -1 ? 1 : (highlightIndex == index ? 0.6 : 0.2)
294 transform: Translate {
295 x: listView.viewTranslation + translationX * listView.scaleFactor
298 Behavior on explicitScaleFactor {
299 SequentialAnimation {
301 script: if (!explicitScale)
302 explicitlyScaled = false
305 duration: explicitScaleFactor === 1.0 ? 250 : 150
306 easing.type: Easing.InOutQuad
309 script: if (explicitScale)
310 explicitlyScaled = true
316 item.explicitlyScaled = Qt.binding(function() { return explicitlyScaled; })
317 item.model = Qt.binding(function() { return model; })
326 listView.itemClicked(index, item)
330 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