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
1=== modified file 'app/components/CMakeLists.txt'
2--- app/components/CMakeLists.txt 2015-02-08 04:06:28 +0000
3+++ app/components/CMakeLists.txt 2015-02-22 02:24:31 +0000
4@@ -1,3 +1,4 @@
5+add_subdirectory(Delegates)
6 add_subdirectory(Dialog)
7 add_subdirectory(HeadState)
8 add_subdirectory(Flickables)
9
10=== added directory 'app/components/Delegates'
11=== added file 'app/components/Delegates/CMakeLists.txt'
12--- app/components/Delegates/CMakeLists.txt 1970-01-01 00:00:00 +0000
13+++ app/components/Delegates/CMakeLists.txt 2015-02-22 02:24:31 +0000
14@@ -0,0 +1,4 @@
15+# make the qml files visible on qtcreator
16+file(GLOB DELEGATES_QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml)
17+
18+add_custom_target(com_ubuntu_music_DELEGATES_QMLFiles ALL SOURCES ${DELEGATES_QML_FILES})
19
20=== renamed file 'app/components/Card.qml' => 'app/components/Delegates/Card.qml'
21--- app/components/Card.qml 2014-11-11 18:08:40 +0000
22+++ app/components/Delegates/Card.qml 2015-02-22 02:24:31 +0000
23@@ -17,6 +17,7 @@
24
25 import QtQuick 2.3
26 import Ubuntu.Components 1.1
27+import "../"
28
29
30 Item {
31
32=== renamed file 'app/components/ListItemWithActions.qml' => 'app/components/Delegates/ListItemWithActions.qml'
33--- app/components/ListItemWithActions.qml 2015-01-18 00:06:06 +0000
34+++ app/components/Delegates/ListItemWithActions.qml 2015-02-22 02:24:31 +0000
35@@ -45,11 +45,8 @@
36 readonly property alias swipping: mainItemMoving.running
37 readonly property bool _showActions: mouseArea.pressed || swipeState != "Normal" || swipping
38
39- property bool reorderable: false // CUSTOM
40- property bool multiselectable: false // CUSTOM
41-
42- property int previousListItemIndex: -1 // CUSTOM
43- property int listItemIndex: index // CUSTOM
44+ property alias _main: main // CUSTOM
45+ property alias pressed: mouseArea.pressed // CUSTOM
46
47 /* internal */
48 property var _visibleRightSideActions: filterVisibleActions(rightSideActions)
49@@ -57,55 +54,6 @@
50 signal itemClicked(var mouse)
51 signal itemPressAndHold(var mouse)
52
53- signal reorder(int from, int to) // CUSTOM
54-
55- onItemPressAndHold: {
56- if (multiselectable) {
57- selectionMode = true
58- }
59- }
60- onListItemIndexChanged: {
61- var i = parent.parent.selectedItems.lastIndexOf(previousListItemIndex)
62-
63- if (i !== -1) {
64- parent.parent.selectedItems[i] = listItemIndex
65- }
66-
67- previousListItemIndex = listItemIndex
68- }
69-
70- onSelectedChanged: {
71- if (selectionMode) {
72- var tmp = parent.parent.selectedItems
73-
74- if (selected) {
75- if (parent.parent.selectedItems.indexOf(listItemIndex) === -1) {
76- tmp.push(listItemIndex)
77- parent.parent.selectedItems = tmp
78- }
79- } else {
80- tmp.splice(parent.parent.selectedItems.indexOf(listItemIndex), 1)
81- parent.parent.selectedItems = tmp
82- }
83- }
84- }
85-
86- onSelectionModeChanged: { // CUSTOM
87- if (reorderable && selectionMode) {
88- resetSwipe()
89- }
90-
91- for (var j=0; j < main.children.length; j++) {
92- main.children[j].anchors.rightMargin = reorderable && selectionMode ? actionReorderLoader.width + units.gu(2) : 0
93- }
94-
95- parent.parent.state = selectionMode ? "multiselectable" : "normal"
96-
97- if (!selectionMode) {
98- selected = false
99- }
100- }
101-
102 function returnToBoundsRTL(direction)
103 {
104 var actionFullWidth = actionWidth + units.gu(2)
105@@ -122,20 +70,11 @@
106 var xOffset = Math.abs(main.x)
107 var index = Math.min(Math.floor(xOffset / actionFullWidth), _visibleRightSideActions.length)
108 var newX = 0
109- var j // CUSTOM
110
111 if (index === _visibleRightSideActions.length) {
112 newX = -(rightActionsView.width - units.gu(2))
113-
114- for (j=0; j < rightSideActions.length; j++) { // CUSTOM
115- rightActionsRepeater.itemAt(j).primed = true
116- }
117 } else if (index >= 1) {
118 newX = -(actionFullWidth * index)
119-
120- for (j=0; j < rightSideActions.length; j++) { // CUSTOM
121- rightActionsRepeater.itemAt(j).primed = j === index
122- }
123 }
124
125 updatePosition(newX)
126@@ -147,10 +86,6 @@
127 if ((direction === "RTL") || (main.x <= (finalX * root.threshold)))
128 finalX = 0
129 updatePosition(finalX)
130-
131- if (leftSideAction !== null) { // CUSTOM
132- leftActionViewLoader.item.primed = main.x > (finalX * root.threshold)
133- }
134 }
135
136 function returnToBounds(direction)
137@@ -206,18 +141,6 @@
138 }
139 }
140
141- function resetPrimed() // CUSTOM
142- {
143- if (leftSideAction !== null) {
144- leftActionViewLoader.item.primed = false
145- }
146-
147- for (var j=0; j < rightSideActions.length; j++) {
148- console.debug(rightActionsRepeater.itemAt(j));
149- rightActionsRepeater.itemAt(j).primed = false
150- }
151- }
152-
153 function resetSwipe()
154 {
155 updatePosition(0)
156@@ -243,39 +166,6 @@
157 mouseArea.state = ""
158 }
159 main.x = pos
160-
161- if (pos === 0) { // CUSTOM
162- //resetPrimed()
163- }
164- }
165-
166- Connections { // CUSTOM
167- target: mainView
168- onListItemSwiping: {
169- if (i !== index) {
170- root.resetSwipe();
171- }
172- }
173- }
174-
175- Connections { // CUSTOM
176- target: root.parent.parent
177- onClearSelection: selected = false
178- onFlickingChanged: {
179- if (root.parent.parent.flicking) {
180- root.resetSwipe()
181- }
182- }
183- onSelectAll: selected = true
184- onStateChanged: selectionMode = root.parent.parent.state === "multiselectable"
185- }
186-
187- Component.onCompleted: { // CUSTOM
188- if (parent.parent.selectedItems.indexOf(index) !== -1) { // FIXME:
189- selected = true
190- }
191-
192- selectionMode = root.parent.parent.state === "multiselectable"
193 }
194
195 // CUSTOM remove animation
196@@ -301,7 +191,7 @@
197 when: selectionMode || selected
198 PropertyChanges {
199 target: selectionIcon
200- source: Qt.resolvedUrl("ListItemActions/CheckBox.qml")
201+ source: Qt.resolvedUrl("../ListItemActions/CheckBox.qml")
202 anchors.leftMargin: units.gu(2)
203 }
204 PropertyChanges {
205@@ -337,8 +227,6 @@
206 width: root.leftActionWidth + actionThreshold
207 color: UbuntuColors.red
208
209- property alias primed: leftActionIcon.primed // CUSTOM
210-
211 Icon {
212 id: leftActionIcon
213 anchors {
214@@ -350,8 +238,6 @@
215 color: Theme.palette.selected.field
216 height: units.gu(3)
217 width: units.gu(3)
218-
219- property bool primed: false // CUSTOM
220 }
221 }
222 }
223@@ -400,8 +286,6 @@
224 height: rightActionsView.height
225 width: root.actionWidth
226
227- property alias primed: img.primed // CUSTOM
228-
229 Icon {
230 id: img
231
232@@ -411,8 +295,6 @@
233 height: units.gu(3)
234 name: modelData.iconName
235 color: root.activeAction === modelData ? UbuntuColors.blue : styleMusic.common.white // CUSTOM
236-
237- property bool primed: false // CUSTOM
238 }
239 }
240 }
241@@ -474,118 +356,6 @@
242 }
243 }
244
245- /* CUSTOM Brighten Component */
246- Rectangle {
247- id: listItemBrighten
248- anchors {
249- fill: main
250- }
251-
252- color: mouseArea.pressed ? styleMusic.common.white : "transparent"
253- opacity: 0.1
254- }
255-
256- /* CUSTOM Reorder Component */
257- Loader {
258- id: actionReorderLoader
259- anchors {
260- bottom: parent.bottom
261- right: main.right
262- rightMargin: units.gu(1)
263- top: parent.top
264- }
265- asynchronous: true
266- sourceComponent: reorderable && selectionMode && root.parent.parent.selectedItems.length === 0 ? actionReorderComponent : undefined
267- }
268-
269- Component {
270- id: actionReorderComponent
271- Item {
272- id: actionReorder
273- width: units.gu(4)
274- visible: reorderable && selectionMode && root.parent.parent.selectedItems.length === 0
275-
276- Icon {
277- anchors {
278- horizontalCenter: parent.horizontalCenter
279- verticalCenter: parent.verticalCenter
280- }
281- name: "navigation-menu" // TODO: use proper image
282- height: width
283- width: units.gu(3)
284- }
285-
286- MouseArea {
287- id: actionReorderMouseArea
288- anchors {
289- fill: parent
290- }
291- property int startY: 0
292- property int startContentY: 0
293-
294- onPressed: {
295- root.parent.parent.interactive = false; // stop scrolling of listview
296- startY = root.y;
297- startContentY = root.parent.parent.contentY;
298- root.z += 10; // force ontop of other elements
299-
300- console.debug("Reorder listitem pressed", root.y)
301- }
302- onMouseYChanged: root.y += mouse.y - (root.height / 2);
303- onReleased: {
304- console.debug("Reorder diff by position", getDiff());
305-
306- var diff = getDiff();
307-
308- // Remove the height of the actual item if moved down
309- if (diff > 0) {
310- diff -= 1;
311- }
312-
313- root.parent.parent.interactive = true; // reenable scrolling
314-
315- if (diff === 0) {
316- // Nothing has changed so reset the item
317- // z index is restored after animation
318- resetListItemYAnimation.start();
319- }
320- else {
321- var newIndex = index + diff;
322-
323- if (newIndex < 0) {
324- newIndex = 0;
325- }
326- else if (newIndex > root.parent.parent.count - 1) {
327- newIndex = root.parent.parent.count - 1;
328- }
329-
330- root.z -= 10; // restore z index
331- reorder(index, newIndex)
332- }
333- }
334-
335- function getDiff() {
336- // Get the amount of items that have been passed over (by centre)
337- return Math.round((((root.y - startY) + (root.parent.parent.contentY - startContentY)) / root.height) + 0.5);
338- }
339- }
340-
341- SequentialAnimation {
342- id: resetListItemYAnimation
343- UbuntuNumberAnimation {
344- target: root;
345- property: "y";
346- to: actionReorderMouseArea.startY
347- }
348- ScriptAction {
349- script: {
350- root.z -= 10; // restore z index
351- }
352- }
353- }
354- }
355- }
356-
357 SequentialAnimation {
358 id: triggerAction
359
360@@ -694,22 +464,22 @@
361 direction = "None"
362 }
363 onClicked: {
364- if (selectionMode) { // CUSTOM
365+ if (selectionMode) { // CUSTOM - selecting a listitem should toggle selection if in selectionMode
366 selected = !selected
367 return
368- }
369- else if (main.x === 0) {
370+ } else if (main.x === 0) {
371 root.itemClicked(mouse)
372 } else if (main.x > 0) {
373 var action = getActionAt(Qt.point(mouse.x, mouse.y))
374 if (action && action !== -1) {
375 //action.triggered(root)
376- removeAnimation.action = action // CUSTOM
377+ removeAnimation.action = action // CUSTOM - use our animation instead
378 removeAnimation.start() // CUSTOM
379 }
380 } else {
381 var actionIndex = getActionAt(Qt.point(mouse.x, mouse.y))
382- if (actionIndex !== -1) {
383+
384+ if (actionIndex !== -1 && actionIndex !== leftSideAction) { // CUSTOM - can be leftAction
385 root.activeItem = rightActionsRepeater.itemAt(actionIndex)
386 root.activeAction = root.rightSideActions[actionIndex]
387 triggerAction.start()
388@@ -723,7 +493,7 @@
389 if (mouseArea.pressed) {
390 updateActiveAction()
391
392- listItemSwiping(index) // CUSTOM
393+ listItemSwiping(index) // CUSTOM - tells other listitems to dismiss any swipe
394 }
395 }
396 onPressAndHold: {
397
398=== added file 'app/components/Delegates/MusicListItem.qml'
399--- app/components/Delegates/MusicListItem.qml 1970-01-01 00:00:00 +0000
400+++ app/components/Delegates/MusicListItem.qml 2015-02-22 02:24:31 +0000
401@@ -0,0 +1,151 @@
402+/*
403+ * Copyright (C) 2013, 2014, 2015
404+ * Andrew Hayzen <ahayzen@gmail.com>
405+ * Nekhelesh Ramananthan <krnekhelesh@gmail.com>
406+ * Victor Thompson <victor.thompson@gmail.com>
407+ *
408+ * This program is free software; you can redistribute it and/or modify
409+ * it under the terms of the GNU General Public License as published by
410+ * the Free Software Foundation; version 3.
411+ *
412+ * This program is distributed in the hope that it will be useful,
413+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
414+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
415+ * GNU General Public License for more details.
416+ *
417+ * You should have received a copy of the GNU General Public License
418+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
419+ */
420+
421+import QtQuick 2.3
422+import Ubuntu.Components 1.1
423+import "../"
424+
425+
426+ListItemWithActions {
427+ id: root
428+
429+ property alias column: musicRow.column
430+ property alias imageSource: musicRow.imageSource
431+
432+ property int listItemIndex: index
433+ property bool multiselectable: false
434+ property int previousListItemIndex: -1
435+ property bool reorderable: false
436+
437+ signal reorder(int from, int to)
438+
439+ onItemPressAndHold: {
440+ if (multiselectable) {
441+ selectionMode = true
442+ }
443+ }
444+
445+ onListItemIndexChanged: {
446+ var i = parent.parent.selectedItems.lastIndexOf(previousListItemIndex)
447+
448+ if (i !== -1) {
449+ parent.parent.selectedItems[i] = listItemIndex
450+ }
451+
452+ previousListItemIndex = listItemIndex
453+ }
454+
455+ onSelectedChanged: {
456+ if (selectionMode) {
457+ var tmp = parent.parent.selectedItems
458+
459+ if (selected) {
460+ if (parent.parent.selectedItems.indexOf(listItemIndex) === -1) {
461+ tmp.push(listItemIndex)
462+ parent.parent.selectedItems = tmp
463+ }
464+ } else {
465+ tmp.splice(parent.parent.selectedItems.indexOf(listItemIndex), 1)
466+ parent.parent.selectedItems = tmp
467+ }
468+ }
469+ }
470+
471+ onSelectionModeChanged: {
472+ if (reorderable && selectionMode) {
473+ resetSwipe()
474+ }
475+
476+ for (var j=0; j < _main.children.length; j++) {
477+ if (_main.children[j] !== actionReorderLoader) {
478+ _main.children[j].anchors.rightMargin = reorderable && selectionMode ? actionReorderLoader.width + units.gu(2) : 0
479+ }
480+ }
481+
482+ parent.parent.state = selectionMode ? "multiselectable" : "normal"
483+
484+ if (!selectionMode) {
485+ selected = false
486+ }
487+ }
488+
489+ /* Highlight the listitem on press */
490+ Rectangle {
491+ id: listItemBrighten
492+ color: root.pressed ? styleMusic.common.white : "transparent"
493+ opacity: 0.1
494+ height: root.height
495+ x: root.x - parent.x // -parent.x due to selectionIcon in ListItemWithActions
496+ width: root.width
497+ }
498+
499+ /* Reorder Component */
500+ Loader {
501+ id: actionReorderLoader
502+ active: reorderable && selectionMode && root.parent.parent.selectedItems.length === 0
503+ anchors {
504+ bottom: parent.bottom
505+ right: parent.right
506+ rightMargin: units.gu(1)
507+ top: parent.top
508+ }
509+ asynchronous: true
510+ source: "../ListItemReorderComponent.qml"
511+ }
512+
513+ Item {
514+ Connections { // Only allow one ListItem to be swiping at any time
515+ target: mainView
516+ onListItemSwiping: {
517+ if (i !== index) {
518+ root.resetSwipe();
519+ }
520+ }
521+ }
522+
523+ Connections { // Connections from signals in the ListView
524+ target: root.parent.parent
525+ onClearSelection: selected = false
526+ onFlickingChanged: {
527+ if (root.parent.parent.flicking) {
528+ root.resetSwipe()
529+ }
530+ }
531+ onSelectAll: selected = true
532+ onStateChanged: selectionMode = root.parent.parent.state === "multiselectable"
533+ }
534+ }
535+
536+
537+ MusicRow {
538+ id: musicRow
539+ anchors {
540+ verticalCenter: parent.verticalCenter
541+ }
542+ height: parent.height
543+ }
544+
545+ Component.onCompleted: { // reload settings as delegates are destroyed
546+ if (parent.parent.selectedItems.indexOf(index) !== -1) {
547+ selected = true
548+ }
549+
550+ selectionMode = root.parent.parent.state === "multiselectable"
551+ }
552+}
553
554=== modified file 'app/components/ListItemActions/AddToPlaylist.qml'
555--- app/components/ListItemActions/AddToPlaylist.qml 2015-02-04 19:01:14 +0000
556+++ app/components/ListItemActions/AddToPlaylist.qml 2015-02-22 02:24:31 +0000
557@@ -25,8 +25,6 @@
558 objectName: "addToPlaylistAction"
559 text: i18n.tr("Add to playlist")
560
561- property bool primed: false
562-
563 onTriggered: {
564 console.debug("Debug: Add track to playlist");
565
566
567=== modified file 'app/components/ListItemActions/Remove.qml'
568--- app/components/ListItemActions/Remove.qml 2014-09-20 10:50:45 +0000
569+++ app/components/ListItemActions/Remove.qml 2015-02-22 02:24:31 +0000
570@@ -25,6 +25,4 @@
571 iconName: "delete"
572 objectName: "swipeDeleteAction"
573 text: i18n.tr("Remove")
574-
575- property bool primed: false
576 }
577
578=== added file 'app/components/ListItemReorderComponent.qml'
579--- app/components/ListItemReorderComponent.qml 1970-01-01 00:00:00 +0000
580+++ app/components/ListItemReorderComponent.qml 2015-02-22 02:24:31 +0000
581@@ -0,0 +1,106 @@
582+/*
583+ * Copyright (C) 2013, 2014, 2015
584+ * Andrew Hayzen <ahayzen@gmail.com>
585+ * Nekhelesh Ramananthan <krnekhelesh@gmail.com>
586+ * Victor Thompson <victor.thompson@gmail.com>
587+ *
588+ * This program is free software; you can redistribute it and/or modify
589+ * it under the terms of the GNU General Public License as published by
590+ * the Free Software Foundation; version 3.
591+ *
592+ * This program is distributed in the hope that it will be useful,
593+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
594+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
595+ * GNU General Public License for more details.
596+ *
597+ * You should have received a copy of the GNU General Public License
598+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
599+ */
600+
601+import QtQuick 2.3
602+import Ubuntu.Components 1.1
603+
604+
605+Item {
606+ id: actionReorder
607+ width: units.gu(4)
608+
609+ Icon {
610+ anchors {
611+ horizontalCenter: parent.horizontalCenter
612+ verticalCenter: parent.verticalCenter
613+ }
614+ name: "navigation-menu" // TODO: use proper image
615+ height: width
616+ width: units.gu(3)
617+ }
618+
619+ MouseArea {
620+ id: actionReorderMouseArea
621+ anchors {
622+ fill: parent
623+ }
624+ property int startY: 0
625+ property int startContentY: 0
626+
627+ onPressed: {
628+ root.parent.parent.interactive = false; // stop scrolling of listview
629+ startY = root.y;
630+ startContentY = root.parent.parent.contentY;
631+ root.z += 10; // force ontop of other elements
632+
633+ console.debug("Reorder listitem pressed", root.y)
634+ }
635+ onMouseYChanged: root.y += mouse.y - (root.height / 2);
636+ onReleased: {
637+ console.debug("Reorder diff by position", getDiff());
638+
639+ var diff = getDiff();
640+
641+ // Remove the height of the actual item if moved down
642+ if (diff > 0) {
643+ diff -= 1;
644+ }
645+
646+ root.parent.parent.interactive = true; // reenable scrolling
647+
648+ if (diff === 0) {
649+ // Nothing has changed so reset the item
650+ // z index is restored after animation
651+ resetListItemYAnimation.start();
652+ }
653+ else {
654+ var newIndex = index + diff;
655+
656+ if (newIndex < 0) {
657+ newIndex = 0;
658+ }
659+ else if (newIndex > root.parent.parent.count - 1) {
660+ newIndex = root.parent.parent.count - 1;
661+ }
662+
663+ root.z -= 10; // restore z index
664+ reorder(index, newIndex)
665+ }
666+ }
667+
668+ function getDiff() {
669+ // Get the amount of items that have been passed over (by centre)
670+ return Math.round((((root.y - startY) + (root.parent.parent.contentY - startContentY)) / root.height) + 0.5);
671+ }
672+ }
673+
674+ SequentialAnimation {
675+ id: resetListItemYAnimation
676+ UbuntuNumberAnimation {
677+ target: root;
678+ property: "y";
679+ to: actionReorderMouseArea.startY
680+ }
681+ ScriptAction {
682+ script: {
683+ root.z -= 10; // restore z index
684+ }
685+ }
686+ }
687+}
688
689=== modified file 'app/ui/AddToPlaylist.qml'
690--- app/ui/AddToPlaylist.qml 2015-02-08 04:06:28 +0000
691+++ app/ui/AddToPlaylist.qml 2015-02-22 02:24:31 +0000
692@@ -26,6 +26,7 @@
693 import "../logic/meta-database.js" as Library
694 import "../logic/playlists.js" as Playlists
695 import "../components"
696+import "../components/Delegates"
697 import "../components/Flickables"
698 import "../components/HeadState"
699
700
701=== modified file 'app/ui/Albums.qml'
702--- app/ui/Albums.qml 2015-02-08 04:06:28 +0000
703+++ app/ui/Albums.qml 2015-02-22 02:24:31 +0000
704@@ -21,6 +21,7 @@
705 import Ubuntu.Components 1.1
706 import Ubuntu.MediaScanner 0.1
707 import "../components"
708+import "../components/Delegates"
709 import "../components/Flickables"
710 import "../components/HeadState"
711
712
713=== modified file 'app/ui/ArtistView.qml'
714--- app/ui/ArtistView.qml 2015-02-08 04:06:28 +0000
715+++ app/ui/ArtistView.qml 2015-02-22 02:24:31 +0000
716@@ -26,6 +26,7 @@
717 import QtQuick.LocalStorage 2.0
718 import "../logic/meta-database.js" as Library
719 import "../components"
720+import "../components/Delegates"
721 import "../components/Flickables"
722 import "../components/ViewButton"
723
724
725=== modified file 'app/ui/Artists.qml'
726--- app/ui/Artists.qml 2015-02-08 04:06:28 +0000
727+++ app/ui/Artists.qml 2015-02-22 02:24:31 +0000
728@@ -27,6 +27,7 @@
729 import "../logic/meta-database.js" as Library
730 import "../logic/playlists.js" as Playlists
731 import "../components"
732+import "../components/Delegates"
733 import "../components/Flickables"
734 import "../components/HeadState"
735
736
737=== modified file 'app/ui/Genres.qml'
738--- app/ui/Genres.qml 2015-02-08 04:06:28 +0000
739+++ app/ui/Genres.qml 2015-02-22 02:24:31 +0000
740@@ -21,6 +21,7 @@
741 import Ubuntu.Components 1.1
742 import Ubuntu.MediaScanner 0.1
743 import "../components"
744+import "../components/Delegates"
745 import "../components/Flickables"
746 import "../components/HeadState"
747
748
749=== modified file 'app/ui/NowPlaying.qml'
750--- app/ui/NowPlaying.qml 2015-02-08 04:06:28 +0000
751+++ app/ui/NowPlaying.qml 2015-02-22 02:24:31 +0000
752@@ -23,6 +23,7 @@
753 import Ubuntu.Components 1.1
754 import Ubuntu.Thumbnailer 0.1
755 import "../components"
756+import "../components/Delegates"
757 import "../components/Flickables"
758 import "../components/HeadState"
759 import "../components/ListItemActions"
760@@ -378,7 +379,6 @@
761 fill: parent
762 topMargin: units.gu(2)
763 }
764- delegate: queueDelegate
765 footer: Item {
766 height: mainView.height - (styleMusic.common.expandHeight + queueList.currentHeight) + units.gu(8)
767 }
768@@ -390,95 +390,62 @@
769
770 onCountChanged: customdebug("Queue: Now has: " + queueList.count + " tracks")
771
772- Component {
773- id: queueDelegate
774- ListItemWithActions {
775- id: queueListItem
776- color: player.currentIndex === index ? "#2c2c34" : styleMusic.mainView.backgroundColor
777- height: queueList.normalHeight
778- objectName: "nowPlayingListItem" + index
779- state: ""
780-
781- leftSideAction: Remove {
782- onTriggered: trackQueue.removeQueueList([index])
783- }
784- multiselectable: true
785- reorderable: true
786- rightSideActions: [
787- AddToPlaylist{
788-
789- }
790- ]
791-
792- onItemClicked: {
793- customdebug("File: " + model.filename) // debugger
794- trackQueueClick(index); // toggle track state
795- }
796- onReorder: {
797- console.debug("Move: ", from, to);
798-
799- trackQueue.model.move(from, to, 1);
800- Library.moveQueueItem(from, to);
801-
802- // Maintain currentIndex with current song
803- if (from === player.currentIndex) {
804- player.currentIndex = to;
805- }
806- else if (from < player.currentIndex && to >= player.currentIndex) {
807- player.currentIndex -= 1;
808- }
809- else if (from > player.currentIndex && to <= player.currentIndex) {
810- player.currentIndex += 1;
811- }
812-
813- queueIndex = player.currentIndex
814- }
815-
816- Item {
817- id: trackContainer;
818- anchors {
819- fill: parent
820- }
821-
822- NumberAnimation {
823- id: trackContainerReorderAnimation
824- target: trackContainer;
825- property: "anchors.leftMargin";
826- duration: queueList.transitionDuration;
827- to: units.gu(2)
828- }
829-
830- NumberAnimation {
831- id: trackContainerResetAnimation
832- target: trackContainer;
833- property: "anchors.leftMargin";
834- duration: queueList.transitionDuration;
835- to: units.gu(0.5)
836- }
837-
838- MusicRow {
839- id: musicRow
840- height: parent.height
841- column: Column {
842- Label {
843- id: trackTitle
844- color: player.currentIndex === index ? UbuntuColors.blue
845- : styleMusic.common.music
846- fontSize: "small"
847- objectName: "titleLabel"
848- text: model.title
849- }
850-
851- Label {
852- id: trackArtist
853- color: styleMusic.common.subtitle
854- fontSize: "x-small"
855- objectName: "artistLabel"
856- text: model.author
857- }
858- }
859- }
860- }
861+ delegate: MusicListItem {
862+ id: queueListItem
863+ color: player.currentIndex === index ? "#2c2c34" : styleMusic.mainView.backgroundColor
864+ column: Column {
865+ Label {
866+ id: trackTitle
867+ color: player.currentIndex === index ? UbuntuColors.blue : styleMusic.common.music
868+ fontSize: "small"
869+ objectName: "titleLabel"
870+ text: model.title
871+ }
872+
873+ Label {
874+ id: trackArtist
875+ color: styleMusic.common.subtitle
876+ fontSize: "x-small"
877+ objectName: "artistLabel"
878+ text: model.author
879+ }
880+ }
881+ height: queueList.normalHeight
882+ objectName: "nowPlayingListItem" + index
883+ state: ""
884+ leftSideAction: Remove {
885+ onTriggered: trackQueue.removeQueueList([index])
886+ }
887+ multiselectable: true
888+ reorderable: true
889+ rightSideActions: [
890+ AddToPlaylist{
891+
892+ }
893+ ]
894+
895+ onItemClicked: {
896+ customdebug("File: " + model.filename) // debugger
897+ trackQueueClick(index); // toggle track state
898+ }
899+ onReorder: {
900+ console.debug("Move: ", from, to);
901+
902+ trackQueue.model.move(from, to, 1);
903+ Library.moveQueueItem(from, to);
904+
905+ // Maintain currentIndex with current song
906+ if (from === player.currentIndex) {
907+ player.currentIndex = to;
908+ }
909+ else if (from < player.currentIndex && to >= player.currentIndex) {
910+ player.currentIndex -= 1;
911+ }
912+ else if (from > player.currentIndex && to <= player.currentIndex) {
913+ player.currentIndex += 1;
914+ }
915+
916+ queueIndex = player.currentIndex
917 }
918 }
919 }
920
921=== modified file 'app/ui/Playlists.qml'
922--- app/ui/Playlists.qml 2015-02-08 04:06:28 +0000
923+++ app/ui/Playlists.qml 2015-02-22 02:24:31 +0000
924@@ -25,6 +25,7 @@
925 import QtQuick.LocalStorage 2.0
926 import "../logic/playlists.js" as Playlists
927 import "../components"
928+import "../components/Delegates"
929 import "../components/Flickables"
930 import "../components/HeadState"
931
932
933=== modified file 'app/ui/Recent.qml'
934--- app/ui/Recent.qml 2015-02-08 04:06:28 +0000
935+++ app/ui/Recent.qml 2015-02-22 02:24:31 +0000
936@@ -28,6 +28,7 @@
937 import "../logic/meta-database.js" as Library
938 import "../logic/playlists.js" as Playlists
939 import "../components"
940+import "../components/Delegates"
941 import "../components/Flickables"
942
943 MusicPage {
944
945=== modified file 'app/ui/Songs.qml'
946--- app/ui/Songs.qml 2015-02-08 04:06:28 +0000
947+++ app/ui/Songs.qml 2015-02-22 02:24:31 +0000
948@@ -25,6 +25,7 @@
949 import QtQuick.LocalStorage 2.0
950 import "../logic/playlists.js" as Playlists
951 import "../components"
952+import "../components/Delegates"
953 import "../components/Flickables"
954 import "../components/HeadState"
955 import "../components/ListItemActions"
956@@ -90,61 +91,43 @@
957 }
958 }
959
960- delegate: trackDelegate
961- Component {
962- id: trackDelegate
963-
964- ListItemWithActions {
965- id: track
966- objectName: "tracksPageListItem" + index
967- height: units.gu(7)
968-
969- multiselectable: true
970- rightSideActions: [
971- AddToQueue {
972- },
973- AddToPlaylist {
974-
975- }
976- ]
977-
978- onItemClicked: {
979- if (songsPage.state === "search") { // only play single track when searching
980- trackQueue.clear()
981- trackQueue.append(songsModelFilter.get(index))
982- trackQueueClick(0)
983- } else {
984- trackClicked(songsModelFilter, index) // play track
985- }
986- }
987-
988- MusicRow {
989- id: musicRow
990- anchors {
991- verticalCenter: parent.verticalCenter
992- }
993- imageSource: {"art": model.art}
994- column: Column {
995- Label {
996- id: trackTitle
997- color: styleMusic.common.music
998- fontSize: "small"
999- objectName: "tracktitle"
1000- text: model.title
1001- }
1002-
1003- Label {
1004- id: trackArtist
1005- color: styleMusic.common.subtitle
1006- fontSize: "x-small"
1007- text: model.author
1008- }
1009- }
1010- }
1011-
1012- states: State {
1013- name: "Current"
1014- when: track.ListView.isCurrentItem
1015+ delegate: MusicListItem {
1016+ id: track
1017+ objectName: "tracksPageListItem" + index
1018+ column: Column {
1019+ Label {
1020+ id: trackTitle
1021+ color: styleMusic.common.music
1022+ fontSize: "small"
1023+ objectName: "tracktitle"
1024+ text: model.title
1025+ }
1026+
1027+ Label {
1028+ id: trackArtist
1029+ color: styleMusic.common.subtitle
1030+ fontSize: "x-small"
1031+ text: model.author
1032+ }
1033+ }
1034+ height: units.gu(7)
1035+ imageSource: {"art": model.art}
1036+ multiselectable: true
1037+ rightSideActions: [
1038+ AddToQueue {
1039+ },
1040+ AddToPlaylist {
1041+
1042+ }
1043+ ]
1044+
1045+ onItemClicked: {
1046+ if (songsPage.state === "search") { // only play single track when searching
1047+ trackQueue.clear()
1048+ trackQueue.append(songsModelFilter.get(index))
1049+ trackQueueClick(0)
1050+ } else {
1051+ trackClicked(songsModelFilter, index) // play track
1052 }
1053 }
1054 }
1055
1056=== modified file 'app/ui/SongsView.qml'
1057--- app/ui/SongsView.qml 2015-02-08 04:06:28 +0000
1058+++ app/ui/SongsView.qml 2015-02-22 02:24:31 +0000
1059@@ -27,6 +27,7 @@
1060 import "../logic/meta-database.js" as Library
1061 import "../logic/playlists.js" as Playlists
1062 import "../components"
1063+import "../components/Delegates"
1064 import "../components/Flickables"
1065 import "../components/HeadState"
1066 import "../components/ListItemActions"
1067@@ -186,11 +187,9 @@
1068 anchors {
1069 fill: parent
1070 }
1071- delegate: albumTracksDelegate
1072 model: isAlbum ? songsModel : albumTracksModel.model
1073 objectName: "songspage-listview"
1074 width: parent.width
1075-
1076 header: BlurredHeader {
1077 rightColumn: Column {
1078 spacing: units.gu(2)
1079@@ -290,92 +289,82 @@
1080 }
1081 }
1082 }
1083-
1084- Component {
1085- id: albumTracksDelegate
1086-
1087- ListItemWithActions {
1088- id: track
1089- objectName: "songsPageListItem" + index
1090- height: units.gu(6)
1091-
1092- leftSideAction: songStackPage.line1 === i18n.tr("Playlist")
1093- ? playlistRemoveAction.item : null
1094- multiselectable: true
1095- reorderable: songStackPage.line1 === i18n.tr("Playlist")
1096- rightSideActions: [
1097- AddToQueue {
1098-
1099- },
1100- AddToPlaylist {
1101-
1102- }
1103- ]
1104-
1105- onItemClicked: {
1106- trackClicked(albumtrackslist.model, index) // play track
1107-
1108- if (isAlbum && songStackPage.line1 !== i18n.tr("Genre")) {
1109- Library.addRecent(albumtrackslist.model.get(0, albumtrackslist.model.RoleModelData).album, "album")
1110- } else if (songStackPage.line1 === i18n.tr("Playlist")) {
1111- Library.addRecent(songStackPage.line2, "playlist")
1112- } else {
1113- console.debug("Unknown type to add to recent")
1114- }
1115-
1116- recentModel.filterRecent()
1117- }
1118- onReorder: {
1119- console.debug("Move: ", from, to);
1120-
1121- Playlists.move(songStackPage.line2, from, to)
1122-
1123- albumTracksModel.filterPlaylistTracks(songStackPage.line2)
1124- }
1125-
1126- Loader {
1127- id: playlistRemoveAction
1128- sourceComponent: Remove {
1129- onTriggered: {
1130- Playlists.removeFromPlaylist(songStackPage.line2, model.i)
1131-
1132- playlistChangedHelper() // update recent/playlist models
1133-
1134- albumTracksModel.filterPlaylistTracks(songStackPage.line2)
1135-
1136- // refresh cover art
1137- songStackPage.covers = Playlists.getPlaylistCovers(songStackPage.line2)
1138- }
1139- }
1140- }
1141-
1142- MusicRow {
1143- id: musicRow
1144- height: parent.height
1145- column: Column {
1146- Label {
1147- id: trackTitle
1148- color: styleMusic.common.music
1149- fontSize: "small"
1150- objectName: "songspage-tracktitle"
1151- text: model.title
1152- }
1153-
1154- Label {
1155- id: trackArtist
1156- color: styleMusic.common.subtitle
1157- fontSize: "x-small"
1158- text: model.author
1159- }
1160- }
1161- }
1162-
1163- Component.onCompleted: {
1164- if (model.date !== undefined)
1165- {
1166- songStackPage.file = model.filename;
1167- songStackPage.year = new Date(model.date).toLocaleString(Qt.locale(),'yyyy');
1168- }
1169+ delegate: MusicListItem {
1170+ id: track
1171+ objectName: "songsPageListItem" + index
1172+ column: Column {
1173+ Label {
1174+ id: trackTitle
1175+ color: styleMusic.common.music
1176+ fontSize: "small"
1177+ objectName: "songspage-tracktitle"
1178+ text: model.title
1179+ }
1180+
1181+ Label {
1182+ id: trackArtist
1183+ color: styleMusic.common.subtitle
1184+ fontSize: "x-small"
1185+ text: model.author
1186+ }
1187+ }
1188+ height: units.gu(6)
1189+
1190+ leftSideAction: songStackPage.line1 === i18n.tr("Playlist")
1191+ ? playlistRemoveAction.item : null
1192+ multiselectable: true
1193+ reorderable: songStackPage.line1 === i18n.tr("Playlist")
1194+ rightSideActions: [
1195+ AddToQueue {
1196+
1197+ },
1198+ AddToPlaylist {
1199+
1200+ }
1201+ ]
1202+
1203+ onItemClicked: {
1204+ trackClicked(albumtrackslist.model, index) // play track
1205+
1206+ if (isAlbum && songStackPage.line1 !== i18n.tr("Genre")) {
1207+ Library.addRecent(albumtrackslist.model.get(0, albumtrackslist.model.RoleModelData).album, "album")
1208+ } else if (songStackPage.line1 === i18n.tr("Playlist")) {
1209+ Library.addRecent(songStackPage.line2, "playlist")
1210+ } else {
1211+ console.debug("Unknown type to add to recent")
1212+ }
1213+
1214+ recentModel.filterRecent()
1215+ }
1216+ onReorder: {
1217+ console.debug("Move: ", from, to);
1218+
1219+ Playlists.move(songStackPage.line2, from, to)
1220+
1221+ albumTracksModel.filterPlaylistTracks(songStackPage.line2)
1222+ }
1223+
1224+ Loader {
1225+ id: playlistRemoveAction
1226+ sourceComponent: Remove {
1227+ onTriggered: {
1228+ Playlists.removeFromPlaylist(songStackPage.line2, model.i)
1229+
1230+ playlistChangedHelper() // update recent/playlist models
1231+
1232+ albumTracksModel.filterPlaylistTracks(songStackPage.line2)
1233+
1234+ // refresh cover art
1235+ songStackPage.covers = Playlists.getPlaylistCovers(songStackPage.line2)
1236+ }
1237+ }
1238+ }
1239+
1240+ Component.onCompleted: {
1241+ if (model.date !== undefined)
1242+ {
1243+ songStackPage.file = model.filename;
1244+ songStackPage.year = new Date(model.date).toLocaleString(Qt.locale(),'yyyy');
1245 }
1246 }
1247 }
1248
1249=== modified file 'tests/autopilot/music_app/__init__.py'
1250--- tests/autopilot/music_app/__init__.py 2015-02-04 04:20:07 +0000
1251+++ tests/autopilot/music_app/__init__.py 2015-02-22 02:24:31 +0000
1252@@ -187,7 +187,7 @@
1253 self.visible.wait_for(True)
1254
1255 def get_track(self, i):
1256- return (self.wait_select_single(ListItemWithActions,
1257+ return (self.wait_select_single(MusicListItem,
1258 objectName="tracksPageListItem" + str(i)))
1259
1260
1261@@ -270,7 +270,7 @@
1262
1263 @ensure_now_playing_list
1264 def get_track(self, i):
1265- return (self.wait_select_single(ListItemWithActions,
1266+ return (self.wait_select_single(MusicListItem,
1267 objectName="nowPlayingListItem" + str(i)))
1268
1269 @ensure_now_playing_full
1270@@ -331,7 +331,7 @@
1271 objectName="songsPageHeaderAlbumArtist")
1272
1273 def get_track(self, i):
1274- return (self.wait_select_single(ListItemWithActions,
1275+ return (self.wait_select_single(MusicListItem,
1276 objectName="songsPageListItem" + str(i)))
1277
1278
1279@@ -358,7 +358,7 @@
1280 now_playing_page.visible.wait_for(True)
1281
1282
1283-class ListItemWithActions(UbuntuUIToolkitCustomProxyObjectBase):
1284+class MusicListItem(UbuntuUIToolkitCustomProxyObjectBase):
1285 @click_object
1286 def click_add_to_playlist_action(self):
1287 return self.wait_select_single(objectName="addToPlaylistAction")

Subscribers

People subscribed via source and target branches