Merge lp:~ahayzen/music-app/remix-add-card-view into lp:music-app/remix

Proposed by Andrew Hayzen
Status: Merged
Approved by: Victor Thompson
Approved revision: 653
Merged at revision: 653
Proposed branch: lp:~ahayzen/music-app/remix-add-card-view
Merge into: lp:music-app/remix
Prerequisite: lp:~ahayzen/music-app/remix-remove-blurred-background
Diff against target: 541 lines (+378/-123)
4 files modified
MusicAlbums.qml (+21/-123)
common/Card.qml (+157/-0)
common/CardView.qml (+41/-0)
common/ColumnFlow.qml (+159/-0)
To merge this branch: bzr merge lp:~ahayzen/music-app/remix-add-card-view
Reviewer Review Type Date Requested Status
Victor Thompson Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Andrew Hayzen Abstain
Review via email: mp+236257@code.launchpad.net

Commit message

* Add CardView and Card
* Implement in Albums tab

Description of the change

* Add CardView and Card
* Implement in Albums tab

A few rough edges and hard coded things but wanted an initial review.

To post a comment you must log in.
642. By Andrew Hayzen

* Cleanup old code

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

I ran autopilot on a utopic VM against #642 and it passed.

$ autopilot3 run music_app
Loading tests from: /home/andy/Workspace/sdk/remix-add-card-view/tests/autopilot

Tests running...
<class 'Xlib.protocol.request.QueryExtension'>

Ran 18 tests in 289.682s
OK

643. By Andrew Hayzen

* Remove unused imports

644. By Andrew Hayzen

* Further tidying

645. By Andrew Hayzen

* Tweak overlay opacity

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

Per the design spec, please 1) add more spacing before the album name and after the artist name, 2) verify that design agrees with the font sizes (look a little big), and 3) we need to verify if the card view is to be used for an Albums view.

review: Needs Fixing
646. By Andrew Hayzen

* Change spacing for labels
* Change font sizes

647. By Andrew Hayzen

* Merge of /remix

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

OK so I have fixed the label issues, verified the font size with design and also discussed the pages that will use CardView.

<ahayzen> jounih, for the Albums tab are we expecting to use the cardview?
<jounih> ahayzen: I think we could use the cardview for everything for now. Maybe some of the views would work better as a listview but we can start off with cards for everything
<jounih> what do you think?
<ahayzen> jounih, my understanding was the 'start' page would be cardview... the albums would be cards.... the artists and songs tabs make sense to be a list i think...and the playlists is up for debate
<jounih> ahayzen: playlists definitely cards - albums and playlists are very similar. OK to use listview for artists and songs - i’ll need to provide a design for that, i’ll do it now
<ahayzen> jounih, yep agreed thanks :)

After discussions with nik he recommended to use the Flow {} component to do the staggered grid, so I am going to investigate that and therefore blocking myself for this mp until a decision is made there.

review: Needs Fixing
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
648. By Andrew Hayzen

* Move to using Flow {}

649. By Andrew Hayzen

* Move to ColumnFlow {}

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
Andrew Hayzen (ahayzen) wrote :

Unblocking myself as we have now moved to ColumnFlow {}, please rereview.

review: Abstain
650. By Andrew Hayzen

* Merge of trunk

651. By Andrew Hayzen

* Add upstream location

652. By Andrew Hayzen

* Merge of trunk

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
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Victor Thompson (vthompson) wrote :

See 1 inline comment. Also, I had tried to implement search with this feature and It seems that sometimes the first column would not start at the very top when it reEval's the layout (http://i.imgur.com/f79dNCL.png). I don't think this is critical to fix, but it might be worth looking into later.

review: Needs Information
653. By Andrew Hayzen

* Fix so that when items are removed from the model there are no gaps

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 :

LGTM! This is a very welcomed improvement!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'MusicAlbums.qml'
2--- MusicAlbums.qml 2014-09-20 15:41:33 +0000
3+++ MusicAlbums.qml 2014-10-06 19:09:48 +0000
4@@ -19,37 +19,17 @@
5
6 import QtQuick 2.3
7 import Ubuntu.Components 1.1
8-import Ubuntu.Components.Popups 1.0
9 import Ubuntu.MediaScanner 0.1
10-import Ubuntu.Thumbnailer 0.1
11-import QtMultimedia 5.0
12-import QtQuick.LocalStorage 2.0
13-import QtGraphicalEffects 1.0
14-import "settings.js" as Settings
15-import "playlists.js" as Playlists
16 import "common"
17
18+
19 MusicPage {
20- id: mainpage
21+ id: albumsPage
22 objectName: "albumsPage"
23 title: i18n.tr("Albums")
24
25- // TODO: This ListView is empty and causes the header to get painted with the desired background color because the
26- // page is now vertically flickable.
27- ListView {
28- anchors.fill: parent
29- anchors.bottomMargin: musicToolbar.mouseAreaOffset + musicToolbar.minimizedHeight
30- }
31-
32- GridView {
33- id: albumlist
34- anchors.fill: parent
35- anchors.leftMargin: units.gu(1)
36- anchors.top: parent.top
37- anchors.topMargin: mainView.header.height + units.gu(1)
38- anchors.bottomMargin: units.gu(1)
39- cellHeight: height/3
40- cellWidth: height/3
41+ CardView {
42+ id: albumCardView
43 model: SortFilterModel {
44 id: albumsModelFilter
45 property alias rowCount: albumsModel.rowCount
46@@ -60,106 +40,24 @@
47 sort.property: "title"
48 sort.order: Qt.AscendingOrder
49 }
50-
51- delegate: albumDelegate
52- flow: GridView.TopToBottom
53-
54- Component {
55- id: albumDelegate
56- Item {
57- property string artist: model.artist
58- property string album: model.title
59- property var covers: [{art: model.art}]
60-
61- id: albumItem
62- height: albumlist.cellHeight - units.gu(1)
63- objectName: "albumsPageGridItem" + index
64- width: albumlist.cellHeight - units.gu(1)
65- anchors.margins: units.gu(1)
66-
67- CoverRow {
68- id: albumShape
69- anchors {
70- top: parent.top
71- left: parent.left
72- verticalCenter: parent.verticalCenter
73- }
74- count: albumItem.covers.length
75- size: albumItem.width
76- covers: albumItem.covers
77- spacing: units.gu(2)
78- }
79- Item { // Background so can see text in current state
80- id: albumBg
81- anchors {
82- bottom: parent.bottom
83- left: parent.left
84- right: parent.right
85- }
86- height: units.gu(6)
87- clip: true
88- UbuntuShape{
89- anchors {
90- bottom: parent.bottom
91- left: parent.left
92- right: parent.right
93- }
94- height: albumShape.height
95- radius: "medium"
96- color: styleMusic.common.black
97- opacity: 0.6
98- }
99- }
100- Label {
101- id: albumArtist
102- objectName: "albums-albumartist"
103- anchors.bottom: parent.bottom
104- anchors.bottomMargin: units.gu(1)
105- anchors.left: parent.left
106- anchors.leftMargin: units.gu(1)
107- anchors.right: parent.right
108- anchors.rightMargin: units.gu(1)
109- color: styleMusic.common.white
110- elide: Text.ElideRight
111- text: model.artist
112- fontSize: "x-small"
113- }
114- Label {
115- id: albumLabel
116- anchors.bottom: parent.bottom
117- anchors.bottomMargin: units.gu(3)
118- anchors.left: parent.left
119- anchors.leftMargin: units.gu(1)
120- anchors.right: parent.right
121- anchors.rightMargin: units.gu(1)
122- color: styleMusic.common.white
123- elide: Text.ElideRight
124- text: model.title
125- fontSize: "small"
126- font.weight: Font.DemiBold
127- }
128-
129-
130- MouseArea {
131- anchors.fill: parent
132- onClicked: {
133- songsPage.album = model.title;
134- songsPage.covers = [{art: model.art}]
135- songsPage.genre = undefined
136- songsPage.isAlbum = true
137- songsPage.line1 = model.artist
138- songsPage.line2 = model.title
139- songsPage.title = i18n.tr("Album")
140-
141- mainPageStack.push(songsPage)
142- }
143-
144- // TODO: If http://pad.lv/1354753 is fixed to expose whether the Shape should appear pressed, update this as well.
145- onPressedChanged: albumShape.pressed = pressed
146- }
147+ delegate: Card {
148+ id: albumCard
149+ imageSource: model.art
150+ objectName: "albumsPageGridItem" + index
151+ primaryText: model.title
152+ secondaryText: model.artist
153+
154+ onClicked: {
155+ songsPage.album = model.title;
156+ songsPage.covers = [{art: model.art}]
157+ songsPage.genre = undefined
158+ songsPage.isAlbum = true
159+ songsPage.line1 = model.artist
160+ songsPage.line2 = model.title
161+ songsPage.title = i18n.tr("Album")
162+
163+ mainPageStack.push(songsPage)
164 }
165 }
166 }
167 }
168-
169-
170
171=== added file 'common/Card.qml'
172--- common/Card.qml 1970-01-01 00:00:00 +0000
173+++ common/Card.qml 2014-10-06 19:09:48 +0000
174@@ -0,0 +1,157 @@
175+/*
176+ * Copyright (C) 2014
177+ * Andrew Hayzen <ahayzen@gmail.com>
178+ *
179+ * This program is free software; you can redistribute it and/or modify
180+ * it under the terms of the GNU General Public License as published by
181+ * the Free Software Foundation; version 3.
182+ *
183+ * This program is distributed in the hope that it will be useful,
184+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
185+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
186+ * GNU General Public License for more details.
187+ *
188+ * You should have received a copy of the GNU General Public License
189+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
190+ */
191+
192+import QtQuick 2.3
193+import Ubuntu.Components 1.1
194+
195+
196+Rectangle {
197+ id: card
198+ color: "transparent"
199+ height: cardColumn.childrenRect.height + 2 * bg.anchors.margins
200+
201+ property alias imageSource: image.source
202+ property alias primaryText: primaryLabel.text
203+ property alias secondaryText: secondaryLabel.text
204+
205+ signal clicked(var mouse)
206+ signal pressAndHold(var mouse)
207+
208+ /* Animations */
209+ Behavior on height {
210+ UbuntuNumberAnimation {
211+
212+ }
213+ }
214+
215+ Behavior on width {
216+ UbuntuNumberAnimation {
217+
218+ }
219+ }
220+
221+ Behavior on x {
222+ UbuntuNumberAnimation {
223+
224+ }
225+ }
226+
227+ Behavior on y {
228+ UbuntuNumberAnimation {
229+
230+ }
231+ }
232+
233+ /* Background for card */
234+ Rectangle {
235+ id: bg
236+ anchors {
237+ fill: parent
238+ margins: units.gu(1)
239+ }
240+ color: "#2c2c34"
241+ }
242+
243+ /* Column containing image and labels */
244+ Column {
245+ id: cardColumn
246+ anchors {
247+ fill: bg
248+ }
249+ spacing: units.gu(0.5)
250+
251+ Image {
252+ id: image
253+ height: parent.width
254+ width: parent.width
255+
256+ onStatusChanged: {
257+ if (status === Image.Error) {
258+ source = Qt.resolvedUrl("../images/music-app-cover@30.png")
259+ }
260+ }
261+ }
262+
263+ Rectangle {
264+ color: "transparent"
265+ height: units.gu(1)
266+ width: units.gu(1)
267+ }
268+
269+ Label {
270+ id: primaryLabel
271+ anchors {
272+ left: parent.left
273+ leftMargin: units.gu(1)
274+ right: parent.right
275+ rightMargin: units.gu(1)
276+ }
277+ color: "#FFF"
278+ elide: Text.ElideRight
279+ fontSize: "small"
280+ opacity: 1.0
281+ wrapMode: Text.WordWrap
282+ }
283+
284+ Label {
285+ id: secondaryLabel
286+ anchors {
287+ left: parent.left
288+ leftMargin: units.gu(1)
289+ right: parent.right
290+ rightMargin: units.gu(1)
291+ }
292+ color: "#FFF"
293+ elide: Text.ElideRight
294+ fontSize: "small"
295+ opacity: 0.4
296+ wrapMode: Text.WordWrap
297+ }
298+
299+ Rectangle {
300+ color: "transparent"
301+ height: units.gu(1.5)
302+ width: units.gu(1)
303+ }
304+ }
305+
306+ /* Overlay for when card is pressed */
307+ Rectangle {
308+ id: overlay
309+ anchors {
310+ fill: bg
311+ }
312+ color: "#000"
313+ opacity: 0
314+
315+ Behavior on opacity {
316+ UbuntuNumberAnimation {
317+
318+ }
319+ }
320+ }
321+
322+ /* Capture mouse events */
323+ MouseArea {
324+ anchors {
325+ fill: parent
326+ }
327+ onClicked: card.clicked(mouse)
328+ onPressAndHold: card.pressAndHold(mouse)
329+ onPressedChanged: overlay.opacity = pressed ? 0.3 : 0
330+ }
331+}
332
333=== added file 'common/CardView.qml'
334--- common/CardView.qml 1970-01-01 00:00:00 +0000
335+++ common/CardView.qml 2014-10-06 19:09:48 +0000
336@@ -0,0 +1,41 @@
337+/*
338+ * Copyright (C) 2014
339+ * Andrew Hayzen <ahayzen@gmail.com>
340+ *
341+ * This program is free software; you can redistribute it and/or modify
342+ * it under the terms of the GNU General Public License as published by
343+ * the Free Software Foundation; version 3.
344+ *
345+ * This program is distributed in the hope that it will be useful,
346+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
347+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
348+ * GNU General Public License for more details.
349+ *
350+ * You should have received a copy of the GNU General Public License
351+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
352+ */
353+
354+import QtQuick 2.3
355+import Ubuntu.Components 1.1
356+
357+
358+Flickable {
359+ anchors {
360+ fill: parent
361+ margins: units.gu(1)
362+ }
363+
364+ // dont use flow.contentHeight as it is inaccurate due to height of labels
365+ // changing as they load
366+ contentHeight: flow.childrenRect.height
367+ contentWidth: width
368+
369+ property alias delegate: flow.delegate
370+ property alias model: flow.model
371+
372+ ColumnFlow {
373+ id: flow
374+ columns: parseInt(width / units.gu(15))
375+ width: parent.width
376+ }
377+}
378
379=== added file 'common/ColumnFlow.qml'
380--- common/ColumnFlow.qml 1970-01-01 00:00:00 +0000
381+++ common/ColumnFlow.qml 2014-10-06 19:09:48 +0000
382@@ -0,0 +1,159 @@
383+/*
384+ * Copyright (C) 2014
385+ * Andrew Hayzen <ahayzen@gmail.com>
386+ * Michael Spencer <sonrisesoftware@gmail.com>
387+ *
388+ * This program is free software; you can redistribute it and/or modify
389+ * it under the terms of the GNU General Public License as published by
390+ * the Free Software Foundation; version 3.
391+ *
392+ * This program is distributed in the hope that it will be useful,
393+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
394+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
395+ * GNU General Public License for more details.
396+ *
397+ * You should have received a copy of the GNU General Public License
398+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
399+ *
400+ * Upstream location:
401+ * https://github.com/iBeliever/ubuntu-ui-extras/blob/master/ColumnFlow.qml
402+ */
403+
404+import QtQuick 2.3
405+
406+
407+Item {
408+ id: columnFlow
409+ property int columns: 1
410+ property bool repeaterCompleted: false
411+ property alias model: repeater.model
412+ property alias delegate: repeater.delegate
413+ property int contentHeight: 0
414+
415+ onColumnsChanged: reEvalColumns()
416+ onModelChanged: reEvalColumns()
417+ onWidthChanged: updateWidths()
418+
419+ function updateWidths() {
420+ if (repeaterCompleted) {
421+ var count = 0
422+
423+ //add the first <column> elements
424+ for (var i = 0; count < columns && i < columnFlow.children.length; i++) {
425+ //print(i, count)
426+ if (!columnFlow.children[i] || String(columnFlow.children[i]).indexOf("QQuickRepeater") == 0)
427+ //|| !columnFlow.children[i].visible) // CUSTOM - view is invisible at start
428+ continue
429+
430+ columnFlow.children[i].width = width / columns
431+
432+ count++
433+ }
434+ }
435+ }
436+
437+ function reEvalColumns() {
438+ if (columnFlow.repeaterCompleted === false)
439+ return
440+
441+ if (columns === 0) {
442+ contentHeight = 0
443+ return
444+ }
445+
446+ var i, j
447+ var columnHeights = new Array(columns);
448+ var lastItem = new Array(columns)
449+ var lastI = -1
450+ var count = 0
451+
452+ //add the first <column> elements
453+ for (i = 0; count < columns && i < columnFlow.children.length; i++) {
454+ // CUSTOM - ignore if has just been removed
455+ if (i === repeater.removeHintIndex && columnFlow.children[i] === repeater.removeHintItem) {
456+ continue
457+ }
458+
459+ if (!columnFlow.children[i] || String(columnFlow.children[i]).indexOf("QQuickRepeater") == 0)
460+ //|| !columnFlow.children[i].visible) // CUSTOM - view is invisible at start
461+ continue
462+
463+ lastItem[count] = i
464+
465+ columnHeights[count] = columnFlow.children[i].height
466+ columnFlow.children[i].anchors.top = columnFlow.top
467+ columnFlow.children[i].anchors.left = (lastI === -1 ? columnFlow.left : columnFlow.children[lastI].right)
468+ columnFlow.children[i].anchors.right = undefined
469+ columnFlow.children[i].width = columnFlow.width / columns
470+
471+ lastI = i
472+ count++
473+ }
474+
475+ //add the other elements
476+ for (i = i; i < columnFlow.children.length; i++) {
477+ var highestHeight = Number.MAX_VALUE
478+ var newColumn = 0
479+
480+ // CUSTOM - ignore if has just been removed
481+ if (i === repeater.removeHintIndex && columnFlow.children[i] === repeater.removeHintItem) {
482+ continue
483+ }
484+
485+ if (!columnFlow.children[i] || String(columnFlow.children[i]).indexOf("QQuickRepeater") == 0)
486+ //|| !columnFlow.children[i].visible) // CUSTOM - view is invisible at start
487+ continue
488+
489+ // find the shortest column
490+ for (j = 0; j < columns; j++) {
491+ if (columnHeights[j] !== null && columnHeights[j] < highestHeight) {
492+ newColumn = j
493+ highestHeight = columnHeights[j]
494+ }
495+ }
496+
497+ // add the element to the shortest column
498+ columnFlow.children[i].anchors.top = columnFlow.children[lastItem[newColumn]].bottom
499+ columnFlow.children[i].anchors.left = columnFlow.children[lastItem[newColumn]].left
500+ columnFlow.children[i].anchors.right = columnFlow.children[lastItem[newColumn]].right
501+
502+ lastItem[newColumn] = i
503+ columnHeights[newColumn] += columnFlow.children[i].height
504+ }
505+
506+ var cHeight = 0
507+
508+ for (i = 0; i < columnHeights.length; i++) {
509+ if (columnHeights[i])
510+ cHeight = Math.max(cHeight, columnHeights[i])
511+ }
512+
513+ contentHeight = cHeight
514+ updateWidths()
515+ }
516+
517+ Repeater {
518+ id: repeater
519+ model: columnFlow.model
520+ Component.onCompleted: {
521+ columnFlow.repeaterCompleted = true
522+ columnFlow.reEvalColumns()
523+ }
524+
525+ // Provide a hint of the removed item
526+ property int removeHintIndex: -1 // CUSTOM
527+ property var removeHintItem // CUSTOM
528+
529+ onItemAdded: columnFlow.reEvalColumns() // CUSTOM - ms2 models are live
530+ onItemRemoved: {
531+ removeHintIndex = index
532+ removeHintItem = item
533+
534+ columnFlow.reEvalColumns() // CUSTOM - ms2 models are live
535+
536+ // Set back to null to allow freeing of memory
537+ removeHintIndex = -1
538+ removeHintItem = undefined
539+ }
540+ }
541+}

Subscribers

People subscribed via source and target branches