Merge lp:~ahayzen/music-app/convergence-tabs-with-sidebar-01 into lp:music-app
- convergence-tabs-with-sidebar-01
- Merge into trunk
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 | ||||||||
Related bugs: |
|
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]
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:976
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
- 977. By Andrew Hayzen
-
* Add missing file and set dividor colour
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:977
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
- 978. By Andrew Hayzen
-
* Set divider to be darker not lighter
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:978
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
- 979. By Andrew Hayzen
-
* Set flickable on fallback PageHeader
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:979
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Victor Thompson (vthompson) wrote : | # |
Added some inline code issues/questions. Will try to do some functional testing as well tonight/tomorrow.
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.
- 980. By Andrew Hayzen
-
* Various fixes
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 :-)
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:980
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Victor Thompson (vthompson) wrote : | # |
I can still reproduce #12 while on the new silo #50.
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
Andrew Hayzen (ahayzen) wrote : | # |
Please retest #12, I've been unable to reproduce this seen moving to extension: Sections {} (as sections: {} will be deprecated).
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
FAILED: Continuous integration, rev:981
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
- 982. By Andrew Hayzen
-
* Merge of trunk
* Fixes for SongView to use PageHeader instead of PageHeadState
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:982
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
- 983. By Andrew Hayzen
-
* Removal of the last PageHeadState to PageHeader
Andrew Hayzen (ahayzen) wrote : | # |
Please test all headers as I found a few that hadn't been converted and therefore were broken.
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:983
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
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)?
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
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote : | # |
PASSED: Continuous integration, rev:984
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
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)
Preview Diff
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 |
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?