Merge lp:~ahayzen/music-app/refactor-move-delegates into lp:music-app

Proposed by Andrew Hayzen
Status: Merged
Approved by: Victor Thompson
Approved revision: 845
Merged at revision: 841
Proposed branch: lp:~ahayzen/music-app/refactor-move-delegates
Merge into: lp:music-app
Diff against target: 1287 lines (+455/-480)
19 files modified
app/components/CMakeLists.txt (+1/-0)
app/components/Delegates/CMakeLists.txt (+4/-0)
app/components/Delegates/Card.qml (+1/-0)
app/components/Delegates/ListItemWithActions.qml (+9/-239)
app/components/Delegates/MusicListItem.qml (+151/-0)
app/components/ListItemActions/AddToPlaylist.qml (+0/-2)
app/components/ListItemActions/Remove.qml (+0/-2)
app/components/ListItemReorderComponent.qml (+106/-0)
app/ui/AddToPlaylist.qml (+1/-0)
app/ui/Albums.qml (+1/-0)
app/ui/ArtistView.qml (+1/-0)
app/ui/Artists.qml (+1/-0)
app/ui/Genres.qml (+1/-0)
app/ui/NowPlaying.qml (+57/-90)
app/ui/Playlists.qml (+1/-0)
app/ui/Recent.qml (+1/-0)
app/ui/Songs.qml (+38/-55)
app/ui/SongsView.qml (+77/-88)
tests/autopilot/music_app/__init__.py (+4/-4)
To merge this branch: bzr merge lp:~ahayzen/music-app/refactor-move-delegates
Reviewer Review Type Date Requested Status
Victor Thompson Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Review via email: mp+249883@code.launchpad.net

Commit message

* Move delegates into their own folder in components
* Split out multiselect/reorder code from ListItemWithActions.qml into MusicListItem.qml and remove use of primed in ListItemWithActions.qml as it is not used anymore
* Fix for ListItemWithActions.qml if user swipes left and clicks where a rightAction would have been (has to be enabled) causes a console error and doesn't reset the swipe

Description of the change

* Move delegates into their own folder in components
* Split out multiselect/reorder code from ListItemWithActions.qml into MusicListItem.qml and remove use of primed in ListItemWithActions.qml as it is not used anymore
* Fix for ListItemWithActions.qml if user swipes left and clicks where a rightAction would have been (has to be enabled) causes a console error and doesn't reset the swipe

To post a comment you must log in.
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
842. By Andrew Hayzen

* Fix for autopilot

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

Is there any way more we could get more code reuse out of MusicListItem.qml? It's a pretty skinny component and we still have a lot of code for each usage of MusicListItem. I'm not seeing an easy way of doing so, however.

review: Needs Information
843. By Andrew Hayzen

* Split reorder code into a separate component and out of ListItemWithActions.qml
* Split out some of the custom multiselect code

844. By Andrew Hayzen

* Pull further multiselect code into MusicListItem.qml
* Remove use of primed as it is not used anymore

845. By Andrew Hayzen

* Sort out autogenerated Component

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

This looks good. One issue I'm having is that deleting a playlist freezes the app. I'm not sure what your change would have done to cause this.

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

The above issue also exists in the refactor series's trunk. I get the following in the console:

QQmlExpression: Attempted to evaluate an expression in an invalid context
qml: called LibraryListModel::filterPlaylist()

Since this wasn't introduced by the code change here, this looks good to me. :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'app/components/CMakeLists.txt'
--- app/components/CMakeLists.txt 2015-02-08 04:06:28 +0000
+++ app/components/CMakeLists.txt 2015-02-22 02:24:31 +0000
@@ -1,3 +1,4 @@
1add_subdirectory(Delegates)
1add_subdirectory(Dialog)2add_subdirectory(Dialog)
2add_subdirectory(HeadState)3add_subdirectory(HeadState)
3add_subdirectory(Flickables)4add_subdirectory(Flickables)
45
=== added directory 'app/components/Delegates'
=== added file 'app/components/Delegates/CMakeLists.txt'
--- app/components/Delegates/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ app/components/Delegates/CMakeLists.txt 2015-02-22 02:24:31 +0000
@@ -0,0 +1,4 @@
1# make the qml files visible on qtcreator
2file(GLOB DELEGATES_QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml)
3
4add_custom_target(com_ubuntu_music_DELEGATES_QMLFiles ALL SOURCES ${DELEGATES_QML_FILES})
05
=== renamed file 'app/components/Card.qml' => 'app/components/Delegates/Card.qml'
--- app/components/Card.qml 2014-11-11 18:08:40 +0000
+++ app/components/Delegates/Card.qml 2015-02-22 02:24:31 +0000
@@ -17,6 +17,7 @@
1717
18import QtQuick 2.318import QtQuick 2.3
19import Ubuntu.Components 1.119import Ubuntu.Components 1.1
20import "../"
2021
2122
22Item {23Item {
2324
=== renamed file 'app/components/ListItemWithActions.qml' => 'app/components/Delegates/ListItemWithActions.qml'
--- app/components/ListItemWithActions.qml 2015-01-18 00:06:06 +0000
+++ app/components/Delegates/ListItemWithActions.qml 2015-02-22 02:24:31 +0000
@@ -45,11 +45,8 @@
45 readonly property alias swipping: mainItemMoving.running45 readonly property alias swipping: mainItemMoving.running
46 readonly property bool _showActions: mouseArea.pressed || swipeState != "Normal" || swipping46 readonly property bool _showActions: mouseArea.pressed || swipeState != "Normal" || swipping
4747
48 property bool reorderable: false // CUSTOM48 property alias _main: main // CUSTOM
49 property bool multiselectable: false // CUSTOM49 property alias pressed: mouseArea.pressed // CUSTOM
50
51 property int previousListItemIndex: -1 // CUSTOM
52 property int listItemIndex: index // CUSTOM
5350
54 /* internal */51 /* internal */
55 property var _visibleRightSideActions: filterVisibleActions(rightSideActions)52 property var _visibleRightSideActions: filterVisibleActions(rightSideActions)
@@ -57,55 +54,6 @@
57 signal itemClicked(var mouse)54 signal itemClicked(var mouse)
58 signal itemPressAndHold(var mouse)55 signal itemPressAndHold(var mouse)
5956
60 signal reorder(int from, int to) // CUSTOM
61
62 onItemPressAndHold: {
63 if (multiselectable) {
64 selectionMode = true
65 }
66 }
67 onListItemIndexChanged: {
68 var i = parent.parent.selectedItems.lastIndexOf(previousListItemIndex)
69
70 if (i !== -1) {
71 parent.parent.selectedItems[i] = listItemIndex
72 }
73
74 previousListItemIndex = listItemIndex
75 }
76
77 onSelectedChanged: {
78 if (selectionMode) {
79 var tmp = parent.parent.selectedItems
80
81 if (selected) {
82 if (parent.parent.selectedItems.indexOf(listItemIndex) === -1) {
83 tmp.push(listItemIndex)
84 parent.parent.selectedItems = tmp
85 }
86 } else {
87 tmp.splice(parent.parent.selectedItems.indexOf(listItemIndex), 1)
88 parent.parent.selectedItems = tmp
89 }
90 }
91 }
92
93 onSelectionModeChanged: { // CUSTOM
94 if (reorderable && selectionMode) {
95 resetSwipe()
96 }
97
98 for (var j=0; j < main.children.length; j++) {
99 main.children[j].anchors.rightMargin = reorderable && selectionMode ? actionReorderLoader.width + units.gu(2) : 0
100 }
101
102 parent.parent.state = selectionMode ? "multiselectable" : "normal"
103
104 if (!selectionMode) {
105 selected = false
106 }
107 }
108
109 function returnToBoundsRTL(direction)57 function returnToBoundsRTL(direction)
110 {58 {
111 var actionFullWidth = actionWidth + units.gu(2)59 var actionFullWidth = actionWidth + units.gu(2)
@@ -122,20 +70,11 @@
122 var xOffset = Math.abs(main.x)70 var xOffset = Math.abs(main.x)
123 var index = Math.min(Math.floor(xOffset / actionFullWidth), _visibleRightSideActions.length)71 var index = Math.min(Math.floor(xOffset / actionFullWidth), _visibleRightSideActions.length)
124 var newX = 072 var newX = 0
125 var j // CUSTOM
12673
127 if (index === _visibleRightSideActions.length) {74 if (index === _visibleRightSideActions.length) {
128 newX = -(rightActionsView.width - units.gu(2))75 newX = -(rightActionsView.width - units.gu(2))
129
130 for (j=0; j < rightSideActions.length; j++) { // CUSTOM
131 rightActionsRepeater.itemAt(j).primed = true
132 }
133 } else if (index >= 1) {76 } else if (index >= 1) {
134 newX = -(actionFullWidth * index)77 newX = -(actionFullWidth * index)
135
136 for (j=0; j < rightSideActions.length; j++) { // CUSTOM
137 rightActionsRepeater.itemAt(j).primed = j === index
138 }
139 }78 }
14079
141 updatePosition(newX)80 updatePosition(newX)
@@ -147,10 +86,6 @@
147 if ((direction === "RTL") || (main.x <= (finalX * root.threshold)))86 if ((direction === "RTL") || (main.x <= (finalX * root.threshold)))
148 finalX = 087 finalX = 0
149 updatePosition(finalX)88 updatePosition(finalX)
150
151 if (leftSideAction !== null) { // CUSTOM
152 leftActionViewLoader.item.primed = main.x > (finalX * root.threshold)
153 }
154 }89 }
15590
156 function returnToBounds(direction)91 function returnToBounds(direction)
@@ -206,18 +141,6 @@
206 }141 }
207 }142 }
208143
209 function resetPrimed() // CUSTOM
210 {
211 if (leftSideAction !== null) {
212 leftActionViewLoader.item.primed = false
213 }
214
215 for (var j=0; j < rightSideActions.length; j++) {
216 console.debug(rightActionsRepeater.itemAt(j));
217 rightActionsRepeater.itemAt(j).primed = false
218 }
219 }
220
221 function resetSwipe()144 function resetSwipe()
222 {145 {
223 updatePosition(0)146 updatePosition(0)
@@ -243,39 +166,6 @@
243 mouseArea.state = ""166 mouseArea.state = ""
244 }167 }
245 main.x = pos168 main.x = pos
246
247 if (pos === 0) { // CUSTOM
248 //resetPrimed()
249 }
250 }
251
252 Connections { // CUSTOM
253 target: mainView
254 onListItemSwiping: {
255 if (i !== index) {
256 root.resetSwipe();
257 }
258 }
259 }
260
261 Connections { // CUSTOM
262 target: root.parent.parent
263 onClearSelection: selected = false
264 onFlickingChanged: {
265 if (root.parent.parent.flicking) {
266 root.resetSwipe()
267 }
268 }
269 onSelectAll: selected = true
270 onStateChanged: selectionMode = root.parent.parent.state === "multiselectable"
271 }
272
273 Component.onCompleted: { // CUSTOM
274 if (parent.parent.selectedItems.indexOf(index) !== -1) { // FIXME:
275 selected = true
276 }
277
278 selectionMode = root.parent.parent.state === "multiselectable"
279 }169 }
280170
281 // CUSTOM remove animation171 // CUSTOM remove animation
@@ -301,7 +191,7 @@
301 when: selectionMode || selected191 when: selectionMode || selected
302 PropertyChanges {192 PropertyChanges {
303 target: selectionIcon193 target: selectionIcon
304 source: Qt.resolvedUrl("ListItemActions/CheckBox.qml")194 source: Qt.resolvedUrl("../ListItemActions/CheckBox.qml")
305 anchors.leftMargin: units.gu(2)195 anchors.leftMargin: units.gu(2)
306 }196 }
307 PropertyChanges {197 PropertyChanges {
@@ -337,8 +227,6 @@
337 width: root.leftActionWidth + actionThreshold227 width: root.leftActionWidth + actionThreshold
338 color: UbuntuColors.red228 color: UbuntuColors.red
339229
340 property alias primed: leftActionIcon.primed // CUSTOM
341
342 Icon {230 Icon {
343 id: leftActionIcon231 id: leftActionIcon
344 anchors {232 anchors {
@@ -350,8 +238,6 @@
350 color: Theme.palette.selected.field238 color: Theme.palette.selected.field
351 height: units.gu(3)239 height: units.gu(3)
352 width: units.gu(3)240 width: units.gu(3)
353
354 property bool primed: false // CUSTOM
355 }241 }
356 }242 }
357 }243 }
@@ -400,8 +286,6 @@
400 height: rightActionsView.height286 height: rightActionsView.height
401 width: root.actionWidth287 width: root.actionWidth
402288
403 property alias primed: img.primed // CUSTOM
404
405 Icon {289 Icon {
406 id: img290 id: img
407291
@@ -411,8 +295,6 @@
411 height: units.gu(3)295 height: units.gu(3)
412 name: modelData.iconName296 name: modelData.iconName
413 color: root.activeAction === modelData ? UbuntuColors.blue : styleMusic.common.white // CUSTOM297 color: root.activeAction === modelData ? UbuntuColors.blue : styleMusic.common.white // CUSTOM
414
415 property bool primed: false // CUSTOM
416 }298 }
417 }299 }
418 }300 }
@@ -474,118 +356,6 @@
474 }356 }
475 }357 }
476358
477 /* CUSTOM Brighten Component */
478 Rectangle {
479 id: listItemBrighten
480 anchors {
481 fill: main
482 }
483
484 color: mouseArea.pressed ? styleMusic.common.white : "transparent"
485 opacity: 0.1
486 }
487
488 /* CUSTOM Reorder Component */
489 Loader {
490 id: actionReorderLoader
491 anchors {
492 bottom: parent.bottom
493 right: main.right
494 rightMargin: units.gu(1)
495 top: parent.top
496 }
497 asynchronous: true
498 sourceComponent: reorderable && selectionMode && root.parent.parent.selectedItems.length === 0 ? actionReorderComponent : undefined
499 }
500
501 Component {
502 id: actionReorderComponent
503 Item {
504 id: actionReorder
505 width: units.gu(4)
506 visible: reorderable && selectionMode && root.parent.parent.selectedItems.length === 0
507
508 Icon {
509 anchors {
510 horizontalCenter: parent.horizontalCenter
511 verticalCenter: parent.verticalCenter
512 }
513 name: "navigation-menu" // TODO: use proper image
514 height: width
515 width: units.gu(3)
516 }
517
518 MouseArea {
519 id: actionReorderMouseArea
520 anchors {
521 fill: parent
522 }
523 property int startY: 0
524 property int startContentY: 0
525
526 onPressed: {
527 root.parent.parent.interactive = false; // stop scrolling of listview
528 startY = root.y;
529 startContentY = root.parent.parent.contentY;
530 root.z += 10; // force ontop of other elements
531
532 console.debug("Reorder listitem pressed", root.y)
533 }
534 onMouseYChanged: root.y += mouse.y - (root.height / 2);
535 onReleased: {
536 console.debug("Reorder diff by position", getDiff());
537
538 var diff = getDiff();
539
540 // Remove the height of the actual item if moved down
541 if (diff > 0) {
542 diff -= 1;
543 }
544
545 root.parent.parent.interactive = true; // reenable scrolling
546
547 if (diff === 0) {
548 // Nothing has changed so reset the item
549 // z index is restored after animation
550 resetListItemYAnimation.start();
551 }
552 else {
553 var newIndex = index + diff;
554
555 if (newIndex < 0) {
556 newIndex = 0;
557 }
558 else if (newIndex > root.parent.parent.count - 1) {
559 newIndex = root.parent.parent.count - 1;
560 }
561
562 root.z -= 10; // restore z index
563 reorder(index, newIndex)
564 }
565 }
566
567 function getDiff() {
568 // Get the amount of items that have been passed over (by centre)
569 return Math.round((((root.y - startY) + (root.parent.parent.contentY - startContentY)) / root.height) + 0.5);
570 }
571 }
572
573 SequentialAnimation {
574 id: resetListItemYAnimation
575 UbuntuNumberAnimation {
576 target: root;
577 property: "y";
578 to: actionReorderMouseArea.startY
579 }
580 ScriptAction {
581 script: {
582 root.z -= 10; // restore z index
583 }
584 }
585 }
586 }
587 }
588
589 SequentialAnimation {359 SequentialAnimation {
590 id: triggerAction360 id: triggerAction
591361
@@ -694,22 +464,22 @@
694 direction = "None"464 direction = "None"
695 }465 }
696 onClicked: {466 onClicked: {
697 if (selectionMode) { // CUSTOM467 if (selectionMode) { // CUSTOM - selecting a listitem should toggle selection if in selectionMode
698 selected = !selected468 selected = !selected
699 return469 return
700 }470 } else if (main.x === 0) {
701 else if (main.x === 0) {
702 root.itemClicked(mouse)471 root.itemClicked(mouse)
703 } else if (main.x > 0) {472 } else if (main.x > 0) {
704 var action = getActionAt(Qt.point(mouse.x, mouse.y))473 var action = getActionAt(Qt.point(mouse.x, mouse.y))
705 if (action && action !== -1) {474 if (action && action !== -1) {
706 //action.triggered(root)475 //action.triggered(root)
707 removeAnimation.action = action // CUSTOM476 removeAnimation.action = action // CUSTOM - use our animation instead
708 removeAnimation.start() // CUSTOM477 removeAnimation.start() // CUSTOM
709 }478 }
710 } else {479 } else {
711 var actionIndex = getActionAt(Qt.point(mouse.x, mouse.y))480 var actionIndex = getActionAt(Qt.point(mouse.x, mouse.y))
712 if (actionIndex !== -1) {481
482 if (actionIndex !== -1 && actionIndex !== leftSideAction) { // CUSTOM - can be leftAction
713 root.activeItem = rightActionsRepeater.itemAt(actionIndex)483 root.activeItem = rightActionsRepeater.itemAt(actionIndex)
714 root.activeAction = root.rightSideActions[actionIndex]484 root.activeAction = root.rightSideActions[actionIndex]
715 triggerAction.start()485 triggerAction.start()
@@ -723,7 +493,7 @@
723 if (mouseArea.pressed) {493 if (mouseArea.pressed) {
724 updateActiveAction()494 updateActiveAction()
725495
726 listItemSwiping(index) // CUSTOM496 listItemSwiping(index) // CUSTOM - tells other listitems to dismiss any swipe
727 }497 }
728 }498 }
729 onPressAndHold: {499 onPressAndHold: {
730500
=== added file 'app/components/Delegates/MusicListItem.qml'
--- app/components/Delegates/MusicListItem.qml 1970-01-01 00:00:00 +0000
+++ app/components/Delegates/MusicListItem.qml 2015-02-22 02:24:31 +0000
@@ -0,0 +1,151 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Nekhelesh Ramananthan <krnekhelesh@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20import QtQuick 2.3
21import Ubuntu.Components 1.1
22import "../"
23
24
25ListItemWithActions {
26 id: root
27
28 property alias column: musicRow.column
29 property alias imageSource: musicRow.imageSource
30
31 property int listItemIndex: index
32 property bool multiselectable: false
33 property int previousListItemIndex: -1
34 property bool reorderable: false
35
36 signal reorder(int from, int to)
37
38 onItemPressAndHold: {
39 if (multiselectable) {
40 selectionMode = true
41 }
42 }
43
44 onListItemIndexChanged: {
45 var i = parent.parent.selectedItems.lastIndexOf(previousListItemIndex)
46
47 if (i !== -1) {
48 parent.parent.selectedItems[i] = listItemIndex
49 }
50
51 previousListItemIndex = listItemIndex
52 }
53
54 onSelectedChanged: {
55 if (selectionMode) {
56 var tmp = parent.parent.selectedItems
57
58 if (selected) {
59 if (parent.parent.selectedItems.indexOf(listItemIndex) === -1) {
60 tmp.push(listItemIndex)
61 parent.parent.selectedItems = tmp
62 }
63 } else {
64 tmp.splice(parent.parent.selectedItems.indexOf(listItemIndex), 1)
65 parent.parent.selectedItems = tmp
66 }
67 }
68 }
69
70 onSelectionModeChanged: {
71 if (reorderable && selectionMode) {
72 resetSwipe()
73 }
74
75 for (var j=0; j < _main.children.length; j++) {
76 if (_main.children[j] !== actionReorderLoader) {
77 _main.children[j].anchors.rightMargin = reorderable && selectionMode ? actionReorderLoader.width + units.gu(2) : 0
78 }
79 }
80
81 parent.parent.state = selectionMode ? "multiselectable" : "normal"
82
83 if (!selectionMode) {
84 selected = false
85 }
86 }
87
88 /* Highlight the listitem on press */
89 Rectangle {
90 id: listItemBrighten
91 color: root.pressed ? styleMusic.common.white : "transparent"
92 opacity: 0.1
93 height: root.height
94 x: root.x - parent.x // -parent.x due to selectionIcon in ListItemWithActions
95 width: root.width
96 }
97
98 /* Reorder Component */
99 Loader {
100 id: actionReorderLoader
101 active: reorderable && selectionMode && root.parent.parent.selectedItems.length === 0
102 anchors {
103 bottom: parent.bottom
104 right: parent.right
105 rightMargin: units.gu(1)
106 top: parent.top
107 }
108 asynchronous: true
109 source: "../ListItemReorderComponent.qml"
110 }
111
112 Item {
113 Connections { // Only allow one ListItem to be swiping at any time
114 target: mainView
115 onListItemSwiping: {
116 if (i !== index) {
117 root.resetSwipe();
118 }
119 }
120 }
121
122 Connections { // Connections from signals in the ListView
123 target: root.parent.parent
124 onClearSelection: selected = false
125 onFlickingChanged: {
126 if (root.parent.parent.flicking) {
127 root.resetSwipe()
128 }
129 }
130 onSelectAll: selected = true
131 onStateChanged: selectionMode = root.parent.parent.state === "multiselectable"
132 }
133 }
134
135
136 MusicRow {
137 id: musicRow
138 anchors {
139 verticalCenter: parent.verticalCenter
140 }
141 height: parent.height
142 }
143
144 Component.onCompleted: { // reload settings as delegates are destroyed
145 if (parent.parent.selectedItems.indexOf(index) !== -1) {
146 selected = true
147 }
148
149 selectionMode = root.parent.parent.state === "multiselectable"
150 }
151}
0152
=== modified file 'app/components/ListItemActions/AddToPlaylist.qml'
--- app/components/ListItemActions/AddToPlaylist.qml 2015-02-04 19:01:14 +0000
+++ app/components/ListItemActions/AddToPlaylist.qml 2015-02-22 02:24:31 +0000
@@ -25,8 +25,6 @@
25 objectName: "addToPlaylistAction"25 objectName: "addToPlaylistAction"
26 text: i18n.tr("Add to playlist")26 text: i18n.tr("Add to playlist")
2727
28 property bool primed: false
29
30 onTriggered: {28 onTriggered: {
31 console.debug("Debug: Add track to playlist");29 console.debug("Debug: Add track to playlist");
3230
3331
=== modified file 'app/components/ListItemActions/Remove.qml'
--- app/components/ListItemActions/Remove.qml 2014-09-20 10:50:45 +0000
+++ app/components/ListItemActions/Remove.qml 2015-02-22 02:24:31 +0000
@@ -25,6 +25,4 @@
25 iconName: "delete"25 iconName: "delete"
26 objectName: "swipeDeleteAction"26 objectName: "swipeDeleteAction"
27 text: i18n.tr("Remove")27 text: i18n.tr("Remove")
28
29 property bool primed: false
30}28}
3129
=== added file 'app/components/ListItemReorderComponent.qml'
--- app/components/ListItemReorderComponent.qml 1970-01-01 00:00:00 +0000
+++ app/components/ListItemReorderComponent.qml 2015-02-22 02:24:31 +0000
@@ -0,0 +1,106 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Nekhelesh Ramananthan <krnekhelesh@gmail.com>
5 * Victor Thompson <victor.thompson@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20import QtQuick 2.3
21import Ubuntu.Components 1.1
22
23
24Item {
25 id: actionReorder
26 width: units.gu(4)
27
28 Icon {
29 anchors {
30 horizontalCenter: parent.horizontalCenter
31 verticalCenter: parent.verticalCenter
32 }
33 name: "navigation-menu" // TODO: use proper image
34 height: width
35 width: units.gu(3)
36 }
37
38 MouseArea {
39 id: actionReorderMouseArea
40 anchors {
41 fill: parent
42 }
43 property int startY: 0
44 property int startContentY: 0
45
46 onPressed: {
47 root.parent.parent.interactive = false; // stop scrolling of listview
48 startY = root.y;
49 startContentY = root.parent.parent.contentY;
50 root.z += 10; // force ontop of other elements
51
52 console.debug("Reorder listitem pressed", root.y)
53 }
54 onMouseYChanged: root.y += mouse.y - (root.height / 2);
55 onReleased: {
56 console.debug("Reorder diff by position", getDiff());
57
58 var diff = getDiff();
59
60 // Remove the height of the actual item if moved down
61 if (diff > 0) {
62 diff -= 1;
63 }
64
65 root.parent.parent.interactive = true; // reenable scrolling
66
67 if (diff === 0) {
68 // Nothing has changed so reset the item
69 // z index is restored after animation
70 resetListItemYAnimation.start();
71 }
72 else {
73 var newIndex = index + diff;
74
75 if (newIndex < 0) {
76 newIndex = 0;
77 }
78 else if (newIndex > root.parent.parent.count - 1) {
79 newIndex = root.parent.parent.count - 1;
80 }
81
82 root.z -= 10; // restore z index
83 reorder(index, newIndex)
84 }
85 }
86
87 function getDiff() {
88 // Get the amount of items that have been passed over (by centre)
89 return Math.round((((root.y - startY) + (root.parent.parent.contentY - startContentY)) / root.height) + 0.5);
90 }
91 }
92
93 SequentialAnimation {
94 id: resetListItemYAnimation
95 UbuntuNumberAnimation {
96 target: root;
97 property: "y";
98 to: actionReorderMouseArea.startY
99 }
100 ScriptAction {
101 script: {
102 root.z -= 10; // restore z index
103 }
104 }
105 }
106}
0107
=== modified file 'app/ui/AddToPlaylist.qml'
--- app/ui/AddToPlaylist.qml 2015-02-08 04:06:28 +0000
+++ app/ui/AddToPlaylist.qml 2015-02-22 02:24:31 +0000
@@ -26,6 +26,7 @@
26import "../logic/meta-database.js" as Library26import "../logic/meta-database.js" as Library
27import "../logic/playlists.js" as Playlists27import "../logic/playlists.js" as Playlists
28import "../components"28import "../components"
29import "../components/Delegates"
29import "../components/Flickables"30import "../components/Flickables"
30import "../components/HeadState"31import "../components/HeadState"
3132
3233
=== modified file 'app/ui/Albums.qml'
--- app/ui/Albums.qml 2015-02-08 04:06:28 +0000
+++ app/ui/Albums.qml 2015-02-22 02:24:31 +0000
@@ -21,6 +21,7 @@
21import Ubuntu.Components 1.121import Ubuntu.Components 1.1
22import Ubuntu.MediaScanner 0.122import Ubuntu.MediaScanner 0.1
23import "../components"23import "../components"
24import "../components/Delegates"
24import "../components/Flickables"25import "../components/Flickables"
25import "../components/HeadState"26import "../components/HeadState"
2627
2728
=== modified file 'app/ui/ArtistView.qml'
--- app/ui/ArtistView.qml 2015-02-08 04:06:28 +0000
+++ app/ui/ArtistView.qml 2015-02-22 02:24:31 +0000
@@ -26,6 +26,7 @@
26import QtQuick.LocalStorage 2.026import QtQuick.LocalStorage 2.0
27import "../logic/meta-database.js" as Library27import "../logic/meta-database.js" as Library
28import "../components"28import "../components"
29import "../components/Delegates"
29import "../components/Flickables"30import "../components/Flickables"
30import "../components/ViewButton"31import "../components/ViewButton"
3132
3233
=== modified file 'app/ui/Artists.qml'
--- app/ui/Artists.qml 2015-02-08 04:06:28 +0000
+++ app/ui/Artists.qml 2015-02-22 02:24:31 +0000
@@ -27,6 +27,7 @@
27import "../logic/meta-database.js" as Library27import "../logic/meta-database.js" as Library
28import "../logic/playlists.js" as Playlists28import "../logic/playlists.js" as Playlists
29import "../components"29import "../components"
30import "../components/Delegates"
30import "../components/Flickables"31import "../components/Flickables"
31import "../components/HeadState"32import "../components/HeadState"
3233
3334
=== modified file 'app/ui/Genres.qml'
--- app/ui/Genres.qml 2015-02-08 04:06:28 +0000
+++ app/ui/Genres.qml 2015-02-22 02:24:31 +0000
@@ -21,6 +21,7 @@
21import Ubuntu.Components 1.121import Ubuntu.Components 1.1
22import Ubuntu.MediaScanner 0.122import Ubuntu.MediaScanner 0.1
23import "../components"23import "../components"
24import "../components/Delegates"
24import "../components/Flickables"25import "../components/Flickables"
25import "../components/HeadState"26import "../components/HeadState"
2627
2728
=== modified file 'app/ui/NowPlaying.qml'
--- app/ui/NowPlaying.qml 2015-02-08 04:06:28 +0000
+++ app/ui/NowPlaying.qml 2015-02-22 02:24:31 +0000
@@ -23,6 +23,7 @@
23import Ubuntu.Components 1.123import Ubuntu.Components 1.1
24import Ubuntu.Thumbnailer 0.124import Ubuntu.Thumbnailer 0.1
25import "../components"25import "../components"
26import "../components/Delegates"
26import "../components/Flickables"27import "../components/Flickables"
27import "../components/HeadState"28import "../components/HeadState"
28import "../components/ListItemActions"29import "../components/ListItemActions"
@@ -378,7 +379,6 @@
378 fill: parent379 fill: parent
379 topMargin: units.gu(2)380 topMargin: units.gu(2)
380 }381 }
381 delegate: queueDelegate
382 footer: Item {382 footer: Item {
383 height: mainView.height - (styleMusic.common.expandHeight + queueList.currentHeight) + units.gu(8)383 height: mainView.height - (styleMusic.common.expandHeight + queueList.currentHeight) + units.gu(8)
384 }384 }
@@ -390,95 +390,62 @@
390390
391 onCountChanged: customdebug("Queue: Now has: " + queueList.count + " tracks")391 onCountChanged: customdebug("Queue: Now has: " + queueList.count + " tracks")
392392
393 Component {393 delegate: MusicListItem {
394 id: queueDelegate394 id: queueListItem
395 ListItemWithActions {395 color: player.currentIndex === index ? "#2c2c34" : styleMusic.mainView.backgroundColor
396 id: queueListItem396 column: Column {
397 color: player.currentIndex === index ? "#2c2c34" : styleMusic.mainView.backgroundColor397 Label {
398 height: queueList.normalHeight398 id: trackTitle
399 objectName: "nowPlayingListItem" + index399 color: player.currentIndex === index ? UbuntuColors.blue : styleMusic.common.music
400 state: ""400 fontSize: "small"
401401 objectName: "titleLabel"
402 leftSideAction: Remove {402 text: model.title
403 onTriggered: trackQueue.removeQueueList([index])403 }
404 }404
405 multiselectable: true405 Label {
406 reorderable: true406 id: trackArtist
407 rightSideActions: [407 color: styleMusic.common.subtitle
408 AddToPlaylist{408 fontSize: "x-small"
409409 objectName: "artistLabel"
410 }410 text: model.author
411 ]411 }
412412 }
413 onItemClicked: {413 height: queueList.normalHeight
414 customdebug("File: " + model.filename) // debugger414 objectName: "nowPlayingListItem" + index
415 trackQueueClick(index); // toggle track state415 state: ""
416 }416 leftSideAction: Remove {
417 onReorder: {417 onTriggered: trackQueue.removeQueueList([index])
418 console.debug("Move: ", from, to);418 }
419419 multiselectable: true
420 trackQueue.model.move(from, to, 1);420 reorderable: true
421 Library.moveQueueItem(from, to);421 rightSideActions: [
422422 AddToPlaylist{
423 // Maintain currentIndex with current song423
424 if (from === player.currentIndex) {424 }
425 player.currentIndex = to;425 ]
426 }426
427 else if (from < player.currentIndex && to >= player.currentIndex) {427 onItemClicked: {
428 player.currentIndex -= 1;428 customdebug("File: " + model.filename) // debugger
429 }429 trackQueueClick(index); // toggle track state
430 else if (from > player.currentIndex && to <= player.currentIndex) {430 }
431 player.currentIndex += 1;431 onReorder: {
432 }432 console.debug("Move: ", from, to);
433433
434 queueIndex = player.currentIndex434 trackQueue.model.move(from, to, 1);
435 }435 Library.moveQueueItem(from, to);
436436
437 Item {437 // Maintain currentIndex with current song
438 id: trackContainer;438 if (from === player.currentIndex) {
439 anchors {439 player.currentIndex = to;
440 fill: parent440 }
441 }441 else if (from < player.currentIndex && to >= player.currentIndex) {
442442 player.currentIndex -= 1;
443 NumberAnimation {443 }
444 id: trackContainerReorderAnimation444 else if (from > player.currentIndex && to <= player.currentIndex) {
445 target: trackContainer;445 player.currentIndex += 1;
446 property: "anchors.leftMargin";446 }
447 duration: queueList.transitionDuration;447
448 to: units.gu(2)448 queueIndex = player.currentIndex
449 }
450
451 NumberAnimation {
452 id: trackContainerResetAnimation
453 target: trackContainer;
454 property: "anchors.leftMargin";
455 duration: queueList.transitionDuration;
456 to: units.gu(0.5)
457 }
458
459 MusicRow {
460 id: musicRow
461 height: parent.height
462 column: Column {
463 Label {
464 id: trackTitle
465 color: player.currentIndex === index ? UbuntuColors.blue
466 : styleMusic.common.music
467 fontSize: "small"
468 objectName: "titleLabel"
469 text: model.title
470 }
471
472 Label {
473 id: trackArtist
474 color: styleMusic.common.subtitle
475 fontSize: "x-small"
476 objectName: "artistLabel"
477 text: model.author
478 }
479 }
480 }
481 }
482 }449 }
483 }450 }
484 }451 }
485452
=== modified file 'app/ui/Playlists.qml'
--- app/ui/Playlists.qml 2015-02-08 04:06:28 +0000
+++ app/ui/Playlists.qml 2015-02-22 02:24:31 +0000
@@ -25,6 +25,7 @@
25import QtQuick.LocalStorage 2.025import QtQuick.LocalStorage 2.0
26import "../logic/playlists.js" as Playlists26import "../logic/playlists.js" as Playlists
27import "../components"27import "../components"
28import "../components/Delegates"
28import "../components/Flickables"29import "../components/Flickables"
29import "../components/HeadState"30import "../components/HeadState"
3031
3132
=== modified file 'app/ui/Recent.qml'
--- app/ui/Recent.qml 2015-02-08 04:06:28 +0000
+++ app/ui/Recent.qml 2015-02-22 02:24:31 +0000
@@ -28,6 +28,7 @@
28import "../logic/meta-database.js" as Library28import "../logic/meta-database.js" as Library
29import "../logic/playlists.js" as Playlists29import "../logic/playlists.js" as Playlists
30import "../components"30import "../components"
31import "../components/Delegates"
31import "../components/Flickables"32import "../components/Flickables"
3233
33MusicPage {34MusicPage {
3435
=== modified file 'app/ui/Songs.qml'
--- app/ui/Songs.qml 2015-02-08 04:06:28 +0000
+++ app/ui/Songs.qml 2015-02-22 02:24:31 +0000
@@ -25,6 +25,7 @@
25import QtQuick.LocalStorage 2.025import QtQuick.LocalStorage 2.0
26import "../logic/playlists.js" as Playlists26import "../logic/playlists.js" as Playlists
27import "../components"27import "../components"
28import "../components/Delegates"
28import "../components/Flickables"29import "../components/Flickables"
29import "../components/HeadState"30import "../components/HeadState"
30import "../components/ListItemActions"31import "../components/ListItemActions"
@@ -90,61 +91,43 @@
90 }91 }
91 }92 }
9293
93 delegate: trackDelegate94 delegate: MusicListItem {
94 Component {95 id: track
95 id: trackDelegate96 objectName: "tracksPageListItem" + index
9697 column: Column {
97 ListItemWithActions {98 Label {
98 id: track99 id: trackTitle
99 objectName: "tracksPageListItem" + index100 color: styleMusic.common.music
100 height: units.gu(7)101 fontSize: "small"
101102 objectName: "tracktitle"
102 multiselectable: true103 text: model.title
103 rightSideActions: [104 }
104 AddToQueue {105
105 },106 Label {
106 AddToPlaylist {107 id: trackArtist
107108 color: styleMusic.common.subtitle
108 }109 fontSize: "x-small"
109 ]110 text: model.author
110111 }
111 onItemClicked: {112 }
112 if (songsPage.state === "search") { // only play single track when searching113 height: units.gu(7)
113 trackQueue.clear()114 imageSource: {"art": model.art}
114 trackQueue.append(songsModelFilter.get(index))115 multiselectable: true
115 trackQueueClick(0)116 rightSideActions: [
116 } else {117 AddToQueue {
117 trackClicked(songsModelFilter, index) // play track118 },
118 }119 AddToPlaylist {
119 }120
120121 }
121 MusicRow {122 ]
122 id: musicRow123
123 anchors {124 onItemClicked: {
124 verticalCenter: parent.verticalCenter125 if (songsPage.state === "search") { // only play single track when searching
125 }126 trackQueue.clear()
126 imageSource: {"art": model.art}127 trackQueue.append(songsModelFilter.get(index))
127 column: Column {128 trackQueueClick(0)
128 Label {129 } else {
129 id: trackTitle130 trackClicked(songsModelFilter, index) // play track
130 color: styleMusic.common.music
131 fontSize: "small"
132 objectName: "tracktitle"
133 text: model.title
134 }
135
136 Label {
137 id: trackArtist
138 color: styleMusic.common.subtitle
139 fontSize: "x-small"
140 text: model.author
141 }
142 }
143 }
144
145 states: State {
146 name: "Current"
147 when: track.ListView.isCurrentItem
148 }131 }
149 }132 }
150 }133 }
151134
=== modified file 'app/ui/SongsView.qml'
--- app/ui/SongsView.qml 2015-02-08 04:06:28 +0000
+++ app/ui/SongsView.qml 2015-02-22 02:24:31 +0000
@@ -27,6 +27,7 @@
27import "../logic/meta-database.js" as Library27import "../logic/meta-database.js" as Library
28import "../logic/playlists.js" as Playlists28import "../logic/playlists.js" as Playlists
29import "../components"29import "../components"
30import "../components/Delegates"
30import "../components/Flickables"31import "../components/Flickables"
31import "../components/HeadState"32import "../components/HeadState"
32import "../components/ListItemActions"33import "../components/ListItemActions"
@@ -186,11 +187,9 @@
186 anchors {187 anchors {
187 fill: parent188 fill: parent
188 }189 }
189 delegate: albumTracksDelegate
190 model: isAlbum ? songsModel : albumTracksModel.model190 model: isAlbum ? songsModel : albumTracksModel.model
191 objectName: "songspage-listview"191 objectName: "songspage-listview"
192 width: parent.width192 width: parent.width
193
194 header: BlurredHeader {193 header: BlurredHeader {
195 rightColumn: Column {194 rightColumn: Column {
196 spacing: units.gu(2)195 spacing: units.gu(2)
@@ -290,92 +289,82 @@
290 }289 }
291 }290 }
292 }291 }
293292 delegate: MusicListItem {
294 Component {293 id: track
295 id: albumTracksDelegate294 objectName: "songsPageListItem" + index
296295 column: Column {
297 ListItemWithActions {296 Label {
298 id: track297 id: trackTitle
299 objectName: "songsPageListItem" + index298 color: styleMusic.common.music
300 height: units.gu(6)299 fontSize: "small"
301300 objectName: "songspage-tracktitle"
302 leftSideAction: songStackPage.line1 === i18n.tr("Playlist")301 text: model.title
303 ? playlistRemoveAction.item : null302 }
304 multiselectable: true303
305 reorderable: songStackPage.line1 === i18n.tr("Playlist")304 Label {
306 rightSideActions: [305 id: trackArtist
307 AddToQueue {306 color: styleMusic.common.subtitle
308307 fontSize: "x-small"
309 },308 text: model.author
310 AddToPlaylist {309 }
311310 }
312 }311 height: units.gu(6)
313 ]312
314313 leftSideAction: songStackPage.line1 === i18n.tr("Playlist")
315 onItemClicked: {314 ? playlistRemoveAction.item : null
316 trackClicked(albumtrackslist.model, index) // play track315 multiselectable: true
317316 reorderable: songStackPage.line1 === i18n.tr("Playlist")
318 if (isAlbum && songStackPage.line1 !== i18n.tr("Genre")) {317 rightSideActions: [
319 Library.addRecent(albumtrackslist.model.get(0, albumtrackslist.model.RoleModelData).album, "album")318 AddToQueue {
320 } else if (songStackPage.line1 === i18n.tr("Playlist")) {319
321 Library.addRecent(songStackPage.line2, "playlist")320 },
322 } else {321 AddToPlaylist {
323 console.debug("Unknown type to add to recent")322
324 }323 }
325324 ]
326 recentModel.filterRecent()325
327 }326 onItemClicked: {
328 onReorder: {327 trackClicked(albumtrackslist.model, index) // play track
329 console.debug("Move: ", from, to);328
330329 if (isAlbum && songStackPage.line1 !== i18n.tr("Genre")) {
331 Playlists.move(songStackPage.line2, from, to)330 Library.addRecent(albumtrackslist.model.get(0, albumtrackslist.model.RoleModelData).album, "album")
332331 } else if (songStackPage.line1 === i18n.tr("Playlist")) {
333 albumTracksModel.filterPlaylistTracks(songStackPage.line2)332 Library.addRecent(songStackPage.line2, "playlist")
334 }333 } else {
335334 console.debug("Unknown type to add to recent")
336 Loader {335 }
337 id: playlistRemoveAction336
338 sourceComponent: Remove {337 recentModel.filterRecent()
339 onTriggered: {338 }
340 Playlists.removeFromPlaylist(songStackPage.line2, model.i)339 onReorder: {
341340 console.debug("Move: ", from, to);
342 playlistChangedHelper() // update recent/playlist models341
343342 Playlists.move(songStackPage.line2, from, to)
344 albumTracksModel.filterPlaylistTracks(songStackPage.line2)343
345344 albumTracksModel.filterPlaylistTracks(songStackPage.line2)
346 // refresh cover art345 }
347 songStackPage.covers = Playlists.getPlaylistCovers(songStackPage.line2)346
348 }347 Loader {
349 }348 id: playlistRemoveAction
350 }349 sourceComponent: Remove {
351350 onTriggered: {
352 MusicRow {351 Playlists.removeFromPlaylist(songStackPage.line2, model.i)
353 id: musicRow352
354 height: parent.height353 playlistChangedHelper() // update recent/playlist models
355 column: Column {354
356 Label {355 albumTracksModel.filterPlaylistTracks(songStackPage.line2)
357 id: trackTitle356
358 color: styleMusic.common.music357 // refresh cover art
359 fontSize: "small"358 songStackPage.covers = Playlists.getPlaylistCovers(songStackPage.line2)
360 objectName: "songspage-tracktitle"359 }
361 text: model.title360 }
362 }361 }
363362
364 Label {363 Component.onCompleted: {
365 id: trackArtist364 if (model.date !== undefined)
366 color: styleMusic.common.subtitle365 {
367 fontSize: "x-small"366 songStackPage.file = model.filename;
368 text: model.author367 songStackPage.year = new Date(model.date).toLocaleString(Qt.locale(),'yyyy');
369 }
370 }
371 }
372
373 Component.onCompleted: {
374 if (model.date !== undefined)
375 {
376 songStackPage.file = model.filename;
377 songStackPage.year = new Date(model.date).toLocaleString(Qt.locale(),'yyyy');
378 }
379 }368 }
380 }369 }
381 }370 }
382371
=== modified file 'tests/autopilot/music_app/__init__.py'
--- tests/autopilot/music_app/__init__.py 2015-02-04 04:20:07 +0000
+++ tests/autopilot/music_app/__init__.py 2015-02-22 02:24:31 +0000
@@ -187,7 +187,7 @@
187 self.visible.wait_for(True)187 self.visible.wait_for(True)
188188
189 def get_track(self, i):189 def get_track(self, i):
190 return (self.wait_select_single(ListItemWithActions,190 return (self.wait_select_single(MusicListItem,
191 objectName="tracksPageListItem" + str(i)))191 objectName="tracksPageListItem" + str(i)))
192192
193193
@@ -270,7 +270,7 @@
270270
271 @ensure_now_playing_list271 @ensure_now_playing_list
272 def get_track(self, i):272 def get_track(self, i):
273 return (self.wait_select_single(ListItemWithActions,273 return (self.wait_select_single(MusicListItem,
274 objectName="nowPlayingListItem" + str(i)))274 objectName="nowPlayingListItem" + str(i)))
275275
276 @ensure_now_playing_full276 @ensure_now_playing_full
@@ -331,7 +331,7 @@
331 objectName="songsPageHeaderAlbumArtist")331 objectName="songsPageHeaderAlbumArtist")
332332
333 def get_track(self, i):333 def get_track(self, i):
334 return (self.wait_select_single(ListItemWithActions,334 return (self.wait_select_single(MusicListItem,
335 objectName="songsPageListItem" + str(i)))335 objectName="songsPageListItem" + str(i)))
336336
337337
@@ -358,7 +358,7 @@
358 now_playing_page.visible.wait_for(True)358 now_playing_page.visible.wait_for(True)
359359
360360
361class ListItemWithActions(UbuntuUIToolkitCustomProxyObjectBase):361class MusicListItem(UbuntuUIToolkitCustomProxyObjectBase):
362 @click_object362 @click_object
363 def click_add_to_playlist_action(self):363 def click_add_to_playlist_action(self):
364 return self.wait_select_single(objectName="addToPlaylistAction")364 return self.wait_select_single(objectName="addToPlaylistAction")

Subscribers

People subscribed via source and target branches