[vlc-commits] [Git][videolan/vlc][master] 9 commits: qml: implement MainViewLoader

Jean-Baptiste Kempf (@jbk) gitlab at videolan.org
Mon Mar 20 10:20:31 UTC 2023



Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC


Commits:
ecf0c9ce by Prince Gupta at 2023-03-20T10:02:14+00:00
qml: implement MainViewLoader

- - - - -
b0ae61dd by Prince Gupta at 2023-03-20T10:02:14+00:00
qml: check nullability in Helpers.get

- - - - -
a9e02b66 by Prince Gupta at 2023-03-20T10:02:14+00:00
qml: use MainViewLoader in VideoAll

- - - - -
ea0eaffa by Prince Gupta at 2023-03-20T10:02:14+00:00
qml: use MainViewLoader for PlaylistMediaList

- - - - -
4643f556 by Prince Gupta at 2023-03-20T10:02:14+00:00
qml: use MainViewLoader in MusicAllArtists component

- - - - -
e3b13a68 by Prince Gupta at 2023-03-20T10:02:14+00:00
qml: use MainViewLoader in MusicAlbums component

- - - - -
84a5bed6 by Prince Gupta at 2023-03-20T10:02:14+00:00
qml: use MainViewLoader in MusicGenres component

- - - - -
62e94c6d by Prince Gupta at 2023-03-20T10:02:14+00:00
qt: allow navigation functions to fallback to default behaviour

- - - - -
f5d79535 by Prince Gupta at 2023-03-20T10:02:14+00:00
qml: handle Navigation Cancel in MainViewLoader

- - - - -


13 changed files:

- modules/gui/qt/Makefile.am
- + modules/gui/qt/maininterface/qml/MainViewLoader.qml
- modules/gui/qt/medialibrary/qml/MusicAlbums.qml
- modules/gui/qt/medialibrary/qml/MusicAllArtists.qml
- modules/gui/qt/medialibrary/qml/MusicGenres.qml
- modules/gui/qt/medialibrary/qml/MusicPlaylistsDisplay.qml
- modules/gui/qt/medialibrary/qml/PlaylistMediaList.qml
- modules/gui/qt/medialibrary/qml/VideoAll.qml
- modules/gui/qt/medialibrary/qml/VideoAllSubDisplay.qml
- modules/gui/qt/medialibrary/qml/VideoPlaylistsDisplay.qml
- modules/gui/qt/util/qml/Helpers.js
- modules/gui/qt/vlc.qrc
- modules/gui/qt/widgets/native/navigation_attached.cpp


Changes:

=====================================
modules/gui/qt/Makefile.am
=====================================
@@ -848,6 +848,7 @@ libqt_plugin_la_QML = \
 	gui/qt/dialogs/toolbar/qml/ToolbarEditorButtonList.qml \
 	gui/qt/maininterface/qml/BannerSources.qml \
 	gui/qt/maininterface/qml/MainInterface.qml \
+	gui/qt/maininterface/qml/MainViewLoader.qml \
 	gui/qt/maininterface/qml/MainDisplay.qml \
 	gui/qt/maininterface/qml/MainGridView.qml \
 	gui/qt/maininterface/qml/MainTableView.qml \


=====================================
modules/gui/qt/maininterface/qml/MainViewLoader.qml
=====================================
@@ -0,0 +1,147 @@
+/*****************************************************************************
+ * Copyright (C) 2023 VLC authors and VideoLAN
+ *
+ * Author: Prince Gupta <guptaprince8832 at gmail.com>
+ *
+ * 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 QtQml.Models 2.11
+
+import org.videolan.vlc 0.1
+
+import "qrc:///widgets/" as Widgets
+import "qrc:///util/" as Util
+import "qrc:///util/Helpers.js" as Helpers
+
+
+Widgets.StackViewExt {
+    id: root
+
+    /*
+      A component for loading view subtypes depending on model or user preferences.
+      It also handles common actions across sub views such as -
+      restoring of initialIndex when view is reloaded and navigation cancel.
+
+      Following are required inputs -
+    */
+
+    // components to load depending on MainCtx.gridView
+    /* required */ property Component grid
+    /* required */ property Component list
+
+    // component to load when provided model is empty
+    /* required */ property Component emptyLabel
+
+    // view's model
+    /* required */ property var model
+
+
+
+    property var selectionModel: Util.SelectableDelegateModel {
+        model: root.model
+    }
+
+    readonly property ColorContext colorContext: ColorContext {
+        id: theme
+
+        colorSet: ColorContext.View
+    }
+
+    // the index to "go to" when the view is loaded
+    property int initialIndex: -1
+
+    // used in custom focus management for explicit "focusReason" transfer
+    readonly property var setCurrentItemFocus: {
+        return Helpers.get(currentItem, "setCurrentItemFocus", _setCurrentItemFocusDefault)
+    }
+
+    property var currentComponent: {
+        if (typeof model === "undefined" || !model)
+            return null // invalid state
+        if (!model.ready && model.count === 0)
+            return emptyLabel
+        else if (MainCtx.gridView)
+            return grid
+        else
+            return list
+    }
+
+    onCurrentComponentChanged: {
+        _loadCurrentViewType()
+    }
+
+    onModelChanged: resetFocus()
+
+    onInitialIndexChanged: resetFocus()
+
+    Connections {
+        target: model
+
+        onCountChanged: {
+            if (model.count === 0 || selectionModel.hasSelection)
+                return
+
+            resetFocus()
+        }
+    }
+
+    function _setCurrentItemFocusDefault(reason) {
+        if (currentItem)
+            currentItem.forceActiveFocus(reason)
+    }
+
+    function _loadCurrentViewType() {
+        if (typeof currentComponent === "undefined" || !currentComponent) {
+            // invalid case, don't show anything
+            clear()
+            return
+        }
+
+        replace(null, currentComponent)
+    }
+
+    // makes the views currentIndex initial index and position view at that index
+    function resetFocus() {
+        if (!model || model.count === 0) return
+
+        var initialIndex = root.initialIndex
+        if (initialIndex >= model.count)
+            initialIndex = 0
+
+        selectionModel.select(model.index(initialIndex, 0), ItemSelectionModel.ClearAndSelect)
+        if (currentItem && currentItem.hasOwnProperty("positionViewAtIndex")) {
+            currentItem.positionViewAtIndex(initialIndex, ItemView.Contain)
+
+            // Table View require this for focus handling
+            if (!MainCtx.gridView)
+                currentItem.currentIndex = initialIndex
+        }
+    }
+
+    // handle cancelAction, if currentIndex is set reset it to 0
+    // otherwise perform default Navigation action
+    Navigation.cancelAction: function () {
+        if (Helpers.get(currentItem, "currentIndex", 0) <= 0) {
+            return false // transfer cancel action to parent
+        }
+
+        currentItem.currentIndex = 0
+        currentItem.positionViewAtIndex(0, ItemView.Contain)
+        return true
+    }
+
+}


=====================================
modules/gui/qt/medialibrary/qml/MusicAlbums.qml
=====================================
@@ -16,31 +16,31 @@
  * 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.11
-import QtQml.Models 2.2
+
 import org.videolan.medialib 0.1
 import org.videolan.vlc 0.1
 
 import "qrc:///util/" as Util
 import "qrc:///widgets/" as Widgets
 import "qrc:///main/" as MainInterface
+import "qrc:///util/Helpers.js" as Helpers
 import "qrc:///style/"
 
-FocusScope {
+MainInterface.MainViewLoader {
     id: root
 
     // Properties
 
-    //the index to "go to" when the view is loaded
-    property int initialIndex: 0
     property int gridViewMarginTop: VLCStyle.margin_large
-    property var gridViewRowX: MainCtx.gridView ? _currentView.rowX : undefined
+    property var gridViewRowX: Helpers.get(currentItem, "rowX", 0)
+
+    readonly property var currentIndex: Helpers.get(currentItem, "currentIndex", - 1)
 
-    readonly property var currentIndex: _currentView.currentIndex
+    property Component header: Item {}
+    readonly property Item headerItem: Helpers.get(currentItem, "headerItem", null)
 
-    property Component header: Item{}
-    readonly property Item headerItem: _currentView ? _currentView.headerItem : null
+    readonly property int contentLeftMargin: Helpers.get(currentItem, "contentLeftMargin", 0)
+    readonly property int contentRightMargin: Helpers.get(currentItem, "contentRightMargin", 0)
 
     property var sortModel: [
         { text: I18n.qtr("Alphabetic"),  criteria: "title"},
@@ -49,39 +49,16 @@ FocusScope {
         { text: I18n.qtr("Artist"),      criteria: "main_artist" },
     ]
 
-    // Aliases
-
-    property alias leftPadding: view.leftPadding
-    property alias rightPadding: view.rightPadding
-
-    property alias model: albumModelId
     property alias parentId: albumModelId.parentId
 
-    readonly property int contentLeftMargin: _currentView ? _currentView.contentLeftMargin : 0
-    readonly property int contentRightMargin: _currentView ? _currentView.contentRightMargin : 0
+    model: albumModelId
 
-    property alias _currentView: view.currentItem
+    grid: gridComponent
+    list: tableComponent
+    emptyLabel: emptyLabelComponent
 
-    onInitialIndexChanged:  resetFocus()
-    onModelChanged: resetFocus()
     onParentIdChanged: resetFocus()
 
-    function resetFocus() {
-        if (albumModelId.count === 0) {
-            return
-        }
-        var initialIndex = root.initialIndex
-        if (initialIndex >= albumModelId.count)
-            initialIndex = 0
-        selectionModel.select(model.index(initialIndex, 0), ItemSelectionModel.ClearAndSelect)
-        if (_currentView)
-            _currentView.positionViewAtIndex(initialIndex, ItemView.Contain)
-    }
-
-    function setCurrentItemFocus(reason) {
-        _currentView.setCurrentItemFocus(reason);
-    }
-
     function _actionAtIndex(index) {
         if (selectionModel.selectedIndexes.length > 1) {
             MediaLib.addAndPlay( model.getIdsForIndexes( selectionModel.selectedIndexes ) )
@@ -90,30 +67,10 @@ FocusScope {
         }
     }
 
-    function _onNavigationCancel() {
-        if (_currentView.currentIndex <= 0) {
-            root.Navigation.defaultNavigationCancel()
-        } else {
-            _currentView.currentIndex = 0;
-            _currentView.positionViewAtIndex(0, ItemView.Contain)
-        }
-    }
-
-
     MLAlbumModel {
         id: albumModelId
-        ml: MediaLib
 
-        onCountChanged: {
-            if (albumModelId.count > 0 && !selectionModel.hasSelection) {
-                root.resetFocus()
-            }
-        }
-    }
-
-    Util.SelectableDelegateModel {
-        id: selectionModel
-        model: albumModelId
+        ml: MediaLib
     }
 
     Widgets.MLDragItem {
@@ -201,7 +158,6 @@ FocusScope {
             }
 
             Navigation.parentItem: root
-            Navigation.cancelAction: root._onNavigationCancel
 
             Connections {
                 target: contextMenu
@@ -282,8 +238,6 @@ FocusScope {
             sortModel: (availableRowWidth < VLCStyle.colWidth(4)) ? _modelSmall
                                                                   : _modelMedium
 
-            Navigation.cancelAction: root._onNavigationCancel
-
             onContextMenuButtonClicked: contextMenu.popup(selectionModel.selectedIndexes, globalMousePos)
             onRightClick: contextMenu.popup(selectionModel.selectedIndexes, globalMousePos)
             onItemDoubleClicked: MediaLib.addAndPlay( model.id )
@@ -310,32 +264,13 @@ FocusScope {
         }
     }
 
-    Widgets.StackViewExt {
-        id: view
-
-        anchors.fill: parent
-
-        focus: albumModelId.count !== 0
-
-        initialItem: MainCtx.gridView ? gridComponent : tableComponent
+    Component {
+        id: emptyLabelComponent
 
-        Connections {
-            target: MainCtx
-            onGridViewChanged: {
-                if (MainCtx.gridView)
-                    view.replace(gridComponent)
-                else
-                    view.replace(tableComponent)
-            }
+        EmptyLabelButton {
+            text: I18n.qtr("No albums found\nPlease try adding sources, by going to the Browse tab")
+            Navigation.parentItem: root
+            cover: VLCStyle.noArtAlbumCover
         }
     }
-
-    EmptyLabelButton {
-        anchors.fill: parent
-        visible: albumModelId.isReady && (albumModelId.count <= 0)
-        focus: visible
-        text: I18n.qtr("No albums found\nPlease try adding sources, by going to the Browse tab")
-        Navigation.parentItem: root
-        cover: VLCStyle.noArtAlbumCover
-    }
 }


=====================================
modules/gui/qt/medialibrary/qml/MusicAllArtists.qml
=====================================
@@ -16,10 +16,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
-import QtQuick.Controls 2.4
 import QtQuick 2.11
-import QtQml.Models 2.2
-import QtQuick.Layouts 1.3
 
 import org.videolan.medialib 0.1
 import org.videolan.vlc 0.1
@@ -27,71 +24,26 @@ import org.videolan.vlc 0.1
 import "qrc:///util/" as Util
 import "qrc:///widgets/" as Widgets
 import "qrc:///main/" as MainInterface
+import "qrc:///util/Helpers.js" as Helpers
 import "qrc:///style/"
 
-FocusScope {
+MainInterface.MainViewLoader {
     id: root
 
     // Properties
 
-    readonly property int currentIndex: _currentView.currentIndex
-    property int initialIndex: 0
-
-    // Aliases
-
-    property alias leftPadding: view.leftPadding
-    property alias rightPadding: view.rightPadding
-
-    property alias model: artistModel
-
-    property alias _currentView: view.currentItem
-
-    onInitialIndexChanged: resetFocus()
+    readonly property int currentIndex: Helpers.get(currentItem, "currentIndex", - 1)
 
     signal requestArtistAlbumView(int reason)
 
-    function resetFocus() {
-        if (artistModel.count === 0)
-            return
-
-        var initialIndex = root.initialIndex
-        if (initialIndex >= artistModel.count)
-            initialIndex = 0
-        selectionModel.select(artistModel.index(initialIndex, 0), ItemSelectionModel.ClearAndSelect)
-        if (_currentView) {
-            _currentView.currentIndex = initialIndex
-            _currentView.positionViewAtIndex(initialIndex, ItemView.Contain)
-        }
-    }
-
-    function setCurrentItemFocus(reason) {
-        _currentView.setCurrentItemFocus(reason);
-    }
-
-    function _onNavigationCancel() {
-        if (_currentView.currentIndex <= 0) {
-            root.Navigation.defaultNavigationCancel()
-        } else {
-            _currentView.currentIndex = 0;
-            _currentView.positionViewAtIndex(0, ItemView.Contain);
-        }
-    }
-
-    MLArtistModel {
+    model: MLArtistModel {
         id: artistModel
         ml: MediaLib
-
-        onCountChanged: {
-            if (artistModel.count > 0 && !selectionModel.hasSelection) {
-                root.resetFocus()
-            }
-        }
     }
 
-    Util.SelectableDelegateModel {
-        id: selectionModel
-        model: artistModel
-    }
+    grid: gridComponent
+    list: tableComponent
+    emptyLabel: emptyLabelComponent
 
     Util.MLContextMenu {
         id: contextMenu
@@ -114,7 +66,6 @@ FocusScope {
         MainInterface.MainGridView {
             id: artistGrid
 
-            anchors.fill: parent
             topMargin: VLCStyle.margin_large
             selectionDelegateModel: selectionModel
             model: artistModel
@@ -123,13 +74,12 @@ FocusScope {
             cellHeight: VLCStyle.gridItem_music_height
 
             Navigation.parentItem: root
-            Navigation.cancelAction: root._onNavigationCancel
 
             onActionAtIndex: {
                 if (selectionModel.selectedIndexes.length > 1) {
                     MediaLib.addAndPlay( artistModel.getIdsForIndexes( selectionModel.selectedIndexes ) )
                 } else {
-                    _currentView.currentIndex = index
+                    currentIndex = index
                     requestArtistAlbumView(Qt.TabFocusReason)
                 }
             }
@@ -214,7 +164,6 @@ FocusScope {
                 }
             }]
 
-            anchors.fill: parent
             selectionDelegateModel: selectionModel
             model: artistModel
             focus: true
@@ -223,13 +172,13 @@ FocusScope {
             headerTopPadding: VLCStyle.margin_normal
 
             Navigation.parentItem: root
-            Navigation.cancelAction: root._onNavigationCancel
 
             onActionForSelection: {
                 if (selection.length > 1) {
                     MediaLib.addAndPlay( artistModel.getIdsForIndexes( selection ) )
                 } else if ( selection.length === 1) {
                     requestArtistAlbumView(Qt.TabFocusReason)
+                    // FIX ME - requestArtistAlbumView will destroy this view
                     MediaLib.addAndPlay( artistModel.getIdForIndex( selection[0] ) )
                 }
             }
@@ -250,33 +199,13 @@ FocusScope {
         }
     }
 
-    Widgets.StackViewExt {
-        id: view
-
-        anchors.fill: parent
-
-        visible: artistModel.count > 0
-        focus: artistModel.count > 0
-        initialItem: MainCtx.gridView ? gridComponent : tableComponent
-    }
+    Component {
+        id: emptyLabelComponent
 
-    Connections {
-        target: MainCtx
-        onGridViewChanged: {
-            if (MainCtx.gridView) {
-                view.replace(gridComponent)
-            } else {
-                view.replace(tableComponent)
-            }
+        EmptyLabelButton {
+            text: I18n.qtr("No artists found\nPlease try adding sources, by going to the Browse tab")
+            Navigation.parentItem: root
+            cover: VLCStyle.noArtArtistCover
         }
     }
-
-    EmptyLabelButton {
-        anchors.fill: parent
-        visible: artistModel.isReady && (artistModel.count <= 0)
-        focus: visible
-        text: I18n.qtr("No artists found\nPlease try adding sources, by going to the Browse tab")
-        Navigation.parentItem: root
-        cover: VLCStyle.noArtArtistCover
-    }
 }


=====================================
modules/gui/qt/medialibrary/qml/MusicGenres.qml
=====================================
@@ -19,15 +19,17 @@ import QtQuick 2.11
 import QtQuick.Controls 2.4
 import QtQuick.Templates 2.4 as T
 import QtQml.Models 2.2
+
 import org.videolan.vlc 0.1
 import org.videolan.medialib 0.1
 
 import "qrc:///util/" as Util
 import "qrc:///widgets/" as Widgets
 import "qrc:///main/" as MainInterface
+import "qrc:///util/Helpers.js" as Helpers
 import "qrc:///style/"
 
-FocusScope {
+MainInterface.MainViewLoader {
     id: root
 
     // Properties
@@ -36,47 +38,18 @@ FocusScope {
         { text: I18n.qtr("Alphabetic"), criteria: "title" }
     ]
 
-    readonly property var currentIndex: _currentView.currentIndex
-
-    //the index to "go to" when the view is loaded
-    property int initialIndex: 0
-
-    // Aliases
-
-    property alias leftPadding: view.leftPadding
-    property alias rightPadding: view.rightPadding
+    readonly property var currentIndex: Helpers.get(currentItem, "currentIndex", - 1)
 
-    property alias model: genreModel
-
-    property alias _currentView: view.currentItem
+    // FIXME: remove this
+    property var _currentView: currentItem
 
     signal showAlbumView(var id, string name, int reason)
 
-    onInitialIndexChanged:  resetFocus()
-
-    function loadView() {
-        if (MainCtx.gridView) {
-            view.replace(gridComponent)
-        } else {
-            view.replace(tableComponent)
-        }
-    }
-
-    function resetFocus() {
-        if (genreModel.count === 0) {
-            return
-        }
-        var initialIndex = root.initialIndex
-        if (initialIndex >= genreModel.count)
-            initialIndex = 0
-        selectionModel.select(genreModel.index(initialIndex, 0), ItemSelectionModel.ClearAndSelect)
-        if (_currentView)
-            _currentView.positionViewAtIndex(initialIndex, ItemView.Contain)
-    }
+    model: genreModel
 
-    function setCurrentItemFocus(reason) {
-        _currentView.setCurrentItemFocus(reason);
-    }
+    list: tableComponent
+    grid: gridComponent
+    emptyLabel: emptyLabelComponent
 
     MLGenreModel {
         id: genreModel
@@ -101,12 +74,6 @@ FocusScope {
         }
     }
 
-    Util.SelectableDelegateModel {
-        id: selectionModel
-
-        model: genreModel
-    }
-
     Widgets.MLDragItem {
         id: genreDragItem
 
@@ -125,10 +92,11 @@ FocusScope {
     onActiveFocusChanged: {
         if (activeFocus && genreModel.count > 0 && !selectionModel.hasSelection) {
             var initialIndex = 0
-            if (_currentView.currentIndex !== -1)
-                initialIndex = _currentView.currentIndex
+            if (currentIndex !== -1)
+                initialIndex = currentIndex
+
             selectionModel.select(genreModel.index(initialIndex, 0), ItemSelectionModel.ClearAndSelect)
-            _currentView.currentIndex = initialIndex
+            currentItem.currentIndex = initialIndex
         }
     }
 
@@ -221,12 +189,6 @@ FocusScope {
             onActionAtIndex: _actionAtIndex(index)
 
             Navigation.parentItem: root
-            Navigation.cancelAction: function() {
-                if (_currentView.currentIndex <= 0)
-                    root.Navigation.defaultNavigationCancel()
-                else
-                    _currentView.currentIndex = 0;
-            }
         }
     }
 
@@ -289,12 +251,6 @@ FocusScope {
             focus: true
             onActionForSelection: _actionAtIndex(selection)
             Navigation.parentItem: root
-            Navigation.cancelAction: function() {
-                if (_currentView.currentIndex <= 0)
-                    root.Navigation.defaultNavigationCancel()
-                else
-                    _currentView.currentIndex = 0;
-            }
             dragItem: genreDragItem
             rowHeight: VLCStyle.tableCoverRow_height
             headerTopPadding: VLCStyle.margin_normal
@@ -319,33 +275,14 @@ FocusScope {
         }
     }
 
-    Widgets.StackViewExt {
-        id: view
-
-        anchors.fill: parent
-
-        initialItem: MainCtx.gridView ? gridComponent : tableComponent
 
-        focus: genreModel.count !== 0
-    }
+    Component {
+        id: emptyLabelComponent
 
-    Connections {
-        target: MainCtx
-        onGridViewChanged: {
-            if (MainCtx.gridView) {
-                view.replace(gridComponent)
-            } else {
-                view.replace(tableComponent)
-            }
+        EmptyLabelButton {
+            text: I18n.qtr("No genres found\nPlease try adding sources, by going to the Browse tab")
+            Navigation.parentItem: root
+            cover: VLCStyle.noArtAlbumCover
         }
     }
-
-    EmptyLabelButton {
-        anchors.fill: parent
-        visible: genreModel.isReady && (genreModel.count <= 0)
-        focus: visible
-        text: I18n.qtr("No genres found\nPlease try adding sources, by going to the Browse tab")
-        Navigation.parentItem: root
-        cover: VLCStyle.noArtAlbumCover
-    }
 }


=====================================
modules/gui/qt/medialibrary/qml/MusicPlaylistsDisplay.qml
=====================================
@@ -102,7 +102,6 @@ Widgets.PageLoader {
                 History.push(["mc", "music", "playlists", "list",
                              { parentId: model.id, name: model.name }]);
 
-                stackView.currentItem.setCurrentItemFocus(reason);
             }
         }
     }


=====================================
modules/gui/qt/medialibrary/qml/PlaylistMediaList.qml
=====================================
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * Copyright (C) 2021 VLC authors and VideoLAN
+ * Copyright (C) 2021-23 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
@@ -27,9 +27,10 @@ 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/"
 
-FocusScope {
+MainInterface.MainViewLoader {
     id: root
 
     //---------------------------------------------------------------------------------------------
@@ -38,9 +39,7 @@ FocusScope {
 
     property bool isMusic: false
 
-    readonly property int currentIndex: _currentView.currentIndex
-
-    property int initialIndex: 0
+    readonly property int currentIndex: Helpers.get(currentItem, "currentIndex", -1)
 
     property var sortModel: [{ text: I18n.qtr("Alphabetic"), criteria: "title" }]
 
@@ -62,16 +61,8 @@ FocusScope {
     property string _placeHolder: (isMusic) ? VLCStyle.noArtAlbumCover
                                             : VLCStyle.noArtVideoCover
 
-    //---------------------------------------------------------------------------------------------
-    // Alias
-    //---------------------------------------------------------------------------------------------
-
-    property alias leftPadding: view.leftPadding
-    property alias rightPadding: view.rightPadding
-
-    property alias model: model
-
-    property alias _currentView: view.currentItem
+    // FIXME: remove this
+    property var _currentView: currentItem
 
     //---------------------------------------------------------------------------------------------
     // Signals
@@ -84,75 +75,44 @@ FocusScope {
     //---------------------------------------------------------------------------------------------
 
     // NOTE: Define the initial position and selection. This is done on activeFocus rather than
-    //       Component.onCompleted because modelSelect.selectedGroup update itself after this
+    //       Component.onCompleted because selectionModel.selectedGroup update itself after this
     //       event.
     onActiveFocusChanged: {
-        if (activeFocus == false || model.count === 0 || modelSelect.hasSelection)
+        if (activeFocus == false || model.count === 0 || selectionModel.hasSelection)
             return;
 
-        var initialIndex = 0;
-
-        if (_currentView.currentIndex !== -1) {
-            initialIndex = _currentView.currentIndex;
-        }
-
-        modelSelect.select(model.index(initialIndex, 0), ItemSelectionModel.ClearAndSelect);
-
-        _currentView.currentIndex = initialIndex;
+        resetFocus()
     }
 
-    onInitialIndexChanged: resetFocus()
 
     //---------------------------------------------------------------------------------------------
-    // Connections
-    //---------------------------------------------------------------------------------------------
-
-    Connections {
-        target: MainCtx
-
-        onGridViewChanged: {
-            if (MainCtx.gridView) view.replace(grid);
-            else                        view.replace(table);
-        }
-    }
-
-    //---------------------------------------------------------------------------------------------
-    // Functions
-    //---------------------------------------------------------------------------------------------
-
-    function resetFocus() {
-        if (model.count === 0)
-            return;
+    // Private
 
-        var initialIndex = root.initialIndex;
+    grid: grid
+    list: table
+    emptyLabel: emptyLabel
 
-        if (initialIndex >= model.count)
-            initialIndex = 0;
+    model: MLPlaylistListModel {
+        ml: MediaLib
 
-        modelSelect.select(model.index(initialIndex, 0), ItemSelectionModel.ClearAndSelect);
+        coverSize: (isMusic) ? Qt.size(512, 512)
+                             : Qt.size(1024, 640)
 
-        if (_currentView)
-            _currentView.positionViewAtIndex(initialIndex, ItemView.Contain);
-    }
+        coverDefault: root._placeHolder
 
-    function setCurrentItemFocus(reason) {
-        _currentView.setCurrentItemFocus(reason);
+        coverPrefix: (isMusic) ? "playlist-music" : "playlist-video"
     }
 
-    //---------------------------------------------------------------------------------------------
-    // Private
-
     function _actionAtIndex() {
-        if (modelSelect.selectedIndexes.length > 1) {
-            MediaLib.addAndPlay(model.getIdsForIndexes(modelSelect.selectedIndexes));
-        } else if (modelSelect.selectedIndexes.length === 1) {
-            var index = modelSelect.selectedIndexes[0];
+        if (root.selectionModel.selectedIndexes.length > 1) {
+            MediaLib.addAndPlay(model.getIdsForIndexes(selectionModel.selectedIndexes));
+        } else if (root.selectionModel.selectedIndexes.length === 1) {
+            var index = selectionModel.selectedIndexes[0];
             showList(model.getDataAt(index), Qt.TabFocusReason);
         }
     }
 
-    function _getCount(model)
-    {
+    function _getCount(model) {
         var count = model.count;
 
         if (count < 100)
@@ -161,55 +121,17 @@ FocusScope {
             return I18n.qtr("99+");
     }
 
-    function _onNavigationCancel() {
-        if (_currentView.currentIndex <= 0) {
-            Navigation.defaultNavigationCancel()
-        } else {
-            _currentView.currentIndex = 0;
-            _currentView.positionViewAtIndex(0, ItemView.Contain);
-        }
-    }
-
     //---------------------------------------------------------------------------------------------
     // Childs
     //---------------------------------------------------------------------------------------------
 
-    MLPlaylistListModel {
-        id: model
-
-        ml: MediaLib
-
-        coverSize: (isMusic) ? Qt.size(512, 512)
-                             : Qt.size(1024, 640)
-
-        coverDefault: root._placeHolder
-
-        coverPrefix: (isMusic) ? "playlist-music" : "playlist-video"
-
-        onCountChanged: {
-            if (count === 0 || modelSelect.hasSelection)
-                return;
-
-            resetFocus();
-        }
-    }
-
-    Widgets.StackViewExt {
-        id: view
-
-        anchors.fill: parent
-
-        initialItem: (MainCtx.gridView) ? grid : table
-
-        focus: (model.count !== 0)
-    }
 
     Widgets.MLDragItem {
         id: dragItemPlaylist
 
         mlModel: model
 
-        indexes: modelSelect.selectedIndexes
+        indexes: selectionModel.selectedIndexes
 
         coverRole: "thumbnail"
 
@@ -218,12 +140,6 @@ FocusScope {
         titleRole: "name"
     }
 
-    Util.SelectableDelegateModel {
-        id: modelSelect
-
-        model: root.model
-    }
-
     PlaylistListContextMenu {
         id: contextMenu
 
@@ -247,12 +163,10 @@ FocusScope {
 
             model: root.model
 
-            selectionDelegateModel: modelSelect
+            selectionDelegateModel: selectionModel
 
             Navigation.parentItem: root
 
-            Navigation.cancelAction: root._onNavigationCancel
-
             delegate: VideoGridItem {
                 //---------------------------------------------------------------------------------
                 // Properties
@@ -288,7 +202,7 @@ FocusScope {
                 onContextMenuButtonClicked: {
                     gridView.rightClickOnItem(index);
 
-                    contextMenu.popup(modelSelect.selectedIndexes, globalMousePos);
+                    contextMenu.popup(selectionModel.selectedIndexes, globalMousePos);
                 }
 
                 //---------------------------------------------------------------------------------
@@ -301,13 +215,13 @@ FocusScope {
             // Events
 
             // NOTE: Define the initial position and selection. This is done on activeFocus rather
-            //       than Component.onCompleted because modelSelect.selectedGroup update itself
+            //       than Component.onCompleted because selectionModel.selectedGroup update itself
             //       after this event.
             onActiveFocusChanged: {
-                if (activeFocus == false || model.count === 0 || modelSelect.hasSelection)
+                if (activeFocus == false || model.count === 0 || selectionModel.hasSelection)
                     return;
 
-                modelSelect.select(model.index(0,0), ItemSelectionModel.ClearAndSelect)
+                selectionModel.select(model.index(0,0), ItemSelectionModel.ClearAndSelect)
             }
 
             onActionAtIndex: _actionAtIndex()
@@ -383,12 +297,11 @@ FocusScope {
             sortModel: (availableRowWidth < VLCStyle.colWidth(4)) ? _modelSmall
                                                                   : _modelMedium
 
-            selectionDelegateModel: modelSelect
+            selectionDelegateModel: selectionModel
 
             dragItem: dragItemPlaylist
 
             Navigation.parentItem: root
-            Navigation.cancelAction: root._onNavigationCancel
 
             //-------------------------------------------------------------------------------------
             // Events
@@ -397,10 +310,10 @@ FocusScope {
 
             onItemDoubleClicked: showList(model, Qt.MouseFocusReason)
 
-            onContextMenuButtonClicked: contextMenu.popup(modelSelect.selectedIndexes,
+            onContextMenuButtonClicked: contextMenu.popup(selectionModel.selectedIndexes,
                                                           globalMousePos)
 
-            onRightClick: contextMenu.popup(modelSelect.selectedIndexes, globalMousePos)
+            onRightClick: contextMenu.popup(selectionModel.selectedIndexes, globalMousePos)
 
             //-------------------------------------------------------------------------------------
             // Childs
@@ -435,16 +348,18 @@ FocusScope {
         }
     }
 
-    EmptyLabelHint {
-        anchors.fill: parent
+    Component {
+        id: emptyLabel
 
-        visible: (model.count === 0)
+        EmptyLabelHint {
+            visible: (model.count === 0)
 
-        focus: visible
+            focus: true
 
-        text: I18n.qtr("No playlists found")
-        hint: I18n.qtr("Right click on a media to add it to a playlist")
+            text: I18n.qtr("No playlists found")
+            hint: I18n.qtr("Right click on a media to add it to a playlist")
 
-        cover: VLCStyle.noArtAlbumCover
+            cover: VLCStyle.noArtAlbumCover
+        }
     }
 }


=====================================
modules/gui/qt/medialibrary/qml/VideoAll.qml
=====================================
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * Copyright (C) 2021 VLC authors and VideoLAN
+ * Copyright (C) 2021-23 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
@@ -30,23 +30,19 @@ import "qrc:///util/"    as Util
 import "qrc:///util/Helpers.js" as Helpers
 import "qrc:///style/"
 
-FocusScope {
+MainInterface.MainViewLoader {
     id: root
 
     // Properties
 
-    readonly property int contentMargin: (_currentView) ? _currentView.contentLeftMargin : 0
+    readonly property int contentMargin: Helpers.get(currentItem, "contentLeftMargin", 0)
 
     // NOTE: Specify an optional header for the view.
     property Component header: null
 
-    property Item headerItem: (_currentView) ? _currentView.headerItem : null
+    property Item headerItem: Helpers.get(currentItem, "headerItem", null)
 
-    readonly property int currentIndex: _currentView.currentIndex
-
-    property int initialIndex: 0
-
-    /* required */ property var model
+    readonly property int currentIndex: Helpers.get(currentItem, "currentIndex", -1)
 
     // NOTE: The ContextMenu depends on the model so we have to provide it too.
     /* required */ property var contextMenu
@@ -56,69 +52,12 @@ FocusScope {
         { text: I18n.qtr("Duration"),   criteria: "duration" }
     ]
 
-    // Aliases
-
-    property alias leftPadding: view.leftPadding
-    property alias rightPadding: view.rightPadding
-
     property alias dragItem: dragItem
 
-    // Private
-
-    property alias _currentView: view.currentItem
-
-    // Events
-
-    onModelChanged: resetFocus()
-
-    onInitialIndexChanged: resetFocus()
-
-    // Connections
-
-    Connections {
-        target: MainCtx
-
-        onGridViewChanged: {
-            if (MainCtx.gridView) view.replace(grid)
-            else                  view.replace(list)
-        }
-    }
-
-    Connections {
-        target: model
-
-        onCountChanged: {
-            if (model.count === 0 || modelSelect.hasSelection)
-                return;
-
-            resetFocus();
-        }
-    }
-
-    // Functions
-
-    function setCurrentItemFocus(reason) {
-        _currentView.setCurrentItemFocus(reason);
-    }
-
-    function resetFocus() {
-        if (!model || model.count === 0) return
-
-        var initialIndex = root.initialIndex
-
-        if (initialIndex >= model.count)
-            initialIndex = 0
+    list: list
+    grid: grid
+    emptyLabel: emptylabel
 
-        modelSelect.select(model.index(initialIndex, 0), ItemSelectionModel.ClearAndSelect)
-
-        if (_currentView) {
-            _currentView.positionViewAtIndex(initialIndex, ItemView.Contain)
-
-            // Table View require this for focus handling
-            if (!MainCtx.gridView)
-                _currentView.currentIndex = initialIndex
-        }
-    }
 
     function getLabel(model) {
         if (!model) return ""
@@ -158,51 +97,18 @@ FocusScope {
             Navigation.defaultNavigationUp()
     }
 
-    function _onNavigationCancel() {
-        if (_currentView.currentIndex <= 0) {
-            Navigation.defaultNavigationCancel()
-        } else {
-            _currentView.currentIndex = 0
-
-            _currentView.positionViewAtIndex(0, ItemView.Contain)
-        }
-    }
-
-    // Children
-
-    readonly property ColorContext colorContext: ColorContext {
-        id: theme
-        colorSet: ColorContext.View
-    }
-
-    Widgets.StackViewExt {
-        id: view
-
-        anchors.fill: parent
-
-        focus: (model.count !== 0)
-
-        initialItem: (MainCtx.gridView) ? grid : list
-    }
-
     Widgets.MLDragItem {
         id: dragItem
 
         mlModel: root.model
 
-        indexes: modelSelect.selectedIndexes
+        indexes: selectionModel.selectedIndexes
 
         coverRole: "thumbnail"
 
         defaultCover: VLCStyle.noArtVideoCover
     }
 
-    Util.SelectableDelegateModel {
-        id: modelSelect
-
-        model: root.model
-    }
-
     Component {
         id: grid
 
@@ -218,7 +124,7 @@ FocusScope {
 
             model: root.model
 
-            selectionDelegateModel: modelSelect
+            selectionDelegateModel: selectionModel
 
             headerDelegate: root.header
 
@@ -230,22 +136,19 @@ FocusScope {
 
             Navigation.upAction: _onNavigationUp
 
-            // NOTE: cancelAction takes a function, we pass it directly.
-            Navigation.cancelAction: root._onNavigationCancel
-
             // Events
 
             // NOTE: Define the initial position and selection. This is done on activeFocus rather
-            //       than Component.onCompleted because modelSelect.selectedGroup update itself
+            //       than Component.onCompleted because selectionModel.selectedGroup update itself
             //       after this event.
             onActiveFocusChanged: {
-                if (activeFocus == false || model.count === 0 || modelSelect.hasSelection)
+                if (activeFocus == false || model.count === 0 || selectionModel.hasSelection)
                     return;
 
                 resetFocus() // restores initialIndex
             }
 
-            onActionAtIndex: root.onAction(modelSelect.selectedIndexes)
+            onActionAtIndex: root.onAction(selectionModel.selectedIndexes)
 
             // Connections
 
@@ -296,7 +199,7 @@ FocusScope {
                     if (root.isInfoExpandPanelAvailable(model))
                         options["information"] = index
 
-                    root.contextMenu.popup(modelSelect.selectedIndexes, globalMousePos, options);
+                    root.contextMenu.popup(selectionModel.selectedIndexes, globalMousePos, options);
                 }
 
                 // Animations
@@ -336,7 +239,7 @@ FocusScope {
 
             model: root.model
 
-            selectionDelegateModel: modelSelect
+            selectionDelegateModel: selectionModel
 
             dragItem: root.dragItem
 
@@ -354,18 +257,15 @@ FocusScope {
 
             Navigation.upAction: _onNavigationUp
 
-            //cancelAction takes a *function* pass it directly
-            Navigation.cancelAction: root._onNavigationCancel
-
             // Events
 
-            onActionForSelection: root.onAction(modelSelect.selectedIndexes)
+            onActionForSelection: root.onAction(selectionModel.selectedIndexes)
 
             onItemDoubleClicked: root.onDoubleClick(model)
 
-            onContextMenuButtonClicked: root.contextMenu.popup(modelSelect.selectedIndexes, globalMousePos)
+            onContextMenuButtonClicked: root.contextMenu.popup(selectionModel.selectedIndexes, globalMousePos)
 
-            onRightClick: root.contextMenu.popup(modelSelect.selectedIndexes, globalMousePos)
+            onRightClick: root.contextMenu.popup(selectionModel.selectedIndexes, globalMousePos)
 
             // Functions
 
@@ -373,20 +273,20 @@ FocusScope {
         }
     }
 
-    EmptyLabelButton {
-        anchors.fill: parent
-
-        coverWidth : VLCStyle.dp(182, VLCStyle.scale)
-        coverHeight: VLCStyle.dp(114, VLCStyle.scale)
+    Component {
+        id: emptylabel
 
-        visible: model.isReady && (model.count <= 0)
+        EmptyLabelButton {
+            coverWidth : VLCStyle.dp(182, VLCStyle.scale)
+            coverHeight: VLCStyle.dp(114, VLCStyle.scale)
 
-        focus: visible
+            focus: true
 
-        text: I18n.qtr("No video found\nPlease try adding sources, by going to the Browse tab")
+            text: I18n.qtr("No video found\nPlease try adding sources, by going to the Browse tab")
 
-        cover: VLCStyle.noArtVideoCover
+            cover: VLCStyle.noArtVideoCover
 
-        Navigation.parentItem: root
+            Navigation.parentItem: root
+        }
     }
 }


=====================================
modules/gui/qt/medialibrary/qml/VideoAllSubDisplay.qml
=====================================
@@ -91,7 +91,7 @@ VideoAll {
         if (headerItem && headerItem.focus)
             headerItem.forceActiveFocus(reason)
         else
-            _currentView.setCurrentItemFocus(reason)
+            currentItem.setCurrentItemFocus(reason)
     }
 
     // VideoAll events reimplementation
@@ -234,7 +234,7 @@ VideoAll {
         Navigation.parentItem: root
 
         Navigation.downAction: function() {
-            _currentView.setCurrentItemFocus(Qt.TabFocusReason);
+            currentItem.setCurrentItemFocus(Qt.TabFocusReason)
         }
 
         onImplicitHeightChanged: {


=====================================
modules/gui/qt/medialibrary/qml/VideoPlaylistsDisplay.qml
=====================================
@@ -102,8 +102,6 @@ Widgets.PageLoader {
             onShowList: {
                 History.push(["mc", "video", "playlists", "list",
                              { parentId: model.id, name: model.name }]);
-
-                stackView.currentItem.setCurrentItemFocus(reason);
             }
         }
     }


=====================================
modules/gui/qt/util/qml/Helpers.js
=====================================
@@ -30,7 +30,7 @@ function isValidInstanceOf(object, type) {
 // If the hash contains no item with the key,
 // or the value is invalid, returns defaultValue
 function get(dict, key, defaultValue) {
-    var v = typeof dict !== "undefined" ? dict[key] : undefined
+    var v = typeof dict !== "undefined" && !!dict ? dict[key] : undefined
     return typeof v === "undefined" ? defaultValue : v
 }
 


=====================================
modules/gui/qt/vlc.qrc
=====================================
@@ -151,6 +151,7 @@
         <file alias="NoMedialibHome.qml">maininterface/qml/NoMedialibHome.qml</file>
         <file alias="MainTableView.qml">maininterface/qml/MainTableView.qml</file>
         <file alias="MainGridView.qml">maininterface/qml/MainGridView.qml</file>
+        <file alias="MainViewLoader.qml">maininterface/qml/MainViewLoader.qml</file>
     </qresource>
     <qresource prefix="/widgets">
         <file alias="ActionButtonOverlay.qml">widgets/qml/ActionButtonOverlay.qml</file>


=====================================
modules/gui/qt/widgets/native/navigation_attached.cpp
=====================================
@@ -36,9 +36,16 @@ void NavigationAttached::defaultNavigationGeneric(QJSValue& jsCallback, QQuickIt
                                         Qt::FocusReason reason)
 {
     if (jsCallback.isCallable()) {
-        jsCallback.call();
+        const auto ret = jsCallback.call();
+
+        // if the function returns nothing or true, the action has been handled, stop the traversal
+        // if the function returns a false explictly, continue traversal
+        if (!ret.isBool() || ret.toBool())
+            return;
     }
-    else if (directionItem)
+
+
+    if (directionItem)
     {
         NavigationAttached* nextItem = qobject_cast<NavigationAttached*>(qmlAttachedPropertiesObject<NavigationAttached>(directionItem));
         if (directionItem->isVisible()



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/869a03432a03189e31338827ad5dd56aa900a7a5...f5d795355e4dab5e4d8853a042aba038c3332fa0

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/869a03432a03189e31338827ad5dd56aa900a7a5...f5d795355e4dab5e4d8853a042aba038c3332fa0
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list