ubuntu-touch-coreapps-reviewers team mailing list archive
-
ubuntu-touch-coreapps-reviewers team
-
Mailing list archive
-
Message #08191
[Merge] lp:~ahayzen/music-app/convergence-tabs-with-sidebar-01 into lp:music-app
Andrew Hayzen has proposed merging lp:~ahayzen/music-app/convergence-tabs-with-sidebar-01 into lp:music-app.
Commit message:
* Implement convergent mode with now playing and queue as a sidebar
Requested reviews:
Music App Developers (music-app-dev)
For more details, see:
https://code.launchpad.net/~ahayzen/music-app/convergence-tabs-with-sidebar-01/+merge/286127
* Implement convergent mode with now playing and queue as a sidebar
Known Issues:
* If in portrait then switch to landscape and back to portrait, old tabbar can be seen underneath (has a transparent bg) but disappears when you push to the stack. I need to create a mini-app and report as a bug to the UITK.
* When tapping search in the header it doesn't focus the TextField until you tap
* Styling on sidebar needs finishing to match design
JUST TO GET DIFF FOR DEMO
--
Your team Music App Developers is requested to review the proposed merge of lp:~ahayzen/music-app/convergence-tabs-with-sidebar-01 into lp:music-app.
=== modified file 'app/components/Flickables/MusicGridView.qml'
--- app/components/Flickables/MusicGridView.qml 2016-01-12 00:30:08 +0000
+++ app/components/Flickables/MusicGridView.qml 2016-02-16 03:14:14 +0000
@@ -25,16 +25,6 @@
fill: parent
leftMargin: units.gu(1)
rightMargin: units.gu(1)
- // FIXME: workaround until pad.lv/1531016 (gridview juddery) is fixed
- // due to anchors.fill: parent not being used when the header is locked
- // an extra margin is needed
- topMargin: {
- if (parent.head.locked) {
- units.gu(6.125) * 2 + units.gu(2) // FIXME: 6.125 is header.height
- } else {
- units.gu(6.125) + units.gu(2) // FIXME: 6.125 is header.height
- }
- }
}
cellHeight: cellSize + heightOffset
cellWidth: cellSize + widthOffset
=== modified file 'app/components/HeadState/SearchHeadState.qml'
--- app/components/HeadState/SearchHeadState.qml 2015-11-17 01:59:32 +0000
+++ app/components/HeadState/SearchHeadState.qml 2016-02-16 03:14:14 +0000
@@ -19,22 +19,23 @@
import QtQuick 2.4
import Ubuntu.Components 1.3
-PageHeadState {
+State {
id: headerState
name: "search"
- head: thisPage.head
- backAction: Action {
+ property list<Action> actions
+ property Action backAction: Action {
id: leaveSearchAction
text: "back"
iconName: "back"
onTriggered: thisPage.state = "default"
}
- contents: TextField {
+ property Item contents: TextField {
id: searchField
anchors {
left: parent ? parent.left : undefined
right: parent ? parent.right : undefined
rightMargin: units.gu(2)
+ verticalCenter: parent ? parent.verticalCenter : undefined
}
color: styleMusic.common.black
hasClearButton: true
@@ -55,12 +56,14 @@
onStateChanged: { // ensure the search is reset (eg pressing Esc)
if (state === "default") {
searchField.text = ""
+ } else if (state === headerState.name) {
+ searchField.forceActiveFocus() // FIXME: doesn't work
}
// FIXME: Workaround for pad.lv/1514143 (keyboard show/hide on view moving)
// by locking the header and forcing a topMargin of page to the header height
- headerState.head.locked = state === headerState.name;
- thisPage.anchors.topMargin = state === headerState.name ? units.gu(6.125) : 0 // FIXME: 6.125 is header.height
+ thisPage.head.locked = state === headerState.name;
+ //thisPage.anchors.topMargin = state === headerState.name ? units.gu(6.125) : 0 // FIXME: 6.125 is header.height
}
onVisibleChanged: {
@@ -76,4 +79,19 @@
property Page thisPage
property alias query: searchField.text
+
+ PropertyChanges {
+ target: thisPage.header
+ contents: headerState.contents
+ }
+
+ PropertyChanges {
+ target: thisPage.header.leadingActionBar
+ actions: headerState.backAction
+ }
+
+ PropertyChanges {
+ target: thisPage.header.trailingActionBar
+ actions: headerState.actions
+ }
}
=== modified file 'app/components/MusicPage.qml'
--- app/components/MusicPage.qml 2015-10-23 03:08:43 +0000
+++ app/components/MusicPage.qml 2016-02-16 03:14:14 +0000
@@ -29,6 +29,59 @@
bottomMargin: musicToolbar.visible ? musicToolbar.height : 0
fill: parent
}
+ head { // hide default header
+ locked: true
+ visible: false
+ }
+ header: PageHeader {
+ id: pageHeader
+ contents: thisPage.head.contents
+ flickable: thisPage.flickable
+ title: thisPage.title
+ leadingActionBar {
+ actions: {
+ if (thisPage.head.backAction !== null) {
+ thisPage.head.backAction
+ } else if (mainPageStack.currentPage === tabs) {
+ tabs.tabActions
+ } else if (mainPageStack.depth > 1) {
+ backActionComponent
+ } else {
+ null
+ }
+ }
+ }
+ sections {
+ model: thisPage.head.sections.model
+ selectedIndex: thisPage.head.sections.selectedIndex
+ }
+ trailingActionBar {
+ actions: thisPage.head.actions
+ }
+
+// Binding {
+// target: pageHeader.leadingActionBar
+// property: "actions"
+// value: thisPage.head.backAction
+// }
+
+ Binding {
+ target: thisPage.head.sections
+ property: "selectedIndex"
+ value: pageHeader.sections.selectedIndex
+ }
+
+ Action {
+ id: tabsActionComponent
+ iconName: "navigation-menu"
+ }
+
+ Action {
+ id: backActionComponent
+ iconName: "back"
+ onTriggered: mainPageStack.pop()
+ }
+ }
property Dialog currentDialog
property bool searchable: false
=== added file 'app/components/NowPlayingSidebar.qml'
--- app/components/NowPlayingSidebar.qml 1970-01-01 00:00:00 +0000
+++ app/components/NowPlayingSidebar.qml 2016-02-16 03:14:14 +0000
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2013, 2014, 2015, 2016
+ * Andrew Hayzen <ahayzen@xxxxxxxxx>
+ * Daniel Holm <d.holmen@xxxxxxxxx>
+ * Victor Thompson <victor.thompson@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.4
+import Ubuntu.Components 1.3
+
+import "HeadState"
+
+Page {
+ id: nowPlayingSidebar
+ anchors {
+ fill: parent
+ }
+ head { // hide default header
+ locked: true
+ visible: false
+ }
+ header: PageHeader {
+ leadingActionBar {
+ actions: nowPlayingSidebar.head.backAction
+ }
+ flickable: queue
+ trailingActionBar {
+ actions: nowPlayingSidebar.head.actions
+ }
+ }
+ state: queue.state === "multiselectable" ? "selection" : "default"
+ states: [
+ PageHeadState {
+ id: defaultState
+
+ name: "default"
+ actions: [
+ Action {
+ enabled: !player.mediaPlayer.playlist.empty
+ iconName: "add-to-playlist"
+ // TRANSLATORS: this action appears in the overflow drawer with limited space (around 18 characters)
+ text: i18n.tr("Add to playlist")
+
+ onTriggered: {
+ var items = []
+
+ items.push(makeDict(player.metaForSource(player.mediaPlayer.playlist.currentItemSource)));
+
+ mainPageStack.push(Qt.resolvedUrl("../ui/AddToPlaylist.qml"),
+ {"chosenElements": items})
+ }
+ },
+ Action {
+ enabled: !player.mediaPlayer.playlist.empty
+ iconName: "delete"
+ objectName: "clearQueue"
+ // TRANSLATORS: this action appears in the overflow drawer with limited space (around 18 characters)
+ text: i18n.tr("Clear queue")
+
+ onTriggered: player.mediaPlayer.playlist.clearWrapper()
+ }
+ ]
+ PropertyChanges {
+ target: nowPlayingSidebar.head
+ backAction: defaultState.backAction
+ actions: defaultState.actions
+ }
+ },
+ MultiSelectHeadState {
+ addToQueue: false
+ listview: queue
+ removable: true
+ thisPage: nowPlayingSidebar
+
+ onRemoved: {
+ // Remove the tracks from the queue
+ // Use slice() to copy the list
+ // so that the indexes don't change as they are removed
+ player.mediaPlayer.playlist.removeItemsWrapper(selectedIndices.slice());
+ }
+ }
+ ]
+
+ Rectangle {
+ anchors {
+ fill: parent
+ }
+ color: "#2c2c34"
+ }
+
+ Queue {
+ id: queue
+ clip: true
+ isSidebar: true
+ header: Column {
+ id: sidebarColumn
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+
+ NowPlayingFullView {
+ anchors {
+ fill: undefined
+ }
+ clip: true
+ height: units.gu(30)
+ width: parent.width
+ }
+
+ NowPlayingToolbar {
+ anchors {
+ fill: undefined
+ }
+ bottomProgressHint: false
+ height: itemSize + 2 * spacing + units.gu(2)
+ itemSize: units.gu(5)
+ spacing: units.gu(0.5)
+ width: parent.width
+ }
+ }
+ }
+}
=== modified file 'app/components/NowPlayingToolbar.qml'
--- app/components/NowPlayingToolbar.qml 2016-01-16 01:30:31 +0000
+++ app/components/NowPlayingToolbar.qml 2016-02-16 03:14:14 +0000
@@ -30,19 +30,23 @@
}
color: styleMusic.common.black
+ property alias bottomProgressHint: playerControlsProgressBar.visible
+ property int itemSize: units.gu(6)
+ property int spacing: units.gu(1)
+
/* Repeat button */
MouseArea {
id: nowPlayingRepeatButton
anchors.right: nowPlayingPreviousButton.left
- anchors.rightMargin: units.gu(1)
+ anchors.rightMargin: spacing
anchors.verticalCenter: nowPlayingPlayButton.verticalCenter
- height: units.gu(6)
+ height: itemSize
width: height
onClicked: player.repeat = !player.repeat
Icon {
id: repeatIcon
- height: units.gu(3)
+ height: itemSize / 2
width: height
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
@@ -58,15 +62,15 @@
id: nowPlayingPreviousButton
enabled: player.mediaPlayer.playlist.canGoPrevious
anchors.right: nowPlayingPlayButton.left
- anchors.rightMargin: units.gu(1)
+ anchors.rightMargin: spacing
anchors.verticalCenter: nowPlayingPlayButton.verticalCenter
- height: units.gu(6)
+ height: itemSize
width: height
onClicked: player.mediaPlayer.playlist.previousWrapper()
Icon {
id: nowPlayingPreviousIndicator
- height: units.gu(3)
+ height: itemSize / 2
width: height
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
@@ -81,13 +85,13 @@
MouseArea {
id: nowPlayingPlayButton
anchors.centerIn: parent
- height: units.gu(10)
+ height: itemSize + 2 * spacing
width: height
onClicked: player.mediaPlayer.toggle()
Icon {
id: nowPlayingPlayIndicator
- height: units.gu(6)
+ height: itemSize
width: height
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
@@ -101,16 +105,16 @@
MouseArea {
id: nowPlayingNextButton
anchors.left: nowPlayingPlayButton.right
- anchors.leftMargin: units.gu(1)
+ anchors.leftMargin: spacing
anchors.verticalCenter: nowPlayingPlayButton.verticalCenter
enabled: player.mediaPlayer.playlist.canGoNext
- height: units.gu(6)
+ height: itemSize
width: height
onClicked: player.mediaPlayer.playlist.nextWrapper()
Icon {
id: nowPlayingNextIndicator
- height: units.gu(3)
+ height: itemSize / 2
width: height
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
@@ -125,15 +129,15 @@
MouseArea {
id: nowPlayingShuffleButton
anchors.left: nowPlayingNextButton.right
- anchors.leftMargin: units.gu(1)
+ anchors.leftMargin: spacing
anchors.verticalCenter: nowPlayingPlayButton.verticalCenter
- height: units.gu(6)
+ height: itemSize
width: height
onClicked: player.shuffle = !player.shuffle
Icon {
id: shuffleIcon
- height: units.gu(3)
+ height: itemSize / 2
width: height
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
=== modified file 'app/components/Queue.qml'
--- app/components/Queue.qml 2016-01-12 11:44:16 +0000
+++ app/components/Queue.qml 2016-02-16 03:14:14 +0000
@@ -38,11 +38,13 @@
model: player.mediaPlayer.playlist
objectName: "nowPlayingqueueList"
+ property bool isSidebar: false
+
onCountChanged: customdebug("Queue: Now has: " + queueList.count + " tracks")
delegate: MusicListItem {
id: queueListItem
- color: player.mediaPlayer.playlist.currentIndex === index ? "#2c2c34" : styleMusic.mainView.backgroundColor
+ color: player.mediaPlayer.playlist.currentIndex === index ? (isSidebar ? "#3d3d45" : "#2c2c34") : styleMusic.mainView.backgroundColor
height: units.gu(7)
leadingActions: ListItemActions {
actions: [
=== modified file 'app/music-app.qml'
--- app/music-app.qml 2016-01-28 01:43:54 +0000
+++ app/music-app.qml 2016-02-16 03:14:14 +0000
@@ -567,23 +567,16 @@
}
}
- Loader {
- id: musicToolbar
- anchors {
- bottom: parent.bottom
- left: parent.left
- right: parent.right
- }
- asynchronous: true
- source: "components/MusicToolbar.qml"
- visible: (mainPageStack.currentPage.showToolbar || mainPageStack.currentPage.showToolbar === undefined) &&
- !firstRun &&
- !noMusic
- z: 200 // put on top of everything else
- }
-
PageStack {
id: mainPageStack
+ anchors {
+ bottom: parent.bottom
+ fill: undefined
+ left: parent.left
+ right: nowPlayingSidebarLoader.left
+ top: parent.top
+ }
+ clip: true // otherwise listitems actions overflow
// Properties storing the current page info
property Page currentMusicPage: null // currentPage can be Tabs
@@ -644,6 +637,39 @@
}
property Tab lastTab: selectedTab
+ property list<Action> tabActions: [
+ Action {
+ enabled: recentTabRepeater.count > 0
+ text: enabled ? recentTabRepeater.itemAt(0).title : ""
+ visible: enabled
+
+ onTriggered: {
+ if (enabled) {
+ tabs.selectedTabIndex = recentTabRepeater.itemAt(0).index
+ }
+ }
+ },
+ Action {
+ text: artistsTab.title
+ onTriggered: tabs.selectedTabIndex = artistsTab.index
+ },
+ Action {
+ text: albumsTab.title
+ onTriggered: tabs.selectedTabIndex = albumsTab.index
+ },
+ Action {
+ text: genresTab.title
+ onTriggered: tabs.selectedTabIndex = genresTab.index
+ },
+ Action {
+ text: songsTab.title
+ onTriggered: tabs.selectedTabIndex = songsTab.index
+ },
+ Action {
+ text: playlistsTab.title
+ onTriggered: tabs.selectedTabIndex = playlistsTab.index
+ }
+ ]
onSelectedTabChanged: {
// pause loading of the models in the old tab
@@ -844,18 +870,70 @@
function pushNowPlaying()
{
- // only push if on a different page
- if (mainPageStack.currentPage.title !== i18n.tr("Now playing")) {
- mainPageStack.push(Qt.resolvedUrl("ui/NowPlaying.qml"), {})
- }
+ if (!wideAspect) {
+ // only push if on a different page
+ if (mainPageStack.currentPage.title !== i18n.tr("Now playing")) {
+ mainPageStack.push(Qt.resolvedUrl("ui/NowPlaying.qml"), {})
+ }
- if (mainPageStack.currentPage.isListView === true) {
- mainPageStack.currentPage.isListView = false; // ensure full view
+ if (mainPageStack.currentPage.isListView === true) {
+ mainPageStack.currentPage.isListView = false; // ensure full view
+ }
}
}
} // end of tabs
}
+ //
+ // Components that are ontop of the PageStack
+ //
+
+ Loader {
+ id: nowPlayingSidebarLoader
+ active: shown || anchors.leftMargin < 0
+ anchors { // start offscreen
+ bottom: parent.bottom
+ left: parent.right
+ leftMargin: shown && status === Loader.Ready ? -width : 0
+ top: parent.top
+ }
+ asynchronous: true
+ source: "components/NowPlayingSidebar.qml"
+ visible: width > 0
+ width: units.gu(30)
+
+ property bool shown: loadedUI && wideAspect && player.mediaPlayer.playlist.itemCount > 0
+
+ Behavior on anchors.leftMargin {
+ NumberAnimation {
+
+ }
+ }
+ }
+
+ Loader {
+ id: musicToolbar
+ active: !wideAspect || anchors.topMargin < 0
+ anchors {
+ left: parent.left
+ right: parent.right
+ top: parent.bottom
+ topMargin: !wideAspect && status === Loader.Ready ? -height : 0
+ }
+ asynchronous: true
+ source: "components/MusicToolbar.qml"
+ visible: (mainPageStack.currentPage && (mainPageStack.currentPage.showToolbar || mainPageStack.currentPage.showToolbar === undefined)) &&
+ !firstRun &&
+ !noMusic &&
+ anchors.topMargin < 0
+
+ Behavior on anchors.topMargin {
+ NumberAnimation {
+
+ }
+ }
+ }
+
LoadingSpinnerComponent {
id: loading
}
=== modified file 'app/ui/AddToPlaylist.qml'
--- app/ui/AddToPlaylist.qml 2016-01-12 01:06:16 +0000
+++ app/ui/AddToPlaylist.qml 2016-02-16 03:14:14 +0000
@@ -59,10 +59,13 @@
// FIXME: workaround for pad.lv/1531016 (gridview juddery)
anchors {
+ bottom: parent.bottom
+ left: parent.left
fill: undefined
+ top: parent.top
}
- height: mainView.height
- width: mainView.width
+ height: mainPageStack.height
+ width: mainPageStack.width
property var chosenElements: []
=== modified file 'app/ui/Albums.qml'
--- app/ui/Albums.qml 2016-01-12 00:30:08 +0000
+++ app/ui/Albums.qml 2016-02-16 03:14:14 +0000
@@ -46,10 +46,13 @@
// FIXME: workaround for pad.lv/1531016 (gridview juddery)
anchors {
+ bottom: parent.bottom
+ left: parent.left
fill: undefined
+ top: parent.top
}
- height: mainView.height
- width: mainView.width
+ height: mainPageStack.height
+ width: mainPageStack.width
// Hack for autopilot otherwise Albums appears as MusicPage
// due to bug 1341671 it is required that there is a property so that
=== modified file 'app/ui/ArtistView.qml'
--- app/ui/ArtistView.qml 2016-01-12 00:30:08 +0000
+++ app/ui/ArtistView.qml 2016-02-16 03:14:14 +0000
@@ -38,10 +38,13 @@
// FIXME: workaround for pad.lv/1531016 (gridview juddery)
anchors {
+ bottom: parent.bottom
+ left: parent.left
fill: undefined
+ top: parent.top
}
- height: mainView.height
- width: mainView.width
+ height: mainPageStack.height
+ width: mainPageStack.width
MusicGridView {
id: artistAlbumView
=== modified file 'app/ui/Artists.qml'
--- app/ui/Artists.qml 2016-01-30 23:58:32 +0000
+++ app/ui/Artists.qml 2016-02-16 03:14:14 +0000
@@ -50,10 +50,13 @@
// FIXME: workaround for pad.lv/1531016 (gridview juddery)
anchors {
+ bottom: parent.bottom
+ left: parent.left
fill: undefined
+ top: parent.top
}
- height: mainView.height
- width: mainView.width
+ height: mainPageStack.height
+ width: mainPageStack.width
// Hack for autopilot otherwise Artists appears as MusicPage
// due to bug 1341671 it is required that there is a property so that
=== modified file 'app/ui/Genres.qml'
--- app/ui/Genres.qml 2016-01-12 00:30:08 +0000
+++ app/ui/Genres.qml 2016-02-16 03:14:14 +0000
@@ -46,10 +46,13 @@
// FIXME: workaround for pad.lv/1531016 (gridview juddery)
anchors {
+ bottom: parent.bottom
+ left: parent.left
fill: undefined
+ top: parent.top
}
- height: mainView.height
- width: mainView.width
+ height: mainPageStack.height
+ width: mainPageStack.width
// Hack for autopilot otherwise Albums appears as MusicPage
// due to bug 1341671 it is required that there is a property so that
=== modified file 'app/ui/NowPlaying.qml'
--- app/ui/NowPlaying.qml 2016-01-16 01:30:31 +0000
+++ app/ui/NowPlaying.qml 2016-02-16 03:14:14 +0000
@@ -59,6 +59,18 @@
}
}
+ onVisibleChanged: {
+ if (wideAspect) {
+ popWaitTimer.start()
+ }
+ }
+
+ Timer { // FIXME: workaround for when entering wideAspect coming back from a stacked page (AddToPlaylist) and the page being deleted breaks the stacked page
+ id: popWaitTimer
+ interval: 250
+ onTriggered: mainPageStack.popPage(nowPlaying);
+ }
+
// Ensure that the listview has loaded before attempting to positionAt
function ensureListViewLoaded() {
if (queueListLoader.item.count === player.mediaPlayer.playlist.itemCount) {
@@ -188,6 +200,16 @@
}
Connections {
+ target: mainView
+ onWideAspectChanged: {
+ // Do not pop if not visible (eg on AddToPlaylist)
+ if (wideAspect && nowPlaying.visible) {
+ mainPageStack.popPage(nowPlaying);
+ }
+ }
+ }
+
+ Connections {
target: player.mediaPlayer.playlist
onEmptyChanged: {
if (player.mediaPlayer.playlist.empty) {
=== modified file 'app/ui/Playlists.qml'
--- app/ui/Playlists.qml 2016-01-12 01:06:16 +0000
+++ app/ui/Playlists.qml 2016-02-16 03:14:14 +0000
@@ -51,10 +51,13 @@
// FIXME: workaround for pad.lv/1531016 (gridview juddery)
anchors {
+ bottom: parent.bottom
+ left: parent.left
fill: undefined
+ top: parent.top
}
- height: mainView.height
- width: mainView.width
+ height: mainPageStack.height
+ width: mainPageStack.width
property bool changed: false
property bool childrenChanged: false
=== modified file 'app/ui/Recent.qml'
--- app/ui/Recent.qml 2016-01-12 01:06:16 +0000
+++ app/ui/Recent.qml 2016-02-16 03:14:14 +0000
@@ -35,10 +35,13 @@
// FIXME: workaround for pad.lv/1531016 (gridview juddery)
anchors {
+ bottom: parent.bottom
+ left: parent.left
fill: undefined
+ top: parent.top
}
- height: mainView.height
- width: mainView.width
+ height: mainPageStack.height
+ width: mainPageStack.width
property bool changed: false
property bool childrenChanged: false
References