@@ -17,8 +17,9 @@
1717 */
1818
1919import QtQuick 2.4
20−import QtMultimedia 5.4
20+import QtMultimedia 5.6
2121import Ubuntu.Components 1.3
22+import QtQuick.LocalStorage 2.0
2223import "../podcasts.js" as Podcasts
2324import "../components"
2425
@@ -26,245 +27,350 @@ Page {
2627 id: nowPlayingPage
2728
2829 visible: false
29− title: i18n.tr("Now Playing")
3030
3131 property bool isNowPlayingPage: true
32− property bool isLandscapeMode: width > height
33−
34− // Landscape rule
35− states: [
36− State {
37− name: "landscape"
38− when: isLandscapeMode
39−
40− PropertyChanges {
41− target: blurredBackground
42− width: parent.width/2.2
43− height: parent.height
32+
33+ header: PageHeader {
34+ title: i18n.tr("Now Playing")
35+
36+ StyleHints {
37+ backgroundColor: podbird.appTheme.background
38+ }
39+
40+ trailingActionBar.actions: Action {
41+ iconName: "delete"
42+ visible: nowPlayingPageSections.selectedIndex === 1
43+ onTriggered: {
44+ Podcasts.clearQueue()
45+ player.playlist.clear()
46+ mainStack.pop()
4447 }
48+ }
4549
46− AnchorChanges {
47− target: blurredBackground
48− anchors {
49− top: parent.top
50− left: parent.left
51− right: undefined
52− }
50+ extension: Sections {
51+ id: nowPlayingPageSections
52+
53+ anchors {
54+ left: parent.left
55+ leftMargin: units.gu(2)
56+ bottom: parent.bottom
5357 }
5458
55− AnchorChanges {
56− target: dataContainer
57− anchors {
58− top: parent.top
59− left: blurredBackground.right
60− right: parent.right
61− bottom: parent.bottom
62− }
59+ StyleHints {
60+ selectedSectionColor: podbird.appTheme.focusText
6361 }
64− }
65− ]
66−
67− BlurredBackground {
68− id: blurredBackground
69−
70− anchors.left: parent.left
71− anchors.top: parent.top
72− anchors.right: parent.right
73− height: title.lineCount === 1 ? parent.height/2 + units.gu(3)
74− : parent.height/2
75− art: currentImage
76−
77− Image {
78− width: Math.min(parent.width/2, parent.height)
79− height: width
80− sourceSize.height: width
81− sourceSize.width: width
82− source: currentImage
83− asynchronous: true
84− anchors.centerIn: parent
62+ model: [i18n.tr("Full view"), i18n.tr("Queue")]
8563 }
8664 }
8765
88− Item {
89− id: dataContainer
66+ VisualItemModel {
67+ id: tabs
9068
91− anchors {
92− top: blurredBackground.bottom
93− left: parent.left
94− right: parent.right
95− bottom: parent.bottom
96− margins: units.gu(2)
97− bottomMargin: isLandscapeMode ? units.gu(4) : units.gu(2)
98− }
69+ Item {
70+ id: nowPlayingItem
9971
100− Label {
101− id: title
102− anchors.left: parent.left
103− anchors.right: parent.right
104− anchors.top: parent.top
105− text: currentName
106− elide: Text.ElideRight
107− textSize: Label.Large
108− maximumLineCount: 2
109− wrapMode: Text.WordWrap
110− color: podbird.appTheme.baseText
111− }
72+ width: tabView.width
73+ height: tabView.height
11274
113− Label {
114− id: artist
115− anchors.left: title.left
116− anchors.right: title.right
117− anchors.top: title.bottom
118− anchors.topMargin: units.gu(1)
119− text: currentArtist
120− elide: Text.ElideRight
121− textSize: Label.Small
122− color: podbird.appTheme.baseSubText
123− }
75+ property bool isLandscapeMode: nowPlayingPage.width > nowPlayingPage.height
12476
125− Slider {
126− id: scrubber
77+ // Landscape rule
78+ states: [
79+ State {
80+ name: "landscape"
81+ when: nowPlayingItem.isLandscapeMode
12782
128− anchors {
129− left: parent.left
130− right: parent.right
131− bottom: controls.top
132− bottomMargin: isLandscapeMode && title.lineCount < 2 ? units.gu(4) : units.gu(2)
133− }
83+ PropertyChanges {
84+ target: blurredBackground
85+ width: nowPlayingPage.width/2.2
86+ height: nowPlayingPage.height
87+ }
13488
135− live: true
136− minimumValue: 0
137− maximumValue: playerLoader.item.duration
138− value: playerLoader.item.position
139− height: units.gu(2)
89+ AnchorChanges {
90+ target: blurredBackground
91+ anchors {
92+ top: nowPlayingItem.top
93+ left: parent.left
94+ right: undefined
95+ }
96+ }
14097
141− onValueChanged: {
142− if (pressed) {
143− playerLoader.item.seek(value);
98+ AnchorChanges {
99+ target: dataContainer
100+ anchors {
101+ top: nowPlayingItem.top
102+ left: blurredBackground.right
103+ right: parent.right
104+ bottom: parent.bottom
105+ }
106+ }
144107 }
145− }
108+ ]
146109
147− function formatValue(v) { return Podcasts.formatTime(v/1000); }
148− StyleHints { foregroundColor: podbird.appTheme.focusText }
149− }
110+ BlurredBackground {
111+ id: blurredBackground
150112
151− Connections {
152− target: playerLoader.item
153− onPositionChanged: scrubber.value = playerLoader.item.position
154− }
113+ anchors.left: parent.left
114+ anchors.top: nowPlayingItem.top
115+ anchors.right: parent.right
116+ height: title.lineCount === 1 ? nowPlayingPage.height/2.3 + units.gu(3)
117+ : nowPlayingPage.height/2.3
118+ art: currentImage
155119
156− Label {
157− id: startTime
158− textSize: Label.Small
159− anchors.left: scrubber.left
160− anchors.top: scrubber.bottom
161− color: podbird.appTheme.baseText
162− text: Podcasts.formatTime(playerLoader.item.position / 1000)
163− }
120+ Image {
121+ width: Math.min(nowPlayingPage.width/2, nowPlayingPage.height/2)
122+ height: width
123+ sourceSize.height: width
124+ sourceSize.width: width
125+ source: currentImage
126+ asynchronous: true
127+ anchors.centerIn: parent
128+ }
129+ }
164130
165− Label {
166− id: endTime
167− textSize: Label.Small
168− anchors.right: scrubber.right
169− anchors.top: scrubber.bottom
170− color: podbird.appTheme.baseText
171− text: Podcasts.formatTime(playerLoader.item.duration / 1000)
172− }
131+ Item {
132+ id: dataContainer
173133
174− Row {
175− id: controls
176−
177− anchors.bottom: parent.bottom
178− anchors.horizontalCenter: parent.horizontalCenter
179− spacing: units.gu(2)
180−
181− AbstractButton {
182− id: skipBackwardButton
183− width: units.gu(6)
184− height: width
185− anchors.verticalCenter: parent.verticalCenter
186− opacity: playerLoader.item.position === 0 ? 0.4 : 1.0
187− onClicked: {
188− if (playerLoader.item.position > 0) {
189− playerLoader.item.seek(playerLoader.item.position - podbird.settings.skipBack * 1000);
190− }
134+ anchors {
135+ top: blurredBackground.bottom
136+ left: parent.left
137+ right: parent.right
138+ bottom: parent.bottom
139+ margins: units.gu(2)
140+ bottomMargin: nowPlayingItem.isLandscapeMode ? units.gu(4) : units.gu(2)
191141 }
192142
193− Row {
194− spacing: units.gu(1)
195− anchors.centerIn: parent
143+ Label {
144+ id: title
145+ anchors.left: parent.left
146+ anchors.right: parent.right
147+ anchors.top: parent.top
148+ text: currentName
149+ elide: Text.ElideRight
150+ textSize: Label.Large
151+ maximumLineCount: 2
152+ wrapMode: Text.WordWrap
153+ color: podbird.appTheme.baseText
154+ }
196155
197− Label {
198− // TRANSLATORS: The string shown in the UI is -15s to denote the number of seconds that the podcast playback will skip backward.
199− // xgettext: no-c-format
200− text: i18n.tr("-%1s").arg(podbird.settings.skipBack)
201− textSize: Label.XxSmall
202− color: podbird.appTheme.baseText
203− anchors.verticalCenter: skipBackwardIcon.verticalCenter
156+ Label {
157+ id: artist
158+ anchors.left: title.left
159+ anchors.right: title.right
160+ anchors.top: title.bottom
161+ anchors.topMargin: units.gu(1)
162+ text: currentArtist
163+ elide: Text.ElideRight
164+ textSize: Label.Small
165+ color: podbird.appTheme.baseSubText
166+ }
167+
168+ Slider {
169+ id: scrubber
170+
171+ anchors {
172+ left: parent.left
173+ right: parent.right
174+ bottom: controls.top
175+ bottomMargin: nowPlayingItem.isLandscapeMode && title.lineCount < 2 ? units.gu(4) : units.gu(2)
204176 }
205177
206− Icon {
207− id: skipBackwardIcon
208− width: units.gu(3)
209− height: width
210− name: "media-seek-backward"
211− color: podbird.appTheme.baseIcon
178+ live: true
179+ minimumValue: 0
180+ maximumValue: player.duration
181+ value: player.position
182+ height: units.gu(2)
183+
184+ onValueChanged: {
185+ if (pressed) {
186+ player.seek(value);
187+ }
212188 }
189+
190+ function formatValue(v) { return Podcasts.formatTime(v/1000); }
191+ StyleHints { foregroundColor: podbird.appTheme.focusText }
213192 }
214− }
215193
216− AbstractButton {
217− id: playButton
218− width: units.gu(10)
219− height: width
220− opacity: playButton.pressed ? 0.4 : 1.0
221− onClicked: playerLoader.item.playbackState === MediaPlayer.PlayingState ? playerLoader.item.pause() : playerLoader.item.play()
194+ Connections {
195+ target: player
196+ onPositionChanged: scrubber.value = player.position
197+ }
222198
223− Icon {
224− id: playIcon
225− width: units.gu(6)
226− height: width
227− anchors.centerIn: parent
228− color: podbird.appTheme.baseIcon
229− name: playerLoader.item.playbackState === MediaPlayer.PlayingState ? "media-playback-pause"
230− : "media-playback-start"
199+ Label {
200+ id: startTime
201+ textSize: Label.Small
202+ anchors.left: scrubber.left
203+ anchors.top: scrubber.bottom
204+ color: podbird.appTheme.baseText
205+ text: Podcasts.formatTime(player.position / 1000)
231206 }
232− }
233207
234− AbstractButton {
235− id: skipForwardButton
236− width: units.gu(6)
237− height: width
238− anchors.verticalCenter: parent.verticalCenter
239− opacity: playerLoader.item.position === 0 ? 0.4 : 1.0
240− onClicked: {
241− if (playerLoader.item.position > 0) {
242− playerLoader.item.seek(playerLoader.item.position + podbird.settings.skipForward * 1000);
243− }
208+ Label {
209+ id: endTime
210+ textSize: Label.Small
211+ anchors.right: scrubber.right
212+ anchors.top: scrubber.bottom
213+ color: podbird.appTheme.baseText
214+ text: Podcasts.formatTime(player.duration / 1000)
244215 }
245216
246217 Row {
218+ id: controls
219+
220+ anchors.bottom: parent.bottom
221+ anchors.horizontalCenter: parent.horizontalCenter
247222 spacing: units.gu(1)
248− anchors.centerIn: parent
249223
250− Icon {
251− id: skipForwardIcon
252− width: units.gu(3)
224+ AbstractButton {
225+ id: mediaBackwardButton
226+ width: units.gu(6)
253227 height: width
254− name: "media-seek-forward"
255− color: podbird.appTheme.baseIcon
228+ anchors.verticalCenter: parent.verticalCenter
229+ enabled: player.playlist.canGoPrevious
230+ opacity: enabled ? 1.0 : 0.4
231+ onClicked: player.playlist.previous()
232+
233+ Icon {
234+ id: mediaBackwardIcon
235+ width: units.gu(3)
236+ height: width
237+ anchors.centerIn: parent
238+ color: podbird.appTheme.baseIcon
239+ name: "media-skip-backward"
240+ }
256241 }
257242
258− Label {
259− // TRANSLATORS: The string shown in the UI is +15s to denote the number of seconds that the podcast playback will skip forward.
260− // xgettext: no-c-format
261− text: i18n.tr("+%1s").arg(podbird.settings.skipForward)
262− textSize: Label.XxSmall
263− color: podbird.appTheme.baseText
264− anchors.verticalCenter: skipForwardIcon.verticalCenter
243+ AbstractButton {
244+ id: skipBackwardButton
245+ width: units.gu(6)
246+ height: width
247+ anchors.verticalCenter: parent.verticalCenter
248+ opacity: player.position === 0 ? 0.4 : 1.0
249+ onClicked: {
250+ if (player.position > 0) {
251+ player.seek(player.position - podbird.settings.skipBack * 1000);
252+ }
253+ }
254+
255+ Row {
256+ spacing: units.gu(1)
257+ anchors.centerIn: parent
258+
259+ Label {
260+ // TRANSLATORS: The string shown in the UI is -15s to denote the number of seconds that the podcast playback will skip backward.
261+ // xgettext: no-c-format
262+ text: i18n.tr("-%1s").arg(podbird.settings.skipBack)
263+ textSize: Label.XxSmall
264+ color: podbird.appTheme.baseText
265+ anchors.verticalCenter: skipBackwardIcon.verticalCenter
266+ }
267+
268+ Icon {
269+ id: skipBackwardIcon
270+ width: units.gu(3)
271+ height: width
272+ name: "media-seek-backward"
273+ color: podbird.appTheme.baseIcon
274+ }
275+ }
276+ }
277+
278+ AbstractButton {
279+ id: playButton
280+ width: units.gu(10)
281+ height: width
282+ opacity: playButton.pressed ? 0.4 : 1.0
283+ onClicked: player.playbackState === MediaPlayer.PlayingState ? player.pause() : player.play()
284+
285+ Icon {
286+ id: playIcon
287+ width: units.gu(6)
288+ height: width
289+ anchors.centerIn: parent
290+ color: podbird.appTheme.baseIcon
291+ name: player.playbackState === MediaPlayer.PlayingState ? "media-playback-pause"
292+ : "media-playback-start"
293+ }
294+ }
295+
296+ AbstractButton {
297+ id: skipForwardButton
298+ width: units.gu(6)
299+ height: width
300+ anchors.verticalCenter: parent.verticalCenter
301+ opacity: player.position === 0 ? 0.4 : 1.0
302+ onClicked: {
303+ if (player.position > 0) {
304+ player.seek(player.position + podbird.settings.skipForward * 1000);
305+ }
306+ }
307+
308+ Row {
309+ spacing: units.gu(1)
310+ anchors.centerIn: parent
311+
312+ Icon {
313+ id: skipForwardIcon
314+ width: units.gu(3)
315+ height: width
316+ name: "media-seek-forward"
317+ color: podbird.appTheme.baseIcon
318+ }
319+
320+ Label {
321+ // TRANSLATORS: The string shown in the UI is +15s to denote the number of seconds that the podcast playback will skip forward.
322+ // xgettext: no-c-format
323+ text: i18n.tr("+%1s").arg(podbird.settings.skipForward)
324+ textSize: Label.XxSmall
325+ color: podbird.appTheme.baseText
326+ anchors.verticalCenter: skipForwardIcon.verticalCenter
327+ }
328+ }
329+ }
330+
331+ AbstractButton {
332+ id: mediaForwardButton
333+ width: units.gu(6)
334+ height: width
335+ anchors.verticalCenter: parent.verticalCenter
336+ enabled: player.playlist.canGoNext
337+ opacity: enabled ? 1.0 : 0.4
338+ onClicked: player.playlist.next()
339+
340+ Icon {
341+ id: mediaForwardIcon
342+ width: units.gu(3)
343+ height: width
344+ anchors.centerIn: parent
345+ color: podbird.appTheme.baseIcon
346+ name: "media-skip-forward"
347+ }
265348 }
266349 }
267350 }
268351 }
352+
353+ Queue {
354+ width: tabView.width
355+ height: tabView.height
356+ }
357+ }
358+
359+ ListView {
360+ id: tabView
361+ model: tabs
362+ interactive: false
363+
364+ anchors {
365+ top: nowPlayingPage.header.bottom
366+ left: parent.left
367+ right: parent.right
368+ bottom: parent.bottom
369+ }
370+
371+ orientation: Qt.Horizontal
372+ snapMode: ListView.SnapOneItem
373+ currentIndex: nowPlayingPageSections.selectedIndex
374+ highlightMoveDuration: UbuntuAnimation.SlowDuration
269375 }
270376}