-
8fffc6e2
PlaylistMediaList.qml 15.46 KiB
/*****************************************************************************
* Copyright (C) 2021-23 VLC authors and VideoLAN
*
* Authors: Benjamin Arnaud <bunjee@omega.gg>
*
* 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
import QtQuick.Controls
import QtQuick.Layouts
import QtQml.Models
import org.videolan.medialib 0.1
import org.videolan.vlc 0.1
import "qrc:///widgets/" as Widgets
import "qrc:///main/" as MainInterface
import "qrc:///util/" as Util
import "qrc:///util/Helpers.js" as Helpers
import "qrc:///style/"
MainInterface.MainViewLoader {
id: root
//---------------------------------------------------------------------------------------------
// Properties
//---------------------------------------------------------------------------------------------
property bool isMusic: false
readonly property int currentIndex: currentItem?.currentIndex ?? -1
property Component header: null
readonly property int contentLeftMargin: currentItem?.contentLeftMargin ?? 0
readonly property int contentRightMargin: currentItem?.contentRightMargin ?? 0
property alias searchPattern: playlistModel.searchPattern
property alias sortOrder: playlistModel.sortOrder
property alias sortCriteria: playlistModel.sortCriteria
//---------------------------------------------------------------------------------------------
// Private
property int _width: (isMusic) ? VLCStyle.gridItem_music_width
: VLCStyle.gridItem_video_width
property int _height: (isMusic) ? VLCStyle.gridItem_music_height
: VLCStyle.gridItem_video_height
property int _widthCover: (isMusic) ? VLCStyle.gridCover_music_width
: VLCStyle.gridCover_video_width
property int _heightCover: (isMusic) ? VLCStyle.gridCover_music_height
: VLCStyle.gridCover_video_height
property string _placeHolder: (isMusic) ? VLCStyle.noArtAlbumCover
: VLCStyle.noArtVideoCover
// FIXME: remove this
property var _currentView: currentItem
//---------------------------------------------------------------------------------------------
// Signals
//---------------------------------------------------------------------------------------------
signal showList(var model, int reason)
//---------------------------------------------------------------------------------------------
// Events
//---------------------------------------------------------------------------------------------
// NOTE: Define the initial position and selection. This is done on activeFocus rather than
// Component.onCompleted because selectionModel.selectedGroup update itself after this
// event.
onActiveFocusChanged: {
if (activeFocus == false || model.count === 0 || selectionModel.hasSelection)
return;
resetFocus()
}
//---------------------------------------------------------------------------------------------
// Private
grid: grid
list: table
emptyLabel: emptyLabel
isSearchable: true
sortModel: [{ text: qsTr("Alphabetic"), criteria: "title" }]
model: MLPlaylistListModel {
id: playlistModel
ml: MediaLib
playlistType: isMusic ? MLPlaylistListModel.PLAYLIST_TYPE_AUDIO
: MLPlaylistListModel.PLAYLIST_TYPE_VIDEO
coverSize: (isMusic) ? Qt.size(512, 512)
: Qt.size(1024, 640)
coverDefault: root._placeHolder
coverPrefix: (isMusic) ? "playlist-music" : "playlist-video"
onTransactionPendingChanged: {
if (transactionPending)
visibilityTimer.start()
else {
visibilityTimer.stop()
progressIndicator.visible = false
}
}
}
function _actionAtIndex() {
if (root.selectionModel.selectedIndexes.length > 1) {
model.addAndPlay( selectionModel.selectedIndexes );
} else if (root.selectionModel.selectedIndexes.length === 1) {
const index = selectionModel.selectedIndexes[0];
showList(model.getDataAt(index), Qt.TabFocusReason);
}
}
function _getCount(model) {
const count = model.count;
if (count < 100)
return count;
else
return qsTr("99+");
}
function _adjustDragAccepted(drag) {
if (!root.model || root.model.transactionPending)
{
drag.accepted = false
return
}
if (drag.source !== dragItemPlaylist && Helpers.isValidInstanceOf(drag.source, Widgets.DragItem))
drag.accepted = true
else if (drag.hasUrls)
drag.accepted = true
else {
drag.accepted = false
}
}
function _dropAction(drop, index) {
const item = drop.source
if (Helpers.isValidInstanceOf(item, Widgets.DragItem)) {
item.getSelectedInputItem().then(inputItems => {
if (index === undefined)
DialogsProvider.playlistsDialog(inputItems)
else
root.model.append(root.model.getItemId(index), inputItems)
})
drop.accepted = true
} else if (drop.hasUrls) {
const urlList = []
for (let url in drop.urls)
urlList.push(drop.urls[url])
if (index === undefined)
DialogsProvider.playlistsDialog(inputItems)
else
root.model.append(root.model.getItemId(index), urlList)
drop.accepted = true
} else {
drop.accepted = false
}
}
//---------------------------------------------------------------------------------------------
// Childs
//---------------------------------------------------------------------------------------------
DropArea {
anchors.fill: parent
onEntered: function(drag) {
root._adjustDragAccepted(drag)
}
onDropped: function(drop) {
root._dropAction(drop)
}
}
Widgets.ProgressIndicator {
id: progressIndicator
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.margins: VLCStyle.margin_small
visible: false
z: 99
text: qsTr("Processing...")
Timer {
id: visibilityTimer
interval: VLCStyle.duration_humanMoment
onTriggered: {
progressIndicator.visible = true
}
}
}
Widgets.MLDragItem {
id: dragItemPlaylist
objectName: "PlaylistMediaListDragItem"
mlModel: model
indexes: indexesFlat ? root.selectionModel.selectedIndexesFlat
: root.selectionModel.selectedIndexes
indexesFlat: !!root.selectionModel.selectedIndexesFlat
coverRole: "thumbnail"
defaultCover: root._placeHolder
}
PlaylistListContextMenu {
id: contextMenu
model: root.model
}
// TBD: Refactor this with MusicGenres ?
Component {
id: grid
MainInterface.MainGridView {
id: gridView
//-------------------------------------------------------------------------------------
// Settings
cellWidth : _width
cellHeight: _height
model: root.model
selectionModel: root.selectionModel
headerDelegate: root.header
Navigation.parentItem: root
delegate: VideoGridItem {
//---------------------------------------------------------------------------------
// Properties
property var model: ({})
property int index: -1
//---------------------------------------------------------------------------------
// Settings
pictureWidth : _widthCover
pictureHeight: _heightCover
title: (model.name) ? model.name
: qsTr("Unknown title")
labels: (model.count > 1) ? [ qsTr("%1 Tracks").arg(_getCount(model)) ]
: [ qsTr("%1 Track") .arg(_getCount(model)) ]
dragItem: dragItemPlaylist
//---------------------------------------------------------------------------------
// Events
onItemClicked: (modifier) => { gridView.leftClickOnItem(modifier, index) }
onItemDoubleClicked: showList(model, Qt.MouseFocusReason)
onPlayClicked: if (model.id) MediaLib.addAndPlay(model.id)
onContextMenuButtonClicked: (_, globalMousePos) => {
gridView.rightClickOnItem(index);
contextMenu.popup(selectionModel.selectedRows(), globalMousePos);
}
//---------------------------------------------------------------------------------
// Animations
Behavior on opacity { NumberAnimation { duration: VLCStyle.duration_short } }
DropArea {
anchors.fill: parent
onEntered: function(drag) {
root._adjustDragAccepted(drag)
}
onDropped: function(drop) {
root._dropAction(drop, index)
}
}
}
//-------------------------------------------------------------------------------------
// Events
// NOTE: Define the initial position and selection. This is done on activeFocus rather
// than Component.onCompleted because selectionModel.selectedGroup update itself
// after this event.
onActiveFocusChanged: {
if (activeFocus == false || model.count === 0 || selectionModel.hasSelection)
return;
selectionModel.select(model.index(0,0), ItemSelectionModel.ClearAndSelect)
}
onActionAtIndex: _actionAtIndex()
//-------------------------------------------------------------------------------------
// Childs
}
}
Component {
id: table
MainInterface.MainTableView {
id: tableView
//-------------------------------------------------------------------------------------
// Properties
property int _columns: Math.max(1, VLCStyle.gridColumnsForWidth(availableRowWidth) - 2)
property var _modelSmall: [{
size: Math.max(2, _columns),
model: {
criteria: "name",
subCriterias: [ "count" ],
text: qsTr("Name"),
headerDelegate: columns.titleHeaderDelegate,
colDelegate : columns.titleDelegate
}
}]
property var _modelMedium: [{
size: 1,
model: {
criteria: "thumbnail",
text: qsTr("Cover"),
isSortable: false,
headerDelegate: columns.titleHeaderDelegate,
colDelegate : columns.titleDelegate
}
}, {
size: _columns,
model: {
criteria: "name",
text: qsTr("Name")
}
}, {
size: 1,
model: {
criteria: "count",
text: qsTr("Tracks"),
isSortable: false
}
}]
//-------------------------------------------------------------------------------------
// Settings
rowHeight: VLCStyle.tableCoverRow_height
model: root.model
sortModel: (availableRowWidth < VLCStyle.colWidth(4)) ? _modelSmall
: _modelMedium
selectionModel: root.selectionModel
dragItem: dragItemPlaylist
header: root.header
acceptDrop: true
Navigation.parentItem: root
//-------------------------------------------------------------------------------------
// Events
onActionForSelection: _actionAtIndex()
onItemDoubleClicked: (_, model) => showList(model, Qt.MouseFocusReason)
onContextMenuButtonClicked: (_, _, globalMousePos) => {
contextMenu.popup(selectionModel.selectedRows(), globalMousePos)
}
onRightClick: (_, _, globalMousePos) => {
contextMenu.popup(selectionModel.selectedRows(), globalMousePos)
}
onDropEntered: (_, _, drag, _) => {
root._adjustDragAccepted(drag)
}
onDropEvent: (_, index, _, drop, _) => {
root._dropAction(drop, index)
}
//-------------------------------------------------------------------------------------
// Childs
Widgets.TableColumns {
id: columns
showTitleText: (tableView.sortModel === tableView._modelSmall)
showCriterias: showTitleText
criteriaCover: "thumbnail"
//---------------------------------------------------------------------------------
// NOTE: When it's music we want the cover to be square
titleCover_width: (isMusic) ? VLCStyle.trackListAlbumCover_width
: VLCStyle.listAlbumCover_width
titleCover_height: (isMusic) ? VLCStyle.trackListAlbumCover_heigth
: VLCStyle.listAlbumCover_height
titleCover_radius: (isMusic) ? VLCStyle.trackListAlbumCover_radius
: VLCStyle.listAlbumCover_radius
//---------------------------------------------------------------------------------
// NOTE: This makes sure we display the playlist count on the item.
function titlecoverLabels(model) {
return [ _getCount(model) ];
}
}
}
}
Component {
id: emptyLabel
Widgets.EmptyLabelHint {
visible: (model.count === 0)
focus: true
text: qsTr("No playlists found")
hint: qsTr("Right click on a media to add it to a playlist")
cover: VLCStyle.noArtAlbumCover
}
}
}