on xenial help sign in
Connection data to the UI
77121cfDiego Sarmentero
Dash/Apps/AppInfo.qmlnew file+88
@@ -0,0 +1,88 @@
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+import "../../Components"
20+
21+Row {
22+ id: root
23+ property alias icon: image.source
24+ property alias appName: appNameLabel.text
25+ property alias rating: ratingStars.rating
26+ property int rated: 0
27+ property int reviews: 0
28+
29+ spacing: units.gu(2)
30+
31+ UbuntuShape {
32+ id: imageShape
33+ width: units.gu(6)
34+ height: units.gu(6)
35+ image: Image {
36+ id: image
37+ sourceSize { width: imageShape.width; height: imageShape.height }
38+ asynchronous: true
39+ fillMode: Image.PreserveAspectFit
40+ }
41+ }
42+
43+ Column {
44+ spacing: units.gu(1)
45+
46+ Label {
47+ id: appNameLabel
48+ fontSize: "large"
49+ color: "white"
50+ style: Text.Raised
51+ styleColor: "black"
52+ opacity: .9
53+ }
54+
55+ Row {
56+ spacing: units.gu(1)
57+
58+ RatingStars {
59+ id: ratingStars
60+ maximumRating: 10
61+ }
62+
63+ Label {
64+ id: ratedLabel
65+ objectName: "ratedLabel"
66+ fontSize: "medium"
67+ color: "white"
68+ style: Text.Raised
69+ styleColor: "black"
70+ opacity: .6
71+ //TRANSLATORS: Number of persons who rated this app
72+ text: i18n.tr("(%1)").arg(root.rated)
73+ }
74+
75+ Label {
76+ id: reviewsLabel
77+ objectName: "reviewsLabel"
78+ fontSize: "medium"
79+ color: "white"
80+ style: Text.Raised
81+ styleColor: "black"
82+ opacity: .6
83+ //TRANSLATORS: Number of persons who wrote reviews for this app
84+ text: i18n.tr("%1 review", "%1 reviews", root.reviews).arg(root.reviews)
85+ }
86+ }
87+ }
88+}
Dash/Apps/AppPreview.qmlnew file+216
@@ -0,0 +1,216 @@
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+import Ubuntu.Components.ListItems 0.1 as ListItem
20+import Ubuntu.DownloadDaemonListener 0.1
21+import ".."
22+import "../../Components"
23+import "../../Components/IconUtil.js" as IconUtil
24+
25+DashPreview {
26+ id: root
27+
28+ signal sendUserReview(string review)
29+
30+ title: root.previewData.title
31+
32+ header: ListView {
33+ id: screenshots
34+ spacing: units.gu(1)
35+ orientation: ListView.Horizontal
36+ height: units.gu(22)
37+ anchors {
38+ left: parent.left
39+ right: parent.right
40+ margins: units.gu(1)
41+ }
42+ model: previewData.infoMap["more-screenshots"]
43+
44+ delegate: UbuntuShape {
45+ id: item
46+ height: screenshots.height
47+ width: units.gu(12)
48+ radius: "medium"
49+ borderSource: ""
50+ image: Image {
51+ asynchronous: true
52+ sourceSize { width: item.width; height: item.height }
53+ source: model ? model.modelData : ""
54+ }
55+ }
56+
57+ MouseArea {
58+ id: mouseArea
59+ anchors.fill: parent
60+ }
61+ }
62+
63+ Component {
64+ id: buttonsComponent
65+
66+ GridView {
67+ id: buttonsGrid
68+ objectName: "gridButtons"
69+
70+ property int numOfRows: (count + 1) / 2
71+ property int spacing: units.gu(1)
72+ height: Math.max(units.gu(5), units.gu(5)*numOfRows + spacing*(numOfRows - 1))
73+
74+ cellWidth: Math.max(units.gu(9), width / 2)
75+ cellHeight: buttonHeight + spacing
76+ property int buttonWidth: count > 1 ? Math.max(0, width / 2 - spacing) : width
77+ property int buttonHeight: units.gu(5)
78+
79+ delegate: Button {
80+ width: Math.max(units.gu(4), buttonsGrid.buttonWidth)
81+ height: buttonsGrid.buttonHeight
82+ color: Theme.palette.selected.foreground
83+ text: modelData.displayName
84+ iconSource: modelData.iconHint
85+ iconPosition: "right"
86+ visible: true
87+ onClicked: {
88+ root.previewData.execute(modelData.id, { })
89+ }
90+ }
91+ focus: false
92+ }
93+ }
94+
95+ Component {
96+ id: progressComponent
97+
98+ ProgressBar {
99+ id: progressBar
100+ objectName: "progressBar"
101+ value: 0
102+ maximumValue: 100
103+ height: units.gu(5)
104+
105+ property var model
106+
107+ Label {
108+ anchors.left: parent.left
109+ anchors.verticalCenter: parent.verticalCenter
110+ anchors.leftMargin: units.gu(1)
111+ fontSize: "large"
112+ color: "white"
113+ style: Text.Raised
114+ styleColor: "black"
115+ opacity: .8
116+ text: progressBar.value + " %"
117+ }
118+
119+ DownloadTracker {
120+ dbusPath: root.previewData.infoMap["progressbar_source"] ? root.previewData.infoMap["progressbar_source"].value : ""
121+
122+ onProgress: {
123+ var percentage = parseInt(received * 100 / total);
124+ progressBar.value = percentage;
125+ }
126+
127+ onFinished: {
128+ root.previewData.execute(progressBar.model[0].id, { })
129+ }
130+ }
131+
132+ }
133+ }
134+
135+ buttons: Loader {
136+ anchors {
137+ left: parent.left
138+ right: parent.right
139+ }
140+
141+ sourceComponent: root.previewData.infoMap["show_progressbar"] ? progressComponent : buttonsComponent
142+
143+ onLoaded: {
144+ item.model = root.previewData.actions;
145+ }
146+ }
147+
148+ body: Column {
149+ spacing: units.gu(1)
150+
151+ AppInfo {
152+ objectName: "appInfo"
153+ appName: root.previewData.title
154+ icon: IconUtil.from_gicon(root.previewData.appIcon)
155+ rating: root.previewData.infoMap["rating"] ? root.previewData.infoMap["rating"].value : 0
156+ rated: root.previewData.infoMap["rated"] ? root.previewData.infoMap["rated"].value : 0
157+ reviews: root.previewData.infoMap["reviews"] ? root.previewData.infoMap["reviews"].value : 0
158+
159+ width: root.width
160+ }
161+
162+ Label {
163+ text: root.previewData.description
164+ fontSize: "medium"
165+ color: Theme.palette.selected.backgroundText
166+ opacity: .6
167+ width: parent.width
168+ wrapMode: Text.WordWrap
169+ style: Text.Raised
170+ styleColor: "black"
171+ }
172+
173+ ListItem.ThinDivider {}
174+
175+ Item {
176+ anchors {
177+ left: parent.left
178+ right: parent.right
179+ }
180+ height: rateLabel.height
181+
182+ Label {
183+ id: rateLabel
184+ fontSize: "medium"
185+ color: "white"
186+ style: Text.Raised
187+ styleColor: "black"
188+ opacity: .9
189+ text: i18n.tr("Rate this")
190+
191+ anchors.left: parent.left
192+ anchors.verticalCenter: parent.verticalCenter
193+ }
194+
195+ RatingStars {
196+ anchors.right: parent.right
197+ anchors.verticalCenter: parent.verticalCenter
198+ }
199+ }
200+
201+ ListItem.ThinDivider {}
202+
203+ AppReviews {
204+ objectName: "appReviews"
205+ anchors {
206+ left: parent.left
207+ right: parent.right
208+ }
209+
210+ model: root.previewData.infoMap["comments"] ? root.previewData.infoMap["comments"].value : undefined
211+
212+ onSendReview: root.sendUserReview(review);
213+ }
214+
215+ }
216+}
Dash/Apps/AppReviews.qmlnew file+203
@@ -0,0 +1,203 @@
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+import Ubuntu.Components.ListItems 0.1 as ListItem
20+import "../../Components"
21+
22+Column {
23+ id: root
24+
25+ property var model
26+
27+ signal sendReview(string review)
28+
29+ spacing: units.gu(2)
30+ state: ""
31+
32+ states: [
33+ State {
34+ name: ""
35+ PropertyChanges { target: sendButton; opacity: 0; }
36+ PropertyChanges { target: reviewField; width: root.width; }
37+ },
38+ State {
39+ name: "editing"
40+ PropertyChanges { target: reviewField; width: (root.width - row.spacing - sendButton.width); }
41+ PropertyChanges { target: sendButton; opacity: 1; }
42+ }
43+ ]
44+
45+ transitions: [
46+ Transition {
47+ from: ""
48+ to: "editing"
49+ SequentialAnimation {
50+ UbuntuNumberAnimation { target: reviewField; properties: "width"; duration: UbuntuAnimation.SlowDuration }
51+ UbuntuNumberAnimation { target: sendButton; properties: "opacity"; duration: UbuntuAnimation.SlowDuration }
52+ }
53+ },
54+ Transition {
55+ from: "editing"
56+ to: ""
57+ SequentialAnimation {
58+ UbuntuNumberAnimation { target: sendButton; properties: "opacity"; duration: UbuntuAnimation.SlowDuration }
59+ UbuntuNumberAnimation { target: reviewField; properties: "width"; duration: UbuntuAnimation.SlowDuration }
60+ }
61+ }
62+ ]
63+
64+ Label {
65+ fontSize: "medium"
66+ color: "white"
67+ style: Text.Raised
68+ styleColor: "black"
69+ opacity: .9
70+ text: i18n.tr("Add a review")
71+ }
72+
73+ Row {
74+ id: row
75+ spacing: units.gu(1)
76+ width: root.width
77+
78+ TextArea {
79+ id: reviewField
80+ objectName: "reviewField"
81+ placeholderText: i18n.tr("Review")
82+ width: parent.width
83+ height: units.gu(4)
84+ contentHeight: units.gu(3) * 5
85+ verticalAlignment: Text.AlignVCenter
86+
87+ Behavior on height { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
88+
89+ onFocusChanged: {
90+ if(reviewField.focus){
91+ root.state = "editing";
92+ reviewField.selectAll();
93+ }
94+ }
95+
96+ onTextChanged: {
97+ if (reviewField.text == "") {
98+ root.state = "";
99+ reviewField.focus = false;
100+ } else if (reviewField.lineCount > 1) {
101+ reviewField.height = reviewField.contentHeight;
102+ }
103+ if (reviewField.lineCount == 1) {
104+ reviewField.height = units.gu(4);
105+ }
106+ }
107+
108+ InverseMouseArea {
109+ anchors.fill: parent
110+ propagateComposedEvents: true
111+ onPressed: {
112+ reviewField.focus = false;
113+ root.state = "";
114+ }
115+ }
116+ }
117+
118+ Button {
119+ id: sendButton
120+ objectName: "sendButton"
121+ width: units.gu(10)
122+ height: units.gu(4)
123+ anchors.bottom: reviewField.bottom
124+ color: Theme.palette.selected.foreground
125+ text: i18n.tr("Send")
126+ opacity: 0
127+
128+ onClicked: {
129+ root.sendReview(reviewField.text);
130+ reviewField.text = ""
131+ }
132+ }
133+ }
134+
135+ ListItem.ThinDivider {}
136+
137+ Label {
138+ fontSize: "medium"
139+ color: "white"
140+ style: Text.Raised
141+ styleColor: "black"
142+ opacity: .9
143+ text: i18n.tr("Comments:")
144+ }
145+
146+ Repeater {
147+ objectName: "commentsArea"
148+ model: root.model
149+
150+ Column {
151+ width: parent.width
152+
153+ Column {
154+
155+ Label {
156+ text: modelData[0]
157+ fontSize: "medium"
158+ color: "white"
159+ opacity: .8
160+ anchors {
161+ left: parent.left
162+ right: parent.right
163+ }
164+ wrapMode: Text.WordWrap
165+ style: Text.Raised
166+ styleColor: "black"
167+ }
168+
169+ Row {
170+ spacing: units.gu(1)
171+
172+ RatingStars {
173+ maximumRating: 10
174+ rating: modelData[1]
175+ }
176+
177+ Label {
178+ text: modelData[2]
179+ fontSize: "medium"
180+ color: Theme.palette.selected.backgroundText
181+ opacity: .6
182+ style: Text.Raised
183+ styleColor: "black"
184+ }
185+ }
186+ }
187+
188+ Label {
189+ text: modelData[3]
190+ fontSize: "medium"
191+ color: Theme.palette.selected.backgroundText
192+ opacity: .6
193+ anchors {
194+ left: parent.left
195+ right: parent.right
196+ }
197+ wrapMode: Text.WordWrap
198+ style: Text.Raised
199+ styleColor: "black"
200+ }
201+ }
202+ }
203+}
Dash/Apps/ApplicationsFilterGrid.qml+3−2
@@ -30,9 +30,10 @@ FilterGrid {
30 delegateHeight: units.gu(9.5)
30 delegateHeight: units.gu(9.5)
31 verticalSpacing: units.gu(2)
31 verticalSpacing: units.gu(2)
32
32
33− signal clicked(int index, variant data)
33+ signal clicked(int index, variant data, real itemY)
34
34
35 delegate: Tile {
35 delegate: Tile {
36+ id: tile
36 objectName: "delegate" + index
37 objectName: "delegate" + index
37 Application {
38 Application {
38 id: application
39 id: application
@@ -52,6 +53,6 @@ FilterGrid {
52 imageWidth: units.gu(8)
53 imageWidth: units.gu(8)
53 imageHeight: units.gu(7.5)
54 imageHeight: units.gu(7.5)
54 source: IconUtil.from_gicon(icon)
55 source: IconUtil.from_gicon(icon)
55− onClicked: filterGrid.clicked(index, application.desktopFile);
56+ onClicked: filterGrid.clicked(index, application.desktopFile, tile.y);
56 }
57 }
57}
58}
Dash/DashApps.qml+95−2
@@ -132,8 +132,10 @@ ScopeView {
132 item.canEnableTerminationMode =
132 item.canEnableTerminationMode =
133 Qt.binding(function() { return isCurrent; })
133 Qt.binding(function() { return isCurrent; })
134 } else {
134 } else {
135− function activateApplication(index, data) {
135+ function activateApplication(index, data, itemY) {
136− shell.activateApplication(data);
136+ effect.positionPx = mapToItem(categoryView, 0, itemY).y
137+ previewLoader.previewData = data;
138+ previewLoader.open = true;
137 }
139 }
138
140
139 item.model = Qt.binding(function() { return categoryModels[modelName]; });
141 item.model = Qt.binding(function() { return categoryModels[modelName]; });
@@ -170,4 +172,95 @@ ScopeView {
170 searchHistory: scopeView.searchHistory
172 searchHistory: scopeView.searchHistory
171 }
173 }
172 }
174 }
175+
176+ OpenEffect {
177+ id: effect
178+ anchors {
179+ fill: parent
180+ bottomMargin: -bottomOverflow
181+ }
182+ sourceItem: categoryView
183+
184+ enabled: gap > 0.0
185+
186+ topGapPx: (1 - gap) * positionPx
187+ topOpacity: (1 - gap * 1.2)
188+ bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
189+ bottomOverflow: units.gu(20)
190+ bottomOpacity: 1 - (gap * 0.8)
191+
192+ property int targetBottomGapPx: height - units.gu(8) - bottomOverflow
193+ property real gap: previewLoader.open ? 1.0 : 0.0
194+
195+ Behavior on gap {
196+ NumberAnimation {
197+ duration: 200
198+ easing.type: Easing.InOutQuad
199+ onRunningChanged: {
200+ if (!previewLoader.open && !running) {
201+ previewLoader.onScreen = false
202+ }
203+ }
204+ }
205+ }
206+ }
207+
208+ Connections {
209+ target: scopeView.scope
210+ onPreviewReady: {
211+ previewLoader.previewData = preview
212+ previewLoader.open = true
213+ }
214+ }
215+
216+ Connections {
217+ ignoreUnknownSignals: true
218+ target: previewLoader.valid ? previewLoader.item : null
219+ onClose: {
220+ previewLoader.open = false
221+ }
222+ }
223+
224+ PreviewDelegateMapper {
225+ id: previewDelegateMapper
226+ }
227+
228+ Loader {
229+ id: previewLoader
230+ property var previewData
231+ height: effect.bottomGapPx - effect.topGapPx
232+ anchors {
233+ top: parent.top
234+ topMargin: effect.topGapPx
235+ left: parent.left
236+ right: parent.right
237+ }
238+ source: onScreen ? previewDelegateMapper.map("preview-application") : ""
239+
240+ property bool open: false
241+ property bool onScreen: false
242+ property bool valid: item !== null
243+
244+ onOpenChanged: {
245+ if (open) {
246+ onScreen = true
247+ }
248+ }
249+
250+ onLoaded: {
251+ item.previewData = previewLoader.previewData
252+ }
253+ }
254+
255+ // TODO: Move as InverseMouseArea to DashPreview
256+ MouseArea {
257+ enabled: previewLoader.onScreen
258+ anchors {
259+ fill: parent
260+ topMargin: effect.bottomGapPx
261+ }
262+ onClicked: {
263+ previewLoader.open = false;
264+ }
265+ }
173}
266}
Dash/DashPreview.qml+12−42
@@ -19,14 +19,15 @@ import Ubuntu.Components 0.1
19
19
20Rectangle {
20Rectangle {
21 id: root
21 id: root
22+
23+ property var previewData
24+
22 property string title: ""
25 property string title: ""
23− property url url: ""
24 property real previewWidthRatio: 0.5
26 property real previewWidthRatio: 0.5
25− property bool playable: false
27+
26− property bool forceSquare: false
28+ property Component header
27 property Component buttons
29 property Component buttons
28− property Component caption
30+ property Component body
29− property Component description
30
31
31 readonly property bool narrowMode: width <= height
32 readonly property bool narrowMode: width <= height
32 readonly property int contentSpacing: units.gu(3)
33 readonly property int contentSpacing: units.gu(3)
@@ -92,7 +93,7 @@ Rectangle {
92 elide: Text.ElideRight
93 elide: Text.ElideRight
93 fontSize: "x-large"
94 fontSize: "x-large"
94 font.weight: Font.Light
95 font.weight: Font.Light
95− color: Theme.palette.selected.backgroundText
96+ color: "#f3f3e7"
96 opacity: 0.9
97 opacity: 0.9
97 text: root.title
98 text: root.title
98 style: Text.Raised
99 style: Text.Raised
@@ -141,38 +142,13 @@ Rectangle {
141 right: parent.right
142 right: parent.right
142 }
143 }
143 height: childrenRect.height
144 height: childrenRect.height
144− spacing: root.contentSpacing
145+ spacing: units.gu(1)
145
146
146− // TODO: replace this UbuntuShape with the Video component once that lands
147+ Loader {
147− // with the player.
148+ id: headerLoader
148− UbuntuShape {
149− id: urlLoader
150 anchors.left: parent.left
149 anchors.left: parent.left
151 anchors.right: parent.right
150 anchors.right: parent.right
152− height: root.forceSquare ? width : width * previewImage.sourceSize.height / previewImage.sourceSize.width
151+ sourceComponent: root.header
153− radius: "medium"
154− image: Image {
155− id: previewImage
156− asynchronous: true
157− source: root.url
158− fillMode: Image.PreserveAspectCrop
159− }
160−
161− Image {
162− objectName: "playButton"
163− anchors.centerIn: parent
164− visible: root.playable
165− readonly property bool bigButton: parent.width > units.gu(40)
166− width: bigButton ? units.gu(8) : units.gu(4.5)
167− height: width
168− source: "graphics/play_button%1%2.png".arg(previewImageMouseArea.pressed ? "_active" : "").arg(bigButton ? "_big" : "")
169− }
170−
171− MouseArea {
172− id: previewImageMouseArea
173− anchors.fill: parent
174− onClicked: root.previewImageClicked()
175− }
176 }
152 }
177 Loader {
153 Loader {
178 id: buttonLoader
154 id: buttonLoader
@@ -180,12 +156,6 @@ Rectangle {
180 anchors.right: parent.right
156 anchors.right: parent.right
181 sourceComponent: root.buttons
157 sourceComponent: root.buttons
182 }
158 }
183− Loader {
184− id: captionLoader
185− anchors.left: parent.left
186− anchors.right: parent.right
187− sourceComponent: root.caption
188− }
189 }
159 }
190 }
160 }
191
161
@@ -213,6 +183,6 @@ Rectangle {
213 parent: root.narrowMode ? leftColumn : rightColumn
183 parent: root.narrowMode ? leftColumn : rightColumn
214 anchors.left: parent.left
184 anchors.left: parent.left
215 anchors.right: parent.right
185 anchors.right: parent.right
216− sourceComponent: root.description
186+ sourceComponent: root.body
217 }
187 }
218}
188}
Dash/Generic/GenericPreview.qml+25−3
@@ -23,12 +23,34 @@ import ".."
23
23
24DashPreview {
24DashPreview {
25 id: genericPreview
25 id: genericPreview
26− property var previewData
27
26
28 title: previewData.title
27 title: previewData.title
29− url: IconUtil.from_gicon(previewData.image)
30 previewWidthRatio: 0.6
28 previewWidthRatio: 0.6
31
29
30+ property url url: IconUtil.from_gicon(previewData.image)
31+
32+ // TODO: replace this UbuntuShape with the Video component once that lands
33+ // with the player.
34+ header: UbuntuShape {
35+ id: urlLoader
36+ anchors.left: parent.left
37+ anchors.right: parent.right
38+ height: width * previewImage.sourceSize.height / previewImage.sourceSize.width
39+ radius: "medium"
40+ image: Image {
41+ id: previewImage
42+ asynchronous: true
43+ source: genericPreview.url
44+ fillMode: Image.PreserveAspectCrop
45+ }
46+
47+ MouseArea {
48+ id: previewImageMouseArea
49+ anchors.fill: parent
50+ onClicked: genericPreview.previewImageClicked()
51+ }
52+ }
53+
32 buttons: GridView {
54 buttons: GridView {
33 id: buttons
55 id: buttons
34 model: genericPreview.previewData.actions
56 model: genericPreview.previewData.actions
@@ -56,7 +78,7 @@ DashPreview {
56 }
78 }
57 focus: false
79 focus: false
58 }
80 }
59− description: Column {
81+ body: Column {
60 spacing: units.gu(2)
82 spacing: units.gu(2)
61
83
62 Label {
84 Label {
Dash/GenericScopeView.qml+1−1
@@ -202,7 +202,7 @@ ScopeView {
202 }
202 }
203
203
204 onLoaded: {
204 onLoaded: {
205− item.previewData = previewLoader.previewData
205+ item.previewData = Qt.binding(function() { return previewLoader.previewData })
206 }
206 }
207 }
207 }
208
208
Dash/PreviewDelegateMapper.qml+2
@@ -19,7 +19,9 @@ import QtQuick 2.0
19QtObject {
19QtObject {
20 property var d: QtObject {
20 property var d: QtObject {
21 readonly property string genericPreview: "Generic/GenericPreview.qml"
21 readonly property string genericPreview: "Generic/GenericPreview.qml"
22+ readonly property string appPreview: "Apps/AppPreview.qml"
22 property var previewDelegateMapping: {"preview-generic": genericPreview,
23 property var previewDelegateMapping: {"preview-generic": genericPreview,
24+ "preview-application": appPreview,
23 }
25 }
24 }
26 }
25
27
Dash/Video/VideoPreview.qml+35−3
@@ -24,7 +24,10 @@ DashPreview {
24
24
25 property var item
25 property var item
26 property alias ready: nfo.ready
26 property alias ready: nfo.ready
27+ property bool playable: false
28+ property string filePath: fileUri != ""
27 readonly property url fileUri: item ? item.fileUri : ""
29 readonly property url fileUri: item ? item.fileUri : ""
30+ property url url: item ? item.nfoUri.replace(/\.nfo$/, "-fanart.jpg") : ""
28
31
29 VideoInfo {
32 VideoInfo {
30 id: nfo
33 id: nfo
@@ -32,9 +35,7 @@ DashPreview {
32 }
35 }
33
36
34 title: nfo.ready ? nfo.video.title : ""
37 title: nfo.ready ? nfo.video.title : ""
35− url: item ? item.nfoUri.replace(/\.nfo$/, "-fanart.jpg") : ""
36 previewWidthRatio: 0.6
38 previewWidthRatio: 0.6
37− playable: fileUri != ""
38
39
39 onPreviewImageClicked: {
40 onPreviewImageClicked: {
40 if (playable) {
41 if (playable) {
@@ -42,6 +43,37 @@ DashPreview {
42 }
43 }
43 }
44 }
44
45
46+ // TODO: replace this UbuntuShape with the Video component once that lands
47+ // with the player.
48+ header: UbuntuShape {
49+ id: urlLoader
50+ anchors.left: parent.left
51+ anchors.right: parent.right
52+ radius: "medium"
53+ image: Image {
54+ id: previewImage
55+ asynchronous: true
56+ source: root.url
57+ fillMode: Image.PreserveAspectCrop
58+ }
59+
60+ Image {
61+ objectName: "playButton"
62+ anchors.centerIn: parent
63+ visible: root.playable
64+ readonly property bool bigButton: parent.width > units.gu(40)
65+ width: bigButton ? units.gu(8) : units.gu(4.5)
66+ height: width
67+ source: "../graphics/play_button%1%2.png".arg(previewImageMouseArea.pressed ? "_active" : "").arg(bigButton ? "_big" : "")
68+ }
69+
70+ MouseArea {
71+ id: previewImageMouseArea
72+ anchors.fill: parent
73+ onClicked: root.previewImageClicked()
74+ }
75+ }
76+
45 buttons: Row {
77 buttons: Row {
46 spacing: units.gu(2)
78 spacing: units.gu(2)
47
79
@@ -64,7 +96,7 @@ DashPreview {
64 }
96 }
65 }
97 }
66
98
67− description: Column {
99+ body: Column {
68 spacing: units.gu(2)
100 spacing: units.gu(2)
69 RatingStars {
101 RatingStars {
70 maximumRating: 10 // FIXME: this should happen on the backend side
102 maximumRating: 10 // FIXME: this should happen on the backend side
plugins/Ubuntu/CMakeLists.txt+1
@@ -1 +1,2 @@
1add_subdirectory(Gestures)
1add_subdirectory(Gestures)
2+add_subdirectory(DownloadDaemonListener)
plugins/Ubuntu/DownloadDaemonListener/CMakeLists.txtnew file+35
@@ -0,0 +1,35 @@
1+include_directories(
2+ ${CMAKE_CURRENT_SOURCE_DIR}
3+ ${CMAKE_CURRENT_BINARY_DIR}
4+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
5+)
6+
7+set(DOWNLOADDAEMONLISTENER_SOURCES
8+ backend.cpp
9+ download_tracker.cpp
10+ interface/downloadtrackeradaptor.cpp
11+ )
12+
13+add_library(DownloadDaemonListener MODULE
14+ ${DOWNLOADDAEMONLISTENER_SOURCES}
15+ )
16+# Because this is an internal support library, we want
17+# to expose all symbols in it. Consider changing this
18+# either to a static library or just using the
19+# files directly in targets.
20+set_target_properties(DownloadDaemonListener PROPERTIES COMPILE_FLAGS -fvisibility=default)
21+
22+qt5_use_modules(DownloadDaemonListener Qml Quick DBus Core)
23+
24+# copy qmldir file into build directory for shadow builds
25+file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/qmldir"
26+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
27+ )
28+
29+install(TARGETS DownloadDaemonListener
30+ DESTINATION ${SHELL_APP_DIR}/plugins/Ubuntu/DownloadDaemonListener
31+ )
32+
33+install(FILES qmldir
34+ DESTINATION ${SHELL_APP_DIR}/plugins/Ubuntu/DownloadDaemonListener
35+ )
plugins/Ubuntu/DownloadDaemonListener/backend.cppnew file+17
@@ -0,0 +1,17 @@
1+#include <QtQml>
2+#include <QtQml/QQmlContext>
3+#include "backend.h"
4+#include "download_tracker.h"
5+
6+
7+void BackendPlugin::registerTypes(const char *uri)
8+{
9+ Q_ASSERT(uri == QLatin1String("Ubuntu.DownloadDaemonListener"));
10+
11+ qmlRegisterType<DownloadTracker>(uri, 0, 1, "DownloadTracker");
12+}
13+
14+void BackendPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
15+{
16+ QQmlExtensionPlugin::initializeEngine(engine, uri);
17+}
plugins/Ubuntu/DownloadDaemonListener/backend.hnew file+16
@@ -0,0 +1,16 @@
1+#ifndef BACKEND_PLUGIN_H
2+#define BACKEND_PLUGIN_H
3+
4+#include <QtQml/QQmlEngine>
5+#include <QtQml/QQmlExtensionPlugin>
6+
7+class BackendPlugin : public QQmlExtensionPlugin
8+{
9+ Q_OBJECT
10+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
11+
12+public:
13+ void registerTypes(const char *uri);
14+ void initializeEngine(QQmlEngine *engine, const char *uri);
15+};
16+#endif // BACKEND_PLUGIN_H
plugins/Ubuntu/DownloadDaemonListener/download_tracker.cppnew file+33
@@ -0,0 +1,33 @@
1+#include "download_tracker.h"
2+#include <QDebug>
3+
4+DownloadTracker::DownloadTracker(QObject *parent) :
5+ QObject(parent)
6+{
7+}
8+
9+bool DownloadTracker::isServiceReady()
10+{
11+ bool ready = false;
12+ if(this->adaptor != nullptr) {
13+ ready = this->adaptor->isValid();
14+ }
15+
16+ return ready;
17+}
18+
19+void DownloadTracker::setDbusPath(QString& path)
20+{
21+ if(path != ""){
22+ this->m_dbusPath = path;
23+ this->adaptor = new DownloadTrackerAdaptor("com.canonical.applications.Downloader", this->m_dbusPath, QDBusConnection::sessionBus(), 0);
24+
25+ this->connect(this->adaptor, SIGNAL(canceled(bool)), this, SIGNAL(canceled(bool)));
26+ this->connect(this->adaptor, SIGNAL(error(const QString &)), this, SIGNAL(error(const QString &)));
27+ this->connect(this->adaptor, SIGNAL(finished(const QString &)), this, SIGNAL(finished(const QString &)));
28+ this->connect(this->adaptor, SIGNAL(paused(bool)), this, SIGNAL(paused(bool)));
29+ this->connect(this->adaptor, SIGNAL(progress(qulonglong, qulonglong)), this, SIGNAL(progress(qulonglong, qulonglong)));
30+ this->connect(this->adaptor, SIGNAL(resumed(bool)), this, SIGNAL(resumed(bool)));
31+ this->connect(this->adaptor, SIGNAL(started(bool)), this, SIGNAL(started(bool)));
32+ }
33+}
plugins/Ubuntu/DownloadDaemonListener/download_tracker.hnew file+36
@@ -0,0 +1,36 @@
1+#ifndef MYTYPE_H
2+#define MYTYPE_H
3+
4+#include <QObject>
5+#include <QList>
6+#include <QDBusObjectPath>
7+#include <interface/downloadtrackeradaptor.h>
8+
9+class DownloadTracker : public QObject
10+{
11+ Q_OBJECT
12+ Q_DISABLE_COPY(DownloadTracker)
13+ Q_PROPERTY(QString dbusPath WRITE setDbusPath)
14+ Q_PROPERTY(bool serviceReady READ isServiceReady)
15+
16+public:
17+ explicit DownloadTracker(QObject *parent = 0);
18+
19+ void setDbusPath(QString& path);
20+ bool isServiceReady();
21+
22+Q_SIGNALS:
23+ void canceled(bool success);
24+ void error(const QString &error);
25+ void finished(const QString &path);
26+ void paused(bool success);
27+ void progress(qulonglong received, qulonglong total);
28+ void resumed(bool success);
29+ void started(bool success);
30+
31+private:
32+ QString m_dbusPath;
33+ DownloadTrackerAdaptor* adaptor;
34+};
35+
36+#endif // MYTYPE_H
plugins/Ubuntu/DownloadDaemonListener/interface/downloadtrackeradaptor.cppnew file+25
@@ -0,0 +1,25 @@
1+/*
2+ * This file was generated by qdbusxml2cpp version 0.8
3+ * Command line was: qdbusxml2cpp -v -c DownloadTrackerAdaptor -p downloadtrackeradaptor.h:downloadtrackeradaptor.cpp -i metatypes.h com.canonical.applications.download.xml
4+ *
5+ * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
6+ *
7+ * This is an auto-generated file.
8+ * This file may have been hand-edited. Look for HAND-EDIT comments
9+ * before re-generating it.
10+ */
11+
12+#include "downloadtrackeradaptor.h"
13+
14+/*
15+ * Implementation of interface class DownloadTrackerAdaptor
16+ */
17+
18+DownloadTrackerAdaptor::DownloadTrackerAdaptor(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)
19+ : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)
20+{
21+}
22+
23+DownloadTrackerAdaptor::~DownloadTrackerAdaptor()
24+{
25+}
plugins/Ubuntu/DownloadDaemonListener/interface/downloadtrackeradaptor.hnew file+125
@@ -0,0 +1,125 @@
1+/*
2+ * This file was generated by qdbusxml2cpp version 0.8
3+ * Command line was: qdbusxml2cpp -v -c DownloadTrackerAdaptor -p downloadtrackeradaptor.h:downloadtrackeradaptor.cpp -i metatypes.h com.canonical.applications.download.xml
4+ *
5+ * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
6+ *
7+ * This is an auto-generated file.
8+ * Do not edit! All changes made to it will be lost.
9+ */
10+
11+#ifndef DOWNLOADTRACKERADAPTOR_H_1374434371
12+#define DOWNLOADTRACKERADAPTOR_H_1374434371
13+
14+#include <QtCore/QObject>
15+#include <QtCore/QByteArray>
16+#include <QtCore/QList>
17+#include <QtCore/QMap>
18+#include <QtCore/QString>
19+#include <QtCore/QStringList>
20+#include <QtCore/QVariant>
21+#include <QtDBus/QtDBus>
22+#include "metatypes.h"
23+
24+/*
25+ * Proxy class for interface com.canonical.applications.Download
26+ */
27+class DownloadTrackerAdaptor: public QDBusAbstractInterface
28+{
29+ Q_OBJECT
30+public:
31+ static inline const char *staticInterfaceName()
32+ { return "com.canonical.applications.Download"; }
33+
34+public:
35+ DownloadTrackerAdaptor(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0);
36+
37+ ~DownloadTrackerAdaptor();
38+
39+public Q_SLOTS: // METHODS
40+ inline QDBusPendingReply<> allowGSMDownload(bool allowed)
41+ {
42+ QList<QVariant> argumentList;
43+ argumentList << QVariant::fromValue(allowed);
44+ return asyncCallWithArgumentList(QLatin1String("allowGSMDownload"), argumentList);
45+ }
46+
47+ inline QDBusPendingReply<> cancel()
48+ {
49+ QList<QVariant> argumentList;
50+ return asyncCallWithArgumentList(QLatin1String("cancel"), argumentList);
51+ }
52+
53+ inline QDBusPendingReply<bool> isGSMDownloadAllowed()
54+ {
55+ QList<QVariant> argumentList;
56+ return asyncCallWithArgumentList(QLatin1String("isGSMDownloadAllowed"), argumentList);
57+ }
58+
59+ inline QDBusPendingReply<QVariantMap> metadata()
60+ {
61+ QList<QVariant> argumentList;
62+ return asyncCallWithArgumentList(QLatin1String("metadata"), argumentList);
63+ }
64+
65+ inline QDBusPendingReply<> pause()
66+ {
67+ QList<QVariant> argumentList;
68+ return asyncCallWithArgumentList(QLatin1String("pause"), argumentList);
69+ }
70+
71+ inline QDBusPendingReply<qulonglong> progress()
72+ {
73+ QList<QVariant> argumentList;
74+ return asyncCallWithArgumentList(QLatin1String("progress"), argumentList);
75+ }
76+
77+ inline QDBusPendingReply<> resume()
78+ {
79+ QList<QVariant> argumentList;
80+ return asyncCallWithArgumentList(QLatin1String("resume"), argumentList);
81+ }
82+
83+ inline QDBusPendingReply<> setThrottle(qulonglong speed)
84+ {
85+ QList<QVariant> argumentList;
86+ argumentList << QVariant::fromValue(speed);
87+ return asyncCallWithArgumentList(QLatin1String("setThrottle"), argumentList);
88+ }
89+
90+ inline QDBusPendingReply<> start()
91+ {
92+ QList<QVariant> argumentList;
93+ return asyncCallWithArgumentList(QLatin1String("start"), argumentList);
94+ }
95+
96+ inline QDBusPendingReply<qulonglong> throttle()
97+ {
98+ QList<QVariant> argumentList;
99+ return asyncCallWithArgumentList(QLatin1String("throttle"), argumentList);
100+ }
101+
102+ inline QDBusPendingReply<qulonglong> totalSize()
103+ {
104+ QList<QVariant> argumentList;
105+ return asyncCallWithArgumentList(QLatin1String("totalSize"), argumentList);
106+ }
107+
108+Q_SIGNALS: // SIGNALS
109+ void canceled(bool success);
110+ void error(const QString &error);
111+ void finished(const QString &path);
112+ void paused(bool success);
113+ void progress(qulonglong received, qulonglong total);
114+ void resumed(bool success);
115+ void started(bool success);
116+};
117+
118+namespace com {
119+ namespace canonical {
120+ namespace applications {
121+ typedef ::DownloadTrackerAdaptor Download;
122+ }
123+ }
124+}
125+#endif
plugins/Ubuntu/DownloadDaemonListener/interface/metatypes.hnew file+28
@@ -0,0 +1,28 @@
1+/*
2+ * Copyright 2013 2013 Canonical Ltd.
3+ *
4+ * This library is free software; you can redistribute it and/or
5+ * modify it under the terms of version 3 of the GNU Lesser General Public
6+ * License as published by the Free Software Foundation.
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 GNU
11+ * General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public
14+ * License along with this library; if not, write to the
15+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16+ * Boston, MA 02110-1301, USA.
17+ */
18+
19+#ifndef DOWNLOADER_LIB_APP_METATYPES_H
20+#define DOWNLOADER_LIB_APP_METATYPES_H
21+
22+#include <QMap>
23+
24+typedef QMap<QString, QString> StringMap;
25+
26+Q_DECLARE_METATYPE(StringMap)
27+
28+#endif // METATYPES_H
plugins/Ubuntu/DownloadDaemonListener/qmldirnew file+3
@@ -0,0 +1,3 @@
1+module Ubuntu.DownloadDaemonListener
2+plugin DownloadDaemonListener
3+typeinfo plugins.qmltypes
plugins/Unity/preview.cpp+7
@@ -94,6 +94,11 @@ QVariant Preview::infoHints()
94 return QVariant::fromValue(m_infoHints);
94 return QVariant::fromValue(m_infoHints);
95}
95}
96
96
97+QVariantMap Preview::infoHintsHash()
98+{
99+ return m_infoHintsHash;
100+}
101+
97QString Preview::image() const
102QString Preview::image() const
98{
103{
99 if (m_unityPreview) {
104 if (m_unityPreview) {
@@ -145,6 +150,7 @@ void Preview::setUnityPreviewBase(unity::dash::Preview::Ptr unityPreview)
145
150
146 qDeleteAll(m_infoHints);
151 qDeleteAll(m_infoHints);
147 m_infoHints.clear();
152 m_infoHints.clear();
153+ m_infoHintsHash.clear();
148 qDeleteAll(m_actions);
154 qDeleteAll(m_actions);
149 m_actions.clear();
155 m_actions.clear();
150
156
@@ -152,6 +158,7 @@ void Preview::setUnityPreviewBase(unity::dash::Preview::Ptr unityPreview)
152 auto hint = new PreviewInfoHint(this);
158 auto hint = new PreviewInfoHint(this);
153 hint->setUnityInfoHint(unityInfoHint);
159 hint->setUnityInfoHint(unityInfoHint);
154 m_infoHints.append(hint);
160 m_infoHints.append(hint);
161+ m_infoHintsHash[hint->id()] = QVariant::fromValue(hint);
155 }
162 }
156
163
157 for (auto unityAction: m_unityPreview->GetActions()) {
164 for (auto unityAction: m_unityPreview->GetActions()) {
plugins/Unity/preview.h+4
@@ -25,6 +25,7 @@
25#include <QString>
25#include <QString>
26#include <QMetaType>
26#include <QMetaType>
27#include <QList>
27#include <QList>
28+#include <QVariantMap>
28
29
29// libunity-core
30// libunity-core
30#include <UnityCore/Preview.h>
31#include <UnityCore/Preview.h>
@@ -42,6 +43,7 @@ class Q_DECL_EXPORT Preview : public QObject
42 Q_PROPERTY(QString description READ description NOTIFY previewChanged)
43 Q_PROPERTY(QString description READ description NOTIFY previewChanged)
43 Q_PROPERTY(QVariant actions READ actions NOTIFY previewChanged)
44 Q_PROPERTY(QVariant actions READ actions NOTIFY previewChanged)
44 Q_PROPERTY(QVariant infoHints READ infoHints NOTIFY previewChanged)
45 Q_PROPERTY(QVariant infoHints READ infoHints NOTIFY previewChanged)
46+ Q_PROPERTY(QVariantMap infoMap READ infoHintsHash NOTIFY previewChanged)
45 Q_PROPERTY(QString image READ image NOTIFY previewChanged)
47 Q_PROPERTY(QString image READ image NOTIFY previewChanged)
46 Q_PROPERTY(QString imageSourceUri READ imageSourceUri NOTIFY previewChanged)
48 Q_PROPERTY(QString imageSourceUri READ imageSourceUri NOTIFY previewChanged)
47
49
@@ -55,6 +57,7 @@ public:
55 QString description() const;
57 QString description() const;
56 QVariant actions();
58 QVariant actions();
57 QVariant infoHints();
59 QVariant infoHints();
60+ QVariantMap infoHintsHash();
58 QString image() const;
61 QString image() const;
59 QString imageSourceUri() const;
62 QString imageSourceUri() const;
60
63
@@ -73,6 +76,7 @@ private:
73
76
74 QList<QObject *> m_actions;
77 QList<QObject *> m_actions;
75 QList<QObject *> m_infoHints;
78 QList<QObject *> m_infoHints;
79+ QVariantMap m_infoHintsHash;
76};
80};
77
81
78Q_DECLARE_METATYPE(Preview *)
82Q_DECLARE_METATYPE(Preview *)
tests/qmltests/CMakeLists.txt+4
@@ -46,6 +46,10 @@ add_qml_test(Dash ScopeView IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_D
46add_qml_test(Dash FilterGrids IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins ${CMAKE_CURRENT_SOURCE_DIR}/plugins
46add_qml_test(Dash FilterGrids IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins ${CMAKE_CURRENT_SOURCE_DIR}/plugins
47 ${CMAKE_BINARY_DIR}/tests/mocks)
47 ${CMAKE_BINARY_DIR}/tests/mocks)
48add_qml_test(Dash/Apps RunningApplicationsGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
48add_qml_test(Dash/Apps RunningApplicationsGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
49+add_qml_test(Dash/Apps AppInfo IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
50+add_qml_test(Dash/Apps AppReviews IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
51+add_qml_test(Dash/Apps AppPreview IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
52+add_qml_test(Dash/Video VideoPreview IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
49add_qml_test(Greeter Lockscreen IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
53add_qml_test(Greeter Lockscreen IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
50 PROPERTIES ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
54 PROPERTIES ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
51add_qml_test(Greeter Tablet IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
55add_qml_test(Greeter Tablet IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
tests/qmltests/Dash/Apps/tst_AppPreview.qmlnew file+171
@@ -0,0 +1,171 @@
1+/*
2+ * Copyright 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 QtTest 1.0
19+import Ubuntu.Components 0.1
20+import "../../../../Dash/Apps"
21+import Unity.Test 0.1 as UT
22+
23+Item {
24+ id: root
25+ width: units.gu(60)
26+ height: units.gu(80)
27+
28+ property var calls
29+
30+ SignalSpy {
31+ id: sendPreviewSpy
32+ target: appPreview
33+ signalName: "sendUserReview"
34+ }
35+
36+ function get_actions() {
37+ var a1 = new Object();
38+ a1.id = 123;
39+ a1.displayName = "action1";
40+ var a2 = new Object();
41+ a2.id = 456;
42+ a2.displayName = "action2";
43+ var a3 = new Object();
44+ a3.id = 789;
45+ a3.displayName = "action3";
46+
47+ return [a1, a2, a3];
48+ }
49+
50+ function get_infomap() {
51+ var map = {
52+ "more-screenshots": screenshots,
53+ "rating": rating,
54+ "rated": rated,
55+ "reviews": reviews,
56+ "comments": comments
57+ }
58+
59+ return map;
60+ }
61+
62+ property string commentary: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus a mi vitae augue rhoncus lobortis ut rutrum metus. Curabitur tortor leo, tristique sed mollis quis, condimentum venenatis nibh.";
63+ QtObject { id: screenshots; property var value: ["fake_image1.png", "fake_image2.png", "fake_image3.png"] }
64+ QtObject { id: rating; property int value: 8 }
65+ QtObject { id: rated; property int value: 120 }
66+ QtObject { id: reviews; property int value: 8 }
67+ QtObject { id: progress; property string value: "source" }
68+ QtObject { id: comments; property var value: [
69+ ["Unity User", 4, "08/20/2013", root.commentary],
70+ ["Unity User", 8, "01/15/2013", root.commentary],
71+ ["Unity User", 10, "10/02/2013", root.commentary],
72+ ]
73+ }
74+
75+ QtObject {
76+ id: data
77+ property string title: "Unity App"
78+ property string appIcon: "fake_image.png"
79+ property string description: "This is an Application description"
80+ property var execute: root.fake_call
81+ property var infoMap: get_infomap()
82+ property var actions: get_actions()
83+ }
84+
85+ function fake_call(id, data){
86+ root.calls[root.calls.length] = id;
87+ }
88+
89+ // The component under test
90+ AppPreview {
91+ id: appPreview
92+ anchors.fill: parent
93+
94+ previewData: data
95+ }
96+
97+ UT.UnityTestCase {
98+ name: "AppPreview"
99+ when: windowShown
100+
101+ function test_actions() {
102+ root.calls = new Array();
103+ var buttons = findChild(appPreview, "gridButtons");
104+ compare(buttons.count, 3, "Not the proper amount of actions detected.");
105+
106+ for(var i = 0; i < buttons.count; i++) {
107+ buttons.currentIndex = i;
108+ buttons.currentItem.clicked();
109+ }
110+
111+ var actions = get_actions();
112+ for(var i = 0; i < actions.length; i++) {
113+ compare(root.calls[i], actions[i].id, "Id of action not found.");
114+ }
115+ }
116+
117+ function test_rated() {
118+ var rated = findChild(appPreview, "ratedLabel");
119+ compare(rated.text, "(120)", "Rates not equal");
120+ }
121+
122+ function test_reviews() {
123+ var rated = findChild(appPreview, "reviewsLabel");
124+ compare(rated.text, i18n.tr("%1 review", "%1 reviews", 8).arg(8), "Reviews don't match");
125+ }
126+
127+ function test_state_nothing() {
128+ var appReviews = findChild(appPreview, "appReviews");
129+ appReviews.state = "";
130+ var reviewField = findChild(appPreview, "reviewField");
131+ var sendButton = findChild(appPreview, "sendButton");
132+ compare(reviewField.width, appReviews.width, "ReviewField width don't match");
133+ compare(sendButton.opacity, 0, "SendButton opacity should be 0");
134+ }
135+
136+ function test_send_review() {
137+ sendPreviewSpy.clear();
138+ var appReviews = findChild(appPreview, "appReviews");
139+ appReviews.sendReview("review");
140+ compare(sendPreviewSpy.count, 1, "SendReview signal not emitted");
141+ }
142+
143+ function test_review_focus() {
144+ sendPreviewSpy.clear();
145+ var appReviews = findChild(appPreview, "appReviews");
146+ var sendButton = findChild(appReviews, "sendButton");
147+ var reviewField = findChild(appReviews, "reviewField");
148+
149+ compare(reviewField.focus, false, "ReviewField shouldn't have focus");
150+ compare(appReviews.state, "", "State should be empty");
151+ mouseClick(reviewField, 1, 1);
152+ compare(reviewField.focus, true, "Review Field should have focus");
153+ compare(appReviews.state, "editing", "State should be 'editing'");
154+ }
155+
156+ function test_comments() {
157+ var commentsArea = findChild(appPreview, "commentsArea");
158+ compare(commentsArea.count, 3);
159+ for(var i = 0; i < 3; i++) {
160+ var username = commentsArea.itemAt(i).children[0].children[0].text
161+ var rate = commentsArea.itemAt(i).children[0].children[1].children[0].rating
162+ var date = commentsArea.itemAt(i).children[0].children[1].children[1].text
163+ var comment = commentsArea.itemAt(i).children[1].text
164+ compare(username, comments.value[i][0], "Username don't match");
165+ compare(rate, comments.value[i][1], "Rating don't match");
166+ compare(date, comments.value[i][2], "Date don't match");
167+ compare(comment, comments.value[i][3], "Comment don't match");
168+ }
169+ }
170+ }
171+}
tests/qmltests/Dash/Video/tst_VideoPreview.qmlnew file+63
@@ -0,0 +1,63 @@
1+/*
2+ * Copyright 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 QtTest 1.0
19+import "../../../../Dash/Video"
20+import Unity.Test 0.1 as UT
21+
22+Item {
23+ id: root
24+ width: units.gu(60)
25+ height: units.gu(80)
26+
27+ SignalSpy {
28+ id: previewClickedSpy
29+ target: preview
30+ signalName: "previewImageClicked"
31+ }
32+
33+ // The component under test
34+ VideoPreview {
35+ id: preview
36+ anchors.fill: parent
37+ }
38+
39+ UT.UnityTestCase {
40+ name: "VideoPreview"
41+ when: windowShown
42+
43+ function test_play_button_data() {
44+ return [
45+ {tag: "playable", playable: true},
46+ {tag: "not playable", playable: false}
47+ ]
48+ }
49+
50+ function test_play_button(data) {
51+ var playButton = findChild(preview, "playButton")
52+ preview.playable = data.playable
53+ compare(playButton.visible, data.playable)
54+ }
55+
56+ function test_play_button_click() {
57+ preview.playable = true
58+ var playButton = findChild(preview, "playButton")
59+ mouseClick(playButton, 1, 1)
60+ tryCompare(previewClickedSpy, "count", 1)
61+ }
62+ }
63+}
tests/qmltests/Dash/tst_DashPreview.qml+2−23
@@ -32,7 +32,6 @@ Item {
32 id: preview
32 id: preview
33 anchors.fill: parent
33 anchors.fill: parent
34 title: "Testing rocks, debugging sucks!"
34 title: "Testing rocks, debugging sucks!"
35− forceSquare: true
36
35
37 buttons: Row {
36 buttons: Row {
38 width: parent.width
37 width: parent.width
@@ -55,9 +54,9 @@ Item {
55 }
54 }
56 }
55 }
57
56
58− caption: Label { text: "Caption label" }
57+ header: Label { text: "Caption label" }
59
58
60− description: Column {
59+ body: Column {
61 id: testContent
60 id: testContent
62 objectName: "testContent"
61 objectName: "testContent"
63 width: parent.width
62 width: parent.width
@@ -102,26 +101,6 @@ Item {
102 compare(closeSpy.count, 1, "Close signal not emitted")
101 compare(closeSpy.count, 1, "Close signal not emitted")
103 }
102 }
104
103
105− function test_play_button_data() {
106− return [
107− {tag: "playable", playable: true},
108− {tag: "not playable", playable: false}
109− ]
110− }
111−
112− function test_play_button(data) {
113− var playButton = findChild(preview, "playButton")
114− preview.playable = data.playable
115− compare(playButton.visible, data.playable)
116− }
117−
118− function test_play_button_click() {
119− preview.playable = true
120− var playButton = findChild(preview, "playButton")
121− mouseClick(playButton, 1, 1)
122− tryCompare(previewClickedSpy, "count", 1)
123− }
124−
125 function test_columns_data() {
104 function test_columns_data() {
126 return [
105 return [
127 {tag: "1 columns", width: units.gu(5), height: units.gu(10), columns: 1},
106 {tag: "1 columns", width: units.gu(5), height: units.gu(10), columns: 1},