Merge lp:~music-app-dev/music-app/prototype-now-playing-side-panel into lp:music-app/remix
- prototype-now-playing-side-panel
- Merge into remix
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 |
Related bugs: |
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.
- 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 MultiSelectList
View.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
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2014-09-08 08:08:13 +0000 |
3 | +++ CMakeLists.txt 2015-02-10 03:13:44 +0000 |
4 | @@ -13,10 +13,10 @@ |
5 | set(DESKTOP_FILE "${PROJECT_NAME}_${APP_NAME}.desktop") |
6 | set(URLS_FILE "${PROJECT_NAME}_${APP_NAME}.url-dispatcher") |
7 | set(APP_HARDCODE music-app) |
8 | -set(MAIN_QML music-app.qml) |
9 | -set(ICON_FILE images/music-app@30.png) |
10 | +set(MAIN_QML app/music-app.qml) |
11 | +set(ICON_FILE app/graphics/music-app@30.png) |
12 | set(AUTOPILOT_DIR music_app) |
13 | -set(UBUNTU_MANIFEST_PATH "click/manifest.json.in" CACHE INTERNAL "Tell QtCreator location and name of the manifest file") |
14 | +set(UBUNTU_MANIFEST_PATH "manifest.json.in" CACHE INTERNAL "Tell QtCreator location and name of the manifest file") |
15 | |
16 | if(CLICK_MODE) |
17 | if(NOT DEFINED BZR_SOURCE) |
18 | @@ -30,6 +30,14 @@ |
19 | set(EXEC "qmlscene -qt5 ${MAIN_QML} --url=%u -I ./plugins") |
20 | set(DESKTOP_DIR ${DATA_DIR}) |
21 | set(URLS_DIR ${DATA_DIR}) |
22 | + |
23 | + if(NOT BZR_REVNO) |
24 | + set(BZR_REVNO "latest") |
25 | + endif(NOT BZR_REVNO) |
26 | + |
27 | + configure_file(manifest.json.in ${CMAKE_CURRENT_BINARY_DIR}/manifest.json) |
28 | + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json apparmor.json |
29 | + music-app-content.json DESTINATION ${CMAKE_INSTALL_PREFIX}) |
30 | else(CLICK_MODE) |
31 | set(DATA_DIR ${CMAKE_INSTALL_DATADIR}/${APP_HARDCODE}) |
32 | set(EXEC ${APP_HARDCODE}) |
33 | @@ -53,7 +61,7 @@ |
34 | file(GLOB SRC_FILES |
35 | RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} |
36 | *.qml *.js *.png *.js *.json) |
37 | -install(DIRECTORY common images DESTINATION ${DATA_DIR}) |
38 | +install(DIRECTORY app DESTINATION ${DATA_DIR}) |
39 | install(FILES ${SRC_FILES} ${ICON_FILE} DESTINATION ${DATA_DIR}) |
40 | |
41 | configure_file(${DESKTOP_FILE}.in.in ${DESKTOP_FILE}.in) |
42 | @@ -71,9 +79,7 @@ |
43 | # Tests |
44 | enable_testing() |
45 | |
46 | -add_subdirectory(click) |
47 | -add_subdirectory(common) |
48 | -add_subdirectory(images) |
49 | +add_subdirectory(app) |
50 | add_subdirectory(po) |
51 | add_subdirectory(tests) |
52 | |
53 | |
54 | === added directory 'app' |
55 | === added file 'app/CMakeLists.txt' |
56 | --- app/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
57 | +++ app/CMakeLists.txt 2015-02-10 03:13:44 +0000 |
58 | @@ -0,0 +1,10 @@ |
59 | +add_subdirectory(components) |
60 | +add_subdirectory(graphics) |
61 | +add_subdirectory(logic) |
62 | +add_subdirectory(ui) |
63 | + |
64 | +# make the qml files visible on qtcreator |
65 | +file(GLOB APP_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} |
66 | +*.qml *.js *.png *.js *.json) |
67 | + |
68 | +add_custom_target(com_ubuntu_music_APP_Files ALL SOURCES ${APP_FILES}) |
69 | |
70 | === renamed directory 'common' => 'app/components' |
71 | === modified file 'app/components/BlurredBackground.qml' |
72 | --- common/BlurredBackground.qml 2014-11-11 02:46:42 +0000 |
73 | +++ app/components/BlurredBackground.qml 2015-02-10 03:13:44 +0000 |
74 | @@ -25,7 +25,7 @@ |
75 | Item { |
76 | width: parent.width |
77 | |
78 | - property string art // : player.currentMetaFile === "" ? Qt.resolvedUrl("../images/music-app-cover@30.png") : player.currentMetaArt |
79 | + property string art // : player.currentMetaFile === "" ? Qt.resolvedUrl("../graphics/music-app-cover@30.png") : player.currentMetaArt |
80 | |
81 | // dark layer |
82 | Rectangle { |
83 | |
84 | === modified file 'app/components/CMakeLists.txt' |
85 | --- common/CMakeLists.txt 2015-01-20 17:47:30 +0000 |
86 | +++ app/components/CMakeLists.txt 2015-02-10 03:13:44 +0000 |
87 | @@ -1,7 +1,11 @@ |
88 | +add_subdirectory(Dialog) |
89 | +add_subdirectory(HeadState) |
90 | +add_subdirectory(Flickables) |
91 | add_subdirectory(ListItemActions) |
92 | +add_subdirectory(ViewButton) |
93 | add_subdirectory(Walkthrough) |
94 | |
95 | # make the qml files visible on qtcreator |
96 | -file(GLOB COMMON_QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml) |
97 | +file(GLOB COMPONENTS_QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml) |
98 | |
99 | -add_custom_target(com_ubuntu_music_COMMON_QMLFiles ALL SOURCES ${COMMON_QML_FILES}) |
100 | +add_custom_target(com_ubuntu_music_COMPONENTS_QMLFiles ALL SOURCES ${COMPONENTS_QML_FILES}) |
101 | |
102 | === modified file 'app/components/CoverGrid.qml' |
103 | --- common/CoverGrid.qml 2015-01-11 16:34:22 +0000 |
104 | +++ app/components/CoverGrid.qml 2015-02-10 03:13:44 +0000 |
105 | @@ -60,7 +60,7 @@ |
106 | ? (coverGrid.covers[index].art !== undefined |
107 | ? coverGrid.covers[index].art |
108 | : "image://albumart/artist=" + coverGrid.covers[index].author + "&album=" + coverGrid.covers[index].album) |
109 | - : Qt.resolvedUrl("../images/music-app-cover@30.png") |
110 | + : Qt.resolvedUrl("../graphics/music-app-cover@30.png") |
111 | |
112 | // TODO: This should be investigated once http://pad.lv/1391368 |
113 | // is resolved. Once it is, these can either be set to |
114 | @@ -73,7 +73,7 @@ |
115 | |
116 | onStatusChanged: { |
117 | if (status === Image.Error) { |
118 | - source = Qt.resolvedUrl("../images/music-app-cover@30.png") |
119 | + source = Qt.resolvedUrl("../graphics/music-app-cover@30.png") |
120 | } else if (status === Image.Ready && index === 0) { |
121 | firstSource = source |
122 | } |
123 | |
124 | === added directory 'app/components/Dialog' |
125 | === added file 'app/components/Dialog/CMakeLists.txt' |
126 | --- app/components/Dialog/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
127 | +++ app/components/Dialog/CMakeLists.txt 2015-02-10 03:13:44 +0000 |
128 | @@ -0,0 +1,4 @@ |
129 | +# make the qml files visible on qtcreator |
130 | +file(GLOB DIALOG_QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml) |
131 | + |
132 | +add_custom_target(com_ubuntu_music_DIALOG_QMLFiles ALL SOURCES ${DIALOG_QML_FILES}) |
133 | |
134 | === added file 'app/components/Dialog/ContentHubErrorDialog.qml' |
135 | --- app/components/Dialog/ContentHubErrorDialog.qml 1970-01-01 00:00:00 +0000 |
136 | +++ app/components/Dialog/ContentHubErrorDialog.qml 2015-02-10 03:13:44 +0000 |
137 | @@ -0,0 +1,38 @@ |
138 | +/* |
139 | + * Copyright (C) 2013, 2014, 2015 |
140 | + * Andrew Hayzen <ahayzen@gmail.com> |
141 | + * Daniel Holm <d.holmen@gmail.com> |
142 | + * Victor Thompson <victor.thompson@gmail.com> |
143 | + * |
144 | + * This program is free software; you can redistribute it and/or modify |
145 | + * it under the terms of the GNU General Public License as published by |
146 | + * the Free Software Foundation; version 3. |
147 | + * |
148 | + * This program is distributed in the hope that it will be useful, |
149 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
150 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
151 | + * GNU General Public License for more details. |
152 | + * |
153 | + * You should have received a copy of the GNU General Public License |
154 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
155 | + */ |
156 | + |
157 | +import QtQuick 2.3 |
158 | +import Ubuntu.Components 1.1 |
159 | +import Ubuntu.Components.Popups 1.0 |
160 | + |
161 | +Dialog { |
162 | + id: dialogContentHubError |
163 | + |
164 | + property alias errorText: errorLabel.text |
165 | + |
166 | + Label { |
167 | + id: errorLabel |
168 | + color: styleMusic.common.black |
169 | + } |
170 | + |
171 | + Button { |
172 | + text: i18n.tr("OK") |
173 | + onClicked: PopupUtils.close(dialogContentHubError) |
174 | + } |
175 | +} |
176 | |
177 | === added file 'app/components/Dialog/ContentHubNotFoundDialog.qml' |
178 | --- app/components/Dialog/ContentHubNotFoundDialog.qml 1970-01-01 00:00:00 +0000 |
179 | +++ app/components/Dialog/ContentHubNotFoundDialog.qml 2015-02-10 03:13:44 +0000 |
180 | @@ -0,0 +1,46 @@ |
181 | +/* |
182 | + * Copyright (C) 2013, 2014, 2015 |
183 | + * Andrew Hayzen <ahayzen@gmail.com> |
184 | + * Daniel Holm <d.holmen@gmail.com> |
185 | + * Victor Thompson <victor.thompson@gmail.com> |
186 | + * |
187 | + * This program is free software; you can redistribute it and/or modify |
188 | + * it under the terms of the GNU General Public License as published by |
189 | + * the Free Software Foundation; version 3. |
190 | + * |
191 | + * This program is distributed in the hope that it will be useful, |
192 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
193 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
194 | + * GNU General Public License for more details. |
195 | + * |
196 | + * You should have received a copy of the GNU General Public License |
197 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
198 | + */ |
199 | + |
200 | +import QtQuick 2.3 |
201 | +import Ubuntu.Components 1.1 |
202 | +import Ubuntu.Components.Popups 1.0 |
203 | + |
204 | +Dialog { |
205 | + id: dialogContentHubNotFound |
206 | + |
207 | + Label { |
208 | + color: styleMusic.common.black |
209 | + text: i18n.tr("Imported file not found") |
210 | + } |
211 | + |
212 | + Button { |
213 | + text: i18n.tr("Wait") |
214 | + onClicked: { |
215 | + PopupUtils.close(dialogContentHubNotFound) |
216 | + |
217 | + contentHubWaitForFile.dialog = PopupUtils.open(Qt.resolvedUrl("ContentHubWaitDialog.qml"), mainView) |
218 | + contentHubWaitForFile.start(); |
219 | + } |
220 | + } |
221 | + |
222 | + Button { |
223 | + text: i18n.tr("Cancel") |
224 | + onClicked: PopupUtils.close(dialogContentHubNotFound) |
225 | + } |
226 | +} |
227 | |
228 | === added file 'app/components/Dialog/ContentHubWaitDialog.qml' |
229 | --- app/components/Dialog/ContentHubWaitDialog.qml 1970-01-01 00:00:00 +0000 |
230 | +++ app/components/Dialog/ContentHubWaitDialog.qml 2015-02-10 03:13:44 +0000 |
231 | @@ -0,0 +1,37 @@ |
232 | +/* |
233 | + * Copyright (C) 2013, 2014, 2015 |
234 | + * Andrew Hayzen <ahayzen@gmail.com> |
235 | + * Daniel Holm <d.holmen@gmail.com> |
236 | + * Victor Thompson <victor.thompson@gmail.com> |
237 | + * |
238 | + * This program is free software; you can redistribute it and/or modify |
239 | + * it under the terms of the GNU General Public License as published by |
240 | + * the Free Software Foundation; version 3. |
241 | + * |
242 | + * This program is distributed in the hope that it will be useful, |
243 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
244 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
245 | + * GNU General Public License for more details. |
246 | + * |
247 | + * You should have received a copy of the GNU General Public License |
248 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
249 | + */ |
250 | + |
251 | +import QtQuick 2.3 |
252 | +import Ubuntu.Components 1.1 |
253 | +import Ubuntu.Components.Popups 1.0 |
254 | +import "../" |
255 | + |
256 | +Dialog { |
257 | + id: dialogContentHubWait |
258 | + |
259 | + LoadingSpinnerComponent { |
260 | + anchors { |
261 | + horizontalCenter: parent.horizontalCenter |
262 | + top: undefined |
263 | + margins: units.gu(0) |
264 | + } |
265 | + loadingText: i18n.tr("Waiting for file(s)...") |
266 | + visible: true |
267 | + } |
268 | +} |
269 | |
270 | === added file 'app/components/Dialog/EditPlaylistDialog.qml' |
271 | --- app/components/Dialog/EditPlaylistDialog.qml 1970-01-01 00:00:00 +0000 |
272 | +++ app/components/Dialog/EditPlaylistDialog.qml 2015-02-10 03:13:44 +0000 |
273 | @@ -0,0 +1,80 @@ |
274 | +/* |
275 | + * Copyright (C) 2013, 2014, 2015 |
276 | + * Andrew Hayzen <ahayzen@gmail.com> |
277 | + * Daniel Holm <d.holmen@gmail.com> |
278 | + * Victor Thompson <victor.thompson@gmail.com> |
279 | + * |
280 | + * This program is free software; you can redistribute it and/or modify |
281 | + * it under the terms of the GNU General Public License as published by |
282 | + * the Free Software Foundation; version 3. |
283 | + * |
284 | + * This program is distributed in the hope that it will be useful, |
285 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
286 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
287 | + * GNU General Public License for more details. |
288 | + * |
289 | + * You should have received a copy of the GNU General Public License |
290 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
291 | + */ |
292 | + |
293 | +import QtQuick 2.3 |
294 | +import Ubuntu.Components 1.1 |
295 | +import Ubuntu.Components.Popups 1.0 |
296 | +import QtQuick.LocalStorage 2.0 |
297 | +import "../../logic/meta-database.js" as Library |
298 | +import "../../logic/playlists.js" as Playlists |
299 | + |
300 | +Dialog { |
301 | + id: dialogEditPlaylist |
302 | + // TRANSLATORS: this is a title of a dialog with a prompt to rename a playlist |
303 | + title: i18n.tr("Rename playlist") |
304 | + |
305 | + property string oldPlaylistName: "" |
306 | + |
307 | + TextField { |
308 | + id: playlistName |
309 | + inputMethodHints: Qt.ImhNoPredictiveText |
310 | + placeholderText: i18n.tr("Enter playlist name") |
311 | + } |
312 | + Label { |
313 | + id: editplaylistoutput |
314 | + color: "red" |
315 | + visible: false |
316 | + } |
317 | + |
318 | + Button { |
319 | + text: i18n.tr("Change") |
320 | + color: styleMusic.dialog.confirmButtonColor |
321 | + onClicked: { |
322 | + editplaylistoutput.visible = true |
323 | + |
324 | + if (playlistName.text.length > 0) { // make sure something is acually inputed |
325 | + console.debug("Debug: User changed name from "+oldPlaylistName+" to "+playlistName.text) |
326 | + |
327 | + if (Playlists.renamePlaylist(oldPlaylistName, playlistName.text) === true) { |
328 | + |
329 | + if (Library.recentContainsPlaylist(oldPlaylistName)) { |
330 | + Library.recentRenamePlaylist(oldPlaylistName, playlistName.text) |
331 | + } |
332 | + |
333 | + line2 = playlistName.text |
334 | + |
335 | + playlistChangedHelper() // update recent/playlist models |
336 | + |
337 | + PopupUtils.close(dialogEditPlaylist) |
338 | + } |
339 | + else { |
340 | + editplaylistoutput.text = i18n.tr("Playlist already exists") |
341 | + } |
342 | + } |
343 | + else { |
344 | + editplaylistoutput.text = i18n.tr("Please type in a name.") |
345 | + } |
346 | + } |
347 | + } |
348 | + Button { |
349 | + text: i18n.tr("Cancel") |
350 | + color: styleMusic.dialog.cancelButtonColor |
351 | + onClicked: PopupUtils.close(dialogEditPlaylist) |
352 | + } |
353 | +} |
354 | |
355 | === added file 'app/components/Dialog/NewPlaylistDialog.qml' |
356 | --- app/components/Dialog/NewPlaylistDialog.qml 1970-01-01 00:00:00 +0000 |
357 | +++ app/components/Dialog/NewPlaylistDialog.qml 2015-02-10 03:13:44 +0000 |
358 | @@ -0,0 +1,75 @@ |
359 | +/* |
360 | + * Copyright (C) 2013, 2014, 2015 |
361 | + * Andrew Hayzen <ahayzen@gmail.com> |
362 | + * Daniel Holm <d.holmen@gmail.com> |
363 | + * Victor Thompson <victor.thompson@gmail.com> |
364 | + * |
365 | + * This program is free software; you can redistribute it and/or modify |
366 | + * it under the terms of the GNU General Public License as published by |
367 | + * the Free Software Foundation; version 3. |
368 | + * |
369 | + * This program is distributed in the hope that it will be useful, |
370 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
371 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
372 | + * GNU General Public License for more details. |
373 | + * |
374 | + * You should have received a copy of the GNU General Public License |
375 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
376 | + */ |
377 | + |
378 | +import QtQuick 2.3 |
379 | +import Ubuntu.Components 1.1 |
380 | +import Ubuntu.Components.Popups 1.0 |
381 | +import QtQuick.LocalStorage 2.0 |
382 | +import "../../logic/playlists.js" as Playlists |
383 | + |
384 | + |
385 | +Dialog { |
386 | + id: dialogNewPlaylist |
387 | + objectName: "dialogNewPlaylist" |
388 | + title: i18n.tr("New playlist") |
389 | + TextField { |
390 | + id: playlistName |
391 | + objectName: "playlistNameTextField" |
392 | + placeholderText: i18n.tr("Enter playlist name") |
393 | + inputMethodHints: Qt.ImhNoPredictiveText |
394 | + } |
395 | + Label { |
396 | + id: newplaylistoutput |
397 | + color: "red" |
398 | + visible: false // should only be visible when an error is made. |
399 | + } |
400 | + |
401 | + Button { |
402 | + text: i18n.tr("Create") |
403 | + color: styleMusic.dialog.confirmButtonColor |
404 | + objectName: "newPlaylistDialogCreateButton" |
405 | + onClicked: { |
406 | + newplaylistoutput.visible = false // make sure its hidden now if there was an error last time |
407 | + if (playlistName.text.length > 0) { // make sure something is acually inputed |
408 | + if (Playlists.addPlaylist(playlistName.text) === true) { |
409 | + console.debug("Debug: User created a new playlist named: ", playlistName.text) |
410 | + |
411 | + playlistModel.filterPlaylists(); // reload model |
412 | + |
413 | + PopupUtils.close(dialogNewPlaylist) |
414 | + } |
415 | + else { |
416 | + console.debug("Debug: Playlist already exists") |
417 | + newplaylistoutput.visible = true |
418 | + newplaylistoutput.text = i18n.tr("Playlist already exists") |
419 | + } |
420 | + } |
421 | + else { |
422 | + newplaylistoutput.visible = true |
423 | + newplaylistoutput.text = i18n.tr("Please type in a name.") |
424 | + } |
425 | + } |
426 | + } |
427 | + |
428 | + Button { |
429 | + text: i18n.tr("Cancel") |
430 | + color: styleMusic.dialog.cancelButtonColor |
431 | + onClicked: PopupUtils.close(dialogNewPlaylist) |
432 | + } |
433 | +} |
434 | |
435 | === added file 'app/components/Dialog/RemovePlaylistDialog.qml' |
436 | --- app/components/Dialog/RemovePlaylistDialog.qml 1970-01-01 00:00:00 +0000 |
437 | +++ app/components/Dialog/RemovePlaylistDialog.qml 2015-02-10 03:13:44 +0000 |
438 | @@ -0,0 +1,59 @@ |
439 | +/* |
440 | + * Copyright (C) 2013, 2014, 2015 |
441 | + * Andrew Hayzen <ahayzen@gmail.com> |
442 | + * Daniel Holm <d.holmen@gmail.com> |
443 | + * Victor Thompson <victor.thompson@gmail.com> |
444 | + * |
445 | + * This program is free software; you can redistribute it and/or modify |
446 | + * it under the terms of the GNU General Public License as published by |
447 | + * the Free Software Foundation; version 3. |
448 | + * |
449 | + * This program is distributed in the hope that it will be useful, |
450 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
451 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
452 | + * GNU General Public License for more details. |
453 | + * |
454 | + * You should have received a copy of the GNU General Public License |
455 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
456 | + */ |
457 | + |
458 | +import QtQuick 2.3 |
459 | +import Ubuntu.Components 1.1 |
460 | +import Ubuntu.Components.Popups 1.0 |
461 | +import QtQuick.LocalStorage 2.0 |
462 | +import "../../logic/meta-database.js" as Library |
463 | +import "../../logic/playlists.js" as Playlists |
464 | + |
465 | +Dialog { |
466 | + id: dialogRemovePlaylist |
467 | + // TRANSLATORS: this is a title of a dialog with a prompt to delete a playlist |
468 | + title: i18n.tr("Permanently delete playlist?") |
469 | + text: "("+i18n.tr("This cannot be undone")+")" |
470 | + |
471 | + property string oldPlaylistName |
472 | + |
473 | + Button { |
474 | + text: i18n.tr("Remove") |
475 | + color: styleMusic.dialog.confirmRemoveButtonColor |
476 | + onClicked: { |
477 | + // removing playlist |
478 | + Playlists.removePlaylist(dialogRemovePlaylist.oldPlaylistName) |
479 | + |
480 | + if (Library.recentContainsPlaylist(dialogRemovePlaylist.oldPlaylistName)) { |
481 | + Library.recentRemovePlaylist(dialogRemovePlaylist.oldPlaylistName) |
482 | + } |
483 | + |
484 | + playlistChangedHelper(true) // update recent/playlist models |
485 | + |
486 | + songStackPage.page = undefined |
487 | + PopupUtils.close(dialogRemovePlaylist) |
488 | + |
489 | + mainPageStack.goBack() |
490 | + } |
491 | + } |
492 | + Button { |
493 | + text: i18n.tr("Cancel") |
494 | + color: styleMusic.dialog.cancelButtonColor |
495 | + onClicked: PopupUtils.close(dialogRemovePlaylist) |
496 | + } |
497 | +} |
498 | |
499 | === added directory 'app/components/Flickables' |
500 | === added file 'app/components/Flickables/CMakeLists.txt' |
501 | --- app/components/Flickables/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
502 | +++ app/components/Flickables/CMakeLists.txt 2015-02-10 03:13:44 +0000 |
503 | @@ -0,0 +1,4 @@ |
504 | +# make the qml files visible on qtcreator |
505 | +file(GLOB FLICKABLES_QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml) |
506 | + |
507 | +add_custom_target(com_ubuntu_music_FLICKABLES_QMLFiles ALL SOURCES ${FLICKABLES_QML_FILES}) |
508 | |
509 | === renamed file 'common/CardView.qml' => 'app/components/Flickables/CardView.qml' |
510 | --- common/CardView.qml 2014-11-23 03:50:40 +0000 |
511 | +++ app/components/Flickables/CardView.qml 2015-02-10 03:13:44 +0000 |
512 | @@ -17,6 +17,7 @@ |
513 | |
514 | import QtQuick 2.3 |
515 | import Ubuntu.Components 1.1 |
516 | +import "../" |
517 | |
518 | |
519 | Flickable { |
520 | |
521 | === added file 'app/components/Flickables/MultiSelectListView.qml' |
522 | --- app/components/Flickables/MultiSelectListView.qml 1970-01-01 00:00:00 +0000 |
523 | +++ app/components/Flickables/MultiSelectListView.qml 2015-02-10 03:13:44 +0000 |
524 | @@ -0,0 +1,51 @@ |
525 | +/* |
526 | + * Copyright (C) 2013, 2014, 2015 |
527 | + * Andrew Hayzen <ahayzen@gmail.com> |
528 | + * Daniel Holm <d.holmen@gmail.com> |
529 | + * Victor Thompson <victor.thompson@gmail.com> |
530 | + * |
531 | + * This program is free software; you can redistribute it and/or modify |
532 | + * it under the terms of the GNU General Public License as published by |
533 | + * the Free Software Foundation; version 3. |
534 | + * |
535 | + * This program is distributed in the hope that it will be useful, |
536 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
537 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
538 | + * GNU General Public License for more details. |
539 | + * |
540 | + * You should have received a copy of the GNU General Public License |
541 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
542 | + */ |
543 | + |
544 | +import QtQuick 2.3 |
545 | +import Ubuntu.Components 1.1 |
546 | + |
547 | +MusicListView { |
548 | + property var selectedItems: [] |
549 | + |
550 | + signal clearSelection() |
551 | + signal closeSelection() |
552 | + signal selectAll() |
553 | + |
554 | + onClearSelection: selectedItems = [] |
555 | + onCloseSelection: { |
556 | + clearSelection() |
557 | + state = "normal" |
558 | + } |
559 | + onSelectAll: { |
560 | + var tmp = selectedItems |
561 | + |
562 | + for (var i=0; i < model.count; i++) { |
563 | + if (tmp.indexOf(i) === -1) { |
564 | + tmp.push(i) |
565 | + } |
566 | + } |
567 | + |
568 | + selectedItems = tmp |
569 | + } |
570 | + onVisibleChanged: { |
571 | + if (!visible) { |
572 | + closeSelection() |
573 | + } |
574 | + } |
575 | +} |
576 | |
577 | === added file 'app/components/Flickables/MusicListView.qml' |
578 | --- app/components/Flickables/MusicListView.qml 1970-01-01 00:00:00 +0000 |
579 | +++ app/components/Flickables/MusicListView.qml 2015-02-10 03:13:44 +0000 |
580 | @@ -0,0 +1,32 @@ |
581 | +/* |
582 | + * Copyright (C) 2013, 2014, 2015 |
583 | + * Andrew Hayzen <ahayzen@gmail.com> |
584 | + * Daniel Holm <d.holmen@gmail.com> |
585 | + * Victor Thompson <victor.thompson@gmail.com> |
586 | + * |
587 | + * This program is free software; you can redistribute it and/or modify |
588 | + * it under the terms of the GNU General Public License as published by |
589 | + * the Free Software Foundation; version 3. |
590 | + * |
591 | + * This program is distributed in the hope that it will be useful, |
592 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
593 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
594 | + * GNU General Public License for more details. |
595 | + * |
596 | + * You should have received a copy of the GNU General Public License |
597 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
598 | + */ |
599 | + |
600 | +import QtQuick 2.3 |
601 | +import Ubuntu.Components 1.1 |
602 | + |
603 | + |
604 | +ListView { |
605 | + Component.onCompleted: { |
606 | + // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition |
607 | + // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration |
608 | + var scaleFactor = units.gridUnit / 8; |
609 | + maximumFlickVelocity = maximumFlickVelocity * scaleFactor; |
610 | + flickDeceleration = flickDeceleration * scaleFactor; |
611 | + } |
612 | +} |
613 | |
614 | === added directory 'app/components/HeadState' |
615 | === added file 'app/components/HeadState/CMakeLists.txt' |
616 | --- app/components/HeadState/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
617 | +++ app/components/HeadState/CMakeLists.txt 2015-02-10 03:13:44 +0000 |
618 | @@ -0,0 +1,4 @@ |
619 | +# make the qml files visible on qtcreator |
620 | +file(GLOB HEAD_STATE_QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml) |
621 | + |
622 | +add_custom_target(com_ubuntu_music_HEAD_STATE_QMLFiles ALL SOURCES ${HEAD_STATE_QML_FILES}) |
623 | |
624 | === added file 'app/components/HeadState/MultiSelectHeadState.qml' |
625 | --- app/components/HeadState/MultiSelectHeadState.qml 1970-01-01 00:00:00 +0000 |
626 | +++ app/components/HeadState/MultiSelectHeadState.qml 2015-02-10 03:13:44 +0000 |
627 | @@ -0,0 +1,108 @@ |
628 | +/* |
629 | + * Copyright (C) 2015 |
630 | + * Andrew Hayzen <ahayzen@gmail.com> |
631 | + * Victor Thompson <victor.thompson@gmail.com> |
632 | + * |
633 | + * This program is free software; you can redistribute it and/or modify |
634 | + * it under the terms of the GNU General Public License as published by |
635 | + * the Free Software Foundation; version 3. |
636 | + * |
637 | + * This program is distributed in the hope that it will be useful, |
638 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
639 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
640 | + * GNU General Public License for more details. |
641 | + * |
642 | + * You should have received a copy of the GNU General Public License |
643 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
644 | + */ |
645 | + |
646 | +import QtQuick 2.3 |
647 | +import Ubuntu.Components 1.1 |
648 | + |
649 | +PageHeadState { |
650 | + id: selectionState |
651 | + actions: [ |
652 | + Action { |
653 | + iconName: "select" |
654 | + text: i18n.tr("Select All") |
655 | + onTriggered: { |
656 | + if (listview.selectedItems.length === listview.model.count) { |
657 | + listview.clearSelection() |
658 | + } else { |
659 | + listview.selectAll() |
660 | + } |
661 | + } |
662 | + }, |
663 | + Action { |
664 | + enabled: listview.selectedItems.length !== 0 |
665 | + iconName: "add-to-playlist" |
666 | + text: i18n.tr("Add to playlist") |
667 | + onTriggered: { |
668 | + var items = [] |
669 | + |
670 | + for (var i=0; i < listview.selectedItems.length; i++) { |
671 | + items.push(makeDict(listview.model.get(listview.selectedItems[i], listview.model.RoleModelData))); |
672 | + } |
673 | + |
674 | + mainPageStack.push(Qt.resolvedUrl("../../ui/AddToPlaylist.qml"), |
675 | + {"chosenElements": items}) |
676 | + |
677 | + listview.closeSelection() |
678 | + } |
679 | + }, |
680 | + Action { |
681 | + enabled: listview.selectedItems.length > 0 |
682 | + iconName: "add" |
683 | + text: i18n.tr("Add to queue") |
684 | + visible: addToQueue |
685 | + |
686 | + onTriggered: { |
687 | + var items = [] |
688 | + |
689 | + for (var i=0; i < listview.selectedItems.length; i++) { |
690 | + items.push(listview.model.get(listview.selectedItems[i], listview.model.RoleModelData)); |
691 | + } |
692 | + |
693 | + trackQueue.appendList(items) |
694 | + |
695 | + listview.closeSelection() |
696 | + } |
697 | + }, |
698 | + Action { |
699 | + enabled: listview.selectedItems.length > 0 |
700 | + iconName: "delete" |
701 | + text: i18n.tr("Delete") |
702 | + visible: removable |
703 | + |
704 | + onTriggered: { |
705 | + removed(listview.selectedItems) |
706 | + |
707 | + listview.closeSelection() |
708 | + } |
709 | + } |
710 | + |
711 | + ] |
712 | + backAction: Action { |
713 | + text: i18n.tr("Cancel selection") |
714 | + iconName: "back" |
715 | + onTriggered: { |
716 | + listview.clearSelection() |
717 | + listview.state = "normal" |
718 | + } |
719 | + } |
720 | + head: thisPage.head |
721 | + name: "selection" |
722 | + |
723 | + PropertyChanges { |
724 | + target: thisPage.head |
725 | + backAction: selectionState.backAction |
726 | + actions: selectionState.actions |
727 | + } |
728 | + |
729 | + property bool addToQueue: true |
730 | + property ListView listview |
731 | + property bool removable: false |
732 | + property Page thisPage |
733 | + |
734 | + signal removed(var selectedItems) |
735 | +} |
736 | |
737 | === added file 'app/components/HeadState/PlaylistsHeadState.qml' |
738 | --- app/components/HeadState/PlaylistsHeadState.qml 1970-01-01 00:00:00 +0000 |
739 | +++ app/components/HeadState/PlaylistsHeadState.qml 2015-02-10 03:13:44 +0000 |
740 | @@ -0,0 +1,47 @@ |
741 | +/* |
742 | + * Copyright (C) 2015 |
743 | + * Andrew Hayzen <ahayzen@gmail.com> |
744 | + * Victor Thompson <victor.thompson@gmail.com> |
745 | + * |
746 | + * This program is free software; you can redistribute it and/or modify |
747 | + * it under the terms of the GNU General Public License as published by |
748 | + * the Free Software Foundation; version 3. |
749 | + * |
750 | + * This program is distributed in the hope that it will be useful, |
751 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
752 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
753 | + * GNU General Public License for more details. |
754 | + * |
755 | + * You should have received a copy of the GNU General Public License |
756 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
757 | + */ |
758 | + |
759 | +import QtQuick 2.3 |
760 | +import Ubuntu.Components 1.1 |
761 | +import Ubuntu.Components.Popups 1.0 |
762 | + |
763 | + |
764 | +PageHeadState { |
765 | + name: "default" |
766 | + head: thisPage.head |
767 | + actions: [ |
768 | + Action { |
769 | + id: newPlaylistAction |
770 | + objectName: "newPlaylistButton" |
771 | + iconName: "add" |
772 | + onTriggered: { |
773 | + customdebug("New playlist.") |
774 | + PopupUtils.open(Qt.resolvedUrl("../Dialog/NewPlaylistDialog.qml"), mainView) |
775 | + } |
776 | + }, |
777 | + Action { |
778 | + id: searchAction |
779 | + iconName: "search" |
780 | + onTriggered: thisPage.state = "search" |
781 | + } |
782 | + ] |
783 | + |
784 | + property alias newPlaylistEnabled: newPlaylistAction.enabled |
785 | + property alias searchEnabled: searchAction.enabled |
786 | + property Page thisPage |
787 | +} |
788 | |
789 | === renamed file 'common/SearchHeadState.qml' => 'app/components/HeadState/SearchHeadState.qml' |
790 | === added file 'app/components/HeadState/SearchableHeadState.qml' |
791 | --- app/components/HeadState/SearchableHeadState.qml 1970-01-01 00:00:00 +0000 |
792 | +++ app/components/HeadState/SearchableHeadState.qml 2015-02-10 03:13:44 +0000 |
793 | @@ -0,0 +1,34 @@ |
794 | +/* |
795 | + * Copyright (C) 2015 |
796 | + * Andrew Hayzen <ahayzen@gmail.com> |
797 | + * Victor Thompson <victor.thompson@gmail.com> |
798 | + * |
799 | + * This program is free software; you can redistribute it and/or modify |
800 | + * it under the terms of the GNU General Public License as published by |
801 | + * the Free Software Foundation; version 3. |
802 | + * |
803 | + * This program is distributed in the hope that it will be useful, |
804 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
805 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
806 | + * GNU General Public License for more details. |
807 | + * |
808 | + * You should have received a copy of the GNU General Public License |
809 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
810 | + */ |
811 | + |
812 | +import QtQuick 2.3 |
813 | +import Ubuntu.Components 1.1 |
814 | + |
815 | + |
816 | +PageHeadState { |
817 | + name: "default" |
818 | + head: thisPage.head |
819 | + actions: Action { |
820 | + id: searchAction |
821 | + iconName: "search" |
822 | + onTriggered: thisPage.state = "search" |
823 | + } |
824 | + |
825 | + property alias searchEnabled: searchAction.enabled |
826 | + property Page thisPage |
827 | +} |
828 | |
829 | === renamed file 'LibraryListModel.qml' => 'app/components/LibraryListModel.qml' |
830 | --- LibraryListModel.qml 2015-01-20 23:58:27 +0000 |
831 | +++ app/components/LibraryListModel.qml 2015-02-10 03:13:44 +0000 |
832 | @@ -19,8 +19,8 @@ |
833 | |
834 | import QtQuick 2.3 |
835 | import QtQuick.LocalStorage 2.0 |
836 | -import "meta-database.js" as Library |
837 | -import "playlists.js" as Playlists |
838 | +import "../logic/meta-database.js" as Library |
839 | +import "../logic/playlists.js" as Playlists |
840 | |
841 | Item { |
842 | id: libraryListModelItem |
843 | |
844 | === modified file 'app/components/ListItemActions/AddToPlaylist.qml' |
845 | --- common/ListItemActions/AddToPlaylist.qml 2015-01-11 15:54:04 +0000 |
846 | +++ app/components/ListItemActions/AddToPlaylist.qml 2015-02-10 03:13:44 +0000 |
847 | @@ -30,13 +30,7 @@ |
848 | onTriggered: { |
849 | console.debug("Debug: Add track to playlist"); |
850 | |
851 | - var comp = Qt.createComponent("../../MusicaddtoPlaylist.qml") |
852 | - var addToPlaylist = comp.createObject(mainPageStack, {"chosenElements": [makeDict(model)]}); |
853 | - |
854 | - if (addToPlaylist == null) { // Error Handling |
855 | - console.log("Error creating object"); |
856 | - } |
857 | - |
858 | - mainPageStack.push(addToPlaylist) |
859 | + mainPageStack.push(Qt.resolvedUrl("../../ui/AddToPlaylist.qml"), |
860 | + {"chosenElements": [makeDict(model)]}) |
861 | } |
862 | } |
863 | |
864 | === modified file 'app/components/ListItemActions/AddToQueue.qml' |
865 | --- common/ListItemActions/AddToQueue.qml 2014-10-28 00:00:26 +0000 |
866 | +++ app/components/ListItemActions/AddToQueue.qml 2015-02-10 03:13:44 +0000 |
867 | @@ -19,7 +19,7 @@ |
868 | import QtQuick 2.3 |
869 | import QtQuick.LocalStorage 2.0 |
870 | import Ubuntu.Components 1.1 |
871 | -import "../../meta-database.js" as Library |
872 | +import "../../logic/meta-database.js" as Library |
873 | |
874 | Action { |
875 | iconName: "add" |
876 | |
877 | === modified file 'app/components/MusicPage.qml' |
878 | --- common/MusicPage.qml 2015-01-10 19:22:08 +0000 |
879 | +++ app/components/MusicPage.qml 2015-02-10 03:13:44 +0000 |
880 | @@ -25,7 +25,7 @@ |
881 | Page { |
882 | id: thisPage |
883 | anchors { |
884 | - bottomMargin: musicToolbar.visible ? musicToolbar.currentHeight : 0 |
885 | + bottomMargin: musicToolbar.visible ? musicToolbar.height : 0 |
886 | fill: parent |
887 | } |
888 | |
889 | @@ -56,7 +56,7 @@ |
890 | |
891 | onVisibleChanged: { |
892 | if (visible) { |
893 | - musicToolbar.setPage(thisPage); |
894 | + mainPageStack.setPage(thisPage); |
895 | } |
896 | } |
897 | } |
898 | |
899 | === modified file 'app/components/MusicRow.qml' |
900 | --- common/MusicRow.qml 2014-10-23 17:40:04 +0000 |
901 | +++ app/components/MusicRow.qml 2015-02-10 03:13:44 +0000 |
902 | @@ -55,7 +55,7 @@ |
903 | |
904 | onStatusChanged: { |
905 | if (status === Image.Error) { |
906 | - source = Qt.resolvedUrl("../images/music-app-cover@30.png") |
907 | + source = Qt.resolvedUrl("../graphics/music-app-cover@30.png") |
908 | } |
909 | } |
910 | visible: imageSource !== undefined |
911 | |
912 | === renamed file 'MusicToolbar.qml' => 'app/components/MusicToolbar.qml' |
913 | --- MusicToolbar.qml 2015-01-23 07:22:52 +0000 |
914 | +++ app/components/MusicToolbar.qml 2015-02-10 03:13:44 +0000 |
915 | @@ -18,335 +18,265 @@ |
916 | */ |
917 | |
918 | import QtQuick 2.3 |
919 | -import QtQuick.LocalStorage 2.0 |
920 | import QtMultimedia 5.0 |
921 | import Ubuntu.Components 1.1 |
922 | -import Ubuntu.Components.Popups 1.0 |
923 | -import "common" |
924 | |
925 | -Item { |
926 | +Rectangle { |
927 | anchors { |
928 | bottom: parent.bottom |
929 | left: parent.left |
930 | right: parent.right |
931 | } |
932 | - |
933 | - // Properties storing the current page info |
934 | - property var currentPage: null |
935 | - property var currentTab: null |
936 | - |
937 | - // Properties and signals for the toolbar |
938 | - property alias currentHeight: musicToolbarPanel.height |
939 | - property alias opened: musicToolbarPanel.opened |
940 | - |
941 | - property bool popping: false |
942 | - |
943 | - /* Helper functions */ |
944 | - |
945 | - // Back button has been pressed, jump up pageStack or back to parent page |
946 | - function goBack() |
947 | - { |
948 | - if (mainPageStack !== null && mainPageStack.depth > 1) { |
949 | - mainPageStack.pop(currentPage) |
950 | - } |
951 | - } |
952 | - |
953 | - // Pop a specific page in the stack |
954 | - function popPage(page) |
955 | - { |
956 | - var tmpPages = [] |
957 | - |
958 | - popping = true |
959 | - |
960 | - while (mainPageStack.currentPage !== page && mainPageStack.depth > 0) { |
961 | - tmpPages.push(mainPageStack.currentPage) |
962 | - mainPageStack.pop() |
963 | - } |
964 | - |
965 | - if (mainPageStack.depth > 0) { |
966 | - mainPageStack.pop() |
967 | - } |
968 | - |
969 | - for (var i=tmpPages.length - 1; i > -1; i--) { |
970 | - mainPageStack.push(tmpPages[i]) |
971 | - } |
972 | - |
973 | - popping = false |
974 | - } |
975 | - |
976 | - // Set the current page, and any parent/stacks |
977 | - function setPage(childPage) |
978 | - { |
979 | - if (!popping) { |
980 | - currentPage = childPage; |
981 | - } |
982 | - } |
983 | - |
984 | - Panel { |
985 | - id: musicToolbarPanel |
986 | + color: styleMusic.common.black |
987 | + height: units.gu(7.25) |
988 | + objectName: "musicToolbarObject" |
989 | + |
990 | + // Hack for autopilot otherwise MusicToolbar appears as QQuickRectangle |
991 | + // due to bug 1341671 it is required that there is a property so that |
992 | + // qml doesn't optimise using the parent type |
993 | + property bool bug1341671workaround: true |
994 | + |
995 | + /* Toolbar controls */ |
996 | + Item { |
997 | + id: toolbarControls |
998 | anchors { |
999 | - left: parent.left |
1000 | - right: parent.right |
1001 | - bottom: parent.bottom |
1002 | - } |
1003 | - height: units.gu(7.25) |
1004 | - locked: true |
1005 | - opened: true |
1006 | - |
1007 | - /* Expanded toolbar */ |
1008 | - Item { |
1009 | - id: musicToolbarExpandedContainer |
1010 | - anchors { |
1011 | - fill: parent |
1012 | - } |
1013 | + fill: parent |
1014 | + } |
1015 | + state: trackQueue.model.count === 0 ? "disabled" : "enabled" |
1016 | + states: [ |
1017 | + State { |
1018 | + name: "disabled" |
1019 | + PropertyChanges { |
1020 | + target: disabledPlayerControlsGroup |
1021 | + visible: true |
1022 | + } |
1023 | + PropertyChanges { |
1024 | + target: enabledPlayerControlsGroup |
1025 | + visible: false |
1026 | + } |
1027 | + }, |
1028 | + State { |
1029 | + name: "enabled" |
1030 | + PropertyChanges { |
1031 | + target: disabledPlayerControlsGroup |
1032 | + visible: false |
1033 | + } |
1034 | + PropertyChanges { |
1035 | + target: enabledPlayerControlsGroup |
1036 | + visible: true |
1037 | + } |
1038 | + } |
1039 | + ] |
1040 | + |
1041 | + /* Disabled (empty state) controls */ |
1042 | + Item { |
1043 | + id: disabledPlayerControlsGroup |
1044 | + anchors { |
1045 | + bottom: playerControlsProgressBar.top |
1046 | + left: parent.left |
1047 | + right: parent.right |
1048 | + top: parent.top |
1049 | + } |
1050 | + |
1051 | + Label { |
1052 | + id: noSongsInQueueLabel |
1053 | + anchors { |
1054 | + left: parent.left |
1055 | + leftMargin: units.gu(2) |
1056 | + right: disabledPlayerControlsPlayButton.left |
1057 | + rightMargin: units.gu(2) |
1058 | + verticalCenter: parent.verticalCenter |
1059 | + } |
1060 | + color: styleMusic.playerControls.labelColor |
1061 | + text: i18n.tr("Tap to shuffle music") |
1062 | + fontSize: "large" |
1063 | + visible: !emptyPageLoader.noMusic |
1064 | + wrapMode: Text.WordWrap |
1065 | + maximumLineCount: 2 |
1066 | + } |
1067 | + |
1068 | + /* Play/Pause button */ |
1069 | + Icon { |
1070 | + id: disabledPlayerControlsPlayButton |
1071 | + anchors { |
1072 | + right: parent.right |
1073 | + rightMargin: units.gu(3) |
1074 | + verticalCenter: parent.verticalCenter |
1075 | + } |
1076 | + color: "#FFF" |
1077 | + height: units.gu(2.5) |
1078 | + name: player.playbackState === MediaPlayer.PlayingState ? |
1079 | + "media-playback-pause" : "media-playback-start" |
1080 | + objectName: "disabledSmallPlayShape" |
1081 | + width: height |
1082 | + } |
1083 | + |
1084 | + /* Click to shuffle music */ |
1085 | + MouseArea { |
1086 | + anchors { |
1087 | + fill: parent |
1088 | + } |
1089 | + onClicked: { |
1090 | + if (emptyPageLoader.noMusic) { |
1091 | + return; |
1092 | + } |
1093 | + |
1094 | + if (trackQueue.model.count === 0) { |
1095 | + playRandomSong(); |
1096 | + } |
1097 | + else { |
1098 | + player.toggle(); |
1099 | + } |
1100 | + } |
1101 | + } |
1102 | + } |
1103 | + |
1104 | + /* Enabled (queue > 0) controls */ |
1105 | + Item { |
1106 | + id: enabledPlayerControlsGroup |
1107 | + anchors { |
1108 | + bottom: playerControlsProgressBar.top |
1109 | + left: parent.left |
1110 | + right: parent.right |
1111 | + top: parent.top |
1112 | + } |
1113 | + |
1114 | + /* Album art in player controls */ |
1115 | + CoverGrid { |
1116 | + id: playerControlsImage |
1117 | + anchors { |
1118 | + bottom: parent.bottom |
1119 | + left: parent.left |
1120 | + top: parent.top |
1121 | + } |
1122 | + covers: [{art: player.currentMetaArt, author: player.currentMetaArtist, album: player.currentMetaArt}] |
1123 | + size: parent.height |
1124 | + } |
1125 | + |
1126 | + /* Column of meta labels */ |
1127 | + Column { |
1128 | + id: playerControlsLabels |
1129 | + anchors { |
1130 | + left: playerControlsImage.right |
1131 | + leftMargin: units.gu(1.5) |
1132 | + right: playerControlsPlayButton.left |
1133 | + rightMargin: units.gu(1) |
1134 | + verticalCenter: parent.verticalCenter |
1135 | + } |
1136 | + |
1137 | + /* Title of track */ |
1138 | + Label { |
1139 | + id: playerControlsTitle |
1140 | + anchors { |
1141 | + left: parent.left |
1142 | + right: parent.right |
1143 | + } |
1144 | + color: "#FFF" |
1145 | + elide: Text.ElideRight |
1146 | + fontSize: "small" |
1147 | + font.weight: Font.DemiBold |
1148 | + text: player.currentMetaTitle === "" |
1149 | + ? player.source : player.currentMetaTitle |
1150 | + } |
1151 | + |
1152 | + /* Artist of track */ |
1153 | + Label { |
1154 | + id: playerControlsArtist |
1155 | + anchors { |
1156 | + left: parent.left |
1157 | + right: parent.right |
1158 | + } |
1159 | + color: "#FFF" |
1160 | + elide: Text.ElideRight |
1161 | + fontSize: "small" |
1162 | + opacity: 0.4 |
1163 | + text: player.currentMetaArtist |
1164 | + } |
1165 | + } |
1166 | + |
1167 | + /* Play/Pause button */ |
1168 | + Icon { |
1169 | + id: playerControlsPlayButton |
1170 | + anchors { |
1171 | + right: parent.right |
1172 | + rightMargin: units.gu(3) |
1173 | + verticalCenter: parent.verticalCenter |
1174 | + } |
1175 | + color: "#FFF" |
1176 | + height: units.gu(2.5) |
1177 | + name: player.playbackState === MediaPlayer.PlayingState ? |
1178 | + "media-playback-pause" : "media-playback-start" |
1179 | + objectName: "playShape" |
1180 | + width: height |
1181 | + } |
1182 | + |
1183 | + /* Mouse area to jump to now playing */ |
1184 | + MouseArea { |
1185 | + anchors { |
1186 | + fill: parent |
1187 | + } |
1188 | + objectName: "jumpNowPlaying" |
1189 | + |
1190 | + onClicked: tabs.pushNowPlaying() |
1191 | + } |
1192 | + |
1193 | + /* Mouse area for the play button (ontop of the jump to now playing) */ |
1194 | + MouseArea { |
1195 | + anchors { |
1196 | + bottom: parent.bottom |
1197 | + horizontalCenter: playerControlsPlayButton.horizontalCenter |
1198 | + top: parent.top |
1199 | + } |
1200 | + onClicked: player.toggle() |
1201 | + width: units.gu(8) |
1202 | + |
1203 | + Rectangle { |
1204 | + anchors { |
1205 | + fill: parent |
1206 | + } |
1207 | + color: "#FFF" |
1208 | + opacity: parent.pressed ? 0.1 : 0 |
1209 | + |
1210 | + Behavior on opacity { |
1211 | + UbuntuNumberAnimation { |
1212 | + duration: UbuntuAnimation.FastDuration |
1213 | + } |
1214 | + } |
1215 | + } |
1216 | + } |
1217 | + } |
1218 | + |
1219 | + /* Object which provides the progress bar when toolbar is minimized */ |
1220 | + Rectangle { |
1221 | + id: playerControlsProgressBar |
1222 | + anchors { |
1223 | + bottom: parent.bottom |
1224 | + left: parent.left |
1225 | + right: parent.right |
1226 | + } |
1227 | + color: styleMusic.common.black |
1228 | + height: units.gu(0.25) |
1229 | |
1230 | Rectangle { |
1231 | - id: musicToolbarPlayerControls |
1232 | + id: playerControlsProgressBarHint |
1233 | anchors { |
1234 | - fill: parent |
1235 | - } |
1236 | - color: "#000" |
1237 | - state: trackQueue.model.count === 0 ? "disabled" : "enabled" |
1238 | - states: [ |
1239 | - State { |
1240 | - name: "disabled" |
1241 | - PropertyChanges { |
1242 | - target: disabledPlayerControlsGroup |
1243 | - visible: true |
1244 | - } |
1245 | - PropertyChanges { |
1246 | - target: enabledPlayerControlsGroup |
1247 | - visible: false |
1248 | - } |
1249 | - }, |
1250 | - State { |
1251 | - name: "enabled" |
1252 | - PropertyChanges { |
1253 | - target: disabledPlayerControlsGroup |
1254 | - visible: false |
1255 | - } |
1256 | - PropertyChanges { |
1257 | - target: enabledPlayerControlsGroup |
1258 | - visible: true |
1259 | - } |
1260 | - } |
1261 | - ] |
1262 | - |
1263 | - /* Disabled (empty state) controls */ |
1264 | - Item { |
1265 | - id: disabledPlayerControlsGroup |
1266 | - anchors { |
1267 | - bottom: playerControlsProgressBar.top |
1268 | - left: parent.left |
1269 | - right: parent.right |
1270 | - top: parent.top |
1271 | - } |
1272 | - |
1273 | - Label { |
1274 | - id: noSongsInQueueLabel |
1275 | - anchors { |
1276 | - left: parent.left |
1277 | - leftMargin: units.gu(2) |
1278 | - right: disabledPlayerControlsPlayButton.left |
1279 | - rightMargin: units.gu(2) |
1280 | - verticalCenter: parent.verticalCenter |
1281 | - } |
1282 | - color: styleMusic.playerControls.labelColor |
1283 | - text: i18n.tr("Tap to shuffle music") |
1284 | - fontSize: "large" |
1285 | - visible: !emptyPage.noMusic |
1286 | - wrapMode: Text.WordWrap |
1287 | - maximumLineCount: 2 |
1288 | - } |
1289 | - |
1290 | - /* Play/Pause button */ |
1291 | - Icon { |
1292 | - id: disabledPlayerControlsPlayButton |
1293 | - anchors { |
1294 | - right: parent.right |
1295 | - rightMargin: units.gu(3) |
1296 | - verticalCenter: parent.verticalCenter |
1297 | - } |
1298 | - color: "#FFF" |
1299 | - height: units.gu(2.5) |
1300 | - name: player.playbackState === MediaPlayer.PlayingState ? |
1301 | - "media-playback-pause" : "media-playback-start" |
1302 | - objectName: "disabledSmallPlayShape" |
1303 | - width: height |
1304 | - } |
1305 | - |
1306 | - /* Click to shuffle music */ |
1307 | - MouseArea { |
1308 | - anchors { |
1309 | - fill: parent |
1310 | - } |
1311 | - onClicked: { |
1312 | - if (emptyPage.noMusic) { |
1313 | - return; |
1314 | - } |
1315 | - |
1316 | - if (trackQueue.model.count === 0) { |
1317 | - playRandomSong(); |
1318 | - } |
1319 | - else { |
1320 | - player.toggle(); |
1321 | - } |
1322 | - } |
1323 | - } |
1324 | - } |
1325 | - |
1326 | - /* Enabled (queue > 0) controls */ |
1327 | - Item { |
1328 | - id: enabledPlayerControlsGroup |
1329 | - anchors { |
1330 | - bottom: playerControlsProgressBar.top |
1331 | - left: parent.left |
1332 | - right: parent.right |
1333 | - top: parent.top |
1334 | - } |
1335 | - |
1336 | - /* Album art in player controls */ |
1337 | - CoverGrid { |
1338 | - id: playerControlsImage |
1339 | - anchors { |
1340 | - bottom: parent.bottom |
1341 | - left: parent.left |
1342 | - top: parent.top |
1343 | - } |
1344 | - covers: [{art: player.currentMetaArt, author: player.currentMetaArtist, album: player.currentMetaArt}] |
1345 | - size: parent.height |
1346 | - } |
1347 | - |
1348 | - /* Column of meta labels */ |
1349 | - Column { |
1350 | - id: playerControlsLabels |
1351 | - anchors { |
1352 | - left: playerControlsImage.right |
1353 | - leftMargin: units.gu(1.5) |
1354 | - right: playerControlsPlayButton.left |
1355 | - rightMargin: units.gu(1) |
1356 | - verticalCenter: parent.verticalCenter |
1357 | - } |
1358 | - |
1359 | - /* Title of track */ |
1360 | - Label { |
1361 | - id: playerControlsTitle |
1362 | - anchors { |
1363 | - left: parent.left |
1364 | - right: parent.right |
1365 | - } |
1366 | - color: "#FFF" |
1367 | - elide: Text.ElideRight |
1368 | - fontSize: "small" |
1369 | - font.weight: Font.DemiBold |
1370 | - text: player.currentMetaTitle === "" |
1371 | - ? player.source : player.currentMetaTitle |
1372 | - } |
1373 | - |
1374 | - /* Artist of track */ |
1375 | - Label { |
1376 | - id: playerControlsArtist |
1377 | - anchors { |
1378 | - left: parent.left |
1379 | - right: parent.right |
1380 | - } |
1381 | - color: "#FFF" |
1382 | - elide: Text.ElideRight |
1383 | - fontSize: "small" |
1384 | - opacity: 0.4 |
1385 | - text: player.currentMetaArtist |
1386 | - } |
1387 | - } |
1388 | - |
1389 | - /* Play/Pause button */ |
1390 | - Icon { |
1391 | - id: playerControlsPlayButton |
1392 | - anchors { |
1393 | - right: parent.right |
1394 | - rightMargin: units.gu(3) |
1395 | - verticalCenter: parent.verticalCenter |
1396 | - } |
1397 | - color: "#FFF" |
1398 | - height: units.gu(2.5) |
1399 | - name: player.playbackState === MediaPlayer.PlayingState ? |
1400 | - "media-playback-pause" : "media-playback-start" |
1401 | - objectName: "playShape" |
1402 | - width: height |
1403 | - } |
1404 | - |
1405 | - MouseArea { |
1406 | - anchors { |
1407 | - bottom: parent.bottom |
1408 | - horizontalCenter: playerControlsPlayButton.horizontalCenter |
1409 | - top: parent.top |
1410 | - } |
1411 | - onClicked: player.toggle() |
1412 | - width: units.gu(8) |
1413 | - |
1414 | - Rectangle { |
1415 | - anchors { |
1416 | - fill: parent |
1417 | - } |
1418 | - color: "#FFF" |
1419 | - opacity: parent.pressed ? 0.1 : 0 |
1420 | - |
1421 | - Behavior on opacity { |
1422 | - UbuntuNumberAnimation { |
1423 | - duration: UbuntuAnimation.FastDuration |
1424 | - } |
1425 | - } |
1426 | - } |
1427 | - } |
1428 | - |
1429 | - /* Mouse area to jump to now playing */ |
1430 | - Item { |
1431 | - anchors { |
1432 | - bottom: parent.bottom |
1433 | - left: parent.left |
1434 | - right: playerControlsLabels.right |
1435 | - top: parent.top |
1436 | - } |
1437 | - objectName: "jumpNowPlaying" |
1438 | - function trigger() { |
1439 | - tabs.pushNowPlaying(); |
1440 | - } |
1441 | - } |
1442 | - } |
1443 | - |
1444 | - /* Object which provides the progress bar when toolbar is minimized */ |
1445 | - Rectangle { |
1446 | - id: playerControlsProgressBar |
1447 | - anchors { |
1448 | - bottom: parent.bottom |
1449 | - left: parent.left |
1450 | - right: parent.right |
1451 | - } |
1452 | - color: styleMusic.common.black |
1453 | - height: units.gu(0.25) |
1454 | - |
1455 | - Rectangle { |
1456 | - id: playerControlsProgressBarHint |
1457 | - anchors { |
1458 | - left: parent.left |
1459 | - top: parent.top |
1460 | - } |
1461 | - color: UbuntuColors.blue |
1462 | - height: parent.height |
1463 | - width: player.duration > 0 ? (player.position / player.duration) * playerControlsProgressBar.width : 0 |
1464 | - |
1465 | - Connections { |
1466 | - target: player |
1467 | - onPositionChanged: { |
1468 | - playerControlsProgressBarHint.width = (player.position / player.duration) * playerControlsProgressBar.width |
1469 | - } |
1470 | - onStopped: { |
1471 | - playerControlsProgressBarHint.width = 0; |
1472 | - } |
1473 | - } |
1474 | + left: parent.left |
1475 | + top: parent.top |
1476 | + } |
1477 | + color: UbuntuColors.blue |
1478 | + height: parent.height |
1479 | + width: player.duration > 0 ? (player.position / player.duration) * playerControlsProgressBar.width : 0 |
1480 | + |
1481 | + Connections { |
1482 | + target: player |
1483 | + onPositionChanged: { |
1484 | + playerControlsProgressBarHint.width = (player.position / player.duration) * playerControlsProgressBar.width |
1485 | + } |
1486 | + onStopped: { |
1487 | + playerControlsProgressBarHint.width = 0; |
1488 | } |
1489 | } |
1490 | } |
1491 | } |
1492 | } |
1493 | } |
1494 | - |
1495 | |
1496 | === added file 'app/components/NowPlaying.qml' |
1497 | --- app/components/NowPlaying.qml 1970-01-01 00:00:00 +0000 |
1498 | +++ app/components/NowPlaying.qml 2015-02-10 03:13:44 +0000 |
1499 | @@ -0,0 +1,550 @@ |
1500 | +/* |
1501 | + * Copyright (C) 2013, 2014, 2015 |
1502 | + * Andrew Hayzen <ahayzen@gmail.com> |
1503 | + * Daniel Holm <d.holmen@gmail.com> |
1504 | + * Victor Thompson <victor.thompson@gmail.com> |
1505 | + * |
1506 | + * This program is free software; you can redistribute it and/or modify |
1507 | + * it under the terms of the GNU General Public License as published by |
1508 | + * the Free Software Foundation; version 3. |
1509 | + * |
1510 | + * This program is distributed in the hope that it will be useful, |
1511 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1512 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1513 | + * GNU General Public License for more details. |
1514 | + * |
1515 | + * You should have received a copy of the GNU General Public License |
1516 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1517 | + */ |
1518 | + |
1519 | +import QtMultimedia 5.0 |
1520 | +import QtQuick 2.3 |
1521 | +import QtQuick.LocalStorage 2.0 |
1522 | +import Ubuntu.Components 1.1 |
1523 | +import Ubuntu.Thumbnailer 0.1 |
1524 | +import "../components" |
1525 | +import "../components/Flickables" |
1526 | +import "../components/HeadState" |
1527 | +import "../components/ListItemActions" |
1528 | +import "../components/Themes/Ambiance" |
1529 | +import "../logic/meta-database.js" as Library |
1530 | +import "../logic/playlists.js" as Playlists |
1531 | + |
1532 | +Item { |
1533 | + id: nowPlayingView |
1534 | + objectName: "nowPlayingView" |
1535 | + |
1536 | + anchors { |
1537 | + fill: parent |
1538 | + } |
1539 | + |
1540 | + property bool isListView: false |
1541 | + |
1542 | + Item { |
1543 | + id: fullview |
1544 | + anchors { |
1545 | + top: parent.top |
1546 | + topMargin: mainView.header.height |
1547 | + } |
1548 | + height: parent.height - mainView.header.height - units.gu(9.5) |
1549 | + visible: !isListView || wideAspect |
1550 | + width: parent.width |
1551 | + |
1552 | + BlurredBackground { |
1553 | + id: blurredBackground |
1554 | + anchors { |
1555 | + left: parent.left |
1556 | + right: parent.right |
1557 | + top: parent.top |
1558 | + } |
1559 | + art: albumImage.firstSource |
1560 | + height: parent.height - units.gu(7) |
1561 | + |
1562 | + Item { |
1563 | + id: albumImageContainer |
1564 | + anchors { |
1565 | + horizontalCenter: parent.horizontalCenter |
1566 | + top: parent.top |
1567 | + } |
1568 | + height: parent.height |
1569 | + width: parent.width |
1570 | + |
1571 | + CoverGrid { |
1572 | + id: albumImage |
1573 | + anchors.centerIn: parent |
1574 | + covers: [{art: player.currentMetaArt, author: player.currentMetaArtist, album: player.currentMetaAlbum}] |
1575 | + size: parent.width > parent.height ? parent.height : parent.width |
1576 | + } |
1577 | + } |
1578 | + |
1579 | + Rectangle { |
1580 | + id: nowPlayingWideAspectLabelsBackground |
1581 | + anchors.bottom: parent.bottom |
1582 | + color: styleMusic.common.black |
1583 | + height: nowPlayingWideAspectTitle.lineCount === 1 ? units.gu(10) : units.gu(13) |
1584 | + opacity: 0.8 |
1585 | + width: parent.width |
1586 | + } |
1587 | + |
1588 | + /* Column for labels in wideAspect */ |
1589 | + Column { |
1590 | + id: nowPlayingWideAspectLabels |
1591 | + spacing: units.gu(1) |
1592 | + anchors { |
1593 | + left: parent.left |
1594 | + leftMargin: units.gu(2) |
1595 | + right: parent.right |
1596 | + rightMargin: units.gu(2) |
1597 | + top: nowPlayingWideAspectLabelsBackground.top |
1598 | + topMargin: nowPlayingWideAspectTitle.lineCount === 1 ? units.gu(2) : units.gu(1.5) |
1599 | + } |
1600 | + |
1601 | + /* Title of track */ |
1602 | + Label { |
1603 | + id: nowPlayingWideAspectTitle |
1604 | + anchors { |
1605 | + left: parent.left |
1606 | + leftMargin: units.gu(1) |
1607 | + right: parent.right |
1608 | + rightMargin: units.gu(1) |
1609 | + } |
1610 | + color: styleMusic.playerControls.labelColor |
1611 | + elide: Text.ElideRight |
1612 | + fontSize: "x-large" |
1613 | + maximumLineCount: 2 |
1614 | + objectName: "playercontroltitle" |
1615 | + text: trackQueue.model.count === 0 ? "" : player.currentMetaTitle === "" ? player.currentMetaFile : player.currentMetaTitle |
1616 | + wrapMode: Text.WordWrap |
1617 | + } |
1618 | + |
1619 | + /* Artist of track */ |
1620 | + Label { |
1621 | + id: nowPlayingWideAspectArtist |
1622 | + anchors { |
1623 | + left: parent.left |
1624 | + leftMargin: units.gu(1) |
1625 | + right: parent.right |
1626 | + rightMargin: units.gu(1) |
1627 | + } |
1628 | + color: styleMusic.nowPlaying.labelSecondaryColor |
1629 | + elide: Text.ElideRight |
1630 | + fontSize: "small" |
1631 | + text: trackQueue.model.count === 0 ? "" : player.currentMetaArtist |
1632 | + } |
1633 | + } |
1634 | + |
1635 | + /* Detect cover art swipe */ |
1636 | + MouseArea { |
1637 | + anchors.fill: parent |
1638 | + property string direction: "None" |
1639 | + property real lastX: -1 |
1640 | + |
1641 | + onPressed: lastX = mouse.x |
1642 | + |
1643 | + onReleased: { |
1644 | + var diff = mouse.x - lastX |
1645 | + if (Math.abs(diff) < units.gu(4)) { |
1646 | + return; |
1647 | + } else if (diff < 0) { |
1648 | + player.nextSong() |
1649 | + } else if (diff > 0) { |
1650 | + player.previousSong() |
1651 | + } |
1652 | + } |
1653 | + } |
1654 | + } |
1655 | + |
1656 | + /* Background for progress bar component */ |
1657 | + Rectangle { |
1658 | + id: musicToolbarFullProgressBackground |
1659 | + anchors { |
1660 | + bottom: parent.bottom |
1661 | + left: parent.left |
1662 | + right: parent.right |
1663 | + top: blurredBackground.bottom |
1664 | + } |
1665 | + color: styleMusic.common.black |
1666 | + } |
1667 | + |
1668 | + /* Progress bar component */ |
1669 | + Item { |
1670 | + id: musicToolbarFullProgressContainer |
1671 | + anchors.left: parent.left |
1672 | + anchors.leftMargin: units.gu(3) |
1673 | + anchors.right: parent.right |
1674 | + anchors.rightMargin: units.gu(3) |
1675 | + anchors.top: blurredBackground.bottom |
1676 | + anchors.topMargin: units.gu(1) |
1677 | + height: units.gu(3) |
1678 | + width: parent.width |
1679 | + |
1680 | + /* Position label */ |
1681 | + Label { |
1682 | + id: musicToolbarFullPositionLabel |
1683 | + anchors.top: progressSliderMusic.bottom |
1684 | + anchors.topMargin: units.gu(-2) |
1685 | + anchors.left: parent.left |
1686 | + color: styleMusic.nowPlaying.labelSecondaryColor |
1687 | + fontSize: "small" |
1688 | + height: parent.height |
1689 | + horizontalAlignment: Text.AlignHCenter |
1690 | + text: durationToString(player.position) |
1691 | + verticalAlignment: Text.AlignVCenter |
1692 | + width: units.gu(3) |
1693 | + } |
1694 | + |
1695 | + Slider { |
1696 | + id: progressSliderMusic |
1697 | + anchors.left: parent.left |
1698 | + anchors.right: parent.right |
1699 | + maximumValue: player.duration // load value at startup |
1700 | + objectName: "progressSliderShape" |
1701 | + style: UbuntuBlueSliderStyle {} |
1702 | + value: player.position // load value at startup |
1703 | + |
1704 | + function formatValue(v) { |
1705 | + if (seeking) { // update position label while dragging |
1706 | + musicToolbarFullPositionLabel.text = durationToString(v) |
1707 | + } |
1708 | + |
1709 | + return durationToString(v) |
1710 | + } |
1711 | + |
1712 | + property bool seeking: false |
1713 | + property bool seeked: false |
1714 | + |
1715 | + onSeekingChanged: { |
1716 | + if (seeking === false) { |
1717 | + musicToolbarFullPositionLabel.text = durationToString(player.position) |
1718 | + } |
1719 | + } |
1720 | + |
1721 | + onPressedChanged: { |
1722 | + seeking = pressed |
1723 | + |
1724 | + if (!pressed) { |
1725 | + seeked = true |
1726 | + player.seek(value) |
1727 | + |
1728 | + musicToolbarFullPositionLabel.text = durationToString(value) |
1729 | + } |
1730 | + } |
1731 | + |
1732 | + Connections { |
1733 | + target: player |
1734 | + onPositionChanged: { |
1735 | + // seeked is a workaround for bug 1310706 as the first position after a seek is sometimes invalid (0) |
1736 | + if (progressSliderMusic.seeking === false && !progressSliderMusic.seeked) { |
1737 | + musicToolbarFullPositionLabel.text = durationToString(player.position) |
1738 | + musicToolbarFullDurationLabel.text = durationToString(player.duration) |
1739 | + |
1740 | + progressSliderMusic.value = player.position |
1741 | + progressSliderMusic.maximumValue = player.duration |
1742 | + } |
1743 | + |
1744 | + progressSliderMusic.seeked = false; |
1745 | + } |
1746 | + onStopped: { |
1747 | + musicToolbarFullPositionLabel.text = durationToString(0); |
1748 | + musicToolbarFullDurationLabel.text = durationToString(0); |
1749 | + } |
1750 | + } |
1751 | + } |
1752 | + |
1753 | + /* Duration label */ |
1754 | + Label { |
1755 | + id: musicToolbarFullDurationLabel |
1756 | + anchors.top: progressSliderMusic.bottom |
1757 | + anchors.topMargin: units.gu(-2) |
1758 | + anchors.right: parent.right |
1759 | + color: styleMusic.nowPlaying.labelSecondaryColor |
1760 | + fontSize: "small" |
1761 | + height: parent.height |
1762 | + horizontalAlignment: Text.AlignHCenter |
1763 | + text: durationToString(player.duration) |
1764 | + verticalAlignment: Text.AlignVCenter |
1765 | + width: units.gu(3) |
1766 | + } |
1767 | + } |
1768 | + } |
1769 | + |
1770 | + Loader { |
1771 | + id: queueListLoader |
1772 | + anchors { |
1773 | + fill: parent |
1774 | + } |
1775 | + asynchronous: true |
1776 | + sourceComponent: MultiSelectListView { |
1777 | + id: queueList |
1778 | + anchors { |
1779 | + bottomMargin: musicToolbarFullContainer.height + units.gu(2) |
1780 | + fill: parent |
1781 | + topMargin: units.gu(2) |
1782 | + } |
1783 | + delegate: queueDelegate |
1784 | + footer: Item { |
1785 | + height: mainView.height - (styleMusic.common.expandHeight + queueList.currentHeight) + units.gu(8) |
1786 | + } |
1787 | + model: trackQueue.model |
1788 | + objectName: "nowPlayingqueueList" |
1789 | + |
1790 | + property int normalHeight: units.gu(6) |
1791 | + property int transitionDuration: 250 // transition length of animations |
1792 | + |
1793 | + onCountChanged: customdebug("Queue: Now has: " + queueList.count + " tracks") |
1794 | + |
1795 | + Component { |
1796 | + id: queueDelegate |
1797 | + ListItemWithActions { |
1798 | + id: queueListItem |
1799 | + color: player.currentIndex === index ? "#2c2c34" : styleMusic.mainView.backgroundColor |
1800 | + height: queueList.normalHeight |
1801 | + objectName: "nowPlayingListItem" + index |
1802 | + state: "" |
1803 | + |
1804 | + leftSideAction: Remove { |
1805 | + onTriggered: trackQueue.removeQueueList([index]) |
1806 | + } |
1807 | + multiselectable: true |
1808 | + reorderable: true |
1809 | + rightSideActions: [ |
1810 | + AddToPlaylist{ |
1811 | + |
1812 | + } |
1813 | + ] |
1814 | + |
1815 | + onItemClicked: { |
1816 | + customdebug("File: " + model.filename) // debugger |
1817 | + trackQueueClick(index); // toggle track state |
1818 | + } |
1819 | + onReorder: { |
1820 | + console.debug("Move: ", from, to); |
1821 | + |
1822 | + trackQueue.model.move(from, to, 1); |
1823 | + Library.moveQueueItem(from, to); |
1824 | + |
1825 | + // Maintain currentIndex with current song |
1826 | + if (from === player.currentIndex) { |
1827 | + player.currentIndex = to; |
1828 | + } |
1829 | + else if (from < player.currentIndex && to >= player.currentIndex) { |
1830 | + player.currentIndex -= 1; |
1831 | + } |
1832 | + else if (from > player.currentIndex && to <= player.currentIndex) { |
1833 | + player.currentIndex += 1; |
1834 | + } |
1835 | + |
1836 | + queueIndex = player.currentIndex |
1837 | + } |
1838 | + |
1839 | + Item { |
1840 | + id: trackContainer; |
1841 | + anchors { |
1842 | + fill: parent |
1843 | + } |
1844 | + |
1845 | + NumberAnimation { |
1846 | + id: trackContainerReorderAnimation |
1847 | + target: trackContainer; |
1848 | + property: "anchors.leftMargin"; |
1849 | + duration: queueList.transitionDuration; |
1850 | + to: units.gu(2) |
1851 | + } |
1852 | + |
1853 | + NumberAnimation { |
1854 | + id: trackContainerResetAnimation |
1855 | + target: trackContainer; |
1856 | + property: "anchors.leftMargin"; |
1857 | + duration: queueList.transitionDuration; |
1858 | + to: units.gu(0.5) |
1859 | + } |
1860 | + |
1861 | + MusicRow { |
1862 | + id: musicRow |
1863 | + height: parent.height |
1864 | + column: Column { |
1865 | + Label { |
1866 | + id: trackTitle |
1867 | + color: player.currentIndex === index ? UbuntuColors.blue |
1868 | + : styleMusic.common.music |
1869 | + fontSize: "small" |
1870 | + objectName: "titleLabel" |
1871 | + text: model.title |
1872 | + } |
1873 | + |
1874 | + Label { |
1875 | + id: trackArtist |
1876 | + color: styleMusic.common.subtitle |
1877 | + fontSize: "x-small" |
1878 | + objectName: "artistLabel" |
1879 | + text: model.author |
1880 | + } |
1881 | + } |
1882 | + } |
1883 | + } |
1884 | + } |
1885 | + } |
1886 | + } |
1887 | + visible: isListView || wideAspect |
1888 | + } |
1889 | + |
1890 | + /* Full toolbar */ |
1891 | + Rectangle { |
1892 | + id: musicToolbarFullContainer |
1893 | + anchors.bottom: parent.bottom |
1894 | + color: styleMusic.common.black |
1895 | + height: units.gu(10) |
1896 | + width: parent.width |
1897 | + |
1898 | + /* Repeat button */ |
1899 | + MouseArea { |
1900 | + id: nowPlayingRepeatButton |
1901 | + anchors.right: nowPlayingPreviousButton.left |
1902 | + anchors.rightMargin: units.gu(1) |
1903 | + anchors.verticalCenter: nowPlayingPlayButton.verticalCenter |
1904 | + height: units.gu(6) |
1905 | + opacity: player.repeat && !emptyPageLoader.noMusic ? 1 : .4 |
1906 | + width: height |
1907 | + onClicked: player.repeat = !player.repeat |
1908 | + |
1909 | + Icon { |
1910 | + id: repeatIcon |
1911 | + height: units.gu(3) |
1912 | + width: height |
1913 | + anchors.verticalCenter: parent.verticalCenter |
1914 | + anchors.horizontalCenter: parent.horizontalCenter |
1915 | + color: "white" |
1916 | + name: "media-playlist-repeat" |
1917 | + objectName: "repeatShape" |
1918 | + opacity: player.repeat && !emptyPageLoader.noMusic ? 1 : .4 |
1919 | + } |
1920 | + } |
1921 | + |
1922 | + /* Previous button */ |
1923 | + MouseArea { |
1924 | + id: nowPlayingPreviousButton |
1925 | + anchors.right: nowPlayingPlayButton.left |
1926 | + anchors.rightMargin: units.gu(1) |
1927 | + anchors.verticalCenter: nowPlayingPlayButton.verticalCenter |
1928 | + height: units.gu(6) |
1929 | + opacity: trackQueue.model.count === 0 ? .4 : 1 |
1930 | + width: height |
1931 | + onClicked: player.previousSong() |
1932 | + |
1933 | + Icon { |
1934 | + id: nowPlayingPreviousIndicator |
1935 | + height: units.gu(3) |
1936 | + width: height |
1937 | + anchors.verticalCenter: parent.verticalCenter |
1938 | + anchors.horizontalCenter: parent.horizontalCenter |
1939 | + color: "white" |
1940 | + name: "media-skip-backward" |
1941 | + objectName: "previousShape" |
1942 | + opacity: 1 |
1943 | + } |
1944 | + } |
1945 | + |
1946 | + /* Play/Pause button */ |
1947 | + MouseArea { |
1948 | + id: nowPlayingPlayButton |
1949 | + anchors.centerIn: parent |
1950 | + height: units.gu(10) |
1951 | + width: height |
1952 | + onClicked: player.toggle() |
1953 | + |
1954 | + Icon { |
1955 | + id: nowPlayingPlayIndicator |
1956 | + height: units.gu(6) |
1957 | + width: height |
1958 | + anchors.verticalCenter: parent.verticalCenter |
1959 | + anchors.horizontalCenter: parent.horizontalCenter |
1960 | + opacity: emptyPageLoader.noMusic ? .4 : 1 |
1961 | + color: "white" |
1962 | + name: player.playbackState === MediaPlayer.PlayingState ? "media-playback-pause" : "media-playback-start" |
1963 | + objectName: "playShape" |
1964 | + } |
1965 | + } |
1966 | + |
1967 | + /* Next button */ |
1968 | + MouseArea { |
1969 | + id: nowPlayingNextButton |
1970 | + anchors.left: nowPlayingPlayButton.right |
1971 | + anchors.leftMargin: units.gu(1) |
1972 | + anchors.verticalCenter: nowPlayingPlayButton.verticalCenter |
1973 | + height: units.gu(6) |
1974 | + opacity: trackQueue.model.count === 0 ? .4 : 1 |
1975 | + width: height |
1976 | + onClicked: player.nextSong() |
1977 | + |
1978 | + Icon { |
1979 | + id: nowPlayingNextIndicator |
1980 | + height: units.gu(3) |
1981 | + width: height |
1982 | + anchors.verticalCenter: parent.verticalCenter |
1983 | + anchors.horizontalCenter: parent.horizontalCenter |
1984 | + color: "white" |
1985 | + name: "media-skip-forward" |
1986 | + objectName: "forwardShape" |
1987 | + opacity: 1 |
1988 | + } |
1989 | + } |
1990 | + |
1991 | + /* Shuffle button */ |
1992 | + MouseArea { |
1993 | + id: nowPlayingShuffleButton |
1994 | + anchors.left: nowPlayingNextButton.right |
1995 | + anchors.leftMargin: units.gu(1) |
1996 | + anchors.verticalCenter: nowPlayingPlayButton.verticalCenter |
1997 | + height: units.gu(6) |
1998 | + opacity: player.shuffle && !emptyPageLoader.noMusic ? 1 : .4 |
1999 | + width: height |
2000 | + onClicked: player.shuffle = !player.shuffle |
2001 | + |
2002 | + Icon { |
2003 | + id: shuffleIcon |
2004 | + height: units.gu(3) |
2005 | + width: height |
2006 | + anchors.verticalCenter: parent.verticalCenter |
2007 | + anchors.horizontalCenter: parent.horizontalCenter |
2008 | + color: "white" |
2009 | + name: "media-playlist-shuffle" |
2010 | + objectName: "shuffleShape" |
2011 | + opacity: player.shuffle && !emptyPageLoader.noMusic ? 1 : .4 |
2012 | + } |
2013 | + } |
2014 | + |
2015 | + /* Object which provides the progress bar when in the queue */ |
2016 | + Rectangle { |
2017 | + id: playerControlsProgressBar |
2018 | + anchors { |
2019 | + bottom: parent.bottom |
2020 | + left: parent.left |
2021 | + right: parent.right |
2022 | + } |
2023 | + color: styleMusic.common.black |
2024 | + height: units.gu(0.25) |
2025 | + visible: isListView || wideAspect |
2026 | + |
2027 | + Rectangle { |
2028 | + id: playerControlsProgressBarHint |
2029 | + anchors { |
2030 | + left: parent.left |
2031 | + bottom: parent.bottom |
2032 | + } |
2033 | + color: UbuntuColors.blue |
2034 | + height: parent.height |
2035 | + width: player.duration > 0 ? (player.position / player.duration) * playerControlsProgressBar.width : 0 |
2036 | + |
2037 | + Connections { |
2038 | + target: player |
2039 | + onPositionChanged: { |
2040 | + playerControlsProgressBarHint.width = (player.position / player.duration) * playerControlsProgressBar.width |
2041 | + } |
2042 | + onStopped: { |
2043 | + playerControlsProgressBarHint.width = 0; |
2044 | + } |
2045 | + } |
2046 | + } |
2047 | + } |
2048 | + } |
2049 | +} |
2050 | |
2051 | === renamed file 'Player.qml' => 'app/components/Player.qml' |
2052 | === added file 'app/components/PlaylistsEmptyState.qml' |
2053 | --- app/components/PlaylistsEmptyState.qml 1970-01-01 00:00:00 +0000 |
2054 | +++ app/components/PlaylistsEmptyState.qml 2015-02-10 03:13:44 +0000 |
2055 | @@ -0,0 +1,58 @@ |
2056 | +/* |
2057 | + * Copyright (C) 2013, 2014, 2015 |
2058 | + * Andrew Hayzen <ahayzen@gmail.com> |
2059 | + * Daniel Holm <d.holmen@gmail.com> |
2060 | + * Victor Thompson <victor.thompson@gmail.com> |
2061 | + * |
2062 | + * This program is free software; you can redistribute it and/or modify |
2063 | + * it under the terms of the GNU General Public License as published by |
2064 | + * the Free Software Foundation; version 3. |
2065 | + * |
2066 | + * This program is distributed in the hope that it will be useful, |
2067 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2068 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2069 | + * GNU General Public License for more details. |
2070 | + * |
2071 | + * You should have received a copy of the GNU General Public License |
2072 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2073 | + */ |
2074 | + |
2075 | +import QtQuick 2.3 |
2076 | +import Ubuntu.Components 1.1 |
2077 | + |
2078 | + |
2079 | +Rectangle { |
2080 | + id: playlistsEmptyState |
2081 | + anchors { |
2082 | + fill: parent |
2083 | + } |
2084 | + color: mainView.backgroundColor |
2085 | + |
2086 | + Column { |
2087 | + anchors.centerIn: parent |
2088 | + spacing: units.gu(4) |
2089 | + width: units.gu(36) |
2090 | + |
2091 | + Label { |
2092 | + color: styleMusic.libraryEmpty.labelColor |
2093 | + elide: Text.ElideRight |
2094 | + fontSize: "x-large" |
2095 | + horizontalAlignment: Text.AlignLeft |
2096 | + maximumLineCount: 2 |
2097 | + text: i18n.tr("No playlists found") |
2098 | + width: parent.width |
2099 | + wrapMode: Text.WordWrap |
2100 | + } |
2101 | + |
2102 | + Label { |
2103 | + color: styleMusic.libraryEmpty.labelColor |
2104 | + elide: Text.ElideRight |
2105 | + fontSize: "large" |
2106 | + horizontalAlignment: Text.AlignLeft |
2107 | + maximumLineCount: 4 |
2108 | + text: i18n.tr("Get more out of Music by tapping the %1 icon to start making playlists for every mood and occasion.").arg('"+"') |
2109 | + width: parent.width |
2110 | + wrapMode: Text.WordWrap |
2111 | + } |
2112 | + } |
2113 | +} |
2114 | |
2115 | === renamed file 'Style.qml' => 'app/components/Style.qml' |
2116 | === added directory 'app/components/ViewButton' |
2117 | === added file 'app/components/ViewButton/CMakeLists.txt' |
2118 | --- app/components/ViewButton/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
2119 | +++ app/components/ViewButton/CMakeLists.txt 2015-02-10 03:13:44 +0000 |
2120 | @@ -0,0 +1,4 @@ |
2121 | +# make the qml files visible on qtcreator |
2122 | +file(GLOB VIEW_BUTTON_QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml) |
2123 | + |
2124 | +add_custom_target(com_ubuntu_music_VIEW_BUTTON_QMLFiles ALL SOURCES ${VIEW_BUTTON_QML_FILES}) |
2125 | |
2126 | === added file 'app/components/ViewButton/PlayAllButton.qml' |
2127 | --- app/components/ViewButton/PlayAllButton.qml 1970-01-01 00:00:00 +0000 |
2128 | +++ app/components/ViewButton/PlayAllButton.qml 2015-02-10 03:13:44 +0000 |
2129 | @@ -0,0 +1,34 @@ |
2130 | +/* |
2131 | + * Copyright (C) 2013, 2014, 2015 |
2132 | + * Andrew Hayzen <ahayzen@gmail.com> |
2133 | + * Daniel Holm <d.holmen@gmail.com> |
2134 | + * Victor Thompson <victor.thompson@gmail.com> |
2135 | + * |
2136 | + * This program is free software; you can redistribute it and/or modify |
2137 | + * it under the terms of the GNU General Public License as published by |
2138 | + * the Free Software Foundation; version 3. |
2139 | + * |
2140 | + * This program is distributed in the hope that it will be useful, |
2141 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2142 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2143 | + * GNU General Public License for more details. |
2144 | + * |
2145 | + * You should have received a copy of the GNU General Public License |
2146 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2147 | + */ |
2148 | + |
2149 | +import QtQuick 2.3 |
2150 | +import Ubuntu.Components 1.1 |
2151 | + |
2152 | + |
2153 | +Button { |
2154 | + color: UbuntuColors.green |
2155 | + height: units.gu(4) |
2156 | + // TRANSLATORS: this appears in a button with limited space (around 14 characters) |
2157 | + text: i18n.tr("Play all") |
2158 | + width: units.gu(15) |
2159 | + |
2160 | + property var model |
2161 | + |
2162 | + onClicked: trackClicked(model, 0) // play track |
2163 | +} |
2164 | |
2165 | === added file 'app/components/ViewButton/QueueAllButton.qml' |
2166 | --- app/components/ViewButton/QueueAllButton.qml 1970-01-01 00:00:00 +0000 |
2167 | +++ app/components/ViewButton/QueueAllButton.qml 2015-02-10 03:13:44 +0000 |
2168 | @@ -0,0 +1,46 @@ |
2169 | +/* |
2170 | + * Copyright (C) 2013, 2014, 2015 |
2171 | + * Andrew Hayzen <ahayzen@gmail.com> |
2172 | + * Daniel Holm <d.holmen@gmail.com> |
2173 | + * Victor Thompson <victor.thompson@gmail.com> |
2174 | + * |
2175 | + * This program is free software; you can redistribute it and/or modify |
2176 | + * it under the terms of the GNU General Public License as published by |
2177 | + * the Free Software Foundation; version 3. |
2178 | + * |
2179 | + * This program is distributed in the hope that it will be useful, |
2180 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2181 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2182 | + * GNU General Public License for more details. |
2183 | + * |
2184 | + * You should have received a copy of the GNU General Public License |
2185 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2186 | + */ |
2187 | + |
2188 | +import QtQuick 2.3 |
2189 | +import Ubuntu.Components 1.1 |
2190 | + |
2191 | + |
2192 | +Button { |
2193 | + height: units.gu(4) |
2194 | + strokeColor: UbuntuColors.green |
2195 | + width: units.gu(15) |
2196 | + |
2197 | + property var model |
2198 | + |
2199 | + onClicked: addQueueFromModel(model) |
2200 | + |
2201 | + Text { |
2202 | + anchors { |
2203 | + centerIn: parent |
2204 | + } |
2205 | + color: "white" |
2206 | + elide: Text.ElideRight |
2207 | + height: parent.height |
2208 | + horizontalAlignment: Text.AlignHCenter |
2209 | + // TRANSLATORS: this appears in a button with limited space (around 14 characters) |
2210 | + text: i18n.tr("Queue all") |
2211 | + verticalAlignment: Text.AlignVCenter |
2212 | + width: parent.width - units.gu(2) |
2213 | + } |
2214 | +} |
2215 | |
2216 | === added file 'app/components/ViewButton/ShuffleButton.qml' |
2217 | --- app/components/ViewButton/ShuffleButton.qml 1970-01-01 00:00:00 +0000 |
2218 | +++ app/components/ViewButton/ShuffleButton.qml 2015-02-10 03:13:44 +0000 |
2219 | @@ -0,0 +1,46 @@ |
2220 | +/* |
2221 | + * Copyright (C) 2013, 2014, 2015 |
2222 | + * Andrew Hayzen <ahayzen@gmail.com> |
2223 | + * Daniel Holm <d.holmen@gmail.com> |
2224 | + * Victor Thompson <victor.thompson@gmail.com> |
2225 | + * |
2226 | + * This program is free software; you can redistribute it and/or modify |
2227 | + * it under the terms of the GNU General Public License as published by |
2228 | + * the Free Software Foundation; version 3. |
2229 | + * |
2230 | + * This program is distributed in the hope that it will be useful, |
2231 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2232 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2233 | + * GNU General Public License for more details. |
2234 | + * |
2235 | + * You should have received a copy of the GNU General Public License |
2236 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2237 | + */ |
2238 | + |
2239 | +import QtQuick 2.3 |
2240 | +import Ubuntu.Components 1.1 |
2241 | + |
2242 | + |
2243 | +Button { |
2244 | + height: units.gu(4) |
2245 | + strokeColor: UbuntuColors.green |
2246 | + width: units.gu(15) |
2247 | + |
2248 | + property var model |
2249 | + |
2250 | + onClicked: shuffleModel(model) |
2251 | + |
2252 | + Text { |
2253 | + anchors { |
2254 | + centerIn: parent |
2255 | + } |
2256 | + color: "white" |
2257 | + elide: Text.ElideRight |
2258 | + height: parent.height |
2259 | + horizontalAlignment: Text.AlignHCenter |
2260 | + // TRANSLATORS: this appears in a button with limited space (around 14 characters) |
2261 | + text: i18n.tr("Shuffle") |
2262 | + verticalAlignment: Text.AlignVCenter |
2263 | + width: parent.width - units.gu(2) |
2264 | + } |
2265 | +} |
2266 | |
2267 | === modified file 'app/components/Walkthrough/Slide1.qml' |
2268 | --- common/Walkthrough/Slide1.qml 2015-02-02 14:03:12 +0000 |
2269 | +++ app/components/Walkthrough/Slide1.qml 2015-02-10 03:13:44 +0000 |
2270 | @@ -39,7 +39,7 @@ |
2271 | height: (parent.height - bodyText.contentHeight - introductionText.height - 4*units.gu(4))/2 |
2272 | image: Image { |
2273 | id: centerImage |
2274 | - source: Qt.resolvedUrl("../../images/music-app@30.png") |
2275 | + source: Qt.resolvedUrl("../../graphics/music-app@30.png") |
2276 | } |
2277 | radius: "medium" |
2278 | width: height |
2279 | |
2280 | === modified file 'app/components/Walkthrough/Slide2.qml' |
2281 | --- common/Walkthrough/Slide2.qml 2015-02-02 13:37:23 +0000 |
2282 | +++ app/components/Walkthrough/Slide2.qml 2015-02-10 03:13:44 +0000 |
2283 | @@ -38,7 +38,7 @@ |
2284 | } |
2285 | height: (parent.height - bodyText.contentHeight - introductionText.height - 4*units.gu(4))/2 |
2286 | fillMode: Image.PreserveAspectFit |
2287 | - source: Qt.resolvedUrl("../../images/sd_phone_icon.png") |
2288 | + source: Qt.resolvedUrl("../../graphics/sd_phone_icon.png") |
2289 | } |
2290 | |
2291 | Label { |
2292 | |
2293 | === modified file 'app/components/Walkthrough/Slide3.qml' |
2294 | --- common/Walkthrough/Slide3.qml 2015-02-02 13:37:23 +0000 |
2295 | +++ app/components/Walkthrough/Slide3.qml 2015-02-10 03:13:44 +0000 |
2296 | @@ -38,7 +38,7 @@ |
2297 | } |
2298 | height: (parent.height - introductionText.height - finalMessage.contentHeight - 4.5*units.gu(4))/2 |
2299 | fillMode: Image.PreserveAspectFit |
2300 | - source: Qt.resolvedUrl("../../images/music_download_icon.png") |
2301 | + source: Qt.resolvedUrl("../../graphics/music_download_icon.png") |
2302 | } |
2303 | |
2304 | Label { |
2305 | |
2306 | === modified file 'app/components/Walkthrough/Walkthrough.qml' |
2307 | --- common/Walkthrough/Walkthrough.qml 2015-01-31 02:47:16 +0000 |
2308 | +++ app/components/Walkthrough/Walkthrough.qml 2015-02-10 03:13:44 +0000 |
2309 | @@ -134,7 +134,7 @@ |
2310 | anchors.verticalCenter: parent.verticalCenter |
2311 | antialiasing: true |
2312 | height: width |
2313 | - source: listView.currentIndex == index ? "../../images/Ellipse@27.png" : "../../images/Ellipse_15_opacity@27.png" |
2314 | + source: listView.currentIndex == index ? "../../graphics/Ellipse@27.png" : "../../graphics/Ellipse_15_opacity@27.png" |
2315 | width: units.gu(1.5) |
2316 | } |
2317 | } |
2318 | |
2319 | === renamed file 'WorkerModelLoader.qml' => 'app/components/WorkerModelLoader.qml' |
2320 | --- WorkerModelLoader.qml 2015-02-01 03:35:07 +0000 |
2321 | +++ app/components/WorkerModelLoader.qml 2015-02-10 03:13:44 +0000 |
2322 | @@ -21,7 +21,7 @@ |
2323 | |
2324 | WorkerScript { |
2325 | id: worker |
2326 | - source: "worker-library-loader.js" |
2327 | + source: "../logic/worker-library-loader.js" |
2328 | |
2329 | property bool canLoad: true |
2330 | property bool completed: false |
2331 | |
2332 | === renamed directory 'images' => 'app/graphics' |
2333 | === added directory 'app/logic' |
2334 | === added file 'app/logic/CMakeLists.txt' |
2335 | --- app/logic/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
2336 | +++ app/logic/CMakeLists.txt 2015-02-10 03:13:44 +0000 |
2337 | @@ -0,0 +1,4 @@ |
2338 | +# make the qml files visible on qtcreator |
2339 | +file(GLOB LOGIC_JS_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.js) |
2340 | + |
2341 | +add_custom_target(com_ubuntu_music_LOGIC_JSFiles ALL SOURCES ${LOGIC_JS_FILES}) |
2342 | |
2343 | === renamed file 'meta-database.js' => 'app/logic/meta-database.js' |
2344 | === renamed file 'playlists.js' => 'app/logic/playlists.js' |
2345 | === renamed file 'worker-library-loader.js' => 'app/logic/worker-library-loader.js' |
2346 | === renamed file 'music-app.qml' => 'app/music-app.qml' |
2347 | --- music-app.qml 2015-02-05 22:40:14 +0000 |
2348 | +++ app/music-app.qml 2015-02-10 03:13:44 +0000 |
2349 | @@ -22,15 +22,17 @@ |
2350 | import Ubuntu.Components.Popups 1.0 |
2351 | import Ubuntu.Components.ListItems 1.0 as ListItem |
2352 | import Ubuntu.Content 0.1 |
2353 | +import Ubuntu.Layouts 1.0 |
2354 | import Ubuntu.MediaScanner 0.1 |
2355 | import Qt.labs.settings 1.0 |
2356 | import QtMultimedia 5.0 |
2357 | import QtQuick.LocalStorage 2.0 |
2358 | import QtGraphicalEffects 1.0 |
2359 | import UserMetrics 0.1 |
2360 | -import "meta-database.js" as Library |
2361 | -import "playlists.js" as Playlists |
2362 | -import "common" |
2363 | +import "logic/meta-database.js" as Library |
2364 | +import "logic/playlists.js" as Playlists |
2365 | +import "components" |
2366 | +import "ui" |
2367 | |
2368 | MainView { |
2369 | objectName: "music" |
2370 | @@ -55,10 +57,10 @@ |
2371 | focus: true |
2372 | Keys.onPressed: { |
2373 | if(event.key === Qt.Key_Escape) { |
2374 | - if (musicToolbar.currentPage.searchable && musicToolbar.currentPage.state === "search") { |
2375 | - musicToolbar.currentPage.state = "default" |
2376 | + if (mainPageStack.currentMusicPage.searchable && mainPageStack.currentMusicPage.state === "search") { |
2377 | + mainPageStack.currentMusicPage.state = "default" |
2378 | } else { |
2379 | - musicToolbar.goBack(); // Esc Go back |
2380 | + mainPageStack.goBack(); // Esc Go back |
2381 | } |
2382 | } |
2383 | else if(event.modifiers === Qt.AltModifier) { |
2384 | @@ -95,8 +97,8 @@ |
2385 | player.repeat = !player.repeat |
2386 | break; |
2387 | case Qt.Key_F: // Ctrl+F Show Search popup |
2388 | - if (musicToolbar.currentPage.searchable && musicToolbar.currentPage.state === "default") { |
2389 | - musicToolbar.currentPage.state = "search" |
2390 | + if (mainPageStack.currentMusicPage.searchable && mainPageStack.currentMusicPage.state === "default") { |
2391 | + mainPageStack.currentMusicPage.state = "search" |
2392 | header.show() |
2393 | } |
2394 | |
2395 | @@ -159,7 +161,7 @@ |
2396 | id: backAction |
2397 | text: i18n.tr("Back") |
2398 | keywords: i18n.tr("Go back to last page") |
2399 | - onTriggered: musicToolbar.goBack(); |
2400 | + onTriggered: mainPageStack.goBack(); |
2401 | } |
2402 | |
2403 | // With a default Quit action only the first 4 actions are displayed |
2404 | @@ -304,7 +306,7 @@ |
2405 | |
2406 | if (success === true) { |
2407 | if (contentHubWaitForFile.processId === -1) { |
2408 | - contentHubWaitForFile.dialog = PopupUtils.open(contentHubWait, mainView) |
2409 | + contentHubWaitForFile.dialog = PopupUtils.open(Qt.resolvedUrl("components/Dialog/ContentHubWaitDialog.qml"), mainView) |
2410 | contentHubWaitForFile.searchPaths = contentHub.searchPaths; |
2411 | contentHubWaitForFile.processId = processId; |
2412 | contentHubWaitForFile.start(); |
2413 | @@ -318,7 +320,7 @@ |
2414 | } |
2415 | } |
2416 | else { |
2417 | - var errordialog = PopupUtils.open(contentHubError, mainView) |
2418 | + var errordialog = PopupUtils.open(Qt.resolvedUrl("components/Dialog/ContentHubErrorDialog.qml"), mainView) |
2419 | errordialog.errorText = err.join("\n") |
2420 | } |
2421 | } |
2422 | @@ -416,7 +418,7 @@ |
2423 | stopTimer(); |
2424 | |
2425 | console.debug("File(s) were not found", JSON.stringify(searchPaths)) |
2426 | - PopupUtils.open(contentHubNotFound, mainView) |
2427 | + PopupUtils.open(Qt.resolvedUrl("components/Dialog/ContentHubNotFoundDialog.qml"), mainView) |
2428 | } |
2429 | } |
2430 | else { |
2431 | @@ -435,67 +437,6 @@ |
2432 | } |
2433 | } |
2434 | |
2435 | - Component { |
2436 | - id: contentHubWait |
2437 | - Dialog { |
2438 | - id: dialogContentHubWait |
2439 | - |
2440 | - LoadingSpinnerComponent { |
2441 | - anchors { |
2442 | - margins: units.gu(0) |
2443 | - } |
2444 | - loadingText: i18n.tr("Waiting for file(s)...") |
2445 | - visible: true |
2446 | - } |
2447 | - } |
2448 | - } |
2449 | - |
2450 | - Component { |
2451 | - id: contentHubError |
2452 | - Dialog { |
2453 | - id: dialogContentHubError |
2454 | - |
2455 | - property alias errorText: errorLabel.text |
2456 | - |
2457 | - Label { |
2458 | - id: errorLabel |
2459 | - color: styleMusic.common.black |
2460 | - } |
2461 | - |
2462 | - Button { |
2463 | - text: i18n.tr("OK") |
2464 | - onClicked: PopupUtils.close(dialogContentHubError) |
2465 | - } |
2466 | - } |
2467 | - } |
2468 | - |
2469 | - Component { |
2470 | - id: contentHubNotFound |
2471 | - Dialog { |
2472 | - id: dialogContentHubNotFound |
2473 | - |
2474 | - Label { |
2475 | - color: styleMusic.common.black |
2476 | - text: i18n.tr("Imported file not found") |
2477 | - } |
2478 | - |
2479 | - Button { |
2480 | - text: i18n.tr("Wait") |
2481 | - onClicked: { |
2482 | - PopupUtils.close(dialogContentHubNotFound) |
2483 | - |
2484 | - contentHubWaitForFile.dialog = PopupUtils.open(contentHubWait, mainView) |
2485 | - contentHubWaitForFile.start(); |
2486 | - } |
2487 | - } |
2488 | - |
2489 | - Button { |
2490 | - text: i18n.tr("Cancel") |
2491 | - onClicked: PopupUtils.close(dialogContentHubNotFound) |
2492 | - } |
2493 | - } |
2494 | - } |
2495 | - |
2496 | // UserMetrics to show Music stuff on welcome screen |
2497 | Metric { |
2498 | id: songsMetric |
2499 | @@ -607,9 +548,7 @@ |
2500 | |
2501 | // Display walkthrough on first run, even if the user has music |
2502 | if (firstRun) { |
2503 | - var comp = Qt.createComponent("common/Walkthrough/FirstRunWalkthrough.qml") |
2504 | - var walkthrough = comp.createObject(mainPageStack, {}); |
2505 | - mainPageStack.push(walkthrough) |
2506 | + mainPageStack.push(Qt.resolvedUrl("components/Walkthrough/FirstRunWalkthrough.qml"), {}) |
2507 | } |
2508 | |
2509 | if (args.values.url) { |
2510 | @@ -808,10 +747,10 @@ |
2511 | |
2512 | // TODO: improve in refactoring to be able detect when a track is removed |
2513 | // Update playlists page |
2514 | - if (musicPlaylistPage.visible) { |
2515 | + if (playlistsPage.visible) { |
2516 | playlistModel.filterPlaylists() |
2517 | } else { |
2518 | - musicPlaylistPage.changed = true |
2519 | + playlistsPage.changed = true |
2520 | } |
2521 | } |
2522 | } |
2523 | @@ -847,10 +786,10 @@ |
2524 | console.debug("Removed recent:", JSON.stringify(removed)) |
2525 | Library.recentRemoveAlbums(removed) |
2526 | |
2527 | - if (musicStartPage.visible) { |
2528 | + if (recentPage.visible) { |
2529 | recentModel.filterRecent() |
2530 | } else { |
2531 | - musicStartPage.changed = true |
2532 | + recentPage.changed = true |
2533 | } |
2534 | } |
2535 | } |
2536 | @@ -965,7 +904,7 @@ |
2537 | if (trackQueue.model.count === 0) { |
2538 | // Nothing in the queue so stop and pop the queue |
2539 | player.stop() |
2540 | - musicToolbar.goBack() |
2541 | + mainPageStack.goBack() |
2542 | } else if (items.indexOf(player.currentIndex) > -1) { |
2543 | // Current track was removed |
2544 | |
2545 | @@ -1022,6 +961,7 @@ |
2546 | if (preLoadComplete) |
2547 | { |
2548 | loading.visible = false |
2549 | +<<<<<<< TREE |
2550 | playlistTab.loading = false |
2551 | playlistTab.populated = true |
2552 | } |
2553 | @@ -1401,82 +1341,339 @@ |
2554 | |
2555 | Image { |
2556 | id: imageEmptySD |
2557 | +======= |
2558 | + playlistsTab.loading = false |
2559 | + playlistsTab.populated = true |
2560 | + } |
2561 | + } |
2562 | + } |
2563 | + |
2564 | + Layouts { |
2565 | + objectName: "musicLayout" |
2566 | + id: layouts |
2567 | + anchors.fill: parent |
2568 | + layouts: [ |
2569 | + ConditionalLayout { |
2570 | + name: "wideAspect" |
2571 | + when: wideAspect |
2572 | + Rectangle { |
2573 | + anchors.fill: parent |
2574 | + color: "transparent" |
2575 | + Rectangle { |
2576 | + width: layouts.width*0.375 |
2577 | +>>>>>>> MERGE-SOURCE |
2578 | anchors { |
2579 | - verticalCenter: parent.verticalCenter |
2580 | - } |
2581 | - antialiasing: true |
2582 | - fillMode: Image.PreserveAspectFit |
2583 | - height: units.gu(7) |
2584 | - smooth: true |
2585 | - source: "images/music_empty_SD.png" |
2586 | - } |
2587 | - } |
2588 | - |
2589 | - Label { |
2590 | - color: styleMusic.libraryEmpty.labelColor |
2591 | - elide: Text.ElideRight |
2592 | - fontSize: "x-large" |
2593 | - horizontalAlignment: Text.AlignLeft |
2594 | - maximumLineCount: 2 |
2595 | - text: i18n.tr("No music found") |
2596 | - width: parent.width |
2597 | - wrapMode: Text.WordWrap |
2598 | - } |
2599 | - |
2600 | - Label { |
2601 | - color: styleMusic.libraryEmpty.labelColor |
2602 | - elide: Text.ElideRight |
2603 | - fontSize: "large" |
2604 | - horizontalAlignment: Text.AlignLeft |
2605 | - maximumLineCount: 4 |
2606 | - text: i18n.tr("Connect your device to any computer and simply drag files to the Music folder or insert removable media with music.") |
2607 | - width: parent.width |
2608 | - wrapMode: Text.WordWrap |
2609 | - } |
2610 | - } |
2611 | - } |
2612 | - |
2613 | - // Overlay to show when no playlists are on the device |
2614 | - Rectangle { |
2615 | - id: playlistsEmpty |
2616 | + top: parent.top |
2617 | + bottom:parent.bottom |
2618 | + right: parent.right |
2619 | + } |
2620 | + color: "transparent" |
2621 | + |
2622 | + NowPlaying { |
2623 | + id: nowPlaying |
2624 | + Layouts.item: "nowPlaying" |
2625 | + } |
2626 | + } |
2627 | + } |
2628 | + } |
2629 | + ] |
2630 | + |
2631 | + Loader { |
2632 | + id: musicToolbar |
2633 | + anchors { |
2634 | + bottom: parent.bottom |
2635 | + left: parent.left |
2636 | + right: parent.right |
2637 | + } |
2638 | + asynchronous: true |
2639 | + source: "components/MusicToolbar.qml" |
2640 | + visible: mainPageStack.currentPage.title !== i18n.tr("Now playing") && |
2641 | + mainPageStack.currentPage.title !== i18n.tr("Queue") && |
2642 | + mainPageStack.currentPage.title !== i18n.tr("Select playlist") && |
2643 | + !firstRun && |
2644 | + !wideAspect |
2645 | + z: 200 // put on top of everything else |
2646 | + } |
2647 | + |
2648 | + PageStack { |
2649 | + id: mainPageStack |
2650 | + |
2651 | + // Properties storing the current page info |
2652 | + property Page currentMusicPage: null // currentPage can be Tabs |
2653 | + property bool popping: false |
2654 | + |
2655 | + /* Helper functions */ |
2656 | + |
2657 | + // Go back up the stack if possible |
2658 | + function goBack() { |
2659 | + if (depth > 1) { |
2660 | + pop() |
2661 | + } |
2662 | + } |
2663 | + |
2664 | + // Pop a specific page in the stack |
2665 | + function popPage(page) { |
2666 | + var tmpPages = [] |
2667 | + |
2668 | + popping = true |
2669 | + |
2670 | + while (currentPage !== page && depth > 0) { |
2671 | + tmpPages.push(currentPage) |
2672 | + pop() |
2673 | + } |
2674 | + |
2675 | + if (depth > 0) { |
2676 | + pop() |
2677 | + } |
2678 | + |
2679 | + for (var i=tmpPages.length - 1; i > -1; i--) { |
2680 | + push(tmpPages[i]) |
2681 | + } |
2682 | + |
2683 | + popping = false |
2684 | + } |
2685 | + |
2686 | + // Set the current page, and any parent/stacks |
2687 | + function setPage(childPage) { |
2688 | + if (!popping) { |
2689 | + currentMusicPage = childPage; |
2690 | + } |
2691 | + } |
2692 | + |
2693 | + Tabs { |
2694 | + id: tabs |
2695 | + anchors { |
2696 | + fill: parent |
2697 | + } |
2698 | + |
2699 | + property Tab lastTab: selectedTab |
2700 | + |
2701 | + onSelectedTabChanged: { |
2702 | + // pause loading of the models in the old tab |
2703 | + if (lastTab !== null && lastTab !== selectedTab) { |
2704 | + allowLoading(lastTab, false); |
2705 | + } |
2706 | + |
2707 | + lastTab = selectedTab; |
2708 | + |
2709 | + ensurePopulated(selectedTab); |
2710 | + } |
2711 | + |
2712 | + onSelectedTabIndexChanged: { |
2713 | + if (loadedUI) { // store the tab index if changed by the user |
2714 | + startupSettings.tabIndex = selectedTabIndex |
2715 | + } |
2716 | + } |
2717 | + |
2718 | + // Use a repeater to 'hide' the recent tab when the model is empty |
2719 | + // A repeater is used because the Tabs component respects adds and |
2720 | + // removes. Whereas replacing the list tabChildren does not appear |
2721 | + // to respect removes and setting the page as active: false causes |
2722 | + // the page to be blank but the action to still in the overflow |
2723 | + Repeater { |
2724 | + id: recentTabRepeater |
2725 | + // If the model has not loaded and at startup the db was not empty |
2726 | + // then show recent or |
2727 | + // If the workerlist has been set and it has values then show recent |
2728 | + model: (!recentModel.preLoadComplete && !startupRecentEmpty) || |
2729 | + (recentModel.workerList !== undefined && |
2730 | + recentModel.workerList.length > 0) ? 1 : 0 |
2731 | + delegate: Component { |
2732 | + // First tab is all music |
2733 | + Tab { |
2734 | + property bool populated: recentTabRepeater.populated |
2735 | + property var loader: [recentModel.filterRecent] |
2736 | + property bool loading: recentTabRepeater.loading |
2737 | + property var model: [recentModel, albumTracksModel] |
2738 | + id: recentTab |
2739 | + objectName: "recentTab" |
2740 | + anchors.fill: parent |
2741 | + title: page.title |
2742 | + |
2743 | + // Tab content begins here |
2744 | + page: Recent { |
2745 | + id: recentPage |
2746 | + } |
2747 | + } |
2748 | + } |
2749 | + |
2750 | + // Store the startup state of the db separately otherwise |
2751 | + // it breaks the binding of model |
2752 | + property bool startupRecentEmpty: Library.isRecentEmpty() |
2753 | + |
2754 | + // cached values of the recent model that are copied when |
2755 | + // the tab is created |
2756 | + property bool loading: false |
2757 | + property bool populated: false |
2758 | + |
2759 | + onCountChanged: { |
2760 | + if (count === 0 && loadedUI) { |
2761 | + // Jump to the albums tab when recent is empty |
2762 | + tabs.selectedTabIndex = albumsTab.index |
2763 | + } else if (count > 0 && !loadedUI) { |
2764 | + // UI is still loading and recent tab has been inserted |
2765 | + // so move the selected index 'down' as the value is |
2766 | + // not auto updated - this is for the case of loading |
2767 | + // directly to the recent tab (otherwise the content |
2768 | + // appears as the second tab but the tabs think they are |
2769 | + // on the first tab) |
2770 | + tabs.selectedTabIndex -= 1 |
2771 | + } else if (count > 0 && loadedUI) { |
2772 | + // tab inserted while the app is running so move the |
2773 | + // selected index 'up' to keep the same position |
2774 | + tabs.selectedTabIndex += 1 |
2775 | + } |
2776 | + } |
2777 | + } |
2778 | + |
2779 | + // Second tab is arists |
2780 | + Tab { |
2781 | + property bool populated: true |
2782 | + property var loader: [] |
2783 | + property bool loading: false |
2784 | + property var model: [] |
2785 | + id: artistsTab |
2786 | + objectName: "artistsTab" |
2787 | + anchors.fill: parent |
2788 | + title: page.title |
2789 | + |
2790 | + // tab content |
2791 | + page: Artists { |
2792 | + id: artistsPage |
2793 | + } |
2794 | + } |
2795 | + |
2796 | + // third tab is albums |
2797 | + Tab { |
2798 | + property bool populated: true |
2799 | + property var loader: [] |
2800 | + property bool loading: false |
2801 | + property var model: [] |
2802 | + id: albumsTab |
2803 | + objectName: "albumsTab" |
2804 | + anchors.fill: parent |
2805 | + title: page.title |
2806 | + |
2807 | + // Tab content begins here |
2808 | + page: Albums { |
2809 | + id: albumsPage |
2810 | + } |
2811 | + } |
2812 | + |
2813 | + // forth tab is genres |
2814 | + Tab { |
2815 | + property bool populated: true |
2816 | + property var loader: [] |
2817 | + property bool loading: false |
2818 | + property var model: [] |
2819 | + id: genresTab |
2820 | + objectName: "genresTab" |
2821 | + anchors.fill: parent |
2822 | + title: page.title |
2823 | + |
2824 | + // Tab content begins here |
2825 | + page: Genres { |
2826 | + id: genresPage |
2827 | + } |
2828 | + } |
2829 | + |
2830 | + // fourth tab is all songs |
2831 | + Tab { |
2832 | + property bool populated: true |
2833 | + property var loader: [] |
2834 | + property bool loading: false |
2835 | + property var model: [] |
2836 | + id: songsTab |
2837 | + objectName: "songsTab" |
2838 | + anchors.fill: parent |
2839 | + title: page.title |
2840 | + |
2841 | + // Tab content begins here |
2842 | + page: Songs { |
2843 | + id: tracksPage |
2844 | + } |
2845 | + } |
2846 | + |
2847 | + |
2848 | + // fifth tab is the playlists |
2849 | + Tab { |
2850 | + property bool populated: false |
2851 | + property var loader: [playlistModel.filterPlaylists] |
2852 | + property bool loading: false |
2853 | + property var model: [playlistModel, albumTracksModel] |
2854 | + id: playlistsTab |
2855 | + objectName: "playlistsTab" |
2856 | + anchors.fill: parent |
2857 | + title: page.title |
2858 | + |
2859 | + // Tab content begins here |
2860 | + page: Playlists { |
2861 | + id: playlistsPage |
2862 | + } |
2863 | + } |
2864 | + |
2865 | + // Set the models in the tab to allow/disallow loading |
2866 | + function allowLoading(tabToLoad, state) |
2867 | + { |
2868 | + if (tabToLoad !== undefined && tabToLoad.model !== undefined) |
2869 | + { |
2870 | + for (var i=0; i < tabToLoad.model.length; i++) |
2871 | + { |
2872 | + tabToLoad.model[i].canLoad = state; |
2873 | + } |
2874 | + } |
2875 | + } |
2876 | + |
2877 | + function ensurePopulated(selectedTab) |
2878 | + { |
2879 | + allowLoading(selectedTab, true); // allow loading of the models |
2880 | + |
2881 | + if (!selectedTab.populated && !selectedTab.loading && loadedUI) { |
2882 | + loading.visible = true |
2883 | + selectedTab.loading = true |
2884 | + |
2885 | + if (selectedTab.loader !== undefined) |
2886 | + { |
2887 | + for (var i=0; i < selectedTab.loader.length; i++) |
2888 | + { |
2889 | + selectedTab.loader[i](); |
2890 | + } |
2891 | + } |
2892 | + } |
2893 | + loading.visible = selectedTab.loading || !selectedTab.populated |
2894 | + } |
2895 | + |
2896 | + function pushNowPlaying() |
2897 | + { |
2898 | + // only push if on a different page |
2899 | + if (mainPageStack.currentPage.title !== i18n.tr("Now playing") |
2900 | + && mainPageStack.currentPage.title !== i18n.tr("Queue")) { |
2901 | + mainPageStack.push(Qt.resolvedUrl("components/NowPlaying.qml"), {}) |
2902 | + } |
2903 | + |
2904 | + if (mainPageStack.currentPage.title === i18n.tr("Queue")) { |
2905 | + mainPageStack.currentPage.isListView = false; // ensure full view |
2906 | + } |
2907 | + } |
2908 | + } // end of tabs |
2909 | + } |
2910 | + |
2911 | + Loader { |
2912 | + id: emptyPageLoader |
2913 | + // Do not be active if content-hub is importing due to the models resetting |
2914 | + // this then causes the empty page loader to partially run then showing a blank header |
2915 | + active: noMusic && !firstRun && contentHubWaitForFile.processId === -1 |
2916 | anchors { |
2917 | fill: parent |
2918 | - topMargin: -emptyPage.header.height |
2919 | - } |
2920 | - color: mainView.backgroundColor |
2921 | - visible: emptyPage.noPlaylists && !emptyPage.noMusic && (playlistTab.index === tabs.selectedTab.index || mainPageStack.currentPage.title === i18n.tr("Select playlist")) |
2922 | - |
2923 | - Column { |
2924 | - anchors.centerIn: parent |
2925 | - spacing: units.gu(4) |
2926 | - width: units.gu(36) |
2927 | - |
2928 | - Label { |
2929 | - color: styleMusic.libraryEmpty.labelColor |
2930 | - elide: Text.ElideRight |
2931 | - fontSize: "x-large" |
2932 | - horizontalAlignment: Text.AlignLeft |
2933 | - maximumLineCount: 2 |
2934 | - text: i18n.tr("No playlists found") |
2935 | - width: parent.width |
2936 | - wrapMode: Text.WordWrap |
2937 | - } |
2938 | - |
2939 | - Label { |
2940 | - color: styleMusic.libraryEmpty.labelColor |
2941 | - elide: Text.ElideRight |
2942 | - fontSize: "large" |
2943 | - horizontalAlignment: Text.AlignLeft |
2944 | - maximumLineCount: 4 |
2945 | - text: i18n.tr("Get more out of Music by tapping the %1 icon to start making playlists for every mood and occasion.").arg('"+"') |
2946 | - width: parent.width |
2947 | - wrapMode: Text.WordWrap |
2948 | - } |
2949 | - } |
2950 | - } |
2951 | - } |
2952 | - |
2953 | - LoadingSpinnerComponent { |
2954 | - id: loading |
2955 | + } |
2956 | + source: "ui/LibraryEmptyState.qml" |
2957 | + visible: active |
2958 | + |
2959 | + property bool noMusic: allSongsModel.rowCount === 0 && allSongsModelModel.status === SongsModel.Ready && loadedUI |
2960 | + } |
2961 | + |
2962 | + LoadingSpinnerComponent { |
2963 | + id: loading |
2964 | + } |
2965 | } |
2966 | } // end of main view |
2967 | |
2968 | === added directory 'app/ui' |
2969 | === renamed file 'MusicaddtoPlaylist.qml' => 'app/ui/AddToPlaylist.qml' |
2970 | --- MusicaddtoPlaylist.qml 2015-01-28 02:27:52 +0000 |
2971 | +++ app/ui/AddToPlaylist.qml 2015-02-10 03:13:44 +0000 |
2972 | @@ -23,9 +23,11 @@ |
2973 | import Ubuntu.Components.ListItems 1.0 as ListItem |
2974 | import Ubuntu.Components.Popups 1.0 |
2975 | import QtQuick.LocalStorage 2.0 |
2976 | -import "meta-database.js" as Library |
2977 | -import "playlists.js" as Playlists |
2978 | -import "common" |
2979 | +import "../logic/meta-database.js" as Library |
2980 | +import "../logic/playlists.js" as Playlists |
2981 | +import "../components" |
2982 | +import "../components/Flickables" |
2983 | +import "../components/HeadState" |
2984 | |
2985 | |
2986 | /* NOTE: |
2987 | @@ -45,25 +47,10 @@ |
2988 | searchResultsCount: addToPlaylistModelFilter.count |
2989 | state: "default" |
2990 | states: [ |
2991 | - PageHeadState { |
2992 | - name: "default" |
2993 | - head: addToPlaylistPage.head |
2994 | - actions: [ |
2995 | - Action { |
2996 | - enabled: allSongsModel.count > 0 |
2997 | - objectName: "newPlaylistButton" |
2998 | - iconName: "add" |
2999 | - onTriggered: { |
3000 | - customdebug("New playlist.") |
3001 | - PopupUtils.open(newPlaylistDialog, mainView) |
3002 | - } |
3003 | - }, |
3004 | - Action { |
3005 | - enabled: playlistModel.model.count > 0 && allSongsModel.count > 0 |
3006 | - iconName: "search" |
3007 | - onTriggered: addToPlaylistPage.state = "search" |
3008 | - } |
3009 | - ] |
3010 | + PlaylistsHeadState { |
3011 | + newPlaylistEnabled: allSongsModel.count > 0 |
3012 | + searchEnabled: playlistModel.model.count > 0 && allSongsModel.count > 0 |
3013 | + thisPage: addToPlaylistPage |
3014 | }, |
3015 | SearchHeadState { |
3016 | id: searchHeader |
3017 | @@ -129,8 +116,20 @@ |
3018 | page.covers = Playlists.getPlaylistCovers(name) |
3019 | } |
3020 | |
3021 | - musicToolbar.goBack(); // go back to the previous page |
3022 | + mainPageStack.goBack(); // go back to the previous page |
3023 | } |
3024 | } |
3025 | } |
3026 | + |
3027 | + // Overlay to show when no playlists are on the device |
3028 | + Loader { |
3029 | + anchors { |
3030 | + fill: parent |
3031 | + topMargin: -playlistsPage.header.height |
3032 | + } |
3033 | + active: playlistModel.model.count === 0 && playlistModel.workerComplete |
3034 | + asynchronous: true |
3035 | + source: "../components/PlaylistsEmptyState.qml" |
3036 | + visible: active |
3037 | + } |
3038 | } |
3039 | |
3040 | === renamed file 'MusicAlbums.qml' => 'app/ui/Albums.qml' |
3041 | --- MusicAlbums.qml 2015-01-20 12:02:52 +0000 |
3042 | +++ app/ui/Albums.qml 2015-02-10 03:13:44 +0000 |
3043 | @@ -20,7 +20,9 @@ |
3044 | import QtQuick 2.3 |
3045 | import Ubuntu.Components 1.1 |
3046 | import Ubuntu.MediaScanner 0.1 |
3047 | -import "common" |
3048 | +import "../components" |
3049 | +import "../components/Flickables" |
3050 | +import "../components/HeadState" |
3051 | |
3052 | |
3053 | MusicPage { |
3054 | @@ -31,14 +33,9 @@ |
3055 | searchResultsCount: albumsModelFilter.count |
3056 | state: "default" |
3057 | states: [ |
3058 | - PageHeadState { |
3059 | - name: "default" |
3060 | - head: albumsPage.head |
3061 | - actions: Action { |
3062 | - enabled: albumsModelFilter.count > 0 |
3063 | - iconName: "search" |
3064 | - onTriggered: albumsPage.state = "search" |
3065 | - } |
3066 | + SearchableHeadState { |
3067 | + thisPage: albumsPage |
3068 | + searchEnabled: albumsModelFilter.count > 0 |
3069 | }, |
3070 | SearchHeadState { |
3071 | id: searchHeader |
3072 | @@ -46,6 +43,11 @@ |
3073 | } |
3074 | ] |
3075 | |
3076 | + // Hack for autopilot otherwise Albums appears as MusicPage |
3077 | + // due to bug 1341671 it is required that there is a property so that |
3078 | + // qml doesn't optimise using the parent type |
3079 | + property bool bug1341671workaround: true |
3080 | + |
3081 | CardView { |
3082 | id: albumCardView |
3083 | model: SortFilterModel { |
3084 | @@ -70,24 +72,17 @@ |
3085 | secondaryText: model.artist != "" ? model.artist : i18n.tr("Unknown Artist") |
3086 | |
3087 | onClicked: { |
3088 | - var comp = Qt.createComponent("common/SongsPage.qml") |
3089 | - var songsPage = comp.createObject(mainPageStack, |
3090 | - { |
3091 | - "album": model.title, |
3092 | - "artist": model.artist, |
3093 | - "covers": [{art: model.art}], |
3094 | - "isAlbum": true, |
3095 | - "genre": undefined, |
3096 | - "title": i18n.tr("Album"), |
3097 | - "line1": model.artist, |
3098 | - "line2": model.title, |
3099 | - }); |
3100 | - |
3101 | - if (songsPage == null) { // Error Handling |
3102 | - console.log("Error creating object"); |
3103 | - } |
3104 | - |
3105 | - mainPageStack.push(songsPage) |
3106 | + mainPageStack.push(Qt.resolvedUrl("SongsView.qml"), |
3107 | + { |
3108 | + "album": model.title, |
3109 | + "artist": model.artist, |
3110 | + "covers": [{art: model.art}], |
3111 | + "isAlbum": true, |
3112 | + "genre": undefined, |
3113 | + "title": i18n.tr("Album"), |
3114 | + "line1": model.artist, |
3115 | + "line2": model.title |
3116 | + }) |
3117 | } |
3118 | } |
3119 | } |
3120 | |
3121 | === renamed file 'common/AlbumsPage.qml' => 'app/ui/ArtistView.qml' |
3122 | --- common/AlbumsPage.qml 2015-01-30 01:23:39 +0000 |
3123 | +++ app/ui/ArtistView.qml 2015-02-10 03:13:44 +0000 |
3124 | @@ -24,11 +24,14 @@ |
3125 | import Ubuntu.MediaScanner 0.1 |
3126 | import Ubuntu.Thumbnailer 0.1 |
3127 | import QtQuick.LocalStorage 2.0 |
3128 | -import "../meta-database.js" as Library |
3129 | +import "../logic/meta-database.js" as Library |
3130 | +import "../components" |
3131 | +import "../components/Flickables" |
3132 | +import "../components/ViewButton" |
3133 | |
3134 | MusicPage { |
3135 | - id: albumStackPage |
3136 | - objectName: "albumsArtistPage" |
3137 | + id: artistViewPage |
3138 | + objectName: "artistViewPage" |
3139 | visible: false |
3140 | |
3141 | property string artist: "" |
3142 | @@ -50,57 +53,17 @@ |
3143 | header: BlurredHeader { |
3144 | rightColumn: Column { |
3145 | spacing: units.gu(2) |
3146 | - Button { |
3147 | - id: shuffleRow |
3148 | - height: units.gu(4) |
3149 | - strokeColor: UbuntuColors.green |
3150 | - width: units.gu(15) |
3151 | - Text { |
3152 | - anchors { |
3153 | - centerIn: parent |
3154 | - } |
3155 | - color: "white" |
3156 | - elide: Text.ElideRight |
3157 | - height: parent.height |
3158 | - horizontalAlignment: Text.AlignHCenter |
3159 | - // TRANSLATORS: this appears in a button with limited space (around 14 characters) |
3160 | - text: i18n.tr("Shuffle") |
3161 | - verticalAlignment: Text.AlignVCenter |
3162 | - width: parent.width - units.gu(2) |
3163 | - } |
3164 | - onClicked: shuffleModel(songArtistModel) |
3165 | - } |
3166 | - Button { |
3167 | - id: queueAllRow |
3168 | - height: units.gu(4) |
3169 | - strokeColor: UbuntuColors.green |
3170 | - width: units.gu(15) |
3171 | - Text { |
3172 | - anchors { |
3173 | - centerIn: parent |
3174 | - } |
3175 | - color: "white" |
3176 | - elide: Text.ElideRight |
3177 | - height: parent.height |
3178 | - horizontalAlignment: Text.AlignHCenter |
3179 | - // TRANSLATORS: this appears in a button with limited space (around 14 characters) |
3180 | - text: i18n.tr("Queue all") |
3181 | - verticalAlignment: Text.AlignVCenter |
3182 | - width: parent.width - units.gu(2) |
3183 | - } |
3184 | - onClicked: addQueueFromModel(songArtistModel) |
3185 | - } |
3186 | - Button { |
3187 | - id: playRow |
3188 | - color: UbuntuColors.green |
3189 | - height: units.gu(4) |
3190 | - // TRANSLATORS: this appears in a button with limited space (around 14 characters) |
3191 | - text: i18n.tr("Play all") |
3192 | - width: units.gu(15) |
3193 | - onClicked: trackClicked(songArtistModel, 0, true) |
3194 | + ShuffleButton { |
3195 | + model: songArtistModel |
3196 | + } |
3197 | + QueueAllButton { |
3198 | + model: songArtistModel |
3199 | + } |
3200 | + PlayAllButton { |
3201 | + model: songArtistModel |
3202 | } |
3203 | } |
3204 | - coverSources: albumStackPage.covers |
3205 | + coverSources: artistViewPage.covers |
3206 | height: units.gu(30) |
3207 | bottomColumn: Column { |
3208 | Label { |
3209 | @@ -139,18 +102,18 @@ |
3210 | |
3211 | SongsModel { |
3212 | id: songArtistModel |
3213 | - albumArtist: albumStackPage.artist |
3214 | + albumArtist: artistViewPage.artist |
3215 | store: musicStore |
3216 | } |
3217 | } |
3218 | itemWidth: units.gu(12) |
3219 | model: AlbumsModel { |
3220 | id: albumsModel |
3221 | - albumArtist: albumStackPage.artist |
3222 | + albumArtist: artistViewPage.artist |
3223 | store: musicStore |
3224 | onStatusChanged: { |
3225 | if (albumsModel.status === SongsModel.Ready && loaded && albumsModel.count === 0) { |
3226 | - musicToolbar.popPage(albumStackPage) |
3227 | + mainPageStack.popPage(artistViewPage) |
3228 | } |
3229 | } |
3230 | } |
3231 | @@ -162,24 +125,17 @@ |
3232 | secondaryTextVisible: false |
3233 | |
3234 | onClicked: { |
3235 | - var comp = Qt.createComponent("SongsPage.qml") |
3236 | - var songsPage = comp.createObject(mainPageStack, |
3237 | - { |
3238 | - "album": model.title, |
3239 | - "artist": model.artist, |
3240 | - "covers": [{art: model.art}], |
3241 | - "isAlbum": true, |
3242 | - "genre": undefined, |
3243 | - "title": i18n.tr("Album"), |
3244 | - "line1": model.artist != "" ? model.artist : i18n.tr("Unknown Artist"), |
3245 | - "line2": model.title != "" ? model.title : i18n.tr("Unknown Album"), |
3246 | - }); |
3247 | - |
3248 | - if (songsPage == null) { // Error Handling |
3249 | - console.log("Error creating object"); |
3250 | - } |
3251 | - |
3252 | - mainPageStack.push(songsPage) |
3253 | + mainPageStack.push(Qt.resolvedUrl("SongsView.qml"), |
3254 | + { |
3255 | + "album": model.title, |
3256 | + "artist": model.artist, |
3257 | + "covers": [{art: model.art}], |
3258 | + "isAlbum": true, |
3259 | + "genre": undefined, |
3260 | + "title": i18n.tr("Album"), |
3261 | + "line1": model.artist != "" ? model.artist : i18n.tr("Unknown Artist"), |
3262 | + "line2": model.title != "" ? model.title : i18n.tr("Unknown Album") |
3263 | + }) |
3264 | } |
3265 | } |
3266 | } |
3267 | |
3268 | === renamed file 'MusicArtists.qml' => 'app/ui/Artists.qml' |
3269 | --- MusicArtists.qml 2015-01-20 12:02:52 +0000 |
3270 | +++ app/ui/Artists.qml 2015-02-10 03:13:44 +0000 |
3271 | @@ -24,9 +24,11 @@ |
3272 | import Ubuntu.MediaScanner 0.1 |
3273 | import Ubuntu.Thumbnailer 0.1 |
3274 | import QtQuick.LocalStorage 2.0 |
3275 | -import "meta-database.js" as Library |
3276 | -import "playlists.js" as Playlists |
3277 | -import "common" |
3278 | +import "../logic/meta-database.js" as Library |
3279 | +import "../logic/playlists.js" as Playlists |
3280 | +import "../components" |
3281 | +import "../components/Flickables" |
3282 | +import "../components/HeadState" |
3283 | |
3284 | |
3285 | MusicPage { |
3286 | @@ -37,14 +39,9 @@ |
3287 | searchResultsCount: artistsModelFilter.count |
3288 | state: "default" |
3289 | states: [ |
3290 | - PageHeadState { |
3291 | - name: "default" |
3292 | - head: artistsPage.head |
3293 | - actions: Action { |
3294 | - enabled: artistsModelFilter.count > 0 |
3295 | - iconName: "search" |
3296 | - onTriggered: artistsPage.state = "search" |
3297 | - } |
3298 | + SearchableHeadState { |
3299 | + thisPage: artistsPage |
3300 | + searchEnabled: artistsModelFilter.count > 0 |
3301 | }, |
3302 | SearchHeadState { |
3303 | id: searchHeader |
3304 | @@ -52,6 +49,11 @@ |
3305 | } |
3306 | ] |
3307 | |
3308 | + // Hack for autopilot otherwise Artists appears as MusicPage |
3309 | + // due to bug 1341671 it is required that there is a property so that |
3310 | + // qml doesn't optimise using the parent type |
3311 | + property bool bug1341671workaround: true |
3312 | + |
3313 | CardView { |
3314 | id: artistCardView |
3315 | itemWidth: units.gu(12) |
3316 | @@ -97,21 +99,13 @@ |
3317 | } |
3318 | } |
3319 | |
3320 | - |
3321 | onClicked: { |
3322 | - var comp = Qt.createComponent("common/AlbumsPage.qml") |
3323 | - var albumsPage = comp.createObject(mainPageStack, |
3324 | - { |
3325 | - "artist": model.artist, |
3326 | - "covers": artistCard.coverSources, |
3327 | - "title": i18n.tr("Artist"), |
3328 | - }); |
3329 | - |
3330 | - if (albumsPage == null) { // Error Handling |
3331 | - console.log("Error creating object"); |
3332 | - } |
3333 | - |
3334 | - mainPageStack.push(albumsPage) |
3335 | + mainPageStack.push(Qt.resolvedUrl("ArtistView.qml"), |
3336 | + { |
3337 | + "artist": model.artist, |
3338 | + "covers": artistCard.coverSources, |
3339 | + "title": i18n.tr("Artist") |
3340 | + }) |
3341 | } |
3342 | } |
3343 | } |
3344 | |
3345 | === added file 'app/ui/CMakeLists.txt' |
3346 | --- app/ui/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
3347 | +++ app/ui/CMakeLists.txt 2015-02-10 03:13:44 +0000 |
3348 | @@ -0,0 +1,4 @@ |
3349 | +# make the qml files visible on qtcreator |
3350 | +file(GLOB UI_QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.qml) |
3351 | + |
3352 | +add_custom_target(com_ubuntu_music_UI_QMLFiles ALL SOURCES ${UI_QML_FILES}) |
3353 | |
3354 | === renamed file 'MusicGenres.qml' => 'app/ui/Genres.qml' |
3355 | --- MusicGenres.qml 2015-01-20 12:02:52 +0000 |
3356 | +++ app/ui/Genres.qml 2015-02-10 03:13:44 +0000 |
3357 | @@ -20,7 +20,9 @@ |
3358 | import QtQuick 2.3 |
3359 | import Ubuntu.Components 1.1 |
3360 | import Ubuntu.MediaScanner 0.1 |
3361 | -import "common" |
3362 | +import "../components" |
3363 | +import "../components/Flickables" |
3364 | +import "../components/HeadState" |
3365 | |
3366 | |
3367 | MusicPage { |
3368 | @@ -31,14 +33,9 @@ |
3369 | searchResultsCount: genresModelFilter.count |
3370 | state: "default" |
3371 | states: [ |
3372 | - PageHeadState { |
3373 | - name: "default" |
3374 | - head: genresPage.head |
3375 | - actions: Action { |
3376 | - enabled: genresModelFilter.count > 0 |
3377 | - iconName: "search" |
3378 | - onTriggered: genresPage.state = "search" |
3379 | - } |
3380 | + SearchableHeadState { |
3381 | + thisPage: genresPage |
3382 | + searchEnabled: genresModelFilter.count > 0 |
3383 | }, |
3384 | SearchHeadState { |
3385 | id: searchHeader |
3386 | @@ -46,6 +43,11 @@ |
3387 | } |
3388 | ] |
3389 | |
3390 | + // Hack for autopilot otherwise Albums appears as MusicPage |
3391 | + // due to bug 1341671 it is required that there is a property so that |
3392 | + // qml doesn't optimise using the parent type |
3393 | + property bool bug1341671workaround: true |
3394 | + |
3395 | CardView { |
3396 | id: genreCardView |
3397 | itemWidth: units.gu(12) |
3398 | @@ -98,23 +100,16 @@ |
3399 | } |
3400 | |
3401 | onClicked: { |
3402 | - var comp = Qt.createComponent("common/SongsPage.qml") |
3403 | - var songsPage = comp.createObject(mainPageStack, |
3404 | - { |
3405 | - "covers": genreCard.coverSources, |
3406 | - "album": undefined, |
3407 | - "isAlbum": true, |
3408 | - "genre": model.genre, |
3409 | - "title": i18n.tr("Genre"), |
3410 | - "line2": model.genre, |
3411 | - "line1": i18n.tr("Genre") |
3412 | - }); |
3413 | - |
3414 | - if (songsPage == null) { // Error Handling |
3415 | - console.log("Error creating object"); |
3416 | - } |
3417 | - |
3418 | - mainPageStack.push(songsPage) |
3419 | + mainPageStack.push(Qt.resolvedUrl("SongsView.qml"), |
3420 | + { |
3421 | + "covers": genreCard.coverSources, |
3422 | + "album": undefined, |
3423 | + "isAlbum": true, |
3424 | + "genre": model.genre, |
3425 | + "title": i18n.tr("Genre"), |
3426 | + "line2": model.genre, |
3427 | + "line1": i18n.tr("Genre") |
3428 | + }) |
3429 | } |
3430 | } |
3431 | } |
3432 | |
3433 | === added file 'app/ui/LibraryEmptyState.qml' |
3434 | --- app/ui/LibraryEmptyState.qml 1970-01-01 00:00:00 +0000 |
3435 | +++ app/ui/LibraryEmptyState.qml 2015-02-10 03:13:44 +0000 |
3436 | @@ -0,0 +1,123 @@ |
3437 | +/* |
3438 | + * Copyright (C) 2013, 2014, 2015 |
3439 | + * Andrew Hayzen <ahayzen@gmail.com> |
3440 | + * Daniel Holm <d.holmen@gmail.com> |
3441 | + * Victor Thompson <victor.thompson@gmail.com> |
3442 | + * |
3443 | + * This program is free software; you can redistribute it and/or modify |
3444 | + * it under the terms of the GNU General Public License as published by |
3445 | + * the Free Software Foundation; version 3. |
3446 | + * |
3447 | + * This program is distributed in the hope that it will be useful, |
3448 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3449 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3450 | + * GNU General Public License for more details. |
3451 | + * |
3452 | + * You should have received a copy of the GNU General Public License |
3453 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3454 | + */ |
3455 | + |
3456 | +import QtQuick 2.3 |
3457 | +import Ubuntu.Components 1.1 |
3458 | + |
3459 | +Page { |
3460 | + id: libraryEmptyPage |
3461 | + anchors { |
3462 | + fill: parent |
3463 | + } |
3464 | + |
3465 | + // Overlay to show when no tracks detected on the device |
3466 | + Rectangle { |
3467 | + id: libraryEmpty |
3468 | + anchors { |
3469 | + fill: parent |
3470 | + topMargin: -libraryEmptyPage.header.height |
3471 | + } |
3472 | + color: mainView.backgroundColor |
3473 | + |
3474 | + Column { |
3475 | + id: noMusicTextColumn |
3476 | + anchors { |
3477 | + centerIn: parent |
3478 | + } |
3479 | + spacing: units.gu(4) |
3480 | + width: units.gu(36) |
3481 | + |
3482 | + Row { |
3483 | + anchors { |
3484 | + horizontalCenter: parent.horizontalCenter |
3485 | + } |
3486 | + |
3487 | + Item { |
3488 | + height: parent.height |
3489 | + width: imageEmptyDownload.width + units.gu(2) |
3490 | + |
3491 | + Image { |
3492 | + id: imageEmptyDownload |
3493 | + anchors { |
3494 | + horizontalCenter: parent.horizontalCenter |
3495 | + verticalCenter: parent.verticalCenter |
3496 | + } |
3497 | + antialiasing: true |
3498 | + fillMode: Image.PreserveAspectFit |
3499 | + height: units.gu(10) |
3500 | + smooth: true |
3501 | + source: "../graphics/music_empty_download.png" |
3502 | + } |
3503 | + } |
3504 | + |
3505 | + Item { |
3506 | + height: parent.height |
3507 | + width: units.gu(7) |
3508 | + |
3509 | + Image { |
3510 | + id: imageSep |
3511 | + anchors { |
3512 | + horizontalCenter: parent.horizontalCenter |
3513 | + verticalCenter: parent.verticalCenter |
3514 | + } |
3515 | + antialiasing: true |
3516 | + fillMode: Image.PreserveAspectFit |
3517 | + height: units.gu(6) |
3518 | + smooth: true |
3519 | + source: "../graphics/div.png" |
3520 | + } |
3521 | + } |
3522 | + |
3523 | + Image { |
3524 | + id: imageEmptySD |
3525 | + anchors { |
3526 | + verticalCenter: parent.verticalCenter |
3527 | + } |
3528 | + antialiasing: true |
3529 | + fillMode: Image.PreserveAspectFit |
3530 | + height: units.gu(7) |
3531 | + smooth: true |
3532 | + source: "../graphics/music_empty_SD.png" |
3533 | + } |
3534 | + } |
3535 | + |
3536 | + Label { |
3537 | + color: styleMusic.libraryEmpty.labelColor |
3538 | + elide: Text.ElideRight |
3539 | + fontSize: "x-large" |
3540 | + horizontalAlignment: Text.AlignLeft |
3541 | + maximumLineCount: 2 |
3542 | + text: i18n.tr("No music found") |
3543 | + width: parent.width |
3544 | + wrapMode: Text.WordWrap |
3545 | + } |
3546 | + |
3547 | + Label { |
3548 | + color: styleMusic.libraryEmpty.labelColor |
3549 | + elide: Text.ElideRight |
3550 | + fontSize: "large" |
3551 | + horizontalAlignment: Text.AlignLeft |
3552 | + maximumLineCount: 4 |
3553 | + text: i18n.tr("Connect your device to any computer and simply drag files to the Music folder or insert removable media with music.") |
3554 | + width: parent.width |
3555 | + wrapMode: Text.WordWrap |
3556 | + } |
3557 | + } |
3558 | + } |
3559 | +} |
3560 | |
3561 | === renamed file 'MusicNowPlaying.qml' => 'app/ui/NowPlayingView.qml' |
3562 | --- MusicNowPlaying.qml 2015-01-28 02:30:43 +0000 |
3563 | +++ app/ui/NowPlayingView.qml 2015-02-10 03:13:44 +0000 |
3564 | @@ -22,11 +22,13 @@ |
3565 | import QtQuick.LocalStorage 2.0 |
3566 | import Ubuntu.Components 1.1 |
3567 | import Ubuntu.Thumbnailer 0.1 |
3568 | -import "common" |
3569 | -import "common/ListItemActions" |
3570 | -import "common/Themes/Ambiance" |
3571 | -import "meta-database.js" as Library |
3572 | -import "playlists.js" as Playlists |
3573 | +import "../components" |
3574 | +import "../components/Flickables" |
3575 | +import "../components/HeadState" |
3576 | +import "../components/ListItemActions" |
3577 | +import "../components/Themes/Ambiance" |
3578 | +import "../logic/meta-database.js" as Library |
3579 | +import "../logic/playlists.js" as Playlists |
3580 | |
3581 | MusicPage { |
3582 | id: nowPlaying |
3583 | @@ -35,7 +37,7 @@ |
3584 | title: isListView ? queueTitle : nowPlayingTitle |
3585 | visible: false |
3586 | |
3587 | - property bool isListView: false |
3588 | + property alias isListView: nowPlaying.isListView |
3589 | // TRANSLATORS: this appears in the header with limited space (around 20 characters) |
3590 | property string nowPlayingTitle: i18n.tr("Now playing") |
3591 | // TRANSLATORS: this appears in the header with limited space (around 20 characters) |
3592 | @@ -98,14 +100,8 @@ |
3593 | |
3594 | items.push(makeDict(trackQueue.model.get(player.currentIndex))); |
3595 | |
3596 | - var comp = Qt.createComponent("MusicaddtoPlaylist.qml") |
3597 | - var addToPlaylist = comp.createObject(mainPageStack, {"chosenElements": items}); |
3598 | - |
3599 | - if (addToPlaylist == null) { // Error Handling |
3600 | - console.log("Error creating object"); |
3601 | - } |
3602 | - |
3603 | - mainPageStack.push(addToPlaylist) |
3604 | + mainPageStack.push(Qt.resolvedUrl("AddToPlaylist.qml"), |
3605 | + {"chosenElements": items}) |
3606 | } |
3607 | }, |
3608 | Action { |
3609 | @@ -126,619 +122,22 @@ |
3610 | actions: defaultState.actions |
3611 | } |
3612 | }, |
3613 | - PageHeadState { |
3614 | - id: selectionState |
3615 | - |
3616 | - name: "selection" |
3617 | - backAction: Action { |
3618 | - text: i18n.tr("Cancel selection") |
3619 | - iconName: "back" |
3620 | - onTriggered: { |
3621 | - queueListLoader.item.clearSelection() |
3622 | - queueListLoader.item.state = "normal" |
3623 | - } |
3624 | - } |
3625 | - actions: [ |
3626 | - Action { |
3627 | - text: i18n.tr("Select All") |
3628 | - iconName: "select" |
3629 | - onTriggered: { |
3630 | - if (queueListLoader.item.selectedItems.length === trackQueue.model.count) { |
3631 | - queueListLoader.item.clearSelection() |
3632 | - } else { |
3633 | - queueListLoader.item.selectAll() |
3634 | - } |
3635 | - } |
3636 | - }, |
3637 | - Action { |
3638 | - enabled: queueListLoader.item.selectedItems.length > 0 |
3639 | - iconName: "add-to-playlist" |
3640 | - text: i18n.tr("Add to playlist") |
3641 | - onTriggered: { |
3642 | - var items = [] |
3643 | - |
3644 | - for (var i=0; i < queueListLoader.item.selectedItems.length; i++) { |
3645 | - items.push(makeDict(trackQueue.model.get(queueListLoader.item.selectedItems[i]))); |
3646 | - } |
3647 | - |
3648 | - var comp = Qt.createComponent("MusicaddtoPlaylist.qml") |
3649 | - var addToPlaylist = comp.createObject(mainPageStack, {"chosenElements": items}); |
3650 | - |
3651 | - if (addToPlaylist == null) { // Error Handling |
3652 | - console.log("Error creating object"); |
3653 | - } |
3654 | - |
3655 | - mainPageStack.push(addToPlaylist) |
3656 | - |
3657 | - queueListLoader.item.closeSelection() |
3658 | - } |
3659 | - }, |
3660 | - Action { |
3661 | - enabled: queueListLoader.item.selectedItems.length > 0 |
3662 | - iconName: "delete" |
3663 | - text: i18n.tr("Delete") |
3664 | - onTriggered: { |
3665 | - // Remove the tracks from the queue |
3666 | - // Use slice() to copy the list |
3667 | - // so that the indexes don't change as they are removed |
3668 | - trackQueue.removeQueueList(queueListLoader.item.selectedItems.slice()) |
3669 | - |
3670 | - queueListLoader.item.closeSelection() |
3671 | - } |
3672 | - } |
3673 | - ] |
3674 | - PropertyChanges { |
3675 | - target: nowPlaying.head |
3676 | - backAction: selectionState.backAction |
3677 | - actions: selectionState.actions |
3678 | + MultiSelectHeadState { |
3679 | + addToQueue: false |
3680 | + listview: queueListLoader.item |
3681 | + removable: true |
3682 | + thisPage: nowPlaying |
3683 | + |
3684 | + onRemoved: { |
3685 | + // Remove the tracks from the queue |
3686 | + // Use slice() to copy the list |
3687 | + // so that the indexes don't change as they are removed |
3688 | + trackQueue.removeQueueList(selectedItems.slice()) |
3689 | } |
3690 | } |
3691 | ] |
3692 | |
3693 | - Item { |
3694 | - id: fullview |
3695 | - anchors { |
3696 | - top: parent.top |
3697 | - topMargin: mainView.header.height |
3698 | - } |
3699 | - height: parent.height - mainView.header.height - units.gu(9.5) |
3700 | - visible: !isListView |
3701 | - width: parent.width |
3702 | - |
3703 | - BlurredBackground { |
3704 | - id: blurredBackground |
3705 | - anchors { |
3706 | - left: parent.left |
3707 | - right: parent.right |
3708 | - top: parent.top |
3709 | - } |
3710 | - art: albumImage.firstSource |
3711 | - height: parent.height - units.gu(7) |
3712 | - |
3713 | - Item { |
3714 | - id: albumImageContainer |
3715 | - anchors { |
3716 | - horizontalCenter: parent.horizontalCenter |
3717 | - top: parent.top |
3718 | - } |
3719 | - height: parent.height |
3720 | - width: parent.width |
3721 | - |
3722 | - CoverGrid { |
3723 | - id: albumImage |
3724 | - anchors.centerIn: parent |
3725 | - covers: [{art: player.currentMetaArt, author: player.currentMetaArtist, album: player.currentMetaAlbum}] |
3726 | - size: parent.width > parent.height ? parent.height : parent.width |
3727 | - } |
3728 | - } |
3729 | - |
3730 | - Rectangle { |
3731 | - id: nowPlayingWideAspectLabelsBackground |
3732 | - anchors.bottom: parent.bottom |
3733 | - color: styleMusic.common.black |
3734 | - height: nowPlayingWideAspectTitle.lineCount === 1 ? units.gu(10) : units.gu(13) |
3735 | - opacity: 0.8 |
3736 | - width: parent.width |
3737 | - } |
3738 | - |
3739 | - /* Column for labels in wideAspect */ |
3740 | - Column { |
3741 | - id: nowPlayingWideAspectLabels |
3742 | - spacing: units.gu(1) |
3743 | - anchors { |
3744 | - left: parent.left |
3745 | - leftMargin: units.gu(2) |
3746 | - right: parent.right |
3747 | - rightMargin: units.gu(2) |
3748 | - top: nowPlayingWideAspectLabelsBackground.top |
3749 | - topMargin: nowPlayingWideAspectTitle.lineCount === 1 ? units.gu(2) : units.gu(1.5) |
3750 | - } |
3751 | - |
3752 | - /* Title of track */ |
3753 | - Label { |
3754 | - id: nowPlayingWideAspectTitle |
3755 | - anchors { |
3756 | - left: parent.left |
3757 | - leftMargin: units.gu(1) |
3758 | - right: parent.right |
3759 | - rightMargin: units.gu(1) |
3760 | - } |
3761 | - color: styleMusic.playerControls.labelColor |
3762 | - elide: Text.ElideRight |
3763 | - fontSize: "x-large" |
3764 | - maximumLineCount: 2 |
3765 | - objectName: "playercontroltitle" |
3766 | - text: trackQueue.model.count === 0 ? "" : player.currentMetaTitle === "" ? player.currentMetaFile : player.currentMetaTitle |
3767 | - wrapMode: Text.WordWrap |
3768 | - } |
3769 | - |
3770 | - /* Artist of track */ |
3771 | - Label { |
3772 | - id: nowPlayingWideAspectArtist |
3773 | - anchors { |
3774 | - left: parent.left |
3775 | - leftMargin: units.gu(1) |
3776 | - right: parent.right |
3777 | - rightMargin: units.gu(1) |
3778 | - } |
3779 | - color: styleMusic.nowPlaying.labelSecondaryColor |
3780 | - elide: Text.ElideRight |
3781 | - fontSize: "small" |
3782 | - text: trackQueue.model.count === 0 ? "" : player.currentMetaArtist |
3783 | - } |
3784 | - } |
3785 | - |
3786 | - /* Detect cover art swipe */ |
3787 | - MouseArea { |
3788 | - anchors.fill: parent |
3789 | - property string direction: "None" |
3790 | - property real lastX: -1 |
3791 | - |
3792 | - onPressed: lastX = mouse.x |
3793 | - |
3794 | - onReleased: { |
3795 | - var diff = mouse.x - lastX |
3796 | - if (Math.abs(diff) < units.gu(4)) { |
3797 | - return; |
3798 | - } else if (diff < 0) { |
3799 | - player.nextSong() |
3800 | - } else if (diff > 0) { |
3801 | - player.previousSong() |
3802 | - } |
3803 | - } |
3804 | - } |
3805 | - } |
3806 | - |
3807 | - /* Background for progress bar component */ |
3808 | - Rectangle { |
3809 | - id: musicToolbarFullProgressBackground |
3810 | - anchors { |
3811 | - bottom: parent.bottom |
3812 | - left: parent.left |
3813 | - right: parent.right |
3814 | - top: blurredBackground.bottom |
3815 | - } |
3816 | - color: styleMusic.common.black |
3817 | - } |
3818 | - |
3819 | - /* Progress bar component */ |
3820 | - Item { |
3821 | - id: musicToolbarFullProgressContainer |
3822 | - anchors.left: parent.left |
3823 | - anchors.leftMargin: units.gu(3) |
3824 | - anchors.right: parent.right |
3825 | - anchors.rightMargin: units.gu(3) |
3826 | - anchors.top: blurredBackground.bottom |
3827 | - anchors.topMargin: units.gu(1) |
3828 | - height: units.gu(3) |
3829 | - width: parent.width |
3830 | - |
3831 | - /* Position label */ |
3832 | - Label { |
3833 | - id: musicToolbarFullPositionLabel |
3834 | - anchors.top: progressSliderMusic.bottom |
3835 | - anchors.topMargin: units.gu(-2) |
3836 | - anchors.left: parent.left |
3837 | - color: styleMusic.nowPlaying.labelSecondaryColor |
3838 | - fontSize: "small" |
3839 | - height: parent.height |
3840 | - horizontalAlignment: Text.AlignHCenter |
3841 | - text: durationToString(player.position) |
3842 | - verticalAlignment: Text.AlignVCenter |
3843 | - width: units.gu(3) |
3844 | - } |
3845 | - |
3846 | - Slider { |
3847 | - id: progressSliderMusic |
3848 | - anchors.left: parent.left |
3849 | - anchors.right: parent.right |
3850 | - maximumValue: player.duration // load value at startup |
3851 | - objectName: "progressSliderShape" |
3852 | - style: UbuntuBlueSliderStyle {} |
3853 | - value: player.position // load value at startup |
3854 | - |
3855 | - function formatValue(v) { |
3856 | - if (seeking) { // update position label while dragging |
3857 | - musicToolbarFullPositionLabel.text = durationToString(v) |
3858 | - } |
3859 | - |
3860 | - return durationToString(v) |
3861 | - } |
3862 | - |
3863 | - property bool seeking: false |
3864 | - property bool seeked: false |
3865 | - |
3866 | - onSeekingChanged: { |
3867 | - if (seeking === false) { |
3868 | - musicToolbarFullPositionLabel.text = durationToString(player.position) |
3869 | - } |
3870 | - } |
3871 | - |
3872 | - onPressedChanged: { |
3873 | - seeking = pressed |
3874 | - |
3875 | - if (!pressed) { |
3876 | - seeked = true |
3877 | - player.seek(value) |
3878 | - |
3879 | - musicToolbarFullPositionLabel.text = durationToString(value) |
3880 | - } |
3881 | - } |
3882 | - |
3883 | - Connections { |
3884 | - target: player |
3885 | - onPositionChanged: { |
3886 | - // seeked is a workaround for bug 1310706 as the first position after a seek is sometimes invalid (0) |
3887 | - if (progressSliderMusic.seeking === false && !progressSliderMusic.seeked) { |
3888 | - musicToolbarFullPositionLabel.text = durationToString(player.position) |
3889 | - musicToolbarFullDurationLabel.text = durationToString(player.duration) |
3890 | - |
3891 | - progressSliderMusic.value = player.position |
3892 | - progressSliderMusic.maximumValue = player.duration |
3893 | - } |
3894 | - |
3895 | - progressSliderMusic.seeked = false; |
3896 | - } |
3897 | - onStopped: { |
3898 | - musicToolbarFullPositionLabel.text = durationToString(0); |
3899 | - musicToolbarFullDurationLabel.text = durationToString(0); |
3900 | - } |
3901 | - } |
3902 | - } |
3903 | - |
3904 | - /* Duration label */ |
3905 | - Label { |
3906 | - id: musicToolbarFullDurationLabel |
3907 | - anchors.top: progressSliderMusic.bottom |
3908 | - anchors.topMargin: units.gu(-2) |
3909 | - anchors.right: parent.right |
3910 | - color: styleMusic.nowPlaying.labelSecondaryColor |
3911 | - fontSize: "small" |
3912 | - height: parent.height |
3913 | - horizontalAlignment: Text.AlignHCenter |
3914 | - text: durationToString(player.duration) |
3915 | - verticalAlignment: Text.AlignVCenter |
3916 | - width: units.gu(3) |
3917 | - } |
3918 | - } |
3919 | - } |
3920 | - |
3921 | - Loader { |
3922 | - id: queueListLoader |
3923 | - anchors { |
3924 | - fill: parent |
3925 | - } |
3926 | - asynchronous: true |
3927 | - sourceComponent: ListView { |
3928 | - id: queueList |
3929 | - anchors { |
3930 | - bottomMargin: musicToolbarFullContainer.height + units.gu(2) |
3931 | - fill: parent |
3932 | - topMargin: units.gu(2) |
3933 | - } |
3934 | - delegate: queueDelegate |
3935 | - footer: Item { |
3936 | - height: mainView.height - (styleMusic.common.expandHeight + queueList.currentHeight) + units.gu(8) |
3937 | - } |
3938 | - model: trackQueue.model |
3939 | - objectName: "nowPlayingqueueList" |
3940 | - |
3941 | - property int normalHeight: units.gu(6) |
3942 | - property int transitionDuration: 250 // transition length of animations |
3943 | - |
3944 | - onCountChanged: { |
3945 | - customdebug("Queue: Now has: " + queueList.count + " tracks") |
3946 | - } |
3947 | - |
3948 | - // Requirements for ListItemWithActions |
3949 | - property var selectedItems: [] |
3950 | - |
3951 | - signal clearSelection() |
3952 | - signal closeSelection() |
3953 | - signal selectAll() |
3954 | - |
3955 | - onClearSelection: selectedItems = [] |
3956 | - onCloseSelection: { |
3957 | - clearSelection() |
3958 | - state = "normal" |
3959 | - } |
3960 | - onSelectAll: { |
3961 | - var tmp = selectedItems |
3962 | - |
3963 | - for (var i=0; i < model.count; i++) { |
3964 | - if (tmp.indexOf(i) === -1) { |
3965 | - tmp.push(i) |
3966 | - } |
3967 | - } |
3968 | - |
3969 | - selectedItems = tmp |
3970 | - } |
3971 | - onVisibleChanged: { |
3972 | - if (!visible) { |
3973 | - closeSelection() |
3974 | - } |
3975 | - } |
3976 | - |
3977 | - Component.onCompleted: { |
3978 | - // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition |
3979 | - // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration |
3980 | - var scaleFactor = units.gridUnit / 8; |
3981 | - maximumFlickVelocity = maximumFlickVelocity * scaleFactor; |
3982 | - flickDeceleration = flickDeceleration * scaleFactor; |
3983 | - } |
3984 | - |
3985 | - Component { |
3986 | - id: queueDelegate |
3987 | - ListItemWithActions { |
3988 | - id: queueListItem |
3989 | - color: player.currentIndex === index ? "#2c2c34" : styleMusic.mainView.backgroundColor |
3990 | - height: queueList.normalHeight |
3991 | - objectName: "nowPlayingListItem" + index |
3992 | - state: "" |
3993 | - |
3994 | - leftSideAction: Remove { |
3995 | - onTriggered: trackQueue.removeQueueList([index]) |
3996 | - } |
3997 | - multiselectable: true |
3998 | - reorderable: true |
3999 | - rightSideActions: [ |
4000 | - AddToPlaylist{ |
4001 | - |
4002 | - } |
4003 | - ] |
4004 | - |
4005 | - onItemClicked: { |
4006 | - customdebug("File: " + model.filename) // debugger |
4007 | - trackQueueClick(index); // toggle track state |
4008 | - } |
4009 | - onReorder: { |
4010 | - console.debug("Move: ", from, to); |
4011 | - |
4012 | - trackQueue.model.move(from, to, 1); |
4013 | - Library.moveQueueItem(from, to); |
4014 | - |
4015 | - // Maintain currentIndex with current song |
4016 | - if (from === player.currentIndex) { |
4017 | - player.currentIndex = to; |
4018 | - } |
4019 | - else if (from < player.currentIndex && to >= player.currentIndex) { |
4020 | - player.currentIndex -= 1; |
4021 | - } |
4022 | - else if (from > player.currentIndex && to <= player.currentIndex) { |
4023 | - player.currentIndex += 1; |
4024 | - } |
4025 | - |
4026 | - queueIndex = player.currentIndex |
4027 | - } |
4028 | - |
4029 | - Item { |
4030 | - id: trackContainer; |
4031 | - anchors { |
4032 | - fill: parent |
4033 | - } |
4034 | - |
4035 | - NumberAnimation { |
4036 | - id: trackContainerReorderAnimation |
4037 | - target: trackContainer; |
4038 | - property: "anchors.leftMargin"; |
4039 | - duration: queueList.transitionDuration; |
4040 | - to: units.gu(2) |
4041 | - } |
4042 | - |
4043 | - NumberAnimation { |
4044 | - id: trackContainerResetAnimation |
4045 | - target: trackContainer; |
4046 | - property: "anchors.leftMargin"; |
4047 | - duration: queueList.transitionDuration; |
4048 | - to: units.gu(0.5) |
4049 | - } |
4050 | - |
4051 | - MusicRow { |
4052 | - id: musicRow |
4053 | - height: parent.height |
4054 | - column: Column { |
4055 | - Label { |
4056 | - id: trackTitle |
4057 | - color: player.currentIndex === index ? UbuntuColors.blue |
4058 | - : styleMusic.common.music |
4059 | - fontSize: "small" |
4060 | - objectName: "titleLabel" |
4061 | - text: model.title |
4062 | - } |
4063 | - |
4064 | - Label { |
4065 | - id: trackArtist |
4066 | - color: styleMusic.common.subtitle |
4067 | - fontSize: "x-small" |
4068 | - objectName: "artistLabel" |
4069 | - text: model.author |
4070 | - } |
4071 | - } |
4072 | - } |
4073 | - } |
4074 | - } |
4075 | - } |
4076 | - } |
4077 | - visible: isListView |
4078 | - } |
4079 | - |
4080 | - /* Full toolbar */ |
4081 | - Rectangle { |
4082 | - id: musicToolbarFullContainer |
4083 | - anchors.bottom: parent.bottom |
4084 | - color: styleMusic.common.black |
4085 | - height: units.gu(10) |
4086 | - width: parent.width |
4087 | - |
4088 | - /* Repeat button */ |
4089 | - MouseArea { |
4090 | - id: nowPlayingRepeatButton |
4091 | - anchors.right: nowPlayingPreviousButton.left |
4092 | - anchors.rightMargin: units.gu(1) |
4093 | - anchors.verticalCenter: nowPlayingPlayButton.verticalCenter |
4094 | - height: units.gu(6) |
4095 | - opacity: player.repeat && !emptyPage.noMusic ? 1 : .4 |
4096 | - width: height |
4097 | - onClicked: player.repeat = !player.repeat |
4098 | - |
4099 | - Icon { |
4100 | - id: repeatIcon |
4101 | - height: units.gu(3) |
4102 | - width: height |
4103 | - anchors.verticalCenter: parent.verticalCenter |
4104 | - anchors.horizontalCenter: parent.horizontalCenter |
4105 | - color: "white" |
4106 | - name: "media-playlist-repeat" |
4107 | - objectName: "repeatShape" |
4108 | - opacity: player.repeat && !emptyPage.noMusic ? 1 : .4 |
4109 | - } |
4110 | - } |
4111 | - |
4112 | - /* Previous button */ |
4113 | - MouseArea { |
4114 | - id: nowPlayingPreviousButton |
4115 | - anchors.right: nowPlayingPlayButton.left |
4116 | - anchors.rightMargin: units.gu(1) |
4117 | - anchors.verticalCenter: nowPlayingPlayButton.verticalCenter |
4118 | - height: units.gu(6) |
4119 | - opacity: trackQueue.model.count === 0 ? .4 : 1 |
4120 | - width: height |
4121 | - onClicked: player.previousSong() |
4122 | - |
4123 | - Icon { |
4124 | - id: nowPlayingPreviousIndicator |
4125 | - height: units.gu(3) |
4126 | - width: height |
4127 | - anchors.verticalCenter: parent.verticalCenter |
4128 | - anchors.horizontalCenter: parent.horizontalCenter |
4129 | - color: "white" |
4130 | - name: "media-skip-backward" |
4131 | - objectName: "previousShape" |
4132 | - opacity: 1 |
4133 | - } |
4134 | - } |
4135 | - |
4136 | - /* Play/Pause button */ |
4137 | - MouseArea { |
4138 | - id: nowPlayingPlayButton |
4139 | - anchors.centerIn: parent |
4140 | - height: units.gu(10) |
4141 | - width: height |
4142 | - onClicked: player.toggle() |
4143 | - |
4144 | - Icon { |
4145 | - id: nowPlayingPlayIndicator |
4146 | - height: units.gu(6) |
4147 | - width: height |
4148 | - anchors.verticalCenter: parent.verticalCenter |
4149 | - anchors.horizontalCenter: parent.horizontalCenter |
4150 | - opacity: emptyPage.noMusic ? .4 : 1 |
4151 | - color: "white" |
4152 | - name: player.playbackState === MediaPlayer.PlayingState ? "media-playback-pause" : "media-playback-start" |
4153 | - objectName: "playShape" |
4154 | - } |
4155 | - } |
4156 | - |
4157 | - /* Next button */ |
4158 | - MouseArea { |
4159 | - id: nowPlayingNextButton |
4160 | - anchors.left: nowPlayingPlayButton.right |
4161 | - anchors.leftMargin: units.gu(1) |
4162 | - anchors.verticalCenter: nowPlayingPlayButton.verticalCenter |
4163 | - height: units.gu(6) |
4164 | - opacity: trackQueue.model.count === 0 ? .4 : 1 |
4165 | - width: height |
4166 | - onClicked: player.nextSong() |
4167 | - |
4168 | - Icon { |
4169 | - id: nowPlayingNextIndicator |
4170 | - height: units.gu(3) |
4171 | - width: height |
4172 | - anchors.verticalCenter: parent.verticalCenter |
4173 | - anchors.horizontalCenter: parent.horizontalCenter |
4174 | - color: "white" |
4175 | - name: "media-skip-forward" |
4176 | - objectName: "forwardShape" |
4177 | - opacity: 1 |
4178 | - } |
4179 | - } |
4180 | - |
4181 | - /* Shuffle button */ |
4182 | - MouseArea { |
4183 | - id: nowPlayingShuffleButton |
4184 | - anchors.left: nowPlayingNextButton.right |
4185 | - anchors.leftMargin: units.gu(1) |
4186 | - anchors.verticalCenter: nowPlayingPlayButton.verticalCenter |
4187 | - height: units.gu(6) |
4188 | - opacity: player.shuffle && !emptyPage.noMusic ? 1 : .4 |
4189 | - width: height |
4190 | - onClicked: player.shuffle = !player.shuffle |
4191 | - |
4192 | - Icon { |
4193 | - id: shuffleIcon |
4194 | - height: units.gu(3) |
4195 | - width: height |
4196 | - anchors.verticalCenter: parent.verticalCenter |
4197 | - anchors.horizontalCenter: parent.horizontalCenter |
4198 | - color: "white" |
4199 | - name: "media-playlist-shuffle" |
4200 | - objectName: "shuffleShape" |
4201 | - opacity: player.shuffle && !emptyPage.noMusic ? 1 : .4 |
4202 | - } |
4203 | - } |
4204 | - |
4205 | - /* Object which provides the progress bar when in the queue */ |
4206 | - Rectangle { |
4207 | - id: playerControlsProgressBar |
4208 | - anchors { |
4209 | - bottom: parent.bottom |
4210 | - left: parent.left |
4211 | - right: parent.right |
4212 | - } |
4213 | - color: styleMusic.common.black |
4214 | - height: units.gu(0.25) |
4215 | - visible: isListView |
4216 | - |
4217 | - Rectangle { |
4218 | - id: playerControlsProgressBarHint |
4219 | - anchors { |
4220 | - left: parent.left |
4221 | - bottom: parent.bottom |
4222 | - } |
4223 | - color: UbuntuColors.blue |
4224 | - height: parent.height |
4225 | - width: player.duration > 0 ? (player.position / player.duration) * playerControlsProgressBar.width : 0 |
4226 | - |
4227 | - Connections { |
4228 | - target: player |
4229 | - onPositionChanged: { |
4230 | - playerControlsProgressBarHint.width = (player.position / player.duration) * playerControlsProgressBar.width |
4231 | - } |
4232 | - onStopped: { |
4233 | - playerControlsProgressBarHint.width = 0; |
4234 | - } |
4235 | - } |
4236 | - } |
4237 | - } |
4238 | + NowPlaying { |
4239 | + id: view |
4240 | } |
4241 | } |
4242 | |
4243 | === renamed file 'MusicPlaylists.qml' => 'app/ui/Playlists.qml' |
4244 | --- MusicPlaylists.qml 2015-01-21 00:10:33 +0000 |
4245 | +++ app/ui/Playlists.qml 2015-02-10 03:13:44 +0000 |
4246 | @@ -23,8 +23,10 @@ |
4247 | import Ubuntu.Components.Popups 1.0 |
4248 | import QtMultimedia 5.0 |
4249 | import QtQuick.LocalStorage 2.0 |
4250 | -import "playlists.js" as Playlists |
4251 | -import "common" |
4252 | +import "../logic/playlists.js" as Playlists |
4253 | +import "../components" |
4254 | +import "../components/Flickables" |
4255 | +import "../components/HeadState" |
4256 | |
4257 | // page for the playlists |
4258 | MusicPage { |
4259 | @@ -37,25 +39,10 @@ |
4260 | searchResultsCount: playlistModelFilter.count |
4261 | state: "default" |
4262 | states: [ |
4263 | - PageHeadState { |
4264 | - name: "default" |
4265 | - head: playlistsPage.head |
4266 | - actions: [ |
4267 | - Action { |
4268 | - enabled: allSongsModel.count > 0 |
4269 | - objectName: "newPlaylistButton" |
4270 | - iconName: "add" |
4271 | - onTriggered: { |
4272 | - customdebug("New playlist.") |
4273 | - PopupUtils.open(newPlaylistDialog, mainView) |
4274 | - } |
4275 | - }, |
4276 | - Action { |
4277 | - enabled: playlistModel.model.count > 0 && allSongsModel.count > 0 |
4278 | - iconName: "search" |
4279 | - onTriggered: playlistsPage.state = "search" |
4280 | - } |
4281 | - ] |
4282 | + PlaylistsHeadState { |
4283 | + newPlaylistEnabled: allSongsModel.count > 0 |
4284 | + searchEnabled: playlistModel.model.count > 0 && allSongsModel.count > 0 |
4285 | + thisPage: playlistsPage |
4286 | }, |
4287 | SearchHeadState { |
4288 | id: searchHeader |
4289 | @@ -99,25 +86,30 @@ |
4290 | onClicked: { |
4291 | albumTracksModel.filterPlaylistTracks(model.name) |
4292 | |
4293 | - var comp = Qt.createComponent("common/SongsPage.qml") |
4294 | - var songsPage = comp.createObject(mainPageStack, |
4295 | - { |
4296 | - "album": undefined, |
4297 | - "covers": coverSources, |
4298 | - "isAlbum": false, |
4299 | - "genre": undefined, |
4300 | - "page": playlistsPage, |
4301 | - "title": i18n.tr("Playlist"), |
4302 | - "line1": i18n.tr("Playlist"), |
4303 | - "line2": model.name, |
4304 | - }); |
4305 | - |
4306 | - if (songsPage == null) { // Error Handling |
4307 | - console.log("Error creating object"); |
4308 | - } |
4309 | - |
4310 | - mainPageStack.push(songsPage) |
4311 | + mainPageStack.push(Qt.resolvedUrl("SongsView.qml"), |
4312 | + { |
4313 | + "album": undefined, |
4314 | + "covers": coverSources, |
4315 | + "isAlbum": false, |
4316 | + "genre": undefined, |
4317 | + "page": playlistsPage, |
4318 | + "title": i18n.tr("Playlist"), |
4319 | + "line1": i18n.tr("Playlist"), |
4320 | + "line2": model.name, |
4321 | + }) |
4322 | } |
4323 | } |
4324 | } |
4325 | + |
4326 | + // Overlay to show when no playlists are on the device |
4327 | + Loader { |
4328 | + anchors { |
4329 | + fill: parent |
4330 | + topMargin: -playlistsPage.header.height |
4331 | + } |
4332 | + active: playlistModel.model.count === 0 && playlistModel.workerComplete |
4333 | + asynchronous: true |
4334 | + source: "../components/PlaylistsEmptyState.qml" |
4335 | + visible: active |
4336 | + } |
4337 | } |
4338 | |
4339 | === renamed file 'MusicStart.qml' => 'app/ui/Recent.qml' |
4340 | --- MusicStart.qml 2014-11-10 22:20:24 +0000 |
4341 | +++ app/ui/Recent.qml 2015-02-10 03:13:44 +0000 |
4342 | @@ -25,12 +25,14 @@ |
4343 | import Ubuntu.Thumbnailer 0.1 |
4344 | import QtMultimedia 5.0 |
4345 | import QtQuick.LocalStorage 2.0 |
4346 | -import "meta-database.js" as Library |
4347 | -import "playlists.js" as Playlists |
4348 | -import "common" |
4349 | +import "../logic/meta-database.js" as Library |
4350 | +import "../logic/playlists.js" as Playlists |
4351 | +import "../components" |
4352 | +import "../components/Flickables" |
4353 | |
4354 | MusicPage { |
4355 | - id: mainpage |
4356 | + id: recentPage |
4357 | + objectName: "recentPage" |
4358 | title: i18n.tr("Recent") |
4359 | |
4360 | property bool changed: false |
4361 | @@ -83,25 +85,18 @@ |
4362 | albumTracksModel.filterPlaylistTracks(model.data) |
4363 | } |
4364 | |
4365 | - var comp = Qt.createComponent("common/SongsPage.qml") |
4366 | - var songsPage = comp.createObject(mainPageStack, |
4367 | - { |
4368 | - "album": model.type !== "playlist" ? model.data : undefined, |
4369 | - "artist": model.type !== "playlist" ? recentAlbumSongs.get(0, SongsModel.RoleModelData).artist : undefined, |
4370 | - "covers": coverSources, |
4371 | - "isAlbum": (model.type === "album"), |
4372 | - "genre": undefined, |
4373 | - "page": mainpage, |
4374 | - "title": (model.type === "album") ? i18n.tr("Album") : i18n.tr("Playlist"), |
4375 | - "line1": secondaryText, |
4376 | - "line2": primaryText, |
4377 | - }); |
4378 | - |
4379 | - if (songsPage == null) { // Error Handling |
4380 | - console.log("Error creating object"); |
4381 | - } |
4382 | - |
4383 | - mainPageStack.push(songsPage) |
4384 | + mainPageStack.push(Qt.resolvedUrl("SongsView.qml"), |
4385 | + { |
4386 | + "album": model.type !== "playlist" ? model.data : undefined, |
4387 | + "artist": model.type !== "playlist" ? recentAlbumSongs.get(0, SongsModel.RoleModelData).artist : undefined, |
4388 | + "covers": coverSources, |
4389 | + "isAlbum": (model.type === "album"), |
4390 | + "genre": undefined, |
4391 | + "page": recentPage, |
4392 | + "title": (model.type === "album") ? i18n.tr("Album") : i18n.tr("Playlist"), |
4393 | + "line1": secondaryText, |
4394 | + "line2": primaryText, |
4395 | + }) |
4396 | } |
4397 | } |
4398 | } |
4399 | |
4400 | === renamed file 'MusicTracks.qml' => 'app/ui/Songs.qml' |
4401 | --- MusicTracks.qml 2015-01-26 03:20:37 +0000 |
4402 | +++ app/ui/Songs.qml 2015-02-10 03:13:44 +0000 |
4403 | @@ -23,99 +23,41 @@ |
4404 | import Ubuntu.Thumbnailer 0.1 |
4405 | import QtMultimedia 5.0 |
4406 | import QtQuick.LocalStorage 2.0 |
4407 | -import "playlists.js" as Playlists |
4408 | -import "common" |
4409 | -import "common/ListItemActions" |
4410 | +import "../logic/playlists.js" as Playlists |
4411 | +import "../components" |
4412 | +import "../components/Flickables" |
4413 | +import "../components/HeadState" |
4414 | +import "../components/ListItemActions" |
4415 | |
4416 | |
4417 | MusicPage { |
4418 | - id: tracksPage |
4419 | - objectName: "tracksPage" |
4420 | + id: songsPage |
4421 | + objectName: "songsPage" |
4422 | title: i18n.tr("Songs") |
4423 | searchable: true |
4424 | searchResultsCount: songsModelFilter.count |
4425 | state: "default" |
4426 | states: [ |
4427 | - PageHeadState { |
4428 | - name: "default" |
4429 | - head: tracksPage.head |
4430 | - actions: Action { |
4431 | - iconName: "search" |
4432 | - onTriggered: tracksPage.state = "search" |
4433 | - } |
4434 | + SearchableHeadState { |
4435 | + thisPage: songsPage |
4436 | + searchEnabled: songsModelFilter.count > 0 |
4437 | }, |
4438 | - PageHeadState { |
4439 | - id: selectionState |
4440 | - name: "selection" |
4441 | - backAction: Action { |
4442 | - text: i18n.tr("Cancel selection") |
4443 | - iconName: "back" |
4444 | - onTriggered: { |
4445 | - tracklist.clearSelection() |
4446 | - tracklist.state = "normal" |
4447 | - } |
4448 | - } |
4449 | - head: tracksPage.head |
4450 | - actions: [ |
4451 | - Action { |
4452 | - iconName: "select" |
4453 | - text: i18n.tr("Select All") |
4454 | - onTriggered: { |
4455 | - if (tracklist.selectedItems.length === tracklist.model.count) { |
4456 | - tracklist.clearSelection() |
4457 | - } else { |
4458 | - tracklist.selectAll() |
4459 | - } |
4460 | - } |
4461 | - }, |
4462 | - Action { |
4463 | - enabled: tracklist.selectedItems.length !== 0 |
4464 | - iconName: "add-to-playlist" |
4465 | - text: i18n.tr("Add to playlist") |
4466 | - onTriggered: { |
4467 | - var items = [] |
4468 | - |
4469 | - for (var i=0; i < tracklist.selectedItems.length; i++) { |
4470 | - items.push(makeDict(tracklist.model.get(tracklist.selectedItems[i], tracklist.model.RoleModelData))); |
4471 | - } |
4472 | - |
4473 | - var comp = Qt.createComponent("MusicaddtoPlaylist.qml") |
4474 | - var addToPlaylist = comp.createObject(mainPageStack, {"chosenElements": items}); |
4475 | - |
4476 | - if (addToPlaylist == null) { // Error Handling |
4477 | - console.log("Error creating object"); |
4478 | - } |
4479 | - |
4480 | - mainPageStack.push(addToPlaylist) |
4481 | - |
4482 | - tracklist.closeSelection() |
4483 | - } |
4484 | - }, |
4485 | - Action { |
4486 | - enabled: tracklist.selectedItems.length > 0 |
4487 | - iconName: "add" |
4488 | - text: i18n.tr("Add to queue") |
4489 | - onTriggered: { |
4490 | - var items = [] |
4491 | - |
4492 | - for (var i=0; i < tracklist.selectedItems.length; i++) { |
4493 | - items.push(tracklist.model.get(tracklist.selectedItems[i], tracklist.model.RoleModelData)); |
4494 | - } |
4495 | - |
4496 | - trackQueue.appendList(items) |
4497 | - |
4498 | - tracklist.closeSelection() |
4499 | - } |
4500 | - } |
4501 | - ] |
4502 | + MultiSelectHeadState { |
4503 | + listview: tracklist |
4504 | + thisPage: songsPage |
4505 | }, |
4506 | SearchHeadState { |
4507 | id: searchHeader |
4508 | - thisPage: tracksPage |
4509 | + thisPage: songsPage |
4510 | } |
4511 | ] |
4512 | |
4513 | - ListView { |
4514 | + // Hack for autopilot otherwise Albums appears as MusicPage |
4515 | + // due to bug 1341671 it is required that there is a property so that |
4516 | + // qml doesn't optimise using the parent type |
4517 | + property bool bug1341671workaround: true |
4518 | + |
4519 | + MultiSelectListView { |
4520 | id: tracklist |
4521 | anchors { |
4522 | bottomMargin: units.gu(2) |
4523 | @@ -139,49 +81,12 @@ |
4524 | filterCaseSensitivity: Qt.CaseInsensitive |
4525 | } |
4526 | |
4527 | - Component.onCompleted: { |
4528 | - // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition |
4529 | - // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration |
4530 | - var scaleFactor = units.gridUnit / 8; |
4531 | - maximumFlickVelocity = maximumFlickVelocity * scaleFactor; |
4532 | - flickDeceleration = flickDeceleration * scaleFactor; |
4533 | - } |
4534 | - |
4535 | - // Requirements for ListItemWithActions |
4536 | - property var selectedItems: [] |
4537 | - |
4538 | - signal clearSelection() |
4539 | - signal closeSelection() |
4540 | - signal selectAll() |
4541 | - |
4542 | - onClearSelection: selectedItems = [] |
4543 | - onCloseSelection: { |
4544 | - clearSelection() |
4545 | - state = "normal" |
4546 | - } |
4547 | onStateChanged: { |
4548 | if (state === "multiselectable") { |
4549 | - tracksPage.state = "selection" |
4550 | + songsPage.state = "selection" |
4551 | } else { |
4552 | searchHeader.query = "" // force query back to default |
4553 | - tracksPage.state = "default" |
4554 | - } |
4555 | - } |
4556 | - |
4557 | - onSelectAll: { |
4558 | - var tmp = selectedItems |
4559 | - |
4560 | - for (var i=0; i < model.count; i++) { |
4561 | - if (tmp.indexOf(i) === -1) { |
4562 | - tmp.push(i) |
4563 | - } |
4564 | - } |
4565 | - |
4566 | - selectedItems = tmp |
4567 | - } |
4568 | - onVisibleChanged: { |
4569 | - if (!visible) { |
4570 | - closeSelection() |
4571 | + songsPage.state = "default" |
4572 | } |
4573 | } |
4574 | |
4575 | @@ -204,7 +109,7 @@ |
4576 | ] |
4577 | |
4578 | onItemClicked: { |
4579 | - if (tracksPage.state === "search") { // only play single track when searching |
4580 | + if (songsPage.state === "search") { // only play single track when searching |
4581 | trackQueue.clear() |
4582 | trackQueue.append(songsModelFilter.get(index)) |
4583 | trackQueueClick(0) |
4584 | |
4585 | === renamed file 'common/SongsPage.qml' => 'app/ui/SongsView.qml' |
4586 | --- common/SongsPage.qml 2015-01-30 01:23:39 +0000 |
4587 | +++ app/ui/SongsView.qml 2015-02-10 03:13:44 +0000 |
4588 | @@ -24,9 +24,13 @@ |
4589 | import Ubuntu.MediaScanner 0.1 |
4590 | import Ubuntu.Thumbnailer 0.1 |
4591 | import QtQuick.LocalStorage 2.0 |
4592 | -import "../meta-database.js" as Library |
4593 | -import "../playlists.js" as Playlists |
4594 | -import "ListItemActions" |
4595 | +import "../logic/meta-database.js" as Library |
4596 | +import "../logic/playlists.js" as Playlists |
4597 | +import "../components" |
4598 | +import "../components/Flickables" |
4599 | +import "../components/HeadState" |
4600 | +import "../components/ListItemActions" |
4601 | +import "../components/ViewButton" |
4602 | |
4603 | MusicPage { |
4604 | id: songStackPage |
4605 | @@ -121,7 +125,7 @@ |
4606 | objectName: "editPlaylist" |
4607 | iconName: "edit" |
4608 | onTriggered: { |
4609 | - var dialog = PopupUtils.open(editPlaylistDialog, mainView) |
4610 | + var dialog = PopupUtils.open(Qt.resolvedUrl("../components/Dialog/EditPlaylistDialog.qml"), mainView) |
4611 | dialog.oldPlaylistName = line2 |
4612 | } |
4613 | }, |
4614 | @@ -129,7 +133,7 @@ |
4615 | objectName: "deletePlaylist" |
4616 | iconName: "delete" |
4617 | onTriggered: { |
4618 | - var dialog = PopupUtils.open(removePlaylistDialog, mainView) |
4619 | + var dialog = PopupUtils.open(Qt.resolvedUrl("../components/Dialog/RemovePlaylistDialog.qml"), mainView) |
4620 | dialog.oldPlaylistName = line2 |
4621 | } |
4622 | } |
4623 | @@ -140,100 +144,29 @@ |
4624 | actions: playlistState.actions |
4625 | } |
4626 | }, |
4627 | - PageHeadState { |
4628 | - id: selectionState |
4629 | - name: "selection" |
4630 | - backAction: Action { |
4631 | - text: i18n.tr("Cancel selection") |
4632 | - iconName: "back" |
4633 | - onTriggered: { |
4634 | - albumtrackslist.clearSelection() |
4635 | - albumtrackslist.state = "normal" |
4636 | - } |
4637 | - } |
4638 | - actions: [ |
4639 | - Action { |
4640 | - iconName: "select" |
4641 | - text: i18n.tr("Select All") |
4642 | - onTriggered: { |
4643 | - if (albumtrackslist.selectedItems.length === albumtrackslist.model.count) { |
4644 | - albumtrackslist.clearSelection() |
4645 | - } else { |
4646 | - albumtrackslist.selectAll() |
4647 | - } |
4648 | - } |
4649 | - }, |
4650 | - Action { |
4651 | - enabled: albumtrackslist.selectedItems.length > 0 |
4652 | - iconName: "add-to-playlist" |
4653 | - text: i18n.tr("Add to playlist") |
4654 | - onTriggered: { |
4655 | - var items = [] |
4656 | - |
4657 | - for (var i=0; i < albumtrackslist.selectedItems.length; i++) { |
4658 | - items.push(makeDict(albumtrackslist.model.get(albumtrackslist.selectedItems[i], albumtrackslist.model.RoleModelData))); |
4659 | - } |
4660 | - |
4661 | - var comp = Qt.createComponent("../MusicaddtoPlaylist.qml") |
4662 | - var addToPlaylist = comp.createObject(mainPageStack, {"chosenElements": items, "page": songStackPage}); |
4663 | - |
4664 | - if (addToPlaylist == null) { // Error Handling |
4665 | - console.log("Error creating object"); |
4666 | - } |
4667 | - |
4668 | - mainPageStack.push(addToPlaylist) |
4669 | - |
4670 | - albumtrackslist.closeSelection() |
4671 | - } |
4672 | - }, |
4673 | - Action { |
4674 | - enabled: albumtrackslist.selectedItems.length > 0 |
4675 | - iconName: "add" |
4676 | - text: i18n.tr("Add to queue") |
4677 | - onTriggered: { |
4678 | - var items = [] |
4679 | - |
4680 | - for (var i=0; i < albumtrackslist.selectedItems.length; i++) { |
4681 | - items.push(albumtrackslist.model.get(albumtrackslist.selectedItems[i], albumtrackslist.model.RoleModelData)); |
4682 | - } |
4683 | - |
4684 | - trackQueue.appendList(items) |
4685 | - |
4686 | - albumtrackslist.closeSelection() |
4687 | - } |
4688 | - }, |
4689 | - Action { |
4690 | - enabled: albumtrackslist.selectedItems.length > 0 |
4691 | - iconName: "delete" |
4692 | - text: i18n.tr("Delete") |
4693 | - visible: songStackPage.line1 === i18n.tr("Playlist") |
4694 | - onTriggered: { |
4695 | - for (var i=0; i < albumtrackslist.selectedItems.length; i++) { |
4696 | - Playlists.removeFromPlaylist(songStackPage.line2, albumtrackslist.selectedItems[i]) |
4697 | - |
4698 | - // Update indexes as an index has been removed |
4699 | - for (var j=i + 1; j < albumtrackslist.selectedItems.length; j++) { |
4700 | - if (albumtrackslist.selectedItems[j] > albumtrackslist.selectedItems[i]) { |
4701 | - albumtrackslist.selectedItems[j]--; |
4702 | - } |
4703 | - } |
4704 | - } |
4705 | - |
4706 | - albumtrackslist.closeSelection() |
4707 | - |
4708 | - playlistChangedHelper() // update recent/playlist models |
4709 | - |
4710 | - albumTracksModel.filterPlaylistTracks(songStackPage.line2) |
4711 | - |
4712 | - // refresh cover art |
4713 | - songStackPage.covers = Playlists.getPlaylistCovers(songStackPage.line2) |
4714 | - } |
4715 | - } |
4716 | - ] |
4717 | - PropertyChanges { |
4718 | - target: songStackPage.head |
4719 | - backAction: selectionState.backAction |
4720 | - actions: selectionState.actions |
4721 | + MultiSelectHeadState { |
4722 | + listview: albumtrackslist |
4723 | + removable: songStackPage.line1 === i18n.tr("Playlist") |
4724 | + thisPage: songStackPage |
4725 | + |
4726 | + onRemoved: { |
4727 | + for (var i=0; i < selectedItems.length; i++) { |
4728 | + Playlists.removeFromPlaylist(songStackPage.line2, selectedItems[i]) |
4729 | + |
4730 | + // Update indexes as an index has been removed |
4731 | + for (var j=i + 1; j < selectedItems.length; j++) { |
4732 | + if (selectedItems[j] > selectedItems[i]) { |
4733 | + selectedItems[j]--; |
4734 | + } |
4735 | + } |
4736 | + } |
4737 | + |
4738 | + playlistChangedHelper() // update recent/playlist models |
4739 | + |
4740 | + albumTracksModel.filterPlaylistTracks(songStackPage.line2) |
4741 | + |
4742 | + // refresh cover art |
4743 | + songStackPage.covers = Playlists.getPlaylistCovers(songStackPage.line2) |
4744 | } |
4745 | } |
4746 | ] |
4747 | @@ -243,12 +176,12 @@ |
4748 | store: musicStore |
4749 | onStatusChanged: { |
4750 | if (songsModel.status === SongsModel.Ready && loaded && songsModel.count === 0) { |
4751 | - musicToolbar.popPage(songStackPage) |
4752 | + mainPageStack.popPage(songStackPage) |
4753 | } |
4754 | } |
4755 | } |
4756 | |
4757 | - ListView { |
4758 | + MultiSelectListView { |
4759 | id: albumtrackslist |
4760 | anchors { |
4761 | fill: parent |
4762 | @@ -258,67 +191,12 @@ |
4763 | objectName: "songspage-listview" |
4764 | width: parent.width |
4765 | |
4766 | - // Requirements for ListItemWithActions |
4767 | - property var selectedItems: [] |
4768 | - |
4769 | - signal clearSelection() |
4770 | - signal closeSelection() |
4771 | - signal selectAll() |
4772 | - |
4773 | - onClearSelection: selectedItems = [] |
4774 | - onCloseSelection: { |
4775 | - clearSelection() |
4776 | - state = "normal" |
4777 | - } |
4778 | - onSelectAll: { |
4779 | - var tmp = selectedItems |
4780 | - |
4781 | - for (var i=0; i < model.count; i++) { |
4782 | - if (tmp.indexOf(i) === -1) { |
4783 | - tmp.push(i) |
4784 | - } |
4785 | - } |
4786 | - |
4787 | - selectedItems = tmp |
4788 | - } |
4789 | - onVisibleChanged: { |
4790 | - if (!visible) { |
4791 | - closeSelection() |
4792 | - } |
4793 | - } |
4794 | - |
4795 | - Component.onCompleted: { |
4796 | - // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition |
4797 | - // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration |
4798 | - var scaleFactor = units.gridUnit / 8; |
4799 | - maximumFlickVelocity = maximumFlickVelocity * scaleFactor; |
4800 | - flickDeceleration = flickDeceleration * scaleFactor; |
4801 | - } |
4802 | - |
4803 | header: BlurredHeader { |
4804 | rightColumn: Column { |
4805 | spacing: units.gu(2) |
4806 | - Button { |
4807 | - id: shuffleRow |
4808 | - height: units.gu(4) |
4809 | - strokeColor: UbuntuColors.green |
4810 | - width: units.gu(15) |
4811 | - Text { |
4812 | - anchors { |
4813 | - centerIn: parent |
4814 | - } |
4815 | - color: "white" |
4816 | - elide: Text.ElideRight |
4817 | - height: parent.height |
4818 | - horizontalAlignment: Text.AlignHCenter |
4819 | - // TRANSLATORS: this appears in a button with limited space (around 14 characters) |
4820 | - text: i18n.tr("Shuffle") |
4821 | - verticalAlignment: Text.AlignVCenter |
4822 | - width: parent.width - units.gu(2) |
4823 | - } |
4824 | + ShuffleButton { |
4825 | + model: albumtrackslist.model |
4826 | onClicked: { |
4827 | - shuffleModel(albumtrackslist.model) // play track |
4828 | - |
4829 | if (isAlbum && songStackPage.line1 !== i18n.tr("Genre")) { |
4830 | Library.addRecent(albumtrackslist.model.get(0, albumtrackslist.model.RoleModelData).album, "album") |
4831 | } else if (songStackPage.line1 === i18n.tr("Playlist")) { |
4832 | @@ -330,36 +208,12 @@ |
4833 | recentModel.filterRecent() |
4834 | } |
4835 | } |
4836 | - Button { |
4837 | - id: queueAllRow |
4838 | - height: units.gu(4) |
4839 | - strokeColor: UbuntuColors.green |
4840 | - width: units.gu(15) |
4841 | - Text { |
4842 | - anchors { |
4843 | - centerIn: parent |
4844 | - } |
4845 | - color: "white" |
4846 | - elide: Text.ElideRight |
4847 | - height: parent.height |
4848 | - horizontalAlignment: Text.AlignHCenter |
4849 | - // TRANSLATORS: this appears in a button with limited space (around 14 characters) |
4850 | - text: i18n.tr("Queue all") |
4851 | - verticalAlignment: Text.AlignVCenter |
4852 | - width: parent.width - units.gu(2) |
4853 | - } |
4854 | - onClicked: addQueueFromModel(albumtrackslist.model) |
4855 | + QueueAllButton { |
4856 | + model: albumtrackslist.model |
4857 | } |
4858 | - Button { |
4859 | - id: playRow |
4860 | - color: UbuntuColors.green |
4861 | - height: units.gu(4) |
4862 | - // TRANSLATORS: this appears in a button with limited space (around 14 characters) |
4863 | - text: i18n.tr("Play all") |
4864 | - width: units.gu(15) |
4865 | + PlayAllButton { |
4866 | + model: albumtrackslist.model |
4867 | onClicked: { |
4868 | - trackClicked(albumtrackslist.model, 0) // play track |
4869 | - |
4870 | if (isAlbum && songStackPage.line1 !== i18n.tr("Genre")) { |
4871 | Library.addRecent(albumtrackslist.model.get(0, albumtrackslist.model.RoleModelData).album, "album") |
4872 | } else if (songStackPage.line1 === i18n.tr("Playlist")) { |
4873 | @@ -528,101 +382,4 @@ |
4874 | } |
4875 | |
4876 | Component.onCompleted: loaded = true |
4877 | - |
4878 | - // Edit name of playlist dialog |
4879 | - Component { |
4880 | - id: editPlaylistDialog |
4881 | - Dialog { |
4882 | - id: dialogEditPlaylist |
4883 | - // TRANSLATORS: this is a title of a dialog with a prompt to rename a playlist |
4884 | - title: i18n.tr("Rename playlist") |
4885 | - |
4886 | - property string oldPlaylistName: "" |
4887 | - |
4888 | - TextField { |
4889 | - id: playlistName |
4890 | - inputMethodHints: Qt.ImhNoPredictiveText |
4891 | - placeholderText: i18n.tr("Enter playlist name") |
4892 | - } |
4893 | - Label { |
4894 | - id: editplaylistoutput |
4895 | - color: "red" |
4896 | - visible: false |
4897 | - } |
4898 | - |
4899 | - Button { |
4900 | - text: i18n.tr("Change") |
4901 | - color: styleMusic.dialog.confirmButtonColor |
4902 | - onClicked: { |
4903 | - editplaylistoutput.visible = true |
4904 | - |
4905 | - if (playlistName.text.length > 0) { // make sure something is acually inputed |
4906 | - console.debug("Debug: User changed name from "+oldPlaylistName+" to "+playlistName.text) |
4907 | - |
4908 | - if (Playlists.renamePlaylist(oldPlaylistName, playlistName.text) === true) { |
4909 | - |
4910 | - if (Library.recentContainsPlaylist(oldPlaylistName)) { |
4911 | - Library.recentRenamePlaylist(oldPlaylistName, playlistName.text) |
4912 | - } |
4913 | - |
4914 | - line2 = playlistName.text |
4915 | - |
4916 | - playlistChangedHelper() // update recent/playlist models |
4917 | - |
4918 | - PopupUtils.close(dialogEditPlaylist) |
4919 | - } |
4920 | - else { |
4921 | - editplaylistoutput.text = i18n.tr("Playlist already exists") |
4922 | - } |
4923 | - } |
4924 | - else { |
4925 | - editplaylistoutput.text = i18n.tr("Please type in a name.") |
4926 | - } |
4927 | - } |
4928 | - } |
4929 | - Button { |
4930 | - text: i18n.tr("Cancel") |
4931 | - color: styleMusic.dialog.cancelButtonColor |
4932 | - onClicked: PopupUtils.close(dialogEditPlaylist) |
4933 | - } |
4934 | - } |
4935 | - } |
4936 | - |
4937 | - // Remove playlist dialog |
4938 | - Component { |
4939 | - id: removePlaylistDialog |
4940 | - Dialog { |
4941 | - id: dialogRemovePlaylist |
4942 | - // TRANSLATORS: this is a title of a dialog with a prompt to delete a playlist |
4943 | - title: i18n.tr("Permanently delete playlist?") |
4944 | - text: "("+i18n.tr("This cannot be undone")+")" |
4945 | - |
4946 | - property string oldPlaylistName |
4947 | - |
4948 | - Button { |
4949 | - text: i18n.tr("Remove") |
4950 | - color: styleMusic.dialog.confirmRemoveButtonColor |
4951 | - onClicked: { |
4952 | - // removing playlist |
4953 | - Playlists.removePlaylist(dialogRemovePlaylist.oldPlaylistName) |
4954 | - |
4955 | - if (Library.recentContainsPlaylist(dialogRemovePlaylist.oldPlaylistName)) { |
4956 | - Library.recentRemovePlaylist(dialogRemovePlaylist.oldPlaylistName) |
4957 | - } |
4958 | - |
4959 | - playlistChangedHelper(true) // update recent/playlist models |
4960 | - |
4961 | - songStackPage.page = undefined |
4962 | - PopupUtils.close(dialogRemovePlaylist) |
4963 | - |
4964 | - musicToolbar.goBack() |
4965 | - } |
4966 | - } |
4967 | - Button { |
4968 | - text: i18n.tr("Cancel") |
4969 | - color: styleMusic.dialog.cancelButtonColor |
4970 | - onClicked: PopupUtils.close(dialogRemovePlaylist) |
4971 | - } |
4972 | - } |
4973 | - } |
4974 | } |
4975 | |
4976 | === renamed file 'click/apparmor.json' => 'apparmor.json' |
4977 | === removed directory 'click' |
4978 | === removed file 'click/CMakeLists.txt' |
4979 | --- click/CMakeLists.txt 2014-07-01 23:13:50 +0000 |
4980 | +++ click/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
4981 | @@ -1,15 +0,0 @@ |
4982 | -if(CLICK_MODE) |
4983 | - if(NOT BZR_REVNO) |
4984 | - set(BZR_REVNO "latest") |
4985 | - endif(NOT BZR_REVNO) |
4986 | - configure_file(manifest.json.in ${CMAKE_CURRENT_BINARY_DIR}/manifest.json) |
4987 | - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json apparmor.json |
4988 | - music-app-content.json DESTINATION ${CMAKE_INSTALL_PREFIX}) |
4989 | -endif(CLICK_MODE) |
4990 | - |
4991 | -# make the click files visible on qtcreator |
4992 | -file(GLOB CLICK_FILES |
4993 | - RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} |
4994 | - *.json *.json.in) |
4995 | - |
4996 | -add_custom_target(com_ubuntu_music_CLICKFiles ALL SOURCES ${CLICK_FILES}) |
4997 | |
4998 | === renamed file 'click/manifest.json.in' => 'manifest.json.in' |
4999 | === renamed file 'click/music-app-content.json' => 'music-app-content.json' |
5000 | === modified file 'tests/autopilot/music_app/__init__.py' |