Merge lp:~music-app-dev/music-app/prototype-now-playing-side-panel into lp:music-app/remix

Proposed by Victor Thompson
Status: Superseded
Proposed branch: lp:~music-app-dev/music-app/prototype-now-playing-side-panel
Merge into: lp:music-app/remix
Diff against target: 5322 lines (+2426/-1833) (has conflicts)
55 files modified
CMakeLists.txt (+13/-7)
app/CMakeLists.txt (+10/-0)
app/components/BlurredBackground.qml (+1/-1)
app/components/CMakeLists.txt (+6/-2)
app/components/CoverGrid.qml (+2/-2)
app/components/Dialog/CMakeLists.txt (+4/-0)
app/components/Dialog/ContentHubErrorDialog.qml (+38/-0)
app/components/Dialog/ContentHubNotFoundDialog.qml (+46/-0)
app/components/Dialog/ContentHubWaitDialog.qml (+37/-0)
app/components/Dialog/EditPlaylistDialog.qml (+80/-0)
app/components/Dialog/NewPlaylistDialog.qml (+75/-0)
app/components/Dialog/RemovePlaylistDialog.qml (+59/-0)
app/components/Flickables/CMakeLists.txt (+4/-0)
app/components/Flickables/CardView.qml (+1/-0)
app/components/Flickables/MultiSelectListView.qml (+51/-0)
app/components/Flickables/MusicListView.qml (+32/-0)
app/components/HeadState/CMakeLists.txt (+4/-0)
app/components/HeadState/MultiSelectHeadState.qml (+108/-0)
app/components/HeadState/PlaylistsHeadState.qml (+47/-0)
app/components/HeadState/SearchableHeadState.qml (+34/-0)
app/components/LibraryListModel.qml (+2/-2)
app/components/ListItemActions/AddToPlaylist.qml (+2/-8)
app/components/ListItemActions/AddToQueue.qml (+1/-1)
app/components/MusicPage.qml (+2/-2)
app/components/MusicRow.qml (+1/-1)
app/components/MusicToolbar.qml (+244/-314)
app/components/NowPlaying.qml (+550/-0)
app/components/PlaylistsEmptyState.qml (+58/-0)
app/components/ViewButton/CMakeLists.txt (+4/-0)
app/components/ViewButton/PlayAllButton.qml (+34/-0)
app/components/ViewButton/QueueAllButton.qml (+46/-0)
app/components/ViewButton/ShuffleButton.qml (+46/-0)
app/components/Walkthrough/Slide1.qml (+1/-1)
app/components/Walkthrough/Slide2.qml (+1/-1)
app/components/Walkthrough/Slide3.qml (+1/-1)
app/components/Walkthrough/Walkthrough.qml (+1/-1)
app/components/WorkerModelLoader.qml (+1/-1)
app/logic/CMakeLists.txt (+4/-0)
app/music-app.qml (+352/-155)
app/ui/AddToPlaylist.qml (+22/-23)
app/ui/Albums.qml (+22/-27)
app/ui/ArtistView.qml (+29/-73)
app/ui/Artists.qml (+19/-25)
app/ui/CMakeLists.txt (+4/-0)
app/ui/Genres.qml (+21/-26)
app/ui/LibraryEmptyState.qml (+123/-0)
app/ui/NowPlayingView.qml (+23/-624)
app/ui/Playlists.qml (+31/-39)
app/ui/Recent.qml (+18/-23)
app/ui/Songs.qml (+23/-118)
app/ui/SongsView.qml (+40/-283)
click/CMakeLists.txt (+0/-15)
tests/autopilot/music_app/__init__.py (+38/-44)
tests/autopilot/music_app/tests/__init__.py (+2/-2)
tests/autopilot/music_app/tests/test_music.py (+8/-11)
Text conflict in app/music-app.qml
To merge this branch: bzr merge lp:~music-app-dev/music-app/prototype-now-playing-side-panel
Reviewer Review Type Date Requested Status
Music App Developers Pending
Review via email: mp+249143@code.launchpad.net

This proposal has been superseded by a proposal from 2015-02-10.

Commit message

* Conditional layouts for the music app

Description of the change

DO NOT MERGE, just want to keep this MP to see a diff of what's been added.

To post a comment you must log in.
842. By Victor Thompson

Add ItemLayout to the Layouts.

Unmerged revisions

842. By Victor Thompson

Add ItemLayout to the Layouts.

841. By Victor Thompson

start conditional layout prototype

840. By Andrew Hayzen

* Create generic MultiSelectListView.qml for handling multiselect
* Create MusicListView.qml with our patches to ListView
* Move Flickables from components into their own Flickables folder.

Approved by Victor Thompson, Ubuntu Phone Apps Jenkins Bot.

839. By Andrew Hayzen

* Do not allow LibraryEmptyState loader to be active if content-hub is running. Fixes: https://bugs.launchpad.net/bugs/1418366.

Approved by Victor Thompson, Ubuntu Phone Apps Jenkins Bot.

838. By Andrew Hayzen

* Move all Dialog components into their own files in components/Dialog.

Approved by Victor Thompson, Ubuntu Phone Apps Jenkins Bot.

837. By Andrew Hayzen

* Create common components for Play All, Queue All and Shuffle buttons within 'Views'.

Approved by Victor Thompson, Ubuntu Phone Apps Jenkins Bot.

836. By Andrew Hayzen

* Split EmptyStates into a page (for noMusic) and a component (for noPlaylists) and load in async.

Approved by Ubuntu Phone Apps Jenkins Bot, Victor Thompson.

835. By Andrew Hayzen

* Move existing head states into HeadState folder
* Create MultiSelectHeadState SearchableHeadState PlaylistHeadState
* Migrate AddToPlaylist Albums Artists Genres NowPlaying Playlists Songs SongsView to use the new HeadState.

Approved by Ubuntu Phone Apps Jenkins Bot, Victor Thompson.

834. By Andrew Hayzen

* Make use of SDK PageStack.push(url, properties) instead of creating the component, object and pushing ourselves.

Approved by Victor Thompson, Ubuntu Phone Apps Jenkins Bot.

833. By Andrew Hayzen

* Rename common to components
* Move all qml components in components
* Move any JS scripts into logic.

Approved by Ubuntu Phone Apps Jenkins Bot, Victor Thompson.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2014-09-08 08:08:13 +0000
+++ CMakeLists.txt 2015-02-10 03:13:44 +0000
@@ -13,10 +13,10 @@
13set(DESKTOP_FILE "${PROJECT_NAME}_${APP_NAME}.desktop")13set(DESKTOP_FILE "${PROJECT_NAME}_${APP_NAME}.desktop")
14set(URLS_FILE "${PROJECT_NAME}_${APP_NAME}.url-dispatcher")14set(URLS_FILE "${PROJECT_NAME}_${APP_NAME}.url-dispatcher")
15set(APP_HARDCODE music-app)15set(APP_HARDCODE music-app)
16set(MAIN_QML music-app.qml)16set(MAIN_QML app/music-app.qml)
17set(ICON_FILE images/music-app@30.png)17set(ICON_FILE app/graphics/music-app@30.png)
18set(AUTOPILOT_DIR music_app)18set(AUTOPILOT_DIR music_app)
19set(UBUNTU_MANIFEST_PATH "click/manifest.json.in" CACHE INTERNAL "Tell QtCreator location and name of the manifest file")19set(UBUNTU_MANIFEST_PATH "manifest.json.in" CACHE INTERNAL "Tell QtCreator location and name of the manifest file")
2020
21if(CLICK_MODE)21if(CLICK_MODE)
22 if(NOT DEFINED BZR_SOURCE)22 if(NOT DEFINED BZR_SOURCE)
@@ -30,6 +30,14 @@
30 set(EXEC "qmlscene -qt5 ${MAIN_QML} --url=%u -I ./plugins")30 set(EXEC "qmlscene -qt5 ${MAIN_QML} --url=%u -I ./plugins")
31 set(DESKTOP_DIR ${DATA_DIR})31 set(DESKTOP_DIR ${DATA_DIR})
32 set(URLS_DIR ${DATA_DIR})32 set(URLS_DIR ${DATA_DIR})
33
34 if(NOT BZR_REVNO)
35 set(BZR_REVNO "latest")
36 endif(NOT BZR_REVNO)
37
38 configure_file(manifest.json.in ${CMAKE_CURRENT_BINARY_DIR}/manifest.json)
39 install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json apparmor.json
40 music-app-content.json DESTINATION ${CMAKE_INSTALL_PREFIX})
33else(CLICK_MODE)41else(CLICK_MODE)
34 set(DATA_DIR ${CMAKE_INSTALL_DATADIR}/${APP_HARDCODE})42 set(DATA_DIR ${CMAKE_INSTALL_DATADIR}/${APP_HARDCODE})
35 set(EXEC ${APP_HARDCODE})43 set(EXEC ${APP_HARDCODE})
@@ -53,7 +61,7 @@
53file(GLOB SRC_FILES61file(GLOB SRC_FILES
54 RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}62 RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
55 *.qml *.js *.png *.js *.json)63 *.qml *.js *.png *.js *.json)
56install(DIRECTORY common images DESTINATION ${DATA_DIR})64install(DIRECTORY app DESTINATION ${DATA_DIR})
57install(FILES ${SRC_FILES} ${ICON_FILE} DESTINATION ${DATA_DIR})65install(FILES ${SRC_FILES} ${ICON_FILE} DESTINATION ${DATA_DIR})
5866
59configure_file(${DESKTOP_FILE}.in.in ${DESKTOP_FILE}.in)67configure_file(${DESKTOP_FILE}.in.in ${DESKTOP_FILE}.in)
@@ -71,9 +79,7 @@
71# Tests79# Tests
72enable_testing()80enable_testing()
7381
74add_subdirectory(click)82add_subdirectory(app)
75add_subdirectory(common)
76add_subdirectory(images)
77add_subdirectory(po)83add_subdirectory(po)
78add_subdirectory(tests)84add_subdirectory(tests)
7985
8086
=== added directory 'app'
=== added file 'app/CMakeLists.txt'
--- app/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ app/CMakeLists.txt 2015-02-10 03:13:44 +0000
@@ -0,0 +1,10 @@
1add_subdirectory(components)
2add_subdirectory(graphics)
3add_subdirectory(logic)
4add_subdirectory(ui)
5
6# make the qml files visible on qtcreator
7file(GLOB APP_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
8*.qml *.js *.png *.js *.json)
9
10add_custom_target(com_ubuntu_music_APP_Files ALL SOURCES ${APP_FILES})
011
=== renamed directory 'common' => 'app/components'
=== modified file 'app/components/BlurredBackground.qml'
--- common/BlurredBackground.qml 2014-11-11 02:46:42 +0000
+++ app/components/BlurredBackground.qml 2015-02-10 03:13:44 +0000
@@ -25,7 +25,7 @@
25Item {25Item {
26 width: parent.width26 width: parent.width
2727
28 property string art // : player.currentMetaFile === "" ? Qt.resolvedUrl("../images/music-app-cover@30.png") : player.currentMetaArt28 property string art // : player.currentMetaFile === "" ? Qt.resolvedUrl("../graphics/music-app-cover@30.png") : player.currentMetaArt
2929
30 // dark layer30 // dark layer
31 Rectangle {31 Rectangle {
3232
=== modified file 'app/components/CMakeLists.txt'
--- common/CMakeLists.txt 2015-01-20 17:47:30 +0000
+++ app/components/CMakeLists.txt 2015-02-10 03:13:44 +0000
@@ -1,7 +1,11 @@
1add_subdirectory(Dialog)
2add_subdirectory(HeadState)
3add_subdirectory(Flickables)
1add_subdirectory(ListItemActions)4add_subdirectory(ListItemActions)
5add_subdirectory(ViewButton)
2add_subdirectory(Walkthrough)6add_subdirectory(Walkthrough)
37
4# make the qml files visible on qtcreator8# make the qml files visible on qtcreator
5file(GLOB COMMON_QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml)9file(GLOB COMPONENTS_QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml)
610
7add_custom_target(com_ubuntu_music_COMMON_QMLFiles ALL SOURCES ${COMMON_QML_FILES})11add_custom_target(com_ubuntu_music_COMPONENTS_QMLFiles ALL SOURCES ${COMPONENTS_QML_FILES})
812
=== modified file 'app/components/CoverGrid.qml'
--- common/CoverGrid.qml 2015-01-11 16:34:22 +0000
+++ app/components/CoverGrid.qml 2015-02-10 03:13:44 +0000
@@ -60,7 +60,7 @@
60 ? (coverGrid.covers[index].art !== undefined60 ? (coverGrid.covers[index].art !== undefined
61 ? coverGrid.covers[index].art61 ? coverGrid.covers[index].art
62 : "image://albumart/artist=" + coverGrid.covers[index].author + "&album=" + coverGrid.covers[index].album)62 : "image://albumart/artist=" + coverGrid.covers[index].author + "&album=" + coverGrid.covers[index].album)
63 : Qt.resolvedUrl("../images/music-app-cover@30.png")63 : Qt.resolvedUrl("../graphics/music-app-cover@30.png")
6464
65 // TODO: This should be investigated once http://pad.lv/139136865 // TODO: This should be investigated once http://pad.lv/1391368
66 // is resolved. Once it is, these can either be set to66 // is resolved. Once it is, these can either be set to
@@ -73,7 +73,7 @@
7373
74 onStatusChanged: {74 onStatusChanged: {
75 if (status === Image.Error) {75 if (status === Image.Error) {
76 source = Qt.resolvedUrl("../images/music-app-cover@30.png")76 source = Qt.resolvedUrl("../graphics/music-app-cover@30.png")
77 } else if (status === Image.Ready && index === 0) {77 } else if (status === Image.Ready && index === 0) {
78 firstSource = source78 firstSource = source
79 }79 }
8080
=== added directory 'app/components/Dialog'
=== added file 'app/components/Dialog/CMakeLists.txt'
--- app/components/Dialog/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ app/components/Dialog/CMakeLists.txt 2015-02-10 03:13:44 +0000
@@ -0,0 +1,4 @@
1# make the qml files visible on qtcreator
2file(GLOB DIALOG_QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml)
3
4add_custom_target(com_ubuntu_music_DIALOG_QMLFiles ALL SOURCES ${DIALOG_QML_FILES})
05
=== added file 'app/components/Dialog/ContentHubErrorDialog.qml'
--- app/components/Dialog/ContentHubErrorDialog.qml 1970-01-01 00:00:00 +0000
+++ app/components/Dialog/ContentHubErrorDialog.qml 2015-02-10 03:13:44 +0000
@@ -0,0 +1,38 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@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 Ubuntu.Components.Popups 1.0
23
24Dialog {
25 id: dialogContentHubError
26
27 property alias errorText: errorLabel.text
28
29 Label {
30 id: errorLabel
31 color: styleMusic.common.black
32 }
33
34 Button {
35 text: i18n.tr("OK")
36 onClicked: PopupUtils.close(dialogContentHubError)
37 }
38}
039
=== added file 'app/components/Dialog/ContentHubNotFoundDialog.qml'
--- app/components/Dialog/ContentHubNotFoundDialog.qml 1970-01-01 00:00:00 +0000
+++ app/components/Dialog/ContentHubNotFoundDialog.qml 2015-02-10 03:13:44 +0000
@@ -0,0 +1,46 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@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 Ubuntu.Components.Popups 1.0
23
24Dialog {
25 id: dialogContentHubNotFound
26
27 Label {
28 color: styleMusic.common.black
29 text: i18n.tr("Imported file not found")
30 }
31
32 Button {
33 text: i18n.tr("Wait")
34 onClicked: {
35 PopupUtils.close(dialogContentHubNotFound)
36
37 contentHubWaitForFile.dialog = PopupUtils.open(Qt.resolvedUrl("ContentHubWaitDialog.qml"), mainView)
38 contentHubWaitForFile.start();
39 }
40 }
41
42 Button {
43 text: i18n.tr("Cancel")
44 onClicked: PopupUtils.close(dialogContentHubNotFound)
45 }
46}
047
=== added file 'app/components/Dialog/ContentHubWaitDialog.qml'
--- app/components/Dialog/ContentHubWaitDialog.qml 1970-01-01 00:00:00 +0000
+++ app/components/Dialog/ContentHubWaitDialog.qml 2015-02-10 03:13:44 +0000
@@ -0,0 +1,37 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@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 Ubuntu.Components.Popups 1.0
23import "../"
24
25Dialog {
26 id: dialogContentHubWait
27
28 LoadingSpinnerComponent {
29 anchors {
30 horizontalCenter: parent.horizontalCenter
31 top: undefined
32 margins: units.gu(0)
33 }
34 loadingText: i18n.tr("Waiting for file(s)...")
35 visible: true
36 }
37}
038
=== added file 'app/components/Dialog/EditPlaylistDialog.qml'
--- app/components/Dialog/EditPlaylistDialog.qml 1970-01-01 00:00:00 +0000
+++ app/components/Dialog/EditPlaylistDialog.qml 2015-02-10 03:13:44 +0000
@@ -0,0 +1,80 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@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 Ubuntu.Components.Popups 1.0
23import QtQuick.LocalStorage 2.0
24import "../../logic/meta-database.js" as Library
25import "../../logic/playlists.js" as Playlists
26
27Dialog {
28 id: dialogEditPlaylist
29 // TRANSLATORS: this is a title of a dialog with a prompt to rename a playlist
30 title: i18n.tr("Rename playlist")
31
32 property string oldPlaylistName: ""
33
34 TextField {
35 id: playlistName
36 inputMethodHints: Qt.ImhNoPredictiveText
37 placeholderText: i18n.tr("Enter playlist name")
38 }
39 Label {
40 id: editplaylistoutput
41 color: "red"
42 visible: false
43 }
44
45 Button {
46 text: i18n.tr("Change")
47 color: styleMusic.dialog.confirmButtonColor
48 onClicked: {
49 editplaylistoutput.visible = true
50
51 if (playlistName.text.length > 0) { // make sure something is acually inputed
52 console.debug("Debug: User changed name from "+oldPlaylistName+" to "+playlistName.text)
53
54 if (Playlists.renamePlaylist(oldPlaylistName, playlistName.text) === true) {
55
56 if (Library.recentContainsPlaylist(oldPlaylistName)) {
57 Library.recentRenamePlaylist(oldPlaylistName, playlistName.text)
58 }
59
60 line2 = playlistName.text
61
62 playlistChangedHelper() // update recent/playlist models
63
64 PopupUtils.close(dialogEditPlaylist)
65 }
66 else {
67 editplaylistoutput.text = i18n.tr("Playlist already exists")
68 }
69 }
70 else {
71 editplaylistoutput.text = i18n.tr("Please type in a name.")
72 }
73 }
74 }
75 Button {
76 text: i18n.tr("Cancel")
77 color: styleMusic.dialog.cancelButtonColor
78 onClicked: PopupUtils.close(dialogEditPlaylist)
79 }
80}
081
=== added file 'app/components/Dialog/NewPlaylistDialog.qml'
--- app/components/Dialog/NewPlaylistDialog.qml 1970-01-01 00:00:00 +0000
+++ app/components/Dialog/NewPlaylistDialog.qml 2015-02-10 03:13:44 +0000
@@ -0,0 +1,75 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@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 Ubuntu.Components.Popups 1.0
23import QtQuick.LocalStorage 2.0
24import "../../logic/playlists.js" as Playlists
25
26
27Dialog {
28 id: dialogNewPlaylist
29 objectName: "dialogNewPlaylist"
30 title: i18n.tr("New playlist")
31 TextField {
32 id: playlistName
33 objectName: "playlistNameTextField"
34 placeholderText: i18n.tr("Enter playlist name")
35 inputMethodHints: Qt.ImhNoPredictiveText
36 }
37 Label {
38 id: newplaylistoutput
39 color: "red"
40 visible: false // should only be visible when an error is made.
41 }
42
43 Button {
44 text: i18n.tr("Create")
45 color: styleMusic.dialog.confirmButtonColor
46 objectName: "newPlaylistDialogCreateButton"
47 onClicked: {
48 newplaylistoutput.visible = false // make sure its hidden now if there was an error last time
49 if (playlistName.text.length > 0) { // make sure something is acually inputed
50 if (Playlists.addPlaylist(playlistName.text) === true) {
51 console.debug("Debug: User created a new playlist named: ", playlistName.text)
52
53 playlistModel.filterPlaylists(); // reload model
54
55 PopupUtils.close(dialogNewPlaylist)
56 }
57 else {
58 console.debug("Debug: Playlist already exists")
59 newplaylistoutput.visible = true
60 newplaylistoutput.text = i18n.tr("Playlist already exists")
61 }
62 }
63 else {
64 newplaylistoutput.visible = true
65 newplaylistoutput.text = i18n.tr("Please type in a name.")
66 }
67 }
68 }
69
70 Button {
71 text: i18n.tr("Cancel")
72 color: styleMusic.dialog.cancelButtonColor
73 onClicked: PopupUtils.close(dialogNewPlaylist)
74 }
75}
076
=== added file 'app/components/Dialog/RemovePlaylistDialog.qml'
--- app/components/Dialog/RemovePlaylistDialog.qml 1970-01-01 00:00:00 +0000
+++ app/components/Dialog/RemovePlaylistDialog.qml 2015-02-10 03:13:44 +0000
@@ -0,0 +1,59 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@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 Ubuntu.Components.Popups 1.0
23import QtQuick.LocalStorage 2.0
24import "../../logic/meta-database.js" as Library
25import "../../logic/playlists.js" as Playlists
26
27Dialog {
28 id: dialogRemovePlaylist
29 // TRANSLATORS: this is a title of a dialog with a prompt to delete a playlist
30 title: i18n.tr("Permanently delete playlist?")
31 text: "("+i18n.tr("This cannot be undone")+")"
32
33 property string oldPlaylistName
34
35 Button {
36 text: i18n.tr("Remove")
37 color: styleMusic.dialog.confirmRemoveButtonColor
38 onClicked: {
39 // removing playlist
40 Playlists.removePlaylist(dialogRemovePlaylist.oldPlaylistName)
41
42 if (Library.recentContainsPlaylist(dialogRemovePlaylist.oldPlaylistName)) {
43 Library.recentRemovePlaylist(dialogRemovePlaylist.oldPlaylistName)
44 }
45
46 playlistChangedHelper(true) // update recent/playlist models
47
48 songStackPage.page = undefined
49 PopupUtils.close(dialogRemovePlaylist)
50
51 mainPageStack.goBack()
52 }
53 }
54 Button {
55 text: i18n.tr("Cancel")
56 color: styleMusic.dialog.cancelButtonColor
57 onClicked: PopupUtils.close(dialogRemovePlaylist)
58 }
59}
060
=== added directory 'app/components/Flickables'
=== added file 'app/components/Flickables/CMakeLists.txt'
--- app/components/Flickables/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ app/components/Flickables/CMakeLists.txt 2015-02-10 03:13:44 +0000
@@ -0,0 +1,4 @@
1# make the qml files visible on qtcreator
2file(GLOB FLICKABLES_QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml)
3
4add_custom_target(com_ubuntu_music_FLICKABLES_QMLFiles ALL SOURCES ${FLICKABLES_QML_FILES})
05
=== renamed file 'common/CardView.qml' => 'app/components/Flickables/CardView.qml'
--- common/CardView.qml 2014-11-23 03:50:40 +0000
+++ app/components/Flickables/CardView.qml 2015-02-10 03:13:44 +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
22Flickable {23Flickable {
2324
=== added file 'app/components/Flickables/MultiSelectListView.qml'
--- app/components/Flickables/MultiSelectListView.qml 1970-01-01 00:00:00 +0000
+++ app/components/Flickables/MultiSelectListView.qml 2015-02-10 03:13:44 +0000
@@ -0,0 +1,51 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@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
23MusicListView {
24 property var selectedItems: []
25
26 signal clearSelection()
27 signal closeSelection()
28 signal selectAll()
29
30 onClearSelection: selectedItems = []
31 onCloseSelection: {
32 clearSelection()
33 state = "normal"
34 }
35 onSelectAll: {
36 var tmp = selectedItems
37
38 for (var i=0; i < model.count; i++) {
39 if (tmp.indexOf(i) === -1) {
40 tmp.push(i)
41 }
42 }
43
44 selectedItems = tmp
45 }
46 onVisibleChanged: {
47 if (!visible) {
48 closeSelection()
49 }
50 }
51}
052
=== added file 'app/components/Flickables/MusicListView.qml'
--- app/components/Flickables/MusicListView.qml 1970-01-01 00:00:00 +0000
+++ app/components/Flickables/MusicListView.qml 2015-02-10 03:13:44 +0000
@@ -0,0 +1,32 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@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
24ListView {
25 Component.onCompleted: {
26 // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
27 // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration
28 var scaleFactor = units.gridUnit / 8;
29 maximumFlickVelocity = maximumFlickVelocity * scaleFactor;
30 flickDeceleration = flickDeceleration * scaleFactor;
31 }
32}
033
=== added directory 'app/components/HeadState'
=== added file 'app/components/HeadState/CMakeLists.txt'
--- app/components/HeadState/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ app/components/HeadState/CMakeLists.txt 2015-02-10 03:13:44 +0000
@@ -0,0 +1,4 @@
1# make the qml files visible on qtcreator
2file(GLOB HEAD_STATE_QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml)
3
4add_custom_target(com_ubuntu_music_HEAD_STATE_QMLFiles ALL SOURCES ${HEAD_STATE_QML_FILES})
05
=== added file 'app/components/HeadState/MultiSelectHeadState.qml'
--- app/components/HeadState/MultiSelectHeadState.qml 1970-01-01 00:00:00 +0000
+++ app/components/HeadState/MultiSelectHeadState.qml 2015-02-10 03:13:44 +0000
@@ -0,0 +1,108 @@
1/*
2 * Copyright (C) 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19import QtQuick 2.3
20import Ubuntu.Components 1.1
21
22PageHeadState {
23 id: selectionState
24 actions: [
25 Action {
26 iconName: "select"
27 text: i18n.tr("Select All")
28 onTriggered: {
29 if (listview.selectedItems.length === listview.model.count) {
30 listview.clearSelection()
31 } else {
32 listview.selectAll()
33 }
34 }
35 },
36 Action {
37 enabled: listview.selectedItems.length !== 0
38 iconName: "add-to-playlist"
39 text: i18n.tr("Add to playlist")
40 onTriggered: {
41 var items = []
42
43 for (var i=0; i < listview.selectedItems.length; i++) {
44 items.push(makeDict(listview.model.get(listview.selectedItems[i], listview.model.RoleModelData)));
45 }
46
47 mainPageStack.push(Qt.resolvedUrl("../../ui/AddToPlaylist.qml"),
48 {"chosenElements": items})
49
50 listview.closeSelection()
51 }
52 },
53 Action {
54 enabled: listview.selectedItems.length > 0
55 iconName: "add"
56 text: i18n.tr("Add to queue")
57 visible: addToQueue
58
59 onTriggered: {
60 var items = []
61
62 for (var i=0; i < listview.selectedItems.length; i++) {
63 items.push(listview.model.get(listview.selectedItems[i], listview.model.RoleModelData));
64 }
65
66 trackQueue.appendList(items)
67
68 listview.closeSelection()
69 }
70 },
71 Action {
72 enabled: listview.selectedItems.length > 0
73 iconName: "delete"
74 text: i18n.tr("Delete")
75 visible: removable
76
77 onTriggered: {
78 removed(listview.selectedItems)
79
80 listview.closeSelection()
81 }
82 }
83
84 ]
85 backAction: Action {
86 text: i18n.tr("Cancel selection")
87 iconName: "back"
88 onTriggered: {
89 listview.clearSelection()
90 listview.state = "normal"
91 }
92 }
93 head: thisPage.head
94 name: "selection"
95
96 PropertyChanges {
97 target: thisPage.head
98 backAction: selectionState.backAction
99 actions: selectionState.actions
100 }
101
102 property bool addToQueue: true
103 property ListView listview
104 property bool removable: false
105 property Page thisPage
106
107 signal removed(var selectedItems)
108}
0109
=== added file 'app/components/HeadState/PlaylistsHeadState.qml'
--- app/components/HeadState/PlaylistsHeadState.qml 1970-01-01 00:00:00 +0000
+++ app/components/HeadState/PlaylistsHeadState.qml 2015-02-10 03:13:44 +0000
@@ -0,0 +1,47 @@
1/*
2 * Copyright (C) 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19import QtQuick 2.3
20import Ubuntu.Components 1.1
21import Ubuntu.Components.Popups 1.0
22
23
24PageHeadState {
25 name: "default"
26 head: thisPage.head
27 actions: [
28 Action {
29 id: newPlaylistAction
30 objectName: "newPlaylistButton"
31 iconName: "add"
32 onTriggered: {
33 customdebug("New playlist.")
34 PopupUtils.open(Qt.resolvedUrl("../Dialog/NewPlaylistDialog.qml"), mainView)
35 }
36 },
37 Action {
38 id: searchAction
39 iconName: "search"
40 onTriggered: thisPage.state = "search"
41 }
42 ]
43
44 property alias newPlaylistEnabled: newPlaylistAction.enabled
45 property alias searchEnabled: searchAction.enabled
46 property Page thisPage
47}
048
=== renamed file 'common/SearchHeadState.qml' => 'app/components/HeadState/SearchHeadState.qml'
=== added file 'app/components/HeadState/SearchableHeadState.qml'
--- app/components/HeadState/SearchableHeadState.qml 1970-01-01 00:00:00 +0000
+++ app/components/HeadState/SearchableHeadState.qml 2015-02-10 03:13:44 +0000
@@ -0,0 +1,34 @@
1/*
2 * Copyright (C) 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Victor Thompson <victor.thompson@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19import QtQuick 2.3
20import Ubuntu.Components 1.1
21
22
23PageHeadState {
24 name: "default"
25 head: thisPage.head
26 actions: Action {
27 id: searchAction
28 iconName: "search"
29 onTriggered: thisPage.state = "search"
30 }
31
32 property alias searchEnabled: searchAction.enabled
33 property Page thisPage
34}
035
=== renamed file 'LibraryListModel.qml' => 'app/components/LibraryListModel.qml'
--- LibraryListModel.qml 2015-01-20 23:58:27 +0000
+++ app/components/LibraryListModel.qml 2015-02-10 03:13:44 +0000
@@ -19,8 +19,8 @@
1919
20import QtQuick 2.320import QtQuick 2.3
21import QtQuick.LocalStorage 2.021import QtQuick.LocalStorage 2.0
22import "meta-database.js" as Library22import "../logic/meta-database.js" as Library
23import "playlists.js" as Playlists23import "../logic/playlists.js" as Playlists
2424
25Item {25Item {
26 id: libraryListModelItem26 id: libraryListModelItem
2727
=== modified file 'app/components/ListItemActions/AddToPlaylist.qml'
--- common/ListItemActions/AddToPlaylist.qml 2015-01-11 15:54:04 +0000
+++ app/components/ListItemActions/AddToPlaylist.qml 2015-02-10 03:13:44 +0000
@@ -30,13 +30,7 @@
30 onTriggered: {30 onTriggered: {
31 console.debug("Debug: Add track to playlist");31 console.debug("Debug: Add track to playlist");
3232
33 var comp = Qt.createComponent("../../MusicaddtoPlaylist.qml")33 mainPageStack.push(Qt.resolvedUrl("../../ui/AddToPlaylist.qml"),
34 var addToPlaylist = comp.createObject(mainPageStack, {"chosenElements": [makeDict(model)]});34 {"chosenElements": [makeDict(model)]})
35
36 if (addToPlaylist == null) { // Error Handling
37 console.log("Error creating object");
38 }
39
40 mainPageStack.push(addToPlaylist)
41 }35 }
42}36}
4337
=== modified file 'app/components/ListItemActions/AddToQueue.qml'
--- common/ListItemActions/AddToQueue.qml 2014-10-28 00:00:26 +0000
+++ app/components/ListItemActions/AddToQueue.qml 2015-02-10 03:13:44 +0000
@@ -19,7 +19,7 @@
19import QtQuick 2.319import QtQuick 2.3
20import QtQuick.LocalStorage 2.020import QtQuick.LocalStorage 2.0
21import Ubuntu.Components 1.121import Ubuntu.Components 1.1
22import "../../meta-database.js" as Library22import "../../logic/meta-database.js" as Library
2323
24Action {24Action {
25 iconName: "add"25 iconName: "add"
2626
=== modified file 'app/components/MusicPage.qml'
--- common/MusicPage.qml 2015-01-10 19:22:08 +0000
+++ app/components/MusicPage.qml 2015-02-10 03:13:44 +0000
@@ -25,7 +25,7 @@
25Page {25Page {
26 id: thisPage26 id: thisPage
27 anchors {27 anchors {
28 bottomMargin: musicToolbar.visible ? musicToolbar.currentHeight : 028 bottomMargin: musicToolbar.visible ? musicToolbar.height : 0
29 fill: parent29 fill: parent
30 }30 }
3131
@@ -56,7 +56,7 @@
5656
57 onVisibleChanged: {57 onVisibleChanged: {
58 if (visible) {58 if (visible) {
59 musicToolbar.setPage(thisPage);59 mainPageStack.setPage(thisPage);
60 }60 }
61 }61 }
62}62}
6363
=== modified file 'app/components/MusicRow.qml'
--- common/MusicRow.qml 2014-10-23 17:40:04 +0000
+++ app/components/MusicRow.qml 2015-02-10 03:13:44 +0000
@@ -55,7 +55,7 @@
5555
56 onStatusChanged: {56 onStatusChanged: {
57 if (status === Image.Error) {57 if (status === Image.Error) {
58 source = Qt.resolvedUrl("../images/music-app-cover@30.png")58 source = Qt.resolvedUrl("../graphics/music-app-cover@30.png")
59 }59 }
60 }60 }
61 visible: imageSource !== undefined61 visible: imageSource !== undefined
6262
=== renamed file 'MusicToolbar.qml' => 'app/components/MusicToolbar.qml'
--- MusicToolbar.qml 2015-01-23 07:22:52 +0000
+++ app/components/MusicToolbar.qml 2015-02-10 03:13:44 +0000
@@ -18,335 +18,265 @@
18 */18 */
1919
20import QtQuick 2.320import QtQuick 2.3
21import QtQuick.LocalStorage 2.0
22import QtMultimedia 5.021import QtMultimedia 5.0
23import Ubuntu.Components 1.122import Ubuntu.Components 1.1
24import Ubuntu.Components.Popups 1.0
25import "common"
2623
27Item {24Rectangle {
28 anchors {25 anchors {
29 bottom: parent.bottom26 bottom: parent.bottom
30 left: parent.left27 left: parent.left
31 right: parent.right28 right: parent.right
32 }29 }
3330 color: styleMusic.common.black
34 // Properties storing the current page info31 height: units.gu(7.25)
35 property var currentPage: null32 objectName: "musicToolbarObject"
36 property var currentTab: null33
3734 // Hack for autopilot otherwise MusicToolbar appears as QQuickRectangle
38 // Properties and signals for the toolbar35 // due to bug 1341671 it is required that there is a property so that
39 property alias currentHeight: musicToolbarPanel.height36 // qml doesn't optimise using the parent type
40 property alias opened: musicToolbarPanel.opened37 property bool bug1341671workaround: true
4138
42 property bool popping: false39 /* Toolbar controls */
4340 Item {
44 /* Helper functions */41 id: toolbarControls
45
46 // Back button has been pressed, jump up pageStack or back to parent page
47 function goBack()
48 {
49 if (mainPageStack !== null && mainPageStack.depth > 1) {
50 mainPageStack.pop(currentPage)
51 }
52 }
53
54 // Pop a specific page in the stack
55 function popPage(page)
56 {
57 var tmpPages = []
58
59 popping = true
60
61 while (mainPageStack.currentPage !== page && mainPageStack.depth > 0) {
62 tmpPages.push(mainPageStack.currentPage)
63 mainPageStack.pop()
64 }
65
66 if (mainPageStack.depth > 0) {
67 mainPageStack.pop()
68 }
69
70 for (var i=tmpPages.length - 1; i > -1; i--) {
71 mainPageStack.push(tmpPages[i])
72 }
73
74 popping = false
75 }
76
77 // Set the current page, and any parent/stacks
78 function setPage(childPage)
79 {
80 if (!popping) {
81 currentPage = childPage;
82 }
83 }
84
85 Panel {
86 id: musicToolbarPanel
87 anchors {42 anchors {
88 left: parent.left43 fill: parent
89 right: parent.right44 }
90 bottom: parent.bottom45 state: trackQueue.model.count === 0 ? "disabled" : "enabled"
91 }46 states: [
92 height: units.gu(7.25)47 State {
93 locked: true48 name: "disabled"
94 opened: true49 PropertyChanges {
9550 target: disabledPlayerControlsGroup
96 /* Expanded toolbar */51 visible: true
97 Item {52 }
98 id: musicToolbarExpandedContainer53 PropertyChanges {
99 anchors {54 target: enabledPlayerControlsGroup
100 fill: parent55 visible: false
101 }56 }
57 },
58 State {
59 name: "enabled"
60 PropertyChanges {
61 target: disabledPlayerControlsGroup
62 visible: false
63 }
64 PropertyChanges {
65 target: enabledPlayerControlsGroup
66 visible: true
67 }
68 }
69 ]
70
71 /* Disabled (empty state) controls */
72 Item {
73 id: disabledPlayerControlsGroup
74 anchors {
75 bottom: playerControlsProgressBar.top
76 left: parent.left
77 right: parent.right
78 top: parent.top
79 }
80
81 Label {
82 id: noSongsInQueueLabel
83 anchors {
84 left: parent.left
85 leftMargin: units.gu(2)
86 right: disabledPlayerControlsPlayButton.left
87 rightMargin: units.gu(2)
88 verticalCenter: parent.verticalCenter
89 }
90 color: styleMusic.playerControls.labelColor
91 text: i18n.tr("Tap to shuffle music")
92 fontSize: "large"
93 visible: !emptyPageLoader.noMusic
94 wrapMode: Text.WordWrap
95 maximumLineCount: 2
96 }
97
98 /* Play/Pause button */
99 Icon {
100 id: disabledPlayerControlsPlayButton
101 anchors {
102 right: parent.right
103 rightMargin: units.gu(3)
104 verticalCenter: parent.verticalCenter
105 }
106 color: "#FFF"
107 height: units.gu(2.5)
108 name: player.playbackState === MediaPlayer.PlayingState ?
109 "media-playback-pause" : "media-playback-start"
110 objectName: "disabledSmallPlayShape"
111 width: height
112 }
113
114 /* Click to shuffle music */
115 MouseArea {
116 anchors {
117 fill: parent
118 }
119 onClicked: {
120 if (emptyPageLoader.noMusic) {
121 return;
122 }
123
124 if (trackQueue.model.count === 0) {
125 playRandomSong();
126 }
127 else {
128 player.toggle();
129 }
130 }
131 }
132 }
133
134 /* Enabled (queue > 0) controls */
135 Item {
136 id: enabledPlayerControlsGroup
137 anchors {
138 bottom: playerControlsProgressBar.top
139 left: parent.left
140 right: parent.right
141 top: parent.top
142 }
143
144 /* Album art in player controls */
145 CoverGrid {
146 id: playerControlsImage
147 anchors {
148 bottom: parent.bottom
149 left: parent.left
150 top: parent.top
151 }
152 covers: [{art: player.currentMetaArt, author: player.currentMetaArtist, album: player.currentMetaArt}]
153 size: parent.height
154 }
155
156 /* Column of meta labels */
157 Column {
158 id: playerControlsLabels
159 anchors {
160 left: playerControlsImage.right
161 leftMargin: units.gu(1.5)
162 right: playerControlsPlayButton.left
163 rightMargin: units.gu(1)
164 verticalCenter: parent.verticalCenter
165 }
166
167 /* Title of track */
168 Label {
169 id: playerControlsTitle
170 anchors {
171 left: parent.left
172 right: parent.right
173 }
174 color: "#FFF"
175 elide: Text.ElideRight
176 fontSize: "small"
177 font.weight: Font.DemiBold
178 text: player.currentMetaTitle === ""
179 ? player.source : player.currentMetaTitle
180 }
181
182 /* Artist of track */
183 Label {
184 id: playerControlsArtist
185 anchors {
186 left: parent.left
187 right: parent.right
188 }
189 color: "#FFF"
190 elide: Text.ElideRight
191 fontSize: "small"
192 opacity: 0.4
193 text: player.currentMetaArtist
194 }
195 }
196
197 /* Play/Pause button */
198 Icon {
199 id: playerControlsPlayButton
200 anchors {
201 right: parent.right
202 rightMargin: units.gu(3)
203 verticalCenter: parent.verticalCenter
204 }
205 color: "#FFF"
206 height: units.gu(2.5)
207 name: player.playbackState === MediaPlayer.PlayingState ?
208 "media-playback-pause" : "media-playback-start"
209 objectName: "playShape"
210 width: height
211 }
212
213 /* Mouse area to jump to now playing */
214 MouseArea {
215 anchors {
216 fill: parent
217 }
218 objectName: "jumpNowPlaying"
219
220 onClicked: tabs.pushNowPlaying()
221 }
222
223 /* Mouse area for the play button (ontop of the jump to now playing) */
224 MouseArea {
225 anchors {
226 bottom: parent.bottom
227 horizontalCenter: playerControlsPlayButton.horizontalCenter
228 top: parent.top
229 }
230 onClicked: player.toggle()
231 width: units.gu(8)
232
233 Rectangle {
234 anchors {
235 fill: parent
236 }
237 color: "#FFF"
238 opacity: parent.pressed ? 0.1 : 0
239
240 Behavior on opacity {
241 UbuntuNumberAnimation {
242 duration: UbuntuAnimation.FastDuration
243 }
244 }
245 }
246 }
247 }
248
249 /* Object which provides the progress bar when toolbar is minimized */
250 Rectangle {
251 id: playerControlsProgressBar
252 anchors {
253 bottom: parent.bottom
254 left: parent.left
255 right: parent.right
256 }
257 color: styleMusic.common.black
258 height: units.gu(0.25)
102259
103 Rectangle {260 Rectangle {
104 id: musicToolbarPlayerControls261 id: playerControlsProgressBarHint
105 anchors {262 anchors {
106 fill: parent263 left: parent.left
107 }264 top: parent.top
108 color: "#000"265 }
109 state: trackQueue.model.count === 0 ? "disabled" : "enabled"266 color: UbuntuColors.blue
110 states: [267 height: parent.height
111 State {268 width: player.duration > 0 ? (player.position / player.duration) * playerControlsProgressBar.width : 0
112 name: "disabled"269
113 PropertyChanges {270 Connections {
114 target: disabledPlayerControlsGroup271 target: player
115 visible: true272 onPositionChanged: {
116 }273 playerControlsProgressBarHint.width = (player.position / player.duration) * playerControlsProgressBar.width
117 PropertyChanges {274 }
118 target: enabledPlayerControlsGroup275 onStopped: {
119 visible: false276 playerControlsProgressBarHint.width = 0;
120 }
121 },
122 State {
123 name: "enabled"
124 PropertyChanges {
125 target: disabledPlayerControlsGroup
126 visible: false
127 }
128 PropertyChanges {
129 target: enabledPlayerControlsGroup
130 visible: true
131 }
132 }
133 ]
134
135 /* Disabled (empty state) controls */
136 Item {
137 id: disabledPlayerControlsGroup
138 anchors {
139 bottom: playerControlsProgressBar.top
140 left: parent.left
141 right: parent.right
142 top: parent.top
143 }
144
145 Label {
146 id: noSongsInQueueLabel
147 anchors {
148 left: parent.left
149 leftMargin: units.gu(2)
150 right: disabledPlayerControlsPlayButton.left
151 rightMargin: units.gu(2)
152 verticalCenter: parent.verticalCenter
153 }
154 color: styleMusic.playerControls.labelColor
155 text: i18n.tr("Tap to shuffle music")
156 fontSize: "large"
157 visible: !emptyPage.noMusic
158 wrapMode: Text.WordWrap
159 maximumLineCount: 2
160 }
161
162 /* Play/Pause button */
163 Icon {
164 id: disabledPlayerControlsPlayButton
165 anchors {
166 right: parent.right
167 rightMargin: units.gu(3)
168 verticalCenter: parent.verticalCenter
169 }
170 color: "#FFF"
171 height: units.gu(2.5)
172 name: player.playbackState === MediaPlayer.PlayingState ?
173 "media-playback-pause" : "media-playback-start"
174 objectName: "disabledSmallPlayShape"
175 width: height
176 }
177
178 /* Click to shuffle music */
179 MouseArea {
180 anchors {
181 fill: parent
182 }
183 onClicked: {
184 if (emptyPage.noMusic) {
185 return;
186 }
187
188 if (trackQueue.model.count === 0) {
189 playRandomSong();
190 }
191 else {
192 player.toggle();
193 }
194 }
195 }
196 }
197
198 /* Enabled (queue > 0) controls */
199 Item {
200 id: enabledPlayerControlsGroup
201 anchors {
202 bottom: playerControlsProgressBar.top
203 left: parent.left
204 right: parent.right
205 top: parent.top
206 }
207
208 /* Album art in player controls */
209 CoverGrid {
210 id: playerControlsImage
211 anchors {
212 bottom: parent.bottom
213 left: parent.left
214 top: parent.top
215 }
216 covers: [{art: player.currentMetaArt, author: player.currentMetaArtist, album: player.currentMetaArt}]
217 size: parent.height
218 }
219
220 /* Column of meta labels */
221 Column {
222 id: playerControlsLabels
223 anchors {
224 left: playerControlsImage.right
225 leftMargin: units.gu(1.5)
226 right: playerControlsPlayButton.left
227 rightMargin: units.gu(1)
228 verticalCenter: parent.verticalCenter
229 }
230
231 /* Title of track */
232 Label {
233 id: playerControlsTitle
234 anchors {
235 left: parent.left
236 right: parent.right
237 }
238 color: "#FFF"
239 elide: Text.ElideRight
240 fontSize: "small"
241 font.weight: Font.DemiBold
242 text: player.currentMetaTitle === ""
243 ? player.source : player.currentMetaTitle
244 }
245
246 /* Artist of track */
247 Label {
248 id: playerControlsArtist
249 anchors {
250 left: parent.left
251 right: parent.right
252 }
253 color: "#FFF"
254 elide: Text.ElideRight
255 fontSize: "small"
256 opacity: 0.4
257 text: player.currentMetaArtist
258 }
259 }
260
261 /* Play/Pause button */
262 Icon {
263 id: playerControlsPlayButton
264 anchors {
265 right: parent.right
266 rightMargin: units.gu(3)
267 verticalCenter: parent.verticalCenter
268 }
269 color: "#FFF"
270 height: units.gu(2.5)
271 name: player.playbackState === MediaPlayer.PlayingState ?
272 "media-playback-pause" : "media-playback-start"
273 objectName: "playShape"
274 width: height
275 }
276
277 MouseArea {
278 anchors {
279 bottom: parent.bottom
280 horizontalCenter: playerControlsPlayButton.horizontalCenter
281 top: parent.top
282 }
283 onClicked: player.toggle()
284 width: units.gu(8)
285
286 Rectangle {
287 anchors {
288 fill: parent
289 }
290 color: "#FFF"
291 opacity: parent.pressed ? 0.1 : 0
292
293 Behavior on opacity {
294 UbuntuNumberAnimation {
295 duration: UbuntuAnimation.FastDuration
296 }
297 }
298 }
299 }
300
301 /* Mouse area to jump to now playing */
302 Item {
303 anchors {
304 bottom: parent.bottom
305 left: parent.left
306 right: playerControlsLabels.right
307 top: parent.top
308 }
309 objectName: "jumpNowPlaying"
310 function trigger() {
311 tabs.pushNowPlaying();
312 }
313 }
314 }
315
316 /* Object which provides the progress bar when toolbar is minimized */
317 Rectangle {
318 id: playerControlsProgressBar
319 anchors {
320 bottom: parent.bottom
321 left: parent.left
322 right: parent.right
323 }
324 color: styleMusic.common.black
325 height: units.gu(0.25)
326
327 Rectangle {
328 id: playerControlsProgressBarHint
329 anchors {
330 left: parent.left
331 top: parent.top
332 }
333 color: UbuntuColors.blue
334 height: parent.height
335 width: player.duration > 0 ? (player.position / player.duration) * playerControlsProgressBar.width : 0
336
337 Connections {
338 target: player
339 onPositionChanged: {
340 playerControlsProgressBarHint.width = (player.position / player.duration) * playerControlsProgressBar.width
341 }
342 onStopped: {
343 playerControlsProgressBarHint.width = 0;
344 }
345 }
346 }277 }
347 }278 }
348 }279 }
349 }280 }
350 }281 }
351}282}
352
353283
=== added file 'app/components/NowPlaying.qml'
--- app/components/NowPlaying.qml 1970-01-01 00:00:00 +0000
+++ app/components/NowPlaying.qml 2015-02-10 03:13:44 +0000
@@ -0,0 +1,550 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@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 QtMultimedia 5.0
21import QtQuick 2.3
22import QtQuick.LocalStorage 2.0
23import Ubuntu.Components 1.1
24import Ubuntu.Thumbnailer 0.1
25import "../components"
26import "../components/Flickables"
27import "../components/HeadState"
28import "../components/ListItemActions"
29import "../components/Themes/Ambiance"
30import "../logic/meta-database.js" as Library
31import "../logic/playlists.js" as Playlists
32
33Item {
34 id: nowPlayingView
35 objectName: "nowPlayingView"
36
37 anchors {
38 fill: parent
39 }
40
41 property bool isListView: false
42
43 Item {
44 id: fullview
45 anchors {
46 top: parent.top
47 topMargin: mainView.header.height
48 }
49 height: parent.height - mainView.header.height - units.gu(9.5)
50 visible: !isListView || wideAspect
51 width: parent.width
52
53 BlurredBackground {
54 id: blurredBackground
55 anchors {
56 left: parent.left
57 right: parent.right
58 top: parent.top
59 }
60 art: albumImage.firstSource
61 height: parent.height - units.gu(7)
62
63 Item {
64 id: albumImageContainer
65 anchors {
66 horizontalCenter: parent.horizontalCenter
67 top: parent.top
68 }
69 height: parent.height
70 width: parent.width
71
72 CoverGrid {
73 id: albumImage
74 anchors.centerIn: parent
75 covers: [{art: player.currentMetaArt, author: player.currentMetaArtist, album: player.currentMetaAlbum}]
76 size: parent.width > parent.height ? parent.height : parent.width
77 }
78 }
79
80 Rectangle {
81 id: nowPlayingWideAspectLabelsBackground
82 anchors.bottom: parent.bottom
83 color: styleMusic.common.black
84 height: nowPlayingWideAspectTitle.lineCount === 1 ? units.gu(10) : units.gu(13)
85 opacity: 0.8
86 width: parent.width
87 }
88
89 /* Column for labels in wideAspect */
90 Column {
91 id: nowPlayingWideAspectLabels
92 spacing: units.gu(1)
93 anchors {
94 left: parent.left
95 leftMargin: units.gu(2)
96 right: parent.right
97 rightMargin: units.gu(2)
98 top: nowPlayingWideAspectLabelsBackground.top
99 topMargin: nowPlayingWideAspectTitle.lineCount === 1 ? units.gu(2) : units.gu(1.5)
100 }
101
102 /* Title of track */
103 Label {
104 id: nowPlayingWideAspectTitle
105 anchors {
106 left: parent.left
107 leftMargin: units.gu(1)
108 right: parent.right
109 rightMargin: units.gu(1)
110 }
111 color: styleMusic.playerControls.labelColor
112 elide: Text.ElideRight
113 fontSize: "x-large"
114 maximumLineCount: 2
115 objectName: "playercontroltitle"
116 text: trackQueue.model.count === 0 ? "" : player.currentMetaTitle === "" ? player.currentMetaFile : player.currentMetaTitle
117 wrapMode: Text.WordWrap
118 }
119
120 /* Artist of track */
121 Label {
122 id: nowPlayingWideAspectArtist
123 anchors {
124 left: parent.left
125 leftMargin: units.gu(1)
126 right: parent.right
127 rightMargin: units.gu(1)
128 }
129 color: styleMusic.nowPlaying.labelSecondaryColor
130 elide: Text.ElideRight
131 fontSize: "small"
132 text: trackQueue.model.count === 0 ? "" : player.currentMetaArtist
133 }
134 }
135
136 /* Detect cover art swipe */
137 MouseArea {
138 anchors.fill: parent
139 property string direction: "None"
140 property real lastX: -1
141
142 onPressed: lastX = mouse.x
143
144 onReleased: {
145 var diff = mouse.x - lastX
146 if (Math.abs(diff) < units.gu(4)) {
147 return;
148 } else if (diff < 0) {
149 player.nextSong()
150 } else if (diff > 0) {
151 player.previousSong()
152 }
153 }
154 }
155 }
156
157 /* Background for progress bar component */
158 Rectangle {
159 id: musicToolbarFullProgressBackground
160 anchors {
161 bottom: parent.bottom
162 left: parent.left
163 right: parent.right
164 top: blurredBackground.bottom
165 }
166 color: styleMusic.common.black
167 }
168
169 /* Progress bar component */
170 Item {
171 id: musicToolbarFullProgressContainer
172 anchors.left: parent.left
173 anchors.leftMargin: units.gu(3)
174 anchors.right: parent.right
175 anchors.rightMargin: units.gu(3)
176 anchors.top: blurredBackground.bottom
177 anchors.topMargin: units.gu(1)
178 height: units.gu(3)
179 width: parent.width
180
181 /* Position label */
182 Label {
183 id: musicToolbarFullPositionLabel
184 anchors.top: progressSliderMusic.bottom
185 anchors.topMargin: units.gu(-2)
186 anchors.left: parent.left
187 color: styleMusic.nowPlaying.labelSecondaryColor
188 fontSize: "small"
189 height: parent.height
190 horizontalAlignment: Text.AlignHCenter
191 text: durationToString(player.position)
192 verticalAlignment: Text.AlignVCenter
193 width: units.gu(3)
194 }
195
196 Slider {
197 id: progressSliderMusic
198 anchors.left: parent.left
199 anchors.right: parent.right
200 maximumValue: player.duration // load value at startup
201 objectName: "progressSliderShape"
202 style: UbuntuBlueSliderStyle {}
203 value: player.position // load value at startup
204
205 function formatValue(v) {
206 if (seeking) { // update position label while dragging
207 musicToolbarFullPositionLabel.text = durationToString(v)
208 }
209
210 return durationToString(v)
211 }
212
213 property bool seeking: false
214 property bool seeked: false
215
216 onSeekingChanged: {
217 if (seeking === false) {
218 musicToolbarFullPositionLabel.text = durationToString(player.position)
219 }
220 }
221
222 onPressedChanged: {
223 seeking = pressed
224
225 if (!pressed) {
226 seeked = true
227 player.seek(value)
228
229 musicToolbarFullPositionLabel.text = durationToString(value)
230 }
231 }
232
233 Connections {
234 target: player
235 onPositionChanged: {
236 // seeked is a workaround for bug 1310706 as the first position after a seek is sometimes invalid (0)
237 if (progressSliderMusic.seeking === false && !progressSliderMusic.seeked) {
238 musicToolbarFullPositionLabel.text = durationToString(player.position)
239 musicToolbarFullDurationLabel.text = durationToString(player.duration)
240
241 progressSliderMusic.value = player.position
242 progressSliderMusic.maximumValue = player.duration
243 }
244
245 progressSliderMusic.seeked = false;
246 }
247 onStopped: {
248 musicToolbarFullPositionLabel.text = durationToString(0);
249 musicToolbarFullDurationLabel.text = durationToString(0);
250 }
251 }
252 }
253
254 /* Duration label */
255 Label {
256 id: musicToolbarFullDurationLabel
257 anchors.top: progressSliderMusic.bottom
258 anchors.topMargin: units.gu(-2)
259 anchors.right: parent.right
260 color: styleMusic.nowPlaying.labelSecondaryColor
261 fontSize: "small"
262 height: parent.height
263 horizontalAlignment: Text.AlignHCenter
264 text: durationToString(player.duration)
265 verticalAlignment: Text.AlignVCenter
266 width: units.gu(3)
267 }
268 }
269 }
270
271 Loader {
272 id: queueListLoader
273 anchors {
274 fill: parent
275 }
276 asynchronous: true
277 sourceComponent: MultiSelectListView {
278 id: queueList
279 anchors {
280 bottomMargin: musicToolbarFullContainer.height + units.gu(2)
281 fill: parent
282 topMargin: units.gu(2)
283 }
284 delegate: queueDelegate
285 footer: Item {
286 height: mainView.height - (styleMusic.common.expandHeight + queueList.currentHeight) + units.gu(8)
287 }
288 model: trackQueue.model
289 objectName: "nowPlayingqueueList"
290
291 property int normalHeight: units.gu(6)
292 property int transitionDuration: 250 // transition length of animations
293
294 onCountChanged: customdebug("Queue: Now has: " + queueList.count + " tracks")
295
296 Component {
297 id: queueDelegate
298 ListItemWithActions {
299 id: queueListItem
300 color: player.currentIndex === index ? "#2c2c34" : styleMusic.mainView.backgroundColor
301 height: queueList.normalHeight
302 objectName: "nowPlayingListItem" + index
303 state: ""
304
305 leftSideAction: Remove {
306 onTriggered: trackQueue.removeQueueList([index])
307 }
308 multiselectable: true
309 reorderable: true
310 rightSideActions: [
311 AddToPlaylist{
312
313 }
314 ]
315
316 onItemClicked: {
317 customdebug("File: " + model.filename) // debugger
318 trackQueueClick(index); // toggle track state
319 }
320 onReorder: {
321 console.debug("Move: ", from, to);
322
323 trackQueue.model.move(from, to, 1);
324 Library.moveQueueItem(from, to);
325
326 // Maintain currentIndex with current song
327 if (from === player.currentIndex) {
328 player.currentIndex = to;
329 }
330 else if (from < player.currentIndex && to >= player.currentIndex) {
331 player.currentIndex -= 1;
332 }
333 else if (from > player.currentIndex && to <= player.currentIndex) {
334 player.currentIndex += 1;
335 }
336
337 queueIndex = player.currentIndex
338 }
339
340 Item {
341 id: trackContainer;
342 anchors {
343 fill: parent
344 }
345
346 NumberAnimation {
347 id: trackContainerReorderAnimation
348 target: trackContainer;
349 property: "anchors.leftMargin";
350 duration: queueList.transitionDuration;
351 to: units.gu(2)
352 }
353
354 NumberAnimation {
355 id: trackContainerResetAnimation
356 target: trackContainer;
357 property: "anchors.leftMargin";
358 duration: queueList.transitionDuration;
359 to: units.gu(0.5)
360 }
361
362 MusicRow {
363 id: musicRow
364 height: parent.height
365 column: Column {
366 Label {
367 id: trackTitle
368 color: player.currentIndex === index ? UbuntuColors.blue
369 : styleMusic.common.music
370 fontSize: "small"
371 objectName: "titleLabel"
372 text: model.title
373 }
374
375 Label {
376 id: trackArtist
377 color: styleMusic.common.subtitle
378 fontSize: "x-small"
379 objectName: "artistLabel"
380 text: model.author
381 }
382 }
383 }
384 }
385 }
386 }
387 }
388 visible: isListView || wideAspect
389 }
390
391 /* Full toolbar */
392 Rectangle {
393 id: musicToolbarFullContainer
394 anchors.bottom: parent.bottom
395 color: styleMusic.common.black
396 height: units.gu(10)
397 width: parent.width
398
399 /* Repeat button */
400 MouseArea {
401 id: nowPlayingRepeatButton
402 anchors.right: nowPlayingPreviousButton.left
403 anchors.rightMargin: units.gu(1)
404 anchors.verticalCenter: nowPlayingPlayButton.verticalCenter
405 height: units.gu(6)
406 opacity: player.repeat && !emptyPageLoader.noMusic ? 1 : .4
407 width: height
408 onClicked: player.repeat = !player.repeat
409
410 Icon {
411 id: repeatIcon
412 height: units.gu(3)
413 width: height
414 anchors.verticalCenter: parent.verticalCenter
415 anchors.horizontalCenter: parent.horizontalCenter
416 color: "white"
417 name: "media-playlist-repeat"
418 objectName: "repeatShape"
419 opacity: player.repeat && !emptyPageLoader.noMusic ? 1 : .4
420 }
421 }
422
423 /* Previous button */
424 MouseArea {
425 id: nowPlayingPreviousButton
426 anchors.right: nowPlayingPlayButton.left
427 anchors.rightMargin: units.gu(1)
428 anchors.verticalCenter: nowPlayingPlayButton.verticalCenter
429 height: units.gu(6)
430 opacity: trackQueue.model.count === 0 ? .4 : 1
431 width: height
432 onClicked: player.previousSong()
433
434 Icon {
435 id: nowPlayingPreviousIndicator
436 height: units.gu(3)
437 width: height
438 anchors.verticalCenter: parent.verticalCenter
439 anchors.horizontalCenter: parent.horizontalCenter
440 color: "white"
441 name: "media-skip-backward"
442 objectName: "previousShape"
443 opacity: 1
444 }
445 }
446
447 /* Play/Pause button */
448 MouseArea {
449 id: nowPlayingPlayButton
450 anchors.centerIn: parent
451 height: units.gu(10)
452 width: height
453 onClicked: player.toggle()
454
455 Icon {
456 id: nowPlayingPlayIndicator
457 height: units.gu(6)
458 width: height
459 anchors.verticalCenter: parent.verticalCenter
460 anchors.horizontalCenter: parent.horizontalCenter
461 opacity: emptyPageLoader.noMusic ? .4 : 1
462 color: "white"
463 name: player.playbackState === MediaPlayer.PlayingState ? "media-playback-pause" : "media-playback-start"
464 objectName: "playShape"
465 }
466 }
467
468 /* Next button */
469 MouseArea {
470 id: nowPlayingNextButton
471 anchors.left: nowPlayingPlayButton.right
472 anchors.leftMargin: units.gu(1)
473 anchors.verticalCenter: nowPlayingPlayButton.verticalCenter
474 height: units.gu(6)
475 opacity: trackQueue.model.count === 0 ? .4 : 1
476 width: height
477 onClicked: player.nextSong()
478
479 Icon {
480 id: nowPlayingNextIndicator
481 height: units.gu(3)
482 width: height
483 anchors.verticalCenter: parent.verticalCenter
484 anchors.horizontalCenter: parent.horizontalCenter
485 color: "white"
486 name: "media-skip-forward"
487 objectName: "forwardShape"
488 opacity: 1
489 }
490 }
491
492 /* Shuffle button */
493 MouseArea {
494 id: nowPlayingShuffleButton
495 anchors.left: nowPlayingNextButton.right
496 anchors.leftMargin: units.gu(1)
497 anchors.verticalCenter: nowPlayingPlayButton.verticalCenter
498 height: units.gu(6)
499 opacity: player.shuffle && !emptyPageLoader.noMusic ? 1 : .4
500 width: height
501 onClicked: player.shuffle = !player.shuffle
502
503 Icon {
504 id: shuffleIcon
505 height: units.gu(3)
506 width: height
507 anchors.verticalCenter: parent.verticalCenter
508 anchors.horizontalCenter: parent.horizontalCenter
509 color: "white"
510 name: "media-playlist-shuffle"
511 objectName: "shuffleShape"
512 opacity: player.shuffle && !emptyPageLoader.noMusic ? 1 : .4
513 }
514 }
515
516 /* Object which provides the progress bar when in the queue */
517 Rectangle {
518 id: playerControlsProgressBar
519 anchors {
520 bottom: parent.bottom
521 left: parent.left
522 right: parent.right
523 }
524 color: styleMusic.common.black
525 height: units.gu(0.25)
526 visible: isListView || wideAspect
527
528 Rectangle {
529 id: playerControlsProgressBarHint
530 anchors {
531 left: parent.left
532 bottom: parent.bottom
533 }
534 color: UbuntuColors.blue
535 height: parent.height
536 width: player.duration > 0 ? (player.position / player.duration) * playerControlsProgressBar.width : 0
537
538 Connections {
539 target: player
540 onPositionChanged: {
541 playerControlsProgressBarHint.width = (player.position / player.duration) * playerControlsProgressBar.width
542 }
543 onStopped: {
544 playerControlsProgressBarHint.width = 0;
545 }
546 }
547 }
548 }
549 }
550}
0551
=== renamed file 'Player.qml' => 'app/components/Player.qml'
=== added file 'app/components/PlaylistsEmptyState.qml'
--- app/components/PlaylistsEmptyState.qml 1970-01-01 00:00:00 +0000
+++ app/components/PlaylistsEmptyState.qml 2015-02-10 03:13:44 +0000
@@ -0,0 +1,58 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@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
24Rectangle {
25 id: playlistsEmptyState
26 anchors {
27 fill: parent
28 }
29 color: mainView.backgroundColor
30
31 Column {
32 anchors.centerIn: parent
33 spacing: units.gu(4)
34 width: units.gu(36)
35
36 Label {
37 color: styleMusic.libraryEmpty.labelColor
38 elide: Text.ElideRight
39 fontSize: "x-large"
40 horizontalAlignment: Text.AlignLeft
41 maximumLineCount: 2
42 text: i18n.tr("No playlists found")
43 width: parent.width
44 wrapMode: Text.WordWrap
45 }
46
47 Label {
48 color: styleMusic.libraryEmpty.labelColor
49 elide: Text.ElideRight
50 fontSize: "large"
51 horizontalAlignment: Text.AlignLeft
52 maximumLineCount: 4
53 text: i18n.tr("Get more out of Music by tapping the %1 icon to start making playlists for every mood and occasion.").arg('"+"')
54 width: parent.width
55 wrapMode: Text.WordWrap
56 }
57 }
58}
059
=== renamed file 'Style.qml' => 'app/components/Style.qml'
=== added directory 'app/components/ViewButton'
=== added file 'app/components/ViewButton/CMakeLists.txt'
--- app/components/ViewButton/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ app/components/ViewButton/CMakeLists.txt 2015-02-10 03:13:44 +0000
@@ -0,0 +1,4 @@
1# make the qml files visible on qtcreator
2file(GLOB VIEW_BUTTON_QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml)
3
4add_custom_target(com_ubuntu_music_VIEW_BUTTON_QMLFiles ALL SOURCES ${VIEW_BUTTON_QML_FILES})
05
=== added file 'app/components/ViewButton/PlayAllButton.qml'
--- app/components/ViewButton/PlayAllButton.qml 1970-01-01 00:00:00 +0000
+++ app/components/ViewButton/PlayAllButton.qml 2015-02-10 03:13:44 +0000
@@ -0,0 +1,34 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@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
24Button {
25 color: UbuntuColors.green
26 height: units.gu(4)
27 // TRANSLATORS: this appears in a button with limited space (around 14 characters)
28 text: i18n.tr("Play all")
29 width: units.gu(15)
30
31 property var model
32
33 onClicked: trackClicked(model, 0) // play track
34}
035
=== added file 'app/components/ViewButton/QueueAllButton.qml'
--- app/components/ViewButton/QueueAllButton.qml 1970-01-01 00:00:00 +0000
+++ app/components/ViewButton/QueueAllButton.qml 2015-02-10 03:13:44 +0000
@@ -0,0 +1,46 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@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
24Button {
25 height: units.gu(4)
26 strokeColor: UbuntuColors.green
27 width: units.gu(15)
28
29 property var model
30
31 onClicked: addQueueFromModel(model)
32
33 Text {
34 anchors {
35 centerIn: parent
36 }
37 color: "white"
38 elide: Text.ElideRight
39 height: parent.height
40 horizontalAlignment: Text.AlignHCenter
41 // TRANSLATORS: this appears in a button with limited space (around 14 characters)
42 text: i18n.tr("Queue all")
43 verticalAlignment: Text.AlignVCenter
44 width: parent.width - units.gu(2)
45 }
46}
047
=== added file 'app/components/ViewButton/ShuffleButton.qml'
--- app/components/ViewButton/ShuffleButton.qml 1970-01-01 00:00:00 +0000
+++ app/components/ViewButton/ShuffleButton.qml 2015-02-10 03:13:44 +0000
@@ -0,0 +1,46 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@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
24Button {
25 height: units.gu(4)
26 strokeColor: UbuntuColors.green
27 width: units.gu(15)
28
29 property var model
30
31 onClicked: shuffleModel(model)
32
33 Text {
34 anchors {
35 centerIn: parent
36 }
37 color: "white"
38 elide: Text.ElideRight
39 height: parent.height
40 horizontalAlignment: Text.AlignHCenter
41 // TRANSLATORS: this appears in a button with limited space (around 14 characters)
42 text: i18n.tr("Shuffle")
43 verticalAlignment: Text.AlignVCenter
44 width: parent.width - units.gu(2)
45 }
46}
047
=== modified file 'app/components/Walkthrough/Slide1.qml'
--- common/Walkthrough/Slide1.qml 2015-02-02 14:03:12 +0000
+++ app/components/Walkthrough/Slide1.qml 2015-02-10 03:13:44 +0000
@@ -39,7 +39,7 @@
39 height: (parent.height - bodyText.contentHeight - introductionText.height - 4*units.gu(4))/239 height: (parent.height - bodyText.contentHeight - introductionText.height - 4*units.gu(4))/2
40 image: Image {40 image: Image {
41 id: centerImage41 id: centerImage
42 source: Qt.resolvedUrl("../../images/music-app@30.png")42 source: Qt.resolvedUrl("../../graphics/music-app@30.png")
43 }43 }
44 radius: "medium"44 radius: "medium"
45 width: height45 width: height
4646
=== modified file 'app/components/Walkthrough/Slide2.qml'
--- common/Walkthrough/Slide2.qml 2015-02-02 13:37:23 +0000
+++ app/components/Walkthrough/Slide2.qml 2015-02-10 03:13:44 +0000
@@ -38,7 +38,7 @@
38 }38 }
39 height: (parent.height - bodyText.contentHeight - introductionText.height - 4*units.gu(4))/239 height: (parent.height - bodyText.contentHeight - introductionText.height - 4*units.gu(4))/2
40 fillMode: Image.PreserveAspectFit40 fillMode: Image.PreserveAspectFit
41 source: Qt.resolvedUrl("../../images/sd_phone_icon.png")41 source: Qt.resolvedUrl("../../graphics/sd_phone_icon.png")
42 }42 }
4343
44 Label {44 Label {
4545
=== modified file 'app/components/Walkthrough/Slide3.qml'
--- common/Walkthrough/Slide3.qml 2015-02-02 13:37:23 +0000
+++ app/components/Walkthrough/Slide3.qml 2015-02-10 03:13:44 +0000
@@ -38,7 +38,7 @@
38 }38 }
39 height: (parent.height - introductionText.height - finalMessage.contentHeight - 4.5*units.gu(4))/239 height: (parent.height - introductionText.height - finalMessage.contentHeight - 4.5*units.gu(4))/2
40 fillMode: Image.PreserveAspectFit40 fillMode: Image.PreserveAspectFit
41 source: Qt.resolvedUrl("../../images/music_download_icon.png")41 source: Qt.resolvedUrl("../../graphics/music_download_icon.png")
42 }42 }
4343
44 Label {44 Label {
4545
=== modified file 'app/components/Walkthrough/Walkthrough.qml'
--- common/Walkthrough/Walkthrough.qml 2015-01-31 02:47:16 +0000
+++ app/components/Walkthrough/Walkthrough.qml 2015-02-10 03:13:44 +0000
@@ -134,7 +134,7 @@
134 anchors.verticalCenter: parent.verticalCenter134 anchors.verticalCenter: parent.verticalCenter
135 antialiasing: true135 antialiasing: true
136 height: width136 height: width
137 source: listView.currentIndex == index ? "../../images/Ellipse@27.png" : "../../images/Ellipse_15_opacity@27.png"137 source: listView.currentIndex == index ? "../../graphics/Ellipse@27.png" : "../../graphics/Ellipse_15_opacity@27.png"
138 width: units.gu(1.5)138 width: units.gu(1.5)
139 }139 }
140 }140 }
141141
=== renamed file 'WorkerModelLoader.qml' => 'app/components/WorkerModelLoader.qml'
--- WorkerModelLoader.qml 2015-02-01 03:35:07 +0000
+++ app/components/WorkerModelLoader.qml 2015-02-10 03:13:44 +0000
@@ -21,7 +21,7 @@
2121
22WorkerScript {22WorkerScript {
23 id: worker23 id: worker
24 source: "worker-library-loader.js"24 source: "../logic/worker-library-loader.js"
2525
26 property bool canLoad: true26 property bool canLoad: true
27 property bool completed: false27 property bool completed: false
2828
=== renamed directory 'images' => 'app/graphics'
=== added directory 'app/logic'
=== added file 'app/logic/CMakeLists.txt'
--- app/logic/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ app/logic/CMakeLists.txt 2015-02-10 03:13:44 +0000
@@ -0,0 +1,4 @@
1# make the qml files visible on qtcreator
2file(GLOB LOGIC_JS_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.js)
3
4add_custom_target(com_ubuntu_music_LOGIC_JSFiles ALL SOURCES ${LOGIC_JS_FILES})
05
=== renamed file 'meta-database.js' => 'app/logic/meta-database.js'
=== renamed file 'playlists.js' => 'app/logic/playlists.js'
=== renamed file 'worker-library-loader.js' => 'app/logic/worker-library-loader.js'
=== renamed file 'music-app.qml' => 'app/music-app.qml'
--- music-app.qml 2015-02-05 22:40:14 +0000
+++ app/music-app.qml 2015-02-10 03:13:44 +0000
@@ -22,15 +22,17 @@
22import Ubuntu.Components.Popups 1.022import Ubuntu.Components.Popups 1.0
23import Ubuntu.Components.ListItems 1.0 as ListItem23import Ubuntu.Components.ListItems 1.0 as ListItem
24import Ubuntu.Content 0.124import Ubuntu.Content 0.1
25import Ubuntu.Layouts 1.0
25import Ubuntu.MediaScanner 0.126import Ubuntu.MediaScanner 0.1
26import Qt.labs.settings 1.027import Qt.labs.settings 1.0
27import QtMultimedia 5.028import QtMultimedia 5.0
28import QtQuick.LocalStorage 2.029import QtQuick.LocalStorage 2.0
29import QtGraphicalEffects 1.030import QtGraphicalEffects 1.0
30import UserMetrics 0.131import UserMetrics 0.1
31import "meta-database.js" as Library32import "logic/meta-database.js" as Library
32import "playlists.js" as Playlists33import "logic/playlists.js" as Playlists
33import "common"34import "components"
35import "ui"
3436
35MainView {37MainView {
36 objectName: "music"38 objectName: "music"
@@ -55,10 +57,10 @@
55 focus: true57 focus: true
56 Keys.onPressed: {58 Keys.onPressed: {
57 if(event.key === Qt.Key_Escape) {59 if(event.key === Qt.Key_Escape) {
58 if (musicToolbar.currentPage.searchable && musicToolbar.currentPage.state === "search") {60 if (mainPageStack.currentMusicPage.searchable && mainPageStack.currentMusicPage.state === "search") {
59 musicToolbar.currentPage.state = "default"61 mainPageStack.currentMusicPage.state = "default"
60 } else {62 } else {
61 musicToolbar.goBack(); // Esc Go back63 mainPageStack.goBack(); // Esc Go back
62 }64 }
63 }65 }
64 else if(event.modifiers === Qt.AltModifier) {66 else if(event.modifiers === Qt.AltModifier) {
@@ -95,8 +97,8 @@
95 player.repeat = !player.repeat97 player.repeat = !player.repeat
96 break;98 break;
97 case Qt.Key_F: // Ctrl+F Show Search popup99 case Qt.Key_F: // Ctrl+F Show Search popup
98 if (musicToolbar.currentPage.searchable && musicToolbar.currentPage.state === "default") {100 if (mainPageStack.currentMusicPage.searchable && mainPageStack.currentMusicPage.state === "default") {
99 musicToolbar.currentPage.state = "search"101 mainPageStack.currentMusicPage.state = "search"
100 header.show()102 header.show()
101 }103 }
102104
@@ -159,7 +161,7 @@
159 id: backAction161 id: backAction
160 text: i18n.tr("Back")162 text: i18n.tr("Back")
161 keywords: i18n.tr("Go back to last page")163 keywords: i18n.tr("Go back to last page")
162 onTriggered: musicToolbar.goBack();164 onTriggered: mainPageStack.goBack();
163 }165 }
164166
165 // With a default Quit action only the first 4 actions are displayed167 // With a default Quit action only the first 4 actions are displayed
@@ -304,7 +306,7 @@
304306
305 if (success === true) {307 if (success === true) {
306 if (contentHubWaitForFile.processId === -1) {308 if (contentHubWaitForFile.processId === -1) {
307 contentHubWaitForFile.dialog = PopupUtils.open(contentHubWait, mainView)309 contentHubWaitForFile.dialog = PopupUtils.open(Qt.resolvedUrl("components/Dialog/ContentHubWaitDialog.qml"), mainView)
308 contentHubWaitForFile.searchPaths = contentHub.searchPaths;310 contentHubWaitForFile.searchPaths = contentHub.searchPaths;
309 contentHubWaitForFile.processId = processId;311 contentHubWaitForFile.processId = processId;
310 contentHubWaitForFile.start();312 contentHubWaitForFile.start();
@@ -318,7 +320,7 @@
318 }320 }
319 }321 }
320 else {322 else {
321 var errordialog = PopupUtils.open(contentHubError, mainView)323 var errordialog = PopupUtils.open(Qt.resolvedUrl("components/Dialog/ContentHubErrorDialog.qml"), mainView)
322 errordialog.errorText = err.join("\n")324 errordialog.errorText = err.join("\n")
323 }325 }
324 }326 }
@@ -416,7 +418,7 @@
416 stopTimer();418 stopTimer();
417419
418 console.debug("File(s) were not found", JSON.stringify(searchPaths))420 console.debug("File(s) were not found", JSON.stringify(searchPaths))
419 PopupUtils.open(contentHubNotFound, mainView)421 PopupUtils.open(Qt.resolvedUrl("components/Dialog/ContentHubNotFoundDialog.qml"), mainView)
420 }422 }
421 }423 }
422 else {424 else {
@@ -435,67 +437,6 @@
435 }437 }
436 }438 }
437439
438 Component {
439 id: contentHubWait
440 Dialog {
441 id: dialogContentHubWait
442
443 LoadingSpinnerComponent {
444 anchors {
445 margins: units.gu(0)
446 }
447 loadingText: i18n.tr("Waiting for file(s)...")
448 visible: true
449 }
450 }
451 }
452
453 Component {
454 id: contentHubError
455 Dialog {
456 id: dialogContentHubError
457
458 property alias errorText: errorLabel.text
459
460 Label {
461 id: errorLabel
462 color: styleMusic.common.black
463 }
464
465 Button {
466 text: i18n.tr("OK")
467 onClicked: PopupUtils.close(dialogContentHubError)
468 }
469 }
470 }
471
472 Component {
473 id: contentHubNotFound
474 Dialog {
475 id: dialogContentHubNotFound
476
477 Label {
478 color: styleMusic.common.black
479 text: i18n.tr("Imported file not found")
480 }
481
482 Button {
483 text: i18n.tr("Wait")
484 onClicked: {
485 PopupUtils.close(dialogContentHubNotFound)
486
487 contentHubWaitForFile.dialog = PopupUtils.open(contentHubWait, mainView)
488 contentHubWaitForFile.start();
489 }
490 }
491
492 Button {
493 text: i18n.tr("Cancel")
494 onClicked: PopupUtils.close(dialogContentHubNotFound)
495 }
496 }
497 }
498
499 // UserMetrics to show Music stuff on welcome screen440 // UserMetrics to show Music stuff on welcome screen
500 Metric {441 Metric {
501 id: songsMetric442 id: songsMetric
@@ -607,9 +548,7 @@
607548
608 // Display walkthrough on first run, even if the user has music549 // Display walkthrough on first run, even if the user has music
609 if (firstRun) {550 if (firstRun) {
610 var comp = Qt.createComponent("common/Walkthrough/FirstRunWalkthrough.qml")551 mainPageStack.push(Qt.resolvedUrl("components/Walkthrough/FirstRunWalkthrough.qml"), {})
611 var walkthrough = comp.createObject(mainPageStack, {});
612 mainPageStack.push(walkthrough)
613 }552 }
614553
615 if (args.values.url) {554 if (args.values.url) {
@@ -808,10 +747,10 @@
808747
809 // TODO: improve in refactoring to be able detect when a track is removed748 // TODO: improve in refactoring to be able detect when a track is removed
810 // Update playlists page749 // Update playlists page
811 if (musicPlaylistPage.visible) {750 if (playlistsPage.visible) {
812 playlistModel.filterPlaylists()751 playlistModel.filterPlaylists()
813 } else {752 } else {
814 musicPlaylistPage.changed = true753 playlistsPage.changed = true
815 }754 }
816 }755 }
817 }756 }
@@ -847,10 +786,10 @@
847 console.debug("Removed recent:", JSON.stringify(removed))786 console.debug("Removed recent:", JSON.stringify(removed))
848 Library.recentRemoveAlbums(removed)787 Library.recentRemoveAlbums(removed)
849788
850 if (musicStartPage.visible) {789 if (recentPage.visible) {
851 recentModel.filterRecent()790 recentModel.filterRecent()
852 } else {791 } else {
853 musicStartPage.changed = true792 recentPage.changed = true
854 }793 }
855 }794 }
856 }795 }
@@ -965,7 +904,7 @@
965 if (trackQueue.model.count === 0) {904 if (trackQueue.model.count === 0) {
966 // Nothing in the queue so stop and pop the queue905 // Nothing in the queue so stop and pop the queue
967 player.stop()906 player.stop()
968 musicToolbar.goBack()907 mainPageStack.goBack()
969 } else if (items.indexOf(player.currentIndex) > -1) {908 } else if (items.indexOf(player.currentIndex) > -1) {
970 // Current track was removed909 // Current track was removed
971910
@@ -1022,6 +961,7 @@
1022 if (preLoadComplete)961 if (preLoadComplete)
1023 {962 {
1024 loading.visible = false963 loading.visible = false
964<<<<<<< TREE
1025 playlistTab.loading = false965 playlistTab.loading = false
1026 playlistTab.populated = true966 playlistTab.populated = true
1027 }967 }
@@ -1401,82 +1341,339 @@
14011341
1402 Image {1342 Image {
1403 id: imageEmptySD1343 id: imageEmptySD
1344=======
1345 playlistsTab.loading = false
1346 playlistsTab.populated = true
1347 }
1348 }
1349 }
1350
1351 Layouts {
1352 objectName: "musicLayout"
1353 id: layouts
1354 anchors.fill: parent
1355 layouts: [
1356 ConditionalLayout {
1357 name: "wideAspect"
1358 when: wideAspect
1359 Rectangle {
1360 anchors.fill: parent
1361 color: "transparent"
1362 Rectangle {
1363 width: layouts.width*0.375
1364>>>>>>> MERGE-SOURCE
1404 anchors {1365 anchors {
1405 verticalCenter: parent.verticalCenter1366 top: parent.top
1406 }1367 bottom:parent.bottom
1407 antialiasing: true1368 right: parent.right
1408 fillMode: Image.PreserveAspectFit1369 }
1409 height: units.gu(7)1370 color: "transparent"
1410 smooth: true1371
1411 source: "images/music_empty_SD.png"1372 NowPlaying {
1412 }1373 id: nowPlaying
1413 }1374 Layouts.item: "nowPlaying"
14141375 }
1415 Label {1376 }
1416 color: styleMusic.libraryEmpty.labelColor1377 }
1417 elide: Text.ElideRight1378 }
1418 fontSize: "x-large"1379 ]
1419 horizontalAlignment: Text.AlignLeft1380
1420 maximumLineCount: 21381 Loader {
1421 text: i18n.tr("No music found")1382 id: musicToolbar
1422 width: parent.width1383 anchors {
1423 wrapMode: Text.WordWrap1384 bottom: parent.bottom
1424 }1385 left: parent.left
14251386 right: parent.right
1426 Label {1387 }
1427 color: styleMusic.libraryEmpty.labelColor1388 asynchronous: true
1428 elide: Text.ElideRight1389 source: "components/MusicToolbar.qml"
1429 fontSize: "large"1390 visible: mainPageStack.currentPage.title !== i18n.tr("Now playing") &&
1430 horizontalAlignment: Text.AlignLeft1391 mainPageStack.currentPage.title !== i18n.tr("Queue") &&
1431 maximumLineCount: 41392 mainPageStack.currentPage.title !== i18n.tr("Select playlist") &&
1432 text: i18n.tr("Connect your device to any computer and simply drag files to the Music folder or insert removable media with music.")1393 !firstRun &&
1433 width: parent.width1394 !wideAspect
1434 wrapMode: Text.WordWrap1395 z: 200 // put on top of everything else
1435 }1396 }
1436 }1397
1437 }1398 PageStack {
14381399 id: mainPageStack
1439 // Overlay to show when no playlists are on the device1400
1440 Rectangle {1401 // Properties storing the current page info
1441 id: playlistsEmpty1402 property Page currentMusicPage: null // currentPage can be Tabs
1403 property bool popping: false
1404
1405 /* Helper functions */
1406
1407 // Go back up the stack if possible
1408 function goBack() {
1409 if (depth > 1) {
1410 pop()
1411 }
1412 }
1413
1414 // Pop a specific page in the stack
1415 function popPage(page) {
1416 var tmpPages = []
1417
1418 popping = true
1419
1420 while (currentPage !== page && depth > 0) {
1421 tmpPages.push(currentPage)
1422 pop()
1423 }
1424
1425 if (depth > 0) {
1426 pop()
1427 }
1428
1429 for (var i=tmpPages.length - 1; i > -1; i--) {
1430 push(tmpPages[i])
1431 }
1432
1433 popping = false
1434 }
1435
1436 // Set the current page, and any parent/stacks
1437 function setPage(childPage) {
1438 if (!popping) {
1439 currentMusicPage = childPage;
1440 }
1441 }
1442
1443 Tabs {
1444 id: tabs
1445 anchors {
1446 fill: parent
1447 }
1448
1449 property Tab lastTab: selectedTab
1450
1451 onSelectedTabChanged: {
1452 // pause loading of the models in the old tab
1453 if (lastTab !== null && lastTab !== selectedTab) {
1454 allowLoading(lastTab, false);
1455 }
1456
1457 lastTab = selectedTab;
1458
1459 ensurePopulated(selectedTab);
1460 }
1461
1462 onSelectedTabIndexChanged: {
1463 if (loadedUI) { // store the tab index if changed by the user
1464 startupSettings.tabIndex = selectedTabIndex
1465 }
1466 }
1467
1468 // Use a repeater to 'hide' the recent tab when the model is empty
1469 // A repeater is used because the Tabs component respects adds and
1470 // removes. Whereas replacing the list tabChildren does not appear
1471 // to respect removes and setting the page as active: false causes
1472 // the page to be blank but the action to still in the overflow
1473 Repeater {
1474 id: recentTabRepeater
1475 // If the model has not loaded and at startup the db was not empty
1476 // then show recent or
1477 // If the workerlist has been set and it has values then show recent
1478 model: (!recentModel.preLoadComplete && !startupRecentEmpty) ||
1479 (recentModel.workerList !== undefined &&
1480 recentModel.workerList.length > 0) ? 1 : 0
1481 delegate: Component {
1482 // First tab is all music
1483 Tab {
1484 property bool populated: recentTabRepeater.populated
1485 property var loader: [recentModel.filterRecent]
1486 property bool loading: recentTabRepeater.loading
1487 property var model: [recentModel, albumTracksModel]
1488 id: recentTab
1489 objectName: "recentTab"
1490 anchors.fill: parent
1491 title: page.title
1492
1493 // Tab content begins here
1494 page: Recent {
1495 id: recentPage
1496 }
1497 }
1498 }
1499
1500 // Store the startup state of the db separately otherwise
1501 // it breaks the binding of model
1502 property bool startupRecentEmpty: Library.isRecentEmpty()
1503
1504 // cached values of the recent model that are copied when
1505 // the tab is created
1506 property bool loading: false
1507 property bool populated: false
1508
1509 onCountChanged: {
1510 if (count === 0 && loadedUI) {
1511 // Jump to the albums tab when recent is empty
1512 tabs.selectedTabIndex = albumsTab.index
1513 } else if (count > 0 && !loadedUI) {
1514 // UI is still loading and recent tab has been inserted
1515 // so move the selected index 'down' as the value is
1516 // not auto updated - this is for the case of loading
1517 // directly to the recent tab (otherwise the content
1518 // appears as the second tab but the tabs think they are
1519 // on the first tab)
1520 tabs.selectedTabIndex -= 1
1521 } else if (count > 0 && loadedUI) {
1522 // tab inserted while the app is running so move the
1523 // selected index 'up' to keep the same position
1524 tabs.selectedTabIndex += 1
1525 }
1526 }
1527 }
1528
1529 // Second tab is arists
1530 Tab {
1531 property bool populated: true
1532 property var loader: []
1533 property bool loading: false
1534 property var model: []
1535 id: artistsTab
1536 objectName: "artistsTab"
1537 anchors.fill: parent
1538 title: page.title
1539
1540 // tab content
1541 page: Artists {
1542 id: artistsPage
1543 }
1544 }
1545
1546 // third tab is albums
1547 Tab {
1548 property bool populated: true
1549 property var loader: []
1550 property bool loading: false
1551 property var model: []
1552 id: albumsTab
1553 objectName: "albumsTab"
1554 anchors.fill: parent
1555 title: page.title
1556
1557 // Tab content begins here
1558 page: Albums {
1559 id: albumsPage
1560 }
1561 }
1562
1563 // forth tab is genres
1564 Tab {
1565 property bool populated: true
1566 property var loader: []
1567 property bool loading: false
1568 property var model: []
1569 id: genresTab
1570 objectName: "genresTab"
1571 anchors.fill: parent
1572 title: page.title
1573
1574 // Tab content begins here
1575 page: Genres {
1576 id: genresPage
1577 }
1578 }
1579
1580 // fourth tab is all songs
1581 Tab {
1582 property bool populated: true
1583 property var loader: []
1584 property bool loading: false
1585 property var model: []
1586 id: songsTab
1587 objectName: "songsTab"
1588 anchors.fill: parent
1589 title: page.title
1590
1591 // Tab content begins here
1592 page: Songs {
1593 id: tracksPage
1594 }
1595 }
1596
1597
1598 // fifth tab is the playlists
1599 Tab {
1600 property bool populated: false
1601 property var loader: [playlistModel.filterPlaylists]
1602 property bool loading: false
1603 property var model: [playlistModel, albumTracksModel]
1604 id: playlistsTab
1605 objectName: "playlistsTab"
1606 anchors.fill: parent
1607 title: page.title
1608
1609 // Tab content begins here
1610 page: Playlists {
1611 id: playlistsPage
1612 }
1613 }
1614
1615 // Set the models in the tab to allow/disallow loading
1616 function allowLoading(tabToLoad, state)
1617 {
1618 if (tabToLoad !== undefined && tabToLoad.model !== undefined)
1619 {
1620 for (var i=0; i < tabToLoad.model.length; i++)
1621 {
1622 tabToLoad.model[i].canLoad = state;
1623 }
1624 }
1625 }
1626
1627 function ensurePopulated(selectedTab)
1628 {
1629 allowLoading(selectedTab, true); // allow loading of the models
1630
1631 if (!selectedTab.populated && !selectedTab.loading && loadedUI) {
1632 loading.visible = true
1633 selectedTab.loading = true
1634
1635 if (selectedTab.loader !== undefined)
1636 {
1637 for (var i=0; i < selectedTab.loader.length; i++)
1638 {
1639 selectedTab.loader[i]();
1640 }
1641 }
1642 }
1643 loading.visible = selectedTab.loading || !selectedTab.populated
1644 }
1645
1646 function pushNowPlaying()
1647 {
1648 // only push if on a different page
1649 if (mainPageStack.currentPage.title !== i18n.tr("Now playing")
1650 && mainPageStack.currentPage.title !== i18n.tr("Queue")) {
1651 mainPageStack.push(Qt.resolvedUrl("components/NowPlaying.qml"), {})
1652 }
1653
1654 if (mainPageStack.currentPage.title === i18n.tr("Queue")) {
1655 mainPageStack.currentPage.isListView = false; // ensure full view
1656 }
1657 }
1658 } // end of tabs
1659 }
1660
1661 Loader {
1662 id: emptyPageLoader
1663 // Do not be active if content-hub is importing due to the models resetting
1664 // this then causes the empty page loader to partially run then showing a blank header
1665 active: noMusic && !firstRun && contentHubWaitForFile.processId === -1
1442 anchors {1666 anchors {
1443 fill: parent1667 fill: parent
1444 topMargin: -emptyPage.header.height1668 }
1445 }1669 source: "ui/LibraryEmptyState.qml"
1446 color: mainView.backgroundColor1670 visible: active
1447 visible: emptyPage.noPlaylists && !emptyPage.noMusic && (playlistTab.index === tabs.selectedTab.index || mainPageStack.currentPage.title === i18n.tr("Select playlist"))1671
14481672 property bool noMusic: allSongsModel.rowCount === 0 && allSongsModelModel.status === SongsModel.Ready && loadedUI
1449 Column {1673 }
1450 anchors.centerIn: parent1674
1451 spacing: units.gu(4)1675 LoadingSpinnerComponent {
1452 width: units.gu(36)1676 id: loading
14531677 }
1454 Label {
1455 color: styleMusic.libraryEmpty.labelColor
1456 elide: Text.ElideRight
1457 fontSize: "x-large"
1458 horizontalAlignment: Text.AlignLeft
1459 maximumLineCount: 2
1460 text: i18n.tr("No playlists found")
1461 width: parent.width
1462 wrapMode: Text.WordWrap
1463 }
1464
1465 Label {
1466 color: styleMusic.libraryEmpty.labelColor
1467 elide: Text.ElideRight
1468 fontSize: "large"
1469 horizontalAlignment: Text.AlignLeft
1470 maximumLineCount: 4
1471 text: i18n.tr("Get more out of Music by tapping the %1 icon to start making playlists for every mood and occasion.").arg('"+"')
1472 width: parent.width
1473 wrapMode: Text.WordWrap
1474 }
1475 }
1476 }
1477 }
1478
1479 LoadingSpinnerComponent {
1480 id: loading
1481 }1678 }
1482} // end of main view1679} // end of main view
14831680
=== added directory 'app/ui'
=== renamed file 'MusicaddtoPlaylist.qml' => 'app/ui/AddToPlaylist.qml'
--- MusicaddtoPlaylist.qml 2015-01-28 02:27:52 +0000
+++ app/ui/AddToPlaylist.qml 2015-02-10 03:13:44 +0000
@@ -23,9 +23,11 @@
23import Ubuntu.Components.ListItems 1.0 as ListItem23import Ubuntu.Components.ListItems 1.0 as ListItem
24import Ubuntu.Components.Popups 1.024import Ubuntu.Components.Popups 1.0
25import QtQuick.LocalStorage 2.025import QtQuick.LocalStorage 2.0
26import "meta-database.js" as Library26import "../logic/meta-database.js" as Library
27import "playlists.js" as Playlists27import "../logic/playlists.js" as Playlists
28import "common"28import "../components"
29import "../components/Flickables"
30import "../components/HeadState"
2931
3032
31/* NOTE:33/* NOTE:
@@ -45,25 +47,10 @@
45 searchResultsCount: addToPlaylistModelFilter.count47 searchResultsCount: addToPlaylistModelFilter.count
46 state: "default"48 state: "default"
47 states: [49 states: [
48 PageHeadState {50 PlaylistsHeadState {
49 name: "default"51 newPlaylistEnabled: allSongsModel.count > 0
50 head: addToPlaylistPage.head52 searchEnabled: playlistModel.model.count > 0 && allSongsModel.count > 0
51 actions: [53 thisPage: addToPlaylistPage
52 Action {
53 enabled: allSongsModel.count > 0
54 objectName: "newPlaylistButton"
55 iconName: "add"
56 onTriggered: {
57 customdebug("New playlist.")
58 PopupUtils.open(newPlaylistDialog, mainView)
59 }
60 },
61 Action {
62 enabled: playlistModel.model.count > 0 && allSongsModel.count > 0
63 iconName: "search"
64 onTriggered: addToPlaylistPage.state = "search"
65 }
66 ]
67 },54 },
68 SearchHeadState {55 SearchHeadState {
69 id: searchHeader56 id: searchHeader
@@ -129,8 +116,20 @@
129 page.covers = Playlists.getPlaylistCovers(name)116 page.covers = Playlists.getPlaylistCovers(name)
130 }117 }
131118
132 musicToolbar.goBack(); // go back to the previous page119 mainPageStack.goBack(); // go back to the previous page
133 }120 }
134 }121 }
135 }122 }
123
124 // Overlay to show when no playlists are on the device
125 Loader {
126 anchors {
127 fill: parent
128 topMargin: -playlistsPage.header.height
129 }
130 active: playlistModel.model.count === 0 && playlistModel.workerComplete
131 asynchronous: true
132 source: "../components/PlaylistsEmptyState.qml"
133 visible: active
134 }
136}135}
137136
=== renamed file 'MusicAlbums.qml' => 'app/ui/Albums.qml'
--- MusicAlbums.qml 2015-01-20 12:02:52 +0000
+++ app/ui/Albums.qml 2015-02-10 03:13:44 +0000
@@ -20,7 +20,9 @@
20import QtQuick 2.320import QtQuick 2.3
21import Ubuntu.Components 1.121import Ubuntu.Components 1.1
22import Ubuntu.MediaScanner 0.122import Ubuntu.MediaScanner 0.1
23import "common"23import "../components"
24import "../components/Flickables"
25import "../components/HeadState"
2426
2527
26MusicPage {28MusicPage {
@@ -31,14 +33,9 @@
31 searchResultsCount: albumsModelFilter.count33 searchResultsCount: albumsModelFilter.count
32 state: "default"34 state: "default"
33 states: [35 states: [
34 PageHeadState {36 SearchableHeadState {
35 name: "default"37 thisPage: albumsPage
36 head: albumsPage.head38 searchEnabled: albumsModelFilter.count > 0
37 actions: Action {
38 enabled: albumsModelFilter.count > 0
39 iconName: "search"
40 onTriggered: albumsPage.state = "search"
41 }
42 },39 },
43 SearchHeadState {40 SearchHeadState {
44 id: searchHeader41 id: searchHeader
@@ -46,6 +43,11 @@
46 }43 }
47 ]44 ]
4845
46 // Hack for autopilot otherwise Albums appears as MusicPage
47 // due to bug 1341671 it is required that there is a property so that
48 // qml doesn't optimise using the parent type
49 property bool bug1341671workaround: true
50
49 CardView {51 CardView {
50 id: albumCardView52 id: albumCardView
51 model: SortFilterModel {53 model: SortFilterModel {
@@ -70,24 +72,17 @@
70 secondaryText: model.artist != "" ? model.artist : i18n.tr("Unknown Artist")72 secondaryText: model.artist != "" ? model.artist : i18n.tr("Unknown Artist")
7173
72 onClicked: {74 onClicked: {
73 var comp = Qt.createComponent("common/SongsPage.qml")75 mainPageStack.push(Qt.resolvedUrl("SongsView.qml"),
74 var songsPage = comp.createObject(mainPageStack,76 {
75 {77 "album": model.title,
76 "album": model.title,78 "artist": model.artist,
77 "artist": model.artist,79 "covers": [{art: model.art}],
78 "covers": [{art: model.art}],80 "isAlbum": true,
79 "isAlbum": true,81 "genre": undefined,
80 "genre": undefined,82 "title": i18n.tr("Album"),
81 "title": i18n.tr("Album"),83 "line1": model.artist,
82 "line1": model.artist,84 "line2": model.title
83 "line2": model.title,85 })
84 });
85
86 if (songsPage == null) { // Error Handling
87 console.log("Error creating object");
88 }
89
90 mainPageStack.push(songsPage)
91 }86 }
92 }87 }
93 }88 }
9489
=== renamed file 'common/AlbumsPage.qml' => 'app/ui/ArtistView.qml'
--- common/AlbumsPage.qml 2015-01-30 01:23:39 +0000
+++ app/ui/ArtistView.qml 2015-02-10 03:13:44 +0000
@@ -24,11 +24,14 @@
24import Ubuntu.MediaScanner 0.124import Ubuntu.MediaScanner 0.1
25import Ubuntu.Thumbnailer 0.125import Ubuntu.Thumbnailer 0.1
26import QtQuick.LocalStorage 2.026import QtQuick.LocalStorage 2.0
27import "../meta-database.js" as Library27import "../logic/meta-database.js" as Library
28import "../components"
29import "../components/Flickables"
30import "../components/ViewButton"
2831
29MusicPage {32MusicPage {
30 id: albumStackPage33 id: artistViewPage
31 objectName: "albumsArtistPage"34 objectName: "artistViewPage"
32 visible: false35 visible: false
3336
34 property string artist: ""37 property string artist: ""
@@ -50,57 +53,17 @@
50 header: BlurredHeader {53 header: BlurredHeader {
51 rightColumn: Column {54 rightColumn: Column {
52 spacing: units.gu(2)55 spacing: units.gu(2)
53 Button {56 ShuffleButton {
54 id: shuffleRow57 model: songArtistModel
55 height: units.gu(4)58 }
56 strokeColor: UbuntuColors.green59 QueueAllButton {
57 width: units.gu(15)60 model: songArtistModel
58 Text {61 }
59 anchors {62 PlayAllButton {
60 centerIn: parent63 model: songArtistModel
61 }
62 color: "white"
63 elide: Text.ElideRight
64 height: parent.height
65 horizontalAlignment: Text.AlignHCenter
66 // TRANSLATORS: this appears in a button with limited space (around 14 characters)
67 text: i18n.tr("Shuffle")
68 verticalAlignment: Text.AlignVCenter
69 width: parent.width - units.gu(2)
70 }
71 onClicked: shuffleModel(songArtistModel)
72 }
73 Button {
74 id: queueAllRow
75 height: units.gu(4)
76 strokeColor: UbuntuColors.green
77 width: units.gu(15)
78 Text {
79 anchors {
80 centerIn: parent
81 }
82 color: "white"
83 elide: Text.ElideRight
84 height: parent.height
85 horizontalAlignment: Text.AlignHCenter
86 // TRANSLATORS: this appears in a button with limited space (around 14 characters)
87 text: i18n.tr("Queue all")
88 verticalAlignment: Text.AlignVCenter
89 width: parent.width - units.gu(2)
90 }
91 onClicked: addQueueFromModel(songArtistModel)
92 }
93 Button {
94 id: playRow
95 color: UbuntuColors.green
96 height: units.gu(4)
97 // TRANSLATORS: this appears in a button with limited space (around 14 characters)
98 text: i18n.tr("Play all")
99 width: units.gu(15)
100 onClicked: trackClicked(songArtistModel, 0, true)
101 }64 }
102 }65 }
103 coverSources: albumStackPage.covers66 coverSources: artistViewPage.covers
104 height: units.gu(30)67 height: units.gu(30)
105 bottomColumn: Column {68 bottomColumn: Column {
106 Label {69 Label {
@@ -139,18 +102,18 @@
139102
140 SongsModel {103 SongsModel {
141 id: songArtistModel104 id: songArtistModel
142 albumArtist: albumStackPage.artist105 albumArtist: artistViewPage.artist
143 store: musicStore106 store: musicStore
144 }107 }
145 }108 }
146 itemWidth: units.gu(12)109 itemWidth: units.gu(12)
147 model: AlbumsModel {110 model: AlbumsModel {
148 id: albumsModel111 id: albumsModel
149 albumArtist: albumStackPage.artist112 albumArtist: artistViewPage.artist
150 store: musicStore113 store: musicStore
151 onStatusChanged: {114 onStatusChanged: {
152 if (albumsModel.status === SongsModel.Ready && loaded && albumsModel.count === 0) {115 if (albumsModel.status === SongsModel.Ready && loaded && albumsModel.count === 0) {
153 musicToolbar.popPage(albumStackPage)116 mainPageStack.popPage(artistViewPage)
154 }117 }
155 }118 }
156 }119 }
@@ -162,24 +125,17 @@
162 secondaryTextVisible: false125 secondaryTextVisible: false
163126
164 onClicked: {127 onClicked: {
165 var comp = Qt.createComponent("SongsPage.qml")128 mainPageStack.push(Qt.resolvedUrl("SongsView.qml"),
166 var songsPage = comp.createObject(mainPageStack,129 {
167 {130 "album": model.title,
168 "album": model.title,131 "artist": model.artist,
169 "artist": model.artist,132 "covers": [{art: model.art}],
170 "covers": [{art: model.art}],133 "isAlbum": true,
171 "isAlbum": true,134 "genre": undefined,
172 "genre": undefined,135 "title": i18n.tr("Album"),
173 "title": i18n.tr("Album"),136 "line1": model.artist != "" ? model.artist : i18n.tr("Unknown Artist"),
174 "line1": model.artist != "" ? model.artist : i18n.tr("Unknown Artist"),137 "line2": model.title != "" ? model.title : i18n.tr("Unknown Album")
175 "line2": model.title != "" ? model.title : i18n.tr("Unknown Album"),138 })
176 });
177
178 if (songsPage == null) { // Error Handling
179 console.log("Error creating object");
180 }
181
182 mainPageStack.push(songsPage)
183 }139 }
184 }140 }
185 }141 }
186142
=== renamed file 'MusicArtists.qml' => 'app/ui/Artists.qml'
--- MusicArtists.qml 2015-01-20 12:02:52 +0000
+++ app/ui/Artists.qml 2015-02-10 03:13:44 +0000
@@ -24,9 +24,11 @@
24import Ubuntu.MediaScanner 0.124import Ubuntu.MediaScanner 0.1
25import Ubuntu.Thumbnailer 0.125import Ubuntu.Thumbnailer 0.1
26import QtQuick.LocalStorage 2.026import QtQuick.LocalStorage 2.0
27import "meta-database.js" as Library27import "../logic/meta-database.js" as Library
28import "playlists.js" as Playlists28import "../logic/playlists.js" as Playlists
29import "common"29import "../components"
30import "../components/Flickables"
31import "../components/HeadState"
3032
3133
32MusicPage {34MusicPage {
@@ -37,14 +39,9 @@
37 searchResultsCount: artistsModelFilter.count39 searchResultsCount: artistsModelFilter.count
38 state: "default"40 state: "default"
39 states: [41 states: [
40 PageHeadState {42 SearchableHeadState {
41 name: "default"43 thisPage: artistsPage
42 head: artistsPage.head44 searchEnabled: artistsModelFilter.count > 0
43 actions: Action {
44 enabled: artistsModelFilter.count > 0
45 iconName: "search"
46 onTriggered: artistsPage.state = "search"
47 }
48 },45 },
49 SearchHeadState {46 SearchHeadState {
50 id: searchHeader47 id: searchHeader
@@ -52,6 +49,11 @@
52 }49 }
53 ]50 ]
5451
52 // Hack for autopilot otherwise Artists appears as MusicPage
53 // due to bug 1341671 it is required that there is a property so that
54 // qml doesn't optimise using the parent type
55 property bool bug1341671workaround: true
56
55 CardView {57 CardView {
56 id: artistCardView58 id: artistCardView
57 itemWidth: units.gu(12)59 itemWidth: units.gu(12)
@@ -97,21 +99,13 @@
97 }99 }
98 }100 }
99101
100
101 onClicked: {102 onClicked: {
102 var comp = Qt.createComponent("common/AlbumsPage.qml")103 mainPageStack.push(Qt.resolvedUrl("ArtistView.qml"),
103 var albumsPage = comp.createObject(mainPageStack,104 {
104 {105 "artist": model.artist,
105 "artist": model.artist,106 "covers": artistCard.coverSources,
106 "covers": artistCard.coverSources,107 "title": i18n.tr("Artist")
107 "title": i18n.tr("Artist"),108 })
108 });
109
110 if (albumsPage == null) { // Error Handling
111 console.log("Error creating object");
112 }
113
114 mainPageStack.push(albumsPage)
115 }109 }
116 }110 }
117 }111 }
118112
=== added file 'app/ui/CMakeLists.txt'
--- app/ui/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ app/ui/CMakeLists.txt 2015-02-10 03:13:44 +0000
@@ -0,0 +1,4 @@
1# make the qml files visible on qtcreator
2file(GLOB UI_QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml)
3
4add_custom_target(com_ubuntu_music_UI_QMLFiles ALL SOURCES ${UI_QML_FILES})
05
=== renamed file 'MusicGenres.qml' => 'app/ui/Genres.qml'
--- MusicGenres.qml 2015-01-20 12:02:52 +0000
+++ app/ui/Genres.qml 2015-02-10 03:13:44 +0000
@@ -20,7 +20,9 @@
20import QtQuick 2.320import QtQuick 2.3
21import Ubuntu.Components 1.121import Ubuntu.Components 1.1
22import Ubuntu.MediaScanner 0.122import Ubuntu.MediaScanner 0.1
23import "common"23import "../components"
24import "../components/Flickables"
25import "../components/HeadState"
2426
2527
26MusicPage {28MusicPage {
@@ -31,14 +33,9 @@
31 searchResultsCount: genresModelFilter.count33 searchResultsCount: genresModelFilter.count
32 state: "default"34 state: "default"
33 states: [35 states: [
34 PageHeadState {36 SearchableHeadState {
35 name: "default"37 thisPage: genresPage
36 head: genresPage.head38 searchEnabled: genresModelFilter.count > 0
37 actions: Action {
38 enabled: genresModelFilter.count > 0
39 iconName: "search"
40 onTriggered: genresPage.state = "search"
41 }
42 },39 },
43 SearchHeadState {40 SearchHeadState {
44 id: searchHeader41 id: searchHeader
@@ -46,6 +43,11 @@
46 }43 }
47 ]44 ]
4845
46 // Hack for autopilot otherwise Albums appears as MusicPage
47 // due to bug 1341671 it is required that there is a property so that
48 // qml doesn't optimise using the parent type
49 property bool bug1341671workaround: true
50
49 CardView {51 CardView {
50 id: genreCardView52 id: genreCardView
51 itemWidth: units.gu(12)53 itemWidth: units.gu(12)
@@ -98,23 +100,16 @@
98 }100 }
99101
100 onClicked: {102 onClicked: {
101 var comp = Qt.createComponent("common/SongsPage.qml")103 mainPageStack.push(Qt.resolvedUrl("SongsView.qml"),
102 var songsPage = comp.createObject(mainPageStack,104 {
103 {105 "covers": genreCard.coverSources,
104 "covers": genreCard.coverSources,106 "album": undefined,
105 "album": undefined,107 "isAlbum": true,
106 "isAlbum": true,108 "genre": model.genre,
107 "genre": model.genre,109 "title": i18n.tr("Genre"),
108 "title": i18n.tr("Genre"),110 "line2": model.genre,
109 "line2": model.genre,111 "line1": i18n.tr("Genre")
110 "line1": i18n.tr("Genre")112 })
111 });
112
113 if (songsPage == null) { // Error Handling
114 console.log("Error creating object");
115 }
116
117 mainPageStack.push(songsPage)
118 }113 }
119 }114 }
120 }115 }
121116
=== added file 'app/ui/LibraryEmptyState.qml'
--- app/ui/LibraryEmptyState.qml 1970-01-01 00:00:00 +0000
+++ app/ui/LibraryEmptyState.qml 2015-02-10 03:13:44 +0000
@@ -0,0 +1,123 @@
1/*
2 * Copyright (C) 2013, 2014, 2015
3 * Andrew Hayzen <ahayzen@gmail.com>
4 * Daniel Holm <d.holmen@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
23Page {
24 id: libraryEmptyPage
25 anchors {
26 fill: parent
27 }
28
29 // Overlay to show when no tracks detected on the device
30 Rectangle {
31 id: libraryEmpty
32 anchors {
33 fill: parent
34 topMargin: -libraryEmptyPage.header.height
35 }
36 color: mainView.backgroundColor
37
38 Column {
39 id: noMusicTextColumn
40 anchors {
41 centerIn: parent
42 }
43 spacing: units.gu(4)
44 width: units.gu(36)
45
46 Row {
47 anchors {
48 horizontalCenter: parent.horizontalCenter
49 }
50
51 Item {
52 height: parent.height
53 width: imageEmptyDownload.width + units.gu(2)
54
55 Image {
56 id: imageEmptyDownload
57 anchors {
58 horizontalCenter: parent.horizontalCenter
59 verticalCenter: parent.verticalCenter
60 }
61 antialiasing: true
62 fillMode: Image.PreserveAspectFit
63 height: units.gu(10)
64 smooth: true
65 source: "../graphics/music_empty_download.png"
66 }
67 }
68
69 Item {
70 height: parent.height
71 width: units.gu(7)
72
73 Image {
74 id: imageSep
75 anchors {
76 horizontalCenter: parent.horizontalCenter
77 verticalCenter: parent.verticalCenter
78 }
79 antialiasing: true
80 fillMode: Image.PreserveAspectFit
81 height: units.gu(6)
82 smooth: true
83 source: "../graphics/div.png"
84 }
85 }
86
87 Image {
88 id: imageEmptySD
89 anchors {
90 verticalCenter: parent.verticalCenter
91 }
92 antialiasing: true
93 fillMode: Image.PreserveAspectFit
94 height: units.gu(7)
95 smooth: true
96 source: "../graphics/music_empty_SD.png"
97 }
98 }
99
100 Label {
101 color: styleMusic.libraryEmpty.labelColor
102 elide: Text.ElideRight
103 fontSize: "x-large"
104 horizontalAlignment: Text.AlignLeft
105 maximumLineCount: 2
106 text: i18n.tr("No music found")
107 width: parent.width
108 wrapMode: Text.WordWrap
109 }
110
111 Label {
112 color: styleMusic.libraryEmpty.labelColor
113 elide: Text.ElideRight
114 fontSize: "large"
115 horizontalAlignment: Text.AlignLeft
116 maximumLineCount: 4
117 text: i18n.tr("Connect your device to any computer and simply drag files to the Music folder or insert removable media with music.")
118 width: parent.width
119 wrapMode: Text.WordWrap
120 }
121 }
122 }
123}
0124
=== renamed file 'MusicNowPlaying.qml' => 'app/ui/NowPlayingView.qml'
--- MusicNowPlaying.qml 2015-01-28 02:30:43 +0000
+++ app/ui/NowPlayingView.qml 2015-02-10 03:13:44 +0000
@@ -22,11 +22,13 @@
22import QtQuick.LocalStorage 2.022import QtQuick.LocalStorage 2.0
23import Ubuntu.Components 1.123import Ubuntu.Components 1.1
24import Ubuntu.Thumbnailer 0.124import Ubuntu.Thumbnailer 0.1
25import "common"25import "../components"
26import "common/ListItemActions"26import "../components/Flickables"
27import "common/Themes/Ambiance"27import "../components/HeadState"
28import "meta-database.js" as Library28import "../components/ListItemActions"
29import "playlists.js" as Playlists29import "../components/Themes/Ambiance"
30import "../logic/meta-database.js" as Library
31import "../logic/playlists.js" as Playlists
3032
31MusicPage {33MusicPage {
32 id: nowPlaying34 id: nowPlaying
@@ -35,7 +37,7 @@
35 title: isListView ? queueTitle : nowPlayingTitle37 title: isListView ? queueTitle : nowPlayingTitle
36 visible: false38 visible: false
3739
38 property bool isListView: false40 property alias isListView: nowPlaying.isListView
39 // TRANSLATORS: this appears in the header with limited space (around 20 characters)41 // TRANSLATORS: this appears in the header with limited space (around 20 characters)
40 property string nowPlayingTitle: i18n.tr("Now playing") 42 property string nowPlayingTitle: i18n.tr("Now playing")
41 // TRANSLATORS: this appears in the header with limited space (around 20 characters)43 // TRANSLATORS: this appears in the header with limited space (around 20 characters)
@@ -98,14 +100,8 @@
98100
99 items.push(makeDict(trackQueue.model.get(player.currentIndex)));101 items.push(makeDict(trackQueue.model.get(player.currentIndex)));
100102
101 var comp = Qt.createComponent("MusicaddtoPlaylist.qml")103 mainPageStack.push(Qt.resolvedUrl("AddToPlaylist.qml"),
102 var addToPlaylist = comp.createObject(mainPageStack, {"chosenElements": items});104 {"chosenElements": items})
103
104 if (addToPlaylist == null) { // Error Handling
105 console.log("Error creating object");
106 }
107
108 mainPageStack.push(addToPlaylist)
109 }105 }
110 },106 },
111 Action {107 Action {
@@ -126,619 +122,22 @@
126 actions: defaultState.actions122 actions: defaultState.actions
127 }123 }
128 },124 },
129 PageHeadState {125 MultiSelectHeadState {
130 id: selectionState126 addToQueue: false
131127 listview: queueListLoader.item
132 name: "selection"128 removable: true
133 backAction: Action {129 thisPage: nowPlaying
134 text: i18n.tr("Cancel selection")130
135 iconName: "back"131 onRemoved: {
136 onTriggered: {132 // Remove the tracks from the queue
137 queueListLoader.item.clearSelection()133 // Use slice() to copy the list
138 queueListLoader.item.state = "normal"134 // so that the indexes don't change as they are removed
139 }135 trackQueue.removeQueueList(selectedItems.slice())
140 }
141 actions: [
142 Action {
143 text: i18n.tr("Select All")
144 iconName: "select"
145 onTriggered: {
146 if (queueListLoader.item.selectedItems.length === trackQueue.model.count) {
147 queueListLoader.item.clearSelection()
148 } else {
149 queueListLoader.item.selectAll()
150 }
151 }
152 },
153 Action {
154 enabled: queueListLoader.item.selectedItems.length > 0
155 iconName: "add-to-playlist"
156 text: i18n.tr("Add to playlist")
157 onTriggered: {
158 var items = []
159
160 for (var i=0; i < queueListLoader.item.selectedItems.length; i++) {
161 items.push(makeDict(trackQueue.model.get(queueListLoader.item.selectedItems[i])));
162 }
163
164 var comp = Qt.createComponent("MusicaddtoPlaylist.qml")
165 var addToPlaylist = comp.createObject(mainPageStack, {"chosenElements": items});
166
167 if (addToPlaylist == null) { // Error Handling
168 console.log("Error creating object");
169 }
170
171 mainPageStack.push(addToPlaylist)
172
173 queueListLoader.item.closeSelection()
174 }
175 },
176 Action {
177 enabled: queueListLoader.item.selectedItems.length > 0
178 iconName: "delete"
179 text: i18n.tr("Delete")
180 onTriggered: {
181 // Remove the tracks from the queue
182 // Use slice() to copy the list
183 // so that the indexes don't change as they are removed
184 trackQueue.removeQueueList(queueListLoader.item.selectedItems.slice())
185
186 queueListLoader.item.closeSelection()
187 }
188 }
189 ]
190 PropertyChanges {
191 target: nowPlaying.head
192 backAction: selectionState.backAction
193 actions: selectionState.actions
194 }136 }
195 }137 }
196 ]138 ]
197139
198 Item {140 NowPlaying {
199 id: fullview141 id: view
200 anchors {
201 top: parent.top
202 topMargin: mainView.header.height
203 }
204 height: parent.height - mainView.header.height - units.gu(9.5)
205 visible: !isListView
206 width: parent.width
207
208 BlurredBackground {
209 id: blurredBackground
210 anchors {
211 left: parent.left
212 right: parent.right
213 top: parent.top
214 }
215 art: albumImage.firstSource
216 height: parent.height - units.gu(7)
217
218 Item {
219 id: albumImageContainer
220 anchors {
221 horizontalCenter: parent.horizontalCenter
222 top: parent.top
223 }
224 height: parent.height
225 width: parent.width
226
227 CoverGrid {
228 id: albumImage
229 anchors.centerIn: parent
230 covers: [{art: player.currentMetaArt, author: player.currentMetaArtist, album: player.currentMetaAlbum}]
231 size: parent.width > parent.height ? parent.height : parent.width
232 }
233 }
234
235 Rectangle {
236 id: nowPlayingWideAspectLabelsBackground
237 anchors.bottom: parent.bottom
238 color: styleMusic.common.black
239 height: nowPlayingWideAspectTitle.lineCount === 1 ? units.gu(10) : units.gu(13)
240 opacity: 0.8
241 width: parent.width
242 }
243
244 /* Column for labels in wideAspect */
245 Column {
246 id: nowPlayingWideAspectLabels
247 spacing: units.gu(1)
248 anchors {
249 left: parent.left
250 leftMargin: units.gu(2)
251 right: parent.right
252 rightMargin: units.gu(2)
253 top: nowPlayingWideAspectLabelsBackground.top
254 topMargin: nowPlayingWideAspectTitle.lineCount === 1 ? units.gu(2) : units.gu(1.5)
255 }
256
257 /* Title of track */
258 Label {
259 id: nowPlayingWideAspectTitle
260 anchors {
261 left: parent.left
262 leftMargin: units.gu(1)
263 right: parent.right
264 rightMargin: units.gu(1)
265 }
266 color: styleMusic.playerControls.labelColor
267 elide: Text.ElideRight
268 fontSize: "x-large"
269 maximumLineCount: 2
270 objectName: "playercontroltitle"
271 text: trackQueue.model.count === 0 ? "" : player.currentMetaTitle === "" ? player.currentMetaFile : player.currentMetaTitle
272 wrapMode: Text.WordWrap
273 }
274
275 /* Artist of track */
276 Label {
277 id: nowPlayingWideAspectArtist
278 anchors {
279 left: parent.left
280 leftMargin: units.gu(1)
281 right: parent.right
282 rightMargin: units.gu(1)
283 }
284 color: styleMusic.nowPlaying.labelSecondaryColor
285 elide: Text.ElideRight
286 fontSize: "small"
287 text: trackQueue.model.count === 0 ? "" : player.currentMetaArtist
288 }
289 }
290
291 /* Detect cover art swipe */
292 MouseArea {
293 anchors.fill: parent
294 property string direction: "None"
295 property real lastX: -1
296
297 onPressed: lastX = mouse.x
298
299 onReleased: {
300 var diff = mouse.x - lastX
301 if (Math.abs(diff) < units.gu(4)) {
302 return;
303 } else if (diff < 0) {
304 player.nextSong()
305 } else if (diff > 0) {
306 player.previousSong()
307 }
308 }
309 }
310 }
311
312 /* Background for progress bar component */
313 Rectangle {
314 id: musicToolbarFullProgressBackground
315 anchors {
316 bottom: parent.bottom
317 left: parent.left
318 right: parent.right
319 top: blurredBackground.bottom
320 }
321 color: styleMusic.common.black
322 }
323
324 /* Progress bar component */
325 Item {
326 id: musicToolbarFullProgressContainer
327 anchors.left: parent.left
328 anchors.leftMargin: units.gu(3)
329 anchors.right: parent.right
330 anchors.rightMargin: units.gu(3)
331 anchors.top: blurredBackground.bottom
332 anchors.topMargin: units.gu(1)
333 height: units.gu(3)
334 width: parent.width
335
336 /* Position label */
337 Label {
338 id: musicToolbarFullPositionLabel
339 anchors.top: progressSliderMusic.bottom
340 anchors.topMargin: units.gu(-2)
341 anchors.left: parent.left
342 color: styleMusic.nowPlaying.labelSecondaryColor
343 fontSize: "small"
344 height: parent.height
345 horizontalAlignment: Text.AlignHCenter
346 text: durationToString(player.position)
347 verticalAlignment: Text.AlignVCenter
348 width: units.gu(3)
349 }
350
351 Slider {
352 id: progressSliderMusic
353 anchors.left: parent.left
354 anchors.right: parent.right
355 maximumValue: player.duration // load value at startup
356 objectName: "progressSliderShape"
357 style: UbuntuBlueSliderStyle {}
358 value: player.position // load value at startup
359
360 function formatValue(v) {
361 if (seeking) { // update position label while dragging
362 musicToolbarFullPositionLabel.text = durationToString(v)
363 }
364
365 return durationToString(v)
366 }
367
368 property bool seeking: false
369 property bool seeked: false
370
371 onSeekingChanged: {
372 if (seeking === false) {
373 musicToolbarFullPositionLabel.text = durationToString(player.position)
374 }
375 }
376
377 onPressedChanged: {
378 seeking = pressed
379
380 if (!pressed) {
381 seeked = true
382 player.seek(value)
383
384 musicToolbarFullPositionLabel.text = durationToString(value)
385 }
386 }
387
388 Connections {
389 target: player
390 onPositionChanged: {
391 // seeked is a workaround for bug 1310706 as the first position after a seek is sometimes invalid (0)
392 if (progressSliderMusic.seeking === false && !progressSliderMusic.seeked) {
393 musicToolbarFullPositionLabel.text = durationToString(player.position)
394 musicToolbarFullDurationLabel.text = durationToString(player.duration)
395
396 progressSliderMusic.value = player.position
397 progressSliderMusic.maximumValue = player.duration
398 }
399
400 progressSliderMusic.seeked = false;
401 }
402 onStopped: {
403 musicToolbarFullPositionLabel.text = durationToString(0);
404 musicToolbarFullDurationLabel.text = durationToString(0);
405 }
406 }
407 }
408
409 /* Duration label */
410 Label {
411 id: musicToolbarFullDurationLabel
412 anchors.top: progressSliderMusic.bottom
413 anchors.topMargin: units.gu(-2)
414 anchors.right: parent.right
415 color: styleMusic.nowPlaying.labelSecondaryColor
416 fontSize: "small"
417 height: parent.height
418 horizontalAlignment: Text.AlignHCenter
419 text: durationToString(player.duration)
420 verticalAlignment: Text.AlignVCenter
421 width: units.gu(3)
422 }
423 }
424 }
425
426 Loader {
427 id: queueListLoader
428 anchors {
429 fill: parent
430 }
431 asynchronous: true
432 sourceComponent: ListView {
433 id: queueList
434 anchors {
435 bottomMargin: musicToolbarFullContainer.height + units.gu(2)
436 fill: parent
437 topMargin: units.gu(2)
438 }
439 delegate: queueDelegate
440 footer: Item {
441 height: mainView.height - (styleMusic.common.expandHeight + queueList.currentHeight) + units.gu(8)
442 }
443 model: trackQueue.model
444 objectName: "nowPlayingqueueList"
445
446 property int normalHeight: units.gu(6)
447 property int transitionDuration: 250 // transition length of animations
448
449 onCountChanged: {
450 customdebug("Queue: Now has: " + queueList.count + " tracks")
451 }
452
453 // Requirements for ListItemWithActions
454 property var selectedItems: []
455
456 signal clearSelection()
457 signal closeSelection()
458 signal selectAll()
459
460 onClearSelection: selectedItems = []
461 onCloseSelection: {
462 clearSelection()
463 state = "normal"
464 }
465 onSelectAll: {
466 var tmp = selectedItems
467
468 for (var i=0; i < model.count; i++) {
469 if (tmp.indexOf(i) === -1) {
470 tmp.push(i)
471 }
472 }
473
474 selectedItems = tmp
475 }
476 onVisibleChanged: {
477 if (!visible) {
478 closeSelection()
479 }
480 }
481
482 Component.onCompleted: {
483 // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
484 // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration
485 var scaleFactor = units.gridUnit / 8;
486 maximumFlickVelocity = maximumFlickVelocity * scaleFactor;
487 flickDeceleration = flickDeceleration * scaleFactor;
488 }
489
490 Component {
491 id: queueDelegate
492 ListItemWithActions {
493 id: queueListItem
494 color: player.currentIndex === index ? "#2c2c34" : styleMusic.mainView.backgroundColor
495 height: queueList.normalHeight
496 objectName: "nowPlayingListItem" + index
497 state: ""
498
499 leftSideAction: Remove {
500 onTriggered: trackQueue.removeQueueList([index])
501 }
502 multiselectable: true
503 reorderable: true
504 rightSideActions: [
505 AddToPlaylist{
506
507 }
508 ]
509
510 onItemClicked: {
511 customdebug("File: " + model.filename) // debugger
512 trackQueueClick(index); // toggle track state
513 }
514 onReorder: {
515 console.debug("Move: ", from, to);
516
517 trackQueue.model.move(from, to, 1);
518 Library.moveQueueItem(from, to);
519
520 // Maintain currentIndex with current song
521 if (from === player.currentIndex) {
522 player.currentIndex = to;
523 }
524 else if (from < player.currentIndex && to >= player.currentIndex) {
525 player.currentIndex -= 1;
526 }
527 else if (from > player.currentIndex && to <= player.currentIndex) {
528 player.currentIndex += 1;
529 }
530
531 queueIndex = player.currentIndex
532 }
533
534 Item {
535 id: trackContainer;
536 anchors {
537 fill: parent
538 }
539
540 NumberAnimation {
541 id: trackContainerReorderAnimation
542 target: trackContainer;
543 property: "anchors.leftMargin";
544 duration: queueList.transitionDuration;
545 to: units.gu(2)
546 }
547
548 NumberAnimation {
549 id: trackContainerResetAnimation
550 target: trackContainer;
551 property: "anchors.leftMargin";
552 duration: queueList.transitionDuration;
553 to: units.gu(0.5)
554 }
555
556 MusicRow {
557 id: musicRow
558 height: parent.height
559 column: Column {
560 Label {
561 id: trackTitle
562 color: player.currentIndex === index ? UbuntuColors.blue
563 : styleMusic.common.music
564 fontSize: "small"
565 objectName: "titleLabel"
566 text: model.title
567 }
568
569 Label {
570 id: trackArtist
571 color: styleMusic.common.subtitle
572 fontSize: "x-small"
573 objectName: "artistLabel"
574 text: model.author
575 }
576 }
577 }
578 }
579 }
580 }
581 }
582 visible: isListView
583 }
584
585 /* Full toolbar */
586 Rectangle {
587 id: musicToolbarFullContainer
588 anchors.bottom: parent.bottom
589 color: styleMusic.common.black
590 height: units.gu(10)
591 width: parent.width
592
593 /* Repeat button */
594 MouseArea {
595 id: nowPlayingRepeatButton
596 anchors.right: nowPlayingPreviousButton.left
597 anchors.rightMargin: units.gu(1)
598 anchors.verticalCenter: nowPlayingPlayButton.verticalCenter
599 height: units.gu(6)
600 opacity: player.repeat && !emptyPage.noMusic ? 1 : .4
601 width: height
602 onClicked: player.repeat = !player.repeat
603
604 Icon {
605 id: repeatIcon
606 height: units.gu(3)
607 width: height
608 anchors.verticalCenter: parent.verticalCenter
609 anchors.horizontalCenter: parent.horizontalCenter
610 color: "white"
611 name: "media-playlist-repeat"
612 objectName: "repeatShape"
613 opacity: player.repeat && !emptyPage.noMusic ? 1 : .4
614 }
615 }
616
617 /* Previous button */
618 MouseArea {
619 id: nowPlayingPreviousButton
620 anchors.right: nowPlayingPlayButton.left
621 anchors.rightMargin: units.gu(1)
622 anchors.verticalCenter: nowPlayingPlayButton.verticalCenter
623 height: units.gu(6)
624 opacity: trackQueue.model.count === 0 ? .4 : 1
625 width: height
626 onClicked: player.previousSong()
627
628 Icon {
629 id: nowPlayingPreviousIndicator
630 height: units.gu(3)
631 width: height
632 anchors.verticalCenter: parent.verticalCenter
633 anchors.horizontalCenter: parent.horizontalCenter
634 color: "white"
635 name: "media-skip-backward"
636 objectName: "previousShape"
637 opacity: 1
638 }
639 }
640
641 /* Play/Pause button */
642 MouseArea {
643 id: nowPlayingPlayButton
644 anchors.centerIn: parent
645 height: units.gu(10)
646 width: height
647 onClicked: player.toggle()
648
649 Icon {
650 id: nowPlayingPlayIndicator
651 height: units.gu(6)
652 width: height
653 anchors.verticalCenter: parent.verticalCenter
654 anchors.horizontalCenter: parent.horizontalCenter
655 opacity: emptyPage.noMusic ? .4 : 1
656 color: "white"
657 name: player.playbackState === MediaPlayer.PlayingState ? "media-playback-pause" : "media-playback-start"
658 objectName: "playShape"
659 }
660 }
661
662 /* Next button */
663 MouseArea {
664 id: nowPlayingNextButton
665 anchors.left: nowPlayingPlayButton.right
666 anchors.leftMargin: units.gu(1)
667 anchors.verticalCenter: nowPlayingPlayButton.verticalCenter
668 height: units.gu(6)
669 opacity: trackQueue.model.count === 0 ? .4 : 1
670 width: height
671 onClicked: player.nextSong()
672
673 Icon {
674 id: nowPlayingNextIndicator
675 height: units.gu(3)
676 width: height
677 anchors.verticalCenter: parent.verticalCenter
678 anchors.horizontalCenter: parent.horizontalCenter
679 color: "white"
680 name: "media-skip-forward"
681 objectName: "forwardShape"
682 opacity: 1
683 }
684 }
685
686 /* Shuffle button */
687 MouseArea {
688 id: nowPlayingShuffleButton
689 anchors.left: nowPlayingNextButton.right
690 anchors.leftMargin: units.gu(1)
691 anchors.verticalCenter: nowPlayingPlayButton.verticalCenter
692 height: units.gu(6)
693 opacity: player.shuffle && !emptyPage.noMusic ? 1 : .4
694 width: height
695 onClicked: player.shuffle = !player.shuffle
696
697 Icon {
698 id: shuffleIcon
699 height: units.gu(3)
700 width: height
701 anchors.verticalCenter: parent.verticalCenter
702 anchors.horizontalCenter: parent.horizontalCenter
703 color: "white"
704 name: "media-playlist-shuffle"
705 objectName: "shuffleShape"
706 opacity: player.shuffle && !emptyPage.noMusic ? 1 : .4
707 }
708 }
709
710 /* Object which provides the progress bar when in the queue */
711 Rectangle {
712 id: playerControlsProgressBar
713 anchors {
714 bottom: parent.bottom
715 left: parent.left
716 right: parent.right
717 }
718 color: styleMusic.common.black
719 height: units.gu(0.25)
720 visible: isListView
721
722 Rectangle {
723 id: playerControlsProgressBarHint
724 anchors {
725 left: parent.left
726 bottom: parent.bottom
727 }
728 color: UbuntuColors.blue
729 height: parent.height
730 width: player.duration > 0 ? (player.position / player.duration) * playerControlsProgressBar.width : 0
731
732 Connections {
733 target: player
734 onPositionChanged: {
735 playerControlsProgressBarHint.width = (player.position / player.duration) * playerControlsProgressBar.width
736 }
737 onStopped: {
738 playerControlsProgressBarHint.width = 0;
739 }
740 }
741 }
742 }
743 }142 }
744}143}
745144
=== renamed file 'MusicPlaylists.qml' => 'app/ui/Playlists.qml'
--- MusicPlaylists.qml 2015-01-21 00:10:33 +0000
+++ app/ui/Playlists.qml 2015-02-10 03:13:44 +0000
@@ -23,8 +23,10 @@
23import Ubuntu.Components.Popups 1.023import Ubuntu.Components.Popups 1.0
24import QtMultimedia 5.024import QtMultimedia 5.0
25import QtQuick.LocalStorage 2.025import QtQuick.LocalStorage 2.0
26import "playlists.js" as Playlists26import "../logic/playlists.js" as Playlists
27import "common"27import "../components"
28import "../components/Flickables"
29import "../components/HeadState"
2830
29// page for the playlists31// page for the playlists
30MusicPage {32MusicPage {
@@ -37,25 +39,10 @@
37 searchResultsCount: playlistModelFilter.count39 searchResultsCount: playlistModelFilter.count
38 state: "default"40 state: "default"
39 states: [41 states: [
40 PageHeadState {42 PlaylistsHeadState {
41 name: "default"43 newPlaylistEnabled: allSongsModel.count > 0
42 head: playlistsPage.head44 searchEnabled: playlistModel.model.count > 0 && allSongsModel.count > 0
43 actions: [45 thisPage: playlistsPage
44 Action {
45 enabled: allSongsModel.count > 0
46 objectName: "newPlaylistButton"
47 iconName: "add"
48 onTriggered: {
49 customdebug("New playlist.")
50 PopupUtils.open(newPlaylistDialog, mainView)
51 }
52 },
53 Action {
54 enabled: playlistModel.model.count > 0 && allSongsModel.count > 0
55 iconName: "search"
56 onTriggered: playlistsPage.state = "search"
57 }
58 ]
59 },46 },
60 SearchHeadState {47 SearchHeadState {
61 id: searchHeader48 id: searchHeader
@@ -99,25 +86,30 @@
99 onClicked: {86 onClicked: {
100 albumTracksModel.filterPlaylistTracks(model.name)87 albumTracksModel.filterPlaylistTracks(model.name)
10188
102 var comp = Qt.createComponent("common/SongsPage.qml")89 mainPageStack.push(Qt.resolvedUrl("SongsView.qml"),
103 var songsPage = comp.createObject(mainPageStack,90 {
104 {91 "album": undefined,
105 "album": undefined,92 "covers": coverSources,
106 "covers": coverSources,93 "isAlbum": false,
107 "isAlbum": false,94 "genre": undefined,
108 "genre": undefined,95 "page": playlistsPage,
109 "page": playlistsPage,96 "title": i18n.tr("Playlist"),
110 "title": i18n.tr("Playlist"),97 "line1": i18n.tr("Playlist"),
111 "line1": i18n.tr("Playlist"),98 "line2": model.name,
112 "line2": model.name,99 })
113 });
114
115 if (songsPage == null) { // Error Handling
116 console.log("Error creating object");
117 }
118
119 mainPageStack.push(songsPage)
120 }100 }
121 }101 }
122 }102 }
103
104 // Overlay to show when no playlists are on the device
105 Loader {
106 anchors {
107 fill: parent
108 topMargin: -playlistsPage.header.height
109 }
110 active: playlistModel.model.count === 0 && playlistModel.workerComplete
111 asynchronous: true
112 source: "../components/PlaylistsEmptyState.qml"
113 visible: active
114 }
123}115}
124116
=== renamed file 'MusicStart.qml' => 'app/ui/Recent.qml'
--- MusicStart.qml 2014-11-10 22:20:24 +0000
+++ app/ui/Recent.qml 2015-02-10 03:13:44 +0000
@@ -25,12 +25,14 @@
25import Ubuntu.Thumbnailer 0.125import Ubuntu.Thumbnailer 0.1
26import QtMultimedia 5.026import QtMultimedia 5.0
27import QtQuick.LocalStorage 2.027import QtQuick.LocalStorage 2.0
28import "meta-database.js" as Library28import "../logic/meta-database.js" as Library
29import "playlists.js" as Playlists29import "../logic/playlists.js" as Playlists
30import "common"30import "../components"
31import "../components/Flickables"
3132
32MusicPage {33MusicPage {
33 id: mainpage34 id: recentPage
35 objectName: "recentPage"
34 title: i18n.tr("Recent")36 title: i18n.tr("Recent")
3537
36 property bool changed: false38 property bool changed: false
@@ -83,25 +85,18 @@
83 albumTracksModel.filterPlaylistTracks(model.data)85 albumTracksModel.filterPlaylistTracks(model.data)
84 }86 }
8587
86 var comp = Qt.createComponent("common/SongsPage.qml")88 mainPageStack.push(Qt.resolvedUrl("SongsView.qml"),
87 var songsPage = comp.createObject(mainPageStack,89 {
88 {90 "album": model.type !== "playlist" ? model.data : undefined,
89 "album": model.type !== "playlist" ? model.data : undefined,91 "artist": model.type !== "playlist" ? recentAlbumSongs.get(0, SongsModel.RoleModelData).artist : undefined,
90 "artist": model.type !== "playlist" ? recentAlbumSongs.get(0, SongsModel.RoleModelData).artist : undefined,92 "covers": coverSources,
91 "covers": coverSources,93 "isAlbum": (model.type === "album"),
92 "isAlbum": (model.type === "album"),94 "genre": undefined,
93 "genre": undefined,95 "page": recentPage,
94 "page": mainpage,96 "title": (model.type === "album") ? i18n.tr("Album") : i18n.tr("Playlist"),
95 "title": (model.type === "album") ? i18n.tr("Album") : i18n.tr("Playlist"),97 "line1": secondaryText,
96 "line1": secondaryText,98 "line2": primaryText,
97 "line2": primaryText,99 })
98 });
99
100 if (songsPage == null) { // Error Handling
101 console.log("Error creating object");
102 }
103
104 mainPageStack.push(songsPage)
105 }100 }
106 }101 }
107 }102 }
108103
=== renamed file 'MusicTracks.qml' => 'app/ui/Songs.qml'
--- MusicTracks.qml 2015-01-26 03:20:37 +0000
+++ app/ui/Songs.qml 2015-02-10 03:13:44 +0000
@@ -23,99 +23,41 @@
23import Ubuntu.Thumbnailer 0.123import Ubuntu.Thumbnailer 0.1
24import QtMultimedia 5.024import QtMultimedia 5.0
25import QtQuick.LocalStorage 2.025import QtQuick.LocalStorage 2.0
26import "playlists.js" as Playlists26import "../logic/playlists.js" as Playlists
27import "common"27import "../components"
28import "common/ListItemActions"28import "../components/Flickables"
29import "../components/HeadState"
30import "../components/ListItemActions"
2931
3032
31MusicPage {33MusicPage {
32 id: tracksPage34 id: songsPage
33 objectName: "tracksPage"35 objectName: "songsPage"
34 title: i18n.tr("Songs")36 title: i18n.tr("Songs")
35 searchable: true37 searchable: true
36 searchResultsCount: songsModelFilter.count38 searchResultsCount: songsModelFilter.count
37 state: "default"39 state: "default"
38 states: [40 states: [
39 PageHeadState {41 SearchableHeadState {
40 name: "default"42 thisPage: songsPage
41 head: tracksPage.head43 searchEnabled: songsModelFilter.count > 0
42 actions: Action {
43 iconName: "search"
44 onTriggered: tracksPage.state = "search"
45 }
46 },44 },
47 PageHeadState {45 MultiSelectHeadState {
48 id: selectionState46 listview: tracklist
49 name: "selection"47 thisPage: songsPage
50 backAction: Action {
51 text: i18n.tr("Cancel selection")
52 iconName: "back"
53 onTriggered: {
54 tracklist.clearSelection()
55 tracklist.state = "normal"
56 }
57 }
58 head: tracksPage.head
59 actions: [
60 Action {
61 iconName: "select"
62 text: i18n.tr("Select All")
63 onTriggered: {
64 if (tracklist.selectedItems.length === tracklist.model.count) {
65 tracklist.clearSelection()
66 } else {
67 tracklist.selectAll()
68 }
69 }
70 },
71 Action {
72 enabled: tracklist.selectedItems.length !== 0
73 iconName: "add-to-playlist"
74 text: i18n.tr("Add to playlist")
75 onTriggered: {
76 var items = []
77
78 for (var i=0; i < tracklist.selectedItems.length; i++) {
79 items.push(makeDict(tracklist.model.get(tracklist.selectedItems[i], tracklist.model.RoleModelData)));
80 }
81
82 var comp = Qt.createComponent("MusicaddtoPlaylist.qml")
83 var addToPlaylist = comp.createObject(mainPageStack, {"chosenElements": items});
84
85 if (addToPlaylist == null) { // Error Handling
86 console.log("Error creating object");
87 }
88
89 mainPageStack.push(addToPlaylist)
90
91 tracklist.closeSelection()
92 }
93 },
94 Action {
95 enabled: tracklist.selectedItems.length > 0
96 iconName: "add"
97 text: i18n.tr("Add to queue")
98 onTriggered: {
99 var items = []
100
101 for (var i=0; i < tracklist.selectedItems.length; i++) {
102 items.push(tracklist.model.get(tracklist.selectedItems[i], tracklist.model.RoleModelData));
103 }
104
105 trackQueue.appendList(items)
106
107 tracklist.closeSelection()
108 }
109 }
110 ]
111 },48 },
112 SearchHeadState {49 SearchHeadState {
113 id: searchHeader50 id: searchHeader
114 thisPage: tracksPage51 thisPage: songsPage
115 }52 }
116 ]53 ]
11754
118 ListView {55 // Hack for autopilot otherwise Albums appears as MusicPage
56 // due to bug 1341671 it is required that there is a property so that
57 // qml doesn't optimise using the parent type
58 property bool bug1341671workaround: true
59
60 MultiSelectListView {
119 id: tracklist61 id: tracklist
120 anchors {62 anchors {
121 bottomMargin: units.gu(2)63 bottomMargin: units.gu(2)
@@ -139,49 +81,12 @@
139 filterCaseSensitivity: Qt.CaseInsensitive81 filterCaseSensitivity: Qt.CaseInsensitive
140 }82 }
14183
142 Component.onCompleted: {
143 // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
144 // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration
145 var scaleFactor = units.gridUnit / 8;
146 maximumFlickVelocity = maximumFlickVelocity * scaleFactor;
147 flickDeceleration = flickDeceleration * scaleFactor;
148 }
149
150 // Requirements for ListItemWithActions
151 property var selectedItems: []
152
153 signal clearSelection()
154 signal closeSelection()
155 signal selectAll()
156
157 onClearSelection: selectedItems = []
158 onCloseSelection: {
159 clearSelection()
160 state = "normal"
161 }
162 onStateChanged: {84 onStateChanged: {
163 if (state === "multiselectable") {85 if (state === "multiselectable") {
164 tracksPage.state = "selection"86 songsPage.state = "selection"
165 } else {87 } else {
166 searchHeader.query = "" // force query back to default88 searchHeader.query = "" // force query back to default
167 tracksPage.state = "default"89 songsPage.state = "default"
168 }
169 }
170
171 onSelectAll: {
172 var tmp = selectedItems
173
174 for (var i=0; i < model.count; i++) {
175 if (tmp.indexOf(i) === -1) {
176 tmp.push(i)
177 }
178 }
179
180 selectedItems = tmp
181 }
182 onVisibleChanged: {
183 if (!visible) {
184 closeSelection()
185 }90 }
186 }91 }
18792
@@ -204,7 +109,7 @@
204 ]109 ]
205110
206 onItemClicked: {111 onItemClicked: {
207 if (tracksPage.state === "search") { // only play single track when searching112 if (songsPage.state === "search") { // only play single track when searching
208 trackQueue.clear()113 trackQueue.clear()
209 trackQueue.append(songsModelFilter.get(index))114 trackQueue.append(songsModelFilter.get(index))
210 trackQueueClick(0)115 trackQueueClick(0)
211116
=== renamed file 'common/SongsPage.qml' => 'app/ui/SongsView.qml'
--- common/SongsPage.qml 2015-01-30 01:23:39 +0000
+++ app/ui/SongsView.qml 2015-02-10 03:13:44 +0000
@@ -24,9 +24,13 @@
24import Ubuntu.MediaScanner 0.124import Ubuntu.MediaScanner 0.1
25import Ubuntu.Thumbnailer 0.125import Ubuntu.Thumbnailer 0.1
26import QtQuick.LocalStorage 2.026import QtQuick.LocalStorage 2.0
27import "../meta-database.js" as Library27import "../logic/meta-database.js" as Library
28import "../playlists.js" as Playlists28import "../logic/playlists.js" as Playlists
29import "ListItemActions"29import "../components"
30import "../components/Flickables"
31import "../components/HeadState"
32import "../components/ListItemActions"
33import "../components/ViewButton"
3034
31MusicPage {35MusicPage {
32 id: songStackPage36 id: songStackPage
@@ -121,7 +125,7 @@
121 objectName: "editPlaylist"125 objectName: "editPlaylist"
122 iconName: "edit"126 iconName: "edit"
123 onTriggered: {127 onTriggered: {
124 var dialog = PopupUtils.open(editPlaylistDialog, mainView)128 var dialog = PopupUtils.open(Qt.resolvedUrl("../components/Dialog/EditPlaylistDialog.qml"), mainView)
125 dialog.oldPlaylistName = line2129 dialog.oldPlaylistName = line2
126 }130 }
127 },131 },
@@ -129,7 +133,7 @@
129 objectName: "deletePlaylist"133 objectName: "deletePlaylist"
130 iconName: "delete"134 iconName: "delete"
131 onTriggered: {135 onTriggered: {
132 var dialog = PopupUtils.open(removePlaylistDialog, mainView)136 var dialog = PopupUtils.open(Qt.resolvedUrl("../components/Dialog/RemovePlaylistDialog.qml"), mainView)
133 dialog.oldPlaylistName = line2137 dialog.oldPlaylistName = line2
134 }138 }
135 }139 }
@@ -140,100 +144,29 @@
140 actions: playlistState.actions144 actions: playlistState.actions
141 }145 }
142 },146 },
143 PageHeadState {147 MultiSelectHeadState {
144 id: selectionState148 listview: albumtrackslist
145 name: "selection"149 removable: songStackPage.line1 === i18n.tr("Playlist")
146 backAction: Action {150 thisPage: songStackPage
147 text: i18n.tr("Cancel selection")151
148 iconName: "back"152 onRemoved: {
149 onTriggered: {153 for (var i=0; i < selectedItems.length; i++) {
150 albumtrackslist.clearSelection()154 Playlists.removeFromPlaylist(songStackPage.line2, selectedItems[i])
151 albumtrackslist.state = "normal"155
152 }156 // Update indexes as an index has been removed
153 }157 for (var j=i + 1; j < selectedItems.length; j++) {
154 actions: [158 if (selectedItems[j] > selectedItems[i]) {
155 Action {159 selectedItems[j]--;
156 iconName: "select"160 }
157 text: i18n.tr("Select All")161 }
158 onTriggered: {162 }
159 if (albumtrackslist.selectedItems.length === albumtrackslist.model.count) {163
160 albumtrackslist.clearSelection()164 playlistChangedHelper() // update recent/playlist models
161 } else {165
162 albumtrackslist.selectAll()166 albumTracksModel.filterPlaylistTracks(songStackPage.line2)
163 }167
164 }168 // refresh cover art
165 },169 songStackPage.covers = Playlists.getPlaylistCovers(songStackPage.line2)
166 Action {
167 enabled: albumtrackslist.selectedItems.length > 0
168 iconName: "add-to-playlist"
169 text: i18n.tr("Add to playlist")
170 onTriggered: {
171 var items = []
172
173 for (var i=0; i < albumtrackslist.selectedItems.length; i++) {
174 items.push(makeDict(albumtrackslist.model.get(albumtrackslist.selectedItems[i], albumtrackslist.model.RoleModelData)));
175 }
176
177 var comp = Qt.createComponent("../MusicaddtoPlaylist.qml")
178 var addToPlaylist = comp.createObject(mainPageStack, {"chosenElements": items, "page": songStackPage});
179
180 if (addToPlaylist == null) { // Error Handling
181 console.log("Error creating object");
182 }
183
184 mainPageStack.push(addToPlaylist)
185
186 albumtrackslist.closeSelection()
187 }
188 },
189 Action {
190 enabled: albumtrackslist.selectedItems.length > 0
191 iconName: "add"
192 text: i18n.tr("Add to queue")
193 onTriggered: {
194 var items = []
195
196 for (var i=0; i < albumtrackslist.selectedItems.length; i++) {
197 items.push(albumtrackslist.model.get(albumtrackslist.selectedItems[i], albumtrackslist.model.RoleModelData));
198 }
199
200 trackQueue.appendList(items)
201
202 albumtrackslist.closeSelection()
203 }
204 },
205 Action {
206 enabled: albumtrackslist.selectedItems.length > 0
207 iconName: "delete"
208 text: i18n.tr("Delete")
209 visible: songStackPage.line1 === i18n.tr("Playlist")
210 onTriggered: {
211 for (var i=0; i < albumtrackslist.selectedItems.length; i++) {
212 Playlists.removeFromPlaylist(songStackPage.line2, albumtrackslist.selectedItems[i])
213
214 // Update indexes as an index has been removed
215 for (var j=i + 1; j < albumtrackslist.selectedItems.length; j++) {
216 if (albumtrackslist.selectedItems[j] > albumtrackslist.selectedItems[i]) {
217 albumtrackslist.selectedItems[j]--;
218 }
219 }
220 }
221
222 albumtrackslist.closeSelection()
223
224 playlistChangedHelper() // update recent/playlist models
225
226 albumTracksModel.filterPlaylistTracks(songStackPage.line2)
227
228 // refresh cover art
229 songStackPage.covers = Playlists.getPlaylistCovers(songStackPage.line2)
230 }
231 }
232 ]
233 PropertyChanges {
234 target: songStackPage.head
235 backAction: selectionState.backAction
236 actions: selectionState.actions
237 }170 }
238 }171 }
239 ]172 ]
@@ -243,12 +176,12 @@
243 store: musicStore176 store: musicStore
244 onStatusChanged: {177 onStatusChanged: {
245 if (songsModel.status === SongsModel.Ready && loaded && songsModel.count === 0) {178 if (songsModel.status === SongsModel.Ready && loaded && songsModel.count === 0) {
246 musicToolbar.popPage(songStackPage)179 mainPageStack.popPage(songStackPage)
247 }180 }
248 }181 }
249 }182 }
250183
251 ListView {184 MultiSelectListView {
252 id: albumtrackslist185 id: albumtrackslist
253 anchors {186 anchors {
254 fill: parent187 fill: parent
@@ -258,67 +191,12 @@
258 objectName: "songspage-listview"191 objectName: "songspage-listview"
259 width: parent.width192 width: parent.width
260193
261 // Requirements for ListItemWithActions
262 property var selectedItems: []
263
264 signal clearSelection()
265 signal closeSelection()
266 signal selectAll()
267
268 onClearSelection: selectedItems = []
269 onCloseSelection: {
270 clearSelection()
271 state = "normal"
272 }
273 onSelectAll: {
274 var tmp = selectedItems
275
276 for (var i=0; i < model.count; i++) {
277 if (tmp.indexOf(i) === -1) {
278 tmp.push(i)
279 }
280 }
281
282 selectedItems = tmp
283 }
284 onVisibleChanged: {
285 if (!visible) {
286 closeSelection()
287 }
288 }
289
290 Component.onCompleted: {
291 // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
292 // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration
293 var scaleFactor = units.gridUnit / 8;
294 maximumFlickVelocity = maximumFlickVelocity * scaleFactor;
295 flickDeceleration = flickDeceleration * scaleFactor;
296 }
297
298 header: BlurredHeader {194 header: BlurredHeader {
299 rightColumn: Column {195 rightColumn: Column {
300 spacing: units.gu(2)196 spacing: units.gu(2)
301 Button {197 ShuffleButton {
302 id: shuffleRow198 model: albumtrackslist.model
303 height: units.gu(4)
304 strokeColor: UbuntuColors.green
305 width: units.gu(15)
306 Text {
307 anchors {
308 centerIn: parent
309 }
310 color: "white"
311 elide: Text.ElideRight
312 height: parent.height
313 horizontalAlignment: Text.AlignHCenter
314 // TRANSLATORS: this appears in a button with limited space (around 14 characters)
315 text: i18n.tr("Shuffle")
316 verticalAlignment: Text.AlignVCenter
317 width: parent.width - units.gu(2)
318 }
319 onClicked: {199 onClicked: {
320 shuffleModel(albumtrackslist.model) // play track
321
322 if (isAlbum && songStackPage.line1 !== i18n.tr("Genre")) {200 if (isAlbum && songStackPage.line1 !== i18n.tr("Genre")) {
323 Library.addRecent(albumtrackslist.model.get(0, albumtrackslist.model.RoleModelData).album, "album")201 Library.addRecent(albumtrackslist.model.get(0, albumtrackslist.model.RoleModelData).album, "album")
324 } else if (songStackPage.line1 === i18n.tr("Playlist")) {202 } else if (songStackPage.line1 === i18n.tr("Playlist")) {
@@ -330,36 +208,12 @@
330 recentModel.filterRecent()208 recentModel.filterRecent()
331 }209 }
332 }210 }
333 Button {211 QueueAllButton {
334 id: queueAllRow212 model: albumtrackslist.model
335 height: units.gu(4)
336 strokeColor: UbuntuColors.green
337 width: units.gu(15)
338 Text {
339 anchors {
340 centerIn: parent
341 }
342 color: "white"
343 elide: Text.ElideRight
344 height: parent.height
345 horizontalAlignment: Text.AlignHCenter
346 // TRANSLATORS: this appears in a button with limited space (around 14 characters)
347 text: i18n.tr("Queue all")
348 verticalAlignment: Text.AlignVCenter
349 width: parent.width - units.gu(2)
350 }
351 onClicked: addQueueFromModel(albumtrackslist.model)
352 }213 }
353 Button {214 PlayAllButton {
354 id: playRow215 model: albumtrackslist.model
355 color: UbuntuColors.green
356 height: units.gu(4)
357 // TRANSLATORS: this appears in a button with limited space (around 14 characters)
358 text: i18n.tr("Play all")
359 width: units.gu(15)
360 onClicked: {216 onClicked: {
361 trackClicked(albumtrackslist.model, 0) // play track
362
363 if (isAlbum && songStackPage.line1 !== i18n.tr("Genre")) {217 if (isAlbum && songStackPage.line1 !== i18n.tr("Genre")) {
364 Library.addRecent(albumtrackslist.model.get(0, albumtrackslist.model.RoleModelData).album, "album")218 Library.addRecent(albumtrackslist.model.get(0, albumtrackslist.model.RoleModelData).album, "album")
365 } else if (songStackPage.line1 === i18n.tr("Playlist")) {219 } else if (songStackPage.line1 === i18n.tr("Playlist")) {
@@ -528,101 +382,4 @@
528 }382 }
529383
530 Component.onCompleted: loaded = true384 Component.onCompleted: loaded = true
531
532 // Edit name of playlist dialog
533 Component {
534 id: editPlaylistDialog
535 Dialog {
536 id: dialogEditPlaylist
537 // TRANSLATORS: this is a title of a dialog with a prompt to rename a playlist
538 title: i18n.tr("Rename playlist")
539
540 property string oldPlaylistName: ""
541
542 TextField {
543 id: playlistName
544 inputMethodHints: Qt.ImhNoPredictiveText
545 placeholderText: i18n.tr("Enter playlist name")
546 }
547 Label {
548 id: editplaylistoutput
549 color: "red"
550 visible: false
551 }
552
553 Button {
554 text: i18n.tr("Change")
555 color: styleMusic.dialog.confirmButtonColor
556 onClicked: {
557 editplaylistoutput.visible = true
558
559 if (playlistName.text.length > 0) { // make sure something is acually inputed
560 console.debug("Debug: User changed name from "+oldPlaylistName+" to "+playlistName.text)
561
562 if (Playlists.renamePlaylist(oldPlaylistName, playlistName.text) === true) {
563
564 if (Library.recentContainsPlaylist(oldPlaylistName)) {
565 Library.recentRenamePlaylist(oldPlaylistName, playlistName.text)
566 }
567
568 line2 = playlistName.text
569
570 playlistChangedHelper() // update recent/playlist models
571
572 PopupUtils.close(dialogEditPlaylist)
573 }
574 else {
575 editplaylistoutput.text = i18n.tr("Playlist already exists")
576 }
577 }
578 else {
579 editplaylistoutput.text = i18n.tr("Please type in a name.")
580 }
581 }
582 }
583 Button {
584 text: i18n.tr("Cancel")
585 color: styleMusic.dialog.cancelButtonColor
586 onClicked: PopupUtils.close(dialogEditPlaylist)
587 }
588 }
589 }
590
591 // Remove playlist dialog
592 Component {
593 id: removePlaylistDialog
594 Dialog {
595 id: dialogRemovePlaylist
596 // TRANSLATORS: this is a title of a dialog with a prompt to delete a playlist
597 title: i18n.tr("Permanently delete playlist?")
598 text: "("+i18n.tr("This cannot be undone")+")"
599
600 property string oldPlaylistName
601
602 Button {
603 text: i18n.tr("Remove")
604 color: styleMusic.dialog.confirmRemoveButtonColor
605 onClicked: {
606 // removing playlist
607 Playlists.removePlaylist(dialogRemovePlaylist.oldPlaylistName)
608
609 if (Library.recentContainsPlaylist(dialogRemovePlaylist.oldPlaylistName)) {
610 Library.recentRemovePlaylist(dialogRemovePlaylist.oldPlaylistName)
611 }
612
613 playlistChangedHelper(true) // update recent/playlist models
614
615 songStackPage.page = undefined
616 PopupUtils.close(dialogRemovePlaylist)
617
618 musicToolbar.goBack()
619 }
620 }
621 Button {
622 text: i18n.tr("Cancel")
623 color: styleMusic.dialog.cancelButtonColor
624 onClicked: PopupUtils.close(dialogRemovePlaylist)
625 }
626 }
627 }
628}385}
629386
=== renamed file 'click/apparmor.json' => 'apparmor.json'
=== removed directory 'click'
=== removed file 'click/CMakeLists.txt'
--- click/CMakeLists.txt 2014-07-01 23:13:50 +0000
+++ click/CMakeLists.txt 1970-01-01 00:00:00 +0000
@@ -1,15 +0,0 @@
1if(CLICK_MODE)
2 if(NOT BZR_REVNO)
3 set(BZR_REVNO "latest")
4 endif(NOT BZR_REVNO)
5 configure_file(manifest.json.in ${CMAKE_CURRENT_BINARY_DIR}/manifest.json)
6 install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json apparmor.json
7 music-app-content.json DESTINATION ${CMAKE_INSTALL_PREFIX})
8endif(CLICK_MODE)
9
10# make the click files visible on qtcreator
11file(GLOB CLICK_FILES
12 RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
13 *.json *.json.in)
14
15add_custom_target(com_ubuntu_music_CLICKFiles ALL SOURCES ${CLICK_FILES})
160
=== renamed file 'click/manifest.json.in' => 'manifest.json.in'
=== renamed file 'click/music-app-content.json' => 'music-app-content.json'
=== modified file 'tests/autopilot/music_app/__init__.py'
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches