@@ -21,14 +21,14 @@ import "../Components/ListItems" as ListItems
22ScopeView {
22ScopeView {
23 id: scopeView
23 id: scopeView
24− readonly property alias previewShown: previewLoader.onScreen
24+ readonly property alias previewShown: previewListView.onScreen
25 property bool enableHeightBehaviorOnNextCreation: false
25 property bool enableHeightBehaviorOnNextCreation: false
27 moving: categoryView.moving
27 moving: categoryView.moving
29 onIsCurrentChanged: {
29 onIsCurrentChanged: {
30 pageHeader.resetSearch();
30 pageHeader.resetSearch();
31− previewLoader.open = false;
31+ previewListView.open = false;
34 onMovementStarted: categoryView.showHeader()
34 onMovementStarted: categoryView.showHeader()
@@ -57,8 +57,8 @@ ScopeView {
58 Connections {
58 Connections {
59 target: scopeView.scope
59 target: scopeView.scope
60− onShowDash: previewLoader.open = false;
60+ onShowDash: previewListView.open = false;
61− onHideDash: previewLoader.open = false;
61+ onHideDash: previewListView.open = false;
64 ScopeListView {
64 ScopeListView {
@@ -66,12 +66,18 @@ ScopeView {
66 objectName: "categoryListView"
66 objectName: "categoryListView"
67 anchors.fill: parent
67 anchors.fill: parent
68 model: scopeView.categories
68 model: scopeView.categories
69− forceNoClip: previewLoader.onScreen
69+ forceNoClip: previewListView.onScreen
71 onAtYEndChanged: if (atYEnd) endReached()
71 onAtYEndChanged: if (atYEnd) endReached()
72 onMovingChanged: if (moving && atYEnd) endReached()
72 onMovingChanged: if (moving && atYEnd) endReached()
74 property string expandedCategoryId: ""
74 property string expandedCategoryId: ""
75+ signal correctExpandedCategory();
77+ Behavior on contentY {
78+ enabled: previewListView.open
79+ UbuntuNumberAnimation {}
76 delegate: ListItems.Base {
82 delegate: ListItems.Base {
77 id: baseItem
83 id: baseItem
@@ -123,33 +129,50 @@ ScopeView {
123 Connections {
129 Connections {
124 target: rendererLoader.item
130 target: rendererLoader.item
125 onClicked: {
131 onClicked: {
132+ // Prepare the preview in case activate() triggers a preview only
126 effect.positionPx = mapToItem(categoryView, 0, itemY).y
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")) {
127− scopeView.scope.activate(delegateItem.model.uri,
144+ scopeView.scope.activate(item.uri, item.icon, item.category, 0, item.mimetype, item.title,
128− delegateItem.model.icon,
145+ item.comment, item.dndUri, item.metadata)
129− delegateItem.model.category,
146+ } else {
130− 0,
147+ previewListView.open = true
131− delegateItem.model.mimetype,
148+
132− delegateItem.model.title,
149+ scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title,
133− delegateItem.model.comment,
150+ item.comment, item.dndUri, item.metadata)
134− delegateItem.model.dndUri,
151+ }
135− delegateItem.model.metadata)
137 onPressAndHold: {
153 onPressAndHold: {
138 effect.positionPx = mapToItem(categoryView, 0, itemY).y
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+ var item = model.get(index)
139− scopeView.scope.preview( delegateItem.model.uri,
163+ scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title,
140− delegateItem.model.icon,
164+ item.comment, item.dndUri, item.metadata)
141− delegateItem.model.category,
143− delegateItem.model.mimetype,
144− delegateItem.model.title,
145− delegateItem.model.comment,
146− delegateItem.model.dndUri,
147− delegateItem.model.metadata)
150 Connections {
167 Connections {
151 target: categoryView
168 target: categoryView
152 onExpandedCategoryIdChanged: {
169 onExpandedCategoryIdChanged: {
170+ collapseAllButExpandedCategory();
172+ onCorrectExpandedCategory: {
173+ collapseAllButExpandedCategory();
175+ function collapseAllButExpandedCategory() {
153 var item = rendererLoader.item;
176 var item = rendererLoader.item;
154 if (item.expandable) {
177 if (item.expandable) {
155 var shouldFilter = categoryId != categoryView.expandedCategoryId;
178 var shouldFilter = categoryId != categoryView.expandedCategoryId;
@@ -157,13 +180,15 @@ ScopeView {
157 // If the filter animation will be seen start it, otherwise, just flip the switch
180 // If the filter animation will be seen start it, otherwise, just flip the switch
158 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
181 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
159 var growingVisible = !shouldFilter && y + height < categoryView.height;
182 var growingVisible = !shouldFilter && y + height < categoryView.height;
183+ if (!previewListView.open || !shouldFilter) {
160− if (shrinkingVisible || growingVisible) {
184+ if (shrinkingVisible || growingVisible) {
161− item.startFilterAnimation(shouldFilter)
185+ item.startFilterAnimation(shouldFilter)
162− } else {
186+ } else {
163− item.filter = shouldFilter;
187+ item.filter = shouldFilter;
165− if (!shouldFilter) {
189+ if (!shouldFilter && !previewListView.open) {
166− categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
190+ categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
@@ -269,6 +294,7 @@ ScopeView {
270 OpenEffect {
295 OpenEffect {
271 id: effect
296 id: effect
297+ objectName: "openEffect"
272 anchors {
298 anchors {
273 fill: parent
299 fill: parent
274 bottomMargin: -bottomOverflow
300 bottomMargin: -bottomOverflow
@@ -281,29 +307,44 @@ ScopeView {
281 topOpacity: (1 - gap * 1.2)
307 topOpacity: (1 - gap * 1.2)
282 bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
308 bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
283 bottomOverflow: units.gu(20)
309 bottomOverflow: units.gu(20)
284− bottomOpacity: 1 - (gap * 0.8)
310+ live: !expansionAnimation.running
286 property int targetBottomGapPx: height - units.gu(8) - bottomOverflow
312 property int targetBottomGapPx: height - units.gu(8) - bottomOverflow
287− property real gap: previewLoader.open ? 1.0 : 0.0
313+ property real gap: previewListView.open ? 1.0 : 0.0
289 Behavior on gap {
315 Behavior on gap {
290 NumberAnimation {
316 NumberAnimation {
317+ id: expansionAnimation
291 duration: 200
318 duration: 200
292 easing.type: Easing.InOutQuad
319 easing.type: Easing.InOutQuad
293 onRunningChanged: {
320 onRunningChanged: {
294− if (!previewLoader.open && !running) {
321+ if (!previewListView.open && !running) {
295− previewLoader.onScreen = false
322+ previewListView.onScreen = false
327+ Behavior on positionPx {
328+ enabled: previewListView.open
329+ UbuntuNumberAnimation {}
302 Connections {
333 Connections {
303 target: scopeView.scope
334 target: scopeView.scope
304 onPreviewReady: {
335 onPreviewReady: {
336+ if (previewListView.init) {
337+ // Preview was triggered because of a click on the item. Need to expand now.
338+ if (!previewListView.open) {
339+ previewListView.open = true
342+ var index = previewListView.currentIndex
343+ previewListView.currentIndex = -1
344+ previewListView.currentIndex = index
345+ previewListView.init = false
305− previewLoader.previewData = preview
347+ previewListView.currentItem.previewData = preview
306− previewLoader.open = true
@@ -311,18 +352,9 @@ ScopeView {
311 id: previewDelegateMapper
352 id: previewDelegateMapper
314− Connections {
355+ ListView {
315− ignoreUnknownSignals: true
356+ id: previewListView
316− target: previewLoader.valid ? previewLoader.item : null
357+ objectName: "previewListView"
318− previewLoader.open = false
323− objectName: "previewLoader"
325− property var previewData
326 height: effect.bottomGapPx - effect.topGapPx
358 height: effect.bottomGapPx - effect.topGapPx
327 anchors {
359 anchors {
328 top: parent.top
360 top: parent.top
@@ -330,32 +362,168 @@ ScopeView {
330 left: parent.left
362 left: parent.left
331 right: parent.right
363 right: parent.right
333− source: onScreen ? previewDelegateMapper.map(previewLoader.previewData.rendererName) : ""
365+ orientation: ListView.Horizontal
366+ highlightRangeMode: ListView.StrictlyEnforceRange
367+ snapMode: ListView.SnapOneItem
368+ boundsBehavior: Flickable.DragAndOvershootBounds
369+ highlightMoveDuration: 250
370+ flickDeceleration: units.gu(625)
371+ maximumFlickVelocity: width * 5
374+ // To be set before opening the preview
375+ property string categoryId: ""
376+ property var categoryDelegate
378+ // because the ListView is built asynchronous, setting the
379+ // currentIndex directly won't work. We need to refresh it
380+ // when the first preview is ready to be displayed.
381+ property bool init: true
383+ onCurrentIndexChanged: {
384+ var row = Math.floor(currentIndex / categoryDelegate.columns);
385+ if (categoryDelegate.collapsedRowCount <= row) {
386+ categoryView.expandedCategoryId = categoryId
390+ categoryDelegate.highlightIndex = currentIndex
393+ if (!init && model !== undefined) {
394+ var item = model.get(currentIndex)
395+ scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title, item.comment, item.dndUri, item.metadata)
398+ var itemY = categoryView.contentItem.mapFromItem(categoryDelegate.currentItem).y;
399+ var newContentY = itemY - effect.positionPx - categoryDelegate.verticalSpacing;
400+ var effectAdjust = effect.positionPx;
401+ if (newContentY < 0) {
402+ effectAdjust += newContentY;
405+ if (newContentY > Math.max(0, categoryView.contentHeight - categoryView.height)) {
406+ effectAdjust += -(categoryView.contentHeight - categoryView.height) + newContentY
407+ newContentY = categoryView.contentHeight - categoryView.height;
410+ effect.positionPx = effectAdjust;
411+ categoryView.contentY = newContentY - categoryView.originY;
335 property bool open: false
414 property bool open: false
336 property bool onScreen: false
415 property bool onScreen: false
337− property bool valid: item !== null
339 onOpenChanged: {
417 onOpenChanged: {
340 if (open) {
418 if (open) {
341− onScreen = true
419+ onScreen = true;
420+ categoryDelegate.highlightIndex = currentIndex;
421+ pageHeader.unfocus();
423+ // Cancel any pending preview requests or actions
424+ if (previewListView.currentItem.previewData !== undefined) {
425+ previewListView.currentItem.previewData.cancelAction();
427+ scopeView.scope.cancelActivation();
429+ categoryView.correctExpandedCategory();
430+ categoryDelegate.highlightIndex = -1;
345− onLoaded: {
434+ Rectangle {
346− item.previewData = Qt.binding(function() { return previewLoader.previewData })
435+ anchors.fill: parent
436+ color: Qt.rgba(0, 0, 0, .3)
442+ objectName: "previewLoader" + index
443+ height: previewListView.height
444+ width: previewListView.width
446+ source: previewListView.onScreen ?
447+ (previewData !== undefined ? previewDelegateMapper.map(previewData.rendererName) : "DashPreviewPlaceholder.qml") : ""
449+ onPreviewDataChanged: {
450+ if (previewData !== undefined && source.toString().indexOf("DashPreviewPlaceholder.qml") != -1) {
451+ previewLoader.opacity = 0;
456+ if (previewData !== undefined) {
463+ target: previewLoader
467+ duration: UbuntuAnimation.BriskDuration
470+ property var previewData
471+ property bool valid: item !== null
474+ if (previewListView.onScreen && previewData !== undefined) {
475+ item.previewData = Qt.binding(function() { return previewData })
480+ ignoreUnknownSignals: true
483+ previewListView.open = false
487+ function closePreviewSpinner() {
489+ item.showProcessingAction = false;
496+ objectName: "pointerArrow"
498+ top: previewListView.bottom
500+ leftMargin: previewListView.categoryDelegate !== undefined && previewListView.categoryDelegate.currentItem ?
501+ previewListView.categoryDelegate.currentItem.center + (-width + margins) / 2 : 0
503+ Behavior on leftMargin {
505+ duration: UbuntuAnimation.FastDuration
511+ property int margins: previewListView.categoryDelegate ? previewListView.categoryDelegate.margins : 0
512+ opacity: previewListView.open ? .5 : 0
514+ source: "graphics/tooltip_arrow.png"
350 // TODO: Move as InverseMouseArea to DashPreview
517 // TODO: Move as InverseMouseArea to DashPreview
351 MouseArea {
518 MouseArea {
519+ objectName: "closePreviewMouseArea"
352− enabled: previewLoader.onScreen
520+ enabled: previewListView.onScreen
353 anchors {
521 anchors {
354 fill: parent
522 fill: parent
355 topMargin: effect.bottomGapPx
523 topMargin: effect.bottomGapPx
357 onClicked: {
525 onClicked: {
358− previewLoader.open = false;
526+ previewListView.open = false;