1
mirror of https://code.videolan.org/videolan/vlc synced 2024-08-27 04:21:53 +02:00

qml: Create MediaGroupList

Signed-off-by: Pierre Lamot <pierre@videolabs.io>
This commit is contained in:
Benjamin Arnaud 2021-03-18 10:01:33 +01:00 committed by Pierre Lamot
parent 2ad8e3001f
commit 0278e9dad0
5 changed files with 413 additions and 3 deletions

View File

@ -671,6 +671,7 @@ libqt_plugin_la_QML = \
gui/qt/medialibrary/qml/ArtistTopBanner.qml \
gui/qt/medialibrary/qml/AudioGridItem.qml \
gui/qt/medialibrary/qml/EmptyLabel.qml \
gui/qt/medialibrary/qml/MediaGroupList.qml \
gui/qt/medialibrary/qml/MusicAlbums.qml \
gui/qt/medialibrary/qml/MusicAlbumsDisplay.qml \
gui/qt/medialibrary/qml/MusicAlbumsGridExpandDelegate.qml \

View File

@ -0,0 +1,393 @@
/*****************************************************************************
* Copyright (C) 2021 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 2.11
import org.videolan.medialib 0.1
import "qrc:///widgets/" as Widgets
import "qrc:///main/" as MainInterface
import "qrc:///util/" as Util
import "qrc:///style/"
Widgets.NavigableFocusScope {
id: root
//---------------------------------------------------------------------------------------------
// Properties
//---------------------------------------------------------------------------------------------
readonly property int currentIndex: currentItem.currentIndex
property int initialIndex: 0
property var sortModel: [
{ text: i18n.qtr("Alphabetic"), criteria: "name" },
{ text: i18n.qtr("Date"), criteria: "date" }
]
//---------------------------------------------------------------------------------------------
// Alias
//---------------------------------------------------------------------------------------------
property alias model: model
property alias currentItem: view.currentItem
//---------------------------------------------------------------------------------------------
// Signals
//---------------------------------------------------------------------------------------------
signal showList(variant model)
//---------------------------------------------------------------------------------------------
// Settings
//---------------------------------------------------------------------------------------------
navigationCancel: function() {
if (currentItem.currentIndex > 0) {
currentItem.currentIndex = 0;
currentItem.positionViewAtIndex(0, ItemView.Contain);
} else {
defaultNavigationCancel();
}
}
//---------------------------------------------------------------------------------------------
// Events
//---------------------------------------------------------------------------------------------
onModelChanged: resetFocus()
onInitialIndexChanged: resetFocus()
//---------------------------------------------------------------------------------------------
// Connections
//---------------------------------------------------------------------------------------------
Connections {
target: mainInterface
onGridViewChanged: {
if (mainInterface.gridView) view.replace(grid);
else view.replace(list);
}
}
//---------------------------------------------------------------------------------------------
// Functions
//---------------------------------------------------------------------------------------------
function setCurrentItemFocus() { listView.currentItem.forceActiveFocus() }
function resetFocus() {
if (model.count === 0) return;
var initialIndex = root.initialIndex;
if (initialIndex >= model.count)
initialIndex = 0;
modelSelect.select(model.index(initialIndex, 0), ItemSelectionModel.ClearAndSelect);
if (currentItem)
currentItem.positionViewAtIndex(initialIndex, ItemView.Contain);
}
//---------------------------------------------------------------------------------------------
// Private
function _actionAtIndex() {
if (modelSelect.selectedIndexes.length > 1) {
_play(model.getIdsForIndexes(modelSelect.selectedIndexes));
} else if (modelSelect.selectedIndexes.length === 1) {
var index = modelSelect.selectedIndexes[0];
_showList(model.getDataAt(index));
}
}
function _play(ids) {
g_mainDisplay.showPlayer();
medialib.addAndPlay(ids);
}
function _showList(model)
{
// NOTE: If the count is 1 we consider the group is a media.
if (model.count == 1)
_play(model.id);
else
showList(model);
}
//---------------------------------------------------------------------------------------------
function _getLabels(model, string)
{
var count = model.count;
if (count === 1) {
return [
model.resolution_name || "",
model.channel || ""
].filter(function(a) { return a !== "" });
} else return [
string.arg(count)
];
}
//---------------------------------------------------------------------------------------------
// Childs
//---------------------------------------------------------------------------------------------
MLGroupListModel {
id: model
ml: medialib
onCountChanged: {
if (count === 0 || modelSelect.hasSelection) return;
resetFocus();
}
}
Util.SelectableDelegateModel {
id: modelSelect
model: root.model
}
Widgets.StackViewExt {
id: view
anchors.fill: parent
initialItem: (mainInterface.gridView) ? grid : list
focus: (model.count !== 0)
}
GroupListContextMenu {
id: contextMenu
model: root.model
}
Widgets.DragItem {
id: dragItem
function updateComponents(maxCovers) {
var items = modelSelect.selectedIndexes.slice(0, maxCovers).map(function (x){
return model.getDataAt(x.row);
})
var covers = items.map(function (item) {
return { artwork: item.thumbnail || VLCStyle.noArtCover }
});
var title = items.map(function (item) {
return item.title
}).join(", ");
return {
covers: covers,
title: title,
count: modelSelect.selectedIndexes.length
}
}
function getSelectedInputItem() {
return model.getItemsForIndexes(modelSelect.selectedIndexes);
}
}
//---------------------------------------------------------------------------------------------
// Components
Component {
id: grid
MainInterface.MainGridView {
id: gridView
//-------------------------------------------------------------------------------------
// Settings
cellWidth : VLCStyle.gridItem_video_width
cellHeight: VLCStyle.gridItem_video_height
topMargin: VLCStyle.margin_large
model: root.model
delegateModel: modelSelect
activeFocusOnTab: true
navigationParent: root
expandDelegate: VideoInfoExpandPanel {
width: gridView.width
x: 0
navigationParent: gridView
navigationUp : function() { gridView.retract() }
navigationDown : function() { gridView.retract() }
navigationCancel: function() { gridView.retract() }
onRetract: gridView.retract()
}
delegate: VideoGridItem {
id: gridItem
//---------------------------------------------------------------------------------
// Settings
opacity: (gridView.expandIndex !== -1
&&
gridView.expandIndex !== gridItem.index) ? 0.7 : 1
title: (model.name) ? model.name
: i18n.qtr("Unknown title")
labels: _getLabels(model, i18n.qtr("%1 Medias"))
// NOTE: We don't want to show the indicator for a group.
showNewIndicator: (model.count === 1)
dragItem: root.dragItem
//---------------------------------------------------------------------------------
// Events
onItemClicked: gridView.leftClickOnItem(modifier, index)
onItemDoubleClicked: _showList(model)
onContextMenuButtonClicked: {
gridView.rightClickOnItem(index);
contextMenu.popup(modelSelect.selectedIndexes, globalMousePos,
{ "information" : index });
}
//---------------------------------------------------------------------------------
// Animations
Behavior on opacity { NumberAnimation { duration: 100 } }
}
//-------------------------------------------------------------------------------------
// Events
// NOTE: Define the initial position and selection. This is done on activeFocus rather
// than Component.onCompleted because modelSelect.selectedGroup update itself
// after this event.
onActiveFocusChanged: {
if (activeFocus == false || model.count === 0 || modelSelect.hasSelection) return;
modelSelect.select(model.index(0,0), ItemSelectionModel.ClearAndSelect)
}
onSelectAll: modelSelect.selectAll()
onSelectionUpdated: modelSelect.updateSelection(keyModifiers, oldIndex, newIndex)
onActionAtIndex: _actionAtIndex()
//-------------------------------------------------------------------------------------
// Connections
Connections {
target: contextMenu
onShowMediaInformation: gridView.switchExpandItem(index)
}
}
}
Component {
id: list
VideoListDisplay
{
id: listView
//-------------------------------------------------------------------------------------
// Settings
model: root.model
mainCriteria: "name"
selectionDelegateModel: modelSelect
dragItem: root.dragItem
header: root.header
headerTopPadding: VLCStyle.margin_normal
headerPositioning: ListView.InlineHeader
navigationParent: root
//-------------------------------------------------------------------------------------
// Events
onActionForSelection: _actionAtIndex()
onContextMenuButtonClicked: contextMenu.popup(modelSelect.selectedIndexes,
menuParent.mapToGlobal(0,0))
onRightClick: contextMenu.popup(modelSelect.selectedIndexes, globalMousePos)
//-------------------------------------------------------------------------------------
// Functions
function onLabels(model) {
return _getLabels(model, "%1");
}
}
}
EmptyLabel {
anchors.fill: parent
coverWidth : VLCStyle.dp(182, VLCStyle.scale)
coverHeight: VLCStyle.dp(114, VLCStyle.scale)
visible: (model.count === 0)
text: i18n.qtr("No video found\nPlease try adding sources, by going to the Network tab")
cover: VLCStyle.noArtVideoCover
navigationParent: root
focus: visible
}
}

View File

@ -99,6 +99,22 @@ MainInterface.MainTableView {
}
}
//---------------------------------------------------------------------------------------------
// Functions
//---------------------------------------------------------------------------------------------
// Events
function onLabels(model)
{
if (model === null)
return [];
return [
model.resolution_name || "",
model.channel || ""
].filter(function(a) { return a !== "" });
}
//---------------------------------------------------------------------------------------------
// Childs
//---------------------------------------------------------------------------------------------
@ -112,9 +128,7 @@ MainInterface.MainTableView {
titleCover_radius: VLCStyle.listAlbumCover_radius
function titlecoverLabels(model) {
return [!model ? "" : model.resolution_name
, model ? "" : model.channel
].filter(function(a) { return a !== "" })
return listView_id.onLabels(model);
}
}
}

View File

@ -271,6 +271,7 @@
</qresource>
<qresource prefix="/medialibrary">
<file alias="EmptyLabel.qml">medialibrary/qml/EmptyLabel.qml</file>
<file alias="MediaGroupList.qml">medialibrary/qml/MediaGroupList.qml</file>
<file alias="MusicAlbums.qml">medialibrary/qml/MusicAlbums.qml</file>
<file alias="MusicDisplay.qml">medialibrary/qml/MusicDisplay.qml</file>
<file alias="MusicGenres.qml">medialibrary/qml/MusicGenres.qml</file>

View File

@ -818,6 +818,7 @@ modules/gui/qt/maininterface/qml/MainInterface.qml
modules/gui/qt/maininterface/qml/NoMedialibHome.qml
modules/gui/qt/medialibrary/qml/ArtistTopBanner.qml
modules/gui/qt/medialibrary/qml/AudioGridItem.qml
modules/gui/qt/medialibrary/qml/MediaGroupList.qml
modules/gui/qt/medialibrary/qml/MusicAlbums.qml
modules/gui/qt/medialibrary/qml/MusicAlbumsDisplay.qml
modules/gui/qt/medialibrary/qml/MusicAlbumsGridExpandDelegate.qml