Merge lp:~ahayzen/music-app/convergence-tabs-with-sidebar-01 into lp:music-app

Proposed by Andrew Hayzen
Status: Merged
Approved by: Victor Thompson
Approved revision: 984
Merged at revision: 998
Proposed branch: lp:~ahayzen/music-app/convergence-tabs-with-sidebar-01
Merge into: lp:music-app
Diff against target: 2029 lines (+1077/-432)
26 files modified
app/components/BlurredBackground.qml (+3/-1)
app/components/Flickables/MusicGridView.qml (+0/-10)
app/components/HeadState/EmptyHeadState.qml (+56/-0)
app/components/HeadState/MultiSelectHeadState.qml (+89/-76)
app/components/HeadState/PlaylistHeadState.qml (+78/-0)
app/components/HeadState/PlaylistsHeadState.qml (+54/-20)
app/components/HeadState/QueueHeadState.qml (+89/-0)
app/components/HeadState/SearchHeadState.qml (+59/-46)
app/components/HeadState/SearchableHeadState.qml (+45/-8)
app/components/MusicPage.qml (+39/-0)
app/components/NowPlayingFullView.qml (+76/-68)
app/components/NowPlayingSidebar.qml (+134/-0)
app/components/NowPlayingToolbar.qml (+18/-14)
app/components/Queue.qml (+5/-1)
app/music-app.qml (+101/-23)
app/ui/AddToPlaylist.qml (+5/-2)
app/ui/Albums.qml (+5/-2)
app/ui/ArtistView.qml (+12/-2)
app/ui/Artists.qml (+5/-2)
app/ui/ContentHubExport.qml (+34/-26)
app/ui/Genres.qml (+5/-2)
app/ui/NowPlaying.qml (+113/-77)
app/ui/Playlists.qml (+5/-2)
app/ui/Recent.qml (+41/-15)
app/ui/SongsView.qml (+5/-35)
debian/changelog (+1/-0)
To merge this branch: bzr merge lp:~ahayzen/music-app/convergence-tabs-with-sidebar-01
Reviewer Review Type Date Requested Status
Victor Thompson Approve
Jenkins Bot continuous-integration Approve
Review via email: mp+286127@code.launchpad.net

Commit message

* Implement convergent mode with now playing and queue as a sidebar

Description of the change

* Implement convergent mode with now playing and queue as a sidebar

Known Issues:
* When the app is starting and is wide enough for wideAspect, a black box appears where the async loader ends up (this happens for all apps seems to be they are launched in portrait and resized too late) [bug 1548096]

To post a comment you must log in.
Revision history for this message
Victor Thompson (vthompson) wrote :

Here are a few screenshots: http://imgur.com/a/sOqGU

I think the sidebar needs the most work if you compare it to the spec. However, the header also seems to differ in color?

review: Needs Fixing
Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
977. By Andrew Hayzen

* Add missing file and set dividor colour

Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
978. By Andrew Hayzen

* Set divider to be darker not lighter

Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
979. By Andrew Hayzen

* Set flickable on fallback PageHeader

Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Victor Thompson (vthompson) wrote :

Added some inline code issues/questions. Will try to do some functional testing as well tonight/tomorrow.

review: Needs Fixing
Revision history for this message
Victor Thompson (vthompson) wrote :

I've noticed a few things while testing this mp:

1. The top of the queue is obscured by the header sections.
2. Only applicable to the new SDK in silo 50, but the header sections seem quite a bit larger.
3. Only applicable to the new SDK in silo 50, but when I select Queue, then select Full view the header disappears.

review: Needs Fixing
980. By Andrew Hayzen

* Various fixes

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

Issues:
1) Was there a specific reason to do this? Did you plan on referencing it elsewhere?
2) It'd be tiny, but since we replicate this all over the place it would be nice to make a MusicStyleHints or perhaps put the "1.1" value in the Style.qml file. Actually, maybe create a base MusicState for all these to implement?
3) This bug was fixed in OTA9, you can leave the workaround in if you want, but otherwise we should put together an MP to remove it.
4) Move this commented out code? Or will it be needed when bug is resolved?
5) This is a nitpick, but when we have consecutive updates, let's do 2013-2016, etc. You can leave it as-is for this MP though if you'd like.
6) I know we haven't been consistent, but we should avoid hardcoding this value all the time. Consider moving it to Style.qml
7) Please add parens to clarify the order of operations.
8) Does this deserve it's own component at all? I'd kind of argue that it's not necessary, but having a MusicTabActions component might be "nice" since this logic is rather simple.
9) Move below fill? Occurs a few times in various files.
10) The top of the queue is obscured by the header sections.
11) Only applicable to the new SDK in silo 50, but the header sections seem quite a bit larger.
12) Only applicable to the new SDK in silo 50, but when I select Queue, then select Full view the header disappears.

Resolutions:
1) This is required so that the sidebar can change the colour
2, 6) Styling stuff, I'd like to move to using proper themeing ASAP, so is it best just to leave hardcoded for now?
3) Removed code, please test
4) Fixed
5) I was told somewhere else it is best to specify all the years, and reduces confusion if someone then contributes in 2018 but not 2017 (as that'd have to be 2013-2016, 2018 not 2013-2018). But happy to change if that is what people want.
7) Fixed
8) I'm not sure, as in that component it'd have to reference the id tabs, also I was wondering in an additional MP if I could declare the tabs from a model with repeaters, which would then simplify this code alot.
9) Fixed
10) Fixed
11) "As per design", will probably be the response from upstream
12) Investigating... something weird going on :-)

Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Victor Thompson (vthompson) wrote :

I can still reproduce #12 while on the new silo #50.

Revision history for this message
Victor Thompson (vthompson) wrote :

Figuring out why issue #12 is happening should be a priority so we can provide these features to early users of the tablet device.

981. By Andrew Hayzen

* Switch to using extension: Sections {} rather than sections: {} as that is deprecated

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

Please retest #12, I've been unable to reproduce this seen moving to extension: Sections {} (as sections: {} will be deprecated).

Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
982. By Andrew Hayzen

* Merge of trunk
* Fixes for SongView to use PageHeader instead of PageHeadState

Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
983. By Andrew Hayzen

* Removal of the last PageHeadState to PageHeader

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

Please test all headers as I found a few that hadn't been converted and therefore were broken.

Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Victor Thompson (vthompson) wrote :

:( I can still reproduce the header bug. Was there anything that landed in rc-proposed that I'll need to test with (I'm still on a silo)?

review: Needs Fixing
Revision history for this message
Victor Thompson (vthompson) wrote :

Also, it seems like maybe the bug occurs more often when you have a large queue.

984. By Andrew Hayzen

* Create a separate page head state for FullView to get around flickable changing causing header to disappear

Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Victor Thompson (vthompson) wrote :

Ok, the additional changes look good and fix the issue. Sometimes when I change orientations the header hides but can be re-shown. lgtm!

(but we should still try to track down what's going on)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'app/components/BlurredBackground.qml'
2--- app/components/BlurredBackground.qml 2016-01-16 01:30:31 +0000
3+++ app/components/BlurredBackground.qml 2016-04-06 02:18:30 +0000
4@@ -26,13 +26,15 @@
5 width: parent.width
6
7 property string art
8+ // need to expose color so sidebar can set its own
9+ property string color: "black"
10
11 // dark layer
12 Rectangle {
13 anchors {
14 fill: parent
15 }
16- color: "black"
17+ color: parent.color
18 }
19
20 // the album art
21
22=== modified file 'app/components/Flickables/MusicGridView.qml'
23--- app/components/Flickables/MusicGridView.qml 2016-01-12 00:30:08 +0000
24+++ app/components/Flickables/MusicGridView.qml 2016-04-06 02:18:30 +0000
25@@ -25,16 +25,6 @@
26 fill: parent
27 leftMargin: units.gu(1)
28 rightMargin: units.gu(1)
29- // FIXME: workaround until pad.lv/1531016 (gridview juddery) is fixed
30- // due to anchors.fill: parent not being used when the header is locked
31- // an extra margin is needed
32- topMargin: {
33- if (parent.head.locked) {
34- units.gu(6.125) * 2 + units.gu(2) // FIXME: 6.125 is header.height
35- } else {
36- units.gu(6.125) + units.gu(2) // FIXME: 6.125 is header.height
37- }
38- }
39 }
40 cellHeight: cellSize + heightOffset
41 cellWidth: cellSize + widthOffset
42
43=== added file 'app/components/HeadState/EmptyHeadState.qml'
44--- app/components/HeadState/EmptyHeadState.qml 1970-01-01 00:00:00 +0000
45+++ app/components/HeadState/EmptyHeadState.qml 2016-04-06 02:18:30 +0000
46@@ -0,0 +1,56 @@
47+/*
48+ * Copyright (C) 2016
49+ * Andrew Hayzen <ahayzen@gmail.com>
50+ * Victor Thompson <victor.thompson@gmail.com>
51+ *
52+ * This program is free software; you can redistribute it and/or modify
53+ * it under the terms of the GNU General Public License as published by
54+ * the Free Software Foundation; version 3.
55+ *
56+ * This program is distributed in the hope that it will be useful,
57+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
58+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
59+ * GNU General Public License for more details.
60+ *
61+ * You should have received a copy of the GNU General Public License
62+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
63+ */
64+
65+import QtQuick 2.4
66+import Ubuntu.Components 1.3
67+
68+State {
69+ name: "default"
70+
71+ property PageHeader thisHeader: PageHeader {
72+ flickable: thisPage.flickable
73+ leadingActionBar {
74+ actions: {
75+ if (mainPageStack.currentPage === tabs) {
76+ tabs.tabActions
77+ } else if (mainPageStack.depth > 1) {
78+ backActionComponent
79+ }
80+ }
81+ }
82+ title: thisPage.title
83+ visible: thisPage.state === "default"
84+
85+ Action {
86+ id: backActionComponent
87+ iconName: "back"
88+ onTriggered: mainPageStack.pop()
89+ }
90+
91+ StyleHints {
92+ backgroundColor: mainView.headerColor
93+ dividerColor: Qt.darker(mainView.headerColor, 1.1)
94+ }
95+ }
96+ property Item thisPage
97+
98+ PropertyChanges {
99+ target: thisPage
100+ header: thisHeader
101+ }
102+}
103
104=== modified file 'app/components/HeadState/MultiSelectHeadState.qml'
105--- app/components/HeadState/MultiSelectHeadState.qml 2016-03-19 17:12:29 +0000
106+++ app/components/HeadState/MultiSelectHeadState.qml 2016-04-06 02:18:30 +0000
107@@ -20,87 +20,100 @@
108 import Ubuntu.Components 1.3
109 import "../Flickables"
110
111-PageHeadState {
112- id: selectionState
113- actions: [
114- Action {
115- iconName: "select"
116- text: i18n.tr("Select All")
117- onTriggered: {
118- if (listview.getSelectedIndices().length === listview.model.count) {
119- listview.clearSelection()
120- } else {
121- listview.selectAll()
122- }
123- }
124- },
125- Action {
126- iconName: "add-to-playlist"
127- text: i18n.tr("Add to playlist")
128- visible: listview !== null ? listview.getSelectedIndices().length > 0 : false
129- onTriggered: {
130- var items = []
131- var indicies = listview.getSelectedIndices();
132-
133- for (var i=0; i < indicies.length; i++) {
134- items.push(makeDict(listview.model.get(indicies[i], listview.model.RoleModelData)));
135- }
136-
137- mainPageStack.push(Qt.resolvedUrl("../../ui/AddToPlaylist.qml"),
138- {"chosenElements": items})
139-
140- listview.closeSelection()
141- }
142- },
143- Action {
144- iconName: "add"
145- text: i18n.tr("Add to queue")
146- visible: listview !== null ? (listview.getSelectedIndices().length > 0) && addToQueue: false
147-
148- onTriggered: {
149- var items = [];
150- var indicies = listview.getSelectedIndices();
151-
152- for (var i=0; i < indicies.length; i++) {
153- items.push(Qt.resolvedUrl(listview.model.get(indicies[i], listview.model.RoleModelData).filename));
154- }
155-
156- player.mediaPlayer.playlist.addItems(items);
157-
158- listview.closeSelection()
159- }
160- },
161- Action {
162- iconName: "delete"
163- text: i18n.tr("Delete")
164- visible: listview !== null ? (listview.getSelectedIndices().length > 0) && removable : false
165-
166- onTriggered: {
167- removed(listview.getSelectedIndices())
168-
169- listview.closeSelection()
170- }
171- }
172-
173- ]
174- backAction: Action {
175- text: i18n.tr("Cancel selection")
176- iconName: "back"
177- onTriggered: listview.closeSelection()
178- }
179- head: thisPage.head
180+State {
181 name: "selection"
182
183- PropertyChanges {
184- target: thisPage.head
185- backAction: selectionState.backAction
186- actions: selectionState.actions
187- }
188-
189 property bool addToQueue: true
190 property MultiSelectListView listview
191 property bool removable: false
192- property Page thisPage
193+ property PageHeader thisHeader: PageHeader {
194+ id: selectionState
195+ flickable: thisPage.flickable
196+ leadingActionBar {
197+ actions: [
198+ Action {
199+ text: i18n.tr("Cancel selection")
200+ iconName: "back"
201+ onTriggered: listview.closeSelection()
202+ }
203+ ]
204+ }
205+ title: thisPage.title
206+ trailingActionBar {
207+ actions: [
208+ Action {
209+ iconName: "select"
210+ text: i18n.tr("Select All")
211+ onTriggered: {
212+ if (listview.getSelectedIndices().length === listview.model.count) {
213+ listview.clearSelection()
214+ } else {
215+ listview.selectAll()
216+ }
217+ }
218+ },
219+ Action {
220+ iconName: "add-to-playlist"
221+ text: i18n.tr("Add to playlist")
222+ visible: listview !== null ? listview.getSelectedIndices().length > 0 : false
223+ onTriggered: {
224+ var items = []
225+ var indicies = listview.getSelectedIndices();
226+
227+ for (var i=0; i < indicies.length; i++) {
228+ items.push(makeDict(listview.model.get(indicies[i], listview.model.RoleModelData)));
229+ }
230+
231+ mainPageStack.push(Qt.resolvedUrl("../../ui/AddToPlaylist.qml"),
232+ {"chosenElements": items})
233+
234+ listview.closeSelection()
235+ }
236+ },
237+ Action {
238+ iconName: "add"
239+ text: i18n.tr("Add to queue")
240+ visible: listview !== null ? (listview.getSelectedIndices().length > 0) && addToQueue: false
241+
242+ onTriggered: {
243+ var items = [];
244+ var indicies = listview.getSelectedIndices();
245+
246+ for (var i=0; i < indicies.length; i++) {
247+ items.push(Qt.resolvedUrl(listview.model.get(indicies[i], listview.model.RoleModelData).filename));
248+ }
249+
250+ player.mediaPlayer.playlist.addItems(items);
251+
252+ listview.closeSelection()
253+ }
254+ },
255+ Action {
256+ iconName: "delete"
257+ text: i18n.tr("Delete")
258+ visible: listview !== null ? (listview.getSelectedIndices().length > 0) && removable : false
259+
260+ onTriggered: {
261+ removed(listview.getSelectedIndices())
262+
263+ listview.closeSelection()
264+ }
265+ }
266+ ]
267+ }
268+ visible: thisPage.state === "selection"
269+
270+ StyleHints {
271+ backgroundColor: mainView.headerColor
272+ dividerColor: Qt.darker(mainView.headerColor, 1.1)
273+ }
274+ }
275+ property Item thisPage
276
277 signal removed(var selectedIndices)
278+
279+ PropertyChanges {
280+ target: thisPage
281+ header: thisHeader
282+ }
283 }
284
285=== added file 'app/components/HeadState/PlaylistHeadState.qml'
286--- app/components/HeadState/PlaylistHeadState.qml 1970-01-01 00:00:00 +0000
287+++ app/components/HeadState/PlaylistHeadState.qml 2016-04-06 02:18:30 +0000
288@@ -0,0 +1,78 @@
289+/*
290+ * Copyright (C) 2016
291+ * Andrew Hayzen <ahayzen@gmail.com>
292+ * Victor Thompson <victor.thompson@gmail.com>
293+ *
294+ * This program is free software; you can redistribute it and/or modify
295+ * it under the terms of the GNU General Public License as published by
296+ * the Free Software Foundation; version 3.
297+ *
298+ * This program is distributed in the hope that it will be useful,
299+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
300+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
301+ * GNU General Public License for more details.
302+ *
303+ * You should have received a copy of the GNU General Public License
304+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
305+ */
306+
307+import QtQuick 2.4
308+import Ubuntu.Components 1.3
309+import Ubuntu.Components.Popups 1.3
310+
311+State {
312+ id: playlistHeadState
313+ name: "playlist"
314+
315+ property PageHeader thisHeader: PageHeader {
316+ flickable: thisPage.flickable
317+ leadingActionBar {
318+ actions: {
319+ if (mainPageStack.currentPage === tabs) {
320+ tabs.tabActions
321+ } else if (mainPageStack.depth > 1) {
322+ backActionComponent
323+ }
324+ }
325+ }
326+ title: thisPage.title
327+ trailingActionBar {
328+ actions: [
329+ Action {
330+ objectName: "editPlaylist"
331+ iconName: "edit"
332+ onTriggered: {
333+ thisPage.currentDialog = PopupUtils.open(Qt.resolvedUrl("../Dialog/EditPlaylistDialog.qml"), mainView)
334+ thisPage.currentDialog.oldPlaylistName = line2
335+ }
336+ },
337+ Action {
338+ objectName: "deletePlaylist"
339+ iconName: "delete"
340+ onTriggered: {
341+ thisPage.currentDialog = PopupUtils.open(Qt.resolvedUrl("../Dialog/RemovePlaylistDialog.qml"), mainView)
342+ thisPage.currentDialog.oldPlaylistName = line2
343+ }
344+ }
345+ ]
346+ }
347+ visible: thisPage.state === "playlist"
348+
349+ Action {
350+ id: backActionComponent
351+ iconName: "back"
352+ onTriggered: mainPageStack.pop()
353+ }
354+
355+ StyleHints {
356+ backgroundColor: mainView.headerColor
357+ dividerColor: Qt.darker(mainView.headerColor, 1.1)
358+ }
359+ }
360+ property Item thisPage
361+
362+ PropertyChanges {
363+ target: thisPage
364+ header: thisHeader
365+ }
366+}
367
368=== modified file 'app/components/HeadState/PlaylistsHeadState.qml'
369--- app/components/HeadState/PlaylistsHeadState.qml 2016-03-19 17:12:29 +0000
370+++ app/components/HeadState/PlaylistsHeadState.qml 2016-04-06 02:18:30 +0000
371@@ -20,28 +20,62 @@
372 import Ubuntu.Components 1.3
373 import Ubuntu.Components.Popups 1.3
374
375-
376-PageHeadState {
377+State {
378 name: "default"
379- head: thisPage.head
380- actions: [
381- Action {
382- id: newPlaylistAction
383- objectName: "newPlaylistButton"
384- iconName: "add"
385- onTriggered: {
386- customdebug("New playlist.")
387- thisPage.currentDialog = PopupUtils.open(Qt.resolvedUrl("../Dialog/NewPlaylistDialog.qml"), mainView)
388- }
389- },
390- Action {
391- id: searchAction
392- iconName: "search"
393- onTriggered: thisPage.state = "search"
394- }
395- ]
396
397 property alias newPlaylistEnabled: newPlaylistAction.visible
398 property alias searchEnabled: searchAction.visible
399- property Page thisPage
400+ property PageHeader thisHeader: PageHeader {
401+ id: headerState
402+ flickable: thisPage.flickable
403+ leadingActionBar {
404+ actions: {
405+ if (mainPageStack.currentPage === tabs) {
406+ tabs.tabActions
407+ } else if (mainPageStack.depth > 1) {
408+ backActionComponent
409+ }
410+ }
411+ }
412+ title: thisPage.title
413+ trailingActionBar {
414+ actions: [
415+ Action {
416+ id: newPlaylistAction
417+ objectName: "newPlaylistButton"
418+ iconName: "add"
419+ onTriggered: {
420+ customdebug("New playlist.")
421+ thisPage.currentDialog = PopupUtils.open(Qt.resolvedUrl("../Dialog/NewPlaylistDialog.qml"), mainView)
422+ }
423+ },
424+ Action {
425+ id: searchAction
426+ iconName: "search"
427+ onTriggered: {
428+ thisPage.state = "search";
429+ thisPage.header.contents.forceActiveFocus();
430+ }
431+ }
432+ ]
433+ }
434+ visible: thisPage.state === "default"
435+
436+ Action {
437+ id: backActionComponent
438+ iconName: "back"
439+ onTriggered: mainPageStack.pop()
440+ }
441+
442+ StyleHints {
443+ backgroundColor: mainView.headerColor
444+ dividerColor: Qt.darker(mainView.headerColor, 1.1)
445+ }
446+ }
447+ property Item thisPage
448+
449+ PropertyChanges {
450+ target: thisPage
451+ header: thisHeader
452+ }
453 }
454
455=== added file 'app/components/HeadState/QueueHeadState.qml'
456--- app/components/HeadState/QueueHeadState.qml 1970-01-01 00:00:00 +0000
457+++ app/components/HeadState/QueueHeadState.qml 2016-04-06 02:18:30 +0000
458@@ -0,0 +1,89 @@
459+/*
460+ * Copyright (C) 2016
461+ * Andrew Hayzen <ahayzen@gmail.com>
462+ * Victor Thompson <victor.thompson@gmail.com>
463+ *
464+ * This program is free software; you can redistribute it and/or modify
465+ * it under the terms of the GNU General Public License as published by
466+ * the Free Software Foundation; version 3.
467+ *
468+ * This program is distributed in the hope that it will be useful,
469+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
470+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
471+ * GNU General Public License for more details.
472+ *
473+ * You should have received a copy of the GNU General Public License
474+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
475+ */
476+
477+import QtQuick 2.4
478+import Ubuntu.Components 1.3
479+
480+
481+State {
482+ id: state
483+ name: stateName
484+
485+ // Need to be able to change state name and State.name is not notifyable
486+ property string stateName: "default"
487+ property PageHeader thisHeader: PageHeader {
488+ id: headerState
489+ flickable: thisPage.flickable
490+ leadingActionBar {
491+ actions: {
492+ if (mainPageStack.currentPage === tabs) {
493+ tabs.tabActions
494+ } else if (mainPageStack.depth > 1) {
495+ backActionComponent
496+ }
497+ }
498+ }
499+ title: thisPage.title
500+ trailingActionBar {
501+ actions: [
502+ Action {
503+ enabled: !player.mediaPlayer.playlist.empty
504+ iconName: "add-to-playlist"
505+ // TRANSLATORS: this action appears in the overflow drawer with limited space (around 18 characters)
506+ text: i18n.tr("Add to playlist")
507+
508+ onTriggered: {
509+ var items = []
510+
511+ items.push(makeDict(player.metaForSource(player.mediaPlayer.playlist.currentItemSource)));
512+
513+ mainPageStack.push(Qt.resolvedUrl("../../ui/AddToPlaylist.qml"),
514+ {"chosenElements": items})
515+ }
516+ },
517+ Action {
518+ enabled: !player.mediaPlayer.playlist.empty
519+ iconName: "delete"
520+ objectName: "clearQueue"
521+ // TRANSLATORS: this action appears in the overflow drawer with limited space (around 18 characters)
522+ text: i18n.tr("Clear queue")
523+
524+ onTriggered: player.mediaPlayer.playlist.clearWrapper()
525+ }
526+ ]
527+ }
528+ visible: thisPage.state === state.stateName
529+
530+ Action {
531+ id: backActionComponent
532+ iconName: "back"
533+ onTriggered: mainPageStack.pop()
534+ }
535+
536+ StyleHints {
537+ backgroundColor: mainView.headerColor
538+ dividerColor: Qt.darker(mainView.headerColor, 1.1)
539+ }
540+ }
541+ property Item thisPage
542+
543+ PropertyChanges {
544+ target: thisPage
545+ header: thisHeader
546+ }
547+}
548
549=== modified file 'app/components/HeadState/SearchHeadState.qml'
550--- app/components/HeadState/SearchHeadState.qml 2016-02-26 20:09:49 +0000
551+++ app/components/HeadState/SearchHeadState.qml 2016-04-06 02:18:30 +0000
552@@ -19,60 +19,73 @@
553 import QtQuick 2.4
554 import Ubuntu.Components 1.3
555
556-PageHeadState {
557- id: headerState
558+State {
559 name: "search"
560- head: thisPage.head
561- backAction: Action {
562- id: leaveSearchAction
563- text: "back"
564- iconName: "back"
565- onTriggered: thisPage.state = "default"
566- }
567- contents: TextField {
568- id: searchField
569- anchors {
570- left: parent ? parent.left : undefined
571- right: parent ? parent.right : undefined
572- rightMargin: units.gu(2)
573- }
574- hasClearButton: true
575- inputMethodHints: Qt.ImhNoPredictiveText
576- placeholderText: i18n.tr("Search music")
577+
578+ property PageHeader thisHeader: PageHeader {
579+ id: headerState
580+ contents: TextField {
581+ id: searchField
582+ anchors {
583+ left: parent ? parent.left : undefined
584+ right: parent ? parent.right : undefined
585+ verticalCenter: parent ? parent.verticalCenter : undefined
586+ }
587+ focus: true
588+ hasClearButton: true
589+ inputMethodHints: Qt.ImhNoPredictiveText
590+ placeholderText: i18n.tr("Search music")
591+
592+ // Use the page onVisible as the text field goes visible=false when switching states
593+ // This is used when popping from the pageStack and returning back to a page with search
594+ Connections {
595+ target: thisPage
596+
597+ onStateChanged: { // ensure the search is reset (eg pressing Esc)
598+ if (state === "default") {
599+ searchField.text = ""
600+ }
601+ }
602+
603+ onVisibleChanged: {
604+ // clear when the page becomes visible not invisible
605+ // if invisible is used the delegates can be destroyed which
606+ // have created the pushed component
607+ if (visible) {
608+ thisPage.state = "default"
609+ }
610+ }
611+ }
612+ }
613+ flickable: thisPage.flickable
614+ leadingActionBar {
615+ actions: [
616+ Action {
617+ id: leaveSearchAction
618+ text: "back"
619+ iconName: "back"
620+ onTriggered: thisPage.state = "default"
621+ }
622+ ]
623+ }
624+ visible: thisPage.state === "search"
625
626 onVisibleChanged: {
627 if (visible) {
628- forceActiveFocus()
629+ searchField.forceActiveFocus()
630 }
631 }
632
633- // Use the page onVisible as the text field goes visible=false when switching states
634- // This is used when popping from the pageStack and returning back to a page with search
635- Connections {
636- target: thisPage
637-
638- onStateChanged: { // ensure the search is reset (eg pressing Esc)
639- if (state === "default") {
640- searchField.text = ""
641- }
642-
643- // FIXME: Workaround for pad.lv/1514143 (keyboard show/hide on view moving)
644- // by locking the header and forcing a topMargin of page to the header height
645- headerState.head.locked = state === headerState.name;
646- thisPage.anchors.topMargin = state === headerState.name ? units.gu(6.125) : 0 // FIXME: 6.125 is header.height
647- }
648-
649- onVisibleChanged: {
650- // clear when the page becomes visible not invisible
651- // if invisible is used the delegates can be destroyed which
652- // have created the pushed component
653- if (visible) {
654- thisPage.state = "default"
655- }
656- }
657+ StyleHints {
658+ backgroundColor: mainView.headerColor
659+ dividerColor: Qt.darker(mainView.headerColor, 1.1)
660 }
661 }
662-
663- property Page thisPage
664+ property Item thisPage
665 property alias query: searchField.text
666+
667+ PropertyChanges {
668+ target: thisPage
669+ header: thisHeader
670+ }
671 }
672
673=== modified file 'app/components/HeadState/SearchableHeadState.qml'
674--- app/components/HeadState/SearchableHeadState.qml 2015-08-12 23:36:44 +0000
675+++ app/components/HeadState/SearchableHeadState.qml 2016-04-06 02:18:30 +0000
676@@ -20,15 +20,52 @@
677 import Ubuntu.Components 1.3
678
679
680-PageHeadState {
681+State {
682 name: "default"
683- head: thisPage.head
684- actions: Action {
685- id: searchAction
686- iconName: "search"
687- onTriggered: thisPage.state = "search"
688- }
689
690 property alias searchEnabled: searchAction.enabled
691- property Page thisPage
692+ property PageHeader thisHeader: PageHeader {
693+ id: headerState
694+ flickable: thisPage.flickable
695+ leadingActionBar {
696+ actions: {
697+ if (mainPageStack.currentPage === tabs) {
698+ tabs.tabActions
699+ } else if (mainPageStack.depth > 1) {
700+ backActionComponent
701+ }
702+ }
703+ }
704+ title: thisPage.title
705+ trailingActionBar {
706+ actions: [
707+ Action {
708+ id: searchAction
709+ iconName: "search"
710+ onTriggered: {
711+ thisPage.state = "search";
712+ thisPage.header.contents.forceActiveFocus();
713+ }
714+ }
715+ ]
716+ }
717+ visible: thisPage.state === "default"
718+
719+ Action {
720+ id: backActionComponent
721+ iconName: "back"
722+ onTriggered: mainPageStack.pop()
723+ }
724+
725+ StyleHints {
726+ backgroundColor: mainView.headerColor
727+ dividerColor: Qt.darker(mainView.headerColor, 1.1)
728+ }
729+ }
730+ property Item thisPage
731+
732+ PropertyChanges {
733+ target: thisPage
734+ header: thisHeader
735+ }
736 }
737
738=== modified file 'app/components/MusicPage.qml'
739--- app/components/MusicPage.qml 2015-10-23 03:08:43 +0000
740+++ app/components/MusicPage.qml 2016-04-06 02:18:30 +0000
741@@ -29,8 +29,47 @@
742 bottomMargin: musicToolbar.visible ? musicToolbar.height : 0
743 fill: parent
744 }
745+ head { // hide default header
746+ locked: true
747+ visible: false
748+ }
749+ header: PageHeader {
750+ id: pageHeader
751+ extension: Sections {
752+ // Create a fake head section when needed
753+ // resolves issues with parent.top binding to the incorrect place
754+ // when head sections are then added by the state
755+ model: hasSections ? [" "] : []
756+ }
757+
758+ flickable: thisPage.flickable
759+ leadingActionBar {
760+ actions: {
761+ if (mainPageStack.currentPage === tabs) {
762+ tabs.tabActions
763+ } else if (mainPageStack.depth > 1) {
764+ backActionComponent
765+ } else {
766+ null
767+ }
768+ }
769+ }
770+ title: thisPage.title
771+
772+ StyleHints {
773+ backgroundColor: mainView.headerColor
774+ dividerColor: Qt.darker(mainView.headerColor, 1.1)
775+ }
776+
777+ Action {
778+ id: backActionComponent
779+ iconName: "back"
780+ onTriggered: mainPageStack.pop()
781+ }
782+ }
783
784 property Dialog currentDialog
785+ property bool hasSections: false
786 property bool searchable: false
787 property int searchResultsCount
788 property bool showToolbar: true
789
790=== modified file 'app/components/NowPlayingFullView.qml'
791--- app/components/NowPlayingFullView.qml 2016-03-04 03:50:27 +0000
792+++ app/components/NowPlayingFullView.qml 2016-04-06 02:18:30 +0000
793@@ -29,6 +29,9 @@
794 fill: parent
795 }
796
797+ property string backgroundColor: styleMusic.common.black
798+ property bool sidebar: false
799+
800 BlurredBackground {
801 id: blurredBackground
802 anchors {
803@@ -37,7 +40,8 @@
804 top: parent.top
805 }
806 art: albumImage.firstSource
807- height: parent.height - units.gu(7)
808+ color: backgroundColor
809+ height: parent.height - (sidebar ? units.gu(7) + nowPlayingWideAspectLabelsBackground.height : units.gu(7))
810
811 Item {
812 id: albumImageContainer
813@@ -56,70 +60,6 @@
814 }
815 }
816
817- Rectangle {
818- id: nowPlayingWideAspectLabelsBackground
819- anchors.bottom: parent.bottom
820- color: styleMusic.common.black
821- height: nowPlayingWideAspectTitle.lineCount === 1 ? units.gu(10) : units.gu(13)
822- opacity: 0.8
823- width: parent.width
824- }
825-
826- /* Column for labels in wideAspect */
827- Column {
828- id: nowPlayingWideAspectLabels
829- spacing: units.gu(1)
830- anchors {
831- left: parent.left
832- leftMargin: units.gu(2)
833- right: parent.right
834- rightMargin: units.gu(2)
835- top: nowPlayingWideAspectLabelsBackground.top
836- topMargin: nowPlayingWideAspectTitle.lineCount === 1 ? units.gu(2) : units.gu(1.5)
837- }
838-
839- /* Title of track */
840- Label {
841- id: nowPlayingWideAspectTitle
842- anchors {
843- left: parent.left
844- leftMargin: units.gu(1)
845- right: parent.right
846- rightMargin: units.gu(1)
847- }
848- color: styleMusic.playerControls.labelColor
849- elide: Text.ElideRight
850- fontSize: "x-large"
851- maximumLineCount: 2
852- objectName: "playercontroltitle"
853- text: {
854- if (player.mediaPlayer.playlist.empty) {
855- ""
856- } else if (player.currentMeta.title === "") {
857- player.mediaPlayer.playlist.currentSource
858- } else {
859- player.currentMeta.title
860- }
861- }
862- wrapMode: Text.WordWrap
863- }
864-
865- /* Artist of track */
866- Label {
867- id: nowPlayingWideAspectArtist
868- anchors {
869- left: parent.left
870- leftMargin: units.gu(1)
871- right: parent.right
872- rightMargin: units.gu(1)
873- }
874- color: styleMusic.nowPlaying.labelSecondaryColor
875- elide: Text.ElideRight
876- fontSize: "small"
877- text: player.mediaPlayer.playlist.empty ? "" : player.currentMeta.author
878- }
879- }
880-
881 /* Detect cover art swipe */
882 MouseArea {
883 anchors.fill: parent
884@@ -142,6 +82,74 @@
885 }
886 }
887
888+ Rectangle {
889+ id: nowPlayingWideAspectLabelsBackground
890+ anchors {
891+ bottom: sidebar ? undefined : blurredBackground.bottom
892+ left: parent.left
893+ right: parent.right
894+ top: sidebar ? blurredBackground.bottom : undefined
895+ }
896+ color: backgroundColor
897+ height: nowPlayingWideAspectTitle.lineCount === 1 ? units.gu(10) : units.gu(13)
898+ opacity: sidebar ? 1.0 : 0.8
899+ }
900+
901+ /* Column for labels in wideAspect */
902+ Column {
903+ id: nowPlayingWideAspectLabels
904+ spacing: units.gu(1)
905+ anchors {
906+ left: parent.left
907+ leftMargin: units.gu(2)
908+ right: parent.right
909+ rightMargin: units.gu(2)
910+ top: nowPlayingWideAspectLabelsBackground.top
911+ topMargin: nowPlayingWideAspectTitle.lineCount === 1 ? units.gu(2) : units.gu(1.5)
912+ }
913+
914+ /* Title of track */
915+ Label {
916+ id: nowPlayingWideAspectTitle
917+ anchors {
918+ left: parent.left
919+ leftMargin: units.gu(1)
920+ right: parent.right
921+ rightMargin: units.gu(1)
922+ }
923+ color: styleMusic.playerControls.labelColor
924+ elide: Text.ElideRight
925+ fontSize: "x-large"
926+ maximumLineCount: 2
927+ objectName: "playercontroltitle"
928+ text: {
929+ if (player.mediaPlayer.playlist.empty) {
930+ ""
931+ } else if (player.currentMeta.title === "") {
932+ player.mediaPlayer.playlist.currentSource
933+ } else {
934+ player.currentMeta.title
935+ }
936+ }
937+ wrapMode: Text.WordWrap
938+ }
939+
940+ /* Artist of track */
941+ Label {
942+ id: nowPlayingWideAspectArtist
943+ anchors {
944+ left: parent.left
945+ leftMargin: units.gu(1)
946+ right: parent.right
947+ rightMargin: units.gu(1)
948+ }
949+ color: styleMusic.nowPlaying.labelSecondaryColor
950+ elide: Text.ElideRight
951+ fontSize: "small"
952+ text: player.mediaPlayer.playlist.empty ? "" : player.currentMeta.author
953+ }
954+ }
955+
956 /* Background for progress bar component */
957 Rectangle {
958 id: musicToolbarFullProgressBackground
959@@ -149,9 +157,9 @@
960 bottom: parent.bottom
961 left: parent.left
962 right: parent.right
963- top: blurredBackground.bottom
964+ top: sidebar ? nowPlayingWideAspectLabelsBackground.bottom : blurredBackground.bottom
965 }
966- color: styleMusic.common.black
967+ color: backgroundColor
968 }
969
970 /* Progress bar component */
971@@ -161,7 +169,7 @@
972 anchors.leftMargin: units.gu(3)
973 anchors.right: parent.right
974 anchors.rightMargin: units.gu(3)
975- anchors.top: blurredBackground.bottom
976+ anchors.top: sidebar ? nowPlayingWideAspectLabelsBackground.bottom : blurredBackground.bottom
977 anchors.topMargin: units.gu(1)
978 height: units.gu(3)
979 width: parent.width
980
981=== added file 'app/components/NowPlayingSidebar.qml'
982--- app/components/NowPlayingSidebar.qml 1970-01-01 00:00:00 +0000
983+++ app/components/NowPlayingSidebar.qml 2016-04-06 02:18:30 +0000
984@@ -0,0 +1,134 @@
985+/*
986+ * Copyright (C) 2013, 2014, 2015, 2016
987+ * Andrew Hayzen <ahayzen@gmail.com>
988+ * Daniel Holm <d.holmen@gmail.com>
989+ * Victor Thompson <victor.thompson@gmail.com>
990+ *
991+ * This program is free software; you can redistribute it and/or modify
992+ * it under the terms of the GNU General Public License as published by
993+ * the Free Software Foundation; version 3.
994+ *
995+ * This program is distributed in the hope that it will be useful,
996+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
997+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
998+ * GNU General Public License for more details.
999+ *
1000+ * You should have received a copy of the GNU General Public License
1001+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1002+ */
1003+
1004+import QtQuick 2.4
1005+import Ubuntu.Components 1.3
1006+
1007+import "HeadState"
1008+
1009+Rectangle {
1010+ id: nowPlayingSidebar
1011+ anchors {
1012+ fill: parent
1013+ }
1014+ color: "#2c2c34"
1015+ state: queue.state === "multiselectable" ? "selection" : "default"
1016+ states: [
1017+ QueueHeadState {
1018+ thisHeader {
1019+ leadingActionBar {
1020+ actions: [] // hide tab bar
1021+ }
1022+ z: 100 // put on top of content
1023+ }
1024+ thisPage: nowPlayingSidebar
1025+ },
1026+ MultiSelectHeadState {
1027+ addToQueue: false
1028+ listview: queue
1029+ removable: true
1030+ thisHeader {
1031+ z: 100 // put on top of content
1032+ }
1033+ thisPage: nowPlayingSidebar
1034+
1035+ onRemoved: {
1036+ // Remove the tracks from the queue
1037+ // Use slice() to copy the list
1038+ // so that the indexes don't change as they are removed
1039+ player.mediaPlayer.playlist.removeItemsWrapper(selectedIndices.slice());
1040+ }
1041+ }
1042+ ]
1043+ property alias flickable: queue // fake normal Page
1044+ property Item header: PageHeader {
1045+ id: pageHeader
1046+ leadingActionBar {
1047+ actions: nowPlayingSidebar.head.backAction
1048+ }
1049+ flickable: queue
1050+ trailingActionBar {
1051+ actions: nowPlayingSidebar.head.actions
1052+ }
1053+ z: 100 // put on top of content
1054+
1055+ StyleHints {
1056+ backgroundColor: mainView.headerColor
1057+ }
1058+ }
1059+ property Item previousHeader: null
1060+ property string title: "" // fake normal Page
1061+
1062+ onHeaderChanged: { // Copy what SDK does to parent header correctly
1063+ if (previousHeader) {
1064+ previousHeader.parent = null
1065+ }
1066+
1067+ header.parent = nowPlayingSidebar
1068+ previousHeader = header;
1069+ }
1070+
1071+ Loader {
1072+ anchors {
1073+ left: parent.left
1074+ right: parent.right
1075+ top: parent.top
1076+ }
1077+ height: units.gu(6.125)
1078+ sourceComponent: header
1079+ }
1080+
1081+ Queue {
1082+ id: queue
1083+ anchors {
1084+ bottomMargin: 0
1085+ topMargin: 0
1086+ }
1087+ clip: true
1088+ isSidebar: true
1089+ header: Column {
1090+ id: sidebarColumn
1091+ anchors {
1092+ left: parent.left
1093+ right: parent.right
1094+ }
1095+
1096+ NowPlayingFullView {
1097+ anchors {
1098+ fill: undefined
1099+ }
1100+ backgroundColor: "#2c2c34"
1101+ clip: true
1102+ height: units.gu(47)
1103+ sidebar: true
1104+ width: parent.width
1105+ }
1106+
1107+ NowPlayingToolbar {
1108+ anchors {
1109+ fill: undefined
1110+ }
1111+ bottomProgressHint: false
1112+ color: "#2c2c34"
1113+ height: itemSize + 2 * spacing + units.gu(2)
1114+ width: parent.width
1115+ }
1116+ }
1117+ }
1118+}
1119
1120=== modified file 'app/components/NowPlayingToolbar.qml'
1121--- app/components/NowPlayingToolbar.qml 2016-01-16 01:30:31 +0000
1122+++ app/components/NowPlayingToolbar.qml 2016-04-06 02:18:30 +0000
1123@@ -30,19 +30,23 @@
1124 }
1125 color: styleMusic.common.black
1126
1127+ property alias bottomProgressHint: playerControlsProgressBar.visible
1128+ property int itemSize: units.gu(6)
1129+ property int spacing: units.gu(1)
1130+
1131 /* Repeat button */
1132 MouseArea {
1133 id: nowPlayingRepeatButton
1134 anchors.right: nowPlayingPreviousButton.left
1135- anchors.rightMargin: units.gu(1)
1136+ anchors.rightMargin: spacing
1137 anchors.verticalCenter: nowPlayingPlayButton.verticalCenter
1138- height: units.gu(6)
1139+ height: itemSize
1140 width: height
1141 onClicked: player.repeat = !player.repeat
1142
1143 Icon {
1144 id: repeatIcon
1145- height: units.gu(3)
1146+ height: itemSize / 2
1147 width: height
1148 anchors.verticalCenter: parent.verticalCenter
1149 anchors.horizontalCenter: parent.horizontalCenter
1150@@ -58,15 +62,15 @@
1151 id: nowPlayingPreviousButton
1152 enabled: player.mediaPlayer.playlist.canGoPrevious
1153 anchors.right: nowPlayingPlayButton.left
1154- anchors.rightMargin: units.gu(1)
1155+ anchors.rightMargin: spacing
1156 anchors.verticalCenter: nowPlayingPlayButton.verticalCenter
1157- height: units.gu(6)
1158+ height: itemSize
1159 width: height
1160 onClicked: player.mediaPlayer.playlist.previousWrapper()
1161
1162 Icon {
1163 id: nowPlayingPreviousIndicator
1164- height: units.gu(3)
1165+ height: itemSize / 2
1166 width: height
1167 anchors.verticalCenter: parent.verticalCenter
1168 anchors.horizontalCenter: parent.horizontalCenter
1169@@ -81,13 +85,13 @@
1170 MouseArea {
1171 id: nowPlayingPlayButton
1172 anchors.centerIn: parent
1173- height: units.gu(10)
1174+ height: itemSize + (2 * spacing)
1175 width: height
1176 onClicked: player.mediaPlayer.toggle()
1177
1178 Icon {
1179 id: nowPlayingPlayIndicator
1180- height: units.gu(6)
1181+ height: itemSize
1182 width: height
1183 anchors.verticalCenter: parent.verticalCenter
1184 anchors.horizontalCenter: parent.horizontalCenter
1185@@ -101,16 +105,16 @@
1186 MouseArea {
1187 id: nowPlayingNextButton
1188 anchors.left: nowPlayingPlayButton.right
1189- anchors.leftMargin: units.gu(1)
1190+ anchors.leftMargin: spacing
1191 anchors.verticalCenter: nowPlayingPlayButton.verticalCenter
1192 enabled: player.mediaPlayer.playlist.canGoNext
1193- height: units.gu(6)
1194+ height: itemSize
1195 width: height
1196 onClicked: player.mediaPlayer.playlist.nextWrapper()
1197
1198 Icon {
1199 id: nowPlayingNextIndicator
1200- height: units.gu(3)
1201+ height: itemSize / 2
1202 width: height
1203 anchors.verticalCenter: parent.verticalCenter
1204 anchors.horizontalCenter: parent.horizontalCenter
1205@@ -125,15 +129,15 @@
1206 MouseArea {
1207 id: nowPlayingShuffleButton
1208 anchors.left: nowPlayingNextButton.right
1209- anchors.leftMargin: units.gu(1)
1210+ anchors.leftMargin: spacing
1211 anchors.verticalCenter: nowPlayingPlayButton.verticalCenter
1212- height: units.gu(6)
1213+ height: itemSize
1214 width: height
1215 onClicked: player.shuffle = !player.shuffle
1216
1217 Icon {
1218 id: shuffleIcon
1219- height: units.gu(3)
1220+ height: itemSize / 2
1221 width: height
1222 anchors.verticalCenter: parent.verticalCenter
1223 anchors.horizontalCenter: parent.horizontalCenter
1224
1225=== modified file 'app/components/Queue.qml'
1226--- app/components/Queue.qml 2016-01-12 11:44:16 +0000
1227+++ app/components/Queue.qml 2016-04-06 02:18:30 +0000
1228@@ -29,7 +29,9 @@
1229 MultiSelectListView {
1230 id: queueList
1231 anchors {
1232+ bottomMargin: units.gu(1)
1233 fill: parent
1234+ topMargin: units.gu(1)
1235 }
1236 autoModelMove: false // ensures we use moveItem() not move() in onReorder
1237 footer: Item {
1238@@ -38,11 +40,13 @@
1239 model: player.mediaPlayer.playlist
1240 objectName: "nowPlayingqueueList"
1241
1242+ property bool isSidebar: false
1243+
1244 onCountChanged: customdebug("Queue: Now has: " + queueList.count + " tracks")
1245
1246 delegate: MusicListItem {
1247 id: queueListItem
1248- color: player.mediaPlayer.playlist.currentIndex === index ? "#2c2c34" : styleMusic.mainView.backgroundColor
1249+ color: player.mediaPlayer.playlist.currentIndex === index ? (isSidebar ? "#3d3d45" : "#2c2c34") : (isSidebar ? "#2c2c34" : styleMusic.mainView.backgroundColor)
1250 height: units.gu(7)
1251 leadingActions: ListItemActions {
1252 actions: [
1253
1254=== modified file 'app/music-app.qml'
1255--- app/music-app.qml 2016-01-28 01:43:54 +0000
1256+++ app/music-app.qml 2016-04-06 02:18:30 +0000
1257@@ -115,7 +115,7 @@
1258 break;
1259 case Qt.Key_J: // Ctrl+J Jump to playing song
1260 tabs.pushNowPlaying()
1261- mainPageStack.currentPage.isListView = true
1262+ mainPageStack.currentPage.setListView(true)
1263 break;
1264 case Qt.Key_N: // Ctrl+N Show Now playing
1265 tabs.pushNowPlaying()
1266@@ -282,7 +282,7 @@
1267
1268 signal listItemSwiping(int i)
1269
1270- property bool wideAspect: width >= units.gu(70) && loadedUI
1271+ property bool wideAspect: width >= units.gu(95) && loadedUI
1272 property bool loadedUI: false // property to detect if the UI has finished
1273
1274 // FUNCTIONS
1275@@ -567,23 +567,16 @@
1276 }
1277 }
1278
1279- Loader {
1280- id: musicToolbar
1281- anchors {
1282- bottom: parent.bottom
1283- left: parent.left
1284- right: parent.right
1285- }
1286- asynchronous: true
1287- source: "components/MusicToolbar.qml"
1288- visible: (mainPageStack.currentPage.showToolbar || mainPageStack.currentPage.showToolbar === undefined) &&
1289- !firstRun &&
1290- !noMusic
1291- z: 200 // put on top of everything else
1292- }
1293-
1294 PageStack {
1295 id: mainPageStack
1296+ anchors {
1297+ bottom: parent.bottom
1298+ fill: undefined
1299+ left: parent.left
1300+ right: nowPlayingSidebarLoader.left
1301+ top: parent.top
1302+ }
1303+ clip: true // otherwise listitems actions overflow
1304
1305 // Properties storing the current page info
1306 property Page currentMusicPage: null // currentPage can be Tabs
1307@@ -644,6 +637,39 @@
1308 }
1309
1310 property Tab lastTab: selectedTab
1311+ property list<Action> tabActions: [
1312+ Action {
1313+ enabled: recentTabRepeater.count > 0
1314+ text: enabled ? recentTabRepeater.itemAt(0).title : ""
1315+ visible: enabled
1316+
1317+ onTriggered: {
1318+ if (enabled) {
1319+ tabs.selectedTabIndex = recentTabRepeater.itemAt(0).index
1320+ }
1321+ }
1322+ },
1323+ Action {
1324+ text: artistsTab.title
1325+ onTriggered: tabs.selectedTabIndex = artistsTab.index
1326+ },
1327+ Action {
1328+ text: albumsTab.title
1329+ onTriggered: tabs.selectedTabIndex = albumsTab.index
1330+ },
1331+ Action {
1332+ text: genresTab.title
1333+ onTriggered: tabs.selectedTabIndex = genresTab.index
1334+ },
1335+ Action {
1336+ text: songsTab.title
1337+ onTriggered: tabs.selectedTabIndex = songsTab.index
1338+ },
1339+ Action {
1340+ text: playlistsTab.title
1341+ onTriggered: tabs.selectedTabIndex = playlistsTab.index
1342+ }
1343+ ]
1344
1345 onSelectedTabChanged: {
1346 // pause loading of the models in the old tab
1347@@ -844,18 +870,70 @@
1348
1349 function pushNowPlaying()
1350 {
1351- // only push if on a different page
1352- if (mainPageStack.currentPage.title !== i18n.tr("Now playing")) {
1353- mainPageStack.push(Qt.resolvedUrl("ui/NowPlaying.qml"), {})
1354- }
1355+ if (!wideAspect) {
1356+ // only push if on a different page
1357+ if (mainPageStack.currentPage.title !== i18n.tr("Now playing")) {
1358+ mainPageStack.push(Qt.resolvedUrl("ui/NowPlaying.qml"), {})
1359+ }
1360
1361- if (mainPageStack.currentPage.isListView === true) {
1362- mainPageStack.currentPage.isListView = false; // ensure full view
1363+ if (mainPageStack.currentPage.isListView === true) {
1364+ mainPageStack.currentPage.setListView(false); // ensure full view
1365+ }
1366 }
1367 }
1368 } // end of tabs
1369 }
1370
1371+ //
1372+ // Components that are ontop of the PageStack
1373+ //
1374+
1375+ Loader {
1376+ id: nowPlayingSidebarLoader
1377+ active: shown || anchors.leftMargin < 0
1378+ anchors { // start offscreen
1379+ bottom: parent.bottom
1380+ left: parent.right
1381+ leftMargin: shown && status === Loader.Ready ? -width : 0
1382+ top: parent.top
1383+ }
1384+ asynchronous: true
1385+ source: "components/NowPlayingSidebar.qml"
1386+ visible: width > 0
1387+ width: units.gu(40)
1388+
1389+ property bool shown: loadedUI && wideAspect && player.mediaPlayer.playlist.itemCount > 0
1390+
1391+ Behavior on anchors.leftMargin {
1392+ NumberAnimation {
1393+
1394+ }
1395+ }
1396+ }
1397+
1398+ Loader {
1399+ id: musicToolbar
1400+ active: !wideAspect || anchors.topMargin < 0
1401+ anchors {
1402+ left: parent.left
1403+ right: parent.right
1404+ top: parent.bottom
1405+ topMargin: !wideAspect && status === Loader.Ready ? -height : 0
1406+ }
1407+ asynchronous: true
1408+ source: "components/MusicToolbar.qml"
1409+ visible: (mainPageStack.currentPage && (mainPageStack.currentPage.showToolbar || mainPageStack.currentPage.showToolbar === undefined)) &&
1410+ !firstRun &&
1411+ !noMusic &&
1412+ anchors.topMargin < 0
1413+
1414+ Behavior on anchors.topMargin {
1415+ NumberAnimation {
1416+
1417+ }
1418+ }
1419+ }
1420+
1421 LoadingSpinnerComponent {
1422 id: loading
1423 }
1424
1425=== modified file 'app/ui/AddToPlaylist.qml'
1426--- app/ui/AddToPlaylist.qml 2016-02-28 01:51:17 +0000
1427+++ app/ui/AddToPlaylist.qml 2016-04-06 02:18:30 +0000
1428@@ -59,10 +59,13 @@
1429
1430 // FIXME: workaround for pad.lv/1531016 (gridview juddery)
1431 anchors {
1432+ bottom: parent.bottom
1433 fill: undefined
1434+ left: parent.left
1435+ top: parent.top
1436 }
1437- height: mainView.height + musicToolbar.height
1438- width: mainView.width
1439+ height: mainPageStack.height + musicToolbar.height
1440+ width: mainPageStack.width
1441
1442 property var chosenElements: []
1443
1444
1445=== modified file 'app/ui/Albums.qml'
1446--- app/ui/Albums.qml 2016-01-12 00:30:08 +0000
1447+++ app/ui/Albums.qml 2016-04-06 02:18:30 +0000
1448@@ -46,10 +46,13 @@
1449
1450 // FIXME: workaround for pad.lv/1531016 (gridview juddery)
1451 anchors {
1452+ bottom: parent.bottom
1453 fill: undefined
1454+ left: parent.left
1455+ top: parent.top
1456 }
1457- height: mainView.height
1458- width: mainView.width
1459+ height: mainPageStack.height
1460+ width: mainPageStack.width
1461
1462 // Hack for autopilot otherwise Albums appears as MusicPage
1463 // due to bug 1341671 it is required that there is a property so that
1464
1465=== modified file 'app/ui/ArtistView.qml'
1466--- app/ui/ArtistView.qml 2016-01-12 00:30:08 +0000
1467+++ app/ui/ArtistView.qml 2016-04-06 02:18:30 +0000
1468@@ -26,11 +26,18 @@
1469 import "../components"
1470 import "../components/Delegates"
1471 import "../components/Flickables"
1472+import "../components/HeadState"
1473 import "../components/ViewButton"
1474
1475 MusicPage {
1476 id: artistViewPage
1477 objectName: "artistViewPage"
1478+ state: "default"
1479+ states: [
1480+ EmptyHeadState {
1481+ thisPage: artistViewPage
1482+ }
1483+ ]
1484
1485 property string artist: ""
1486 property var covers: []
1487@@ -38,10 +45,13 @@
1488
1489 // FIXME: workaround for pad.lv/1531016 (gridview juddery)
1490 anchors {
1491+ bottom: parent.bottom
1492 fill: undefined
1493+ left: parent.left
1494+ top: parent.top
1495 }
1496- height: mainView.height
1497- width: mainView.width
1498+ height: mainPageStack.height
1499+ width: mainPageStack.width
1500
1501 MusicGridView {
1502 id: artistAlbumView
1503
1504=== modified file 'app/ui/Artists.qml'
1505--- app/ui/Artists.qml 2016-01-30 23:58:32 +0000
1506+++ app/ui/Artists.qml 2016-04-06 02:18:30 +0000
1507@@ -50,10 +50,13 @@
1508
1509 // FIXME: workaround for pad.lv/1531016 (gridview juddery)
1510 anchors {
1511+ bottom: parent.bottom
1512 fill: undefined
1513+ left: parent.left
1514+ top: parent.top
1515 }
1516- height: mainView.height
1517- width: mainView.width
1518+ height: mainPageStack.height
1519+ width: mainPageStack.width
1520
1521 // Hack for autopilot otherwise Artists appears as MusicPage
1522 // due to bug 1341671 it is required that there is a property so that
1523
1524=== modified file 'app/ui/ContentHubExport.qml'
1525--- app/ui/ContentHubExport.qml 2016-03-16 16:50:54 +0000
1526+++ app/ui/ContentHubExport.qml 2016-04-06 02:18:30 +0000
1527@@ -36,34 +36,39 @@
1528 showToolbar: false
1529 state: "default"
1530 states: [
1531- PageHeadState {
1532- id: defaultState
1533- name: "default"
1534- actions: [
1535- tickAction,
1536- searchAction,
1537- ]
1538- backAction: Action {
1539- iconName: "close"
1540- onTriggered: {
1541- transfer.items = [];
1542- transfer.state = ContentTransfer.Aborted;
1543-
1544- mainPageStack.pop()
1545- }
1546- }
1547-
1548- PropertyChanges {
1549- target: contentHubExportPage.head
1550- backAction: defaultState.backAction
1551- actions: defaultState.actions
1552- }
1553+ EmptyHeadState {
1554+ thisHeader {
1555+ leadingActionBar {
1556+ actions: [
1557+ Action {
1558+ iconName: "close"
1559+ onTriggered: {
1560+ transfer.items = [];
1561+ transfer.state = ContentTransfer.Aborted;
1562+
1563+ mainPageStack.pop()
1564+ }
1565+ }
1566+ ]
1567+ }
1568+ trailingActionBar {
1569+ actions: [
1570+ tickAction,
1571+ searchAction,
1572+ ]
1573+ }
1574+ }
1575+ thisPage: contentHubExportPage
1576 },
1577 SearchHeadState {
1578 id: searchHeader
1579- actions: [
1580- tickAction,
1581- ]
1582+ thisHeader {
1583+ trailingActionBar {
1584+ actions: [
1585+ tickAction,
1586+ ]
1587+ }
1588+ }
1589 thisPage: contentHubExportPage
1590
1591 onQueryChanged: trackList.clearSelection()
1592@@ -84,7 +89,10 @@
1593 id: searchAction
1594 enabled: songsModelFilter.count > 0
1595 iconName: "search"
1596- onTriggered: contentHubExportPage.state = "search"
1597+ onTriggered: {
1598+ contentHubExportPage.state = "search"
1599+ contentHubExportPage.header.contents.forceActiveFocus();
1600+ }
1601 }
1602 Action {
1603 id: tickAction
1604
1605=== modified file 'app/ui/Genres.qml'
1606--- app/ui/Genres.qml 2016-01-12 00:30:08 +0000
1607+++ app/ui/Genres.qml 2016-04-06 02:18:30 +0000
1608@@ -46,10 +46,13 @@
1609
1610 // FIXME: workaround for pad.lv/1531016 (gridview juddery)
1611 anchors {
1612+ bottom: parent.bottom
1613 fill: undefined
1614+ left: parent.left
1615+ top: parent.top
1616 }
1617- height: mainView.height
1618- width: mainView.width
1619+ height: mainPageStack.height
1620+ width: mainPageStack.width
1621
1622 // Hack for autopilot otherwise Albums appears as MusicPage
1623 // due to bug 1341671 it is required that there is a property so that
1624
1625=== modified file 'app/ui/NowPlaying.qml'
1626--- app/ui/NowPlaying.qml 2016-01-16 01:30:31 +0000
1627+++ app/ui/NowPlaying.qml 2016-04-06 02:18:30 +0000
1628@@ -28,10 +28,86 @@
1629 MusicPage {
1630 id: nowPlaying
1631 flickable: isListView ? queueListLoader.item : null // Ensures that the header is shown in fullview
1632+ hasSections: true
1633 objectName: "nowPlayingPage"
1634 showToolbar: false
1635+ state: {
1636+ if (isListView) {
1637+ if (queueListLoader.item.state === "multiselectable") {
1638+ "selection"
1639+ } else {
1640+ "default"
1641+ }
1642+ } else {
1643+ "fullview"
1644+ }
1645+ }
1646+ states: [
1647+ // FIXME: fullview has its own state for now as changing the flickable
1648+ // property sometimes causes the header to disappear
1649+ QueueHeadState {
1650+ stateName: "fullview"
1651+ thisHeader {
1652+ extension: Sections {
1653+ model: defaultStateSections.model
1654+ selectedIndex: 0
1655+
1656+ onSelectedIndexChanged: {
1657+ if (selectedIndex == 1) {
1658+ isListView = !isListView;
1659+ selectedIndex = 0;
1660+ }
1661+ }
1662+ }
1663+ flickable: null
1664+ }
1665+ thisPage: nowPlaying
1666+ },
1667+ QueueHeadState {
1668+ thisHeader {
1669+ extension: Sections {
1670+ model: defaultStateSections.model
1671+ selectedIndex: 1
1672+
1673+ onSelectedIndexChanged: {
1674+ if (selectedIndex == 0) {
1675+ isListView = !isListView;
1676+ selectedIndex = 1;
1677+ }
1678+ }
1679+ }
1680+ flickable: queueListLoader.item
1681+ }
1682+ thisPage: nowPlaying
1683+ },
1684+ MultiSelectHeadState {
1685+ addToQueue: false
1686+ listview: queueListLoader.item
1687+ removable: true
1688+ thisHeader {
1689+ extension: Sections {
1690+ model: defaultStateSections.model
1691+ selectedIndex: 1
1692+
1693+ onSelectedIndexChanged: {
1694+ if (selectedIndex == 0) {
1695+ isListView = !isListView;
1696+ selectedIndex = 1;
1697+ }
1698+ }
1699+ }
1700+ }
1701+ thisPage: nowPlaying
1702+
1703+ onRemoved: {
1704+ // Remove the tracks from the queue
1705+ // Use slice() to copy the list
1706+ // so that the indexes don't change as they are removed
1707+ player.mediaPlayer.playlist.removeItemsWrapper(selectedIndices.slice());
1708+ }
1709+ }
1710+ ]
1711 title: nowPlayingTitle
1712- visible: false
1713
1714 property bool isListView: false
1715 // TRANSLATORS: this appears in the header with limited space (around 20 characters)
1716@@ -58,6 +134,27 @@
1717 queueListLoader.item.closeSelection()
1718 }
1719 }
1720+ onVisibleChanged: {
1721+ if (wideAspect) {
1722+ popWaitTimer.start()
1723+ }
1724+ }
1725+
1726+ Timer { // FIXME: workaround for when entering wideAspect coming back from a stacked page (AddToPlaylist) and the page being deleted breaks the stacked page
1727+ id: popWaitTimer
1728+ interval: 250
1729+ onTriggered: mainPageStack.popPage(nowPlaying);
1730+ }
1731+
1732+ PageHeadSections {
1733+ id: defaultStateSections
1734+ model: [fullViewTitle, queueTitle]
1735+
1736+ onSelectedIndexChanged: isListView = selectedIndex == 1
1737+
1738+ // Set at startup to avoid binding loop
1739+ Component.onCompleted: selectedIndex = isListView ? 1 : 0
1740+ }
1741
1742 // Ensure that the listview has loaded before attempting to positionAt
1743 function ensureListViewLoaded() {
1744@@ -77,86 +174,17 @@
1745 queueListLoader.item.positionViewAtIndex(index, ListView.Center);
1746 }
1747
1748- PageHeadSections {
1749- id: defaultStateSections
1750- model: [fullViewTitle, queueTitle]
1751- selectedIndex: isListView
1752- }
1753-
1754- head {
1755- sections {
1756- model: defaultStateSections.model
1757- selectedIndex: defaultStateSections.selectedIndex
1758- onSelectedIndexChanged: isListView = !isListView
1759- }
1760- }
1761-
1762- state: isListView && queueListLoader.item.state === "multiselectable" ? "selection" : "default"
1763- states: [
1764- PageHeadState {
1765- id: defaultState
1766-
1767- name: "default"
1768- actions: [
1769- Action {
1770- enabled: !player.mediaPlayer.playlist.empty
1771- iconName: "add-to-playlist"
1772- // TRANSLATORS: this action appears in the overflow drawer with limited space (around 18 characters)
1773- text: i18n.tr("Add to playlist")
1774- visible: !isListView
1775-
1776- onTriggered: {
1777- var items = []
1778-
1779- items.push(makeDict(player.metaForSource(player.mediaPlayer.playlist.currentItemSource)));
1780-
1781- mainPageStack.push(Qt.resolvedUrl("AddToPlaylist.qml"),
1782- {"chosenElements": items})
1783- }
1784- },
1785- Action {
1786- enabled: !player.mediaPlayer.playlist.empty
1787- iconName: "delete"
1788- objectName: "clearQueue"
1789- // TRANSLATORS: this action appears in the overflow drawer with limited space (around 18 characters)
1790- text: i18n.tr("Clear queue")
1791- visible: isListView
1792-
1793- onTriggered: player.mediaPlayer.playlist.clearWrapper()
1794- }
1795- ]
1796- PropertyChanges {
1797- target: nowPlaying.head
1798- backAction: defaultState.backAction
1799- actions: defaultState.actions
1800- }
1801- },
1802- MultiSelectHeadState {
1803- addToQueue: false
1804- listview: queueListLoader.item
1805- removable: true
1806- thisPage: nowPlaying
1807-
1808- onRemoved: {
1809- // Remove the tracks from the queue
1810- // Use slice() to copy the list
1811- // so that the indexes don't change as they are removed
1812- player.mediaPlayer.playlist.removeItemsWrapper(selectedIndices.slice());
1813- }
1814- }
1815- ]
1816+ function setListView(listView) {
1817+ defaultStateSections.selectedIndex = listView ? 1 : 0;
1818+ }
1819
1820 Loader {
1821 anchors {
1822 bottom: nowPlayingToolbarLoader.top
1823 left: parent.left
1824 right: parent.right
1825- top: parent.top
1826- topMargin: headerHeight
1827+ top: nowPlaying.header.bottom
1828 }
1829-
1830- property real headerHeight: units.gu(10.125) // FIXME: 10.125 is the header.height with the page sections
1831-
1832 source: "../components/NowPlayingFullView.qml"
1833 visible: !isListView
1834 }
1835@@ -165,11 +193,9 @@
1836 id: queueListLoader
1837 anchors {
1838 bottom: nowPlayingToolbarLoader.top
1839- bottomMargin: units.gu(2)
1840 left: parent.left
1841 right: parent.right
1842- top: parent.top
1843- topMargin: units.gu(2)
1844+ top: parent.top // Don't use header.bottom otherwise flickery
1845 }
1846 asynchronous: true
1847 source: "../components/Queue.qml"
1848@@ -188,6 +214,16 @@
1849 }
1850
1851 Connections {
1852+ target: mainView
1853+ onWideAspectChanged: {
1854+ // Do not pop if not visible (eg on AddToPlaylist)
1855+ if (wideAspect && nowPlaying.visible) {
1856+ mainPageStack.popPage(nowPlaying);
1857+ }
1858+ }
1859+ }
1860+
1861+ Connections {
1862 target: player.mediaPlayer.playlist
1863 onEmptyChanged: {
1864 if (player.mediaPlayer.playlist.empty) {
1865
1866=== modified file 'app/ui/Playlists.qml'
1867--- app/ui/Playlists.qml 2016-01-29 02:20:55 +0000
1868+++ app/ui/Playlists.qml 2016-04-06 02:18:30 +0000
1869@@ -51,10 +51,13 @@
1870
1871 // FIXME: workaround for pad.lv/1531016 (gridview juddery)
1872 anchors {
1873+ bottom: parent.bottom
1874 fill: undefined
1875+ left: parent.left
1876+ top: parent.top
1877 }
1878- height: mainView.height
1879- width: mainView.width
1880+ height: mainPageStack.height
1881+ width: mainPageStack.width
1882
1883 property bool changed: false
1884 property bool childrenChanged: false
1885
1886=== modified file 'app/ui/Recent.qml'
1887--- app/ui/Recent.qml 2016-01-12 01:06:16 +0000
1888+++ app/ui/Recent.qml 2016-04-06 02:18:30 +0000
1889@@ -31,14 +31,53 @@
1890 MusicPage {
1891 id: recentPage
1892 objectName: "recentPage"
1893+ header: PageHeader {
1894+ flickable: recentPage.flickable
1895+ leadingActionBar {
1896+ actions: {
1897+ if (mainPageStack.currentPage === tabs) {
1898+ tabs.tabActions
1899+ } else if (mainPageStack.depth > 1) {
1900+ backActionComponent
1901+ }
1902+ }
1903+ }
1904+ title: recentPage.title
1905+ trailingActionBar {
1906+ actions: [
1907+ Action {
1908+ enabled: recentModel.model.count > 0
1909+ iconName: "delete"
1910+ onTriggered: {
1911+ Library.clearRecentHistory()
1912+ recentModel.filterRecent()
1913+ }
1914+ }
1915+ ]
1916+ }
1917+
1918+ Action {
1919+ id: backActionComponent
1920+ iconName: "back"
1921+ onTriggered: mainPageStack.pop()
1922+ }
1923+
1924+ StyleHints {
1925+ backgroundColor: mainView.headerColor
1926+ dividerColor: Qt.darker(mainView.headerColor, 1.1)
1927+ }
1928+ }
1929 title: i18n.tr("Recent")
1930
1931 // FIXME: workaround for pad.lv/1531016 (gridview juddery)
1932 anchors {
1933+ bottom: parent.bottom
1934 fill: undefined
1935+ left: parent.left
1936+ top: parent.top
1937 }
1938- height: mainView.height
1939- width: mainView.width
1940+ height: mainPageStack.height
1941+ width: mainPageStack.width
1942
1943 property bool changed: false
1944 property bool childrenChanged: false
1945@@ -56,19 +95,6 @@
1946 onTriggered: recentModel.filterRecent()
1947 }
1948
1949- head {
1950- actions: [
1951- Action {
1952- enabled: recentModel.model.count > 0
1953- iconName: "delete"
1954- onTriggered: {
1955- Library.clearRecentHistory()
1956- recentModel.filterRecent()
1957- }
1958- }
1959- ]
1960- }
1961-
1962 MusicGridView {
1963 id: recentGridView
1964 itemWidth: units.gu(15)
1965
1966=== modified file 'app/ui/SongsView.qml'
1967--- app/ui/SongsView.qml 2016-01-29 02:20:55 +0000
1968+++ app/ui/SongsView.qml 2016-04-06 02:18:30 +0000
1969@@ -110,43 +110,13 @@
1970 }
1971 }
1972
1973- state: albumtrackslist.state === "multiselectable" ? "selection" : (songStackPage.line1 === i18n.tr("Playlist") ? "playlist" : "album")
1974+ state: albumtrackslist.state === "multiselectable" ? "selection" : (songStackPage.line1 === i18n.tr("Playlist") ? "playlist" : "default")
1975 states: [
1976- PageHeadState {
1977- id: albumState
1978- name: "album"
1979- PropertyChanges {
1980- target: songStackPage.head
1981- backAction: albumState.backAction
1982- actions: albumState.actions
1983- }
1984+ EmptyHeadState {
1985+ thisPage: songStackPage
1986 },
1987- PageHeadState {
1988- id: playlistState
1989- name: "playlist"
1990- actions: [
1991- Action {
1992- objectName: "editPlaylist"
1993- iconName: "edit"
1994- onTriggered: {
1995- currentDialog = PopupUtils.open(Qt.resolvedUrl("../components/Dialog/EditPlaylistDialog.qml"), mainView)
1996- currentDialog.oldPlaylistName = line2
1997- }
1998- },
1999- Action {
2000- objectName: "deletePlaylist"
2001- iconName: "delete"
2002- onTriggered: {
2003- currentDialog = PopupUtils.open(Qt.resolvedUrl("../components/Dialog/RemovePlaylistDialog.qml"), mainView)
2004- currentDialog.oldPlaylistName = line2
2005- }
2006- }
2007- ]
2008- PropertyChanges {
2009- target: songStackPage.head
2010- backAction: playlistState.backAction
2011- actions: playlistState.actions
2012- }
2013+ PlaylistHeadState {
2014+ thisPage: songStackPage
2015 },
2016 MultiSelectHeadState {
2017 listview: albumtrackslist
2018
2019=== modified file 'debian/changelog'
2020--- debian/changelog 2016-03-23 02:12:30 +0000
2021+++ debian/changelog 2016-04-06 02:18:30 +0000
2022@@ -14,6 +14,7 @@
2023
2024 [ Andrew Hayzen ]
2025 * Fix so that a press and hold cannot disable selection in the ContentHubExport.qml (LP: #1538838)
2026+ * Implement convergent mode with now playing and queue as a sidebar (LP: #1253761)
2027
2028 [ Ken VanDine ]
2029 * Bump Ubuntu.Content import to 1.3 to fix theme issue

Subscribers

People subscribed via source and target branches