vlc | branch: master | Benjamin Arnaud <benjamin.arn...@videolabs.io> | Fri Feb 19 11:25:34 2021 +0100| [12be577e6811bc0387d3776c81314bf990907faa] | committer: Pierre Lamot
qml: split VideoDisplay content in VideoAllDisplay and VideoList Signed-off-by: Pierre Lamot <pie...@videolabs.io> > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=12be577e6811bc0387d3776c81314bf990907faa --- modules/gui/qt/Makefile.am | 2 + modules/gui/qt/medialibrary/qml/VideoAll.qml | 352 +++++++++++++++++++++ .../gui/qt/medialibrary/qml/VideoAllDisplay.qml | 102 ++++++ modules/gui/qt/medialibrary/qml/VideoDisplay.qml | 290 +++-------------- modules/gui/qt/vlc.qrc | 2 + po/POTFILES.in | 2 + 6 files changed, 503 insertions(+), 247 deletions(-) diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am index 7de2b03424..6df4b7c71b 100644 --- a/modules/gui/qt/Makefile.am +++ b/modules/gui/qt/Makefile.am @@ -664,6 +664,8 @@ libqt_plugin_la_QML = \ gui/qt/medialibrary/qml/MusicTracksDisplay.qml \ gui/qt/medialibrary/qml/UrlListDisplay.qml \ gui/qt/medialibrary/qml/VideoDisplay.qml \ + gui/qt/medialibrary/qml/VideoAll.qml \ + gui/qt/medialibrary/qml/VideoAllDisplay.qml \ gui/qt/medialibrary/qml/VideoDisplayRecentVideos.qml \ gui/qt/medialibrary/qml/VideoGridItem.qml \ gui/qt/medialibrary/qml/VideoInfoExpandPanel.qml \ diff --git a/modules/gui/qt/medialibrary/qml/VideoAll.qml b/modules/gui/qt/medialibrary/qml/VideoAll.qml new file mode 100644 index 0000000000..0287a61907 --- /dev/null +++ b/modules/gui/qt/medialibrary/qml/VideoAll.qml @@ -0,0 +1,352 @@ +/***************************************************************************** + * Copyright (C) 2021 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 2.11 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.3 +import QtQml.Models 2.2 + +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 + //--------------------------------------------------------------------------------------------- + + // NOTE: Specify an optionnal header for the view. + property Component header: undefined + + property Item headerItem: (currentItem) ? currentItem.headerItem : undefined + + readonly property int currentIndex: currentItem.currentIndex + + property int initialIndex: 0 + + property var sortModel: [ + { text: i18n.qtr("Alphabetic"), criteria: "title" }, + { text: i18n.qtr("Duration"), criteria: "duration_short" } + ] + + //--------------------------------------------------------------------------------------------- + // Aliases + //--------------------------------------------------------------------------------------------- + + // NOTE: This is used to determine which media(s) shall be displayed. + property alias parentId: model.parentId + + property alias model: model + + property alias currentItem: view.currentItem + + //--------------------------------------------------------------------------------------------- + + property alias dragItem: dragItem + + //--------------------------------------------------------------------------------------------- + // Settings + //--------------------------------------------------------------------------------------------- + + navigationCancel: function() { + if (currentItem.currentIndex <= 0) { + defaultNavigationCancel() + } else { + currentItem.currentIndex = 0; + + currentItem.positionViewAtIndex(0, ItemView.Contain); + } + } + + //--------------------------------------------------------------------------------------------- + // 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(index) { + g_mainDisplay.showPlayer(); + + medialib.addAndPlay(model.getIdsForIndexes(modelSelect.selectedIndexes)); + } + + //--------------------------------------------------------------------------------------------- + // Childs + //--------------------------------------------------------------------------------------------- + + MLVideoModel { + id: model + + ml: medialib + + onCountChanged: { + if (count === 0 || modelSelect.hasSelection) return; + + resetFocus(); + } + } + + Widgets.StackViewExt { + id: view + + anchors.fill: parent + + clip: true + + initialItem: (mainInterface.gridView) ? grid : list + + focus: (model.count !== 0) + } + + 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 insertIntoPlaylist(index) { + medialib.insertIntoPlaylist(index, + model.getIdsForIndexes(modelSelect.selectedIndexes)); + } + } + + Util.SelectableDelegateModel { + id: modelSelect + + model: root.model + } + + VideoContextMenu { + id: contextMenu + + model: root.model + } + + Component { + id: grid + + MainInterface.MainGridView { + id: gridView + + //------------------------------------------------------------------------------------- + // Properties + + property Item currentItem: Item{} + + //------------------------------------------------------------------------------------- + // Settings + + cellWidth : VLCStyle.gridItem_video_width + cellHeight: VLCStyle.gridItem_video_height + + model: root.model + + delegateModel: modelSelect + + headerDelegate: root.header + + activeFocusOnTab: true + + navigationParent: root + navigationUpItem: (headerItem) ? headerItem.focusItem : undefined + + expandDelegate: VideoInfoExpandPanel { + width: gridView.width + + x: 0 + + navigationParent: gridView + + navigationCancel: function() { gridView.retract() } + navigationUp : function() { gridView.retract() } + navigationDown : function() { gridView.retract() } + + onRetract: gridView.retract() + } + + delegate: VideoGridItem { + id: gridItem + + //--------------------------------------------------------------------------------- + // Settings + + opacity: (gridView.expandIndex !== -1 + && + gridView.expandIndex !== gridItem.index) ? 0.7 : 1 + + dragItem: root.dragItem + + //--------------------------------------------------------------------------------- + // Events + + onItemClicked: gridView.leftClickOnItem(modifier, index) + + 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(index) + + //------------------------------------------------------------------------------------- + // Connections + + Connections { + target: contextMenu + + onShowMediaInformation: gridView.switchExpandItem(index) + } + } + } + + Component { + id: list + + VideoListDisplay + { + id: listView + + //------------------------------------------------------------------------------------- + // Settings + + model: root.model + + selectionDelegateModel: modelSelect + + dragItem: root.dragItem + + header: root.header + + headerTopPadding: VLCStyle.margin_normal + + headerPositioning: ListView.InlineHeader + + navigationParent: root + navigationUpItem: (headerItem) ? headerItem.focus : undefined + + //------------------------------------------------------------------------------------- + // Events + + onContextMenuButtonClicked: contextMenu.popup(modelSelect.selectedIndexes, + menuParent.mapToGlobal(0,0)) + + onRightClick: contextMenu.popup(modelSelect.selectedIndexes, globalMousePos) + } + } + + 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 + } +} diff --git a/modules/gui/qt/medialibrary/qml/VideoAllDisplay.qml b/modules/gui/qt/medialibrary/qml/VideoAllDisplay.qml new file mode 100644 index 0000000000..e2f5675970 --- /dev/null +++ b/modules/gui/qt/medialibrary/qml/VideoAllDisplay.qml @@ -0,0 +1,102 @@ +/***************************************************************************** + * Copyright (C) 2021 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 2.11 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.3 +import QtQml.Models 2.2 + +import org.videolan.medialib 0.1 + +import "qrc:///widgets/" as Widgets +import "qrc:///main/" as MainInterface +import "qrc:///util/" as Util +import "qrc:///style/" + +VideoAll { + id: root + + //--------------------------------------------------------------------------------------------- + // Events + //--------------------------------------------------------------------------------------------- + + onCurrentIndexChanged: { + history.update([ "mc", "video", { "initialIndex": currentIndex }]) + } + + //--------------------------------------------------------------------------------------------- + // Settings + //--------------------------------------------------------------------------------------------- + + MLRecentsVideoModel { + id: modelRecent + + ml: medialib + } + + header: Column { + property Item focusItem: loader.item.focusItem + + width: root.width + + topPadding: VLCStyle.margin_normal + + spacing: VLCStyle.margin_normal + + Loader { + id: loader + + width: parent.width + + height: item.implicitHeight + + active: (modelRecent.count) + + visible: active + + focus: true + + sourceComponent: VideoDisplayRecentVideos { + id: component + + width: parent.width + + model: modelRecent + + focus: true + + navigationParent: root + + navigationDown: function() { + component.focus = false; + + currentItem.setCurrentItemFocus(); + } + } + } + + Widgets.SubtitleLabel { + width: root.width + + leftPadding : VLCStyle.margin_xlarge + bottomPadding: VLCStyle.margin_xsmall + + text: i18n.qtr("Videos") + } + } +} diff --git a/modules/gui/qt/medialibrary/qml/VideoDisplay.qml b/modules/gui/qt/medialibrary/qml/VideoDisplay.qml index 77ca86efb1..6a6d8c43a2 100644 --- a/modules/gui/qt/medialibrary/qml/VideoDisplay.qml +++ b/modules/gui/qt/medialibrary/qml/VideoDisplay.qml @@ -15,279 +15,75 @@ * 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 QtQuick 2.11 import QtQuick.Controls 2.4 -import QtQuick.Layouts 1.3 -import QtQml.Models 2.2 +import QtQuick.Layouts 1.3 +import QtQml.Models 2.2 import org.videolan.medialib 0.1 -import "qrc:///util/" as Util import "qrc:///widgets/" as Widgets -import "qrc:///main/" as MainInterface import "qrc:///style/" -Widgets.NavigableFocusScope { +Widgets.PageLoader { id: root - readonly property var currentIndex: view.currentItem.currentIndex - property Item headerItem: view.currentItem.headerItem - //the index to "go to" when the view is loaded - property var initialIndex: 0 - - property alias contentModel: videoModel - property var sortModel: [ - { text: i18n.qtr("Alphabetic"), criteria: "title"}, - { text: i18n.qtr("Duration"), criteria: "duration_short" } - ] - - navigationCancel: function() { - if (view.currentItem.currentIndex <= 0) { - defaultNavigationCancel() - } else { - view.currentItem.currentIndex = 0; - view.currentItem.positionViewAtIndex(0, ItemView.Contain) - } - } - - onCurrentIndexChanged: { - history.update([ "mc", "video", {"initialIndex": currentIndex}]) - } - - onInitialIndexChanged: resetFocus() - onContentModelChanged: resetFocus() - function resetFocus() { - if (videoModel.count === 0) - return - var initialIndex = root.initialIndex - if (initialIndex >= videoModel.count) - initialIndex = 0 - selectionModel.select(videoModel.index(initialIndex, 0), ItemSelectionModel.ClearAndSelect) - if (view.currentItem) - view.currentItem.positionViewAtIndex(initialIndex, ItemView.Contain) - } + //--------------------------------------------------------------------------------------------- + // Properties + //--------------------------------------------------------------------------------------------- - function _actionAtIndex(index) { - g_mainDisplay.showPlayer() - medialib.addAndPlay( videoModel.getIdsForIndexes( selectionModel.selectedIndexes ) ) - } + property bool isViewMultiView: true - MLVideoModel { - id: videoModel - ml: medialib + property var contentModel + property var sortModel - onCountChanged: { - if (videoModel.count > 0 && !selectionModel.hasSelection) { - root.resetFocus() - } + property var tabModel: ListModel { + Component.onCompleted: { + pageModel.forEach(function(e) { + append({ + name : e.name, + displayText: e.displayText + }) + }) } } - Util.SelectableDelegateModel { - id: selectionModel - model: videoModel - } + property Component localMenuDelegate: Widgets.LocalTabBar { + currentView: root.view - VideoContextMenu { - id: contextMenu - model: videoModel - } + model: tabModel - MLRecentsVideoModel { - id: recentVideoModel - ml: medialib + onClicked: root.loadIndex(index) } - property Component header: Column { - id: videoHeader - - width: root.width + //--------------------------------------------------------------------------------------------- + // Settings + //--------------------------------------------------------------------------------------------- - property Item focusItem: recentVideosViewLoader.item.focusItem + defaultPage: "all" - topPadding: VLCStyle.margin_normal - spacing: VLCStyle.margin_normal - - Loader { - id: recentVideosViewLoader - width: parent.width - height: item.implicitHeight - active: recentVideoModel.count > 0 - visible: recentVideoModel.count > 0 - focus: true - - sourceComponent: VideoDisplayRecentVideos { - id: recentVideosView - - width: parent.width - - model: recentVideoModel - focus: true - navigationParent: root - navigationDown: function() { - recentVideosView.focus = false - view.currentItem.setCurrentItemFocus() - } - } + pageModel: [{ + name: "all", + displayText: i18n.qtr("All"), + url: "qrc:///medialibrary/VideoAllDisplay.qml" } + ] - Widgets.SubtitleLabel { - id: videosLabel - leftPadding: VLCStyle.margin_xlarge - bottomPadding: VLCStyle.margin_xsmall - width: root.width - text: i18n.qtr("Videos") - } - } - - Component { - id: gridComponent - - MainInterface.MainGridView { - id: videosGV - property Item currentItem: Item{} - - activeFocusOnTab:true - delegateModel: selectionModel - model: videoModel - - headerDelegate: root.header - - delegate: VideoGridItem { - id: videoGridItem - - opacity: videosGV.expandIndex !== -1 && videosGV.expandIndex !== videoGridItem.index ? .7 : 1 - dragItem: videoDragItem - - onContextMenuButtonClicked: { - videosGV.rightClickOnItem(index) - contextMenu.popup(selectionModel.selectedIndexes, globalMousePos, { - "information" : index - } ) - } - - onItemClicked : videosGV.leftClickOnItem(modifier, index) - - Behavior on opacity { - NumberAnimation { - duration: 100 - } - } - } - - expandDelegate: VideoInfoExpandPanel { - onRetract: videosGV.retract() - - x: 0 - width: videosGV.width - navigationParent: videosGV - navigationCancel: function() { videosGV.retract() } - navigationUp: function() { videosGV.retract() } - navigationDown: function() { videosGV.retract() } - } - - navigationParent: root - navigationUpItem: headerItem.focusItem - - /* - *define the intial position/selection - * This is done on activeFocus rather than Component.onCompleted because selectionModel. - * selectedGroup update itself after this event - */ - onActiveFocusChanged: { - if (activeFocus && videoModel.count > 0 && !selectionModel.hasSelection) { - selectionModel.select(videoModel.index(0,0), ItemSelectionModel.ClearAndSelect) - } - } - - cellWidth: VLCStyle.gridItem_video_width - cellHeight: VLCStyle.gridItem_video_height - - onSelectAll:selectionModel.selectAll() - onSelectionUpdated: selectionModel.updateSelection( keyModifiers, oldIndex, newIndex ) - onActionAtIndex: _actionAtIndex( index ) - - Connections { - target: contextMenu - onShowMediaInformation: videosGV.switchExpandItem(index) - } - } - - } - - Component { - id: listComponent - VideoListDisplay { - height: view.height - width: view.width - model: videoModel - dragItem: videoDragItem - navigationParent: root - navigationUpItem: headerItem.focusItem - - header: root.header - headerTopPadding: VLCStyle.margin_normal - headerPositioning: ListView.InlineHeader - - selectionDelegateModel: selectionModel - - onContextMenuButtonClicked: contextMenu.popup(selectionModel.selectedIndexes, menuParent.mapToGlobal(0,0) ) - - onRightClick: contextMenu.popup(selectionModel.selectedIndexes, globalMousePos ) - - function setCurrentItemFocus() { - currentItem.forceActiveFocus() - } - } - } - - - Widgets.DragItem { - id: videoDragItem - - function updateComponents(maxCovers) { - var items = selectionModel.selectedIndexes.slice(0, maxCovers).map(function (x){ - return videoModel.getDataAt(x.row) - }) - var title = items.map(function (item){ return item.title}).join(", ") - var covers = items.map(function (item) { return {artwork: item.thumbnail || VLCStyle.noArtCover}}) - return { - covers: covers, - title: title, - count: selectionModel.selectedIndexes.length - } - } + onCurrentItemChanged: { + isViewMultiView = (currentItem.isViewMultiView === undefined + || + currentItem.isViewMultiView); - function insertIntoPlaylist(index) { - medialib.insertIntoPlaylist(index, videoModel.getIdsForIndexes(selectionModel.selectedIndexes)) - } + contentModel = currentItem.model; + sortModel = currentItem.sortModel; } - Widgets.StackViewExt { - id: view - anchors.fill:parent - clip: true - focus: videoModel.count !== 0 - initialItem: mainInterface.gridView ? gridComponent : listComponent - Connections { - target: mainInterface - onGridViewChanged: { - if (mainInterface.gridView) - view.replace(gridComponent) - else - view.replace(listComponent) - } - } - } + //--------------------------------------------------------------------------------------------- + // Functions + //--------------------------------------------------------------------------------------------- - EmptyLabel { - anchors.fill: parent - visible: videoModel.count === 0 - focus: visible - text: i18n.qtr("No video found\nPlease try adding sources, by going to the Network tab") - navigationParent: root - cover: VLCStyle.noArtVideoCover - coverWidth: VLCStyle.dp(182, VLCStyle.scale) - coverHeight: VLCStyle.dp(114, VLCStyle.scale) + function loadIndex(index) { + history.push(["mc", "video", root.pageModel[index].name]); } } diff --git a/modules/gui/qt/vlc.qrc b/modules/gui/qt/vlc.qrc index f04c4e7eea..59504dfd18 100644 --- a/modules/gui/qt/vlc.qrc +++ b/modules/gui/qt/vlc.qrc @@ -270,6 +270,8 @@ <file alias="MusicDisplay.qml">medialibrary/qml/MusicDisplay.qml</file> <file alias="MusicGenres.qml">medialibrary/qml/MusicGenres.qml</file> <file alias="VideoDisplay.qml">medialibrary/qml/VideoDisplay.qml</file> + <file alias="VideoAll.qml">medialibrary/qml/VideoAll.qml</file> + <file alias="VideoAllDisplay.qml">medialibrary/qml/VideoAllDisplay.qml</file> <file alias="MusicAlbumsDisplay.qml">medialibrary/qml/MusicAlbumsDisplay.qml</file> <file alias="MusicAlbumsGridExpandDelegate.qml">medialibrary/qml/MusicAlbumsGridExpandDelegate.qml</file> <file alias="MusicArtist.qml">medialibrary/qml/MusicArtist.qml</file> diff --git a/po/POTFILES.in b/po/POTFILES.in index 7e525cd659..f0a7a984b6 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -826,6 +826,8 @@ modules/gui/qt/medialibrary/qml/VideoDisplayRecentVideos.qml modules/gui/qt/medialibrary/qml/VideoGridItem.qml modules/gui/qt/medialibrary/qml/VideoInfoExpandPanel.qml modules/gui/qt/medialibrary/qml/VideoListDisplay.qml +modules/gui/qt/medialibrary/qml/VideoAll.qml +modules/gui/qt/medialibrary/qml/VideoAllDisplay.qml modules/gui/qt/menus/qml/Menubar.qml modules/gui/qt/network/qml/DiscoverDisplay.qml modules/gui/qt/network/qml/DiscoverUrlDisplay.qml _______________________________________________ vlc-commits mailing list vlc-commits@videolan.org https://mailman.videolan.org/listinfo/vlc-commits