on xenial help sign in
add tests for switching previews
c560489Michael Zanetti
Components/Carousel.qml+27−9
@@ -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+ /// exposes the delegate of the currentItem
51+ readonly property alias currentItem: listView.currentItem
52+ /// exposes the distance to the next row (only one row in carousel, so it's the topMargins
53+ readonly property alias verticalSpacing: listView.verticalMargin
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: disabledNewContentX
81 property real newContentX: disabledNewContentX
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'],
@@ -143,6 +150,7 @@ Item {
143 orientation: ListView.Horizontal
150 orientation: ListView.Horizontal
144
151
145 function itemClicked(index, delegateItem) {
152 function itemClicked(index, delegateItem) {
153+ listView.currentIndex = index
146 var x = CarouselJS.getXFromContinuousIndex(index,
154 var x = CarouselJS.getXFromContinuousIndex(index,
147 realWidth,
155 realWidth,
148 realContentWidth,
156 realContentWidth,
@@ -151,11 +159,11 @@ Item {
151 gapToEndPhase,
159 gapToEndPhase,
152 carousel.drawBuffer)
160 carousel.drawBuffer)
153
161
154− if (Math.abs(x - contentX) < 1) {
162+ if (Math.abs(x - contentX) < 1 && delegateItem !== undefined) {
155 /* We're clicking the selected item and
163 /* We're clicking the selected item and
156 we're in the neighbourhood of radius 1 pixel from it.
164 we're in the neighbourhood of radius 1 pixel from it.
157 Let's emit the clicked signal. */
165 Let's emit the clicked signal. */
158− carousel.clicked(index, delegateItem, delegateItem.y)
166+ carousel.clicked(index, listView.model, delegateItem.y)
159 return
167 return
160 }
168 }
161
169
@@ -175,11 +183,11 @@ Item {
175 gapToEndPhase,
183 gapToEndPhase,
176 carousel.drawBuffer);
184 carousel.drawBuffer);
177
185
178− if (Math.abs(x - contentX) < 1) {
186+ if (Math.abs(x - contentX) < 1 && delegateItem !== undefined) {
179 /* We're pressAndHold the selected item and
187 /* We're pressAndHold the selected item and
180 we're in the neighbourhood of radius 1 pixel from it.
188 we're in the neighbourhood of radius 1 pixel from it.
181 Let's emit the pressAndHold signal. */
189 Let's emit the pressAndHold signal. */
182− carousel.pressAndHold(index, delegateItem, delegateItem.y);
190+ carousel.pressAndHold(index, listView.model, delegateItem.y);
183 return;
191 return;
184 }
192 }
185
193
@@ -190,6 +198,12 @@ Item {
190 newContentXAnimation.start();
198 newContentXAnimation.start();
191 }
199 }
192
200
201+ onHighlightIndexChanged: {
202+ if (highlightIndex != -1) {
203+ itemClicked(highlightIndex)
204+ }
205+ }
206+
193 onMovementStarted: {
207 onMovementStarted: {
194 stepAnimation.stop()
208 stepAnimation.stop()
195 newContentXAnimation.stop()
209 newContentXAnimation.stop()
@@ -273,14 +287,18 @@ Item {
273 itemTranslationScale,
287 itemTranslationScale,
274 listView.maximumItemTranslation)
288 listView.maximumItemTranslation)
275
289
290+ readonly property real xTransform: listView.viewTranslation + translationX * listView.scaleFactor
291+ readonly property real center: x - listView.contentX + xTransform - drawBuffer + (width/2)
292+
276 width: listView.tileWidth
293 width: listView.tileWidth
277 height: listView.tileHeight
294 height: listView.tileHeight
278 scale: itemScale * explicitScaleFactor
295 scale: itemScale * explicitScaleFactor
279 sourceComponent: itemComponent
296 sourceComponent: itemComponent
280 z: cachedTiles - Math.abs(index - listView.selectedIndex)
297 z: cachedTiles - Math.abs(index - listView.selectedIndex)
298+ opacity: highlightIndex == -1 ? 1 : (highlightIndex == index ? 0.6 : 0.2)
281
299
282 transform: Translate {
300 transform: Translate {
283− x: listView.viewTranslation + translationX * listView.scaleFactor
301+ x: xTransform
284 }
302 }
285
303
286 Behavior on explicitScaleFactor {
304 Behavior on explicitScaleFactor {
Components/FilterGrid.qml+3
@@ -54,6 +54,7 @@ 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
@@ -62,6 +63,8 @@ Item {
62 readonly property alias flicking: iconTileGrid.flicking
63 readonly property alias flicking: iconTileGrid.flicking
63 readonly property alias moving: iconTileGrid.moving
64 readonly property alias moving: iconTileGrid.moving
64 readonly property alias pressDelay: iconTileGrid.pressDelay
65 readonly property alias pressDelay: iconTileGrid.pressDelay
66+ property alias highlightIndex: iconTileGrid.highlightIndex
67+ readonly property alias currentItem: iconTileGrid.currentItem
65
68
66 height: !filterAnimation.running ? childrenRect.height : height
69 height: !filterAnimation.running ? childrenRect.height : height
67 clip: filterAnimation.running
70 clip: filterAnimation.running
Components/LazyImage.qml+2−2
@@ -100,10 +100,10 @@ Item {
100 verticalAlignment: Image.AlignVCenter
100 verticalAlignment: Image.AlignVCenter
101 sourceSize.width: root.scaleTo == "width" ? root.width
101 sourceSize.width: root.scaleTo == "width" ? root.width
102 : root.scaleTo == "fit" && root.width < root.height ? root.width
102 : root.scaleTo == "fit" && root.width < root.height ? root.width
103− : undefined
103+ : 0
104 sourceSize.height: root.scaleTo == "height" ? root.height
104 sourceSize.height: root.scaleTo == "height" ? root.height
105 : root.scale == "fit" && root.height < root.width ? root.height
105 : root.scale == "fit" && root.height < root.width ? root.height
106− : undefined
106+ : 0
107 }
107 }
108 }
108 }
109
109
Components/PageHeader.qml+4
@@ -48,6 +48,10 @@ Item {
48 searchField.focus = false;
48 searchField.focus = false;
49 }
49 }
50
50
51+ function unfocus() {
52+ searchField.focus = false;
53+ }
54+
51 Connections {
55 Connections {
52 target: greeter
56 target: greeter
53 onShownChanged: if (shown) resetSearch()
57 onShownChanged: if (shown) resetSearch()
Components/ResponsiveGridView.qml+10
@@ -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
@@ -41,6 +42,8 @@ Item {
41 readonly property alias pressDelay: gridView.pressDelay
42 readonly property alias pressDelay: gridView.pressDelay
42 property alias delegateCreationBegin: gridView.delegateCreationBegin
43 property alias delegateCreationBegin: gridView.delegateCreationBegin
43 property alias delegateCreationEnd: gridView.delegateCreationEnd
44 property alias delegateCreationEnd: gridView.delegateCreationEnd
45+ property alias highlightIndex: gridView.highlightIndex
46+ readonly property alias currentItem: gridView.currentItem
44
47
45 function contentHeightForRows(rows) {
48 function contentHeightForRows(rows) {
46 return rows * cellHeight + verticalSpacing
49 return rows * cellHeight + verticalSpacing
@@ -77,8 +80,15 @@ Item {
77 property real horizontalSpacing: spacingForColumns(columns)
80 property real horizontalSpacing: spacingForColumns(columns)
78 property real verticalSpacing: horizontalSpacing
81 property real verticalSpacing: horizontalSpacing
79 property int margin: allocatableHorizontalSpace - columns * horizontalSpacing
82 property int margin: allocatableHorizontalSpace - columns * horizontalSpacing
83+ property int highlightIndex: -1
80
84
81 cellWidth: delegateWidth + horizontalSpacing
85 cellWidth: delegateWidth + horizontalSpacing
82 cellHeight: delegateHeight + verticalSpacing
86 cellHeight: delegateHeight + verticalSpacing
87+
88+ onHighlightIndexChanged: {
89+ if (highlightIndex != -1) {
90+ currentIndex = highlightIndex
91+ }
92+ }
83 }
93 }
84}
94}
Components/Tile.qml+9
@@ -26,5 +26,14 @@ AbstractButton {
26 property int imageHeight
26 property int imageHeight
27 property int maximumLineCount: 1
27 property int maximumLineCount: 1
28
28
29+ readonly property int center: (index % GridView.view.columns * width) + (width / 2)
30+
31+ opacity: GridView.view.highlightIndex === -1 ? 1 :
32+ GridView.view.highlightIndex === index ? 0.6 : 0.2
33+
34+ Behavior on opacity {
35+ UbuntuNumberAnimation {}
36+ }
37+
29 style: TileStyle {}
38 style: TileStyle {}
30}
39}
Dash/Apps/AppPreview.qml+9
@@ -44,6 +44,15 @@ GenericPreview {
44 model: previewData.infoMap["more-screenshots"] != null ? previewData.infoMap["more-screenshots"].value : [previewData.image]
44 model: previewData.infoMap["more-screenshots"] != null ? previewData.infoMap["more-screenshots"].value : [previewData.image]
45 cacheBuffer: width * 3
45 cacheBuffer: width * 3
46
46
47+ // FIXME: Because of ListViews inside ListViews inside Flickables inside ListViews (and some more)
48+ // we finally reached the point where this ListView doesn't correctly get swipe input any more but
49+ // instead the parent ListView is the one that is swiped. This MouseArea sort of creates a blocking
50+ // layer to make sure this ListView can be swiped, regardless of what's behind it.
51+ MouseArea {
52+ anchors.fill: parent
53+ enabled: parent.contentWidth > parent.width
54+ }
55+
47 LazyImage {
56 LazyImage {
48 objectName: "placeholderScreenshot"
57 objectName: "placeholderScreenshot"
49 anchors {
58 anchors {
Dash/Apps/ApplicationsFilterGrid.qml+14−5
@@ -19,7 +19,7 @@ import "../../Components"
19import "../../Applications"
19import "../../Applications"
20
20
21FilterGrid {
21FilterGrid {
22− id: filterGrid
22+ id: filtergrid
23
23
24 filter: false
24 filter: false
25 minimumHorizontalSpacing: units.gu(0.5)
25 minimumHorizontalSpacing: units.gu(0.5)
@@ -28,9 +28,11 @@ FilterGrid {
28 delegateHeight: units.gu(9.5)
28 delegateHeight: units.gu(9.5)
29 verticalSpacing: units.gu(2)
29 verticalSpacing: units.gu(2)
30
30
31− signal clicked(int index, variant data)
31+ signal clicked(int index, var model, real itemY)
32+ signal pressAndHold(int index, var model, real itemY)
32
33
33 delegate: Tile {
34 delegate: Tile {
35+ id: tile
34 objectName: "delegate" + index
36 objectName: "delegate" + index
35 Application {
37 Application {
36 id: application
38 id: application
@@ -44,12 +46,19 @@ FilterGrid {
44
46
45 property string icon: model.icon ? model.icon : "../../graphics/applicationIcons/" + application.icon + ".png" // FIXME: this is temporary
47 property string icon: model.icon ? model.icon : "../../graphics/applicationIcons/" + application.icon + ".png" // FIXME: this is temporary
46
48
47− width: filterGrid.cellWidth
49+ width: filtergrid.cellWidth
48− height: filterGrid.cellHeight
50+ height: filtergrid.cellHeight
49 text: model.title ? model.title : application.name // FIXME: this is temporary
51 text: model.title ? model.title : application.name // FIXME: this is temporary
50 imageWidth: units.gu(8)
52 imageWidth: units.gu(8)
51 imageHeight: units.gu(7.5)
53 imageHeight: units.gu(7.5)
52 source: icon
54 source: icon
53− onClicked: filterGrid.clicked(index, application.desktopFile);
55+
56+ onClicked: {
57+ filtergrid.clicked(index, filtergrid.model, tile.y)
58+ }
59+
60+ onPressAndHold: {
61+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
62+ }
54 }
63 }
55}
64}
Dash/Apps/DashPluginFilterGrid.qml+2−4
@@ -37,13 +37,11 @@ GenericFilterGrid {
37 }
37 }
38
38
39 onClicked: {
39 onClicked: {
40− var data = { model: model }
41− filtergrid.clicked(index, data, tile.y)
40+ filtergrid.clicked(index, filtergrid.model, tile.y)
42 }
41 }
43
42
44 onPressAndHold: {
43 onPressAndHold: {
45− var data = { model: model }
46− filtergrid.pressAndHold(index, data, tile.y)
44+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
47 }
45 }
48 }
46 }
49}
47}
Dash/DashPreview.qml+1−3
@@ -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 readonly property int columnWidth: narrowMode ? contentRow.width : (contentRow.width / 3) - contentRow.spacing
36 readonly property int columnWidth: narrowMode ? contentRow.width : (contentRow.width / 3) - contentRow.spacing
37 readonly property int contentSpacing: units.gu(3)
37 readonly property int contentSpacing: units.gu(3)
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) {
@@ -61,7 +60,6 @@ Rectangle {
61 anchors.fill: parent
60 anchors.fill: parent
62 }
61 }
63
62
64−
65 Row {
63 Row {
66 id: contentRow
64 id: contentRow
67 anchors {
65 anchors {
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
@@ -42,13 +42,11 @@ FilterGrid {
42 source: model.icon
42 source: model.icon
43
43
44 onClicked: {
44 onClicked: {
45− var data = { model: model }
46− filtergrid.clicked(index, data, tile.y)
45+ filtergrid.clicked(index, filtergrid.model, tile.y)
47 }
46 }
48
47
49 onPressAndHold: {
48 onPressAndHold: {
50− var data = { model: model }
51− filtergrid.pressAndHold(index, data, tile.y)
49+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
52 }
50 }
53 }
51 }
54}
52}
Dash/Generic/WeatherFilterGrid.qml+2−4
@@ -37,13 +37,11 @@ GenericFilterGrid {
37 style: FlatTileStyle {}
37 style: FlatTileStyle {}
38
38
39 onClicked: {
39 onClicked: {
40− var data = { model: model }
41− filtergrid.clicked(index, data, tile.y)
40+ filtergrid.clicked(index, filtergrid.model, tile.y)
42 }
41 }
43
42
44 onPressAndHold: {
43 onPressAndHold: {
45− var data = { model: model }
46− filtergrid.pressAndHold(index, data, tile.y)
44+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
47 }
45 }
48 }
46 }
49}
47}
Dash/GenericScopeView.qml+220−59
@@ -21,14 +21,14 @@ 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 property bool enableHeightBehaviorOnNextCreation: false
25 property bool enableHeightBehaviorOnNextCreation: false
26
26
27 moving: categoryView.moving
27 moving: categoryView.moving
28
28
29 onIsCurrentChanged: {
29 onIsCurrentChanged: {
30 pageHeader.resetSearch();
30 pageHeader.resetSearch();
31− previewLoader.open = false;
31+ previewListView.open = false;
32 }
32 }
33
33
34 onMovementStarted: categoryView.showHeader()
34 onMovementStarted: categoryView.showHeader()
@@ -57,9 +57,8 @@ ScopeView {
57
57
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;
62− onActivated: previewLoader.closePreviewSpinner();
63 }
62 }
64
63
65 ScopeListView {
64 ScopeListView {
@@ -67,12 +66,18 @@ ScopeView {
67 objectName: "categoryListView"
66 objectName: "categoryListView"
68 anchors.fill: parent
67 anchors.fill: parent
69 model: scopeView.categories
68 model: scopeView.categories
70− forceNoClip: previewLoader.onScreen
69+ forceNoClip: previewListView.onScreen
71
70
72 onAtYEndChanged: if (atYEnd) endReached()
71 onAtYEndChanged: if (atYEnd) endReached()
73 onMovingChanged: if (moving && atYEnd) endReached()
72 onMovingChanged: if (moving && atYEnd) endReached()
74
73
75 property string expandedCategoryId: ""
74 property string expandedCategoryId: ""
75+ signal correctExpandedCategory();
76+
77+ Behavior on contentY {
78+ enabled: previewListView.open
79+ UbuntuNumberAnimation {}
80+ }
76
81
77 delegate: ListItems.Base {
82 delegate: ListItems.Base {
78 id: baseItem
83 id: baseItem
@@ -124,33 +129,50 @@ ScopeView {
124 Connections {
129 Connections {
125 target: rendererLoader.item
130 target: rendererLoader.item
126 onClicked: {
131 onClicked: {
132+ // Prepare the preview in case activate() triggers a preview only
127 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;
139+
140+ var item = model.get(index);
141+
142+ if ((scopeView.scope.id == "applications.scope" && categoryId == "installed")
143+ || (scopeView.scope.id == "home.scope" && categoryId == "applications.scope")) {
128− scopeView.scope.activate(delegateItem.model.uri,
144+ scopeView.scope.activate(item.uri, item.icon, item.category, 0, item.mimetype, item.title,
129− delegateItem.model.icon,
145+ item.comment, item.dndUri, item.metadata)
130− delegateItem.model.category,
146+ } else {
131− 0,
147+ previewListView.open = true
132− delegateItem.model.mimetype,
148+
133− delegateItem.model.title,
149+ scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title,
134− delegateItem.model.comment,
150+ item.comment, item.dndUri, item.metadata)
135− delegateItem.model.dndUri,
151+ }
136− delegateItem.model.metadata)
137 }
152 }
138 onPressAndHold: {
153 onPressAndHold: {
139 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
161+
162+ var item = model.get(index)
140− scopeView.scope.preview( delegateItem.model.uri,
163+ scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title,
141− delegateItem.model.icon,
164+ item.comment, item.dndUri, item.metadata)
142− delegateItem.model.category,
143− 0,
144− delegateItem.model.mimetype,
145− delegateItem.model.title,
146− delegateItem.model.comment,
147− delegateItem.model.dndUri,
148− delegateItem.model.metadata)
149 }
165 }
150 }
166 }
151 Connections {
167 Connections {
152 target: categoryView
168 target: categoryView
153 onExpandedCategoryIdChanged: {
169 onExpandedCategoryIdChanged: {
170+ collapseAllButExpandedCategory();
171+ }
172+ onCorrectExpandedCategory: {
173+ collapseAllButExpandedCategory();
174+ }
175+ function collapseAllButExpandedCategory() {
154 var item = rendererLoader.item;
176 var item = rendererLoader.item;
155 if (item.expandable) {
177 if (item.expandable) {
156 var shouldFilter = categoryId != categoryView.expandedCategoryId;
178 var shouldFilter = categoryId != categoryView.expandedCategoryId;
@@ -158,13 +180,15 @@ ScopeView {
158 // 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
159 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
181 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
160 var growingVisible = !shouldFilter && y + height < categoryView.height;
182 var growingVisible = !shouldFilter && y + height < categoryView.height;
183+ if (!previewListView.open || !shouldFilter) {
161− if (shrinkingVisible || growingVisible) {
184+ if (shrinkingVisible || growingVisible) {
162− item.startFilterAnimation(shouldFilter)
185+ item.startFilterAnimation(shouldFilter)
163− } else {
186+ } else {
164− item.filter = shouldFilter;
187+ item.filter = shouldFilter;
165− }
188+ }
166− if (!shouldFilter) {
189+ if (!shouldFilter && !previewListView.open) {
167− categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
190+ categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
191+ }
168 }
192 }
169 }
193 }
170 }
194 }
@@ -270,6 +294,7 @@ ScopeView {
270
294
271 OpenEffect {
295 OpenEffect {
272 id: effect
296 id: effect
297+ objectName: "openEffect"
273 anchors {
298 anchors {
274 fill: parent
299 fill: parent
275 bottomMargin: -bottomOverflow
300 bottomMargin: -bottomOverflow
@@ -282,29 +307,44 @@ ScopeView {
282 topOpacity: (1 - gap * 1.2)
307 topOpacity: (1 - gap * 1.2)
283 bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
308 bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
284 bottomOverflow: units.gu(20)
309 bottomOverflow: units.gu(20)
285− bottomOpacity: 1 - (gap * 0.8)
310+ live: !expansionAnimation.running
286
311
287 property int targetBottomGapPx: height - units.gu(8) - bottomOverflow
312 property int targetBottomGapPx: height - units.gu(8) - bottomOverflow
288− property real gap: previewLoader.open ? 1.0 : 0.0
313+ property real gap: previewListView.open ? 1.0 : 0.0
289
314
290 Behavior on gap {
315 Behavior on gap {
291 NumberAnimation {
316 NumberAnimation {
317+ id: expansionAnimation
292 duration: 200
318 duration: 200
293 easing.type: Easing.InOutQuad
319 easing.type: Easing.InOutQuad
294 onRunningChanged: {
320 onRunningChanged: {
295− if (!previewLoader.open && !running) {
321+ if (!previewListView.open && !running) {
296− previewLoader.onScreen = false
322+ previewListView.onScreen = false
297 }
323 }
298 }
324 }
299 }
325 }
300 }
326 }
327+ Behavior on positionPx {
328+ enabled: previewListView.open
329+ UbuntuNumberAnimation {}
330+ }
301 }
331 }
302
332
303 Connections {
333 Connections {
304 target: scopeView.scope
334 target: scopeView.scope
305 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
340+ }
341+
342+ var index = previewListView.currentIndex
343+ previewListView.currentIndex = -1
344+ previewListView.currentIndex = index
345+ previewListView.init = false
346+ }
306− previewLoader.previewData = preview
347+ previewListView.currentItem.previewData = preview
307− previewLoader.open = true
308 }
348 }
309 }
349 }
310
350
@@ -312,18 +352,9 @@ ScopeView {
312 id: previewDelegateMapper
352 id: previewDelegateMapper
313 }
353 }
314
354
315− Connections {
355+ ListView {
316− ignoreUnknownSignals: true
356+ id: previewListView
317− target: previewLoader.valid ? previewLoader.item : null
357+ objectName: "previewListView"
318− onClose: {
319− previewLoader.open = false
320− }
321− }
322−
323− Loader {
324− objectName: "previewLoader"
325− id: previewLoader
326− property var previewData
327 height: effect.bottomGapPx - effect.topGapPx
358 height: effect.bottomGapPx - effect.topGapPx
328 anchors {
359 anchors {
329 top: parent.top
360 top: parent.top
@@ -331,38 +362,168 @@ ScopeView {
331 left: parent.left
362 left: parent.left
332 right: parent.right
363 right: parent.right
333 }
364 }
334− 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
372+ cacheBuffer: 0
373+
374+ // To be set before opening the preview
375+ property string categoryId: ""
376+ property var categoryDelegate
377+
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
382+
383+ onCurrentIndexChanged: {
384+ var row = Math.floor(currentIndex / categoryDelegate.columns);
385+ if (categoryDelegate.collapsedRowCount <= row) {
386+ categoryView.expandedCategoryId = categoryId
387+ }
388+
389+ if (open) {
390+ categoryDelegate.highlightIndex = currentIndex
391+ }
392+
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)
396+ }
397+
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;
403+ newContentY = 0;
404+ }
405+ if (newContentY > Math.max(0, categoryView.contentHeight - categoryView.height)) {
406+ effectAdjust += -(categoryView.contentHeight - categoryView.height) + newContentY
407+ newContentY = categoryView.contentHeight - categoryView.height;
408+ }
409+
410+ effect.positionPx = effectAdjust;
411+ categoryView.contentY = newContentY - categoryView.originY;
412+ }
335
413
336 property bool open: false
414 property bool open: false
337 property bool onScreen: false
415 property bool onScreen: false
338− property bool valid: item !== null
339
416
340 onOpenChanged: {
417 onOpenChanged: {
341 if (open) {
418 if (open) {
342− onScreen = true
419+ onScreen = true;
420+ categoryDelegate.highlightIndex = currentIndex;
421+ pageHeader.unfocus();
422+ } else {
423+ // Cancel any pending preview requests or actions
424+ if (previewListView.currentItem.previewData !== undefined) {
425+ previewListView.currentItem.previewData.cancelAction();
426+ }
427+ scopeView.scope.cancelActivation();
428+ model = undefined;
429+ categoryView.correctExpandedCategory();
430+ categoryDelegate.highlightIndex = -1;
343 }
431 }
344 }
432 }
345
433
434+ Rectangle {
435+ anchors.fill: parent
436+ color: Qt.rgba(0, 0, 0, .3)
437+ z: -1
438+ }
439+
440+ delegate: Loader {
441+ id: previewLoader
442+ objectName: "previewLoader" + index
443+ height: previewListView.height
444+ width: previewListView.width
445+ asynchronous: true
446+ source: previewListView.onScreen ?
447+ (previewData !== undefined ? previewDelegateMapper.map(previewData.rendererName) : "DashPreviewPlaceholder.qml") : ""
448+
449+ onPreviewDataChanged: {
450+ if (previewData !== undefined && source.toString().indexOf("DashPreviewPlaceholder.qml") != -1) {
451+ previewLoader.opacity = 0;
452+ }
453+ }
454+
455+ onSourceChanged: {
456+ if (previewData !== undefined) {
457+ fadeIn.start()
458+ }
459+ }
460+
461+ PropertyAnimation {
462+ id: fadeIn
463+ target: previewLoader
464+ property: "opacity"
465+ from: 0.0
466+ to: 1.0
467+ duration: UbuntuAnimation.BriskDuration
468+ }
469+
470+ property var previewData
471+ property bool valid: item !== null
472+
346− onLoaded: {
473+ onLoaded: {
474+ if (previewListView.onScreen && previewData !== undefined) {
347− item.previewData = Qt.binding(function() { return previewLoader.previewData })
475+ item.previewData = Qt.binding(function() { return previewData })
476+ }
477+ }
478+
479+ Connections {
480+ ignoreUnknownSignals: true
481+ target: item
482+ onClose: {
483+ previewListView.open = false
484+ }
485+ }
486+
487+ function closePreviewSpinner() {
488+ if(item) {
489+ item.showProcessingAction = false;
490+ }
491+ }
348 }
492 }
493+ }
494+
495+ Image {
496+ objectName: "pointerArrow"
497+ anchors {
498+ top: previewListView.bottom
499+ left: parent.left
500+ leftMargin: previewListView.categoryDelegate !== undefined && previewListView.categoryDelegate.currentItem ?
501+ previewListView.categoryDelegate.currentItem.center + (-width + margins) / 2 : 0
349
502
350− function closePreviewSpinner() {
503+ Behavior on leftMargin {
351− if(item) {
504+ SmoothedAnimation {
352− item.showProcessingAction = false;
505+ duration: UbuntuAnimation.FastDuration
506+ }
353 }
507 }
354 }
508 }
509+ height: units.gu(1)
510+ width: units.gu(2)
511+ property int margins: previewListView.categoryDelegate ? previewListView.categoryDelegate.margins : 0
512+ opacity: previewListView.open ? .5 : 0
513+
514+ source: "graphics/tooltip_arrow.png"
355 }
515 }
356
516
357 // TODO: Move as InverseMouseArea to DashPreview
517 // TODO: Move as InverseMouseArea to DashPreview
358 MouseArea {
518 MouseArea {
519+ objectName: "closePreviewMouseArea"
359− enabled: previewLoader.onScreen
520+ enabled: previewListView.onScreen
360 anchors {
521 anchors {
361 fill: parent
522 fill: parent
362 topMargin: effect.bottomGapPx
523 topMargin: effect.bottomGapPx
363 }
524 }
364 onClicked: {
525 onClicked: {
365− previewLoader.open = false;
526+ previewListView.open = false;
366 }
527 }
367 }
528 }
368}
529}
Dash/Music/AlbumTile.qmldeleted−116
@@ -1,116 +0,0 @@
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−AbstractButton {
21− id: root
22− property url source
23− property alias album: albumLabel.text
24− property alias artist: artistLabel.text
25− property alias iconWidth: icon.width
26− property alias iconHeight: icon.height
27−
28− UbuntuShape {
29− id: icon
30− anchors {
31− top: parent.top
32− horizontalCenter: parent.horizontalCenter
33− }
34− radius: "medium"
35− image: Image {
36− source: root.source
37− sourceSize { width: icon.width; height: icon.height }
38− asynchronous: true
39− cache: false
40− }
41− }
42−
43− UbuntuShape {
44− id: borderPressed
45−
46− anchors.fill: icon
47− radius: "medium"
48− borderSource: "radius_pressed.sci"
49− opacity: root.pressed ? 1.0 : 0.0
50− Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutQuint } }
51− }
52−
53− Item {
54− id: artistContainer
55− anchors {
56− left: parent.left
57− right: parent.right
58− top: icon.bottom
59− }
60− height: units.gu(2)
61−
62− Label {
63− id: artistLabel
64− anchors {
65− baseline: parent.bottom
66− left: parent.left
67− right: parent.right
68− leftMargin: units.gu(1)
69− rightMargin: units.gu(1)
70− }
71−
72− color: Theme.palette.selected.backgroundText
73− opacity: 0.9
74− style: Text.Raised
75− styleColor: "black"
76− fontSize: "small"
77− elide: Text.ElideMiddle
78− horizontalAlignment: Text.AlignHCenter
79− }
80− }
81−
82− Item {
83− anchors {
84− left: parent.left
85− right: parent.right
86− top: artistContainer.bottom
87− }
88− height: units.gu(1.5)
89−
90− Label {
91− id: albumLabel
92− anchors {
93− baseline: parent.bottom
94− left: parent.left
95− right: parent.right
96− leftMargin: units.gu(1)
97− rightMargin: units.gu(1)
98− }
99−
100− color: Theme.palette.selected.backgroundText
101− opacity: 0.9
102− style: Text.Raised
103− styleColor: "black"
104− fontSize: "small"
105− font.weight: Font.Light
106− elide: Text.ElideMiddle
107− horizontalAlignment: Text.AlignHCenter
108− }
109− }
110−
111− GridView.onRemove: SequentialAnimation {
112− PropertyAction { target: root; property: "GridView.delayRemove"; value: true }
113− NumberAnimation { target: root; property: "opacity"; from: 1; to: 0; duration: 200; easing.type: Easing.InOutQuad }
114− PropertyAction { target: root; property: "GridView.delayRemove"; value: false }
115− }
116−}
Dash/Music/MusicFilterGrid.qml+2−4
@@ -44,13 +44,11 @@ GenericFilterGrid {
44 }
44 }
45
45
46 onClicked: {
46 onClicked: {
47− var data = { model: model }
48− filtergrid.clicked(index, data, tile.y)
47+ filtergrid.clicked(index, filtergrid.model, tile.y)
49 }
48 }
50
49
51 onPressAndHold: {
50 onPressAndHold: {
52− var data = { model: model }
53− filtergrid.pressAndHold(index, data, tile.y)
51+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
54 }
52 }
55 }
53 }
56}
54}
Dash/PreviewDelegateMapper.qml+1
@@ -27,6 +27,7 @@ QtObject {
27 }
27 }
28
28
29 function map(rendererName) {
29 function map(rendererName) {
30+ print("should map preview", rendererName);
30 var customPreview = d.previewDelegateMapping[rendererName]
31 var customPreview = d.previewDelegateMapping[rendererName]
31 if (customPreview != undefined) {
32 if (customPreview != undefined) {
32 return customPreview
33 return customPreview
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/Video/VideoFilterGrid.qml+2−4
@@ -42,13 +42,11 @@ GenericFilterGrid {
42 style: VideoTileStyle {}
42 style: VideoTileStyle {}
43
43
44 onClicked: {
44 onClicked: {
45− var data = { model: model }
46− filtergrid.clicked(index, data, tile.y)
45+ filtergrid.clicked(index, filtergrid.model, tile.y)
47 }
46 }
48
47
49 onPressAndHold: {
48 onPressAndHold: {
50− var data = { model: model }
51− filtergrid.pressAndHold(index, data, tile.y)
49+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
52 }
50 }
53 }
51 }
54}
52}
Dash/graphics/tooltip_arrow@27.pngnew file

Binary file not shown.

tests/mocks/Unity/fake_preview.cpp+5−1
@@ -22,7 +22,7 @@ Preview::Preview(QObject *parent): QObject(parent)
22
22
23QString Preview::rendererName() const
23QString Preview::rendererName() const
24{
24{
25− return "foo";
25+ return "generic-preview";
26}
26}
27
27
28QString Preview::title() const
28QString Preview::title() const
@@ -65,3 +65,7 @@ void Preview::execute(const QString& actionId, const QHash<QString, QVariant>& h
65 Q_UNUSED(actionId);
65 Q_UNUSED(actionId);
66 Q_UNUSED(hints);
66 Q_UNUSED(hints);
67}
67}
68+
69+void Preview::cancelAction()
70+{
71+}
tests/mocks/Unity/fake_preview.h+2
@@ -51,6 +51,8 @@ public:
51
51
52 Q_INVOKABLE void execute(const QString& actionId, const QHash<QString, QVariant>& hints);
52 Q_INVOKABLE void execute(const QString& actionId, const QHash<QString, QVariant>& hints);
53
53
54+ Q_INVOKABLE void cancelAction();
55+
54Q_SIGNALS:
56Q_SIGNALS:
55 void previewChanged();
57 void previewChanged();
56};
58};
tests/mocks/Unity/fake_scope.cpp+21−7
@@ -20,8 +20,6 @@
20
20
21#include <dee.h>
21#include <dee.h>
22
22
23−#include <QDebug>
24−
25static DeeModel* create_categories_model(unsigned category_count);
23static DeeModel* create_categories_model(unsigned category_count);
26static DeeModel* create_results_model(unsigned category_count, unsigned result_count);
24static DeeModel* create_results_model(unsigned category_count, unsigned result_count);
27
25
@@ -39,6 +37,13 @@ Scope::Scope(QObject* parent)
39 m_categories->setResultModel(results_model);
37 m_categories->setResultModel(results_model);
40 m_categories->setModel(create_categories_model(4));
38 m_categories->setModel(create_categories_model(4));
41 m_results->setModel(results_model);
39 m_results->setModel(results_model);
40+
41+ m_timer.setInterval(1000);
42+ m_timer.setSingleShot(true);
43+ connect(&m_timer, &QTimer::timeout, [this]() {
44+ Preview *p = new Preview(this);
45+ Q_EMIT previewReady(p);
46+ });
42}
47}
43
48
44Scope::Scope(QString const& id, QString const& name, bool visible, QObject* parent)
49Scope::Scope(QString const& id, QString const& name, bool visible, QObject* parent)
@@ -55,6 +60,13 @@ Scope::Scope(QString const& id, QString const& name, bool visible, QObject* pare
55 m_categories->setResultModel(results_model);
60 m_categories->setResultModel(results_model);
56 m_categories->setModel(create_categories_model(4));
61 m_categories->setModel(create_categories_model(4));
57 m_results->setModel(results_model);
62 m_results->setModel(results_model);
63+
64+ m_timer.setInterval(1000);
65+ m_timer.setSingleShot(true);
66+ connect(&m_timer, &QTimer::timeout, [this]() {
67+ Preview *p = new Preview(this);
68+ Q_EMIT previewReady(p);
69+ });
58}
70}
59
71
60QString Scope::id() const {
72QString Scope::id() const {
@@ -168,10 +180,8 @@ void Scope::activate(const QVariant &uri, const QVariant &icon_hint, const QVari
168 Q_UNUSED(comment);
180 Q_UNUSED(comment);
169 Q_UNUSED(dnd_uri);
181 Q_UNUSED(dnd_uri);
170 Q_UNUSED(metadata);
182 Q_UNUSED(metadata);
171− qDebug() << "foooooooooooooo activating" << uri;
172
183
173− Preview *p = new Preview();
184+ m_timer.start();
174− Q_EMIT previewReady(p);
175}
185}
176
186
177void Scope::preview(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
187void Scope::preview(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
@@ -188,8 +198,7 @@ void Scope::preview(const QVariant &uri, const QVariant &icon_hint, const QVaria
188 Q_UNUSED(dnd_uri);
198 Q_UNUSED(dnd_uri);
189 Q_UNUSED(metadata);
199 Q_UNUSED(metadata);
190
200
191− Preview *p = new Preview();
201+ m_timer.start();
192− Q_EMIT previewReady(p);
193}
202}
194
203
195static const gchar * categories_model_schema[] = {
204static const gchar * categories_model_schema[] = {
@@ -255,3 +264,8 @@ DeeModel* create_results_model(unsigned category_count, unsigned result_count) {
255 }
264 }
256 return results_model;
265 return results_model;
257}
266}
267+
268+void Scope::cancelActivation()
269+{
270+ m_timer.stop();
271+}
tests/mocks/Unity/fake_scope.h+7−2
@@ -17,10 +17,11 @@
17#ifndef FAKE_SCOPE_H
17#ifndef FAKE_SCOPE_H
18#define FAKE_SCOPE_H
18#define FAKE_SCOPE_H
19
19
20−// Qt
21−#include <QObject>
22#include "fake_categories.h"
20#include "fake_categories.h"
23
21
22+#include <QObject>
23+#include <QDebug>
24+
24class Preview;
25class Preview;
25
26
26class Scope : public QObject
27class Scope : public QObject
@@ -78,6 +79,8 @@ public:
78 const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
79 const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
79 const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata);
80 const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata);
80
81
82+ Q_INVOKABLE void cancelActivation();
83+
81Q_SIGNALS:
84Q_SIGNALS:
82 void idChanged(const QString&);
85 void idChanged(const QString&);
83 void nameChanged(const QString&);
86 void nameChanged(const QString&);
@@ -115,6 +118,8 @@ protected:
115
118
116 Categories* m_categories;
119 Categories* m_categories;
117 DeeListModel* m_results;
120 DeeListModel* m_results;
121+
122+ QTimer m_timer;
118};
123};
119
124
120#endif // FAKE_SCOPE_H
125#endif // FAKE_SCOPE_H
tests/mocks/Unity/fake_unity_plugin.cpp+2
@@ -22,6 +22,7 @@
22// local
22// local
23#include "fake_scopes.h"
23#include "fake_scopes.h"
24#include "fake_categories.h"
24#include "fake_categories.h"
25+#include "fake_preview.h"
25#include "categoryresults.h"
26#include "categoryresults.h"
26
27
27// External
28// External
@@ -37,6 +38,7 @@ void FakeUnityPlugin::registerTypes(const char *uri)
37
38
38 Q_ASSERT(uri == QLatin1String("Unity"));
39 Q_ASSERT(uri == QLatin1String("Unity"));
39
40
41+ qmlRegisterUncreatableType<Preview>(uri, 0, 1, "Preview", "Can't create Preview object in QML.");
40 qmlRegisterType<Scopes>(uri, 0, 1, "Scopes");
42 qmlRegisterType<Scopes>(uri, 0, 1, "Scopes");
41 qmlRegisterType<Scope>(uri, 0, 1, "Scope");
43 qmlRegisterType<Scope>(uri, 0, 1, "Scope");
42 qmlRegisterType<Categories>(uri, 0, 1, "Categories");
44 qmlRegisterType<Categories>(uri, 0, 1, "Categories");
tests/qmltests/Dash/tst_GenericScopeView.qml+119−9
@@ -23,6 +23,7 @@ import Ubuntu.Components 0.1
23import Unity.Test 0.1 as UT
23import Unity.Test 0.1 as UT
24
24
25Item {
25Item {
26+ id: shell
26 width: units.gu(120)
27 width: units.gu(120)
27 height: units.gu(80)
28 height: units.gu(80)
28
29
@@ -34,6 +35,11 @@ Item {
34 }
35 }
35 }
36 }
36
37
38+ property Item applicationManager: Item {
39+ signal sideStageFocusedApplicationChanged()
40+ signal mainStageFocusedApplicationChanged()
41+ }
42+
37 GenericScopeView {
43 GenericScopeView {
38 id: genericScopeView
44 id: genericScopeView
39 anchors.fill: parent
45 anchors.fill: parent
@@ -44,38 +50,142 @@ Item {
44
50
45 function test_isCurrent() {
51 function test_isCurrent() {
46 var pageHeader = findChild(genericScopeView, "pageHeader");
52 var pageHeader = findChild(genericScopeView, "pageHeader");
47− var previewLoader = findChild(genericScopeView, "previewLoader");
53+ var previewListView = findChild(genericScopeView, "previewListView");
48 genericScopeView.isCurrent = true
54 genericScopeView.isCurrent = true
49 pageHeader.searchQuery = "test"
55 pageHeader.searchQuery = "test"
50− previewLoader.open = true
56+ previewListView.open = true
51 genericScopeView.isCurrent = false
57 genericScopeView.isCurrent = false
52 tryCompare(pageHeader, "searchQuery", "")
58 tryCompare(pageHeader, "searchQuery", "")
53 tryCompare(genericScopeView, "previewShown", false);
59 tryCompare(genericScopeView, "previewShown", false);
54 }
60 }
55
61
56 function test_showDash() {
62 function test_showDash() {
57− var previewLoader = findChild(genericScopeView, "previewLoader");
63+ var previewListView = findChild(genericScopeView, "previewListView");
58− previewLoader.open = true;
64+ previewListView.open = true;
59 scopes.get(0).showDash();
65 scopes.get(0).showDash();
60 tryCompare(genericScopeView, "previewShown", false);
66 tryCompare(genericScopeView, "previewShown", false);
61 }
67 }
62
68
63 function test_hideDash() {
69 function test_hideDash() {
64− var previewLoader = findChild(genericScopeView, "previewLoader");
70+ var previewListView = findChild(genericScopeView, "previewListView");
65− previewLoader.open = true;
71+ previewListView.open = true;
66 scopes.get(0).hideDash();
72 scopes.get(0).hideDash();
67 tryCompare(genericScopeView, "previewShown", false);
73 tryCompare(genericScopeView, "previewShown", false);
68 }
74 }
69
75
76+ function openPreview() {
77+ var tile = findChild(genericScopeView, "delegate0");
78+ mouseClick(tile, tile.width / 2, tile.height / 2);
79+ var openEffect = findChild(genericScopeView, "openEffect");
80+ tryCompare(openEffect, "gap", 1);
81+ }
82+
83+ function checkArrowPosition(index) {
84+ var tile = findChild(genericScopeView, "delegate" + index);
85+ var tileCenter = tile.x + tile.width/2;
86+ var pointerArrow = findChild(genericScopeView, "pointerArrow");
87+ var pointerArrowCenter = pointerArrow.x + pointerArrow.width/2;
88+ compare(pointerArrowCenter, tileCenter, "Pointer did not move to tile");
89+ }
90+
91+ function closePreview() {
92+ var closePreviewMouseArea = findChild(genericScopeView, "closePreviewMouseArea");
93+ mouseClick(closePreviewMouseArea, closePreviewMouseArea.width / 2, closePreviewMouseArea.height / 2);
94+
95+ var previewListView = findChild(genericScopeView, "previewListView");
96+ tryCompare(previewListView, "open", false);
97+ var openEffect = findChild(genericScopeView, "openEffect");
98+ tryCompare(openEffect, "gap", 0);
99+
100+ var categoryListView = findChild(genericScopeView, "categoryListView");
101+ categoryListView.flick(0, units.gu(200));
102+ tryCompare(categoryListView, "flicking", false);
103+ }
104+
105+ function test_previewOpenClose() {
106+ var previewListView = findChild(genericScopeView, "previewListView");
107+ tryCompare(previewListView, "open", false);
108+
109+ openPreview();
110+
111+ // check for it opening successfully
112+ var currentPreviewItem = findChild(genericScopeView, "previewLoader0");
113+ tryCompareFunction(function() {
114+ var parts = currentPreviewItem.source.toString().split("/");
115+ var name = parts[parts.length - 1];
116+ return name == "DashPreviewPlaceholder.qml";
117+ },
118+ true);
119+ tryCompareFunction(function() {
120+ var parts = currentPreviewItem.source.toString().split("/");
121+ var name = parts[parts.length - 1];
122+ return name == "GenericPreview.qml";
123+ },
124+ true);
125+ tryCompare(currentPreviewItem, "progress", 1);
126+ tryCompare(previewListView, "open", true);
127+
128+ closePreview();
129+ tryCompare(previewListView, "open", false);
130+ }
131+
132+ function test_previewCycleOne() {
133+ var previewListView = findChild(genericScopeView, "previewListView");
134+ tryCompare(previewListView, "open", false);
135+
136+ openPreview();
137+
138+ // wait for it to be loaded
139+ var currentPreviewItem = findChild(genericScopeView, "previewLoader0");
140+ tryCompareFunction(function() {
141+ var parts = currentPreviewItem.source.toString().split("/");
142+ var name = parts[parts.length - 1];
143+ return name == "GenericPreview.qml";
144+ },
145+ true);
146+ tryCompare(currentPreviewItem, "progress", 1);
147+ waitForRendering(currentPreviewItem);
148+
149+ checkArrowPosition(0);
150+
151+ // flick to the next previews
152+
153+ for (var i = 1; i < previewListView.count; ++i) {
154+
155+ mouseFlick(previewListView, previewListView.width - units.gu(1),
156+ previewListView.height / 2,
157+ units.gu(2),
158+ previewListView.height / 2);
159+
160+ // wait for it to be loaded
161+ var nextPreviewItem = findChild(genericScopeView, "previewLoader" + i);
162+ tryCompareFunction(function() {
163+ var parts = nextPreviewItem.source.toString().split("/");
164+ var name = parts[parts.length - 1];
165+ return name == "GenericPreview.qml";
166+ },
167+ true);
168+ tryCompare(nextPreviewItem, "progress", 1);
169+ waitForRendering(nextPreviewItem);
170+
171+ checkArrowPosition(i);
172+ }
173+
174+ closePreview();
175+ }
176+
70 function test_show_spinner() {
177 function test_show_spinner() {
178+ openPreview();
71− var previewLoader = findChild(genericScopeView, "previewLoader");
179+ var previewListView = findChild(genericScopeView, "previewListView");
72− previewLoader.open = true;
180+ var previewLoader = findChild(genericScopeView, "previewLoader0");
73− previewLoader.source = "../../../Dash/Generic/GenericPreview.qml";
181+
74 previewLoader.item.showProcessingAction = true;
182 previewLoader.item.showProcessingAction = true;
75 var waitingForAction = findChild(genericScopeView, "waitingForActionMouseArea");
183 var waitingForAction = findChild(genericScopeView, "waitingForActionMouseArea");
76 tryCompare(waitingForAction, "enabled", true);
184 tryCompare(waitingForAction, "enabled", true);
77 previewLoader.closePreviewSpinner();
185 previewLoader.closePreviewSpinner();
78 tryCompare(waitingForAction, "enabled", false);
186 tryCompare(waitingForAction, "enabled", false);
187+
188+ closePreview();
79 }
189 }
80 }
190 }
81 }
191 }