Merge lp:~ahayzen/music-app/now-playing-queue-split into lp:music-app/trusty
- now-playing-queue-split
- Merge into trusty
Status: | Rejected |
---|---|
Rejected by: | Andrew Hayzen |
Proposed branch: | lp:~ahayzen/music-app/now-playing-queue-split |
Merge into: | lp:music-app/trusty |
Diff against target: |
1411 lines (+670/-643) 5 files modified
MusicNowPlaying.qml (+75/-630) MusicQueue.qml (+60/-0) MusicToolbar.qml (+10/-7) common/Queue.qml (+510/-0) music-app.qml (+15/-6) |
To merge this branch: | bzr merge lp:~ahayzen/music-app/now-playing-queue-split |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Phone Apps Jenkins Bot | continuous-integration | Needs Fixing | |
Victor Thompson | Needs Fixing | ||
Review via email: mp+225417@code.launchpad.net |
Commit message
* Split now playing and queue into separate components and pages
* Make a common/Queue.qml component which is reusable (for convergence later)
Description of the change
* Split now playing and queue into separate components and pages
* Make a common/Queue.qml component which is reusable (for convergence later)
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
FAILED: Continuous integration, rev:497
http://
Executed test runs:
UNSTABLE: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : | # |
FAILED: Continuous integration, rev:497
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Nicholas Skaggs (nskaggs) wrote : | # |
merge conflict again
1 conflicts encountered.
bzr: ERROR: Conflicts from merge
Andrew Hayzen (ahayzen) wrote : | # |
This mp [0] is likely to be preferred, marking this as WIP for now.
0 - https:/
Unmerged revisions
- 497. By Andrew Hayzen
-
* Merge of trunk
- 496. By Andrew Hayzen
-
* Merge of trunk
- 495. By Andrew Hayzen
-
* Add margin top to now playing
- 494. By Andrew Hayzen
-
* Only jump to current when toolbar is made visible, hide toolbar on scroll
* Tweaks to mouse controls on now playing page - 493. By Andrew Hayzen
-
* Tweaks of now playing for mobile
- 492. By Andrew Hayzen
-
* Split now playing and queue into separate components and pages
* Make a common/Queue.qml component which is reusable (for convergence later)
Preview Diff
1 | === modified file 'MusicNowPlaying.qml' |
2 | --- MusicNowPlaying.qml 2014-06-24 19:35:57 +0000 |
3 | +++ MusicNowPlaying.qml 2014-07-02 23:51:41 +0000 |
4 | @@ -40,636 +40,81 @@ |
5 | anchors.fill: parent |
6 | color: styleMusic.nowPlaying.backgroundColor |
7 | opacity: 0.75 // change later |
8 | - MouseArea { // Block events to lower layers |
9 | - anchors.fill: parent |
10 | - } |
11 | - } |
12 | - |
13 | - Component.onCompleted: { |
14 | - onToolbarShownChanged.connect(jumpToCurrent) |
15 | - } |
16 | - |
17 | - Connections { |
18 | - target: player |
19 | - onCurrentIndexChanged: { |
20 | - if (player.source === "") { |
21 | - return; |
22 | - } |
23 | - |
24 | - collapseExpand(); // Collapse expanded tracks |
25 | - queuelist.currentIndex = player.currentIndex; |
26 | - |
27 | - customdebug("MusicQueue update currentIndex: " + player.source); |
28 | - |
29 | - // Always jump to current track |
30 | - nowPlaying.jumpToCurrent(musicToolbar.opened, nowPlaying, musicToolbar.currentTab) |
31 | - |
32 | - } |
33 | - } |
34 | - |
35 | - function jumpToCurrent(shown, currentPage, currentTab) |
36 | - { |
37 | - // If the toolbar is shown, the page is now playing and snaptrack is enabled |
38 | - if (shown && currentPage === nowPlaying && Settings.getSetting("snaptrack") === "1") |
39 | - { |
40 | - // Then position the view at the current index |
41 | - queuelist.positionViewAtIndex(queuelist.currentIndex, ListView.Beginning); |
42 | - } |
43 | - } |
44 | - |
45 | - function positionAt(index) { |
46 | - queuelist.positionViewAtIndex(index, ListView.Beginning); |
47 | - queuelist.contentY -= header.height; |
48 | - } |
49 | - |
50 | - ListView { |
51 | - id: queuelist |
52 | - objectName: "queuelist" |
53 | - anchors.fill: parent |
54 | - anchors.bottomMargin: musicToolbar.mouseAreaOffset + musicToolbar.minimizedHeight |
55 | - spacing: units.gu(1) |
56 | - delegate: queueDelegate |
57 | - model: trackQueue.model |
58 | - highlightFollowsCurrentItem: false |
59 | - state: "normal" |
60 | - states: [ |
61 | - State { |
62 | - name: "normal" |
63 | - PropertyChanges { |
64 | - target: queuelist |
65 | - interactive: true |
66 | - } |
67 | - }, |
68 | - State { |
69 | - name: "reorder" |
70 | - PropertyChanges { |
71 | - target: queuelist |
72 | - interactive: false |
73 | - } |
74 | - } |
75 | - ] |
76 | - footer: Item { |
77 | - height: mainView.height - (styleMusic.common.expandHeight + queuelist.currentHeight) + units.gu(8) |
78 | - } |
79 | - |
80 | - property int normalHeight: units.gu(12) |
81 | - property int currentHeight: units.gu(48) |
82 | - property int transitionDuration: 250 // transition length of animations |
83 | - |
84 | - onCountChanged: { |
85 | - customdebug("Queue: Now has: " + queuelist.count + " tracks") |
86 | - } |
87 | - |
88 | - onMovementStarted: { |
89 | - musicToolbar.hideToolbar(); |
90 | - } |
91 | - |
92 | - Component { |
93 | - id: queueDelegate |
94 | - ListItem.Standard { |
95 | - id: queueListItem |
96 | - height: queuelist.normalHeight |
97 | - state: queuelist.currentIndex == index ? "current" : "" |
98 | - |
99 | - // cached height used to restore the height after expansion |
100 | - property int cachedHeight: -1 |
101 | - |
102 | - SwipeDelete { |
103 | - id: swipeBackground |
104 | - duration: queuelist.transitionDuration |
105 | - |
106 | - onDeleteStateChanged: { |
107 | - if (deleteState === true) |
108 | - { |
109 | - queueListItemRemoveAnimation.start(); |
110 | - } |
111 | - } |
112 | - } |
113 | - |
114 | - function onCollapseSwipeDelete(indexCol) |
115 | - { |
116 | - if ((indexCol !== index || indexCol === -1) && swipeBackground !== undefined && swipeBackground.direction !== "") |
117 | - { |
118 | - customdebug("auto collapse swipeDelete") |
119 | - queueListItemResetStartAnimation.start(); |
120 | - } |
121 | - } |
122 | - |
123 | - Component.onCompleted: { |
124 | - collapseSwipeDelete.connect(onCollapseSwipeDelete); |
125 | - } |
126 | - |
127 | - MouseArea { |
128 | - id: queueArea |
129 | - anchors.fill: parent |
130 | - |
131 | - property int startX: queueListItem.x |
132 | - property int startY: queueListItem.y |
133 | - property int startMouseY: -1 |
134 | - |
135 | - // Allow dragging on the X axis for swipeDelete if not reordering |
136 | - drag.target: queueListItem |
137 | - drag.axis: Drag.XAxis |
138 | - drag.minimumX: queuelist.state == "reorder" ? 0 : -queueListItem.width |
139 | - drag.maximumX: queuelist.state == "reorder" ? 0 : queueListItem.width |
140 | - |
141 | - /* Get the mouse and item difference from the starting positions */ |
142 | - function getDiff(mouseY) |
143 | - { |
144 | - return (mouseY - startMouseY) + (queueListItem.y - startY); |
145 | - } |
146 | - |
147 | - /* |
148 | - * Has the mouse crossed the current item |
149 | - * True - it has crossed |
150 | - * NULL - it is on the current |
151 | - * False - it has not crossed |
152 | - */ |
153 | - function hasCrossedCurrent(diff, currentOffset) |
154 | - { |
155 | - // Only crossed if in same direction |
156 | - if ((diff > 0 || currentOffset > 0) && (diff <= 0 || currentOffset <= 0)) |
157 | - { |
158 | - return false; |
159 | - } |
160 | - |
161 | - if (Math.abs(diff) > (Math.abs(currentOffset) * queuelist.normalHeight) + queuelist.currentHeight) |
162 | - { |
163 | - return true; |
164 | - } |
165 | - else if (Math.abs(diff) > (Math.abs(currentOffset) * queuelist.normalHeight)) |
166 | - { |
167 | - return null; |
168 | - } |
169 | - else |
170 | - { |
171 | - return false; |
172 | - } |
173 | - } |
174 | - |
175 | - function getNewIndex(mouseY, index) |
176 | - { |
177 | - var diff = getDiff(mouseY); |
178 | - var negPos = diff < 0 ? -1 : 1; |
179 | - var currentOffset = queuelist.currentIndex - index; // get the current offset |
180 | - |
181 | - if (currentOffset < 0) // when current is less the offset is actually +1 |
182 | - { |
183 | - currentOffset += 1; |
184 | - } |
185 | - |
186 | - var hasCrossed = hasCrossedCurrent(diff, currentOffset); |
187 | - |
188 | - if (hasCrossed === true) |
189 | - { |
190 | - /* Take off difference so it just appears like a normalheight |
191 | - * minus when after and add when before */ |
192 | - diff -= negPos * (queuelist.currentHeight - queuelist.normalHeight); |
193 | - } |
194 | - else if (hasCrossed === null) |
195 | - { |
196 | - // Work out how far into the current item it is |
197 | - var tmpDiff = Math.abs(diff) - (Math.abs(currentOffset) * queuelist.normalHeight); |
198 | - |
199 | - // Scale difference so is the same as a normalHeight |
200 | - tmpDiff *= (queuelist.normalHeight / queuelist.currentHeight); |
201 | - |
202 | - // rebuild Diff with new values |
203 | - diff = (currentOffset * queuelist.normalHeight) + (negPos * tmpDiff); |
204 | - } |
205 | - |
206 | - return index + (Math.round(diff / queuelist.normalHeight)); |
207 | - } |
208 | - |
209 | - onClicked: { |
210 | - collapseSwipeDelete(-1); // collapse all expands |
211 | - customdebug("File: " + model.filename) // debugger |
212 | - trackQueueClick(index); // toggle track state |
213 | - } |
214 | - |
215 | - onMouseXChanged: { |
216 | - // Only allow XChange if not in reorder state |
217 | - if (queuelist.state == "reorder") |
218 | - { |
219 | - return; |
220 | - } |
221 | - |
222 | - // New X is less than start so swiping left |
223 | - if (queueListItem.x < startX) |
224 | - { |
225 | - collapseExpand(); |
226 | - swipeBackground.state = "swipingLeft"; |
227 | - startY = queueListItem.y; |
228 | - } |
229 | - // New X is greater sow swiping right |
230 | - else if (queueListItem.x > startX) |
231 | - { |
232 | - collapseExpand(); |
233 | - swipeBackground.state = "swipingRight"; |
234 | - startY = queueListItem.y; |
235 | - } |
236 | - // Same so reset state back to normal |
237 | - else |
238 | - { |
239 | - swipeBackground.state = "normal"; |
240 | - queuelist.state = "normal"; |
241 | - } |
242 | - } |
243 | - |
244 | - onMouseYChanged: { |
245 | - // Y change only affects when in reorder mode |
246 | - if (queuelist.state == "reorder") |
247 | - { |
248 | - /* update the listitem y position so that the |
249 | - * listitem horizontalCenter is under the mouse.y */ |
250 | - queueListItem.y += mouse.y - (queueListItem.height / 2); |
251 | - } |
252 | - } |
253 | - |
254 | - onPressed: { |
255 | - startX = queueListItem.x; |
256 | - startY = queueListItem.y; |
257 | - startMouseY = mouse.y; |
258 | - } |
259 | - |
260 | - onPressAndHold: { |
261 | - // Must be in a normal state to change to reorder state |
262 | - if (queuelist.state == "normal" && swipeBackground.state == "normal" && queuelist.currentIndex != index) |
263 | - { |
264 | - collapseSwipeDelete(-1); // collapse all swipedeletes |
265 | - collapseExpand(); // collapse all |
266 | - customdebug("Pressed and held queued track "+model.filename) |
267 | - queuelist.state = "reorder"; // enable reordering state |
268 | - trackContainerReorderAnimation.start(); |
269 | - } |
270 | - } |
271 | - |
272 | - onReleased: { |
273 | - // Get current state to determine what to do |
274 | - if (queuelist.state == "reorder") |
275 | - { |
276 | - var newIndex = getNewIndex(mouse.y + (queueListItem.height / 2), index); // get new index |
277 | - |
278 | - // Indexes larger than current need -1 because when it is moved the current is removed |
279 | - if (newIndex > index) |
280 | - { |
281 | - newIndex -= 1; |
282 | - } |
283 | - |
284 | - if (newIndex === index) |
285 | - { |
286 | - queueListItemResetAnimation.start(); // reset item position |
287 | - trackContainerResetAnimation.start(); // reset the trackContainer |
288 | - } |
289 | - else |
290 | - { |
291 | - queueListItem.x = startX; // ensure X position is correct |
292 | - trackContainerResetAnimation.start(); // reset the trackContainer |
293 | - |
294 | - // Check that the newIndex is within the range |
295 | - if (newIndex < 0) |
296 | - { |
297 | - newIndex = 0; |
298 | - } |
299 | - else if (newIndex > queuelist.count - 1) |
300 | - { |
301 | - newIndex = queuelist.count - 1; |
302 | - } |
303 | - |
304 | - console.debug("Move: " + index + " To: " + newIndex); |
305 | - queuelist.model.move(index, newIndex, 1); // update the model |
306 | - } |
307 | - } |
308 | - else if (swipeBackground.state == "swipingLeft" || swipeBackground.state == "swipingRight") |
309 | - { |
310 | - var moved = Math.abs(queueListItem.x - startX); |
311 | - |
312 | - // Make sure that item has been dragged far enough |
313 | - if (moved > queueListItem.width / 2 || (swipeBackground.primed === true && moved > units.gu(5))) |
314 | - { |
315 | - if (swipeBackground.primed === false) |
316 | - { |
317 | - collapseSwipeDelete(index); // collapse other swipeDeletes |
318 | - |
319 | - // Move the listitem half way across to reveal the delete button |
320 | - queueListItemPrepareRemoveAnimation.start(); |
321 | - } |
322 | - else |
323 | - { |
324 | - // Check that actually swiping to cancel |
325 | - if (swipeBackground.direction !== "" && |
326 | - swipeBackground.direction !== swipeBackground.state) |
327 | - { |
328 | - // Reset the listitem to the centre |
329 | - queueListItemResetStartAnimation.start(); |
330 | - } |
331 | - else |
332 | - { |
333 | - // Reset the listitem to the centre |
334 | - queueListItemResetAnimation.start(); |
335 | - } |
336 | - } |
337 | - } |
338 | - else |
339 | - { |
340 | - // Reset the listitem to the centre |
341 | - queueListItemResetAnimation.start(); |
342 | - } |
343 | - } |
344 | - |
345 | - // ensure states are normal |
346 | - swipeBackground.state = "normal"; |
347 | - queuelist.state = "normal"; |
348 | - } |
349 | - |
350 | - // Animation to reset the x, y of the queueitem |
351 | - ParallelAnimation { |
352 | - id: queueListItemResetAnimation |
353 | - running: false |
354 | - NumberAnimation { // reset X |
355 | - target: queueListItem |
356 | - property: "x" |
357 | - to: queueArea.startX |
358 | - duration: queuelist.transitionDuration |
359 | - } |
360 | - NumberAnimation { // reset Y |
361 | - target: queueListItem |
362 | - property: "y" |
363 | - to: queueArea.startY |
364 | - duration: queuelist.transitionDuration |
365 | - } |
366 | - } |
367 | - |
368 | - // Animation to reset the x, y of the item |
369 | - ParallelAnimation { |
370 | - id: queueListItemResetStartAnimation |
371 | - running: false |
372 | - NumberAnimation { // reset X |
373 | - target: queueListItem |
374 | - property: "x" |
375 | - to: 0 |
376 | - duration: queuelist.transitionDuration |
377 | - } |
378 | - NumberAnimation { // reset Y |
379 | - target: queueListItem |
380 | - property: "y" |
381 | - to: queueArea.startY |
382 | - duration: queuelist.transitionDuration |
383 | - } |
384 | - onRunningChanged: { |
385 | - if (running === true) |
386 | - { |
387 | - swipeBackground.direction = ""; |
388 | - swipeBackground.primed = false; |
389 | - } |
390 | - } |
391 | - } |
392 | - |
393 | - // Move the listitem half way across to reveal the delete button |
394 | - NumberAnimation { |
395 | - id: queueListItemPrepareRemoveAnimation |
396 | - target: queueListItem |
397 | - property: "x" |
398 | - to: swipeBackground.state == "swipingRight" ? queueListItem.width / 2 : 0 - (queueListItem.width / 2) |
399 | - duration: queuelist.transitionDuration |
400 | - onRunningChanged: { |
401 | - if (running === true) |
402 | - { |
403 | - swipeBackground.direction = swipeBackground.state; |
404 | - swipeBackground.primed = true; |
405 | - } |
406 | - } |
407 | - } |
408 | - |
409 | - ParallelAnimation { |
410 | - id: queueListItemRemoveAnimation |
411 | - running: false |
412 | - NumberAnimation { // 'slide' up |
413 | - target: queueListItem |
414 | - property: "height" |
415 | - to: 0 |
416 | - duration: queuelist.transitionDuration |
417 | - } |
418 | - NumberAnimation { // 'slide' in direction of removal |
419 | - target: queueListItem |
420 | - property: "x" |
421 | - to: swipeBackground.direction === "swipingLeft" ? 0 - queueListItem.width : queueListItem.width |
422 | - duration: queuelist.transitionDuration |
423 | - } |
424 | - onRunningChanged: { |
425 | - if (running === false) |
426 | - { |
427 | - // Remove the item |
428 | - if (index == queuelist.currentIndex) |
429 | - { |
430 | - if (queuelist.count > 1) |
431 | - { |
432 | - // Next song and only play if currently playing |
433 | - player.nextSong(player.isPlaying); |
434 | - } |
435 | - else |
436 | - { |
437 | - player.stop(); |
438 | - } |
439 | - } |
440 | - |
441 | - if (index < player.currentIndex) { |
442 | - player.currentIndex -= 1; |
443 | - } |
444 | - |
445 | - // Remove item from queue and clear caches |
446 | - trackQueue.model.remove(index); |
447 | - } |
448 | - } |
449 | - } |
450 | - } |
451 | - |
452 | - onFocusChanged: { |
453 | - if (focus == false) { |
454 | - selected = false |
455 | - } else { |
456 | - selected = false |
457 | - } |
458 | - } |
459 | - |
460 | - Rectangle { |
461 | - id: trackContainer; |
462 | - anchors { |
463 | - fill: parent |
464 | - margins: units.gu(0.5) |
465 | - rightMargin: expandable.expanderButtonWidth |
466 | - } |
467 | - color: "transparent" |
468 | - |
469 | - NumberAnimation { |
470 | - id: trackContainerReorderAnimation |
471 | - target: trackContainer; |
472 | - property: "anchors.leftMargin"; |
473 | - duration: queuelist.transitionDuration; |
474 | - to: units.gu(2) |
475 | - } |
476 | - |
477 | - NumberAnimation { |
478 | - id: trackContainerResetAnimation |
479 | - target: trackContainer; |
480 | - property: "anchors.leftMargin"; |
481 | - duration: queuelist.transitionDuration; |
482 | - to: units.gu(0.5) |
483 | - } |
484 | - |
485 | - UbuntuShape { |
486 | - id: trackImage |
487 | - anchors.left: parent.left |
488 | - anchors.leftMargin: units.gu(1.5) |
489 | - anchors.top: parent.top |
490 | - height: (queueListItem.state === "current" ? queuelist.currentHeight - units.gu(8) : queuelist.normalHeight) - units.gu(2) |
491 | - width: height |
492 | - image: Image { |
493 | - source: "image://albumart/artist=" + model.author + "&album=" + model.album |
494 | - onStatusChanged: { |
495 | - if (status === Image.Error) { |
496 | - source = Qt.resolvedUrl("images/music-app-cover@30.png") |
497 | - } |
498 | - } |
499 | - } |
500 | - |
501 | - function calcAnchors() { |
502 | - if (trackImage.height > queuelist.normalHeight && mainView.wideAspect) { |
503 | - trackImage.anchors.left = undefined |
504 | - trackImage.anchors.horizontalCenter = trackImage.parent.horizontalCenter |
505 | - } else { |
506 | - trackImage.anchors.left = trackImage.parent.left |
507 | - trackImage.anchors.horizontalCenter = undefined |
508 | - } |
509 | - |
510 | - trackImage.width = trackImage.height; // force width to match height |
511 | - } |
512 | - |
513 | - Connections { |
514 | - target: mainView |
515 | - onWideAspectChanged: trackImage.calcAnchors() |
516 | - } |
517 | - |
518 | - onHeightChanged: { |
519 | - calcAnchors() |
520 | - } |
521 | - Behavior on height { |
522 | - NumberAnimation { |
523 | - target: trackImage; |
524 | - property: "height"; |
525 | - duration: queuelist.transitionDuration; |
526 | - } |
527 | - } |
528 | - } |
529 | - Label { |
530 | - id: nowPlayingArtist |
531 | - objectName: "nowplayingartist" |
532 | - color: styleMusic.nowPlaying.labelSecondaryColor |
533 | - elide: Text.ElideRight |
534 | - height: units.gu(1) |
535 | - text: model.author |
536 | - fontSize: 'small' |
537 | - width: parent.width - trackImage.width - units.gu(3.5) |
538 | - x: trackImage.x + trackImage.width + units.gu(1) |
539 | - y: trackImage.y + units.gu(1) |
540 | - } |
541 | - Label { |
542 | - id: nowPlayingTitle |
543 | - objectName: "nowplayingtitle" |
544 | - color: styleMusic.common.white |
545 | - elide: Text.ElideRight |
546 | - height: units.gu(1) |
547 | - text: model.title |
548 | - fontSize: 'medium' |
549 | - width: parent.width - trackImage.width - units.gu(3.5) |
550 | - x: trackImage.x + trackImage.width + units.gu(1) |
551 | - y: nowPlayingArtist.y + nowPlayingArtist.height + units.gu(1.25) |
552 | - } |
553 | - Label { |
554 | - id: nowPlayingAlbum |
555 | - objectName: "nowplayingalbum" |
556 | - color: styleMusic.nowPlaying.labelSecondaryColor |
557 | - elide: Text.ElideRight |
558 | - height: units.gu(1) |
559 | - text: model.album |
560 | - fontSize: 'x-small' |
561 | - width: parent.width - trackImage.width - units.gu(3.5) |
562 | - x: trackImage.x + trackImage.width + units.gu(1) |
563 | - y: nowPlayingTitle.y + nowPlayingTitle.height + units.gu(1.25) |
564 | - } |
565 | - } |
566 | - |
567 | - Expander { |
568 | - id: expandable |
569 | - anchors { |
570 | - fill: parent |
571 | - } |
572 | - actualListItemHeight: queueListItem.state === "current" ? |
573 | - queuelist.currentHeight : |
574 | - queuelist.normalHeight |
575 | - buttonEnabled: !swipeBackground.primed |
576 | - expanderButtonCentreFromBottom: queuelist.normalHeight - (trackContainer.anchors.margins * 2) - nowPlayingTitle.y - (nowPlayingTitle.height / 2) |
577 | - listItem: queueListItem |
578 | - model: trackQueue.model.get(index) |
579 | - row: Row { |
580 | - AddToPlaylist { |
581 | - } |
582 | - } |
583 | - Behavior on actualListItemHeight { |
584 | - NumberAnimation { |
585 | - target: expandable; |
586 | - property: "actualListItemHeight"; |
587 | - duration: queuelist.transitionDuration; |
588 | - } |
589 | - } |
590 | - } |
591 | - |
592 | - states: State { |
593 | - name: "current" |
594 | - PropertyChanges { |
595 | - target: queueListItem |
596 | - height: queuelist.currentHeight |
597 | - } |
598 | - PropertyChanges { |
599 | - target: nowPlayingArtist |
600 | - width: trackImage.width |
601 | - x: trackImage.x |
602 | - y: trackImage.y + trackImage.height + units.gu(0.5) |
603 | - } |
604 | - PropertyChanges { |
605 | - target: nowPlayingTitle |
606 | - width: trackImage.width |
607 | - x: trackImage.x |
608 | - y: nowPlayingArtist.y + nowPlayingArtist.height + units.gu(1.25) |
609 | - } |
610 | - PropertyChanges { |
611 | - target: nowPlayingAlbum |
612 | - width: trackImage.width |
613 | - x: trackImage.x |
614 | - y: nowPlayingTitle.y + nowPlayingTitle.height + units.gu(1.25) |
615 | - } |
616 | - PropertyChanges { |
617 | - target: expandable |
618 | - expanderButtonCentreFromBottom: queuelist.currentHeight - (trackContainer.anchors.margins * 2) - nowPlayingTitle.y - (nowPlayingTitle.height / 2) |
619 | - } |
620 | - } |
621 | - transitions: Transition { |
622 | - from: ",current" |
623 | - to: "current," |
624 | - NumberAnimation { |
625 | - duration: queuelist.transitionDuration |
626 | - properties: "height,opacity,width,x,y" |
627 | - } |
628 | - |
629 | - onRunningChanged: { |
630 | - if (running === false && ensureVisibleIndex != -1) |
631 | - { |
632 | - queuelist.positionViewAtIndex(ensureVisibleIndex, ListView.Beginning); |
633 | - ensureVisibleIndex = -1; |
634 | - } |
635 | - } |
636 | - } |
637 | - } |
638 | + } |
639 | + |
640 | + Flickable { |
641 | + anchors { |
642 | + fill: parent |
643 | + } |
644 | + |
645 | + Column { |
646 | + id: trackContainer; |
647 | + anchors { |
648 | + horizontalCenter: parent.horizontalCenter |
649 | + } |
650 | + spacing: units.gu(0.5) |
651 | + |
652 | + // Top margin |
653 | + Rectangle { |
654 | + color: "transparent" |
655 | + height: units.gu(1) |
656 | + width: parent.width |
657 | + } |
658 | + |
659 | + UbuntuShape { |
660 | + id: trackImage |
661 | + height: units.gu(38) |
662 | + width: height |
663 | + image: Image { |
664 | + source: "image://albumart/artist=" + player.currentMetaArtist + "&album=" + player.currentMetaAlbum |
665 | + onStatusChanged: { |
666 | + if (status === Image.Error) { |
667 | + source = Qt.resolvedUrl("images/music-app-cover@30.png") |
668 | + } |
669 | + } |
670 | + } |
671 | + } |
672 | + Label { |
673 | + id: nowPlayingArtist |
674 | + objectName: "nowplayingartist" |
675 | + color: styleMusic.nowPlaying.labelSecondaryColor |
676 | + elide: Text.ElideRight |
677 | + text: player.currentMetaArtist |
678 | + fontSize: 'small' |
679 | + width: parent.width |
680 | + } |
681 | + Label { |
682 | + id: nowPlayingTitle |
683 | + objectName: "nowplayingtitle" |
684 | + color: styleMusic.common.white |
685 | + elide: Text.ElideRight |
686 | + text: player.currentMetaTitle |
687 | + fontSize: 'medium' |
688 | + width: parent.width |
689 | + } |
690 | + Label { |
691 | + id: nowPlayingAlbum |
692 | + objectName: "nowplayingalbum" |
693 | + color: styleMusic.nowPlaying.labelSecondaryColor |
694 | + elide: Text.ElideRight |
695 | + text: player.currentMetaAlbum |
696 | + fontSize: 'x-small' |
697 | + width: parent.width |
698 | + } |
699 | + } |
700 | + |
701 | + MouseArea { |
702 | + anchors { |
703 | + fill: parent |
704 | + } |
705 | + onClicked: musicToolbar.goBack() // Go back if background is clicked |
706 | + } |
707 | + |
708 | + MouseArea { |
709 | + anchors { |
710 | + fill: trackContainer |
711 | + } |
712 | + onClicked: player.toggle() |
713 | } |
714 | } |
715 | } |
716 | |
717 | === added file 'MusicQueue.qml' |
718 | --- MusicQueue.qml 1970-01-01 00:00:00 +0000 |
719 | +++ MusicQueue.qml 2014-07-02 23:51:41 +0000 |
720 | @@ -0,0 +1,60 @@ |
721 | +/* |
722 | + * Copyright (C) 2014 Andrew Hayzen <ahayzen@gmail.com> |
723 | + * Daniel Holm <d.holmen@gmail.com> |
724 | + * Victor Thompson <victor.thompson@gmail.com> |
725 | + * |
726 | + * This program is free software; you can redistribute it and/or modify |
727 | + * it under the terms of the GNU General Public License as published by |
728 | + * the Free Software Foundation; version 3. |
729 | + * |
730 | + * This program is distributed in the hope that it will be useful, |
731 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
732 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
733 | + * GNU General Public License for more details. |
734 | + * |
735 | + * You should have received a copy of the GNU General Public License |
736 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
737 | + */ |
738 | + |
739 | + |
740 | +import QtMultimedia 5.0 |
741 | +import QtQuick 2.0 |
742 | +import Ubuntu.Components 0.1 |
743 | +import "common" |
744 | + |
745 | +MusicPage { |
746 | + id: queuePage |
747 | + title: i18n.tr("Queue") |
748 | + visible: false |
749 | + |
750 | + property alias ensureVisibleIndex: queueList.ensureVisibleIndex |
751 | + |
752 | + Rectangle { |
753 | + anchors.fill: parent |
754 | + color: styleMusic.nowPlaying.backgroundColor |
755 | + opacity: 0.75 // change later |
756 | + MouseArea { // Block events to lower layers |
757 | + anchors.fill: parent |
758 | + } |
759 | + } |
760 | + |
761 | + Queue { |
762 | + id: queueList |
763 | + anchors { |
764 | + bottomMargin: musicToolbar.currentHeight |
765 | + fill: parent |
766 | + } |
767 | + clip: false |
768 | + onMovementStarted: musicToolbar.hideToolbar() |
769 | + } |
770 | + |
771 | + Connections { |
772 | + target: musicToolbar |
773 | + onOpenedChanged: { |
774 | + if (musicToolbar.opened) { |
775 | + queueList.ensureVisibleIndex = -1; |
776 | + queueList.ensureVisibleIndex = player.currentIndex; |
777 | + } |
778 | + } |
779 | + } |
780 | +} |
781 | |
782 | === modified file 'MusicToolbar.qml' |
783 | --- MusicToolbar.qml 2014-06-26 17:48:54 +0000 |
784 | +++ MusicToolbar.qml 2014-07-02 23:51:41 +0000 |
785 | @@ -42,7 +42,6 @@ |
786 | property bool shown: false |
787 | property int transitionDuration: 100 |
788 | |
789 | - property alias currentHeight: musicToolbarPanel.height |
790 | property alias minimizedHeight: musicToolbarPanel.minimizedHeight |
791 | property alias expandedHeight: musicToolbarPanel.expandedHeight |
792 | property alias fullHeight: musicToolbarPanel.fullHeight |
793 | @@ -51,19 +50,23 @@ |
794 | property alias animating: musicToolbarPanel.animating |
795 | property alias opened: musicToolbarPanel.opened |
796 | |
797 | + property int currentHeight: opened ? musicToolbarPanel.height : minimizedHeight |
798 | + |
799 | Connections { |
800 | id: pageStackConn |
801 | target: mainPageStack |
802 | |
803 | onCurrentPageChanged: { |
804 | - previousPage = currentPage; |
805 | - |
806 | - // If going back from nowPlaying jump back to tabs |
807 | - if (previousPage === nowPlaying && mainPageStack.currentPage !== nowPlaying) { |
808 | + // If going back from nowplaying->queue->x |
809 | + // jump back to tabs instead of x |
810 | + if (previousPage === nowPlaying && currentPage === musicQueue && |
811 | + mainPageStack.currentPage !== nowPlaying) { |
812 | while (mainPageStack.depth > 1) { |
813 | mainPageStack.pop(mainPageStack.currentPage) |
814 | } |
815 | } |
816 | + |
817 | + previousPage = currentPage; |
818 | } |
819 | } |
820 | |
821 | @@ -96,7 +99,7 @@ |
822 | mainPageStack.pop(currentPage) |
823 | } |
824 | |
825 | - hideToolbar(); |
826 | + showToolbar(); |
827 | } |
828 | |
829 | // Hide the toolbar |
830 | @@ -171,7 +174,7 @@ |
831 | __closeOnContentsClicks: false // TODO: fix bug 1295720 |
832 | |
833 | // The current mode of the controls |
834 | - property string currentMode: wideAspect || (currentPage === nowPlaying) |
835 | + property string currentMode: wideAspect || (currentPage === nowPlaying || currentPage === musicQueue) |
836 | ? "full" : "expanded" |
837 | |
838 | // Properties for the different heights |
839 | |
840 | === added file 'common/Queue.qml' |
841 | --- common/Queue.qml 1970-01-01 00:00:00 +0000 |
842 | +++ common/Queue.qml 2014-07-02 23:51:41 +0000 |
843 | @@ -0,0 +1,510 @@ |
844 | +/* |
845 | + * Copyright (C) 2014 Andrew Hayzen <ahayzen@gmail.com> |
846 | + * Daniel Holm <d.holmen@gmail.com> |
847 | + * Victor Thompson <victor.thompson@gmail.com> |
848 | + * |
849 | + * This program is free software; you can redistribute it and/or modify |
850 | + * it under the terms of the GNU General Public License as published by |
851 | + * the Free Software Foundation; version 3. |
852 | + * |
853 | + * This program is distributed in the hope that it will be useful, |
854 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
855 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
856 | + * GNU General Public License for more details. |
857 | + * |
858 | + * You should have received a copy of the GNU General Public License |
859 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
860 | + */ |
861 | + |
862 | +import QtQuick 2.0 |
863 | +import Ubuntu.Components 0.1 |
864 | +import Ubuntu.Components.ListItems 0.1 as ListItem |
865 | +import "ExpanderItems" |
866 | + |
867 | +ListView { |
868 | + id: queueListView |
869 | + anchors { |
870 | + fill: parent |
871 | + } |
872 | + clip: true |
873 | + model: trackQueue.model |
874 | + state: "normal" |
875 | + states: [ |
876 | + State { |
877 | + name: "normal" |
878 | + PropertyChanges { |
879 | + target: queueListView |
880 | + interactive: true |
881 | + } |
882 | + }, |
883 | + State { |
884 | + name: "reorder" |
885 | + PropertyChanges { |
886 | + target: queueListView |
887 | + interactive: false |
888 | + } |
889 | + } |
890 | + ] |
891 | + |
892 | + property int ensureVisibleIndex: 0 // ensure first index is visible at startup |
893 | + property int normalHeight: units.gu(12) |
894 | + property int transitionDuration: 250 // transition length of animations |
895 | + |
896 | + onEnsureVisibleIndexChanged: { |
897 | + if (ensureVisibleIndex === -1) { |
898 | + return |
899 | + } |
900 | + |
901 | + queueListView.positionViewAtIndex(ensureVisibleIndex, ListView.Contain); |
902 | + } |
903 | + |
904 | + delegate: ListItem.Standard { |
905 | + id: queueListItem |
906 | + height: queueListView.normalHeight |
907 | + |
908 | + // cached height used to restore the height after expansion |
909 | + property int cachedHeight: -1 |
910 | + |
911 | + SwipeDelete { |
912 | + id: swipeBackground |
913 | + duration: queueListView.transitionDuration |
914 | + |
915 | + onDeleteStateChanged: { |
916 | + if (deleteState === true) { |
917 | + queueListItemRemoveAnimation.start(); |
918 | + } |
919 | + } |
920 | + } |
921 | + |
922 | + function onCollapseSwipeDelete(indexCol) |
923 | + { |
924 | + if ((indexCol !== index || indexCol === -1) && swipeBackground !== undefined && swipeBackground.direction !== "") { |
925 | + customdebug("auto collapse swipeDelete") |
926 | + queueListItemResetStartAnimation.start(); |
927 | + } |
928 | + } |
929 | + |
930 | + Component.onCompleted: { |
931 | + collapseSwipeDelete.connect(onCollapseSwipeDelete); |
932 | + } |
933 | + |
934 | + MouseArea { |
935 | + id: queueArea |
936 | + anchors.fill: parent |
937 | + |
938 | + property int startX: queueListItem.x |
939 | + property int startY: queueListItem.y |
940 | + property int startMouseY: -1 |
941 | + |
942 | + // Allow dragging on the X axis for swipeDelete if not reordering |
943 | + drag.target: queueListItem |
944 | + drag.axis: Drag.XAxis |
945 | + drag.minimumX: queueListView.state === "reorder" ? 0 : -queueListItem.width |
946 | + drag.maximumX: queueListView.state === "reorder" ? 0 : queueListItem.width |
947 | + |
948 | + /* Get the mouse and item difference from the starting positions */ |
949 | + function getDiff(mouseY) |
950 | + { |
951 | + return (mouseY - startMouseY) + (queueListItem.y - startY); |
952 | + } |
953 | + |
954 | + function getNewIndex(mouseY, index) |
955 | + { |
956 | + var diff = getDiff(mouseY); |
957 | + var negPos = diff < 0 ? -1 : 1; |
958 | + var currentOffset = queueListView.currentIndex - index; // get the current offset |
959 | + |
960 | + if (currentOffset < 0) // when current is less the offset is actually +1 |
961 | + { |
962 | + currentOffset += 1; |
963 | + } |
964 | + |
965 | + return index + (Math.round(diff / queueListView.normalHeight)); |
966 | + } |
967 | + |
968 | + onClicked: { |
969 | + collapseSwipeDelete(-1); // collapse all expands |
970 | + customdebug("File: " + model.filename) // debugger |
971 | + trackQueueClick(index) |
972 | + } |
973 | + |
974 | + onMouseXChanged: { |
975 | + // Only allow XChange if not in reorder state |
976 | + if (queueListView.state === "reorder") |
977 | + { |
978 | + return; |
979 | + } |
980 | + |
981 | + // New X is less than start so swiping left |
982 | + if (queueListItem.x < startX) |
983 | + { |
984 | + collapseExpand(); |
985 | + swipeBackground.state = "swipingLeft"; |
986 | + startY = queueListItem.y; |
987 | + } |
988 | + // New X is greater sow swiping right |
989 | + else if (queueListItem.x > startX) |
990 | + { |
991 | + collapseExpand(); |
992 | + swipeBackground.state = "swipingRight"; |
993 | + startY = queueListItem.y; |
994 | + } |
995 | + // Same so reset state back to normal |
996 | + else |
997 | + { |
998 | + swipeBackground.state = "normal"; |
999 | + queueListView.state = "normal"; |
1000 | + } |
1001 | + } |
1002 | + |
1003 | + onMouseYChanged: { |
1004 | + // Y change only affects when in reorder mode |
1005 | + if (queueListView.state === "reorder") |
1006 | + { |
1007 | + /* update the listitem y position so that the |
1008 | + * listitem horizontalCenter is under the mouse.y */ |
1009 | + queueListItem.y += mouse.y - (queueListItem.height / 2); |
1010 | + } |
1011 | + } |
1012 | + |
1013 | + onPressed: { |
1014 | + startX = queueListItem.x; |
1015 | + startY = queueListItem.y; |
1016 | + startMouseY = mouse.y; |
1017 | + } |
1018 | + |
1019 | + onPressAndHold: { |
1020 | + // Must be in a normal state to change to reorder state |
1021 | + if (queueListView.state === "normal" && swipeBackground.state == "normal" && queueListView.currentIndex !== index) |
1022 | + { |
1023 | + collapseSwipeDelete(-1); // collapse all swipedeletes |
1024 | + collapseExpand(); // collapse all |
1025 | + customdebug("Pressed and held queued track "+model.filename) |
1026 | + queueListView.state = "reorder"; // enable reordering state |
1027 | + trackContainerReorderAnimation.start(); |
1028 | + } |
1029 | + } |
1030 | + |
1031 | + onReleased: { |
1032 | + // Get current state to determine what to do |
1033 | + if (queueListView.state === "reorder") |
1034 | + { |
1035 | + var newIndex = getNewIndex(mouse.y + (queueListItem.height / 2), index); // get new index |
1036 | + |
1037 | + // Indexes larger than current need -1 because when it is moved the current is removed |
1038 | + if (newIndex > index) |
1039 | + { |
1040 | + newIndex -= 1; |
1041 | + } |
1042 | + |
1043 | + if (newIndex === index) |
1044 | + { |
1045 | + queueListItemResetAnimation.start(); // reset item position |
1046 | + trackContainerResetAnimation.start(); // reset the trackContainer |
1047 | + } |
1048 | + else |
1049 | + { |
1050 | + queueListItem.x = startX; // ensure X position is correct |
1051 | + trackContainerResetAnimation.start(); // reset the trackContainer |
1052 | + |
1053 | + // Check that the newIndex is within the range |
1054 | + if (newIndex < 0) |
1055 | + { |
1056 | + newIndex = 0; |
1057 | + } |
1058 | + else if (newIndex > queueListView.count - 1) |
1059 | + { |
1060 | + newIndex = queueListView.count - 1; |
1061 | + } |
1062 | + |
1063 | + console.debug("Move: " + index + " To: " + newIndex); |
1064 | + queueListView.model.move(index, newIndex, 1); // update the model |
1065 | + } |
1066 | + } |
1067 | + else if (swipeBackground.state == "swipingLeft" || swipeBackground.state == "swipingRight") |
1068 | + { |
1069 | + var moved = Math.abs(queueListItem.x - startX); |
1070 | + |
1071 | + // Make sure that item has been dragged far enough |
1072 | + if (moved > queueListItem.width / 2 || (swipeBackground.primed === true && moved > units.gu(5))) |
1073 | + { |
1074 | + if (swipeBackground.primed === false) |
1075 | + { |
1076 | + collapseSwipeDelete(index); // collapse other swipeDeletes |
1077 | + |
1078 | + // Move the listitem half way across to reveal the delete button |
1079 | + queueListItemPrepareRemoveAnimation.start(); |
1080 | + } |
1081 | + else |
1082 | + { |
1083 | + // Check that actually swiping to cancel |
1084 | + if (swipeBackground.direction !== "" && |
1085 | + swipeBackground.direction !== swipeBackground.state) |
1086 | + { |
1087 | + // Reset the listitem to the centre |
1088 | + queueListItemResetStartAnimation.start(); |
1089 | + } |
1090 | + else |
1091 | + { |
1092 | + // Reset the listitem to the centre |
1093 | + queueListItemResetAnimation.start(); |
1094 | + } |
1095 | + } |
1096 | + } |
1097 | + else |
1098 | + { |
1099 | + // Reset the listitem to the centre |
1100 | + queueListItemResetAnimation.start(); |
1101 | + } |
1102 | + } |
1103 | + |
1104 | + // ensure states are normal |
1105 | + swipeBackground.state = "normal"; |
1106 | + queueListView.state = "normal"; |
1107 | + } |
1108 | + |
1109 | + // Animation to reset the x, y of the queueitem |
1110 | + ParallelAnimation { |
1111 | + id: queueListItemResetAnimation |
1112 | + running: false |
1113 | + NumberAnimation { // reset X |
1114 | + target: queueListItem |
1115 | + property: "x" |
1116 | + to: queueArea.startX |
1117 | + duration: queueListView.transitionDuration |
1118 | + } |
1119 | + NumberAnimation { // reset Y |
1120 | + target: queueListItem |
1121 | + property: "y" |
1122 | + to: queueArea.startY |
1123 | + duration: queueListView.transitionDuration |
1124 | + } |
1125 | + } |
1126 | + |
1127 | + // Animation to reset the x, y of the item |
1128 | + ParallelAnimation { |
1129 | + id: queueListItemResetStartAnimation |
1130 | + running: false |
1131 | + NumberAnimation { // reset X |
1132 | + target: queueListItem |
1133 | + property: "x" |
1134 | + to: 0 |
1135 | + duration: queueListView.transitionDuration |
1136 | + } |
1137 | + NumberAnimation { // reset Y |
1138 | + target: queueListItem |
1139 | + property: "y" |
1140 | + to: queueArea.startY |
1141 | + duration: queueListView.transitionDuration |
1142 | + } |
1143 | + onRunningChanged: { |
1144 | + if (running === true) |
1145 | + { |
1146 | + swipeBackground.direction = ""; |
1147 | + swipeBackground.primed = false; |
1148 | + } |
1149 | + } |
1150 | + } |
1151 | + |
1152 | + // Move the listitem half way across to reveal the delete button |
1153 | + NumberAnimation { |
1154 | + id: queueListItemPrepareRemoveAnimation |
1155 | + target: queueListItem |
1156 | + property: "x" |
1157 | + to: swipeBackground.state == "swipingRight" ? queueListItem.width / 2 : 0 - (queueListItem.width / 2) |
1158 | + duration: queueListView.transitionDuration |
1159 | + onRunningChanged: { |
1160 | + if (running === true) |
1161 | + { |
1162 | + swipeBackground.direction = swipeBackground.state; |
1163 | + swipeBackground.primed = true; |
1164 | + } |
1165 | + } |
1166 | + } |
1167 | + |
1168 | + ParallelAnimation { |
1169 | + id: queueListItemRemoveAnimation |
1170 | + running: false |
1171 | + NumberAnimation { // 'slide' up |
1172 | + target: queueListItem |
1173 | + property: "height" |
1174 | + to: 0 |
1175 | + duration: queueListView.transitionDuration |
1176 | + } |
1177 | + NumberAnimation { // 'slide' in direction of removal |
1178 | + target: queueListItem |
1179 | + property: "x" |
1180 | + to: swipeBackground.direction === "swipingLeft" ? 0 - queueListItem.width : queueListItem.width |
1181 | + duration: queueListView.transitionDuration |
1182 | + } |
1183 | + onRunningChanged: { |
1184 | + if (running === false) |
1185 | + { |
1186 | + // Remove the item |
1187 | + if (index == queueListView.currentIndex) |
1188 | + { |
1189 | + if (queueListView.count > 1) |
1190 | + { |
1191 | + // Next song and only play if currently playing |
1192 | + player.nextSong(player.isPlaying); |
1193 | + } |
1194 | + else |
1195 | + { |
1196 | + player.stop(); |
1197 | + } |
1198 | + } |
1199 | + |
1200 | + if (index < player.currentIndex) { |
1201 | + player.currentIndex -= 1; |
1202 | + } |
1203 | + |
1204 | + // Remove item from queue and clear caches |
1205 | + trackQueue.model.remove(index); |
1206 | + queueChanged = true; |
1207 | + } |
1208 | + } |
1209 | + } |
1210 | + } |
1211 | + |
1212 | + Rectangle { |
1213 | + anchors { |
1214 | + left: parent.left |
1215 | + top: parent.top |
1216 | + } |
1217 | + height: parent.height |
1218 | + color: styleMusic.common.white |
1219 | + width: units.gu(1) |
1220 | + visible: index === player.currentIndex |
1221 | + } |
1222 | + |
1223 | + Rectangle { |
1224 | + id: trackContainer; |
1225 | + anchors { |
1226 | + fill: parent |
1227 | + margins: units.gu(0.5) |
1228 | + rightMargin: expandable.expanderButtonWidth |
1229 | + } |
1230 | + color: "transparent" |
1231 | + |
1232 | + NumberAnimation { |
1233 | + id: trackContainerReorderAnimation |
1234 | + target: trackContainer; |
1235 | + property: "anchors.leftMargin"; |
1236 | + duration: queueListView.transitionDuration; |
1237 | + to: units.gu(2) |
1238 | + } |
1239 | + |
1240 | + NumberAnimation { |
1241 | + id: trackContainerResetAnimation |
1242 | + target: trackContainer; |
1243 | + property: "anchors.leftMargin"; |
1244 | + duration: queueListView.transitionDuration; |
1245 | + to: units.gu(0.5) |
1246 | + } |
1247 | + |
1248 | + UbuntuShape { |
1249 | + id: trackImage |
1250 | + anchors.left: parent.left |
1251 | + anchors.leftMargin: units.gu(1.5) |
1252 | + anchors.top: parent.top |
1253 | + height: queueListView.normalHeight - units.gu(2) |
1254 | + width: height |
1255 | + image: Image { |
1256 | + source: "image://albumart/artist=" + model.author + "&album=" + model.album |
1257 | + onStatusChanged: { |
1258 | + if (status === Image.Error) { |
1259 | + source = Qt.resolvedUrl("../images/music-app-cover@30.png") |
1260 | + } |
1261 | + } |
1262 | + } |
1263 | + |
1264 | + function calcAnchors() { |
1265 | + if (trackImage.height > queueListView.normalHeight && mainView.wideAspect) { |
1266 | + trackImage.anchors.left = undefined |
1267 | + trackImage.anchors.horizontalCenter = trackImage.parent.horizontalCenter |
1268 | + } else { |
1269 | + trackImage.anchors.left = trackImage.parent.left |
1270 | + trackImage.anchors.horizontalCenter = undefined |
1271 | + } |
1272 | + |
1273 | + trackImage.width = trackImage.height; // force width to match height |
1274 | + } |
1275 | + |
1276 | + Connections { |
1277 | + target: mainView |
1278 | + onWideAspectChanged: trackImage.calcAnchors() |
1279 | + } |
1280 | + |
1281 | + onHeightChanged: { |
1282 | + calcAnchors() |
1283 | + } |
1284 | + Behavior on height { |
1285 | + NumberAnimation { |
1286 | + target: trackImage; |
1287 | + property: "height"; |
1288 | + duration: queueListView.transitionDuration; |
1289 | + } |
1290 | + } |
1291 | + } |
1292 | + Label { |
1293 | + id: nowPlayingArtist |
1294 | + objectName: "nowplayingartist" |
1295 | + color: styleMusic.nowPlaying.labelSecondaryColor |
1296 | + elide: Text.ElideRight |
1297 | + height: units.gu(1) |
1298 | + text: model.author |
1299 | + fontSize: 'small' |
1300 | + width: parent.width - trackImage.width - units.gu(3.5) |
1301 | + x: trackImage.x + trackImage.width + units.gu(1) |
1302 | + y: trackImage.y + units.gu(1) |
1303 | + } |
1304 | + Label { |
1305 | + id: nowPlayingTitle |
1306 | + objectName: "nowplayingtitle" |
1307 | + color: styleMusic.common.white |
1308 | + elide: Text.ElideRight |
1309 | + height: units.gu(1) |
1310 | + text: model.title |
1311 | + fontSize: 'medium' |
1312 | + width: parent.width - trackImage.width - units.gu(3.5) |
1313 | + x: trackImage.x + trackImage.width + units.gu(1) |
1314 | + y: nowPlayingArtist.y + nowPlayingArtist.height + units.gu(1.25) |
1315 | + } |
1316 | + Label { |
1317 | + id: nowPlayingAlbum |
1318 | + objectName: "nowplayingalbum" |
1319 | + color: styleMusic.nowPlaying.labelSecondaryColor |
1320 | + elide: Text.ElideRight |
1321 | + height: units.gu(1) |
1322 | + text: model.album |
1323 | + fontSize: 'x-small' |
1324 | + width: parent.width - trackImage.width - units.gu(3.5) |
1325 | + x: trackImage.x + trackImage.width + units.gu(1) |
1326 | + y: nowPlayingTitle.y + nowPlayingTitle.height + units.gu(1.25) |
1327 | + } |
1328 | + } |
1329 | + |
1330 | + Expander { |
1331 | + id: expandable |
1332 | + anchors { |
1333 | + fill: parent |
1334 | + } |
1335 | + actualListItemHeight: queueListView.normalHeight |
1336 | + buttonEnabled: !swipeBackground.primed |
1337 | + expanderButtonCentreFromBottom: queueListView.normalHeight - (trackContainer.anchors.margins * 2) - nowPlayingTitle.y - (nowPlayingTitle.height / 2) |
1338 | + listItem: queueListItem |
1339 | + model: trackQueue.model.get(index) |
1340 | + row: Row { |
1341 | + AddToPlaylist { |
1342 | + } |
1343 | + } |
1344 | + Behavior on actualListItemHeight { |
1345 | + NumberAnimation { |
1346 | + target: expandable; |
1347 | + property: "actualListItemHeight"; |
1348 | + duration: queueListView.transitionDuration; |
1349 | + } |
1350 | + } |
1351 | + } |
1352 | + } |
1353 | +} |
1354 | |
1355 | === modified file 'music-app.qml' |
1356 | --- music-app.qml 2014-06-29 22:19:03 +0000 |
1357 | +++ music-app.qml 2014-07-02 23:51:41 +0000 |
1358 | @@ -466,7 +466,9 @@ |
1359 | |
1360 | // Show the Now Playing page and make sure the track is visible |
1361 | tabs.pushNowPlaying(); |
1362 | - nowPlaying.ensureVisibleIndex = index; |
1363 | + |
1364 | + musicQueue.ensureVisibleIndex = -1; |
1365 | + musicQueue.ensureVisibleIndex = index; |
1366 | |
1367 | musicToolbar.showToolbar(); |
1368 | } |
1369 | @@ -478,16 +480,15 @@ |
1370 | } |
1371 | |
1372 | function trackQueueClick(index) { |
1373 | - if (player.currentIndex === index) { |
1374 | - player.toggle(); |
1375 | - } |
1376 | - else { |
1377 | + if (player.currentIndex !== index || musicToolbar.currentPage !== musicQueue) { |
1378 | player.playSong(trackQueue.model.get(index).filename, index); |
1379 | } |
1380 | |
1381 | // Show the Now Playing page and make sure the track is visible |
1382 | tabs.pushNowPlaying(); |
1383 | - nowPlaying.ensureVisibleIndex = index; |
1384 | + |
1385 | + musicQueue.ensureVisibleIndex = -1; |
1386 | + musicQueue.ensureVisibleIndex = index; |
1387 | |
1388 | musicToolbar.showToolbar(); |
1389 | } |
1390 | @@ -929,6 +930,10 @@ |
1391 | { |
1392 | // only push if on a different page |
1393 | if (mainPageStack.currentPage !== nowPlaying) { |
1394 | + if (mainPageStack.currentPage !== musicQueue) { |
1395 | + mainPageStack.push(musicQueue); |
1396 | + } |
1397 | + |
1398 | mainPageStack.push(nowPlaying); |
1399 | } |
1400 | } |
1401 | @@ -950,6 +955,10 @@ |
1402 | } // end of tabs |
1403 | } |
1404 | |
1405 | + MusicQueue { |
1406 | + id: musicQueue |
1407 | + } |
1408 | + |
1409 | SongsPage { |
1410 | id: songsPage |
1411 | } |
I think we need to think about this from a design perspective a bit more. IMO if we split the Queue and the Now Playing pages They should still be in one page, but the displayed layout should be selectable similar to how the Contacts app has "All" and "Favourites" (sic :P). In addition to that, I think we need to discuss if we even want to make this change. It simplifies things A LOT from an interaction point of view, but it also removes the dynamic feel of the queue that is sort of iconic about the app currently.
One bug I've noticed is that reorder in the queue does not cause the white playing indicator on the left to follow the currently playing item.