From 882c3ea0da2a6dc231987ac13d3f03866331c4ff Mon Sep 17 00:00:00 2001
From: Aleksey Kuznetsov <Aleksey.Kuznetsov@noveogroup.com>
Date: Wed, 30 Dec 2020 16:18:57 +0700
Subject: [PATCH] qml: medialib: Continue watching video section added to the
 section "Video"

"Continue watching" section with list of videos the user started playing
added at the top of the "Video" section.
Scrolling the "Video" page is performed on all area vertically.
Scrolling the "Continue watching" section is horizontal.
In order to preserve loading items on scrolling the content the Continue watching
video section inserted at the top as header of grid view and of list view.

Signed-off-by: Pierre Lamot <pierre@videolabs.io>
---
 modules/gui/qt/Makefile.am                    |   1 +
 .../gui/qt/medialibrary/qml/VideoDisplay.qml  |  70 ++++++--
 .../qml/VideoDisplayRecentVideos.qml          | 157 ++++++++++++++++++
 modules/gui/qt/style/VLCStyle.qml             |   9 +-
 modules/gui/qt/vlc.qrc                        |   1 +
 po/POTFILES.in                                |   1 +
 6 files changed, 226 insertions(+), 13 deletions(-)
 create mode 100644 modules/gui/qt/medialibrary/qml/VideoDisplayRecentVideos.qml

diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am
index 90a81e9e091c..37984f3a95a4 100644
--- a/modules/gui/qt/Makefile.am
+++ b/modules/gui/qt/Makefile.am
@@ -642,6 +642,7 @@ 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/VideoDisplayRecentVideos.qml \
 	gui/qt/medialibrary/qml/VideoGridItem.qml \
 	gui/qt/medialibrary/qml/VideoInfoExpandPanel.qml \
 	gui/qt/medialibrary/qml/VideoListDisplay.qml \
diff --git a/modules/gui/qt/medialibrary/qml/VideoDisplay.qml b/modules/gui/qt/medialibrary/qml/VideoDisplay.qml
index 6718e1b5dce1..9a9e36e0c669 100644
--- a/modules/gui/qt/medialibrary/qml/VideoDisplay.qml
+++ b/modules/gui/qt/medialibrary/qml/VideoDisplay.qml
@@ -30,10 +30,11 @@ import "qrc:///style/"
 Widgets.NavigableFocusScope {
     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 alias contentModel: videoModel
 
     navigationCancel: function() {
         if (view.currentItem.currentIndex <= 0) {
@@ -52,9 +53,8 @@ Widgets.NavigableFocusScope {
     onContentModelChanged: resetFocus()
 
     function resetFocus() {
-        if (videoModel.count === 0) {
+        if (videoModel.count === 0)
             return
-        }
         var initialIndex = root.initialIndex
         if (initialIndex >= videoModel.count)
             initialIndex = 0
@@ -89,6 +89,52 @@ Widgets.NavigableFocusScope {
         model: videoModel
     }
 
+    MLRecentsVideoModel {
+        id: recentVideoModel
+        ml: medialib
+    }
+
+    property Component header: Column {
+        id: videoHeader
+
+        width: root.width
+
+        property Item focusItem: recentVideosViewLoader.item.focusItem
+
+        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()
+                }
+            }
+        }
+
+        Widgets.SubtitleLabel {
+            id: videosLabel
+            leftPadding: VLCStyle.margin_xlarge
+            bottomPadding: VLCStyle.margin_xsmall
+            width: root.width
+            text: i18n.qtr("Videos")
+        }
+    }
 
     Component {
         id: gridComponent
@@ -101,13 +147,7 @@ Widgets.NavigableFocusScope {
             delegateModel: selectionModel
             model: videoModel
 
-            headerDelegate: Widgets.SubtitleLabel {
-                text: i18n.qtr("Videos")
-                leftPadding: videosGV.rowX
-                topPadding: VLCStyle.margin_large
-                bottomPadding: VLCStyle.margin_normal
-                width: root.width
-            }
+            headerDelegate: root.header
 
             delegate: VideoGridItem {
                 id: videoGridItem
@@ -141,6 +181,7 @@ Widgets.NavigableFocusScope {
             }
 
             navigationParent: root
+            navigationUpItem: headerItem.focusItem
 
             /*
              *define the intial position/selection
@@ -168,7 +209,6 @@ Widgets.NavigableFocusScope {
 
     }
 
-
     Component {
         id: listComponent
         VideoListDisplay {
@@ -177,7 +217,11 @@ Widgets.NavigableFocusScope {
             model: videoModel
             dragItem: videoDragItem
             navigationParent: root
+            navigationUpItem: headerItem.focusItem
+
+            header: root.header
             headerTopPadding: VLCStyle.margin_normal
+            headerPositioning: ListView.InlineHeader
 
             selectionDelegateModel: selectionModel
 
@@ -185,6 +229,9 @@ Widgets.NavigableFocusScope {
 
             onRightClick: contextMenu.popup(selectionModel.selectedIndexes, globalMousePos )
 
+            function setCurrentItemFocus() {
+                currentItem.forceActiveFocus()
+            }
         }
     }
 
@@ -225,7 +272,6 @@ Widgets.NavigableFocusScope {
                     view.replace(listComponent)
             }
         }
-
     }
 
     EmptyLabel {
diff --git a/modules/gui/qt/medialibrary/qml/VideoDisplayRecentVideos.qml b/modules/gui/qt/medialibrary/qml/VideoDisplayRecentVideos.qml
new file mode 100644
index 000000000000..afb1901ebd07
--- /dev/null
+++ b/modules/gui/qt/medialibrary/qml/VideoDisplayRecentVideos.qml
@@ -0,0 +1,157 @@
+/*****************************************************************************
+ * Copyright (C) 2019 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 QtQml.Models 2.2
+
+import org.videolan.medialib 0.1
+
+import "qrc:///widgets/" as Widgets
+import "qrc:///util/" as Util
+import "qrc:///style/"
+
+Widgets.NavigableFocusScope {
+    id: root
+
+    property Item focusItem: recentVideosListView
+    implicitHeight: recentVideosColumn.height
+
+    property int leftPadding: VLCStyle.margin_xlarge
+    property int rightPadding: VLCStyle.margin_xlarge
+
+    property int currentIndex: -1
+
+    property var model: undefined;
+
+    onCurrentIndexChanged: {
+        recentVideoListView.currentIndex = _currentIndex
+    }
+
+    function _actionAtIndex(index, model, selectionModel) {
+        g_mainDisplay.showPlayer()
+        medialib.addAndPlay( model.getIdsForIndexes( selectionModel.selectedIndexes ) )
+    }
+
+    onFocusChanged: {
+        if (activeFocus && root.currentIndex === -1 && root.model.count > 0)
+            root.currentIndex = 0
+    }
+
+
+    Util.SelectableDelegateModel {
+        id: recentVideoSelection
+        model: root.model
+    }
+
+    Column {
+        id: recentVideosColumn
+
+        width: root.width
+        spacing: VLCStyle.margin_xsmall
+
+        Widgets.SubtitleLabel {
+            id: continueWatchingLabel
+            leftPadding: VLCStyle.margin_xlarge
+            width: parent.width
+            text: i18n.qtr("Continue Watching")
+        }
+
+        Widgets.KeyNavigableListView {
+            id: recentVideosListView
+
+            width: parent.width
+            implicitHeight: VLCStyle.gridItem_video_height_large
+            spacing: VLCStyle.column_margin_width
+            orientation: ListView.Horizontal
+
+            focus: true
+            navigationParent: root
+            navigationDown: function() {
+                recentVideosListView.focus = false
+                view.currentItem.setCurrentItemFocus()
+            }
+
+            model: root.model
+
+            header: Item {
+                width: VLCStyle.margin_xlarge
+            }
+
+            delegate: Widgets.GridItem {
+                id: recentVideoGridItem
+
+                focus: true
+
+                image: model.thumbnail || VLCStyle.noArtCover
+                title: model.title || i18n.qtr("Unknown title")
+                subtitle: model.duration || ""
+                labels: [
+                    model.resolution_name || "",
+                    model.channel || ""
+                ].filter(function(a) { return a !== "" } )
+                progress: model.progress > 0 ? model.progress : 0
+                pictureWidth: VLCStyle.gridCover_video_width_large
+                pictureHeight: VLCStyle.gridCover_video_height_large
+                playCoverBorder.width: VLCStyle.gridCover_video_border
+                titleMargin: VLCStyle.margin_xxsmall
+                showNewIndicator: true
+                onItemDoubleClicked: {
+                    if ( model.id !== undefined ) {
+                        g_mainDisplay.showPlayer()
+                        medialib.addAndPlay( model.id )
+                    }
+                }
+
+                onPlayClicked: {
+                    if ( model.id !== undefined ) {
+                        g_mainDisplay.showPlayer()
+                        medialib.addAndPlay( model.id )
+                    }
+                }
+
+                onItemClicked: {
+                    recentVideoSelection.updateSelection( modifier , root.model.currentIndex, index )
+                    recentVideosListView.currentIndex = index
+                    recentVideosListView.forceActiveFocus()
+                }
+
+                Connections {
+                    target: recentVideoSelection
+
+                    onSelectionChanged: selected = recentVideoSelection.isSelected(root.model.index(index, 0))
+                }
+
+                Behavior on opacity {
+                    NumberAnimation {
+                        duration: 100
+                    }
+                }
+            }
+
+            footer: Item {
+                width: VLCStyle.margin_xlarge
+            }
+
+            onSelectionUpdated: recentVideoSelection.updateSelection( keyModifiers, oldIndex, newIndex )
+            onActionAtIndex: {
+                g_mainDisplay.showPlayer()
+                medialib.addAndPlay( model.getIdsForIndexes( recentVideoSelection.selectedIndexes ) )
+            }
+        }
+    }
+}
diff --git a/modules/gui/qt/style/VLCStyle.qml b/modules/gui/qt/style/VLCStyle.qml
index 376fec87ac4f..ff630430c50f 100644
--- a/modules/gui/qt/style/VLCStyle.qml
+++ b/modules/gui/qt/style/VLCStyle.qml
@@ -159,6 +159,9 @@ Item {
     property int gridCover_video_height: ( gridCover_video_width * 10.0 ) / 16
     property int gridCover_video_border: dp(3, scale)
 
+    property int gridCover_video_width_large: dp(406, scale)
+    property int gridCover_video_height_large: ( gridCover_video_width_large * 10.0 ) / 16
+
     property int expandCover_music_height: dp(171, scale)
     property int expandCover_music_width: dp(171, scale)
 
@@ -174,13 +177,17 @@ Item {
     property int gridItem_video_width: VLCStyle.gridCover_video_width
     property int gridItem_video_height: VLCStyle.gridCover_video_height + VLCStyle.margin_xxsmall + VLCStyle.fontHeight_normal + VLCStyle.fontHeight_small
 
+    property int gridItem_video_width_large: VLCStyle.gridCover_video_width_large
+    property int gridItem_video_height_large: VLCStyle.gridCover_video_height_large + VLCStyle.margin_xxsmall + VLCStyle.fontHeight_large +
+                                              VLCStyle.margin_xxsmall + VLCStyle.fontHeight_normal
+
     property int column_width: dp(114, scale)
     property int column_margin_width: dp(32, scale)
 
     property int table_cover_border: dp(2, scale)
 
     property int artistBanner_height: dp(200, scale)
-    
+
     //global application size, updated by the root widget
     property int appWidth: 0
     property int appHeight: 0
diff --git a/modules/gui/qt/vlc.qrc b/modules/gui/qt/vlc.qrc
index 10193b2e5513..70a72ac09ec3 100644
--- a/modules/gui/qt/vlc.qrc
+++ b/modules/gui/qt/vlc.qrc
@@ -279,6 +279,7 @@
         <file alias="VideoListDisplay.qml">medialibrary/qml/VideoListDisplay.qml</file>
         <file alias="VideoGridItem.qml">medialibrary/qml/VideoGridItem.qml</file>
         <file alias="AudioGridItem.qml">medialibrary/qml/AudioGridItem.qml</file>
+        <file alias="VideoDisplayRecentVideos.qml">medialibrary/qml/VideoDisplayRecentVideos.qml</file>
     </qresource>
     <qresource prefix="/style">
         <file alias="qmldir">style/qmldir</file>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 317d9d8033ef..d39a436aff35 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -814,6 +814,7 @@ modules/gui/qt/medialibrary/qml/MusicTrackListDisplay.qml
 modules/gui/qt/medialibrary/qml/MusicTracksDisplay.qml
 modules/gui/qt/medialibrary/qml/UrlListDisplay.qml
 modules/gui/qt/medialibrary/qml/VideoDisplay.qml
+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
-- 
GitLab