on xenial help sign in
reset after completely messing up merging
e390a84Michael Zanetti
Components/Carousel.qml+19−6
@@ -45,18 +45,24 @@ Item {
45 property int drawBuffer: width / pathItemCount // an "ok" value - but values used from the listView cause loops
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
46 /// The selected item can be shown in a different size controlled by selectedItemScaleFactor
47 property real selectedItemScaleFactor: 1.1
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
48
54
49 /// Emitted when the user clicked on an item
55 /// Emitted when the user clicked on an item
50 /// @param index is the index of the clicked item
56 /// @param index is the index of the clicked item
51− /// @param delegateItem is the clicked component/delegate itself
57+ /// @param model is the model of all the items in the carousel
52 /// @param itemY is y of the clicked delegate
58 /// @param itemY is y of the clicked delegate
53− signal clicked(int index, var delegateItem, real itemY)
59+ signal clicked(int index, var model, real itemY)
54
60
55 /// Emitted when the user pressed and held on an item
61 /// Emitted when the user pressed and held on an item
56 /// @param index is the index of the held item
62 /// @param index is the index of the held item
57− /// @param delegateItem is the held component/delegate itself
63+ /// @param model is the model of all the items in the carousel
58 /// @param itemY is y of the held delegate
64 /// @param itemY is y of the held delegate
59− signal pressAndHold(int index, var delegateItem, real itemY)
65+ signal pressAndHold(int index, var model, real itemY)
60
66
61 implicitHeight: listView.tileHeight * selectedItemScaleFactor
67 implicitHeight: listView.tileHeight * selectedItemScaleFactor
62
68
@@ -75,6 +81,7 @@ Item {
75 property real newContentX: -1
81 property real newContentX: -1
76 property real pathItemCount: referenceWidth / referenceTileWidth
82 property real pathItemCount: referenceWidth / referenceTileWidth
77 property real tileAspectRatio: 1
83 property real tileAspectRatio: 1
84+ property int highlightIndex: -1
78
85
79 /* The positioning and scaling of the items in the carousel is based on the variable
86 /* 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'],
87 'continuousIndex', a continuous real variable between [0, 'carousel.model.count'],
@@ -140,6 +147,11 @@ Item {
140 flickDeceleration: Math.max(1500 * Math.pow(realWidth / referenceWidth, 1.5), 1500) // 1500 is platform default
147 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
148 maximumFlickVelocity: Math.max(2500 * Math.pow(realWidth / referenceWidth, 1.5), 2500) // 2500 is platform default
142 orientation: ListView.Horizontal
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
143
155
144 function itemClicked(index, delegateItem) {
156 function itemClicked(index, delegateItem) {
145 var x = CarouselJS.getXFromContinuousIndex(index,
157 var x = CarouselJS.getXFromContinuousIndex(index,
@@ -154,7 +166,7 @@ Item {
154 /* We're clicking the selected item and
166 /* We're clicking the selected item and
155 we're in the neighbourhood of radius 1 pixel from it.
167 we're in the neighbourhood of radius 1 pixel from it.
156 Let's emit the clicked signal. */
168 Let's emit the clicked signal. */
157− carousel.clicked(index, delegateItem, delegateItem.y)
169+ carousel.clicked(index, listView.model, delegateItem.y)
158 return
170 return
159 }
171 }
160
172
@@ -178,7 +190,7 @@ Item {
178 /* We're pressAndHold the selected item and
190 /* We're pressAndHold the selected item and
179 we're in the neighbourhood of radius 1 pixel from it.
191 we're in the neighbourhood of radius 1 pixel from it.
180 Let's emit the pressAndHold signal. */
192 Let's emit the pressAndHold signal. */
181− carousel.pressAndHold(index, delegateItem, delegateItem.y);
193+ carousel.pressAndHold(index, listView.model, delegateItem.y);
182 return;
194 return;
183 }
195 }
184
196
@@ -277,6 +289,7 @@ Item {
277 scale: itemScale * explicitScaleFactor
289 scale: itemScale * explicitScaleFactor
278 sourceComponent: itemComponent
290 sourceComponent: itemComponent
279 z: cachedTiles - Math.abs(index - listView.selectedIndex)
291 z: cachedTiles - Math.abs(index - listView.selectedIndex)
292+ opacity: highlightIndex == -1 ? 1 : (highlightIndex == index ? 0.6 : 0.2)
280
293
281 transform: Translate {
294 transform: Translate {
282 x: listView.viewTranslation + translationX * listView.scaleFactor
295 x: listView.viewTranslation + translationX * listView.scaleFactor
Components/FilterGrid.qml+3
@@ -54,12 +54,15 @@ Item {
54 property alias delegateWidth: iconTileGrid.delegateWidth
54 property alias delegateWidth: iconTileGrid.delegateWidth
55 property alias delegateHeight: iconTileGrid.delegateHeight
55 property alias delegateHeight: iconTileGrid.delegateHeight
56 property alias verticalSpacing: iconTileGrid.verticalSpacing
56 property alias verticalSpacing: iconTileGrid.verticalSpacing
57+ readonly property alias margins: iconTileGrid.margins
57 property alias delegate: iconTileGrid.delegate
58 property alias delegate: iconTileGrid.delegate
58 property alias cellWidth: iconTileGrid.cellWidth
59 property alias cellWidth: iconTileGrid.cellWidth
59 property alias cellHeight: iconTileGrid.cellHeight
60 property alias cellHeight: iconTileGrid.cellHeight
60 readonly property alias flicking: iconTileGrid.flicking
61 readonly property alias flicking: iconTileGrid.flicking
61 readonly property alias moving: iconTileGrid.moving
62 readonly property alias moving: iconTileGrid.moving
62 readonly property alias pressDelay: iconTileGrid.pressDelay
63 readonly property alias pressDelay: iconTileGrid.pressDelay
64+ property alias highlightIndex: iconTileGrid.highlightIndex
65+ readonly property bool highlightCentered: false
63
66
64 height: !filterAnimation.running ? childrenRect.height : height
67 height: !filterAnimation.running ? childrenRect.height : height
65 clip: filterAnimation.running
68 clip: filterAnimation.running
Components/ResponsiveGridView.qml+3
@@ -26,6 +26,7 @@ Item {
26 property int maximumNumberOfColumns: 6
26 property int maximumNumberOfColumns: 6
27 readonly property int columns: gridView.columns
27 readonly property int columns: gridView.columns
28 property alias verticalSpacing: gridView.verticalSpacing
28 property alias verticalSpacing: gridView.verticalSpacing
29+ readonly property alias margins: gridView.margin
29 property int delegateWidth
30 property int delegateWidth
30 property int delegateHeight
31 property int delegateHeight
31 property alias model: gridView.model
32 property alias model: gridView.model
@@ -39,6 +40,7 @@ Item {
39 readonly property alias flicking: gridView.flicking
40 readonly property alias flicking: gridView.flicking
40 readonly property alias moving: gridView.moving
41 readonly property alias moving: gridView.moving
41 readonly property alias pressDelay: gridView.pressDelay
42 readonly property alias pressDelay: gridView.pressDelay
43+ property alias highlightIndex: gridView.highlightIndex
42
44
43 function contentHeightForRows(rows) {
45 function contentHeightForRows(rows) {
44 return rows * cellHeight + verticalSpacing
46 return rows * cellHeight + verticalSpacing
@@ -54,6 +56,7 @@ Item {
54 topMargin: verticalSpacing
56 topMargin: verticalSpacing
55 }
57 }
56 clip: parent.height != totalContentHeight
58 clip: parent.height != totalContentHeight
59+ property int highlightIndex: -1
57
60
58 function pixelToGU(value) {
61 function pixelToGU(value) {
59 return Math.floor(value / units.gu(1));
62 return Math.floor(value / units.gu(1));
Components/Tile.qml+6
@@ -26,6 +26,12 @@ AbstractButton {
26 property alias text: label.text
26 property alias text: label.text
27 property int imageWidth
27 property int imageWidth
28 property int imageHeight
28 property int imageHeight
29+ opacity: GridView.view.highlightIndex === -1 ? 1 :
30+ GridView.view.highlightIndex === index ? 0.6 : 0.2
31+
32+ Behavior on opacity {
33+ UbuntuNumberAnimation {}
34+ }
29
35
30 UbuntuShape {
36 UbuntuShape {
31 id: icon
37 id: icon
Dash/Apps/AppPreview.qml+9
@@ -39,6 +39,15 @@ DashPreview {
39 }
39 }
40 model: previewData.infoMap["more-screenshots"] != null ? previewData.infoMap["more-screenshots"].value : [previewData.image]
40 model: previewData.infoMap["more-screenshots"] != null ? previewData.infoMap["more-screenshots"].value : [previewData.image]
41
41
42+ // FIXME: Because of ListViews inside ListViews inside Flickables inside ListViews (and some more)
43+ // we finally reached the point where this ListView doesn't correctly get swipe input any more but
44+ // instead the parent ListView is the one that is swiped. This MouseArea sort of creates a blocking
45+ // layer to make sure this ListView can be swiped, regardless of what's behind it.
46+ MouseArea {
47+ anchors.fill: parent
48+ enabled: parent.contentWidth > parent.width
49+ }
50+
42 delegate: UbuntuShape {
51 delegate: UbuntuShape {
43 id: shape
52 id: shape
44 anchors {
53 anchors {
Dash/DashPreview.qml+2−2
@@ -17,7 +17,7 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19
19
20−Rectangle {
20+Item {
21 id: root
21 id: root
22
22
23 property int keyboardSize: Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0
23 property int keyboardSize: Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0
@@ -36,7 +36,6 @@ Rectangle {
36 signal close()
36 signal close()
37 signal previewImageClicked()
37 signal previewImageClicked()
38
38
39− color: Qt.rgba(0, 0, 0, .3)
40 clip: true
39 clip: true
41
40
42 function ensureVisible(item) {
41 function ensureVisible(item) {
@@ -119,6 +118,7 @@ Rectangle {
119 left: parent.left
118 left: parent.left
120 leftMargin: title.paintedWidth + labelItem.spacing
119 leftMargin: title.paintedWidth + labelItem.spacing
121 }
120 }
121+ visible: title.text !== ""
122 }
122 }
123 }
123 }
124 }
124 }
Dash/DashPreviewPlaceholder.qmlnew file+25
@@ -0,0 +1,25 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+
20+DashPreview {
21+ ActivityIndicator {
22+ anchors.centerIn: parent
23+ running: true
24+ }
25+}
Dash/Generic/GenericFilterGrid.qml+4−6
@@ -28,8 +28,8 @@ FilterGrid {
28 property int iconWidth: units.gu(8)
28 property int iconWidth: units.gu(8)
29 property int iconHeight: units.gu(7.5)
29 property int iconHeight: units.gu(7.5)
30
30
31− signal clicked(int index, var delegateItem, real itemY)
31+ signal clicked(int index, var model, real itemY)
32− signal pressAndHold(int index, var delegateItem, real itemY)
32+ signal pressAndHold(int index, var model, real itemY)
33
33
34 delegate: Tile {
34 delegate: Tile {
35 id: tile
35 id: tile
@@ -45,13 +45,11 @@ FilterGrid {
45 fillMode: Image.PreserveAspectCrop
45 fillMode: Image.PreserveAspectCrop
46
46
47 onClicked: {
47 onClicked: {
48− var data = { model: model }
49− filtergrid.clicked(index, data, tile.y)
48+ filtergrid.clicked(index, filtergrid.model, tile.y)
50 }
49 }
51
50
52 onPressAndHold: {
51 onPressAndHold: {
53− var data = { model: model }
54− filtergrid.pressAndHold(index, data, tile.y)
52+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
55 }
53 }
56 }
54 }
57}
55}
Dash/GenericScopeView.qml+200−54
@@ -21,11 +21,11 @@ import "../Components/ListItems" as ListItems
21
21
22ScopeView {
22ScopeView {
23 id: scopeView
23 id: scopeView
24− readonly property alias previewShown: previewLoader.onScreen
24+ readonly property alias previewShown: previewListView.onScreen
25
25
26 onIsCurrentChanged: {
26 onIsCurrentChanged: {
27 pageHeader.resetSearch();
27 pageHeader.resetSearch();
28− previewLoader.open = false;
28+ previewListView.open = false;
29 }
29 }
30
30
31 onMovementStarted: categoryView.showHeader()
31 onMovementStarted: categoryView.showHeader()
@@ -52,22 +52,29 @@ ScopeView {
52
52
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;
57 }
57 }
58
58
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
64
64
65 onAtYEndChanged: if (atYEnd) endReached()
65 onAtYEndChanged: if (atYEnd) endReached()
66 onMovingChanged: if (moving && atYEnd) endReached()
66 onMovingChanged: if (moving && atYEnd) endReached()
67
67
68 property string expandedCategoryId: ""
68 property string expandedCategoryId: ""
69+ signal correctExpandedCategory();
70+
71+ Behavior on contentY {
72+ enabled: previewListView.open
73+ UbuntuNumberAnimation {}
74+ }
69
75
70 delegate: ListItems.Base {
76 delegate: ListItems.Base {
77+ id: rendererDelegate
71 highlightWhenPressed: false
78 highlightWhenPressed: false
72
79
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;
121+
122+ var item = model.get(index);
123+
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)
117 }
134 }
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
143+
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,
123− 0,
124− delegateItem.model.mimetype,
125− delegateItem.model.title,
126− delegateItem.model.comment,
127− delegateItem.model.dndUri,
128− delegateItem.model.metadata)
129 }
147 }
130 }
148 }
131 Connections {
149 Connections {
132 target: categoryView
150 target: categoryView
133 onExpandedCategoryIdChanged: {
151 onExpandedCategoryIdChanged: {
152+ collapseAllButExpandedCategory();
153+ }
154+ onCorrectExpandedCategory: {
155+ collapseAllButExpandedCategory();
156+ }
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;
145− }
170+ }
146− if (!shouldFilter) {
171+ if (!shouldFilter && !previewListView.open) {
147− categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
172+ categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
173+ }
148 }
174 }
149 }
175 }
150 }
176 }
@@ -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
226
252
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
229
255
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
237 }
263 }
238 }
264 }
239 }
265 }
@@ -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
276+ }
277+
278+ var index = previewListView.currentIndex
279+ previewListView.currentIndex = -1
280+ previewListView.currentIndex = index
281+ previewListView.init = false
282+ }
246− previewLoader.previewData = preview
283+ previewListView.currentItem.previewData = preview
247− previewLoader.open = true
248 }
284 }
249 }
285 }
250
286
@@ -252,18 +288,9 @@ ScopeView {
252 id: previewDelegateMapper
288 id: previewDelegateMapper
253 }
289 }
254
290
255− Connections {
291+ ListView {
256− ignoreUnknownSignals: true
292+ id: previewListView
257− target: previewLoader.valid ? previewLoader.item : null
293+ objectName: "previewListView"
258− onClose: {
259− previewLoader.open = false
260− }
261− }
262−
263− Loader {
264− objectName: "previewLoader"
265− id: 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
273 }
300 }
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
308+ cacheBuffer: 0
309+
310+ // To be set before opening the preview
311+ property string categoryId: ""
312+ property var categoryDelegate
313+
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
318+
319+ // Used internally
320+ property int oldRow: -1
321+ property bool categoryWasFiltered: false
322+ property int initialContenY: 0
323+
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)
329+ if (oldRow != -1) {
330+ if (row < oldRow) {
331+ categoryView.contentY -= categoryDelegate.cellHeight
332+ } else if (row > oldRow){
333+ categoryView.contentY += categoryDelegate.cellHeight
334+ }
335+ }
336+ }
337+ oldRow = row;
338+ if (categoryDelegate.collapsedRowCount <= row) {
339+ categoryView.expandedCategoryId = categoryId
340+ }
341+
342+ if (open) {
343+ categoryDelegate.highlightIndex = currentIndex
344+ }
345+ }
275
346
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
279
349
280 onOpenChanged: {
350 onOpenChanged: {
281 if (open) {
351 if (open) {
282 onScreen = true
352 onScreen = true
353+ categoryDelegate.highlightIndex = currentIndex
354+ } else {
355+ model = undefined
356+ categoryView.correctExpandedCategory();
357+ categoryDelegate.highlightIndex = -1
283 }
358 }
284 }
359 }
285
360
361+ Rectangle {
362+ anchors.fill: parent
363+ color: Qt.rgba(0, 0, 0, .3)
364+ }
365+
366+ delegate: Loader {
367+ id: previewLoader
368+ objectName: "previewLoader"
369+ height: previewListView.height
370+ width: previewListView.width
371+ asynchronous: true
372+ source: previewListView.onScreen ?
373+ (previewData !== undefined ? previewDelegateMapper.map(previewData.rendererName) : "DashPreviewPlaceholder.qml") : ""
374+
375+ onPreviewDataChanged: {
376+ if (previewData !== undefined && source.toString().indexOf("DashPreviewPlaceholder.qml") != -1) {
377+ previewLoader.opacity = 0;
378+ }
379+ }
380+
381+ onSourceChanged: {
382+ if (previewData !== undefined) {
383+ fadeIn.start()
384+ }
385+ }
386+
387+ PropertyAnimation {
388+ id: fadeIn
389+ target: previewLoader
390+ property: "opacity"
391+ from: 0.0
392+ to: 1.0
393+ duration: UbuntuAnimation.BriskDuration
394+ }
395+
396+ property var previewData
397+ property bool valid: item !== null
398+
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 })
402+ }
403+ }
404+
405+ Connections {
406+ ignoreUnknownSignals: true
407+ target: item
408+ onClose: {
409+ previewListView.open = false
410+ }
411+ }
412+ }
413+ }
414+
415+ Image {
416+ anchors {
417+ top: previewListView.bottom
418+ left: parent.left
419+ leftMargin: previewListView.categoryDelegate.highlightCentered ?
420+ (parent.width - width) / 2 :
421+ (highlightColumn * cellWidth) + (-width + cellWidth + margins) / 2
422+ Behavior on leftMargin {
423+ UbuntuNumberAnimation {}
424+ }
288 }
425 }
426+ height: units.gu(1)
427+ width: units.gu(2)
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
433+
434+ source: "graphics/tooltip_arrow.png"
289 }
435 }
290
436
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
297 }
443 }
298 onClicked: {
444 onClicked: {
299− previewLoader.open = false;
445+ previewListView.open = false;
300 }
446 }
301 }
447 }
302}
448}
Dash/SearchableResultModel.qml+4
@@ -25,6 +25,10 @@ SortFilterProxyModel {
25
25
26 filterRole: CategoryResults.RoleTitle
26 filterRole: CategoryResults.RoleTitle
27
27
28+ function get(index) {
29+ return model.get(mapToSource(index))
30+ }
31+
28 onSearchQueryChanged: {
32 onSearchQueryChanged: {
29 if (searchQuery.length == 0) {
33 if (searchQuery.length == 0) {
30 filterRegExp = RegExp("");
34 filterRegExp = RegExp("");
Dash/graphics/tooltip_arrow@27.pngnew file

Binary file not shown.

tests/qmltests/Dash/tst_GenericScopeView.qml+6−6
@@ -44,25 +44,25 @@ Item {
44
44
45 function test_isCurrent() {
45 function test_isCurrent() {
46 var pageHeader = findChild(genericScopeView, "pageHeader");
46 var pageHeader = findChild(genericScopeView, "pageHeader");
47− var previewLoader = findChild(genericScopeView, "previewLoader");
47+ var previewListView = findChild(genericScopeView, "previewListView");
48 genericScopeView.isCurrent = true
48 genericScopeView.isCurrent = true
49 pageHeader.searchQuery = "test"
49 pageHeader.searchQuery = "test"
50− previewLoader.open = true
50+ previewListView.open = true
51 genericScopeView.isCurrent = false
51 genericScopeView.isCurrent = false
52 tryCompare(pageHeader, "searchQuery", "")
52 tryCompare(pageHeader, "searchQuery", "")
53 tryCompare(genericScopeView, "previewShown", false);
53 tryCompare(genericScopeView, "previewShown", false);
54 }
54 }
55
55
56 function test_showDash() {
56 function test_showDash() {
57− var previewLoader = findChild(genericScopeView, "previewLoader");
57+ var previewListView = findChild(genericScopeView, "previewListView");
58− previewLoader.open = true;
58+ previewListView.open = true;
59 scopes.get(0).showDash();
59 scopes.get(0).showDash();
60 tryCompare(genericScopeView, "previewShown", false);
60 tryCompare(genericScopeView, "previewShown", false);
61 }
61 }
62
62
63 function test_hideDash() {
63 function test_hideDash() {
64− var previewLoader = findChild(genericScopeView, "previewLoader");
64+ var previewListView = findChild(genericScopeView, "previewListView");
65− previewLoader.open = true;
65+ previewListView.open = true;
66 scopes.get(0).hideDash();
66 scopes.get(0).hideDash();
67 tryCompare(genericScopeView, "previewShown", false);
67 tryCompare(genericScopeView, "previewShown", false);
68 }
68 }