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
20import "../Components/ListItems" as ListItems
24 readonly property alias previewShown: previewListView.onScreen
25 property bool enableHeightBehaviorOnNextCreation: false
27 moving: categoryView.moving
30 pageHeader.resetSearch();
31 previewListView.open = false;
34 onMovementStarted: categoryView.showHeader()
36 onPositionedAtBeginning: categoryView.positionAtBeginning()
39 target: scopeView.scope
40 property: "searchQuery"
41 value: pageHeader.searchQuery
46 property: "searchQuery"
47 value: scopeView.scope.searchQuery
52 onSearchClicked: if (isCurrent) {
53 pageHeader.triggerSearch()
54 categoryView.showHeader()
59 target: scopeView.scope
60 onShowDash: previewListView.open = false;
61 onHideDash: previewListView.open = false;
66 objectName: "categoryListView"
68 model: scopeView.categories
69 forceNoClip: previewListView.onScreen
71 onAtYEndChanged: if (atYEnd) endReached()
72 onMovingChanged: if (moving && atYEnd) endReached()
74 property string expandedCategoryId: ""
75 signal correctExpandedCategory();
77 Behavior on contentY {
78 enabled: previewListView.open
79 UbuntuNumberAnimation {}
82 delegate: ListItems.Base {
84 highlightWhenPressed: false
86 readonly property bool expandable: rendererLoader.item ? rendererLoader.item.expandable : false
87 readonly property bool filtered: rendererLoader.item ? rendererLoader.item.filter : true
88 readonly property string category: categoryId
98 source: getRenderer(model.renderer, model.contentType, model.rendererHint)
101 if (item.enableHeightBehavior !== undefined && item.enableHeightBehaviorOnNextCreation !== undefined) {
102 item.enableHeightBehavior = scopeView.enableHeightBehaviorOnNextCreation;
103 scopeView.enableHeightBehaviorOnNextCreation = false;
105 if (source.toString().indexOf("Apps/RunningApplicationsGrid.qml") != -1) {
106 // TODO: the running apps grid doesn't support standard scope results model yet
107 item.firstModel = Qt.binding(function() { return results.firstModel })
108 item.secondModel = Qt.binding(function() { return results.secondModel })
109 item.canEnableTerminationMode = Qt.binding(function() { return scopeView.isCurrent })
111 item.model = Qt.binding(function() { return results })
113 item.objectName = Qt.binding(function() { return categoryId })
114 if (item.expandable) {
115 var shouldFilter = categoryId != categoryView.expandedCategoryId;
116 if (shouldFilter != item.filter) {
117 item.filter = shouldFilter;
120 updateDelegateCreationRange();
123 Component.onDestruction: {
124 if (item.enableHeightBehavior !== undefined && item.enableHeightBehaviorOnNextCreation !== undefined) {
125 scopeView.enableHeightBehaviorOnNextCreation = item.enableHeightBehaviorOnNextCreation;
130 target: rendererLoader.item
132 // Prepare the preview in case activate() triggers a preview only
133 effect.positionPx = mapToItem(categoryView, 0, itemY).y
134 previewListView.categoryId = categoryId
135 previewListView.categoryDelegate = rendererLoader.item
136 previewListView.model = model;
137 previewListView.init = true;
138 previewListView.currentIndex = index;
140 var item = model.get(index);
142 if ((scopeView.scope.id == "applications.scope" && categoryId == "installed")
143 || (scopeView.scope.id == "home.scope" && categoryId == "applications.scope")) {
144 scopeView.scope.activate(item.uri, item.icon, item.category, 0, item.mimetype, item.title,
145 item.comment, item.dndUri, item.metadata)
147 previewListView.open = true
149 scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title,
150 item.comment, item.dndUri, item.metadata)
154 effect.positionPx = mapToItem(categoryView, 0, itemY).y
155 previewListView.categoryId = categoryId
156 previewListView.categoryDelegate = rendererLoader.item
157 previewListView.model = model;
158 previewListView.init = true;
159 previewListView.currentIndex = index;
160 previewListView.open = true
162 print("model is", model)
164 var item = model.get(index)
165 scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title,
166 item.comment, item.dndUri, item.metadata)
171 onExpandedCategoryIdChanged: {
172 collapseAllButExpandedCategory();
174 onCorrectExpandedCategory: {
175 collapseAllButExpandedCategory();
177 function collapseAllButExpandedCategory() {
178 var item = rendererLoader.item;
179 if (item.expandable) {
180 var shouldFilter = categoryId != categoryView.expandedCategoryId;
181 if (shouldFilter != item.filter) {
182 // If the filter animation will be seen start it, otherwise, just flip the switch
183 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
184 var growingVisible = !shouldFilter && y + height < categoryView.height;
185 if (!previewListView.open || !shouldFilter) {
186 if (shrinkingVisible || growingVisible) {
187 item.startFilterAnimation(shouldFilter)
189 item.filter = shouldFilter;
191 if (!shouldFilter && !previewListView.open) {
192 categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
198 onContentYChanged: rendererLoader.updateDelegateCreationRange();
199 onHeightChanged: rendererLoader.updateDelegateCreationRange();
202 function updateDelegateCreationRange() {
203 // Do not update the range if we are overshooting up or down, since we'll come back
204 // to the stable position and delete/create items without any reason
205 if (categoryView.contentY < categoryView.originY) {
207 } else if (categoryView.contentY + categoryView.height > categoryView.contentHeight) {
211 if (item.hasOwnProperty("delegateCreationBegin")) {
212 if (baseItem.y + baseItem.height <= 0) {
213 // Not visible (item at top of the list)
214 item.delegateCreationBegin = baseItem.height
215 item.delegateCreationEnd = baseItem.height
216 } else if (baseItem.y >= categoryView.height) {
217 // Not visible (item at bottom of the list)
218 item.delegateCreationBegin = 0
219 item.delegateCreationEnd = 0
221 item.delegateCreationBegin = Math.max(-baseItem.y, 0)
222 item.delegateCreationEnd = Math.min(categoryView.height + item.delegateCreationBegin, baseItem.height)
229 sectionProperty: "name"
230 sectionDelegate: ListItems.Header {
231 property var delegate: categoryView.item(delegateIndex)
232 width: categoryView.width
235 if (delegate && delegate.expandable)
236 return delegate.filtered ? "graphics/header_handlearrow.png" : "graphics/header_handlearrow2.png"
240 if (categoryView.expandedCategoryId != delegate.category)
241 categoryView.expandedCategoryId = delegate.category;
243 categoryView.expandedCategoryId = "";
246 pageHeader: PageHeader {
248 objectName: "pageHeader"
249 width: categoryView.width
250 text: scopeView.scope.name
251 searchEntryEnabled: true
252 scope: scopeView.scope
256 function getDefaultRendererId(contentType) {
257 switch (contentType) {
258 default: return "grid";
262 function getRenderer(rendererId, contentType, rendererHint) {
263 if (rendererId == "default") {
264 rendererId = getDefaultRendererId(contentType);
266 switch (rendererId) {
268 switch (contentType) {
269 case "video": return "Video/VideoFilterGrid.qml";
270 case "music": return "Music/MusicFilterGrid.qml";
272 if (rendererHint == "toggled")
273 return "Apps/DashPluginFilterGrid.qml";
275 return "Generic/GenericFilterGrid.qml";
277 case "weather": return "Generic/WeatherFilterGrid.qml";
278 default: return "Generic/GenericFilterGrid.qml";
282 switch (contentType) {
283 case "music": return "Music/MusicCarousel.qml";
284 default: return "Generic/GenericCarousel.qml";
288 switch (contentType) {
289 case "apps": return "Apps/RunningApplicationsGrid.qml";
290 default: return "Generic/GenericFilterGrid.qml";
293 default: return "Generic/GenericFilterGrid.qml";
301 bottomMargin: -bottomOverflow
303 sourceItem: categoryView
307 topGapPx: (1 - gap) * positionPx
308 topOpacity: (1 - gap * 1.2)
309 bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
310 bottomOverflow: units.gu(20)
311 live: !expansionAnimation.running
313 property int targetBottomGapPx: height - units.gu(8) - bottomOverflow
314 property real gap: previewListView.open ? 1.0 : 0.0
318 id: expansionAnimation
320 easing.type: Easing.InOutQuad
322 if (!previewListView.open && !running) {
323 previewListView.onScreen = false
328 Behavior on positionPx {
329 enabled: previewListView.open
330 UbuntuNumberAnimation {}
335 target: scopeView.scope
337 if (previewListView.init) {
338 // Preview was triggered because of a click on the item. Need to expand now.
339 if (!previewListView.open) {
340 previewListView.open = true
343 var index = previewListView.currentIndex
344 previewListView.currentIndex = -1
345 previewListView.currentIndex = index
346 previewListView.init = false
348 previewListView.currentItem.previewData = preview
352 PreviewDelegateMapper {
353 id: previewDelegateMapper
358 objectName: "previewListView"
359 height: effect.bottomGapPx - effect.topGapPx
362 topMargin: effect.topGapPx
366 orientation: ListView.Horizontal
367 highlightRangeMode: ListView.StrictlyEnforceRange
368 snapMode: ListView.SnapOneItem
369 boundsBehavior: Flickable.DragAndOvershootBounds
370 highlightMoveDuration: 250
371 flickDeceleration: units.gu(625)
372 maximumFlickVelocity: width * 5
375 // To be set before opening the preview
376 property string categoryId: ""
377 property var categoryDelegate
379 // because the ListView is built asynchronous, setting the
380 // currentIndex directly won't work. We need to refresh it
381 // when the first preview is ready to be displayed.
382 property bool init: true
384 onCurrentIndexChanged: {
385 var row = Math.floor(currentIndex / categoryDelegate.columns);
386 if (categoryDelegate.collapsedRowCount <= row) {
387 categoryView.expandedCategoryId = categoryId
391 categoryDelegate.highlightIndex = currentIndex
393 var itemY = categoryView.contentItem.mapFromItem(categoryDelegate.currentItem).y;
394 var newContentY = itemY - effect.positionPx - categoryDelegate.verticalSpacing;
395 var effectAdjust = effect.positionPx;
396 if (newContentY < 0) {
397 effectAdjust += newContentY;
400 if (newContentY > categoryView.contentHeight - categoryView.height) {
401 effectAdjust += -(categoryView.contentHeight - categoryView.height) + newContentY
402 newContentY = categoryView.contentHeight - categoryView.height;
405 effect.positionPx = effectAdjust;
406 categoryView.contentY = newContentY;
409 property bool open: false
410 property bool onScreen: false
415 categoryDelegate.highlightIndex = currentIndex;
417 // Cancel any pending preview requests or actions
418 if (previewListView.currentItem.previewData !== undefined) {
419 previewListView.currentItem.previewData.cancelAction();
421 scopeView.scope.cancelActivation();
423 categoryView.correctExpandedCategory();
424 categoryDelegate.highlightIndex = -1;
430 color: Qt.rgba(0, 0, 0, .3)
436 objectName: "previewLoader"
437 height: previewListView.height
438 width: previewListView.width
440 source: previewListView.onScreen ?
441 (previewData !== undefined ? previewDelegateMapper.map(previewData.rendererName) : "DashPreviewPlaceholder.qml") : ""
443 onPreviewDataChanged: {
444 if (previewData !== undefined && source.toString().indexOf("DashPreviewPlaceholder.qml") != -1) {
445 previewLoader.opacity = 0;
450 if (previewData !== undefined) {
457 target: previewLoader
461 duration: UbuntuAnimation.BriskDuration
464 property var previewData
465 property bool valid: item !== null
468 if (previewListView.onScreen && previewData !== undefined) {
469 item.previewData = Qt.binding(function() { return previewData })
474 ignoreUnknownSignals: true
477 previewListView.open = false
485 top: previewListView.bottom
487 leftMargin: previewListView.categoryDelegate.currentItem ?
488 previewListView.categoryDelegate.currentItem.center + (-width + margins) / 2 : 0
490 Behavior on leftMargin {
491 UbuntuNumberAnimation {}
496 property int margins: previewListView.categoryDelegate ? previewListView.categoryDelegate.margins : 0
497 opacity: previewListView.open ? .5 : 0
499 source: "graphics/tooltip_arrow.png"
502 // TODO: Move as InverseMouseArea to DashPreview
504 enabled: previewListView.onScreen
507 topMargin: effect.bottomGapPx
510 previewListView.open = false;
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