Commit d9f07791 authored by Matthieu Gallien's avatar Matthieu Gallien 🎵
Browse files

introduce GridBrowserView and GridBrowserDelegate for generic grid view

Summary:
add support for more roles in the model to allow generic delegates

provides roles that will be useable by the generic delegates

allow to retrieve the album or the artist with a generic role from models

support everything except open with generic view and delegate

allow all artsist and all albums models to know each other

this is needed to allow the models to generate proxy models linked to an
artist from an album item for example

makes it possible to open and use album or all albums from an artist views

still some things are not working well like the possibility to show the
artist of an album

I still need to report in the new components all the improvements that have gone since I started this branch

Test Plan: works fine

Reviewers: #elisa, astippich

Reviewed By: #elisa, astippich

Tags: #elisa

Maniphest Tasks: T7671

Differential Revision: https://phabricator.kde.org/D9777
parent e09db804
......@@ -345,6 +345,7 @@ set(allalbumsmodeltest_SOURCES
../src/musicalbum.cpp
../src/musicaudiotrack.cpp
../src/allalbumsmodel.cpp
../src/albummodel.cpp
allalbumsmodeltest.cpp
)
......
......@@ -44,8 +44,6 @@ if (Qt5Quick_FOUND AND Qt5Widgets_FOUND)
RatingStar.qml
PlayListEntry.qml
MediaAlbumDelegate.qml
MediaArtistDelegate.qml
MediaBrowser.qml
DraggableItem.qml
PassiveNotification.qml
......@@ -59,14 +57,13 @@ if (Qt5Quick_FOUND AND Qt5Widgets_FOUND)
MediaPlayerControl.qml
ContextView.qml
MediaArtistAlbumView.qml
MediaPlayListView.qml
MediaAlbumView.qml
MediaAllAlbumView.qml
MediaAllArtistView.qml
MediaAllTracksView.qml
MediaTrackDelegate.qml
MediaAlbumTrackDelegate.qml
GridBrowserView.qml
GridBrowserDelegate.qml
ViewSelector.qml
)
......
......@@ -17,9 +17,9 @@
* Boston, MA 02110-1301, USA.
*/
import QtQuick 2.4
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Window 2.2
import QtQml.Models 2.1
import QtQuick.Layouts 1.2
......@@ -27,58 +27,47 @@ import QtGraphicalEffects 1.0
import org.kde.elisa 1.0
FocusScope {
id: mediaServerEntry
Item {
id: gridEntry
property StackView stackView
property MediaPlayList playListModel
property var musicListener
property var playerControl
property var contentDirectoryModel
property var image
property alias name: nameLabel.text
property var imageUrl
property bool shadowForImage
property alias mainText: mainLabel.text
property alias secondaryText: secondaryLabel.text
property var containerData
property bool delegateDisplaySecondaryText: true
signal artistClicked()
signal openArtist(var name)
SystemPalette {
id: myPalette
colorGroup: SystemPalette.Active
}
Theme {
id: elisaTheme
}
signal enqueue(var data);
signal enqueueAndPlay(var data);
signal open();
signal selected();
Action {
id: enqueueAction
text: i18nc("Add all tracks from artist to play list", "Enqueue")
iconName: "media-track-add-amarok"
onTriggered: mediaServerEntry.playListModel.enqueue(mediaServerEntry.name)
text: i18nc("Add whole container to play list", "Enqueue")
iconName: 'media-track-add-amarok'
onTriggered: enqueue(containerData)
}
Action {
id: openAction
text: i18nc("Open artist view", "Open Artist")
text: i18nc("Open view of the container", "Open")
iconName: 'document-open-folder'
onTriggered: openArtist(name)
onTriggered: open()
}
Action {
id: enqueueAndPlayAction
text: i18nc("Clear play list and add all tracks from artist to play list", "Play Now and Replace Play List")
iconName: "media-playback-start"
onTriggered: {
mediaServerEntry.playListModel.clearAndEnqueue(mediaServerEntry.name)
mediaServerEntry.playerControl.ensurePlay()
}
text: i18nc("Clear play list and add whole container to play list", "Play Now and Replace Play List")
iconName: 'media-playback-start'
onTriggered: enqueueAndPlay(containerData)
}
Keys.onReturnPressed: openArtist(name)
Keys.onEnterPressed: openArtist(name)
Keys.onReturnPressed: open()
Keys.onEnterPressed: open()
ColumnLayout {
anchors.fill: parent
......@@ -92,20 +81,26 @@ FocusScope {
acceptedButtons: Qt.LeftButton
focus: true
Layout.preferredHeight: mediaServerEntry.width * 0.85 + elisaTheme.layoutVerticalMargin * 0.5 + nameSize.height
Layout.preferredHeight: gridEntry.width * 0.85 + elisaTheme.layoutVerticalMargin * 0.5 + mainLabelSize.height + secondaryLabelSize.height
Layout.fillWidth: true
onClicked: {
hoverHandle.forceActiveFocus()
artistClicked()
gridEntry.selected()
}
onDoubleClicked: openArtist(name)
onDoubleClicked: open()
TextMetrics {
id: nameSize
font: nameLabel.font
text: nameLabel.text
id: mainLabelSize
font: mainLabel.font
text: mainLabel.text
}
TextMetrics {
id: secondaryLabelSize
font: secondaryLabel.font
text: secondaryLabel.text
}
ColumnLayout {
......@@ -115,11 +110,13 @@ FocusScope {
anchors.fill: parent
Item {
Layout.preferredWidth: mediaServerEntry.width * 0.85
Layout.preferredHeight: mediaServerEntry.width * 0.85
Layout.preferredHeight: gridEntry.width * 0.85
Layout.preferredWidth: gridEntry.width * 0.85
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
focus: true
Loader {
id: hoverLoader
active: false
......@@ -161,23 +158,23 @@ FocusScope {
}
Image {
id: artistDecoration
source: Qt.resolvedUrl(elisaTheme.defaultArtistImage)
id: coverImage
anchors.fill: parent
sourceSize.width: parent.width
sourceSize.height: parent.height
fillMode: Image.PreserveAspectFit
smooth: true
source: gridEntry.imageUrl
asynchronous: true
layer.enabled: image === '' ? false : true
layer.enabled: shadowForImage
layer.effect: DropShadow {
source: coverImage
radius: 10
spread: 0.1
samples: 21
......@@ -188,19 +185,35 @@ FocusScope {
}
LabelWithToolTip {
id: nameLabel
id: mainLabel
font.weight: Font.Bold
color: myPalette.text
horizontalAlignment: Text.AlignLeft
Layout.preferredWidth: mediaServerEntry.width * 0.85
Layout.topMargin: elisaTheme.layoutVerticalMargin * 0.5
Layout.preferredWidth: gridEntry.width * 0.85
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
elide: Text.ElideRight
}
LabelWithToolTip {
id: secondaryLabel
font.weight: Font.Light
color: myPalette.text
horizontalAlignment: Text.AlignLeft
Layout.preferredWidth: gridEntry.width * 0.85
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
visible: delegateDisplaySecondaryText
elide: Text.ElideRight
}
}
}
......@@ -212,7 +225,7 @@ FocusScope {
states: [
State {
name: 'notSelected'
when: !mediaServerEntry.activeFocus && !hoverHandle.containsMouse
when: !gridEntry.activeFocus && !hoverHandle.containsMouse
PropertyChanges {
target: hoverLoader
active: false
......@@ -222,13 +235,13 @@ FocusScope {
opacity: 0.0
}
PropertyChanges {
target: artistDecoration
target: coverImage
opacity: 1
}
},
State {
name: 'hoveredOrSelected'
when: mediaServerEntry.activeFocus || hoverHandle.containsMouse
when: gridEntry.activeFocus || hoverHandle.containsMouse
PropertyChanges {
target: hoverLoader
active: true
......@@ -238,7 +251,7 @@ FocusScope {
opacity: 1.0
}
PropertyChanges {
target: artistDecoration
target: coverImage
opacity: 0.2
}
}
......
......@@ -18,8 +18,8 @@
*/
import QtQuick 2.7
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Window 2.2
import QtQml.Models 2.1
import QtQuick.Layouts 1.2
......@@ -28,14 +28,20 @@ import QtGraphicalEffects 1.0
import org.kde.elisa 1.0
FocusScope {
property var playerControl
property var playListModel
property var artistsModel
property var stackView
property var contentDirectoryModel
property var musicListener
id: gridView
id: rootElement
property bool isFirstPage: false
property string mainTitle
property string secondaryTitle
property url image
property alias model: proxyModel.model
property bool showRating: true
property bool delegateDisplaySecondaryText: true
signal enqueue(var data);
signal enqueueAndPlay(var data);
signal open(var innerModel, var innerMainTitle, var innerSecondaryTitle, var innerImage);
signal goBack()
SystemPalette {
id: myPalette
......@@ -50,16 +56,63 @@ FocusScope {
anchors.fill: parent
spacing: 0
FilterBar {
id: filterBar
labelText: i18nc("Title of the view of all artists", "Artists")
Loader {
sourceComponent: FilterBar {
id: filterBar
labelText: mainTitle
showRating: gridView.showRating
anchors.fill: parent
Binding {
target: model
property: 'filterText'
value: filterBar.filterText
when: isFirstPage
}
Binding {
target: model
property: 'filterRating'
value: filterBar.filterRating
when: isFirstPage
}
}
showRating: false
height: elisaTheme.navigationBarHeight
Layout.preferredHeight: height
Layout.minimumHeight: height
Layout.maximumHeight: height
Layout.fillWidth: true
active: isFirstPage
visible: isFirstPage
}
Loader {
sourceComponent: NavigationActionBar {
id: navigationBar
mainTitle: gridView.mainTitle
secondaryTitle: gridView.secondaryTitle
image: gridView.image
anchors.fill: parent
onEnqueue: gridView.enqueue(mainTitle)
onEnqueueAndPlay: gridView.enqueueAndPlay(mainTitle)
onGoBack: gridView.goBack()
}
height: elisaTheme.navigationBarHeight
Layout.preferredHeight: height
Layout.minimumHeight: height
Layout.maximumHeight: height
Layout.fillWidth: true
active: !isFirstPage
visible: !isFirstPage
}
Rectangle {
......@@ -79,76 +132,40 @@ FocusScope {
focus: true
TextMetrics {
id: textLineHeight
text: 'Artist'
id: secondaryLabelSize
text: 'example'
}
cellWidth: elisaTheme.gridDelegateWidth
cellHeight: elisaTheme.gridDelegateWidth + elisaTheme.layoutVerticalMargin + textLineHeight.height * 2
cellHeight: (delegateDisplaySecondaryText ? elisaTheme.gridDelegateHeight : elisaTheme.gridDelegateHeight - secondaryLabelSize.height)
model: DelegateModel {
id: delegateContentModel
model: SortFilterProxyModel {
sourceModel: artistsModel
filterRole: AllArtistsModel.NameRole
id: proxyModel
filterRegExp: new RegExp(filterBar.filterText, 'i')
}
delegate: MediaArtistDelegate {
delegate: GridBrowserDelegate {
width: contentDirectoryView.cellWidth
height: contentDirectoryView.cellHeight
focus: true
musicListener: rootElement.musicListener
image: if (model.image)
model.image
else
""
name: if (model.name)
model.name
else
""
stackView: rootElement.stackView
playListModel: rootElement.playListModel
playerControl: rootElement.playerControl
contentDirectoryModel: rootElement.contentDirectoryModel
onArtistClicked: contentDirectoryView.currentIndex = index
onOpenArtist: showArtistsAlbums(name)
mainText: model.display
secondaryText: model.secondaryText
imageUrl: model.imageUrl
shadowForImage: model.shadowForImage
containerData: model.containerData
delegateDisplaySecondaryText: gridView.delegateDisplaySecondaryText
onEnqueue: gridView.enqueue(data)
onEnqueueAndPlay: gridView.enqueueAndPlay(data)
onOpen: gridView.open(model.childModel, model.display, model.secondaryText, model.imageUrl)
onSelected: {
forceActiveFocus()
contentDirectoryView.currentIndex = model.index
}
}
}
}
}
}
}
Component {
id: oneArtistView
MediaArtistAlbumView {
playListModel: rootElement.playListModel
contentDirectoryModel: rootElement.contentDirectoryModel
playerControl: rootElement.playerControl
stackView: rootElement.stackView
musicListener: rootElement.musicListener
onShowArtist: showArtistsAlbums(name)
}
}
function showArtistsAlbums(name){
if (stackView.depth === 3) {
stackView.pop()
} else {
stackView.push(oneArtistView, {artistName: name})
}
}
}
/*
* Copyright 2016-2017 Matthieu Gallien <matthieu_gallien@yahoo.fr>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
import QtQuick 2.4
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import QtQuick.Window 2.2
import QtQml.Models 2.1
import QtQuick.Layouts 1.2
import QtGraphicalEffects 1.0
import org.kde.elisa 1.0
FocusScope {
id: mediaServerEntry
property StackView stackView
property MediaPlayList playListModel
property var musicListener
property var playerControl
property var image
property alias title: titleLabel.text
property alias artist: artistLabel.text
property string trackNumber
property bool isSingleDiscAlbum
property var albumData
property var albumId
signal showArtist(var name)
signal albumClicked()
Action {
id: enqueueAction
text: i18nc("Add whole album to play list", "Enqueue")
iconName: 'media-track-add-amarok'
onTriggered: mediaServerEntry.playListModel.enqueue(mediaServerEntry.albumData)
}
Action {
id: openAction
text: i18nc("Open album view", "Open Album")
iconName: 'document-open-folder'
onTriggered: showAlbumTracks()
}
Action {
id: enqueueAndPlayAction
text: i18nc("Clear play list and add whole album to play list", "Play Now and Replace Play List")
iconName: 'media-playback-start'
onTriggered: {
mediaServerEntry.playListModel.clearAndEnqueue(mediaServerEntry.albumData)
mediaServerEntry.playerControl.ensurePlay()
}
}
Keys.onReturnPressed: showAlbumTracks()
Keys.onEnterPressed: showAlbumTracks()
Component {
id: albumViewComponent
MediaAlbumView {
stackView: mediaServerEntry.stackView
playListModel: mediaServerEntry.playListModel
musicListener: mediaServerEntry.musicListener
playerControl: mediaServerEntry.playerControl
albumArtUrl: image
albumName: title
artistName: artist
tracksCount: count
isSingleDiscAlbum: mediaServerEntry.isSingleDiscAlbum
albumData: mediaServerEntry.albumData
albumId: mediaServerEntry.albumId
onShowArtist: mediaServerEntry.showArtist(name)
}
}
function showAlbumTracks() {
stackView.push(albumViewComponent)
}
ColumnLayout {
anchors.fill: parent
spacing: 0
MouseArea {
id: hoverHandle
hoverEnabled: true
acceptedButtons: Qt.LeftButton
focus: true
Layout.preferredHeight: mediaServerEntry.width * 0.85 + elisaTheme.layoutVerticalMargin * 0.5 + titleSize.height + artistSize.height
Layout.fillWidth: true
onClicked: {
hoverHandle.forceActiveFocus()
albumClicked()
}
onDoubleClicked: showAlbumTracks()
TextMetrics {
id: titleSize
font: titleLabel.font
text: titleLabel.text
}
TextMetrics {
id: artistSize
font: artistLabel.font
text: artistLabel.text
}
ColumnLayout {
id: mainData
spacing: 0
anchors.fill: parent
Item {
Layout.preferredHeight: mediaServerEntry.width * 0.85