on xenial help sign in
Introduce the Audio card type
commit2258785858b49fd5f3deb12ce6a8a1b22804caea authorAlbert Astals Cid <albert.astals@canonical.com> dateFri 15 May 2015, 14:57 (11 years ago) parent9d12c18
12 files changed+197−41
plugins/Dash/CMakeLists.txt+4 plugins/Dash/CardAudioProgress.qml+59 plugins/Dash/CardCreator.js+103−1 qml/Dash/DashAudioPlayer/DashAudioPlayer.qml → plugins/Dash/DashAudioPlayer.qml+13−2 plugins/Dash/graphics/music_progress_bg.png plugins/Dash/qmldir+2 qml/Dash/DashAudioPlayer/qmldir−1 qml/Dash/Previews/PreviewAudioPlayback.qml+5−33 qml/Dash/Previews/graphics/music_progress_bg.png tests/qmltests/Dash/CardHelpers.js+1−1 tests/qmltests/Dash/Previews/tst_PreviewAudioPlayback.qml+2−2 tests/qmltests/Dash/tst_Card.qml+8−1
plugins/Dash/CMakeLists.txt+4
@@ -40,3 +40,7 @@ target_link_libraries(Dash-qml
4040qt5_use_modules(Dash-qml Qml Quick Concurrent)
4141
4242add_unity8_plugin(Dash 0.1 Dash TARGETS Dash-qml)
43+
44+install(FILES graphics/music_progress_bg.png DESTINATION ${SHELL_INSTALL_QML}/graphics/)
45+file(COPY graphics/music_progress_bg.png DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/graphics/)
46+
plugins/Dash/CardAudioProgress.qmlnew file+59
@@ -0,0 +1,59 @@
1+/*
2+ * Copyright 2015 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.4
18+import Ubuntu.Components 1.3
19+import Dash 0.1
20+
21+Item {
22+ id: root
23+
24+ implicitHeight: positionLabel.y + positionLabel.height
25+
26+ property int duration: 0
27+ property url source
28+ property color color: Theme.palette.normal.baseText
29+ readonly property int position: DashAudioPlayer.isCurrentSource(source) ? DashAudioPlayer.position / 1000 : 0
30+
31+ AudioProgressBar {
32+ id: progress
33+ anchors { left: parent.left; right: parent.right }
34+ source: root.source
35+ }
36+
37+ Label {
38+ id: positionLabel
39+ anchors {
40+ left: parent.left
41+ top: progress.bottom
42+ }
43+ verticalAlignment: Text.AlignBottom
44+ fontSize: "x-small"
45+ text: DashAudioPlayer.lengthToString(root.position)
46+ color: root.color
47+ }
48+
49+ Label {
50+ anchors {
51+ right: parent.right
52+ top: progress.bottom
53+ }
54+ verticalAlignment: Text.AlignBottom
55+ fontSize: "x-small"
56+ text: DashAudioPlayer.lengthToString(duration)
57+ color: root.color
58+ }
59+}
plugins/Dash/CardCreator.js+103−1
@@ -96,6 +96,40 @@ var kArtShapeHolderCode = 'Item { \n\
9696 } \n\
9797 }\n';
9898
99+// %1 is anchors.fill
100+// %2 is width
101+// %3 is height
102+var kAudioButtonCode = 'AbstractButton { \n\
103+ id: audioButton; \n\
104+ anchors.fill: %1; \n\
105+ width: %2; \n\
106+ height: %3; \n\
107+ readonly property url source: (cardData["quickPreviewData"] && cardData["quickPreviewData"]["uri"]) || ""; \n\
108+ UbuntuShape { \n\
109+ anchors.fill: parent; \n\
110+ visible: parent.pressed; \n\
111+ radius: "medium"; \n\
112+ borderSource: "radius_pressed.sci" \n\
113+ } \n\
114+ Icon { \n\
115+ anchors.fill: parent; \n\
116+ anchors.margins: parent.height > units.gu(5) ? units.gu(2) : units.gu(0); \n\
117+ opacity: 0.9; \n\
118+ name: DashAudioPlayer.isCurrentSource(parent.source) && DashAudioPlayer.playing ? "media-playback-pause" : "media-playback-start"; \n\
119+ } \n\
120+ onClicked: { \n\
121+ if (DashAudioPlayer.isCurrentSource(source)) { \n\
122+ if (DashAudioPlayer.playing) { \n\
123+ DashAudioPlayer.pause(); \n\
124+ } else { \n\
125+ DashAudioPlayer.play(); \n\
126+ } \n\
127+ } else { \n\
128+ DashAudioPlayer.playSource(source); \n\
129+ } \n\
130+ } \n\
131+ }';
132+
99133var kOverlayLoaderCode = 'Loader { \n\
100134 id: overlayLoader; \n\
101135 anchors { \n\
@@ -300,6 +334,22 @@ var kSummaryLabelCode = 'Label { \n\
300334 color: %3; \n\
301335 }\n';
302336
337+// %1 is used as bottom anchor of audio progress bar
338+// %2 is used as left anchor of audio progress bar
339+// %3 is used as text color
340+var kAudioProgressBarCode = 'CardAudioProgress { \n\
341+ id: audioProgressBar; \n\
342+ duration: (cardData["quickPreviewData"] && cardData["quickPreviewData"]["duration"]) || 0; \n\
343+ source: (cardData["quickPreviewData"] && cardData["quickPreviewData"]["uri"]) || ""; \n\
344+ anchors { \n\
345+ bottom: %1; \n\
346+ left: %2; \n\
347+ right: parent.right; \n\
348+ margins: units.gu(1); \n\
349+ } \n\
350+ color: %3; \n\
351+ }';
352+
303353function cardString(template, components) {
304354 var code;
305355 code = 'AbstractButton { \n\
@@ -333,6 +383,19 @@ function cardString(template, components) {
333383 var hasSubtitle = hasTitle && components["subtitle"] || false;
334384 var hasHeaderRow = hasMascot && hasTitle;
335385 var hasAttributes = hasTitle && components["attributes"]["field"] || false;
386+ var isAudio = template["quickPreviewType"] === "audio";
387+
388+ if (isAudio) {
389+ // For now we only support audio cards with [optional] art, title, subtitle
390+ // in horizontal mode
391+ // Anything else makes it behave not like an audio card
392+ if (hasSummary) isAudio = false;
393+ if (!isHorizontal) isAudio = false;
394+ if (hasMascot) isAudio = false;
395+ if (hasEmblem) isAudio = false;
396+ if (headerAsOverlay) isAudio = false;
397+ if (hasAttributes) isAudio = false;
398+ }
336399
337400 if (hasBackground) {
338401 code += kBackgroundLoaderCode;
@@ -388,17 +451,22 @@ function cardString(template, components) {
388451 topMargin: units.gu(1);\n';
389452 }
390453 }
454+
391455 var headerLeftAnchor;
392456 var headerLeftAnchorHasMargin = false;
393457 if (isHorizontal && hasArt) {
394458 headerLeftAnchor = 'left: artShapeHolder.right; \n\
395459 leftMargin: units.gu(1);\n';
396460 headerLeftAnchorHasMargin = true;
461+ } else if (isHorizontal && isAudio) {
462+ headerLeftAnchor = 'left: audioButton.right; \n\
463+ leftMargin: units.gu(1);\n';
464+ headerLeftAnchorHasMargin = true;
397465 } else {
398466 headerLeftAnchor = 'left: parent.left;\n';
399467 }
400468
401− var touchdownOnArtShape = !hasBackground && hasArt && !hasMascot && !hasSummary;
469+ var touchdownOnArtShape = !hasBackground && hasArt && !hasMascot && !hasSummary && !isAudio;
402470
403471 if (hasHeaderRow) {
404472 code += 'readonly property int headerHeight: row.height;\n'
@@ -412,6 +480,14 @@ function cardString(template, components) {
412480 } else {
413481 code += 'readonly property int headerHeight: attributesRow.height;\n'
414482 }
483+ } else if (isAudio) {
484+ if (hasSubtitle) {
485+ code += 'readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin + audioProgressBar.height + audioProgressBar.anchors.topMargin;\n'
486+ } else if (hasTitle) {
487+ code += 'readonly property int headerHeight: titleLabel.height + audioProgressBar.height + audioProgressBar.anchors.topMargin;\n'
488+ } else {
489+ code += 'readonly property int headerHeight: audioProgressBar.height;\n'
490+ }
415491 } else if (hasSubtitle) {
416492 code += 'readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;\n'
417493 } else if (hasTitle) {
@@ -593,6 +669,30 @@ function cardString(template, components) {
593669 code += mascotShapeCode + mascotCode + titleSubtitleCode;
594670 }
595671
672+ if (isAudio) {
673+ var audioProgressBarLeftAnchor = 'audioButton.right';
674+ var audioProgressBarBottomAnchor = 'audioButton.bottom';
675+ var audioProgressBarTextColor = 'root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText';
676+
677+ code += kAudioProgressBarCode.arg(audioProgressBarBottomAnchor)
678+ .arg(audioProgressBarLeftAnchor)
679+ .arg(audioProgressBarTextColor);
680+
681+ var audioButtonAnchorsFill;
682+ var audioButtonWidth;
683+ var audioButtonHeight;
684+ if (hasArt) {
685+ audioButtonAnchorsFill = 'artShapeHolder';
686+ audioButtonWidth = 'undefined';
687+ audioButtonHeight = 'undefined';
688+ } else {
689+ audioButtonAnchorsFill = 'undefined';
690+ audioButtonWidth = 'height';
691+ audioButtonHeight = '(root.fixedHeaderHeight > 0 ? root.fixedHeaderHeight : headerHeight) + 2 * units.gu(1)';
692+ }
693+ code += kAudioButtonCode.arg(audioButtonAnchorsFill).arg(audioButtonWidth).arg(audioButtonHeight);
694+ }
695+
596696 if (hasSummary) {
597697 var summaryTopAnchor;
598698 if (isHorizontal && hasArt) summaryTopAnchor = 'artShapeHolder.bottom';
@@ -631,6 +731,8 @@ function cardString(template, components) {
631731 var implicitHeight = 'implicitHeight: ';
632732 if (hasSummary) {
633733 implicitHeight += 'summary.y + summary.height + units.gu(1);\n';
734+ } else if (isAudio) {
735+ implicitHeight += 'audioButton.height;\n';
634736 } else if (hasHeaderRow) {
635737 implicitHeight += 'row.y + row.height + units.gu(1);\n';
636738 } else if (hasMascot) {
qml/Dash/DashAudioPlayer/DashAudioPlayer.qml → plugins/Dash/DashAudioPlayer.qmlrenamed+13−2
@@ -24,8 +24,19 @@ QtObject {
2424 readonly property bool playing: audio.playbackState === Audio.PlayingState
2525 readonly property bool paused: audio.playbackState === Audio.PausedState
2626 readonly property bool stopped: audio.playbackState === Audio.StoppedState
27+ readonly property alias position: audio.position
2728
28− property alias source: audio.source
29+ function isCurrentSource(source) {
30+ return source === audio.source && source != "";
31+ }
32+
33+ function playSource(newSource) {
34+ stop();
35+ // Make sure we change the source, even if two items point to the same uri location
36+ audio.source = "";
37+ audio.source = newSource;
38+ play();
39+ }
2940
3041 function stop() {
3142 audio.stop();
@@ -47,7 +58,7 @@ QtObject {
4758 }
4859
4960 function lengthToString(s) {
50− if (typeof(s) !== "number" || s <= 0) return "";
61+ if (typeof(s) !== "number" || s < 0) return "";
5162
5263 var sec = "" + s % 60;
5364 if (sec.length == 1) sec = "0" + sec;
plugins/Dash/graphics/music_progress_bg.pngnew file

Binary file not shown.

plugins/Dash/qmldir+2
@@ -2,6 +2,8 @@ module Dash
22plugin Dash-qml
33typeinfo Dash.qmltypes
44singleton CardCreatorCache 0.1 CardCreatorCache.qml
5+singleton DashAudioPlayer 0.1 DashAudioPlayer.qml
56ScopeStyle 0.1 ScopeStyle.qml
67CardAttributes 0.1 CardAttributes.qml
78CroppedImageMinimumSourceSize 0.1 CroppedImageMinimumSourceSize.qml
9+AudioProgressBar 0.1 AudioProgressBar.qml
qml/Dash/DashAudioPlayer/qmldirdeleted−1
@@ -1 +0,0 @@
1−singleton DashAudioPlayer 1.0 DashAudioPlayer.qml
qml/Dash/Previews/PreviewAudioPlayback.qml+5−33
@@ -16,7 +16,7 @@
1616
1717import QtQuick 2.0
1818import Ubuntu.Components 0.1
19−import "../DashAudioPlayer"
19+import Dash 0.1
2020
2121/*! \brief Preview widget for audio tracks.
2222
@@ -45,20 +45,12 @@ PreviewWidget {
4545 objectName: "trackRepeater"
4646 model: root.widgetData["tracks"]
4747
48− function play(item, source) {
49− DashAudioPlayer.stop();
50− // Make sure we change the source, even if two items point to the same uri location
51− DashAudioPlayer.source = "";
52− DashAudioPlayer.source = source;
53− DashAudioPlayer.play();
54− }
55−
5648 delegate: Item {
5749 id: trackItem
5850 objectName: "trackItem" + index
5951
6052 readonly property url sourceUrl: modelData["source"]
61− readonly property bool isPlayingItem: DashAudioPlayer.source == sourceUrl
53+ readonly property bool isPlayingItem: DashAudioPlayer.isCurrentSource(sourceUrl)
6254
6355 anchors { left: parent.left; right: parent.right }
6456 height: units.gu(5)
@@ -92,7 +84,7 @@ PreviewWidget {
9284 DashAudioPlayer.play();
9385 }
9486 } else {
95− trackRepeater.play(trackItem, sourceUrl);
87+ DashAudioPlayer.playSource(sourceUrl);
9688 }
9789 }
9890 }
@@ -128,30 +120,10 @@ PreviewWidget {
128120 elide: Text.ElideRight
129121 }
130122
131− UbuntuShape {
132− id: progressBarFill
133− objectName: "progressBarFill"
134−
135− readonly property int maxWidth: progressBarImage.width - units.dp(4)
136−
137− anchors {
138− left: progressBarImage.left
139− right: progressBarImage.right
140− verticalCenter: progressBarImage.verticalCenter
141− margins: units.dp(2)
142− rightMargin: maxWidth - (maxWidth * DashAudioPlayer.progress) + units.dp(2)
143− }
144− height: units.dp(2)
145− visible: progressBarImage.visible
146− color: UbuntuColors.orange
147− }
148−
149− Image {
150− id: progressBarImage
123+ AudioProgressBar {
151124 anchors { left: parent.left; top: parent.bottom; right: parent.right }
152− height: units.dp(6)
153125 visible: !DashAudioPlayer.stopped && trackItem.isPlayingItem && modelData["length"] > 0
154− source: "graphics/music_progress_bg.png"
126+ source: sourceUrl
155127 }
156128 }
157129
qml/Dash/Previews/graphics/music_progress_bg.pngdeleted

Binary file not shown.

tests/qmltests/Dash/CardHelpers.js+1−1
@@ -16,7 +16,7 @@
1616
1717.pragma library
1818
19−var components = ["title", "art", "subtitle", "mascot", "emblem", "summary", "attributes", "overlayColor"]
19+var components = ["title", "art", "subtitle", "mascot", "emblem", "summary", "attributes", "overlayColor", "quickPreviewData"]
2020
2121var defaultLayout = ' \
2222{ \
tests/qmltests/Dash/Previews/tst_PreviewAudioPlayback.qml+2−2
@@ -18,7 +18,7 @@ import QtQuick 2.0
1818import QtTest 1.0
1919import Ubuntu.Components 0.1
2020import "../../../../qml/Dash/Previews"
21−import "../../../../qml/Dash/DashAudioPlayer"
21+import Dash 0.1
2222import Unity.Test 0.1 as UT
2323import QtMultimedia 5.0
2424
@@ -59,8 +59,8 @@ Rectangle {
5959 function test_time_formatter_data() {
6060 return [
6161 { tag: "NaN", value: "not a number", result: "" },
62− { tag: "0", value: 0, result: "" },
6362 { tag: "-1", value: -1, result: "" },
63+ { tag: "0", value: 0, result: "0:00" },
6464 { tag: "30", value: 30, result: "0:30" },
6565 { tag: "60", value: 60, result: "1:00" },
6666 { tag: "3600", value: 3600, result: "1:00:00" }
tests/qmltests/Dash/tst_Card.qml+8−1
@@ -35,7 +35,8 @@ Rectangle {
3535 "title": "foo",
3636 "subtitle": "bar",
3737 "summary": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
38− "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}]
38+ "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}],
39+ "quickPreviewData": {"uri": "/some/file", "duration": "14"}
3940 }'
4041
4142 property var cardsModel: [
@@ -106,6 +107,11 @@ Rectangle {
106107 "layout": { "template": { "card-layout": "horizontal" },
107108 "components": JSON.parse(Helpers.fullMapping) }
108109 },
110+ {
111+ "name": "Art, title, subtitle - Audio",
112+ "layout": { "template": { "card-layout": "horizontal", "quickPreviewType": "audio" },
113+ "components": { "art": "art", "title": "title", "subtitle": "subtitle", "quickPreviewData": "quickPreviewData" } }
114+ },
109115 ]
110116
111117 CardTool {
@@ -130,6 +136,7 @@ Rectangle {
130136 item.width = Qt.binding(function() { return cardTool.cardWidth || item.implicitWidth; });
131137 item.height = Qt.binding(function() { return cardTool.cardHeight || item.implicitHeight; });
132138 item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight; });
139+ item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
133140 }
134141 }
135142