[vlc-commits] [Git][videolan/vlc][master] 14 commits: qml/Group(s): Remove dedicated group views

Hugo Beauzée-Luyssen (@chouquette) gitlab at videolan.org
Mon Jan 10 13:41:01 UTC 2022



Hugo Beauzée-Luyssen pushed to branch master at VideoLAN / VLC


Commits:
6ee9299c by Benjamin Arnaud at 2022-01-10T13:21:47+00:00
qml/Group(s): Remove dedicated group views

After this, groups will be hidden until we bring them back in VideoAllDisplay.

- - - - -
0e29029e by Benjamin Arnaud at 2022-01-10T13:21:47+00:00
qt/mainctx: Add the 'grouping' property

And save it in settings.

- - - - -
a3ed3d9c by Benjamin Arnaud at 2022-01-10T13:21:47+00:00
qt/mlvideomodel: Remove the 'id' sorting criteria

- - - - -
c369be64 by Benjamin Arnaud at 2022-01-10T13:21:47+00:00
qt/mlgroup(s): Update 'name' to 'title' and fix MLGroup model

- - - - -
c1b85224 by Benjamin Arnaud at 2022-01-10T13:21:47+00:00
qt/mlgrouplistmodel: Add the GROUP_IS_VIDEO role

- - - - -
4ad03d08 by Benjamin Arnaud at 2022-01-10T13:21:47+00:00
qt/qml_menu_wrapper: Update the GroupListContextMenu implementation

- - - - -
8e461377 by Benjamin Arnaud at 2022-01-10T13:21:47+00:00
qt/qml_menu_wrapper: Create SortMenuVideo

- - - - -
685a8e6b by Benjamin Arnaud at 2022-01-10T13:21:47+00:00
qml/SortControl: Add the 'sortMenu' property

And refactor the implementation

- - - - -
ebd6f412 by Benjamin Arnaud at 2022-01-10T13:21:47+00:00
qml/BannerSources: Add sortMenu alias and refactor SortControl

- - - - -
298f4e4b by Benjamin Arnaud at 2022-01-10T13:21:47+00:00
qml/MainDisplay: Let the view provide a custom 'sortMenu'

And refactor the 'loadView' function.

- - - - -
4fc5b8f8 by Benjamin Arnaud at 2022-01-10T13:21:47+00:00
qml/MediaGroupDisplay: Update 'name' to 'title'

- - - - -
62112b14 by Benjamin Arnaud at 2022-01-10T13:21:47+00:00
qml: Create VideoAllSubDisplay

- - - - -
be184c0b by Benjamin Arnaud at 2022-01-10T13:21:47+00:00
qml/VideoAll(s): Add grouping support

- - - - -
454b57b7 by Benjamin Arnaud at 2022-01-10T13:21:47+00:00
qml/VideoDisplay: Add the 'sortMenu' property

- - - - -


23 changed files:

- modules/gui/qt/Makefile.am
- modules/gui/qt/maininterface/mainctx.cpp
- modules/gui/qt/maininterface/mainctx.hpp
- modules/gui/qt/maininterface/mainui.cpp
- modules/gui/qt/maininterface/qml/BannerSources.qml
- modules/gui/qt/maininterface/qml/MainDisplay.qml
- modules/gui/qt/medialibrary/mlgroup.cpp
- modules/gui/qt/medialibrary/mlgroup.hpp
- modules/gui/qt/medialibrary/mlgrouplistmodel.cpp
- modules/gui/qt/medialibrary/mlgrouplistmodel.hpp
- modules/gui/qt/medialibrary/mlvideomodel.cpp
- modules/gui/qt/medialibrary/qml/MediaGroupDisplay.qml
- − modules/gui/qt/medialibrary/qml/MediaGroupList.qml
- modules/gui/qt/medialibrary/qml/VideoAll.qml
- modules/gui/qt/medialibrary/qml/VideoAllDisplay.qml
- + modules/gui/qt/medialibrary/qml/VideoAllSubDisplay.qml
- modules/gui/qt/medialibrary/qml/VideoDisplay.qml
- − modules/gui/qt/medialibrary/qml/VideoGroupsDisplay.qml
- modules/gui/qt/menus/qml_menu_wrapper.cpp
- modules/gui/qt/menus/qml_menu_wrapper.hpp
- modules/gui/qt/vlc.qrc
- modules/gui/qt/widgets/qml/SortControl.qml
- po/POTFILES.in


Changes:

=====================================
modules/gui/qt/Makefile.am
=====================================
@@ -737,7 +737,6 @@ libqt_plugin_la_QML = \
 	gui/qt/medialibrary/qml/AudioGridItem.qml \
 	gui/qt/medialibrary/qml/EmptyLabel.qml \
 	gui/qt/medialibrary/qml/MediaGroupDisplay.qml \
-	gui/qt/medialibrary/qml/MediaGroupList.qml \
 	gui/qt/medialibrary/qml/MusicAlbums.qml \
 	gui/qt/medialibrary/qml/MusicAlbumsDisplay.qml \
 	gui/qt/medialibrary/qml/MusicAlbumsGridExpandDelegate.qml \
@@ -756,7 +755,7 @@ libqt_plugin_la_QML = \
 	gui/qt/medialibrary/qml/VideoDisplay.qml \
 	gui/qt/medialibrary/qml/VideoAll.qml \
 	gui/qt/medialibrary/qml/VideoAllDisplay.qml \
-	gui/qt/medialibrary/qml/VideoGroupsDisplay.qml \
+	gui/qt/medialibrary/qml/VideoAllSubDisplay.qml \
 	gui/qt/medialibrary/qml/PlaylistMediaList.qml \
 	gui/qt/medialibrary/qml/PlaylistMedia.qml \
 	gui/qt/medialibrary/qml/PlaylistMediaDelegate.qml \


=====================================
modules/gui/qt/maininterface/mainctx.cpp
=====================================
@@ -217,6 +217,8 @@ MainCtx::~MainCtx()
     settings->setValue( "playlist-width-factor", playlistWidthFactor);
 
     settings->setValue( "grid-view", m_gridView );
+    settings->setValue( "grouping", m_grouping );
+
     settings->setValue( "color-scheme", m_colorScheme->currentScheme() );
     /* Save the stackCentralW sizes */
     settings->endGroup();
@@ -322,6 +324,8 @@ void MainCtx::loadFromSettingsImpl(const bool callSignals)
 
     loadFromSettings(m_gridView, "MainWindow/grid-view", true, &MainCtx::gridViewChanged);
 
+    loadFromSettings(m_grouping, "MainWindow/grouping", GROUPING_NONE, &MainCtx::groupingChanged);
+
     loadFromSettings(m_showRemainingTime, "MainWindow/ShowRemainingTime", false, &MainCtx::showRemainingTimeChanged);
 
     loadFromSettings(m_pinVideoControls, "MainWindow/pin-video-controls", false, &MainCtx::pinVideoControlsChanged);
@@ -481,6 +485,13 @@ void MainCtx::setGridView(bool asGrid)
     emit gridViewChanged( asGrid );
 }
 
+void MainCtx::setGrouping(Grouping grouping)
+{
+    m_grouping = grouping;
+
+    emit groupingChanged(grouping);
+}
+
 void MainCtx::setInterfaceAlwaysOnTop( bool on_top )
 {
     b_interfaceOnTop = on_top;


=====================================
modules/gui/qt/maininterface/mainctx.hpp
=====================================
@@ -160,6 +160,7 @@ class MainCtx : public QObject
     Q_PROPERTY(bool mediaLibraryAvailable READ hasMediaLibrary CONSTANT FINAL)
     Q_PROPERTY(MediaLib* mediaLibrary READ getMediaLibrary CONSTANT FINAL)
     Q_PROPERTY(bool gridView READ hasGridView WRITE setGridView NOTIFY gridViewChanged FINAL)
+    Q_PROPERTY(Grouping grouping READ grouping WRITE setGrouping NOTIFY groupingChanged FINAL)
     Q_PROPERTY(ColorSchemeModel* colorScheme READ getColorScheme CONSTANT FINAL)
     Q_PROPERTY(bool hasVLM READ hasVLM CONSTANT FINAL)
     Q_PROPERTY(bool clientSideDecoration READ useClientSideDecoration NOTIFY useClientSideDecorationChanged FINAL)
@@ -211,6 +212,16 @@ public:
         RAISE_AUDIO,
         RAISE_AUDIOVIDEO,
     };
+
+    enum Grouping
+    {
+        GROUPING_NONE,
+        GROUPING_NAME,
+        GROUPING_FOLDER
+    };
+
+    Q_ENUM(Grouping)
+
     inline bool isInterfaceFullScreen() const { return m_windowVisibility == QWindow::FullScreen; }
     inline bool isInterfaceVisible() const { return m_windowVisibility != QWindow::Hidden; }
     bool isPlaylistDocked() { return b_playlistDocked; }
@@ -227,6 +238,7 @@ public:
     inline bool hasMediaLibrary() const { return b_hasMedialibrary; }
     inline MediaLib* getMediaLibrary() const { return m_medialib; }
     inline bool hasGridView() const { return m_gridView; }
+    inline Grouping grouping() const { return m_grouping; }
     inline ColorSchemeModel* getColorScheme() const { return m_colorScheme; }
     bool hasVLM() const;
     bool useClientSideDecoration() const;
@@ -304,6 +316,7 @@ protected:
     bool                 b_hasMedialibrary = false;
     MediaLib*            m_medialib = nullptr;
     bool                 m_gridView = false;
+    Grouping             m_grouping = GROUPING_NONE;
     ColorSchemeModel*    m_colorScheme = nullptr;
     bool                 m_windowTitlebar = true;
     bool                 m_hasToolbarMenu = false;
@@ -337,6 +350,7 @@ public slots:
     void setInterfaceAlwaysOnTop( bool );
     void setShowRemainingTime( bool );
     void setGridView( bool );
+    void setGrouping( Grouping );
     void incrementIntfUserScaleFactor( bool increment);
     void setIntfUserScaleFactor( double );
     void setPinVideoControls( bool );
@@ -380,6 +394,7 @@ signals:
     void hasEmbededVideoChanged(bool);
     void showRemainingTimeChanged(bool);
     void gridViewChanged( bool );
+    void groupingChanged( Grouping );
     void colorSchemeChanged( QString );
     void useClientSideDecorationChanged();
     void hasToolbarMenuChanged();


=====================================
modules/gui/qt/maininterface/mainui.cpp
=====================================
@@ -286,6 +286,7 @@ void MainUI::registerQMLTypes()
 
         qmlRegisterType<StringListMenu>( uri, versionMajor, versionMinor, "StringListMenu" );
         qmlRegisterType<SortMenu>( uri, versionMajor, versionMinor, "SortMenu" );
+        qmlRegisterType<SortMenuVideo>( uri, versionMajor, versionMinor, "SortMenuVideo" );
         qmlRegisterType<QmlGlobalMenu>( uri, versionMajor, versionMinor, "QmlGlobalMenu" );
         qmlRegisterType<QmlMenuBar>( uri, versionMajor, versionMinor, "QmlMenuBar" );
         qmlRegisterType<NetworkMediaContextMenu>( uri, versionMajor, versionMinor, "NetworkMediaContextMenu" );


=====================================
modules/gui/qt/maininterface/qml/BannerSources.qml
=====================================
@@ -42,6 +42,7 @@ FocusScope {
 
     signal itemClicked(int index)
 
+    property alias sortMenu: sortControl.menu
     property alias sortModel: sortControl.model
     property var contentModel
     property alias isViewMultiView: list_grid_btn.visible
@@ -262,30 +263,32 @@ FocusScope {
                             Widgets.SortControl {
                                 id: sortControl
 
-                                textRole: "text"
-                                criteriaRole: "criteria"
-
                                 width: VLCStyle.bannerButton_width
                                 height: VLCStyle.bannerButton_height
+
                                 iconSize: VLCStyle.banner_icon_size
 
                                 visible: root.sortModel !== undefined && root.sortModel.length > 1
+
                                 enabled: visible
 
+                                textRole: "text"
+                                criteriaRole: "criteria"
+
+                                sortKey: contentModel ? contentModel.sortCriteria
+                                                      : PlaylistControllerModel.SORT_KEY_NONE
+
+                                sortOrder: contentModel ? contentModel.sortOrder : undefined
+
                                 onSortSelected: {
-                                    if (contentModel !== undefined) {
+                                    if (contentModel !== undefined)
                                         contentModel.sortCriteria = type
-                                    }
                                 }
 
                                 onSortOrderSelected: {
-                                    if (contentModel !== undefined) {
+                                    if (contentModel !== undefined)
                                         contentModel.sortOrder = type
-                                    }
                                 }
-
-                                sortKey: contentModel ? contentModel.sortCriteria : PlaylistControllerModel.SORT_KEY_NONE
-                                sortOrder: contentModel ? contentModel.sortOrder : undefined
                             }
                         }
 


=====================================
modules/gui/qt/maininterface/qml/MainDisplay.qml
=====================================
@@ -59,29 +59,48 @@ FocusScope {
     function loadView() {
         var found = stackView.loadView(root.pageModel, root.view.name, root.view.properties)
 
-        stackView.currentItem.Navigation.parentItem = medialibId
-        stackView.currentItem.Navigation.upItem = sourcesBanner
-        stackView.currentItem.Navigation.rightItem = playlistColumn
-        stackView.currentItem.Navigation.downItem = Qt.binding(function() {
+        var item = stackView.currentItem
+
+        item.Navigation.parentItem = medialibId
+        item.Navigation.upItem = sourcesBanner
+        item.Navigation.rightItem = playlistColumn
+
+        item.Navigation.downItem = Qt.binding(function() {
             return miniPlayer.visible ? miniPlayer : medialibId
         })
 
-        sourcesBanner.localMenuDelegate = Qt.binding(function () { return !!stackView.currentItem.localMenuDelegate ? stackView.currentItem.localMenuDelegate : null })
-        sourcesBanner.sortModel = Qt.binding(function () { return stackView.currentItem.sortModel  })
-        sourcesBanner.contentModel = Qt.binding(function () { return stackView.currentItem.contentModel })
-        sourcesBanner.extraLocalActions = Qt.binding(function () { return stackView.currentItem.extraLocalActions })
+        sourcesBanner.localMenuDelegate = Qt.binding(function () {
+            return !!item.localMenuDelegate ? item.localMenuDelegate : null
+        })
+
+        // NOTE: sortMenu is declared with the SortMenu type, so when it's undefined we have to
+        //       return null to avoid a QML warning.
+        sourcesBanner.sortMenu = Qt.binding(function () {
+            if (item.sortMenu)
+                return item.sortMenu
+            else
+                return null
+        })
+
+        sourcesBanner.sortModel = Qt.binding(function () { return item.sortModel })
+        sourcesBanner.contentModel = Qt.binding(function () { return item.contentModel })
+
+        sourcesBanner.extraLocalActions = Qt.binding(function () { return item.extraLocalActions })
+
         sourcesBanner.isViewMultiView = Qt.binding(function () {
-            return stackView.currentItem.isViewMultiView === undefined || stackView.currentItem.isViewMultiView
+            return item.isViewMultiView === undefined || item.isViewMultiView
         })
+
         // Restore sourcesBanner state
         sourcesBanner.selectedIndex = pageModel.filter(function (e) {
-            return e.listed;
+            return e.listed
         }).findIndex(function (e) {
             return e.name === root.view
         })
-        if (stackView.currentItem.pageModel !== undefined)
-            sourcesBanner.subSelectedIndex = stackView.currentItem.pageModel.findIndex(function (e) {
-                return e.name === stackView.currentItem.view
+
+        if (item.pageModel !== undefined)
+            sourcesBanner.subSelectedIndex = item.pageModel.findIndex(function (e) {
+                return e.name === item.view
             })
 
         if (Player.hasVideoOutput && MainCtx.hasEmbededVideo)


=====================================
modules/gui/qt/medialibrary/mlgroup.cpp
=====================================
@@ -29,7 +29,7 @@
 
 MLGroup::MLGroup(const vlc_ml_group_t * data)
     : MLItemCover(MLItemId(data->i_id, VLC_ML_PARENT_GROUP))
-    , m_name(qfu(data->psz_name))
+    , m_title(qfu(data->psz_name))
     , m_duration(data->i_duration)
     , m_date(data->i_creation_date)
     , m_count(data->i_nb_total_media)
@@ -41,9 +41,9 @@ MLGroup::MLGroup(const vlc_ml_group_t * data)
 // Interface
 //-------------------------------------------------------------------------------------------------
 
-QString MLGroup::getName() const
+QString MLGroup::getTitle() const
 {
-    return m_name;
+    return m_title;
 }
 
 //-------------------------------------------------------------------------------------------------


=====================================
modules/gui/qt/medialibrary/mlgroup.hpp
=====================================
@@ -34,7 +34,7 @@ public:
     MLGroup(const vlc_ml_group_t * data);
 
 public: // Interface
-    QString getName() const;
+    QString getTitle() const;
 
     int64_t getDuration() const;
 
@@ -43,7 +43,7 @@ public: // Interface
     unsigned int getCount() const;
 
 private:
-    QString m_name;
+    QString m_title;
 
     int64_t m_duration;
 


=====================================
modules/gui/qt/medialibrary/mlgrouplistmodel.cpp
=====================================
@@ -44,7 +44,7 @@ static const int MLGROUPLISTMODEL_COVER_HEIGHT = 320 * 2;
 
 static const QHash<QByteArray, vlc_ml_sorting_criteria_t> criterias =
 {
-    { "name",     VLC_ML_SORTING_ALPHA         },
+    { "title",    VLC_ML_SORTING_ALPHA         },
     { "duration", VLC_ML_SORTING_DURATION      },
     { "date",     VLC_ML_SORTING_INSERTIONDATE }
 };
@@ -64,21 +64,22 @@ QHash<int, QByteArray> MLGroupListModel::roleNames() const /* override */
 {
     return
     {
+        { GROUP_IS_VIDEO,           "isVideo"            },
         { GROUP_ID,                 "id"                 },
-        { GROUP_NAME,               "name"               },
+        { GROUP_TITLE,              "title"              },
         { GROUP_THUMBNAIL,          "thumbnail"          },
         { GROUP_DURATION,           "duration"           },
         { GROUP_DATE,               "date"               },
         { GROUP_COUNT,              "count"              },
         // NOTE: Media specific.
         { GROUP_IS_NEW,             "isNew"              },
-        { GROUP_TITLE,              "title"              },
+        { GROUP_FILENAME,           "fileName"           },
+        { GROUP_PROGRESS,           "progress"           },
+        { GROUP_PLAYCOUNT,          "playcount"          },
         { GROUP_RESOLUTION,         "resolution_name"    },
         { GROUP_CHANNEL,            "channel"            },
         { GROUP_MRL,                "mrl"                },
         { GROUP_MRL_DISPLAY,        "display_mrl"        },
-        { GROUP_PROGRESS,           "progress"           },
-        { GROUP_PLAYCOUNT,          "playcount"          },
         { GROUP_VIDEO_TRACK,        "videoDesc"          },
         { GROUP_AUDIO_TRACK,        "audioDesc"          },
         { GROUP_TITLE_FIRST_SYMBOL, "title_first_symbol" }
@@ -98,12 +99,14 @@ QVariant MLGroupListModel::itemRoleData(MLItem *item, const int role) const /* o
         {
             // NOTE: This is the condition for QWidget view(s).
             case Qt::DisplayRole:
-                return QVariant::fromValue(group->getName());
+                return QVariant::fromValue(group->getTitle());
             // NOTE: These are the conditions for QML view(s).
+            case GROUP_IS_VIDEO:
+                return false;
             case GROUP_ID:
                 return QVariant::fromValue(group->getId());
-            case GROUP_NAME:
-                return QVariant::fromValue(group->getName());
+            case GROUP_TITLE:
+                return QVariant::fromValue(group->getTitle());
             case GROUP_THUMBNAIL:
                 return getCover(group);
             case GROUP_DURATION:
@@ -124,9 +127,11 @@ QVariant MLGroupListModel::itemRoleData(MLItem *item, const int role) const /* o
         {
             case Qt::DisplayRole:
                 return QVariant::fromValue(video->getTitle());
+            case GROUP_IS_VIDEO:
+                return true;
             case GROUP_ID:
                 return QVariant::fromValue(video->getId());
-            case GROUP_NAME:
+            case GROUP_TITLE:
                 return QVariant::fromValue(video->getTitle());
             case GROUP_THUMBNAIL:
             {
@@ -147,8 +152,12 @@ QVariant MLGroupListModel::itemRoleData(MLItem *item, const int role) const /* o
             // NOTE: Media specific.
             case GROUP_IS_NEW:
                 return QVariant::fromValue(video->isNew());
-            case GROUP_TITLE:
-                return QVariant::fromValue(video->getTitle());
+            case GROUP_FILENAME:
+                return QVariant::fromValue(video->getFileName());
+            case GROUP_PROGRESS:
+                return QVariant::fromValue(video->getProgress());
+            case GROUP_PLAYCOUNT:
+                return QVariant::fromValue(video->getPlayCount());
             case GROUP_RESOLUTION:
                 return QVariant::fromValue(video->getResolutionName());
             case GROUP_CHANNEL:
@@ -157,10 +166,6 @@ QVariant MLGroupListModel::itemRoleData(MLItem *item, const int role) const /* o
                 return QVariant::fromValue(video->getMRL());
             case GROUP_MRL_DISPLAY:
                 return QVariant::fromValue(video->getDisplayMRL());
-            case GROUP_PROGRESS:
-                return QVariant::fromValue(video->getProgress());
-            case GROUP_PLAYCOUNT:
-                return QVariant::fromValue(video->getPlayCount());
             case GROUP_VIDEO_TRACK:
                 return QVariant::fromValue(video->getVideoDesc());
             case GROUP_AUDIO_TRACK:
@@ -181,7 +186,7 @@ vlc_ml_sorting_criteria_t MLGroupListModel::roleToCriteria(int role) const /* ov
 {
     switch (role)
     {
-        case GROUP_NAME:
+        case GROUP_TITLE:
             return VLC_ML_SORTING_ALPHA;
         case GROUP_DURATION:
             return VLC_ML_SORTING_DURATION;


=====================================
modules/gui/qt/medialibrary/mlgrouplistmodel.hpp
=====================================
@@ -35,29 +35,32 @@ class MLGroupListModel : public MLBaseModel
 public:
     enum Roles
     {
-        GROUP_ID = Qt::UserRole + 1,
-        GROUP_NAME,
+        GROUP_IS_VIDEO = Qt::UserRole + 1,
+        GROUP_ID,
+        GROUP_TITLE,
         GROUP_THUMBNAIL,
         GROUP_DURATION,
         GROUP_DATE,
         GROUP_COUNT,
         // NOTE: Media specific.
         GROUP_IS_NEW,
-        GROUP_TITLE,
+        GROUP_FILENAME,
+        GROUP_PROGRESS,
+        GROUP_PLAYCOUNT,
         GROUP_RESOLUTION,
         GROUP_CHANNEL,
         GROUP_MRL,
         GROUP_MRL_DISPLAY,
-        GROUP_PROGRESS,
-        GROUP_PLAYCOUNT,
         GROUP_VIDEO_TRACK,
         GROUP_AUDIO_TRACK,
+
         GROUP_TITLE_FIRST_SYMBOL
     };
 
 public:
     explicit MLGroupListModel(QObject * parent = nullptr);
 
+
 public: // QAbstractItemModel implementation
     QHash<int, QByteArray> roleNames() const override;
 


=====================================
modules/gui/qt/medialibrary/mlvideomodel.cpp
=====================================
@@ -32,7 +32,6 @@ QVariantList getVariantList(const QList<T> & desc)
 }
 
 QHash<QByteArray, vlc_ml_sorting_criteria_t> MLVideoModel::M_names_to_criteria = {
-    {"id", VLC_ML_SORTING_DEFAULT},
     {"title", VLC_ML_SORTING_ALPHA},
     {"duration", VLC_ML_SORTING_DURATION},
     {"playcount", VLC_ML_SORTING_PLAYCOUNT},


=====================================
modules/gui/qt/medialibrary/qml/MediaGroupDisplay.qml
=====================================
@@ -28,36 +28,32 @@ import "qrc:///style/"
 VideoAll {
     id: root
 
-    //---------------------------------------------------------------------------------------------
     // Properties
-    //---------------------------------------------------------------------------------------------
 
-    property int    initialIndex: 0
-    property MLItemId    initialId
-    property string initialName
+    property int      initialIndex: 0
+    property MLItemId initialId
+    property string   initialName
 
-    //---------------------------------------------------------------------------------------------
     // Aliases
-    //---------------------------------------------------------------------------------------------
 
     // NOTE: This is used to determine which media(s) shall be displayed..
-    property alias parentId: modelGroup.parentId
+    property alias parentId: modelVideo.parentId
 
-    // NOTE: The name of the group.
-    property string name: initialName
+    // NOTE: The title of the group.
+    property string title: initialTitle
 
-    //---------------------------------------------------------------------------------------------
-    // Childs
-    //---------------------------------------------------------------------------------------------
+    // Children
 
     model: MLVideoModel {
-        id: modelGroup
+        id: modelVideo
 
         ml: MediaLib
 
         parentId: initialId
     }
 
+    contextMenu: VideoContextMenu { model: modelVideo }
+
     header: Column {
         width: root.width
 
@@ -71,7 +67,7 @@ VideoAll {
             // NOTE: We want this to be properly aligned with the grid items.
             anchors.leftMargin: contentMargin + VLCStyle.margin_normal
 
-            text: root.name
+            text: root.title
         }
     }
 }


=====================================
modules/gui/qt/medialibrary/qml/MediaGroupList.qml deleted
=====================================
@@ -1,402 +0,0 @@
-/*****************************************************************************
- * Copyright (C) 2021 VLC authors and VideoLAN
- *
- * Authors: Benjamin Arnaud <bunjee at omega.gg>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * ( at your option ) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-
-import QtQuick      2.11
-import QtQml.Models 2.11
-
-import org.videolan.medialib 0.1
-import org.videolan.vlc 0.1
-
-import "qrc:///widgets/" as Widgets
-import "qrc:///main/"    as MainInterface
-import "qrc:///util/"    as Util
-import "qrc:///style/"
-
-FocusScope {
-    id: root
-
-    //---------------------------------------------------------------------------------------------
-    // Properties
-    //---------------------------------------------------------------------------------------------
-
-    readonly property int currentIndex: currentItem.currentIndex
-
-    property int initialIndex: 0
-
-    property var sortModel: [
-        { text: I18n.qtr("Alphabetic"), criteria: "name"     },
-        { text: I18n.qtr("Duration"),   criteria: "duration" },
-        { text: I18n.qtr("Date"),       criteria: "date"     }
-    ]
-
-    //---------------------------------------------------------------------------------------------
-    // Alias
-    //---------------------------------------------------------------------------------------------
-
-    property alias model: model
-
-    // Private
-
-    property alias _currentView: view.currentItem
-
-    //---------------------------------------------------------------------------------------------
-    // Signals
-    //---------------------------------------------------------------------------------------------
-
-    signal showList(var model, int reason)
-
-    //---------------------------------------------------------------------------------------------
-    // Events
-    //---------------------------------------------------------------------------------------------
-
-    onModelChanged: resetFocus()
-
-    onInitialIndexChanged: resetFocus()
-
-    //---------------------------------------------------------------------------------------------
-    // Connections
-    //---------------------------------------------------------------------------------------------
-
-    Connections {
-        target: MainCtx
-
-        onGridViewChanged: {
-            if (MainCtx.gridView) view.replace(grid);
-            else                        view.replace(list);
-        }
-    }
-
-    //---------------------------------------------------------------------------------------------
-    // Functions
-    //---------------------------------------------------------------------------------------------
-
-    function setCurrentItemFocus(reason) {
-        _currentView.setCurrentItemFocus(reason);
-    }
-
-    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 (_currentView)
-            _currentView.positionViewAtIndex(initialIndex, ItemView.Contain);
-    }
-
-    //---------------------------------------------------------------------------------------------
-    // Private
-
-    function _actionAtIndex() {
-        if (modelSelect.selectedIndexes.length > 1) {
-            g_mainDisplay.play(MediaLib, model.getIdsForIndexes(modelSelect.selectedIndexes));
-        } else if (modelSelect.selectedIndexes.length === 1) {
-            var index = modelSelect.selectedIndexes[0];
-            _showList(model.getDataAt(index), Qt.TabFocusReason);
-        }
-    }
-
-    function _showList(model, reason)
-    {
-        // NOTE: If the count is 1 we consider the group is a media.
-        if (model.count == 1)
-            g_mainDisplay.play(MediaLib, model.id);
-        else
-            showList(model, reason);
-    }
-
-    function _onNavigationCancel() {
-        if (_currentView.currentIndex > 0) {
-            _currentView.currentIndex = 0;
-
-            _currentView.positionViewAtIndex(0, ItemView.Contain);
-        } else {
-            root.Navigation.defaultNavigationCancel();
-        }
-    }
-
-    function _getLabels(model, string)
-    {
-        var count = model.count;
-
-        if (count === 1) {
-            return [
-                model.resolution_name || "",
-                model.channel         || ""
-            ].filter(function(a) { return a !== "" });
-        } else {
-            if (count < 100)
-                return [ string.arg(count) ];
-            else
-                return [ string.arg("99+") ];
-        }
-    }
-
-    //---------------------------------------------------------------------------------------------
-    // Childs
-    //---------------------------------------------------------------------------------------------
-
-    MLGroupListModel {
-        id: model
-
-        ml: MediaLib
-
-        onCountChanged: {
-            if (count === 0 || modelSelect.hasSelection)
-                return;
-
-            resetFocus();
-        }
-    }
-
-    Util.SelectableDelegateModel {
-        id: modelSelect
-
-        model: root.model
-    }
-
-    Widgets.StackViewExt {
-        id: view
-
-        anchors.fill: parent
-
-        initialItem: (MainCtx.gridView) ? grid : list
-
-        focus: (model.count !== 0)
-    }
-
-    GroupListContextMenu {
-        id: contextMenu
-
-        model: root.model
-    }
-
-    Widgets.MLDragItem {
-        id: dragItemGroup
-
-        mlModel: model
-
-        indexes: modelSelect.selectedIndexes
-
-        coverRole: "thumbnail"
-
-        titleRole: "name"
-    }
-
-    //---------------------------------------------------------------------------------------------
-    // Components
-
-    Component {
-        id: grid
-
-        MainInterface.MainGridView {
-            id: gridView
-
-            //-------------------------------------------------------------------------------------
-            // Settings
-
-            cellWidth : VLCStyle.gridItem_video_width
-            cellHeight: VLCStyle.gridItem_video_height
-
-            topMargin: VLCStyle.margin_large
-
-            model: root.model
-
-            selectionDelegateModel: modelSelect
-
-            activeFocusOnTab: true
-
-            Navigation.parentItem: root
-            //cancelAction takes a *function* pass it directly
-            Navigation.cancelAction: root._onNavigationCancel
-
-            expandDelegate: VideoInfoExpandPanel {
-                width: gridView.width
-
-                x: 0
-
-                model: root.model
-
-                Navigation.parentItem: gridView
-
-                Navigation.upAction    : function() { gridView.retract() }
-                Navigation.downAction  : function() { gridView.retract() }
-                Navigation.cancelAction: function() { gridView.retract() }
-
-                onRetract: gridView.retract()
-            }
-
-            delegate: VideoGridItem {
-                id: gridItem
-
-                //---------------------------------------------------------------------------------
-                // properties required by ExpandGridView
-
-                property var model: ({})
-                property int index: -1
-
-                //---------------------------------------------------------------------------------
-                // Settings
-
-                opacity: (gridView.expandIndex !== -1
-                          &&
-                          gridView.expandIndex !== gridItem.index) ? 0.7 : 1
-
-                title: (model.name) ? model.name
-                                    : I18n.qtr("Unknown title")
-
-                labels: _getLabels(model, I18n.qtr("%1 Videos"))
-
-                // NOTE: We don't want to show the indicator for a group..
-                // FIXME: Sometimes MLBaseModel::getDataAt returns {} so we use 'isNew === true'.
-                showNewIndicator: (model.count === 1 && model.isNew === true)
-
-                dragItem: dragItemGroup
-
-                selectedUnderlay  : shadows.selected
-                unselectedUnderlay: shadows.unselected
-
-                //---------------------------------------------------------------------------------
-                // Events
-
-                onItemClicked: gridView.leftClickOnItem(modifier, index)
-
-                onItemDoubleClicked: _showList(model, Qt.MouseFocusReason)
-
-                onContextMenuButtonClicked: {
-                    gridView.rightClickOnItem(index);
-
-                    contextMenu.popup(modelSelect.selectedIndexes, globalMousePos,
-                                      { "information" : index });
-                }
-
-                //---------------------------------------------------------------------------------
-                // Animations
-
-                Behavior on opacity { NumberAnimation { duration: VLCStyle.duration_faster } }
-            }
-
-            //-------------------------------------------------------------------------------------
-            // 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)
-            }
-
-            onActionAtIndex: _actionAtIndex()
-
-            //-------------------------------------------------------------------------------------
-            // Connections
-
-            Connections {
-                target: contextMenu
-
-                // FIXME: We need to implement this in qml_menu_wrapper.
-                //onShowMediaInformation: gridView.switchExpandItem(index)
-            }
-
-            //-------------------------------------------------------------------------------------
-            // Childs
-
-            Widgets.GridShadows {
-                id: shadows
-
-                coverWidth : VLCStyle.gridCover_video_width
-                coverHeight: VLCStyle.gridCover_video_height
-            }
-        }
-    }
-
-    Component {
-        id: list
-
-        VideoListDisplay
-        {
-            id: listView
-
-            //-------------------------------------------------------------------------------------
-            // Settings
-
-            model: root.model
-
-            mainCriteria: "name"
-
-            selectionDelegateModel: modelSelect
-
-            dragItem: dragItemGroup
-
-            headerTopPadding: VLCStyle.margin_normal
-
-            headerPositioning: ListView.InlineHeader
-
-            Navigation.parentItem: root
-            //cancelAction takes a *function* pass it directly
-            Navigation.cancelAction: root._onNavigationCancel
-
-            //-------------------------------------------------------------------------------------
-            // Events
-
-            onActionForSelection: _actionAtIndex()
-
-            // NOTE: We make sure we're double clicking on a group.
-            onItemDoubleClicked: if (model.count > 1) showList(model, Qt.MouseFocusReason)
-
-            onContextMenuButtonClicked: contextMenu.popup(modelSelect.selectedIndexes,
-                                                          globalMousePos)
-
-            onRightClick: contextMenu.popup(modelSelect.selectedIndexes, globalMousePos)
-
-            //-------------------------------------------------------------------------------------
-            // Functions
-
-            function onLabels(model) {
-                return _getLabels(model, "%1");
-            }
-        }
-    }
-
-    EmptyLabel {
-        anchors.fill: parent
-
-        coverWidth : VLCStyle.dp(182, VLCStyle.scale)
-        coverHeight: VLCStyle.dp(114, VLCStyle.scale)
-
-        visible: (model.count === 0)
-
-        text: I18n.qtr("No video found\nPlease try adding sources, by going to the Network tab")
-
-        cover: VLCStyle.noArtVideoCover
-
-        Navigation.parentItem: root
-
-        focus: visible
-    }
-}


=====================================
modules/gui/qt/medialibrary/qml/VideoAll.qml
=====================================
@@ -34,9 +34,8 @@ FocusScope {
 
     // Properties
 
-    readonly property int contentMargin: (MainCtx.gridView
-                                          &&
-                                          _currentView) ? _currentView.contentMargin : 0
+    readonly property int contentMargin: (_currentView.contentMargin) ? _currentView.contentMargin
+                                                                      : 0
 
     // NOTE: Specify an optional header for the view.
     property Component header: undefined
@@ -47,7 +46,10 @@ FocusScope {
 
     property int initialIndex: 0
 
-    property MLVideoModel model: MLVideoModel { ml: MediaLib }
+    /* required */ property var model
+
+    // NOTE: The ContextMenu depends on the model so we have to provide it too.
+    /* required */ property var contextMenu
 
     property var sortModel: [
         { text: I18n.qtr("Alphabetic"), criteria: "title"    },
@@ -74,8 +76,8 @@ FocusScope {
         target: MainCtx
 
         onGridViewChanged: {
-            if (MainCtx.gridView) view.replace(grid);
-            else                        view.replace(list);
+            if (MainCtx.gridView) view.replace(grid)
+            else                  view.replace(list)
         }
     }
 
@@ -110,34 +112,51 @@ FocusScope {
             _currentView.positionViewAtIndex(initialIndex, ItemView.Contain)
     }
 
-    // Private
-
-    function _actionAtIndex() {
-        g_mainDisplay.showPlayer();
+    function getLabel(model) {
+        if (!model) return ""
 
-        MediaLib.addAndPlay(model.getIdsForIndexes(modelSelect.selectedIndexes));
+        return [
+            model.resolution_name || "",
+            model.channel || ""
+        ].filter(function(a) { return a !== "" })
     }
 
     // Events
 
+    function onAction(indexes) {
+        g_mainDisplay.showPlayer()
+
+        MediaLib.addAndPlay(model.getIdsForIndexes(indexes))
+    }
+
+    function onDoubleClick(object) {
+        g_mainDisplay.play(MediaLib, object.id)
+    }
+
+    function onLabelGrid(object) { return getLabel(object) }
+    function onLabelList(object) { return getLabel(object) }
+
+    // Private events
+
     function _onNavigationUp() {
-        if (headerItem && headerItem.focus)
-            headerItem.setCurrentItemFocus(Qt.TabFocusReason);
+        // NOTE: We are calling the header focus function when we have one.
+        if (headerItem && (typeof headerItem.setCurrentItemFocus === "function"))
+            headerItem.setCurrentItemFocus(Qt.TabFocusReason)
         else
-            Navigation.defaultNavigationUp();
+            Navigation.defaultNavigationUp()
     }
 
     function _onNavigationCancel() {
         if (_currentView.currentIndex <= 0) {
-            Navigation.defaultNavigationCancel();
+            Navigation.defaultNavigationCancel()
         } else {
-            _currentView.currentIndex = 0;
+            _currentView.currentIndex = 0
 
-            _currentView.positionViewAtIndex(0, ItemView.Contain);
+            _currentView.positionViewAtIndex(0, ItemView.Contain)
         }
     }
 
-    // Childs
+    // Children
 
     Widgets.StackViewExt {
         id: view
@@ -165,12 +184,6 @@ FocusScope {
         model: root.model
     }
 
-    VideoContextMenu {
-        id: contextMenu
-
-        model: root.model
-    }
-
     Component {
         id: grid
 
@@ -194,29 +207,15 @@ FocusScope {
 
             activeFocusOnTab: true
 
+            // Navigation
+
             Navigation.parentItem: root
 
             Navigation.upAction: _onNavigationUp
 
-            //cancelAction takes a *function* pass it directly
+            // NOTE: cancelAction takes a function, we pass it directly.
             Navigation.cancelAction: root._onNavigationCancel
 
-            expandDelegate: VideoInfoExpandPanel {
-                width: gridView.width
-
-                x: 0
-
-                model: root.model
-
-                Navigation.parentItem: gridView
-
-                Navigation.cancelAction: function() { gridView.retract() }
-                Navigation.upAction    : function() { gridView.retract() }
-                Navigation.downAction  : function() { gridView.retract() }
-
-                onRetract: gridView.retract()
-            }
-
             // Events
 
             // NOTE: Define the initial position and selection. This is done on activeFocus rather
@@ -229,17 +228,17 @@ FocusScope {
                 modelSelect.select(model.index(0,0), ItemSelectionModel.ClearAndSelect);
             }
 
-            onActionAtIndex: _actionAtIndex()
+            onActionAtIndex: root.onAction(modelSelect.selectedIndexes)
 
             // Connections
 
             Connections {
-                target: contextMenu
+                target: root.contextMenu
 
                 onShowMediaInformation: gridView.switchExpandItem(index)
             }
 
-            // Childs
+            // Children
 
             Widgets.GridShadows {
                 id: shadows
@@ -262,6 +261,8 @@ FocusScope {
                           &&
                           gridView.expandIndex !== gridItem.index) ? 0.7 : 1
 
+                labels: root.onLabelGrid(model)
+
                 // FIXME: Sometimes MLBaseModel::getDataAt returns {} so we use 'isNew === true'.
                 showNewIndicator: (model.isNew === true)
 
@@ -274,19 +275,35 @@ FocusScope {
 
                 onItemClicked: gridView.leftClickOnItem(modifier, index)
 
-                onItemDoubleClicked: g_mainDisplay.play(MediaLib, model.id)
+                onItemDoubleClicked: root.onDoubleClick(model)
 
                 onContextMenuButtonClicked: {
                     gridView.rightClickOnItem(index);
 
-                    contextMenu.popup(modelSelect.selectedIndexes, globalMousePos,
-                                      { "information" : index });
+                    root.contextMenu.popup(modelSelect.selectedIndexes, globalMousePos,
+                                           { "information" : index });
                 }
 
                 // Animations
 
                 Behavior on opacity { NumberAnimation { duration: VLCStyle.duration_faster } }
             }
+
+            expandDelegate: VideoInfoExpandPanel {
+                width: gridView.width
+
+                x: 0
+
+                model: root.model
+
+                Navigation.parentItem: gridView
+
+                Navigation.cancelAction: function() { gridView.retract() }
+                Navigation.upAction    : function() { gridView.retract() }
+                Navigation.downAction  : function() { gridView.retract() }
+
+                onRetract: gridView.retract()
+            }
         }
     }
 
@@ -313,6 +330,8 @@ FocusScope {
 
             activeFocusOnTab: true
 
+            // Navigation
+
             Navigation.parentItem: root
 
             Navigation.upAction: _onNavigationUp
@@ -322,14 +341,18 @@ FocusScope {
 
             // Events
 
-            onActionForSelection: _actionAtIndex()
+            onActionForSelection: root.onAction(modelSelect.selectedIndexes)
+
+            onItemDoubleClicked: root.onDoubleClick(model)
 
-            onItemDoubleClicked: g_mainDisplay.play(MediaLib, model.id)
+            onContextMenuButtonClicked: root.contextMenu.popup(modelSelect.selectedIndexes,
+                                                               menuParent.mapToGlobal(0,0))
 
-            onContextMenuButtonClicked: contextMenu.popup(modelSelect.selectedIndexes,
-                                                          globalMousePos)
+            onRightClick: root.contextMenu.popup(modelSelect.selectedIndexes, globalMousePos)
 
-            onRightClick: contextMenu.popup(modelSelect.selectedIndexes, globalMousePos)
+            // Functions
+
+            function onLabels(model) { return root.onLabelList(model); }
         }
     }
 
@@ -341,12 +364,12 @@ FocusScope {
 
         visible: (model.count === 0)
 
+        focus: visible
+
         text: I18n.qtr("No video found\nPlease try adding sources, by going to the Network tab")
 
         cover: VLCStyle.noArtVideoCover
 
         Navigation.parentItem: root
-
-        focus: visible
     }
 }


=====================================
modules/gui/qt/medialibrary/qml/VideoAllDisplay.qml
=====================================
@@ -25,102 +25,83 @@ import org.videolan.vlc 0.1
 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 {
+Widgets.PageLoader {
     id: root
 
-    // Events
+    // Properties
 
-    onCurrentIndexChanged: {
-        History.update([ "mc", "video", { "initialIndex": currentIndex }])
-    }
+    property var model
+    property var sortMenu
+    property var sortModel
 
-    // Functions
+    // Settings
 
-    function setCurrentItemFocus(reason) {
-        if (modelRecent.count)
-            headerItem.setCurrentItemFocus(reason);
-        else
-            _currentView.setCurrentItemFocus(reason);
-    }
+    defaultPage: "base"
 
-    // Children
+    pageModel: [{
+        name: "base",
+        component: componentBase
+    }, {
+        name: "group",
+        component: componentGroup
+    }]
 
-    MLRecentsVideoModel {
-        id: modelRecent
+    // Events
 
-        ml: MediaLib
+    onCurrentItemChanged: {
+        model     = currentItem.model
+        sortMenu  = currentItem.sortMenu
+        sortModel = currentItem.sortModel
     }
+    // Functions private
 
-    header: Column {
-        property Item focusItem: (loader.status === Loader.Ready) ? loader.item.focusItem : null
-
-        property alias loader: loader
-
-        width: root.width
-
-        topPadding: VLCStyle.margin_normal
-        bottomPadding: VLCStyle.margin_normal
-
-        // NOTE: We want the header to be visible when we have at least one media visible.
-        //       Otherwise it overlaps the default caption.
-        visible: (model.count)
-
-        // NOTE: Making sure this item will be focused by VideoAll::_onNavigationUp().
-        focus: true
-
-        function setCurrentItemFocus(reason) {
-            var item = loader.item;
-
-            if (item)
-                item.setCurrentItemFocus(reason);
-        }
-
-        Loader {
-            id: loader
-
-            anchors.left : parent.left
-            anchors.right: parent.right
-
-            anchors.margins: root.contentMargin
-
-            height: (status === Loader.Ready) ? item.implicitHeight : 0
-
-            active: (modelRecent.count)
+    function _updateHistoryAll(index) {
+        History.update(["mc", "video", "all", "base", { "initialIndex": index }])
+    }
 
-            visible: active
+    function _updateHistoryGroup(group) {
+        History.update(["mc", "video", "all", "group", {
+                            "initialIndex": group.currentIndex,
+                            "initialId"   : group.parentId,
+                            "initialTitle": group.title
+                        }])
+    }
 
-            sourceComponent: VideoDisplayRecentVideos {
-                id: component
+    // Children
 
-                width: parent.width
+    Component {
+        id: componentBase
 
-                // NOTE: We want grid items to be visible on the sides.
-                displayMargins: root.contentMargin
+        VideoAllSubDisplay {
+            // Events
 
-                model: modelRecent
+            onShowList: {
+                History.push(["mc", "video", "all", "group",
+                             { parentId: model.id, title: model.title }])
 
-                focus: true
+                root.stackView.currentItem.setCurrentItemFocus(reason)
+            }
 
-                Navigation.parentItem: root
+            // NOTE: The model can change over time.
+            onModelChanged: root.model = model
 
-                Navigation.downAction: function() {
-                    _currentView.setCurrentItemFocus(Qt.TabFocusReason);
-                }
-            }
+            onCurrentIndexChanged: root._updateHistoryAll(currentIndex)
         }
+    }
+
+    Component {
+        id: componentGroup
 
-        Widgets.SubtitleLabel {
-            anchors.left: loader.left
-            anchors.right: loader.right
+        MediaGroupDisplay {
+            id: group
 
-            // NOTE: We want this to be properly aligned with the grid items.
-            anchors.leftMargin: VLCStyle.margin_normal
+            anchors.fill: parent
 
-            text: I18n.qtr("Videos")
+            onCurrentIndexChanged: root._updateHistoryGroup(group)
+            onParentIdChanged    : root._updateHistoryGroup(group)
+            onTitleChanged       : root._updateHistoryGroup(group)
         }
     }
 }


=====================================
modules/gui/qt/medialibrary/qml/VideoAllSubDisplay.qml
=====================================
@@ -0,0 +1,230 @@
+/*****************************************************************************
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * Authors: Benjamin Arnaud <bunjee at omega.gg>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+import QtQuick 2.11
+
+import org.videolan.vlc 0.1
+import org.videolan.medialib 0.1
+
+import "qrc:///widgets/" as Widgets
+import "qrc:///style/"
+
+VideoAll {
+    id: root
+
+    // Properties
+
+    // NOTE: We are exposing a custom SortMenu with grouping options.
+    property SortMenuVideo sortMenu: SortMenuVideo {
+        ctx: MainCtx
+
+        onGrouping: MainCtx.grouping = grouping
+    }
+
+    // Private
+
+    property var _meta: (MainCtx.grouping === MainCtx.GROUPING_NONE) ? metaVideo
+                                                                     : metaGroup
+
+    // Signals
+
+    signal showList(var model, int reason)
+
+    // Settings
+
+    anchors.fill: parent
+
+    model: _meta.model
+
+    contextMenu: _meta.contextMenu
+
+    // Functions
+
+    function getLabelGroup(model, string) {
+        if (!model) return ""
+
+        var count = model.count
+
+        if (count === 1) {
+            return getLabel(model)
+        } else {
+            if (count < 100)
+                return [ string.arg(count) ]
+            else
+                return [ string.arg("99+") ]
+        }
+    }
+
+    // VideoAll reimplementation
+
+    function setCurrentItemFocus(reason) {
+        if (modelRecent.count)
+            headerItem.setCurrentItemFocus(reason)
+        else
+            _currentView.setCurrentItemFocus(reason)
+    }
+
+    // VideoAll events reimplementation
+
+    function onAction(indexes) { _meta.onAction(indexes) }
+
+    function onDoubleClick(object) { _meta.onDoubleClick(object) }
+
+    function onLabelGrid(object) { return _meta.onLabelGrid(object) }
+    function onLabelList(object) { return _meta.onLabelList(object) }
+
+    // Children
+
+    QtObject {
+        id: metaVideo
+
+        property var model: MLVideoModel { ml: MediaLib }
+
+        property var contextMenu: VideoContextMenu { model: metaVideo.model }
+
+        function onAction(indexes) {
+            g_mainDisplay.showPlayer()
+
+            MediaLib.addAndPlay(model.getIdsForIndexes(indexes))
+        }
+
+        function onDoubleClick(object) { g_mainDisplay.play(MediaLib, object.id) }
+
+        function onLabelGrid(object) { return root.getLabel(object) }
+        function onLabelList(object) { return root.getLabel(object) }
+    }
+
+    QtObject {
+        id: metaGroup
+
+        property var model: MLGroupListModel { ml: MediaLib }
+
+        property var contextMenu: GroupListContextMenu { model: metaGroup.model }
+
+        function onAction(indexes) {
+            var index = indexes[0]
+
+            var object = model.getDataAt(index);
+
+            if (object.isVideo) {
+                g_mainDisplay.showPlayer()
+
+                MediaLib.addAndPlay(model.getIdsForIndexes(indexes))
+
+                return
+            }
+
+            root.showList(object, Qt.TabFocusReason)
+        }
+
+        function onDoubleClick(object) {
+            if (object.isVideo) {
+                g_mainDisplay.play(MediaLib, object.id)
+
+                return
+            }
+
+            root.showList(object, Qt.MouseFocusReason)
+        }
+
+        function onLabelGrid(object) {
+            return root.getLabelGroup(object, I18n.qtr("%1 Videos"))
+        }
+
+        function onLabelList(object) {
+            return root.getLabelGroup(object, I18n.qtr("%1"))
+        }
+    }
+
+    // Children
+
+    MLRecentsVideoModel {
+        id: modelRecent
+
+        ml: MediaLib
+    }
+
+    header: Column {
+        property Item focusItem: (loader.status === Loader.Ready) ? loader.item.focusItem
+                                                                  : null
+
+        property alias loader: loader
+
+        width: root.width
+
+        topPadding: VLCStyle.margin_normal
+        bottomPadding: VLCStyle.margin_normal
+
+        // NOTE: We want the header to be visible when we have at least one media visible.
+        //       Otherwise it overlaps the default caption.
+        visible: (root.model.count)
+
+        function setCurrentItemFocus(reason) {
+            var item = loader.item;
+
+            if (item)
+                item.setCurrentItemFocus(reason);
+        }
+
+        Loader {
+            id: loader
+
+            anchors.left : parent.left
+            anchors.right: parent.right
+
+            anchors.margins: root.contentMargin
+
+            height: (status === Loader.Ready) ? item.implicitHeight : 0
+
+            active: (modelRecent.count)
+
+            visible: active
+
+            sourceComponent: VideoDisplayRecentVideos {
+                id: component
+
+                width: parent.width
+
+                // NOTE: We want grid items to be visible on the sides.
+                displayMargins: root.contentMargin
+
+                model: modelRecent
+
+                focus: true
+
+                Navigation.parentItem: root
+
+                Navigation.downAction: function() {
+                    _currentView.setCurrentItemFocus(Qt.TabFocusReason);
+                }
+            }
+        }
+
+        Widgets.SubtitleLabel {
+            anchors.left: loader.left
+            anchors.right: loader.right
+
+            // NOTE: We want this to be properly aligned with the grid items.
+            anchors.leftMargin: VLCStyle.margin_normal
+
+            text: I18n.qtr("Videos")
+        }
+    }
+}


=====================================
modules/gui/qt/medialibrary/qml/VideoDisplay.qml
=====================================
@@ -37,6 +37,8 @@ Widgets.PageLoader {
     property bool isViewMultiView: true
 
     property var contentModel
+
+    property var sortMenu
     property var sortModel
 
     property ListModel tabModel: ListModel {
@@ -68,10 +70,6 @@ Widgets.PageLoader {
             name: "all",
             displayText: I18n.qtr("All"),
             url: "qrc:///medialibrary/VideoAllDisplay.qml"
-        },{
-            name: "groups",
-            displayText: I18n.qtr("Groups"),
-            url: "qrc:///medialibrary/VideoGroupsDisplay.qml"
         },{
             name: "playlists",
             displayText: I18n.qtr("Playlists"),
@@ -84,8 +82,10 @@ Widgets.PageLoader {
                            ||
                            currentItem.isViewMultiView);
 
-        contentModel = currentItem.model;
-        sortModel    = currentItem.sortModel;
+        // NOTE: We need bindings because the VideoAll model can change over time.
+        contentModel = Qt.binding(function () { return currentItem.model; })
+        sortMenu     = Qt.binding(function () { return currentItem.sortMenu; })
+        sortModel    = Qt.binding(function () { return currentItem.sortModel; })
     }
 
     //---------------------------------------------------------------------------------------------


=====================================
modules/gui/qt/medialibrary/qml/VideoGroupsDisplay.qml deleted
=====================================
@@ -1,122 +0,0 @@
-/*****************************************************************************
- * Copyright (C) 2021 VLC authors and VideoLAN
- *
- * Authors: Benjamin Arnaud <bunjee at omega.gg>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * ( at your option ) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-
-import QtQuick          2.11
-import QtQuick.Controls 2.4
-import QtQuick.Layouts  1.11
-import QtQml.Models     2.2
-
-import org.videolan.vlc 0.1
-import org.videolan.medialib 0.1
-
-import "qrc:///widgets/" as Widgets
-import "qrc:///style/"
-
-Widgets.PageLoader {
-    id: root
-
-    //---------------------------------------------------------------------------------------------
-    // Aliases
-    //---------------------------------------------------------------------------------------------
-
-    property bool isViewMultiView: true
-
-    property var model
-    property var sortModel
-
-    //---------------------------------------------------------------------------------------------
-    // Settings
-    //---------------------------------------------------------------------------------------------
-
-    defaultPage: "all"
-
-    pageModel: [{
-        name: "all",
-        component: componentAll
-    }, {
-        name: "list",
-        component: componentList
-    }]
-
-    //---------------------------------------------------------------------------------------------
-    // Events
-    //---------------------------------------------------------------------------------------------
-
-    onCurrentItemChanged: {
-        model     = currentItem.model;
-        sortModel = currentItem.sortModel;
-
-        isViewMultiView = (currentItem.isViewMultiView === undefined
-                           ||
-                           currentItem.isViewMultiView);
-    }
-
-    //---------------------------------------------------------------------------------------------
-    // Functions
-    //---------------------------------------------------------------------------------------------
-    // Private
-
-    function _updateHistoryAll(index) {
-        History.update(["mc", "video", "groups", "all", { "initialIndex": index }]);
-    }
-
-    function _updateHistoryList(list) {
-        History.update(["mc", "video", "groups", "list", {
-                            "initialIndex": list.currentIndex,
-                            "initialId"   : list.parentId,
-                            "initialName" : list.name
-                        }]);
-    }
-
-    //---------------------------------------------------------------------------------------------
-    // Childs
-    //---------------------------------------------------------------------------------------------
-
-    Component {
-        id: componentAll
-
-        MediaGroupList {
-            anchors.fill: parent
-
-            onCurrentIndexChanged: _updateHistoryAll(currentIndex)
-
-            onShowList: {
-                History.push(["mc", "video", "groups", "list",
-                             { parentId: model.id, name: model.name }]);
-
-                stackView.currentItem.setCurrentItemFocus(reason);
-            }
-        }
-    }
-
-    Component {
-        id: componentList
-
-        MediaGroupDisplay {
-            id: list
-
-            anchors.fill: parent
-
-            onCurrentIndexChanged: _updateHistoryList(list)
-            onParentIdChanged    : _updateHistoryList(list)
-            onNameChanged        : _updateHistoryList(list)
-        }
-    }
-}


=====================================
modules/gui/qt/menus/qml_menu_wrapper.cpp
=====================================
@@ -32,8 +32,6 @@
 #include "playlist/playlist_controller.hpp"
 #include "playlist/playlist_model.hpp"
 #include "dialogs/dialogs_provider.hpp"
-#include "maininterface/mainctx.hpp"
-
 
 #include <QSignalMapper>
 
@@ -84,12 +82,16 @@ void StringListMenu::popup(const QPoint &point, const QVariantList &stringList)
     m->popup(point);
 }
 
+// SortMenu
+
 SortMenu::~SortMenu()
 {
     if (m_menu)
         delete m_menu;
 }
 
+// Functions
+
 void SortMenu::popup(const QPoint &point, const bool popupAbovePoint, const QVariantList &model)
 {
     if (m_menu)
@@ -117,6 +119,8 @@ void SortMenu::popup(const QPoint &point, const bool popupAbovePoint, const QVar
         });
     }
 
+    onPopup(m_menu);
+
     // m_menu->height() returns invalid height until initial popup call
     // so in case of 'popupAbovePoint', first show the menu and then reposition it
     m_menu->popup(point);
@@ -133,6 +137,57 @@ void SortMenu::close()
         m_menu->close();
 }
 
+// Protected functions
+
+/* virtual */ void SortMenu::onPopup(QMenu *) {}
+
+// SortMenuVideo
+
+// Protected SortMenu reimplementation
+
+void SortMenuVideo::onPopup(QMenu * menu) /* override */
+{
+    if (!m_ctx)
+        return;
+
+    menu->addSeparator();
+
+    struct
+    {
+        const char * title;
+
+        MainCtx::Grouping grouping;
+    }
+    entries[] =
+    {
+        { N_("Do not group videos"), MainCtx::GROUPING_NONE },
+        { N_("Group by name"), MainCtx::GROUPING_NAME },
+    };
+
+    QActionGroup * group = new QActionGroup(this);
+
+    int index = m_ctx->grouping();
+
+    for (size_t i = 0; i < ARRAY_SIZE(entries); i++)
+    {
+        QAction * action = menu->addAction(qtr(entries[i].title));
+
+        action->setCheckable(true);
+
+        MainCtx::Grouping grouping = entries[i].grouping;
+
+        connect(action, &QAction::triggered, this, [this, grouping]()
+        {
+            emit this->grouping(grouping);
+        });
+
+        group->addAction(action);
+
+        if (index == grouping)
+            action->setChecked(true);
+    }
+}
+
 QmlGlobalMenu::QmlGlobalMenu(QObject *parent)
     : VLCMenuBar(parent)
 {
@@ -498,7 +553,7 @@ GroupListContextMenu::~GroupListContextMenu() /* override */
         delete m_menu;
 }
 
-void GroupListContextMenu::popup(const QModelIndexList & selected, QPoint pos, QVariantMap)
+void GroupListContextMenu::popup(const QModelIndexList & selected, QPoint pos, QVariantMap options)
 {
     if (m_model == nullptr)
         return;
@@ -508,8 +563,8 @@ void GroupListContextMenu::popup(const QModelIndexList & selected, QPoint pos, Q
 
     QVariantList ids;
 
-    for (const QModelIndex & modelIndex : selected)
-        ids.push_back(m_model->data(modelIndex, MLGroupListModel::GROUP_ID));
+    for (const QModelIndex & index : selected)
+        ids.push_back(m_model->data(index, MLGroupListModel::GROUP_ID));
 
     m_menu = new QMenu();
 
@@ -517,16 +572,60 @@ void GroupListContextMenu::popup(const QModelIndexList & selected, QPoint pos, Q
 
     QAction * action = m_menu->addAction(qtr("Add and play"));
 
-    connect(action, &QAction::triggered, [ml, ids]() {
-        ml->addAndPlay(ids);
+    connect(action, &QAction::triggered, [ml, ids, options]()
+    {
+        ml->addAndPlay(ids, options["player-options"].toStringList());
     });
 
     action = m_menu->addAction(qtr("Enqueue"));
 
-    connect(action, &QAction::triggered, [ml, ids]() {
+    connect(action, &QAction::triggered, [ml, ids]()
+    {
         ml->addToPlaylist(ids);
     });
 
+    action = m_menu->addAction(qtr("Add to playlist"));
+
+    connect(action, &QAction::triggered, [ids]()
+    {
+        DialogsProvider::getInstance()->playlistsDialog(ids);
+    });
+
+    action = m_menu->addAction(qtr("Play as audio"));
+
+    connect(action, &QAction::triggered, [ml, ids, options]()
+    {
+        QStringList list = options["player-options"].toStringList();
+
+        list.prepend(":no-video");
+
+        ml->addAndPlay(ids, list);
+    });
+
+    // NOTE: At the moment informations are only available for single video(s).
+    if (selected.count() == 1
+        &&
+        m_model->data(selected.first(), MLGroupListModel::GROUP_IS_VIDEO) == true
+        &&
+        options.contains("information") && options["information"].type() == QVariant::Int)
+    {
+        action = m_menu->addAction(qtr("Information"));
+
+        QSignalMapper * mapper = new QSignalMapper(m_menu);
+
+        mapper->setMapping(action, options["information"].toInt());
+
+        connect(action, &QAction::triggered, mapper, QOverload<>::of(&QSignalMapper::map));
+
+#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
+        connect(mapper, &QSignalMapper::mappedInt,
+                this, &GroupListContextMenu::showMediaInformation);
+#else
+        connect(mapper, QOverload<int>::of(&QSignalMapper::mapped),
+                this, &GroupListContextMenu::showMediaInformation);
+#endif
+    }
+
     m_menu->popup(pos);
 }
 


=====================================
modules/gui/qt/menus/qml_menu_wrapper.hpp
=====================================
@@ -24,6 +24,7 @@
 #include <QPoint>
 #include <QQuickItem>
 #include "menus.hpp"
+#include "maininterface/mainctx.hpp"
 
 class MediaLib;
 class MLAlbumModel;
@@ -82,6 +83,9 @@ public:
 
     Q_INVOKABLE void close();
 
+protected:
+    virtual void onPopup(QMenu * menu);
+
 signals:
     void selected(int index);
 
@@ -89,6 +93,18 @@ private:
     QMenu *m_menu = nullptr;
 };
 
+class SortMenuVideo : public SortMenu
+{
+    Q_OBJECT
+
+    SIMPLE_MENU_PROPERTY(MainCtx *, ctx, nullptr)
+
+protected: // SortMenu reimplementation
+    void onPopup(QMenu * menu) override;
+
+signals:
+    void grouping(MainCtx::Grouping grouping);
+};
 
 //inherit VLCMenuBar so we can access menu creation functions
 class QmlGlobalMenu : public VLCMenuBar
@@ -264,15 +280,22 @@ private:
 
 class GroupListContextMenu : public QObject {
     Q_OBJECT
+
     SIMPLE_MENU_PROPERTY(MLGroupListModel *, model, nullptr)
+
 public:
     GroupListContextMenu(QObject * parent = nullptr);
+
     ~GroupListContextMenu() /* override */;
 
 public slots:
     void popup(const QModelIndexList & selected, QPoint pos, QVariantMap options = {});
+
+signals:
+    void showMediaInformation(int index);
+
 private:
-    QMenu* m_menu = nullptr;
+    QMenu * m_menu = nullptr;
 };
 
 //-------------------------------------------------------------------------------------------------


=====================================
modules/gui/qt/vlc.qrc
=====================================
@@ -282,14 +282,13 @@
     <qresource prefix="/medialibrary">
         <file alias="EmptyLabel.qml">medialibrary/qml/EmptyLabel.qml</file>
         <file alias="MediaGroupDisplay.qml">medialibrary/qml/MediaGroupDisplay.qml</file>
-        <file alias="MediaGroupList.qml">medialibrary/qml/MediaGroupList.qml</file>
         <file alias="MusicAlbums.qml">medialibrary/qml/MusicAlbums.qml</file>
         <file alias="MusicDisplay.qml">medialibrary/qml/MusicDisplay.qml</file>
         <file alias="MusicGenres.qml">medialibrary/qml/MusicGenres.qml</file>
         <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="VideoGroupsDisplay.qml">medialibrary/qml/VideoGroupsDisplay.qml</file>
+        <file alias="VideoAllSubDisplay.qml">medialibrary/qml/VideoAllSubDisplay.qml</file>
         <file alias="PlaylistMediaList.qml">medialibrary/qml/PlaylistMediaList.qml</file>
         <file alias="PlaylistMedia.qml">medialibrary/qml/PlaylistMedia.qml</file>
         <file alias="PlaylistMediaDelegate.qml">medialibrary/qml/PlaylistMediaDelegate.qml</file>


=====================================
modules/gui/qt/widgets/qml/SortControl.qml
=====================================
@@ -15,9 +15,8 @@
  * 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.11
 
 import org.videolan.vlc 0.1
 
@@ -27,50 +26,105 @@ import "qrc:///widgets/" as Widgets
 FocusScope {
     id: root
 
-    // when height/width is explicitly set (force size), implicit values will not be used.
-    // when height/width is not explicitly set, IconToolButton will set its ...
-    // height and width to these implicit counterparts because ...
-    // height and width will be set to implicit values when they are not ...
-    // explicitly set.
-    implicitWidth: button.implicitWidth
-    implicitHeight: button.implicitHeight
+    // Properties
 
     property var model: []
+
     property string textRole
     property string criteriaRole
-    // provided for convenience:
-    property alias titleRole: root.textRole
-    property alias keyRole: root.criteriaRole
 
     property bool popupAbove: false
 
     property real listWidth: VLCStyle.widthSortBox
-    property alias focusPolicy: button.focusPolicy
-    property alias iconSize: button.size
 
     property VLCColors colors: VLCStyle.colors
 
+    // NOTE: This allows us provide a custom menu and override sortMenu.
+    property SortMenu menu
+
     // properties that should be handled by parent
     // if they are not updated, tick mark and order mark will not be shown
     property var sortKey: undefined
     property var sortOrder: undefined
 
+    // Private
+
+    property SortMenu _menu: (menu) ? menu
+                                    : sortMenu
+
+    // Aliases
+
+    property alias focusPolicy: button.focusPolicy
+    property alias iconSize: button.size
+
+    // Signals
+
     // sortSelected is triggered with new sorting key when a different sorting key is selected
     // sortOrderSelected is triggered with Qt.AscendingOrder when different sorting key is selected
     // sortOrderSelected is triggered with Qt.AscendingOrder or Qt.DescendingOrder when the same sorting key is selected
     signal sortSelected(int type)
     signal sortOrderSelected(int type)
 
-    onVisibleChanged: {
-        if (!visible)
-            popup.close()
+    // Settings
+
+    // when height/width is explicitly set (force size), implicit values will not be used.
+    // when height/width is not explicitly set, IconToolButton will set its ...
+    // height and width to these implicit counterparts because ...
+    // height and width will be set to implicit values when they are not ...
+    // explicitly set.
+    implicitWidth: button.implicitWidth
+    implicitHeight: button.implicitHeight
+
+    // Events
+
+    onVisibleChanged: if (!visible) _menu.close()
+    onEnabledChanged: if (!enabled) _menu.close()
+
+    // Connections
+
+    Connections {
+        target: (_menu) ? _menu : null
+
+        onSelected: {
+            var selectedSortKey = root.model[index][root.criteriaRole]
+
+            if (root.sortKey !== selectedSortKey) {
+                root.sortSelected(selectedSortKey)
+                root.sortOrderSelected(Qt.AscendingOrder)
+            } else {
+                root.sortOrderSelected(root.sortOrder === Qt.AscendingOrder ? Qt.DescendingOrder
+                                                                            : Qt.AscendingOrder)
+            }
+        }
     }
 
-    onEnabledChanged: {
-        if (!enabled)
-            popup.close()
+    // Functions
+
+    function show() {
+        var model = root.model.map(function(modelData) {
+            var checked = modelData[root.criteriaRole] === sortKey
+            var order = checked ? root.sortOrder : undefined
+            return {
+                "text": modelData[root.textRole],
+                "checked": checked,
+                "order": order
+            }
+        })
+
+        var point
+
+        if (root.popupAbove)
+            point = root.mapToGlobal(0, - VLCStyle.margin_xxsmall)
+        else
+            point = root.mapToGlobal(0, root.height + VLCStyle.margin_xxsmall)
+
+        root._menu.popup(point, root.popupAbove, model)
     }
 
+    // Children
+
+    SortMenu { id: sortMenu }
+
     Widgets.IconToolButton {
         id: button
 
@@ -80,50 +134,18 @@ FocusScope {
         width: root.width
 
         size: VLCStyle.icon_normal
-        iconText: VLCIcons.topbar_sort
-        text: I18n.qtr("Sort")
 
         focus: true
 
-        onClicked: popup.show()
+        text: I18n.qtr("Sort")
+
+        iconText: VLCIcons.topbar_sort
 
         Navigation.parentItem: root
+
         Keys.priority: Keys.AfterItem
         Keys.onPressed: Navigation.defaultKeyAction(event)
-    }
-
-    SortMenu {
-       id: popup
-
-        function show() {
-            var model = root.model.map(function(modelData) {
-                var checked = modelData[root.criteriaRole] === sortKey
-                var order = checked ? root.sortOrder : undefined
-                return {
-                    "text": modelData[root.textRole],
-                    "checked": checked,
-                    "order": order
-                }
-            })
-
-            var point
-
-            if (root.popupAbove)
-                point = root.mapToGlobal(0, - VLCStyle.margin_xxsmall)
-            else
-                point = root.mapToGlobal(0, root.height + VLCStyle.margin_xxsmall)
-
-            popup.popup(point, root.popupAbove, model)
-        }
 
-        onSelected: {
-            var selectedSortKey = root.model[index][root.criteriaRole]
-            if (root.sortKey !== selectedSortKey) {
-                root.sortSelected(selectedSortKey)
-                root.sortOrderSelected(Qt.AscendingOrder)
-            } else {
-                root.sortOrderSelected(root.sortOrder === Qt.AscendingOrder ? Qt.DescendingOrder : Qt.AscendingOrder)
-            }
-        }
+        onClicked: root.show()
     }
 }


=====================================
po/POTFILES.in
=====================================
@@ -824,7 +824,6 @@ modules/gui/qt/maininterface/qml/NoMedialibHome.qml
 modules/gui/qt/medialibrary/qml/ArtistTopBanner.qml
 modules/gui/qt/medialibrary/qml/AudioGridItem.qml
 modules/gui/qt/medialibrary/qml/MediaGroupDisplay.qml
-modules/gui/qt/medialibrary/qml/MediaGroupList.qml
 modules/gui/qt/medialibrary/qml/MusicAlbums.qml
 modules/gui/qt/medialibrary/qml/MusicAlbumsDisplay.qml
 modules/gui/qt/medialibrary/qml/MusicAlbumsGridExpandDelegate.qml
@@ -847,7 +846,7 @@ 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/medialibrary/qml/VideoGroupsDisplay.qml
+modules/gui/qt/medialibrary/qml/VideoAllSubDisplay.qml
 modules/gui/qt/medialibrary/qml/PlaylistMediaList.qml
 modules/gui/qt/medialibrary/qml/PlaylistMedia.qml
 modules/gui/qt/medialibrary/qml/PlaylistMediaDelegate.qml



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/e663ebb034288f48235d3d06b5466f4197183c81...454b57b76a64516b0f9175909dd9571cd294baf7

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




More information about the vlc-commits mailing list