on xenial help sign in
merge with prerequisite branch
41c158fMichael Zanetti
Components/Tile.qml+6−2
@@ -25,10 +25,14 @@ AbstractButton {
25 property string text
25 property string text
26 property int imageWidth
26 property int imageWidth
27 property int imageHeight
27 property int imageHeight
28− opacity: GridView.view.highlightIndex === -1 ? 1 :
29− GridView.view.highlightIndex === index ? 0.6 : 0.2
30 readonly property int center: (index % GridView.view.columns * width) + (width / 2)
28 readonly property int center: (index % GridView.view.columns * width) + (width / 2)
31 property int maximumLineCount: 1
29 property int maximumLineCount: 1
30+ opacity: GridView.view.highlightIndex === -1 ? 1 :
31+ GridView.view.highlightIndex === index ? 0.6 : 0.2
32
32
33 style: TileStyle {}
33 style: TileStyle {}
34+
35+ Behavior on opacity {
36+ UbuntuNumberAnimation {}
37+ }
34}
38}
Dash/Apps/AppPreview.qml+78−131
@@ -16,162 +16,110 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19−import Ubuntu.Components.ListItems 0.1 as ListItem
19+import Ubuntu.Components.ListItems 0.1 as ListItems
20−import Ubuntu.DownloadDaemonListener 0.1
21import ".."
20import ".."
21+import "../Generic"
22+import "../Previews"
22import "../../Components"
23import "../../Components"
23
24
24−DashPreview {
25+GenericPreview {
25 id: root
26 id: root
26
27
27 signal sendUserReview(string review)
28 signal sendUserReview(string review)
28
29
29− title: root.previewData.title
30+ previewImages: previewImagesComponent
31+ description: descriptionComponent
32+ header: headerComponent
30
33
31− header: ListView {
34+ // TODO: Ratings are not yet complete... enable this once they work
32− spacing: units.gu(1)
35+ //ratings: ratingsComponent
33− orientation: ListView.Horizontal
34− height: units.gu(22)
35− anchors {
36− left: parent.left
37− right: parent.right
38− margins: units.gu(1)
39− }
40− model: previewData.infoMap["more-screenshots"] != null ? previewData.infoMap["more-screenshots"].value : [previewData.image]
41−
42− // FIXME: Because of ListViews inside ListViews inside Flickables inside ListViews (and some more)
43− // we finally reached the point where this ListView doesn't correctly get swipe input any more but
44− // instead the parent ListView is the one that is swiped. This MouseArea sort of creates a blocking
45− // layer to make sure this ListView can be swiped, regardless of what's behind it.
46− MouseArea {
47− anchors.fill: parent
48− enabled: parent.contentWidth > parent.width
49− }
50−
51− delegate: UbuntuShape {
52− id: shape
53− anchors {
54− top: parent.top
55− bottom: parent.bottom
56− }
57− width: units.gu(12)
58− radius: "medium"
59− borderSource: ""
60− image: Image {
61− asynchronous: true
62− sourceSize { width: shape.width; height: shape.height }
63− source: modelData ? modelData : ""
64− fillMode: Image.PreserveAspectCrop
65− }
66− }
67− }
68
36
69 Component {
37 Component {
70− id: buttonsComponent
38+ id: previewImagesComponent
71−
39+ ListView {
72− GridView {
40+ id: previewImageListView
73− id: buttonsGrid
74− objectName: "gridButtons"
75−
76− property int numOfRows: (count + 1) / 2
77− property int spacing: units.gu(1)
41+ spacing: units.gu(1)
78− height: Math.max(units.gu(5), units.gu(5)*numOfRows + spacing*(numOfRows - 1))
42+ orientation: ListView.Horizontal
79−
43+ height: units.gu(22)
80− interactive: false
44+ model: previewData.infoMap["more-screenshots"] != null ? previewData.infoMap["more-screenshots"].value : [previewData.image]
81− cellWidth: Math.max(units.gu(9), width / 2)
45+ cacheBuffer: width * 3
82− cellHeight: buttonHeight + spacing
46+
83− property int buttonWidth: count > 1 ? Math.max(0, width / 2 - spacing) : width
47+ // FIXME: Because of ListViews inside ListViews inside Flickables inside ListViews (and some more)
84− property int buttonHeight: units.gu(5)
48+ // we finally reached the point where this ListView doesn't correctly get swipe input any more but
85−
49+ // instead the parent ListView is the one that is swiped. This MouseArea sort of creates a blocking
86− delegate: Button {
50+ // layer to make sure this ListView can be swiped, regardless of what's behind it.
87− width: Math.max(units.gu(4), buttonsGrid.buttonWidth)
51+ MouseArea {
88− height: buttonsGrid.buttonHeight
52+ anchors.fill: parent
89− color: Theme.palette.selected.foreground
53+ enabled: parent.contentWidth > parent.width
90− text: modelData.displayName
91− iconSource: modelData.iconHint
92− iconPosition: "right"
93− onClicked: root.previewData.execute(modelData.id, { })
94 }
54 }
95− }
96− }
97
55
98− Component {
56+ LazyImage {
99− id: progressComponent
57+ objectName: "placeholderScreenshot"
100−
58+ anchors {
101− ProgressBar {
59+ top: parent.top
102− id: progressBar
60+ bottom: parent.bottom
103− objectName: "progressBar"
104− value: 0
105− maximumValue: 100
106− height: units.gu(5)
107−
108− property var model
109−
110− DownloadTracker {
111− service: "com.canonical.applications.Downloader"
112− dbusPath: root.previewData.infoMap["progressbar_source"] ? root.previewData.infoMap["progressbar_source"].value : ""
113−
114− onProgress: {
115− var percentage = parseInt(received * 100 / total);
116− progressBar.value = percentage;
117− }
118−
119− onFinished: {
120− root.previewData.execute(progressBar.model[0].id, { })
121 }
61 }
62+ scaleTo: "height"
63+ source: "broken_image"
64+ initialWidth: units.gu(13)
65+ visible: previewImageListView.count == 0
66+ }
122
67
123− onError: {
68+ delegate: LazyImage {
124− root.previewData.execute(progressBar.model[1].id, { "error": error });
69+ id: shape
70+ anchors {
71+ top: parent.top
72+ bottom: parent.bottom
125 }
73 }
74+ source: modelData ? modelData : ""
75+ scaleTo: "height"
76+ initialWidth: units.gu(13)
126 }
77 }
127−
128 }
78 }
129 }
79 }
130
80
131− buttons: Loader {
81+ Component {
132− anchors {
82+ id: headerComponent
133− left: parent.left
83+ Header {
134− right: parent.right
135− }
136−
137− sourceComponent: root.previewData.infoMap["show_progressbar"] ? progressComponent : buttonsComponent
138−
139− onLoaded: {
140− item.model = root.previewData.actions;
141− }
142− }
143−
144− body: Column {
145− spacing: units.gu(1)
146−
147− AppInfo {
148− objectName: "appInfo"
84+ objectName: "previewHeader"
149− anchors { left: parent.left; right: parent.right }
150−
151− appName: root.previewData.title
85+ title: previewData.title
152− icon: root.previewData.appIcon
86+ icon: previewData.appIcon
87+ subtitle: root.previewData.infoMap["publisher"] ? root.previewData.infoMap["publisher"].value : ""
153 rating: Math.round(root.previewData.rating * 10)
88 rating: Math.round(root.previewData.rating * 10)
154 reviews: root.previewData.numRatings
89 reviews: root.previewData.numRatings
155 rated: root.previewData.infoMap["rated"] ? root.previewData.infoMap["rated"].value : 0
90 rated: root.previewData.infoMap["rated"] ? root.previewData.infoMap["rated"].value : 0
156 }
91 }
92+ }
157
93
94+ Component {
95+ id: descriptionComponent
96+ Column {
97+ spacing: units.gu(1)
98+
158− Label {
99+ Label {
159− anchors { left: parent.left; right: parent.right }
100+ anchors { left: parent.left; right: parent.right }
160− text: root.previewData.description
101+ text: root.previewData.description
161− fontSize: "medium"
102+ fontSize: "medium"
162− color: Theme.palette.selected.backgroundText
103+ color: Theme.palette.selected.backgroundText
163− opacity: .6
104+ opacity: .6
164− wrapMode: Text.WordWrap
105+ wrapMode: Text.WordWrap
165− style: Text.Raised
106+ style: Text.Raised
166− styleColor: "black"
107+ styleColor: "black"
108+ }
167 }
109 }
110+ }
168
111
112+ Component {
113+ id: ratingsComponent
169 Column {
114 Column {
115+ id: columnReviewRating
116+ objectName: "columnReviewRating"
170 visible: root.previewData.rating >= 0
117 visible: root.previewData.rating >= 0
171− anchors { left: parent.left; right: parent.right }
172− spacing: parent.spacing
173
118
119+ spacing: units.gu(1)
120+ height: childrenRect.height
121+
174− ListItem.ThinDivider { }
122+ ListItems.ThinDivider { }
175
123
176 Item {
124 Item {
177 anchors { left: parent.left; right: parent.right }
125 anchors { left: parent.left; right: parent.right }
@@ -197,13 +145,12 @@ DashPreview {
197 }
145 }
198 }
146 }
199
147
200− ListItem.ThinDivider { }
148+ ListItems.ThinDivider { }
201
149
202− AppReviews {
150+ Reviews {
203 id: appReviews
151 id: appReviews
204 objectName: "appReviews"
152 objectName: "appReviews"
205− // TODO: This should make this visible when the feature for reviews/comments is complete.
153+
206− visible: false
207 anchors { left: parent.left; right: parent.right }
154 anchors { left: parent.left; right: parent.right }
208
155
209 model: root.previewData.infoMap["comments"] ? root.previewData.infoMap["comments"].value : undefined
156 model: root.previewData.infoMap["comments"] ? root.previewData.infoMap["comments"].value : undefined
Dash/DashPreview.qml+91−95
@@ -23,19 +23,18 @@ Item {
23 property int keyboardSize: Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0
23 property int keyboardSize: Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0
24 property var previewData
24 property var previewData
25
25
26− property string title: ""
27 property real previewWidthRatio: 0.5
26 property real previewWidthRatio: 0.5
28
27
28+ property Component previewImages
29 property Component header
29 property Component header
30− property Component buttons
30+ property Component actions
31− property Component body
31+ property Component description
32+ property Component ratings
32
33
33− readonly property bool narrowMode: width <= height
34+ readonly property bool narrowMode: width <= height * 1.5
35+ readonly property int columnWidth: narrowMode ? contentRow.width : (contentRow.width / 3) - contentRow.spacing
34 readonly property int contentSpacing: units.gu(3)
36 readonly property int contentSpacing: units.gu(3)
35
37
36− signal close()
37− signal previewImageClicked()
38−
39 clip: true
38 clip: true
40
39
41 function ensureVisible(item) {
40 function ensureVisible(item) {
@@ -60,88 +59,31 @@ Item {
60 anchors.fill: parent
59 anchors.fill: parent
61 }
60 }
62
61
63− Item {
64− id: headerRow
65− height: units.gu(4)
66− anchors {
67− top: parent.top
68− left: parent.left
69− right: parent.right
70− margins: root.contentSpacing
71− }
72−
73− MouseArea {
74− anchors {
75− fill: parent
76− leftMargin: -root.contentSpacing
77− rightMargin: -root.contentSpacing
78− topMargin: -root.contentSpacing
79− }
80−
81− onClicked: root.close();
82− }
83−
84− Item {
85− id: labelItem
86− anchors {
87− fill: parent
88− rightMargin: closePreviewImage.width + spacing
89− }
90−
91− property int spacing: units.gu(2)
92−
93− Label {
94− id: title
95− objectName: "titleLabel"
96− anchors {
97− left: parent.left
98− right: parent.right
99− }
100−
101− elide: Text.ElideRight
102− fontSize: "x-large"
103− font.weight: Font.Light
104− color: Theme.palette.selected.backgroundText
105− opacity: 0.9
106− text: root.title
107− style: Text.Raised
108− styleColor: "black"
109− }
110− Image {
111− id: closePreviewImage
112− source: "graphics/tablet/icon_close_preview.png"
113− width: units.gu(4)
114− height: units.gu(1.5)
115− anchors {
116− bottom: title.bottom
117− bottomMargin: units.dp(7)
118− left: parent.left
119− leftMargin: title.paintedWidth + labelItem.spacing
120− }
121− visible: title.text !== ""
122− }
123− }
124− }
125−
126 Row {
62 Row {
127 id: contentRow
63 id: contentRow
128 anchors {
64 anchors {
129 left: parent.left
65 left: parent.left
66+ top: parent.top
130 right: parent.right
67 right: parent.right
131− top: headerRow.bottom
132 bottom: parent.bottom
68 bottom: parent.bottom
69+ topMargin: root.contentSpacing
70+ leftMargin: root.contentSpacing
133− margins: root.contentSpacing
71+ rightMargin: root.contentSpacing
134 }
72 }
73+ height: childrenRect.height
135
74
136 spacing: units.gu(2)
75 spacing: units.gu(2)
137
76
77+
138 Flickable {
78 Flickable {
139 id: leftFlickable
79 id: leftFlickable
140 objectName: "leftFlickable"
80 objectName: "leftFlickable"
81+ anchors {
141− anchors.top: parent.top
82+ top: parent.top
142− anchors.bottom: parent.bottom
83+ bottom: parent.bottom
143− width: root.narrowMode ? contentRow.width : contentRow.width * root.previewWidthRatio
84+ }
85+ width: root.columnWidth
144− contentHeight: leftColumn.height
86+ contentHeight: leftColumn.height + root.contentSpacing
145 anchors.bottomMargin: root.keyboardSize
87 anchors.bottomMargin: root.keyboardSize
146 clip: true
88 clip: true
147
89
@@ -150,33 +92,44 @@ Item {
150 Column {
92 Column {
151 id: leftColumn
93 id: leftColumn
152 objectName: "leftColumn"
94 objectName: "leftColumn"
95+ height: childrenRect.height
96+ spacing: root.contentSpacing
153 anchors {
97 anchors {
154 left: parent.left
98 left: parent.left
155 right: parent.right
99 right: parent.right
156 }
100 }
157− height: childrenRect.height
101+ }
158− spacing: units.gu(1)
102+ }
159
103
160− Loader {
104+ Flickable {
161− id: headerLoader
105+ id: centerFlickable
162− anchors.left: parent.left
106+ anchors {
163− anchors.right: parent.right
107+ top: parent.top
164− sourceComponent: root.header
108+ bottom: parent.bottom
165− }
109+ }
166− Loader {
110+ width: root.columnWidth
167− id: buttonLoader
111+ contentHeight: centerColumn.height
112+ clip: true
113+
114+ Column {
115+ id: centerColumn
116+ objectName: "centerColumn"
117+ height: childrenRect.height
118+ anchors {
168− anchors.left: parent.left
119+ left: parent.left
169− anchors.right: parent.right
120+ right: parent.right
170− sourceComponent: root.buttons
171 }
121 }
122+ spacing: root.contentSpacing
172 }
123 }
173 }
124 }
174
125
175 Flickable {
126 Flickable {
176 id: rightFlickable
127 id: rightFlickable
128+ anchors {
177− anchors.top: parent.top
129+ top: parent.top
178− anchors.bottom: parent.bottom
130+ bottom: parent.bottom
179− width: narrowMode ? 0 : (contentRow.width - leftColumn.width - contentRow.spacing)
131+ }
132+ width: root.columnWidth
180 contentHeight: rightColumn.height
133 contentHeight: rightColumn.height
181 clip: true
134 clip: true
182
135
@@ -193,9 +146,52 @@ Item {
193 }
146 }
194
147
195 Loader {
148 Loader {
149+ id: previewImageLoader
150+ parent: leftColumn
151+ sourceComponent: root.previewImages
152+ anchors {
153+ left: parent.left
154+ right: parent.right
155+ }
156+ }
157+
158+ Loader {
159+ id: ratingsLoader
196 parent: root.narrowMode ? leftColumn : rightColumn
160 parent: root.narrowMode ? leftColumn : rightColumn
161+ anchors {
162+ left: parent.left
163+ right: parent.right
164+ }
165+ sourceComponent: root.ratings
166+ }
167+
168+ Loader {
169+ id: descriptionLoader
170+ parent: root.narrowMode ? leftColumn : centerColumn
171+ anchors {
197− anchors.left: parent.left
172+ left: parent.left
173+ right: parent.right
174+ }
175+ sourceComponent: root.description
176+ }
177+
178+ Loader {
179+ id: actionsLoader
180+ parent: root.narrowMode ? leftColumn : centerColumn
181+ anchors {
182+ left: parent.left
198− anchors.right: parent.right
183+ right: parent.right
184+ }
199− sourceComponent: root.body
185+ sourceComponent: root.actions
186+ }
187+
188+ Loader {
189+ id: headerLoader
190+ parent: root.narrowMode ? leftColumn : centerColumn
191+ anchors {
192+ left: parent.left
193+ right: parent.right
194+ }
195+ sourceComponent: root.header
200 }
196 }
201}
197}
Dash/Generic/GenericPreview.qml+158−70
@@ -16,90 +16,178 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19+import Ubuntu.Components.ListItems 0.1 as ListItems
20+import Ubuntu.DownloadDaemonListener 0.1
19import "../../Components"
21import "../../Components"
20−import "../../Components/ListItems" as ListItems
21import ".."
22import ".."
23+import "../Previews"
22
24
23DashPreview {
25DashPreview {
24 id: genericPreview
26 id: genericPreview
25
27
26− title: previewData.title
28+ previewImages: previewImagesComponent
27− previewWidthRatio: 0.6
29+ header: headerComponent
28−
30+ actions: (previewData.infoMap !== undefined && previewData.infoMap["show_progressbar"]) ? progressComponent : actionsComponent
29− property url url: previewData.image
31+ description: descriptionComponent
30−
32+
31− header: UbuntuShape {
33+ Component {
32− id: urlLoader
34+ id: previewImagesComponent
33− anchors.left: parent.left
35+
34− anchors.right: parent.right
36+ LazyImage {
35− height: width * previewImage.sourceSize.height / previewImage.sourceSize.width
37+ objectName: "genericPreviewImage"
36− radius: "medium"
38+ anchors {
37− image: Image {
39+ left: parent.left
38− id: previewImage
40+ right: parent.right
39− asynchronous: true
41+ }
42+ scaleTo: "width"
40− source: genericPreview.url
43+ source: genericPreview.previewData.image
41− fillMode: Image.PreserveAspectCrop
44+ initialHeight: width
45+ }
46+ }
47+
48+ Component {
49+ id: headerComponent
50+ Header {
51+ title: previewData.title
52+ subtitle: previewData.subtitle.replace(/[\r\n]+/g, "<br />")
42 }
53 }
43 }
54 }
44
55
45− buttons: GridView {
56+ Component {
46− id: buttons
57+ id: actionsComponent
58+
59+ Column {
60+ id: buttonList
61+ spacing: units.gu(1)
62+ Repeater {
63+ objectName: "buttonList"
47− model: genericPreview.previewData.actions
64+ model: previewData.actions
48−
65+
49− property int numOfRows: (count + 1) / 2
50− property int spacing: units.gu(1)
51− height: Math.max(units.gu(4), units.gu(4)*numOfRows + spacing*(numOfRows - 1))
52−
53− cellWidth: Math.max(units.gu(9), width / 2)
54− cellHeight: buttonHeight + spacing
55− property int buttonWidth: Math.max(0, width / 2 - spacing)
56− property int buttonHeight: units.gu(4)
57−
58− delegate: Button {
66+ delegate: Button {
59− width: Math.max(units.gu(4), buttons.buttonWidth)
67+ objectName: "button" + index
68+ width: parent.width
60− height: buttons.buttonHeight
69+ height: buttonList.buttonHeight
61− color: "#dd4814"
70+ color: Theme.palette.selected.foreground
62− text: modelData.displayName
71+ text: modelData.displayName
63− iconSource: modelData.iconHint
72+ iconSource: modelData.iconHint
64− iconPosition: "right"
73+ iconPosition: "right"
65− visible: true
66− onClicked: {
67− genericPreview.previewData.execute(modelData.id, { })
74+ onClicked: previewData.execute(modelData.id, { })
75+ }
68 }
76 }
69 }
77 }
70− focus: false
71 }
78 }
72− body: Column {
79+
80+ Component {
81+ id: progressComponent
82+
83+ ProgressBar {
84+ id: progressBar
85+ objectName: "progressBar"
86+ value: 0
87+ maximumValue: 100
73− spacing: units.gu(2)
88+ height: units.gu(5)
74−
89+
75− Label {
90+ property var model: previewData.actions
76− anchors { left: parent.left; right: parent.right }
91+
77− visible: text != ""
92+ DownloadTracker {
78− fontSize: "medium"
93+ service: "com.canonical.applications.Downloader"
79− opacity: 0.6
94+ dbusPath: previewData.infoMap["progressbar_source"] ? previewData.infoMap["progressbar_source"].value : ""
80− color: "white"
95+
81− text: previewData.subtitle.replace(/[\r\n]+/g, "<br />")
96+ onProgress: {
82− style: Text.Raised
97+ var percentage = parseInt(received * 100 / total);
83− styleColor: "black"
98+ progressBar.value = percentage;
84− wrapMode: Text.WordWrap
99+ }
85− textFormat: Text.RichText
100+
86− // FIXME: workaround for https://bugreports.qt-project.org/browse/QTBUG-33020
101+ onFinished: {
87− onWidthChanged: { wrapMode = Text.NoWrap; wrapMode = Text.WordWrap }
102+ previewData.execute(progressBar.model[0].id, { })
103+ }
104+
105+ onError: {
106+ previewData.execute(progressBar.model[1].id, { "error": error });
107+ }
108+ }
109+
88 }
110 }
111+ }
89
112
113+ Component {
114+ id: descriptionComponent
115+ Column {
116+ spacing: units.gu(2)
117+
90− Label {
118+ Label {
119+ id: descriptionLabel
91− anchors { left: parent.left; right: parent.right }
120+ anchors { left: parent.left; right: parent.right }
92− visible: text != ""
121+ visible: text != ""
93− fontSize: "small"
122+ fontSize: "small"
94− opacity: 0.6
123+ opacity: 0.6
95− color: "white"
124+ color: "white"
96− text: previewData.description.replace(/[\r\n]+/g, "<br />")
125+ text: previewData.description.replace(/[\r\n]+/g, "<br />")
97− style: Text.Raised
126+ style: Text.Raised
98− styleColor: "black"
127+ styleColor: "black"
99− wrapMode: Text.WordWrap
128+ wrapMode: Text.WordWrap
100− textFormat: Text.RichText
129+ textFormat: Text.RichText
101− // FIXME: workaround for https://bugreports.qt-project.org/browse/QTBUG-33020
130+ // FIXME: workaround for https://bugreports.qt-project.org/browse/QTBUG-33020
102− onWidthChanged: { wrapMode = Text.NoWrap; wrapMode = Text.WordWrap }
131+ onWidthChanged: { wrapMode = Text.NoWrap; wrapMode = Text.WordWrap }
132+ }
133+
134+ Column {
135+ objectName: "infoHintColumn"
136+ anchors {
137+ left: parent.left
138+ right: parent.right
139+ }
140+ Repeater {
141+ objectName: "infoHintRepeater"
142+ model: previewData.infoHints
143+
144+ delegate: Item {
145+ objectName: "infoHintItem" + index
146+ width: parent.width
147+ height: units.gu(5)
148+ Row {
149+ width: parent.width
150+ spacing: units.gu(1)
151+ property int columnWidth: (width - spacing) / 2
152+ anchors.verticalCenter: parent.verticalCenter
153+
154+ Label {
155+ objectName: "displayNameLabel"
156+ visible: valueLabel.visible
157+ fontSize: "small"
158+ opacity: 0.9
159+ color: "white"
160+ horizontalAlignment: Text.AlignLeft
161+ width: parent.columnWidth
162+ text: modelData.displayName
163+ style: Text.Raised
164+ styleColor: "black"
165+ }
166+ Label {
167+ id: valueLabel
168+ objectName: "valueLabel"
169+ visible: modelData.value != ""
170+ fontSize: "small"
171+ opacity: 0.6
172+ color: "white"
173+ horizontalAlignment: Text.AlignRight
174+ width: parent.columnWidth
175+ text: modelData.value ? modelData.value : ""
176+ style: Text.Raised
177+ styleColor: "black"
178+ wrapMode: Text.WordWrap
179+ }
180+ }
181+ ListItems.ThinDivider {
182+ anchors {
183+ left: parent.left
184+ bottom: parent.bottom
185+ right: parent.right
186+ }
187+ }
188+ }
189+ }
190+ }
103 }
191 }
104 }
192 }
105}
193}
Dash/Movie/MoviePreview.qmlnew file+71
@@ -0,0 +1,71 @@
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 ".."
20+import "../Generic"
21+import "../../Components"
22+import "../Previews"
23+
24+GenericPreview {
25+ id: root
26+
27+ property bool ready: previewData ? true : false
28+ property url url: ready ? previewData.image : ""
29+
30+ previewImages: previewImageComponent
31+ header: headerComponent
32+
33+ Component {
34+ id: previewImageComponent
35+ LazyImage {
36+ anchors.left: parent.left
37+ anchors.right: parent.right
38+ scaleTo: "width"
39+ visible: height > 0
40+ source: root.url
41+ initialHeight: width * 10 / 16
42+
43+ Image {
44+ objectName: "playButton"
45+ anchors.centerIn: parent
46+ readonly property bool bigButton: parent.width > units.gu(40)
47+ width: bigButton ? units.gu(8) : units.gu(4.5)
48+ height: width
49+ source: "../graphics/play_button%1%2.png".arg(previewImageMouseArea.pressed ? "_active" : "").arg(bigButton ? "_big" : "")
50+ }
51+
52+ MouseArea {
53+ id: previewImageMouseArea
54+ anchors.fill: parent
55+ onClicked: {
56+ Qt.openUrlExternally(previewData.result.uri);
57+ }
58+ }
59+ }
60+ }
61+
62+ Component {
63+ id: headerComponent
64+ Header {
65+ title: previewData.title
66+ rating: Math.round(previewData.rating * 10)
67+ reviews: previewData.numRatings
68+ rated: previewData.infoMap["rated"] ? previewData.infoMap["rated"].value : 0
69+ }
70+ }
71+}
Dash/PreviewDelegateMapper.qml+1
@@ -22,6 +22,7 @@ QtObject {
22 readonly property string appPreview: "Apps/AppPreview.qml"
22 readonly property string appPreview: "Apps/AppPreview.qml"
23 property var previewDelegateMapping: {"preview-generic": genericPreview,
23 property var previewDelegateMapping: {"preview-generic": genericPreview,
24 "preview-application": appPreview,
24 "preview-application": appPreview,
25+ "preview-movie": "Movie/MoviePreview.qml",
25 }
26 }
26 }
27 }
27
28
Dash/Previews/Header.qmlnew file+109
@@ -0,0 +1,109 @@
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 title: titleLabel.text
25+ property alias subtitle: subtitleLabel.text
26+ property alias rating: ratingStars.rating
27+ property int rated: 0
28+ property int reviews: 0
29+
30+ spacing: units.gu(2)
31+ height: imageShape.visible ? imageShape.height : contentColumn.height
32+
33+ UbuntuShape {
34+ id: imageShape
35+ width: height
36+ height: Math.max(units.gu(6), contentColumn.height)
37+ visible: image.source.toString().length > 0
38+ image: Image {
39+ id: image
40+ sourceSize { width: imageShape.width; height: imageShape.height }
41+ asynchronous: true
42+ fillMode: Image.PreserveAspectFit
43+ }
44+ }
45+
46+ Column {
47+ id: contentColumn
48+ spacing: units.gu(1)
49+ width: parent.width - x
50+
51+ Label {
52+ id: titleLabel
53+ objectName: "titleLabel"
54+ fontSize: "large"
55+ color: "white"
56+ style: Text.Raised
57+ styleColor: "black"
58+ opacity: .9
59+ width: parent.width
60+ elide: Text.ElideRight
61+ }
62+
63+ Label {
64+ id: subtitleLabel
65+ objectName: "subtitleLabel"
66+ fontSize: "medium"
67+ color: "white"
68+ style: Text.Raised
69+ styleColor: "black"
70+ opacity: .6
71+ visible: text.length > 0
72+ }
73+
74+ Row {
75+ visible: root.rating >= 0
76+ spacing: units.gu(1)
77+
78+ RatingStars {
79+ id: ratingStars
80+ maximumRating: 10
81+ rating: -1
82+ }
83+
84+ Label {
85+ id: ratedLabel
86+ objectName: "ratedLabel"
87+ fontSize: "medium"
88+ color: "white"
89+ style: Text.Raised
90+ styleColor: "black"
91+ opacity: .6
92+ //TRANSLATORS: Number of persons who rated this app/video/whatever
93+ text: i18n.tr("(%1)").arg(root.rated)
94+ }
95+
96+ Label {
97+ id: reviewsLabel
98+ objectName: "reviewsLabel"
99+ fontSize: "medium"
100+ color: "white"
101+ style: Text.Raised
102+ styleColor: "black"
103+ opacity: .6
104+ //TRANSLATORS: Number of persons who wrote reviews for this app/video/whatever
105+ text: i18n.tr("%1 review", "%1 reviews", root.reviews).arg(root.reviews)
106+ }
107+ }
108+ }
109+}
Dash/Apps/AppReviews.qml → Dash/Previews/Reviews.qmlrenamed

The content is the same.

Dash/Video/VideoPreview.qmldeleted−210
@@ -1,210 +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−import ".."
20−import "../../Components"
21−
22−DashPreview {
23− id: root
24−
25− property var item
26− property alias ready: nfo.ready
27− property bool playable: fileUri != ""
28− readonly property url fileUri: item ? item.fileUri : ""
29− property url url: item ? item.nfoUri.replace(/\.nfo$/, "-fanart.jpg") : ""
30−
31− VideoInfo {
32− id: nfo
33− source: item ? item.nfoUri : ""
34− }
35−
36− title: nfo.ready ? nfo.video.title : ""
37− previewWidthRatio: 0.6
38−
39− onPreviewImageClicked: {
40− if (playable) {
41− shell.activateApplication('/usr/share/applications/mediaplayer-app.desktop', root.fileUri);
42− }
43− }
44−
45− // TODO: replace this UbuntuShape with the Video component once that lands
46− // with the player.
47− header: UbuntuShape {
48− id: urlLoader
49− anchors.left: parent.left
50− anchors.right: parent.right
51− height: width * previewImage.sourceSize.height / previewImage.sourceSize.width
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−
77− buttons: Row {
78− spacing: units.gu(2)
79−
80− property int buttonWidth: Math.min(units.gu(22), (width - spacing) / 2)
81− Button {
82− width: parent.buttonWidth
83− color: Theme.palette.selected.foreground
84− text: nfo.ready && nfo.video.buyPrice != "" ? "Buy for " + nfo.video.buyPrice : ""
85− visible: text != ""
86− iconSource: "graphics/icon_button_amazon.png"
87− iconPosition: "right"
88− }
89− Button {
90− width: parent.buttonWidth
91− color: Theme.palette.selected.foreground
92− text: nfo.ready && nfo.video.buyPrice != "" ? "Rent for " + nfo.video.rentPrice : ""
93− visible: text != ""
94− iconSource: "graphics/icon_button_u1.png"
95− iconPosition: "right"
96− }
97− }
98−
99− body: Column {
100− spacing: units.gu(2)
101− RatingStars {
102− maximumRating: 10 // FIXME: this should happen on the backend side
103− rating: nfo.ready ? nfo.video.rating: 0
104− }
105−
106− Label {
107− text: nfo.ready ? nfo.video.plot : ""
108− color: Theme.palette.selected.backgroundText
109− opacity: .6
110− fontSize: "medium"
111− width: parent.width
112− wrapMode: Text.WordWrap
113− style: Text.Raised
114− styleColor: "black"
115− }
116−
117− Grid {
118− columns: 2
119− spacing: units.gu(1)
120− width: parent.width
121− property int firstColWidth: units.gu(9)
122− property int secondColWidth: width - firstColWidth - spacing
123− Label {
124− visible: directedLabel.visible
125− fontSize: "small"
126− opacity: 0.9
127− color: "white"
128− horizontalAlignment: Text.AlignRight
129− width: parent.firstColWidth
130− text: i18n.tr("Directed by:")
131− style: Text.Raised
132− styleColor: "black"
133− }
134− Label {
135− id: directedLabel
136− visible: text != ""
137− fontSize: "small"
138− opacity: 0.6
139− color: "white"
140− width: parent.secondColWidth
141− text: nfo.ready ? nfo.video.director : ""
142− style: Text.Raised
143− styleColor: "black"
144− wrapMode: Text.WordWrap
145− }
146− Label {
147− visible: starringLabel.visible
148− fontSize: "small"
149− opacity: 0.9
150− horizontalAlignment: Text.AlignRight
151− color: "white"
152− width: parent.firstColWidth
153− text: i18n.tr("Starring:")
154− style: Text.Raised
155− styleColor: "black"
156− }
157− Label {
158− id: starringLabel
159− visible: text != ""
160− fontSize: "small"
161− opacity: 0.6
162− color: "white"
163− width: parent.secondColWidth
164− text: nfo.ready ? nfo.getActors().join(", ") : ""
165− wrapMode: Text.WordWrap
166− style: Text.Raised
167− styleColor: "black"
168− }
169− Label {
170− visible: authorLabel.visible
171− fontSize: "small"
172− opacity: 0.9
173− horizontalAlignment: Text.AlignRight
174− color: "white"
175− width: parent.firstColWidth
176− text: i18n.tr("Author:")
177− style: Text.Raised
178− styleColor: "black"
179− }
180− Label {
181− id: authorLabel
182− visible: text != ""
183− fontSize: "small"
184− opacity: 0.6
185− color: "white"
186− width: parent.secondColWidth
187− text: nfo.ready ? nfo.video.author : ""
188− wrapMode: Text.WordWrap
189− style: Text.Raised
190− styleColor: "black"
191− }
192− }
193−
194− Label {
195− fontSize: "small"
196− opacity: 0.6
197− color: "white"
198− text: {
199− var parts = []
200− if (nfo.ready) {
201− if (nfo.video.year) parts.push(nfo.video.year)
202− if (nfo.video.runtime) parts.push("%1 minutes".arg(nfo.video.runtime))
203− }
204− return parts.join(", ");
205− }
206− style: Text.Raised
207− styleColor: "black"
208− }
209− }
210−}
tests/qmltests/CMakeLists.txt+2−1
@@ -45,13 +45,14 @@ add_qml_test(Dash Dash IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAUL
45add_qml_test(Dash DashBar IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${CMAKE_CURRENT_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
45add_qml_test(Dash DashBar IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${CMAKE_CURRENT_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
46add_qml_test(Dash DashContent IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS})
46add_qml_test(Dash DashContent IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS})
47add_qml_test(Dash DashPreview)
47add_qml_test(Dash DashPreview)
48+add_qml_test(Dash GenericPreview)
48add_qml_test(Dash GenericScopeView IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
49add_qml_test(Dash GenericScopeView IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
49add_qml_test(Dash ScopeView IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
50add_qml_test(Dash ScopeView IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
50add_qml_test(Dash FilterGrids IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins ${CMAKE_CURRENT_SOURCE_DIR}/plugins
51add_qml_test(Dash FilterGrids IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins ${CMAKE_CURRENT_SOURCE_DIR}/plugins
51 ${CMAKE_BINARY_DIR}/tests/mocks)
52 ${CMAKE_BINARY_DIR}/tests/mocks)
52add_qml_test(Dash/Apps RunningApplicationsGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
53add_qml_test(Dash/Apps RunningApplicationsGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
53add_qml_test(Dash/Apps AppPreview IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
54add_qml_test(Dash/Apps AppPreview IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
54−add_qml_test(Dash/Video VideoPreview IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
55+add_qml_test(Dash/Movie MoviePreview IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks)
55add_qml_test(Greeter Lockscreen IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
56add_qml_test(Greeter Lockscreen IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
56 PROPERTIES ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
57 PROPERTIES ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
57add_qml_test(Greeter Tablet IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
58add_qml_test(Greeter Tablet IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
tests/qmltests/Dash/Apps/tst_AppPreview.qml+57−25
@@ -42,6 +42,7 @@ Item {
42 QtObject { id: rated; property int value: 120 }
42 QtObject { id: rated; property int value: 120 }
43 QtObject { id: reviews; property int value: 8 }
43 QtObject { id: reviews; property int value: 8 }
44 QtObject { id: progress; property string value: "source" }
44 QtObject { id: progress; property string value: "source" }
45+ QtObject { id: publisher; property string value: "Ubuntu Developer" }
45 QtObject { id: comments; property var value: [
46 QtObject { id: comments; property var value: [
46 ["Unity User", 4, "08/20/2013", root.commentary],
47 ["Unity User", 4, "08/20/2013", root.commentary],
47 ["Unity User", 8, "01/15/2013", root.commentary],
48 ["Unity User", 8, "01/15/2013", root.commentary],
@@ -62,7 +63,8 @@ Item {
62 property var infoMap: {
63 property var infoMap: {
63 "more-screenshots": screenshots,
64 "more-screenshots": screenshots,
64 "rated": rated,
65 "rated": rated,
65− "comments": comments
66+ "comments": comments,
67+ "publisher": publisher
66 }
68 }
67 property var actions: [
69 property var actions: [
68 { "id": 123, "displayName": "action1" },
70 { "id": 123, "displayName": "action1" },
@@ -84,6 +86,7 @@ Item {
84 "more-screenshots": screenshots,
86 "more-screenshots": screenshots,
85 "rated": rated,
87 "rated": rated,
86 "comments": comments,
88 "comments": comments,
89+ "publisher": publisher,
87 "progressbar_source": progressSource
90 "progressbar_source": progressSource
88 }
91 }
89 property var actions: [
92 property var actions: [
@@ -109,26 +112,31 @@ Item {
109 name: "AppPreview"
112 name: "AppPreview"
110 when: windowShown
113 when: windowShown
111
114
115+ function init() {
116+ waitForRendering(appPreview)
117+ }
118+
112 function cleanup() {
119 function cleanup() {
113 root.calls = new Array();
120 root.calls = new Array();
114 sendPreviewSpy.clear();
121 sendPreviewSpy.clear();
122+ // TODO: This doesn't work right now as Reviews are disabled
115− var reviewField = findChild(appPreview, "reviewField");
123+ //var reviewField = findChild(appPreview, "reviewField");
116− reviewField.focus = false;
124+ //reviewField.focus = false;
117− reviewField.text = "";
125+ //reviewField.text = "";
118− data.rating = rating.value;
126+ //data.rating = rating.value;
119 dataProgress.infoMap["progressbar_source"].value = "service";
127 dataProgress.infoMap["progressbar_source"].value = "service";
120− var appReviews = findChild(appPreview, "appReviews");
128+ //var appReviews = findChild(appPreview, "appReviews");
121− appReviews.visible = false;
129+ //appReviews.visible = false;
122− appPreview.keyboardSize = 0;
130+ //appPreview.keyboardSize = 0;
123 }
131 }
124
132
125 function test_actions() {
133 function test_actions() {
126− var buttons = findChild(appPreview, "gridButtons");
134+ var buttons = findChild(appPreview, "buttonList");
127 compare(buttons.count, 3, "Not the proper amount of actions detected.");
135 compare(buttons.count, 3, "Not the proper amount of actions detected.");
128
136
129 for(var i = 0; i < buttons.count; i++) {
137 for(var i = 0; i < buttons.count; i++) {
130− buttons.currentIndex = i;
138+ var button = findChild(appPreview, "button" + i);
131− buttons.currentItem.clicked();
139+ mouseClick(button, 1, 1);
132 }
140 }
133
141
134 var actions = data.actions;
142 var actions = data.actions;
@@ -137,6 +145,14 @@ Item {
137 }
145 }
138 }
146 }
139
147
148+ function test_check_app_info() {
149+ var appInfo = findChild(appPreview, "previewHeader");
150+ var titleLabel = findChild(appInfo, "titleLabel");
151+ var subtitleLabel = findChild(appInfo, "subtitleLabel");
152+ compare(titleLabel.text, data.title, "App Title doesn't match.");
153+ compare(subtitleLabel.text, publisher.value, "Publisher name doesn't match.");
154+ }
155+
140 function test_rated() {
156 function test_rated() {
141 var rated = findChild(appPreview, "ratedLabel");
157 var rated = findChild(appPreview, "ratedLabel");
142 compare(rated.text, "(120)", "Rates not equal");
158 compare(rated.text, "(120)", "Rates not equal");
@@ -147,13 +163,16 @@ Item {
147 compare(rated.text, "8 reviews", "Reviews don't match");
163 compare(rated.text, "8 reviews", "Reviews don't match");
148 }
164 }
149
165
166+ // TODO: Disabled as reviewing and commenting is currently disabled
150− function test_send_review() {
167+/* function test_send_review() {
151 var appReviews = findChild(appPreview, "appReviews");
168 var appReviews = findChild(appPreview, "appReviews");
152 appReviews.sendReview("review");
169 appReviews.sendReview("review");
153 sendPreviewSpy.wait();
170 sendPreviewSpy.wait();
154 }
171 }
155
172
156 function test_review_focus() {
173 function test_review_focus() {
174+ var columnReviewRating = findChild(appPreview, "columnReviewRating");
175+ columnReviewRating.visible = true;
157 var appReviews = findChild(appPreview, "appReviews");
176 var appReviews = findChild(appPreview, "appReviews");
158 appReviews.visible = true;
177 appReviews.visible = true;
159 var sendButton = findChild(appReviews, "sendButton");
178 var sendButton = findChild(appReviews, "sendButton");
@@ -194,6 +213,22 @@ Item {
194 verify(buttons.visible == true);
213 verify(buttons.visible == true);
195 }
214 }
196
215
216+ function test_automatic_scroll_on_keyboard_shown() {
217+ waitForRendering(appPreview);
218+ var appReviews = findChild(appPreview, "appReviews");
219+ appReviews.visible = true;
220+
221+ var leftFlickable = findChild(appPreview, "leftFlickable");
222+ leftFlickable.contentY = leftFlickable.contentHeight;
223+ var reviewField = findChild(appReviews, "reviewField");
224+ appPreview.keyboardSize = 400;
225+ appReviews.editing(reviewField);
226+ var newFlickPos = leftFlickable.contentY;
227+ var keyboardY = shell.height - appPreview.keyboardSize;
228+ verify(newFlickPos < keyboardY);
229+ }
230+
231+*/
197 function test_progress_show() {
232 function test_progress_show() {
198 appPreview.previewData = dataProgress;
233 appPreview.previewData = dataProgress;
199 var progress = findChild(appPreview, "progressBar");
234 var progress = findChild(appPreview, "progressBar");
@@ -220,20 +255,17 @@ Item {
220 compare(root.calls[0][1], {"error": "DOWNLOAD ERROR"}, "Data of action not found.");
255 compare(root.calls[0][1], {"error": "DOWNLOAD ERROR"}, "Data of action not found.");
221 }
256 }
222
257
223− function test_automatic_scroll_on_keyboard_shown() {
258+ function test_placeholderScreenshot() {
224− waitForRendering(appPreview);
225− var appReviews = findChild(appPreview, "appReviews");
259+ var placeholderScreenshot = findChild(appPreview, "placeholderScreenshot");
226− appReviews.visible = true;
260+ compare(placeholderScreenshot.visible, false);
227
261
228− var leftFlickable = findChild(appPreview, "leftFlickable");
262+ data.infoMap["more-screenshots"] = [];
229− leftFlickable.contentY = leftFlickable.contentHeight;
263+ appPreview.previewData = data;
230− var reviewField = findChild(appReviews, "reviewField");
264+ tryCompare(placeholderScreenshot, "visible", true);
231− appPreview.keyboardSize = 400;
232− appReviews.editing(reviewField);
233− var newFlickPos = leftFlickable.contentY;
234− var keyboardY = shell.height - appPreview.keyboardSize;
235− verify(newFlickPos < keyboardY);
236− }
237
265
266+ data.infoMap["more-screenshots"] = screenshots;
267+ appPreview.previewData = data;
268+ tryCompare(placeholderScreenshot, "visible", false);
269+ }
238 }
270 }
239}
271}
tests/qmltests/Dash/Movie/tst_MoviePreview.qmlnew file+85
@@ -0,0 +1,85 @@
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/Movie"
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+ property var calls: []
28+ property int counter: 0
29+
30+ function get_actions_data() {
31+ return [
32+ { id: 123, displayName: "Play", iconHint: "image://theme/search" },
33+ { id: 456, displayName: "Buy", iconHint: "image://theme/search" },
34+ { id: 789, displayName: "Delete", iconHint: "image//theme/search" }
35+ ]
36+ }
37+
38+ function fake_callback(id, data){
39+ root.calls[root.calls.length] = id;
40+ }
41+
42+ // The component under test
43+ MoviePreview {
44+ id: moviePreview
45+ anchors.fill: parent
46+
47+ previewData: QtObject {
48+ property string rendererName: "preview-movie"
49+ property string title: "Unity Movie"
50+ property string subtitle: "Subtitle"
51+ property string description: "This is the description"
52+ property string image: "../../tests/qmltests/Components/tst_LazyImage/square.png"
53+ property var actions: get_actions_data()
54+ property string year: "2013"
55+ property real rating: 0.3
56+ property int numRatings: 1
57+ property var execute: fake_callback
58+ }
59+ }
60+
61+ UT.UnityTestCase {
62+ name: "MoviePreviewTest"
63+ when: windowShown
64+
65+ function init() {
66+ waitForRendering(moviePreview);
67+ root.calls = new Array();
68+ }
69+
70+ function test_actions() {
71+ var buttons = findChild(moviePreview, "buttonList");
72+ compare(buttons.count, 3);
73+
74+ for(var i = 0; i < buttons.count; i++) {
75+ var button = findChild(moviePreview, "button" + i);
76+ mouseClick(button, 1, 1);
77+ }
78+
79+ var actions = get_actions_data();
80+ for(var i = 0; i < actions.length; i++) {
81+ compare(root.calls[i], actions[i].id);
82+ }
83+ }
84+ }
85+}
tests/qmltests/Dash/Video/tst_VideoPreview.qmldeleted−63
@@ -1,63 +0,0 @@
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+66−19
@@ -31,15 +31,38 @@ Item {
31 DashPreview {
31 DashPreview {
32 id: preview
32 id: preview
33 anchors.fill: parent
33 anchors.fill: parent
34− title: "Testing rocks, debugging sucks!"
35
34
36− buttons: Row {
35+ previewImages: Rectangle {
36+ width: parent.width
37+ height: units.gu(20)
38+ color: "honeydew"
39+ Label {
40+ anchors.centerIn: parent
41+ text: "Preview images"
42+ }
43+ }
44+
45+ header: Rectangle {
46+ width: parent.width
47+ height: units.gu(10)
48+ color: "papayawhip"
49+ Label {
50+ anchors.centerIn: parent
51+ text: "Caption label"
52+ }
53+ }
54+
55+ actions: Row {
37 width: parent.width
56 width: parent.width
38 height: units.gu(5)
57 height: units.gu(5)
39 Rectangle {
58 Rectangle {
40 width: parent.width / 3
59 width: parent.width / 3
41 height: parent.height
60 height: parent.height
42 color: "blue"
61 color: "blue"
62+ Label {
63+ anchors.centerIn: parent
64+ text: "Button 1"
65+ }
43 MouseArea {
66 MouseArea {
44 id: buttonMouseArea
67 id: buttonMouseArea
45 objectName: "buttonMouseArea"
68 objectName: "buttonMouseArea"
@@ -51,12 +74,15 @@ Item {
51 width: parent.width / 3
74 width: parent.width / 3
52 height: parent.height
75 height: parent.height
53 color: "green"
76 color: "green"
77+ Label {
78+ anchors.centerIn: parent
79+ text: "Button 2"
80+ }
81+
54 }
82 }
55 }
83 }
56
84
57− header: Label { text: "Caption label" }
58−
59− body: Column {
85+ description: Column {
60 id: testContent
86 id: testContent
61 objectName: "testContent"
87 objectName: "testContent"
62 width: parent.width
88 width: parent.width
@@ -65,16 +91,38 @@ Item {
65 width: parent.width
91 width: parent.width
66 height: parent.height / 3
92 height: parent.height / 3
67 color: "green"
93 color: "green"
94+ Label {
95+ anchors.centerIn: parent
96+ text: "Description part 1"
97+ }
68 }
98 }
69 Rectangle {
99 Rectangle {
70 width: parent.width
100 width: parent.width
71 height: parent.height / 3
101 height: parent.height / 3
72 color: "red"
102 color: "red"
103+ Label {
104+ anchors.centerIn: parent
105+ text: "Description part 2"
106+ }
73 }
107 }
74 Rectangle {
108 Rectangle {
75 width: parent.width
109 width: parent.width
76 height: parent.height / 3
110 height: parent.height / 3
77 color: "blue"
111 color: "blue"
112+ Label {
113+ anchors.centerIn: parent
114+ text: "Description part 3"
115+ }
116+ }
117+ }
118+
119+ ratings: Rectangle {
120+ objectName: "ratings"
121+ width: parent.width
122+ height: units.gu(20)
123+ Label {
124+ anchors.centerIn: parent
125+ text: "Ratings"
78 }
126 }
79 }
127 }
80 }
128 }
@@ -95,42 +143,41 @@ Item {
95 name: "DashPreview"
143 name: "DashPreview"
96 when: windowShown
144 when: windowShown
97
145
98− function test_close() {
99− var title = findChild(preview, "titleLabel")
100− mouseClick(title, 1, 1)
101− compare(closeSpy.count, 1, "Close signal not emitted")
102− }
103−
104 function test_columns_data() {
146 function test_columns_data() {
105 return [
147 return [
106 {tag: "1 columns", width: units.gu(5), height: units.gu(10), columns: 1},
148 {tag: "1 columns", width: units.gu(5), height: units.gu(10), columns: 1},
107− {tag: "2 columns", width: units.gu(50), height: units.gu(10), columns: 2}
149+ {tag: "3 columns", width: units.gu(50), height: units.gu(10), columns: 3}
108 ]
150 ]
109 }
151 }
110
152
111 function test_columns(data) {
153 function test_columns(data) {
112 var leftCol = findChild(preview, "leftColumn")
154 var leftCol = findChild(preview, "leftColumn")
155+ var centerCol = findChild(preview, "centerColumn")
113 var rightCol = findChild(preview, "rightColumn")
156 var rightCol = findChild(preview, "rightColumn")
114
157
115 root.width = data.width
158 root.width = data.width
116 root.height = data.height
159 root.height = data.height
117
160
118− // there are 2 columns in DashPreview. On portrait form factors
161+ // there are 3 columns in DashPreview. On portrait form factors
119− // only the left one is used and the right one should be empty.
162+ // only the left one is used and the center and right ones should be empty.
120− // to find out if the content is in the correct column, we get a
163+ // To find out if the content is in the correct column, we get a
121 // reference to the column objects and search only the subtree
164 // reference to the column objects and search only the subtree
122 // to see if the content is in the correct column.
165 // to see if the content is in the correct column.
123− // 1 colum -> content should be in leftColumn
166+ // 1 colum -> all content should be in leftColumn
124− // 2 colums -> content should be in rightColumn
167+ // 3 colums -> content should be in centerColumn, rating in rightColumn
125
168
126 switch(data.columns) {
169 switch(data.columns) {
127 case 1:
170 case 1:
128 var testContent = findChild(leftCol, "testContent")
171 var testContent = findChild(leftCol, "testContent")
129 compare(testContent.objectName, "testContent")
172 compare(testContent.objectName, "testContent")
173+ testContent = findChild(leftCol, "ratings")
174+ compare(testContent.objectName, "ratings")
130 break
175 break
131− case 2:
176+ case 3:
132− var testContent = findChild(rightCol, "testContent")
177+ var testContent = findChild(centerCol, "testContent")
133 compare(testContent.objectName, "testContent")
178 compare(testContent.objectName, "testContent")
179+ testContent = findChild(rightCol, "ratings")
180+ compare(testContent.objectName, "ratings")
134 break
181 break
135 }
182 }
136
183
tests/qmltests/Dash/tst_GenericPreview.qmlnew file+116
@@ -0,0 +1,116 @@
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/Generic"
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+ property var calls: []
28+ property int counter: 0
29+
30+ function get_actions_data() {
31+ return [
32+ { id: 123, displayName: "Button 1", iconHint: "image://theme/search" },
33+ { id: 456, displayName: "Button 2", iconHint: "image://theme/search" },
34+ { id: 789, displayName: "Button 3", iconHint: "image://theme/search" }
35+ ]
36+ }
37+
38+ function fake_callback(id, data){
39+ root.calls[root.calls.length] = id;
40+ }
41+
42+ // The component under test
43+ GenericPreview {
44+ id: genericPreview
45+ anchors.fill: parent
46+
47+ previewData: QtObject {
48+ id: dataObject
49+ property string rendererName: "preview-generic"
50+ property string title: "Unity Generic Preview"
51+ property string subtitle: "Subtitle"
52+ property string description: "This is the description"
53+ property string image: "../../tests/qmltests/Components/tst_LazyImage/square.png"
54+ property var actions: get_actions_data()
55+ property string year: "2013"
56+ property var execute: fake_callback
57+ property var infoHints: [{displayName: "info 1", value: "value 1"},
58+ {displayName: "info 2", value: "value 2"}
59+ ]
60+ }
61+ }
62+
63+ UT.UnityTestCase {
64+ name: "GenericPreviewTest"
65+ when: windowShown
66+
67+ function init() {
68+ waitForRendering(genericPreview)
69+ root.calls = new Array();
70+ }
71+
72+ function test_actions() {
73+ var buttons = findChild(genericPreview, "buttonList");
74+ compare(buttons.count, 3);
75+
76+ for(var i = 0; i < buttons.count; i++) {
77+ var button = findChild(genericPreview, "button" + i);
78+ mouseClick(button, 1, 1);
79+ }
80+
81+ var actions = get_actions_data();
82+ for(var i = 0; i < actions.length; i++) {
83+ compare(root.calls[i], actions[i].id);
84+ }
85+ }
86+
87+ function test_title() {
88+ var titleLabel = findChild(genericPreview, "titleLabel");
89+ compare(titleLabel.text, dataObject.title)
90+ }
91+
92+ function test_subtitle() {
93+ var subtitleLabel = findChild(genericPreview, "subtitleLabel");
94+ compare(subtitleLabel.text, dataObject.subtitle)
95+ }
96+
97+ function test_image() {
98+ var image = findChild(genericPreview, "genericPreviewImage");
99+ tryCompare(image, "height", genericPreview.width - units.gu(6))
100+ }
101+
102+ function test_infoHints() {
103+ var infoHintColumn = findChild(genericPreview, "infoHintColumn")
104+ var infoHintRepeater = findChild(genericPreview, "infoHintRepeater")
105+ compare(infoHintRepeater.count, dataObject.infoHints.length)
106+
107+ for (var i = 0; i < dataObject.infoHints.length; ++i) {
108+ var infoItem = findChild(infoHintColumn, "infoHintItem" + i)
109+ var displayName = findChild(infoItem, "displayNameLabel")
110+ compare(displayName.text, dataObject.infoHints[i].displayName)
111+ var value = findChild(infoItem, "valueLabel")
112+ compare(value.text, dataObject.infoHints[i].value)
113+ }
114+ }
115+ }
116+}