@@ -21,11 +21,11 @@ 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
26 onIsCurrentChanged: {
26 onIsCurrentChanged: {
27 pageHeader.resetSearch();
27 pageHeader.resetSearch();
28− previewLoader.open = false;
28+ previewListView.open = false;
31 onMovementStarted: categoryView.showHeader()
31 onMovementStarted: categoryView.showHeader()
@@ -52,22 +52,29 @@ ScopeView {
53 Connections {
53 Connections {
54 target: scopeView.scope
54 target: scopeView.scope
55− onShowDash: previewLoader.open = false;
55+ onShowDash: previewListView.open = false;
56− onHideDash: previewLoader.open = false;
56+ onHideDash: previewListView.open = false;
59 ScopeListView {
59 ScopeListView {
60 id: categoryView
60 id: categoryView
61 anchors.fill: parent
61 anchors.fill: parent
62 model: scopeView.categories
62 model: scopeView.categories
63− forceNoClip: previewLoader.onScreen
63+ forceNoClip: previewListView.onScreen
65 onAtYEndChanged: if (atYEnd) endReached()
65 onAtYEndChanged: if (atYEnd) endReached()
66 onMovingChanged: if (moving && atYEnd) endReached()
66 onMovingChanged: if (moving && atYEnd) endReached()
68 property string expandedCategoryId: ""
68 property string expandedCategoryId: ""
69+ signal correctExpandedCategory();
71+ Behavior on contentY {
72+ enabled: previewListView.open
73+ UbuntuNumberAnimation {}
70 delegate: ListItems.Base {
76 delegate: ListItems.Base {
71 highlightWhenPressed: false
78 highlightWhenPressed: false
73 readonly property bool expandable: rendererLoader.item ? rendererLoader.item.expandable : false
80 readonly property bool expandable: rendererLoader.item ? rendererLoader.item.expandable : false
@@ -104,33 +111,50 @@ ScopeView {
104 Connections {
111 Connections {
105 target: rendererLoader.item
112 target: rendererLoader.item
106 onClicked: {
113 onClicked: {
114+ // Prepare the preview in case activate() triggers a preview only
107 effect.positionPx = mapToItem(categoryView, 0, itemY).y
115 effect.positionPx = mapToItem(categoryView, 0, itemY).y
116+ previewListView.categoryId = categoryId
117+ previewListView.categoryDelegate = rendererLoader.item
118+ previewListView.model = model;
119+ previewListView.init = true;
120+ previewListView.currentIndex = index;
122+ var item = model.get(index);
124+ if ((scopeView.scope.id == "applications.scope" && categoryId == "installed")
125+ || (scopeView.scope.id == "home.scope" && categoryId == "applications.scope")) {
108− scopeView.scope.activate(delegateItem.model.uri,
126+ scopeView.scope.activate(item.uri, item.icon, item.category, 0, item.mimetype, item.title,
109− delegateItem.model.icon,
127+ item.comment, item.dndUri, item.metadata)
110− delegateItem.model.category,
128+ } else {
111− 0,
129+ previewListView.open = true
112− delegateItem.model.mimetype,
130+
113− delegateItem.model.title,
131+ scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title,
114− delegateItem.model.comment,
132+ item.comment, item.dndUri, item.metadata)
115− delegateItem.model.dndUri,
133+ }
116− delegateItem.model.metadata)
118 onPressAndHold: {
135 onPressAndHold: {
119 effect.positionPx = mapToItem(categoryView, 0, itemY).y
136 effect.positionPx = mapToItem(categoryView, 0, itemY).y
137+ previewListView.categoryId = categoryId
138+ previewListView.categoryDelegate = rendererLoader.item
139+ previewListView.model = model;
140+ previewListView.init = true;
141+ previewListView.currentIndex = index;
142+ previewListView.open = true
144+ var item = model.get(index)
120− scopeView.scope.preview( delegateItem.model.uri,
145+ scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title,
121− delegateItem.model.icon,
146+ item.comment, item.dndUri, item.metadata)
122− delegateItem.model.category,
124− delegateItem.model.mimetype,
125− delegateItem.model.title,
126− delegateItem.model.comment,
127− delegateItem.model.dndUri,
128− delegateItem.model.metadata)
131 Connections {
149 Connections {
132 target: categoryView
150 target: categoryView
133 onExpandedCategoryIdChanged: {
151 onExpandedCategoryIdChanged: {
152+ collapseAllButExpandedCategory();
154+ onCorrectExpandedCategory: {
155+ collapseAllButExpandedCategory();
157+ function collapseAllButExpandedCategory() {
134 var item = rendererLoader.item;
158 var item = rendererLoader.item;
135 if (item.expandable) {
159 if (item.expandable) {
136 var shouldFilter = categoryId != categoryView.expandedCategoryId;
160 var shouldFilter = categoryId != categoryView.expandedCategoryId;
@@ -138,13 +162,15 @@ ScopeView {
138 // If the filter animation will be seen start it, otherwise, just flip the switch
162 // If the filter animation will be seen start it, otherwise, just flip the switch
139 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
163 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
140 var growingVisible = !shouldFilter && y + height < categoryView.height;
164 var growingVisible = !shouldFilter && y + height < categoryView.height;
165+ if (!previewListView.open || !shouldFilter) {
141− if (shrinkingVisible || growingVisible) {
166+ if (shrinkingVisible || growingVisible) {
142− item.startFilterAnimation(shouldFilter)
167+ item.startFilterAnimation(shouldFilter)
143− } else {
168+ } else {
144− item.filter = shouldFilter;
169+ item.filter = shouldFilter;
146− if (!shouldFilter) {
171+ if (!shouldFilter && !previewListView.open) {
147− categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
172+ categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
@@ -222,18 +248,18 @@ ScopeView {
222 topOpacity: (1 - gap * 1.2)
248 topOpacity: (1 - gap * 1.2)
223 bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
249 bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
224 bottomOverflow: units.gu(20)
250 bottomOverflow: units.gu(20)
225− bottomOpacity: 1 - (gap * 0.8)
251+ live: true
227 property int targetBottomGapPx: height - units.gu(8) - bottomOverflow
253 property int targetBottomGapPx: height - units.gu(8) - bottomOverflow
228− property real gap: previewLoader.open ? 1.0 : 0.0
254+ property real gap: previewListView.open ? 1.0 : 0.0
230 Behavior on gap {
256 Behavior on gap {
231 NumberAnimation {
257 NumberAnimation {
232 duration: 200
258 duration: 200
233 easing.type: Easing.InOutQuad
259 easing.type: Easing.InOutQuad
234 onRunningChanged: {
260 onRunningChanged: {
235− if (!previewLoader.open && !running) {
261+ if (!previewListView.open && !running) {
236− previewLoader.onScreen = false
262+ previewListView.onScreen = false
@@ -243,8 +269,18 @@ ScopeView {
243 Connections {
269 Connections {
244 target: scopeView.scope
270 target: scopeView.scope
245 onPreviewReady: {
271 onPreviewReady: {
272+ if (previewListView.init) {
273+ // Preview was triggered because of a click on the item. Need to expand now.
274+ if (!previewListView.open) {
275+ previewListView.open = true
278+ var index = previewListView.currentIndex
279+ previewListView.currentIndex = -1
280+ previewListView.currentIndex = index
281+ previewListView.init = false
246− previewLoader.previewData = preview
283+ previewListView.currentItem.previewData = preview
247− previewLoader.open = true
@@ -252,18 +288,9 @@ ScopeView {
252 id: previewDelegateMapper
288 id: previewDelegateMapper
255− Connections {
291+ ListView {
256− ignoreUnknownSignals: true
292+ id: previewListView
257− target: previewLoader.valid ? previewLoader.item : null
293+ objectName: "previewListView"
259− previewLoader.open = false
264− objectName: "previewLoader"
266− property var previewData
267 height: effect.bottomGapPx - effect.topGapPx
294 height: effect.bottomGapPx - effect.topGapPx
268 anchors {
295 anchors {
269 top: parent.top
296 top: parent.top
@@ -271,32 +298,151 @@ ScopeView {
271 left: parent.left
298 left: parent.left
272 right: parent.right
299 right: parent.right
274− source: onScreen ? previewDelegateMapper.map(previewLoader.previewData.rendererName) : ""
301+ orientation: ListView.Horizontal
302+ highlightRangeMode: ListView.StrictlyEnforceRange
303+ snapMode: ListView.SnapOneItem
304+ boundsBehavior: Flickable.DragAndOvershootBounds
305+ highlightMoveDuration: 250
306+ flickDeceleration: units.gu(625)
307+ maximumFlickVelocity: width * 5
310+ // To be set before opening the preview
311+ property string categoryId: ""
312+ property var categoryDelegate
314+ // because the ListView is built asynchronous, setting the
315+ // currentIndex directly won't work. We need to refresh it
316+ // when the first preview is ready to be displayed.
317+ property bool init: true
320+ property int oldRow: -1
321+ property bool categoryWasFiltered: false
322+ property int initialContenY: 0
324+ onCurrentIndexChanged: {
325+ var row = Math.floor(currentIndex / categoryDelegate.columns);
326+ if (!init && model !== undefined) {
327+ var item = model.get(currentIndex)
328+ scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title, item.comment, item.dndUri, item.metadata)
331+ categoryView.contentY -= categoryDelegate.cellHeight
332+ } else if (row > oldRow){
333+ categoryView.contentY += categoryDelegate.cellHeight
338+ if (categoryDelegate.collapsedRowCount <= row) {
339+ categoryView.expandedCategoryId = categoryId
343+ categoryDelegate.highlightIndex = currentIndex
276 property bool open: false
347 property bool open: false
277 property bool onScreen: false
348 property bool onScreen: false
278− property bool valid: item !== null
280 onOpenChanged: {
350 onOpenChanged: {
281 if (open) {
351 if (open) {
282 onScreen = true
352 onScreen = true
353+ categoryDelegate.highlightIndex = currentIndex
356+ categoryView.correctExpandedCategory();
357+ categoryDelegate.highlightIndex = -1
362+ anchors.fill: parent
363+ color: Qt.rgba(0, 0, 0, .3)
368+ objectName: "previewLoader"
369+ height: previewListView.height
370+ width: previewListView.width
372+ source: previewListView.onScreen ?
373+ (previewData !== undefined ? previewDelegateMapper.map(previewData.rendererName) : "DashPreviewPlaceholder.qml") : ""
375+ onPreviewDataChanged: {
376+ if (previewData !== undefined && source.toString().indexOf("DashPreviewPlaceholder.qml") != -1) {
377+ previewLoader.opacity = 0;
382+ if (previewData !== undefined) {
389+ target: previewLoader
393+ duration: UbuntuAnimation.BriskDuration
396+ property var previewData
397+ property bool valid: item !== null
286− onLoaded: {
399+ onLoaded: {
400+ if (previewListView.onScreen && previewData !== undefined) {
287− item.previewData = Qt.binding(function() { return previewLoader.previewData })
401+ item.previewData = Qt.binding(function() { return previewData })
406+ ignoreUnknownSignals: true
409+ previewListView.open = false
417+ top: previewListView.bottom
419+ leftMargin: previewListView.categoryDelegate.highlightCentered ?
420+ (parent.width - width) / 2 :
421+ (highlightColumn * cellWidth) + (-width + cellWidth + margins) / 2
422+ Behavior on leftMargin {
423+ UbuntuNumberAnimation {}
428+ property int columns: previewListView.categoryDelegate ? previewListView.categoryDelegate.columns : 1
429+ property int highlightColumn: previewListView.currentIndex % columns
430+ property int cellWidth: previewListView.categoryDelegate ? previewListView.categoryDelegate.cellWidth : 0
431+ property int margins: previewListView.categoryDelegate ? previewListView.categoryDelegate.margins : 0
432+ opacity: previewListView.open ? .5 : 0
434+ source: "graphics/tooltip_arrow.png"
291 // TODO: Move as InverseMouseArea to DashPreview
437 // TODO: Move as InverseMouseArea to DashPreview
292 MouseArea {
438 MouseArea {
293− enabled: previewLoader.onScreen
439+ enabled: previewListView.onScreen
294 anchors {
440 anchors {
295 fill: parent
441 fill: parent
296 topMargin: effect.bottomGapPx
442 topMargin: effect.bottomGapPx
298 onClicked: {
444 onClicked: {
299− previewLoader.open = false;
445+ previewListView.open = false;