qml: restore the focus on the last video when navigating back to the video view
[vlc.git] / modules / gui / qt / medialibrary / qml / VideoDisplay.qml
blob8cac270b2e660a2acbded52148cbda309ec99eaa
1 /*****************************************************************************
2  * Copyright (C) 2019 VLC authors and VideoLAN
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * ( at your option ) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
17  *****************************************************************************/
18 import QtQuick 2.11
19 import QtQuick.Controls 2.4
20 import QtQuick.Layouts 1.3
21 import QtQml.Models 2.2
23 import org.videolan.medialib 0.1
25 import "qrc:///util/" as Util
26 import "qrc:///widgets/" as Widgets
27 import "qrc:///dialogs/" as DG
28 import "qrc:///style/"
30 Widgets.NavigableFocusScope {
31     id: root
32     readonly property var currentIndex: view.currentItem.currentIndex
33     //the index to "go to" when the view is loaded
34     property var initialIndex: 0
36     property alias contentModel: videosDelegate.model;
38     navigationCancel: function() {
39         if (view.currentItem.currentIndex <= 0) {
40             defaultNavigationCancel()
41         } else {
42             view.currentItem.currentIndex = 0;
43             view.currentItem.positionViewAtIndex(0, ItemView.Contain)
44         }
45     }
47     onCurrentIndexChanged: {
48         history.update([ "mc", "video", {"initialIndex": currentIndex}])
49     }
51     onInitialIndexChanged: resetFocus()
52     onContentModelChanged: resetFocus()
54     function resetFocus() {
55         if (videosDelegate.items.count === 0) {
56             return
57         }
58         var initialIndex = root.initialIndex
59         if (initialIndex >= videosDelegate.items.count)
60             initialIndex = 0
61         videosDelegate.selectNone()
62         videosDelegate.items.get(initialIndex).inSelected = true
63         view.currentItem.currentIndex = initialIndex
64         view.currentItem.positionViewAtIndex(initialIndex, ItemView.Contain)
65     }
67     DG.ModalDialog {
68         id: deleteDialog
69         rootWindow: root
70         title: i18n.qtr("Are you sure you want to delete?")
71         standardButtons: Dialog.Yes | Dialog.No
73         onAccepted: console.log("Ok clicked")
74         onRejected: console.log("Cancel clicked")
75     }
77     Widgets.MenuExt {
78         id: contextMenu
79         property var model: ({})
80         closePolicy: Popup.CloseOnReleaseOutside | Popup.CloseOnEscape
82         Widgets.MenuItemExt {
83             id: playMenuItem
84             text: "Play from start"
85             onTriggered: medialib.addAndPlay( contextMenu.model.id )
86         }
87         Widgets.MenuItemExt {
88             text: "Play all"
89             onTriggered: console.log("not implemented")
90         }
91         Widgets.MenuItemExt {
92             text: "Play as audio"
93             onTriggered: console.log("not implemented")
94         }
95         Widgets.MenuItemExt {
96             text: "Enqueue"
97             onTriggered: medialib.addToPlaylist( contextMenu.model.id )
98         }
99         Widgets.MenuItemExt {
100             enabled: medialib.gridView
101             text: "Information"
102             onTriggered: {
103                 view.currentItem.switchExpandItem(contextMenu.model.index)
104             }
105         }
106         Widgets.MenuItemExt {
107             text: "Download subtitles"
108             onTriggered: console.log("not implemented")
109         }
110         Widgets.MenuItemExt {
111             text: "Add to playlist"
112             onTriggered: console.log("not implemented")
113         }
114         Widgets.MenuItemExt {
115             text: "Delete"
116             onTriggered: deleteDialog.open()
117         }
119         onClosed: contextMenu.parent.forceActiveFocus()
121     }
122     Util.SelectableDelegateModel {
123         id: videosDelegate
125         model: MLVideoModel {
126             ml: medialib
127         }
128         delegate: Package{
129             Item { Package.name: "grid" }
130         }
132         onCountChanged: {
133             if (videosDelegate.items.count > 0 && videosDelegate.selectedGroup.count === 0) {
134                 root.resetFocus()
135             }
136         }
138         function actionAtIndex(index) {
139             var list = []
140             for (var i = 0; i < videosDelegate.selectedGroup.count; i++)
141                 list.push(videosDelegate.selectedGroup.get(i).model.id)
142             medialib.addAndPlay( list )
143         }
144     }
146     Component {
147         id: gridComponent
149         Widgets.ExpandGridView {
150             id: videosGV
151             property Item currentItem: Item{}
153             activeFocusOnTab:true
154             model: videosDelegate
155             modelCount: videosDelegate.items.count
157             headerDelegate: Widgets.LabelSeparator {
158                 id: videosSeparator
159                 width: videosGV.width
160                 text: i18n.qtr("Videos")
161             }
164             delegate: VideoGridItem {
165                 id: videoGridItem
167                 onContextMenuButtonClicked: {
168                     contextMenu.model = videoGridItem.model
169                     contextMenu.popup()
170                 }
172                 onItemClicked : {
173                     videosDelegate.updateSelection( modifier , videosGV.currentIndex, index)
174                     videosGV.currentIndex = index
175                     videosGV.forceActiveFocus()
176                 }
177             }
179             expandDelegate: VideoInfoExpandPanel {
180                 visible: !videosGV.isAnimating
182                 implicitHeight: videosGV.height - videosGV.cellHeight
183                 width: videosGV.width
185                 onRetract: videosGV.retract()
186                 notchPosition: videosGV.getItemPos(videosGV._expandIndex)[0] + (videosGV.cellWidth / 2)
188                 navigationParent: videosGV
189                 navigationCancel:  function() {  videosGV.retract() }
190                 navigationUp: function() {  videosGV.retract() }
191                 navigationDown: function() { videosGV.retract() }
192             }
194             navigationParent: root
196             /*
197              *define the intial position/selection
198              * This is done on activeFocus rather than Component.onCompleted because videosDelegate.
199              * selectedGroup update itself after this event
200              */
201             onActiveFocusChanged: {
202                 if (activeFocus && videosDelegate.items.count > 0 && videosDelegate.selectedGroup.count === 0) {
203                     videosDelegate.items.get(0).inSelected = true
204                 }
205             }
207             cellWidth: VLCStyle.gridItem_video_width
208             cellHeight: VLCStyle.gridItem_video_height
210             onSelectAll: videosGV.model.selectAll()
211             onSelectionUpdated: videosGV.model.updateSelection( keyModifiers, oldIndex, newIndex )
212             onActionAtIndex: switchExpandItem( index )
213         }
215     }
218     Component {
219         id: listComponent
220         VideoListDisplay {
221             height: view.height
222             width: view.width
223             onContextMenuButtonClicked:{
224                 contextMenu.model = menuModel
225                 contextMenu.popup(menuParent)
226             }
228             onRightClick:{
229                 contextMenu.model = menuModel
230                 contextMenu.popup()
231             }
233             navigationParent: root
234         }
235     }
237     Widgets.StackViewExt {
238         id: view
239         anchors.fill:parent
240         clip: true
241         focus: true
242         initialItem: medialib.gridView ? gridComponent : listComponent
243         Connections {
244             target: medialib
245             onGridViewChanged: {
246                 if (medialib.gridView)
247                     view.replace(gridComponent)
248                 else
249                     view.replace(listComponent)
250             }
251         }
253     }
254     Label {
255         anchors.fill: parent
256         horizontalAlignment: Text.AlignHCenter
257         verticalAlignment: Text.AlignVCenter
258         visible: videosDelegate.items.count === 0
259         font.pixelSize: VLCStyle.fontHeight_xxlarge
260         color: root.activeFocus ? VLCStyle.colors.accent : VLCStyle.colors.text
261         wrapMode: Text.WordWrap
262         text: i18n.qtr("No video found\nPlease try adding sources, by going to the Network tab")
263     }