-
2d905951
MusicArtist.qml 17.02 KiB
/*****************************************************************************
* Copyright (C) 2020 VLC authors and VideoLAN
*
* 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; either version 2 of the License, or
* ( at your option ) any later version.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
import QtQuick.Controls
import QtQuick
import QtQml.Models
import QtQuick.Layouts
import org.videolan.medialib 0.1
import org.videolan.vlc 0.1
import "qrc:///util/" as Util
import "qrc:///util/Helpers.js" as Helpers
import "qrc:///widgets/" as Widgets
import "qrc:///main/" as MainInterface
import "qrc:///style/"
FocusScope {
id: root
property var artist: ({})
//the index to "go to" when the view is loaded
property int initialIndex: 0
property Item headerItem: _currentView ? _currentView.headerItem : null
property bool isSearchable: true
property alias searchPattern: albumModel.searchPattern
property alias sortOrder: albumModel.sortOrder
property alias sortCriteria: albumModel.sortCriteria
// current index of album model
readonly property int currentIndex: {
if (!_currentView)
return -1
else if (MainCtx.gridView)
return _currentView.currentIndex
else
return headerItem.albumsListView.currentIndex
}
property alias rightPadding: view.rightPadding
property alias _currentView: view.currentItem
function navigationShowHeader(y, height) {
const newContentY = Helpers.flickablePositionContaining(_currentView, y, height, 0, 0)
if (newContentY !== _currentView.contentY)
_currentView.contentY = newContentY
}
property Component header: FocusScope {
id: headerFs
property Item albumsListView: albumsLoader.status === Loader.Ready ? albumsLoader.item.albumsListView: null
focus: true
height: col.height
width: root.width
function setCurrentItemFocus(reason) {
if (albumsListView)
albumsListView.setCurrentItemFocus(reason);
else
artistBanner.setCurrentItemFocus(reason);
}
Column {
id: col
height: implicitHeight
width: headerFs.width
ArtistTopBanner {
id: artistBanner
focus: true
width: headerFs.width
rightPadding: root.rightPadding
artist: root.artist
onActiveFocusChanged: {
// make sure content is visible with activeFocus
if (activeFocus)
root.navigationShowHeader(0, height)
}
Navigation.parentItem: root
Navigation.downAction: function() {
if (albumsListView)
albumsListView.setCurrentItemFocus(Qt.TabFocusReason);
else
_currentView.setCurrentItemFocus(Qt.TabFocusReason);
}
}
Widgets.ViewHeader {
view: root
leftPadding: VLCStyle.margin_xlarge
bottomPadding: VLCStyle.layoutTitle_bottom_padding -
(MainCtx.gridView ? 0 : VLCStyle.gridItemSelectedBorder)
text: qsTr("Albums")
}
Loader {
id: albumsLoader
active: !MainCtx.gridView
focus: true
onActiveFocusChanged: {
// make sure content is visible with activeFocus
if (activeFocus)
root.navigationShowHeader(y, height)
}
sourceComponent: Column {
property alias albumsListView: albumsList
width: albumsList.width
height: implicitHeight
spacing: VLCStyle.tableView_spacing
Widgets.KeyNavigableListView {
id: albumsList
focus: true
height: VLCStyle.gridItem_music_height + topMargin + bottomMargin
width: root.width - root.rightPadding
leftMargin: VLCStyle.margin_xlarge
topMargin: VLCStyle.gridItemSelectedBorder
bottomMargin: VLCStyle.gridItemSelectedBorder
model: albumModel
selectionModel: albumSelectionModel
orientation: ListView.Horizontal
spacing: VLCStyle.column_spacing
Navigation.parentItem: root
Navigation.upAction: function() {
artistBanner.setCurrentItemFocus(Qt.TabFocusReason);
}
Navigation.downAction: function() {
root.setCurrentItemFocus(Qt.TabFocusReason);
}
delegate: Widgets.GridItem {
id: gridItem
image: model.cover || ""
fallbackImage: VLCStyle.noArtAlbumCover
title: model.title || qsTr("Unknown title")
subtitle: model.release_year || ""
textAlignHCenter: true
x: selectedBorderWidth
y: selectedBorderWidth
pictureWidth: VLCStyle.gridCover_music_width
pictureHeight: VLCStyle.gridCover_music_height
playCoverBorderWidth: VLCStyle.gridCover_music_border
dragItem: albumDragItem
onPlayClicked: play()
onItemDoubleClicked: play()
onItemClicked: (modifier) => {
albumsList.selectionModel.updateSelection( modifier , albumsList.currentIndex, index )
albumsList.currentIndex = index
albumsList.forceActiveFocus()
}
Connections {
target: albumsList.selectionModel
function onSelectionChanged() {
gridItem.selected = albumsList.selectionModel.isSelected(index)
}
}
function play() {
if ( model.id !== undefined ) {
MediaLib.addAndPlay( model.id )
}
}
}
onActionAtIndex: (index) => { albumModel.addAndPlay( new Array(index) ) }
}
Widgets.ViewHeader {
view: root
leftPadding: VLCStyle.margin_xlarge
topPadding: 0
text: qsTr("Tracks")
}
}
}
}
}
focus: true
onInitialIndexChanged: resetFocus()
function setCurrentItemFocus(reason) {
if (view.currentItem === null) {
Qt.callLater(setCurrentItemFocus, reason)
return
}
view.currentItem.setCurrentItemFocus(reason);
}
function resetFocus() {
if (albumModel.count === 0) {
return
}
let initialIndex = root.initialIndex
if (initialIndex >= albumModel.count)
initialIndex = 0
albumSelectionModel.select(initialIndex, ItemSelectionModel.ClearAndSelect)
const albumsListView = MainCtx.gridView ? _currentView : headerItem.albumsListView
if (albumsListView) {
albumsListView.currentIndex = initialIndex
albumsListView.positionViewAtIndex(initialIndex, ItemView.Contain)
}
}
function _actionAtIndex(index, model, selectionModel) {
if (selectionModel.selectedIndexes.length > 1) {
model.addAndPlay( selectionModel.selectedIndexes )
} else {
model.addAndPlay( new Array(index) )
}
}
function _onNavigationCancel() {
if (_currentView.currentIndex <= 0) {
root.Navigation.defaultNavigationCancel()
} else {
_currentView.currentIndex = 0;
_currentView.positionViewAtIndex(0, ItemView.Contain)
}
if (tableView_id.currentIndex <= 0)
root.Navigation.defaultNavigationCancel()
else
tableView_id.currentIndex = 0;
}
readonly property ColorContext colorContext: ColorContext {
id: theme
colorSet: ColorContext.View
}
MLAlbumModel {
id: albumModel
ml: MediaLib
parentId: artist.id
onCountChanged: {
if (albumModel.count > 0 && !albumSelectionModel.hasSelection) {
root.resetFocus()
}
}
}
ListSelectionModel {
id: albumSelectionModel
model: albumModel
}
Widgets.MLDragItem {
id: albumDragItem
mlModel: albumModel
indexes: indexesFlat ? albumSelectionModel.selectedIndexesFlat
: albumSelectionModel.selectedIndexes
indexesFlat: !!albumSelectionModel.selectedIndexesFlat
defaultCover: VLCStyle.noArtAlbumCover
}
MLAlbumTrackModel {
id: trackModel
ml: MediaLib
parentId: albumModel.parentId
}
Util.MLContextMenu {
id: contextMenu
model: albumModel
}
Util.MLContextMenu {
id: trackContextMenu
model: trackModel
}
Component {
id: gridComponent
MainInterface.MainGridView {
id: gridView_id
focus: true
activeFocusOnTab:true
cellWidth: VLCStyle.gridItem_music_width
cellHeight: VLCStyle.gridItem_music_height
headerDelegate: root.header
selectionModel: albumSelectionModel
model: albumModel
Connections {
target: albumModel
// selectionModel updates but doesn't trigger any signal, this forces selection update in view
function onParentIdChanged() {
currentIndex = -1
}
}
delegate: AudioGridItem {
id: audioGridItem
opacity: gridView_id.expandIndex !== -1 && gridView_id.expandIndex !== audioGridItem.index ? .7 : 1
dragItem: albumDragItem
onItemClicked : (modifier) => {
gridView_id.leftClickOnItem(modifier, index)
}
onItemDoubleClicked: {
gridView_id.switchExpandItem(index)
}
onContextMenuButtonClicked: (_, globalMousePos) => {
gridView_id.rightClickOnItem(index)
contextMenu.popup(albumSelectionModel.selectedIndexes, globalMousePos, { "information" : index})
}
Behavior on opacity {
NumberAnimation {
duration: VLCStyle.duration_short
}
}
}
expandDelegate: MusicAlbumsGridExpandDelegate {
id: expandDelegateId
x: 0
width: gridView_id.width
onRetract: gridView_id.retract()
Navigation.parentItem: root
Navigation.cancelAction: function() {
gridView_id.setCurrentItemFocus(Qt.TabFocusReason);
}
Navigation.upAction: function() {
gridView_id.setCurrentItemFocus(Qt.TabFocusReason);
}
Navigation.downAction: function() {}
}
onActionAtIndex: (index) => {
if (albumSelectionModel.selectedIndexes.length === 1) {
switchExpandItem(index);
expandItem.setCurrentItemFocus(Qt.TabFocusReason);
} else {
_actionAtIndex(index, albumModel, albumSelectionModel);
}
}
Navigation.parentItem: root
Navigation.upAction: function() {
headerItem.setCurrentItemFocus(Qt.TabFocusReason);
}
Navigation.cancelAction: root._onNavigationCancel
Connections {
target: contextMenu
function onShowMediaInformation(index) {
gridView_id.switchExpandItem( index )
}
}
}
}
Component {
id: tableComponent
MainInterface.MainTableView {
id: tableView_id
readonly property int _nbCols: VLCStyle.gridColumnsForWidth(tableView_id.availableRowWidth)
clip: true // content may overflow if not enough space is provided
model: trackModel
onActionForSelection: (selection) => {
model.addAndPlay(selection)
}
header: root.header
headerPositioning: ListView.InlineHeader
rowHeight: VLCStyle.tableCoverRow_height
property var _modelSmall: [{
size: Math.max(2, tableView_id._nbCols),
model: {
criteria: "title",
subCriterias: [ "duration", "album_title" ],
text: qsTr("Title"),
headerDelegate: tableColumns.titleHeaderDelegate,
colDelegate: tableColumns.titleDelegate
}
}]
property var _modelMedium: [{
size: 2,
model: {
criteria: "title",
text: qsTr("Title"),
headerDelegate: tableColumns.titleHeaderDelegate,
colDelegate: tableColumns.titleDelegate
}
}, {
size: Math.max(1, tableView_id._nbCols - 3),
model: {
criteria: "album_title",
text: qsTr("Album")
}
}, {
size: 1,
model: {
criteria: "duration",
text: qsTr("Duration"),
showSection: "",
headerDelegate: tableColumns.timeHeaderDelegate,
colDelegate: tableColumns.timeColDelegate
}
}]
sortModel: (availableRowWidth < VLCStyle.colWidth(4)) ? _modelSmall
: _modelMedium
dragItem: tableDragItem
Navigation.parentItem: root
Navigation.upAction: function() {
headerItem.setCurrentItemFocus(Qt.TabFocusReason);
}
Navigation.cancelAction: root._onNavigationCancel
onItemDoubleClicked: MediaLib.addAndPlay(model.id)
onContextMenuButtonClicked: trackContextMenu.popup(tableView_id.selectionModel.selectedIndexes, globalMousePos)
onRightClick: trackContextMenu.popup(tableView_id.selectionModel.selectedIndexes, globalMousePos)
onDragItemChanged: console.assert(tableView_id.dragItem === tableDragItem)
Widgets.MLDragItem {
id: tableDragItem
mlModel: trackModel
indexes: indexesFlat ? tableView_id.selectionModel.selectedIndexesFlat
: tableView_id.selectionModel.selectedIndexes
indexesFlat: !!tableView_id.selectionModel.selectedIndexesFlat
defaultCover: VLCStyle.noArtArtistCover
}
Widgets.TableColumns {
id: tableColumns
showCriterias: (tableView_id.sortModel === tableView_id._modelSmall)
}
}
}
Widgets.StackViewExt {
id: view
anchors.fill: parent
focus: albumModel.count !== 0
initialItem: MainCtx.gridView ? gridComponent : tableComponent
Connections {
target: MainCtx
function onGridViewChanged() {
if (MainCtx.gridView)
view.replace(gridComponent)
else
view.replace(tableComponent)
}
}
}
}