[vlc-commits] [Git][videolan/vlc][master] 8 commits: qt: make ServicesDiscoveryModel use BaseModel
Steve Lhomme (@robUx4)
gitlab at videolan.org
Fri Sep 15 13:32:40 UTC 2023
Steve Lhomme pushed to branch master at VideoLAN / VLC
Commits:
db55b347 by Pierre Lamot at 2023-09-15T13:04:19+00:00
qt: make ServicesDiscoveryModel use BaseModel
- - - - -
444abdd3 by Pierre Lamot at 2023-09-15T13:04:19+00:00
qml: fix wrong callback called to uninstall service
- - - - -
8ff15cee by Pierre Lamot at 2023-09-15T13:04:19+00:00
qml: remove unecessary SortFilterProxyModel in ServicesManage
- - - - -
7f4cf7b0 by Pierre Lamot at 2023-09-15T13:04:19+00:00
qt: make NetworkSourcesModel use BaseModel
- - - - -
54a31e54 by Pierre Lamot at 2023-09-15T13:04:19+00:00
qml: remove unecessary SortFilterProxyModel in ServicesSources
- - - - -
24d808fc by Pierre Lamot at 2023-09-15T13:04:19+00:00
qt: remove obsolete SortfilterProxyModel
- - - - -
97aeaa45 by Pierre Lamot at 2023-09-15T13:04:19+00:00
qt: fix class/struct mismatch
- - - - -
dd683943 by Pierre Lamot at 2023-09-15T13:04:19+00:00
qt: fix loading state in NetworkMediaModel
- - - - -
13 changed files:
- modules/gui/qt/Makefile.am
- modules/gui/qt/maininterface/mainui.cpp
- modules/gui/qt/medialibrary/mlbasemodel.hpp
- modules/gui/qt/meson.build
- modules/gui/qt/network/networkmediamodel.cpp
- modules/gui/qt/network/networksourcesmodel.cpp
- modules/gui/qt/network/networksourcesmodel.hpp
- modules/gui/qt/network/qml/ServicesManage.qml
- modules/gui/qt/network/qml/ServicesSources.qml
- modules/gui/qt/network/servicesdiscoverymodel.cpp
- modules/gui/qt/network/servicesdiscoverymodel.hpp
- − modules/gui/qt/util/sortfilterproxymodel.cpp
- − modules/gui/qt/util/sortfilterproxymodel.hpp
Changes:
=====================================
modules/gui/qt/Makefile.am
=====================================
@@ -314,8 +314,6 @@ libqt_plugin_la_SOURCES = \
gui/qt/util/selectable_list_model.cpp \
gui/qt/util/selectable_list_model.hpp \
gui/qt/util/singleton.hpp \
- gui/qt/util/sortfilterproxymodel.cpp \
- gui/qt/util/sortfilterproxymodel.hpp \
gui/qt/util/soutchain.cpp gui/qt/util/soutchain.hpp \
gui/qt/util/validators.cpp gui/qt/util/validators.hpp \
gui/qt/util/varcommon_p.hpp \
@@ -509,7 +507,6 @@ nodist_libqt_plugin_la_SOURCES = \
gui/qt/util/flickable_scroll_handler.moc.cpp \
gui/qt/util/renderer_manager.moc.cpp \
gui/qt/util/selectable_list_model.moc.cpp \
- gui/qt/util/sortfilterproxymodel.moc.cpp \
gui/qt/util/validators.moc.cpp \
gui/qt/util/varchoicemodel.moc.cpp \
gui/qt/util/variables.moc.cpp \
=====================================
modules/gui/qt/maininterface/mainui.cpp
=====================================
@@ -38,7 +38,6 @@
#include "util/i18n.hpp"
#include "util/keyhelper.hpp"
#include "style/systempalette.hpp"
-#include "util/sortfilterproxymodel.hpp"
#include "util/navigation_history.hpp"
#include "util/flickable_scroll_handler.hpp"
#include "util/color_svg_image_provider.hpp"
@@ -306,7 +305,6 @@ void MainUI::registerQMLTypes()
qmlRegisterType<NetworkMediaContextMenu>( uri, versionMajor, versionMinor, "NetworkMediaContextMenu" );
qmlRegisterType<NetworkDeviceContextMenu>( uri, versionMajor, versionMinor, "NetworkDeviceContextMenu" );
qmlRegisterType<PlaylistContextMenu>( uri, versionMajor, versionMinor, "PlaylistContextMenu" );
- qmlRegisterType<SortFilterProxyModel>( uri, versionMajor, versionMinor, "SortFilterProxyModel" );
qmlRegisterUncreatableType<NavigationAttached>( uri, versionMajor, versionMinor, "Navigation", "Navigation is only available via attached properties");
=====================================
modules/gui/qt/medialibrary/mlbasemodel.hpp
=====================================
@@ -42,7 +42,7 @@ class MediaLib;
template<typename T>
class ListCache;
using MLListCache = ListCache<std::unique_ptr<MLItem>>;
-struct MLListCacheLoader;
+class MLListCacheLoader;
/* Medialib data loader for the cache */
class MLBaseModelPrivate;
@@ -149,7 +149,7 @@ private:
};
-struct MLListCacheLoader: public QObject, public ListCacheLoader<std::unique_ptr<MLItem>>
+class MLListCacheLoader: public QObject, public ListCacheLoader<std::unique_ptr<MLItem>>
{
Q_OBJECT
public:
=====================================
modules/gui/qt/meson.build
=====================================
@@ -131,7 +131,6 @@ moc_headers = files(
'util/flickable_scroll_handler.hpp',
'util/renderer_manager.hpp',
'util/selectable_list_model.hpp',
- 'util/sortfilterproxymodel.hpp',
'util/validators.hpp',
'util/varchoicemodel.hpp',
'util/variables.hpp',
@@ -438,8 +437,6 @@ some_sources = files(
'util/selectable_list_model.cpp',
'util/selectable_list_model.hpp',
'util/singleton.hpp',
- 'util/sortfilterproxymodel.cpp',
- 'util/sortfilterproxymodel.hpp',
'util/soutchain.cpp',
'util/soutchain.hpp',
'util/validators.cpp',
=====================================
modules/gui/qt/network/networkmediamodel.cpp
=====================================
@@ -224,7 +224,7 @@ public:
public:
bool loading() const override
{
- return m_parsing && BaseModelPrivateT<NetworkMediaItemPtr>::loading();
+ return m_parsing || BaseModelPrivateT<NetworkMediaItemPtr>::loading();
}
LocalListCacheLoader<NetworkMediaItemPtr>::ItemCompare getSortFunction() const
=====================================
modules/gui/qt/network/networksourcesmodel.cpp
=====================================
@@ -16,136 +16,229 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
-#include <QQmlFile>
#include "networksourcesmodel.hpp"
#include "networkmediamodel.hpp"
+#include "util/base_model_p.hpp"
+#include "util/locallistcacheloader.hpp"
+
#include "playlist/media.hpp"
#include "playlist/playlist_controller.hpp"
-NetworkSourcesModel::NetworkSourcesModel( QObject* parent )
- : QAbstractListModel( parent )
-{
-}
+#include <QQmlFile>
-QVariant NetworkSourcesModel::data( const QModelIndex& index, int role ) const
+#include <memory>
+
+#include <vlc_media_source.h>
+#include <vlc_cxx_helpers.hpp>
+
+namespace {
+
+struct SourceItem
{
- if (!m_ctx)
- return {};
- auto idx = index.row();
- if ( idx < 0 || (size_t)idx >= m_items.size() )
- return {};
- const auto& item = m_items[idx];
- switch ( role )
+ SourceItem()
{
- case SOURCE_NAME:
- return item.name;
- case SOURCE_LONGNAME:
- return item.longName;
- case SOURCE_TYPE:
- return idx == 0 ? TYPE_DUMMY : TYPE_SOURCE;
- case SOURCE_ARTWORK:
- return item.artworkUrl;
- default:
- return {};
+ isDummy = true;
}
-}
-QHash<int, QByteArray> NetworkSourcesModel::roleNames() const
-{
- return {
- { SOURCE_NAME, "name" },
- { SOURCE_LONGNAME, "long_name" },
- { SOURCE_TYPE, "type" },
- { SOURCE_ARTWORK, "artwork" }
- };
-}
+ explicit SourceItem(const vlc_media_source_meta* meta)
+ : isDummy(false)
+ , name(qfu(meta->name))
+ , longName(qfu(meta->longname))
+ {
+ if ( name.startsWith( "podcast" ) )
+ {
+ artworkUrl = QUrl("qrc:///sd/podcast.svg");
+ }
+ else if ( name.startsWith("lua{") )
+ {
+ int i_head = name.indexOf( "sd='" ) + 4;
+ int i_tail = name.indexOf( '\'', i_head );
+ const QString iconName = QString( "qrc:///sd/%1.svg" ).arg( name.mid( i_head, i_tail - i_head ) );
+ artworkUrl = QFileInfo::exists( QQmlFile::urlToLocalFileOrQrc(iconName) ) ? QUrl(iconName)
+ : QUrl("qrc:///sd/network.svg");
+ }
+ }
-int NetworkSourcesModel::rowCount(const QModelIndex& parent) const
-{
- if ( parent.isValid() )
- return 0;
- return getCount();
-}
+ bool isDummy;
+ QString name;
+ QString longName;
+ QUrl artworkUrl;
+};
+using SourceItemPtr = std::shared_ptr<SourceItem>;
+using SourceItemLists = std::vector<SourceItemPtr>;
-void NetworkSourcesModel::setCtx(MainCtx* ctx)
-{
- if (ctx) {
- m_ctx = ctx;
- }
- if (m_ctx) {
- initializeMediaTree();
- }
- emit ctxChanged();
}
-int NetworkSourcesModel::getCount() const
+class NetworkSourcesModelPrivate
+ : public BaseModelPrivateT<SourceItemPtr>
+ , public LocalListCacheLoader<SourceItemPtr>::ModelSource
{
- assert( m_items.size() < INT32_MAX );
- return static_cast<int>( m_items.size() );
-}
+ Q_DECLARE_PUBLIC(NetworkSourcesModel);
-QMap<QString, QVariant> NetworkSourcesModel::getDataAt(int idx)
-{
- QMap<QString, QVariant> dataDict;
- QHash<int,QByteArray> roles = roleNames();
- for (auto role: roles.keys()) {
- dataDict[roles[role]] = data(index(idx), role);
+public: //Ctor/Dtor
+ NetworkSourcesModelPrivate(NetworkSourcesModel* pub)
+ : BaseModelPrivateT<SourceItemPtr>(pub)
+ {}
+
+public:
+ const SourceItem* getItemForRow(int row) const
+ {
+ const SourceItemPtr* ref = item(row);
+ if (ref)
+ return ref->get();
+ return nullptr;
}
- return dataDict;
-}
-bool NetworkSourcesModel::initializeMediaTree()
-{
- auto libvlc = vlc_object_instance(m_ctx->getIntf());
+public: // BaseModelPrivate implementation
- if (!m_items.empty()) {
- beginResetModel();
- endResetModel();
- emit countChanged();
+ LocalListCacheLoader<SourceItemPtr>::ItemCompare getSortFunction() const
+ {
+ if (m_sortOrder == Qt::DescendingOrder)
+ return [](const SourceItemPtr& a, const SourceItemPtr& b) {
+ //always put our dummy item first
+ if (a->isDummy)
+ return true;
+ if (b->isDummy)
+ return false;
+ return QString::compare(a->name, b->name, Qt::CaseInsensitive) > 0;
+ };
+ else
+ return [](const SourceItemPtr& a, const SourceItemPtr& b) {
+ //always put our dummy item first
+ if (a->isDummy)
+ return true;
+ if (b->isDummy)
+ return false;
+ return QString::compare(a->name, b->name, Qt::CaseInsensitive) < 0;
+ };
+ }
+
+ std::unique_ptr<ListCacheLoader<SourceItemPtr>> createLoader() const override
+ {
+ return std::make_unique<LocalListCacheLoader<SourceItemPtr>>(
+ this, m_searchPattern, getSortFunction());
}
- m_items = {Item{}}; // dummy item that UI uses to add entry for "add a service"
- auto provider = vlc_media_source_provider_Get( libvlc );
+ bool initializeModel() override
+ {
+ Q_Q(NetworkSourcesModel);
+ if (m_qmlInitializing || !q->m_ctx)
+ return false;
- using SourceMetaPtr = std::unique_ptr<vlc_media_source_meta_list_t,
- decltype( &vlc_media_source_meta_list_Delete )>;
+ auto libvlc = vlc_object_instance(q->m_ctx->getIntf());
- SourceMetaPtr providerList( vlc_media_source_provider_List( provider, static_cast<services_discovery_category_e>(m_sdSource) ),
- &vlc_media_source_meta_list_Delete );
- if ( providerList == nullptr )
- return false;
+ if (!m_items.empty())
+ m_items.clear();
- auto nbProviders = vlc_media_source_meta_list_Count( providerList.get() );
+ auto provider = vlc_media_source_provider_Get( libvlc );
+ //add a dummy item
+ m_items.push_back(std::make_shared<SourceItem>());
- beginResetModel();
- for ( auto i = 0u; i < nbProviders; ++i )
- {
- auto meta = vlc_media_source_meta_list_Get( providerList.get(), i );
+ using SourceMetaPtr = std::unique_ptr<vlc_media_source_meta_list_t,
+ decltype( &vlc_media_source_meta_list_Delete )>;
- Item item;
- item.name = qfu(meta->name);
- item.longName = qfu(meta->longname);
+ SourceMetaPtr providerList( vlc_media_source_provider_List( provider, static_cast<services_discovery_category_e>(m_sdSource) ),
+ &vlc_media_source_meta_list_Delete );
+ if ( providerList == nullptr )
+ return false;
- if ( item.name.startsWith( "podcast" ) )
- {
- item.artworkUrl = QUrl("qrc:///sd/podcast.svg");
- }
- else if ( item.name.startsWith("lua{") )
+ auto nbProviders = vlc_media_source_meta_list_Count( providerList.get() );
+
+ for ( auto i = 0u; i < nbProviders; ++i )
{
- int i_head = item.name.indexOf( "sd='" ) + 4;
- int i_tail = item.name.indexOf( '\'', i_head );
- const QString iconName = QString( "qrc:///sd/%1.svg" ).arg( item.name.mid( i_head, i_tail - i_head ) );
- item.artworkUrl = QFileInfo::exists( QQmlFile::urlToLocalFileOrQrc(iconName) ) ? QUrl(iconName)
- : QUrl("qrc:///sd/network.svg");
+ auto meta = vlc_media_source_meta_list_Get( providerList.get(), i );
+ SourceItemPtr item = std::make_shared<SourceItem>(meta);
+ m_items.push_back( item );
}
- m_items.push_back( std::move(item) );
+ return true;
+ }
+
+public: // LocalListCacheLoader::ModelSource implementation
+ size_t getModelRevision() const override
+ {
+ return 1;
+ }
+
+ std::vector<SourceItemPtr> getModelData(const QString& pattern) const override
+ {
+ if (pattern.isEmpty())
+ return m_items;
+
+ std::vector<SourceItemPtr> items;
+ std::copy_if(
+ m_items.cbegin(), m_items.cend(),
+ std::back_inserter(items),
+ [&pattern](const SourceItemPtr& item){
+ return item->name.contains(pattern, Qt::CaseInsensitive);
+ });
+ return items;
}
- endResetModel();
- emit countChanged();
- return m_items.empty() == false;
+public: // Data
+ services_discovery_category_e m_sdSource = services_discovery_category_e::SD_CAT_INTERNET;
+ std::vector<SourceItemPtr> m_items;
+};
+
+// ListCache specialisation
+
+template<>
+bool ListCache<SourceItemPtr>::compareItems(const SourceItemPtr& a, const SourceItemPtr& b)
+{
+ //just compare the pointers here
+ return a == b;
+}
+
+NetworkSourcesModel::NetworkSourcesModel( QObject* parent )
+ : BaseModel( new NetworkSourcesModelPrivate(this), parent )
+{
+}
+
+QVariant NetworkSourcesModel::data( const QModelIndex& index, int role ) const
+{
+ Q_D(const NetworkSourcesModel);
+ if (!m_ctx)
+ return {};
+
+ const SourceItem* item = d->getItemForRow(index.row());
+ if (!item)
+ return {};
+
+ switch ( role )
+ {
+ case SOURCE_NAME:
+ return item->name;
+ case SOURCE_LONGNAME:
+ return item->longName;
+ case SOURCE_TYPE:
+ return item->isDummy ? TYPE_DUMMY : TYPE_SOURCE;
+ case SOURCE_ARTWORK:
+ return item->artworkUrl;
+ default:
+ return {};
+ }
+}
+
+QHash<int, QByteArray> NetworkSourcesModel::roleNames() const
+{
+ return {
+ { SOURCE_NAME, "name" },
+ { SOURCE_LONGNAME, "long_name" },
+ { SOURCE_TYPE, "type" },
+ { SOURCE_ARTWORK, "artwork" }
+ };
+}
+
+void NetworkSourcesModel::setCtx(MainCtx* ctx)
+{
+ Q_D(NetworkSourcesModel);
+ if (ctx == m_ctx)
+ return;
+
+ m_ctx = ctx;
+ d->initializeModel();
+ emit ctxChanged();
}
=====================================
modules/gui/qt/network/networksourcesmodel.hpp
=====================================
@@ -23,21 +23,16 @@
#include "config.h"
#endif
-#include <QAbstractListModel>
-#include <vlc_media_source.h>
-#include <vlc_cxx_helpers.hpp>
+#include "util/base_model.hpp"
+#include "maininterface/mainctx.hpp"
-#include <maininterface/mainctx.hpp>
-
-#include <memory>
-
-class NetworkSourcesModel : public QAbstractListModel
+class NetworkSourcesModelPrivate;
+class NetworkSourcesModel : public BaseModel
{
Q_OBJECT
Q_PROPERTY(MainCtx* ctx READ getCtx WRITE setCtx NOTIFY ctxChanged FINAL)
- Q_PROPERTY(int count READ getCount NOTIFY countChanged FINAL)
public:
enum Role {
@@ -58,34 +53,18 @@ public:
QVariant data(const QModelIndex& index, int role) const override;
QHash<int, QByteArray> roleNames() const override;
- int rowCount(const QModelIndex& parent = {}) const override;
+public: // properties
void setCtx(MainCtx* ctx);
-
inline MainCtx* getCtx() { return m_ctx; }
- int getCount() const;
-
- Q_INVOKABLE QMap<QString, QVariant> getDataAt(int index);
-
signals:
void ctxChanged();
- void countChanged();
private:
- struct Item
- {
- QString name;
- QString longName;
- QUrl artworkUrl;
- };
-
- bool initializeMediaTree();
-
-private:
- std::vector<Item> m_items;
MainCtx* m_ctx = nullptr;
- services_discovery_category_e m_sdSource = services_discovery_category_e::SD_CAT_INTERNET;
+
+ Q_DECLARE_PRIVATE(NetworkSourcesModel);
};
#endif // MLNetworkSourcesModel_HPP
=====================================
modules/gui/qt/network/qml/ServicesManage.qml
=====================================
@@ -29,7 +29,11 @@ Widgets.KeyNavigableListView {
// required by g_root to indicate view with 'grid' or 'list' mode
readonly property bool isViewMultiView: false
- model: discoveryFilterModel
+ model: ServicesDiscoveryModel {
+ id: discoveryModel
+ ctx: MainCtx
+ }
+
topMargin: VLCStyle.margin_large
leftMargin: VLCStyle.margin_large
rightMargin: VLCStyle.margin_large
@@ -115,9 +119,9 @@ Widgets.KeyNavigableListView {
onClicked: {
if (model.state === ServicesDiscoveryModel.NOTINSTALLED)
- discoveryModel.installService(discoveryFilterModel.mapIndexToSource(index))
+ discoveryModel.installService(index)
else if (model.state === ServicesDiscoveryModel.INSTALLED)
- discoveryModel.installService(discoveryFilterModel.mapIndexToSource(index))
+ discoveryModel.removeService(index)
}
}
}
@@ -148,17 +152,4 @@ Widgets.KeyNavigableListView {
color: servicesView.colorContext.fg.primary
z: 1
}
-
- ServicesDiscoveryModel {
- id: discoveryModel
-
- ctx: MainCtx
- }
-
- SortFilterProxyModel {
- id: discoveryFilterModel
-
- sourceModel: discoveryModel
- searchRole: "name"
- }
}
=====================================
modules/gui/qt/network/qml/ServicesSources.qml
=====================================
@@ -32,7 +32,7 @@ MainInterface.MainGridView {
readonly property bool isViewMultiView: false
selectionDelegateModel: selectionModel
- model: sourcesFilterModel
+ model: sourcesModel
topMargin: VLCStyle.margin_large
cellWidth: VLCStyle.gridItem_network_width
cellHeight: VLCStyle.gridCover_network_height + VLCStyle.margin_xsmall + VLCStyle.fontHeight_normal
@@ -96,7 +96,7 @@ MainInterface.MainGridView {
}
onActionAtIndex: {
- const itemData = sourcesFilterModel.getDataAt(index);
+ const itemData = sourcesModel.getDataAt(index);
if (itemData.type === NetworkSourcesModel.TYPE_DUMMY)
History.push(["mc", "discover", "services", "services_manage"], Qt.TabFocusReason)
@@ -120,13 +120,6 @@ MainInterface.MainGridView {
Util.SelectableDelegateModel {
id: selectionModel
- model: sourcesFilterModel
- }
-
- SortFilterProxyModel {
- id: sourcesFilterModel
-
- sourceModel: sourcesModel
- searchRole: "name"
+ model: sourcesModel
}
}
=====================================
modules/gui/qt/network/servicesdiscoverymodel.cpp
=====================================
@@ -17,54 +17,224 @@
*****************************************************************************/
#include "servicesdiscoverymodel.hpp"
-#include <vlc_addons.h>
+
+#include "util/base_model_p.hpp"
+#include "util/locallistcacheloader.hpp"
#include "medialibrary/mlhelper.hpp"
#include "playlist/media.hpp"
#include "playlist/playlist_controller.hpp"
+#include <memory>
#include <QPixmap>
-ServicesDiscoveryModel::ServicesDiscoveryModel( QObject* parent )
- : QAbstractListModel( parent )
+#include <vlc_media_source.h>
+#include <vlc_addons.h>
+#include <vlc_cxx_helpers.hpp>
+#include <vlc_addons.h>
+
+namespace {
+
+using AddonPtr = vlc_shared_data_ptr_type(addon_entry_t,
+ addon_entry_Hold, addon_entry_Release);
+
+class SDItem {
+public:
+ SDItem( AddonPtr addon )
+ {
+ name = qfu( addon->psz_name );
+ summery = qfu( addon->psz_summary ).trimmed();
+ description = qfu( addon->psz_description ).trimmed();
+ author = qfu( addon->psz_author );
+ sourceUrl = QUrl( addon->psz_source_uri );
+ entry = addon;
+
+ if ( addon->psz_image_data ) {
+ char *cDir = config_GetUserDir( VLC_CACHE_DIR );
+ if (likely(cDir != nullptr))
+ {
+ QDir dir( cDir );
+ free(cDir);
+ dir.mkdir("art");
+ dir.cd("art");
+ dir.mkdir("qt-addon-covers");
+ dir.cd("qt-addon-covers");
+
+ QString id = addons_uuid_to_psz( &addon->uuid );
+ QString filename = QString("addon_thumbnail_%1.png").arg(id);
+ QString absoluteFilePath = dir.absoluteFilePath(filename);
+
+ if ( !QFileInfo::exists( absoluteFilePath )) {
+ QPixmap pixmap;
+ pixmap.loadFromData( QByteArray::fromBase64( QByteArray( addon->psz_image_data ) ),
+ 0,
+ Qt::AutoColor
+ );
+ pixmap.save(absoluteFilePath);
+ }
+ artworkUrl = QUrl::fromLocalFile( absoluteFilePath );
+ }
+ }
+ else if ( addon->e_flags & ADDON_BROKEN )
+ artworkUrl = QUrl( ":/addons/addon_broken.svg" );
+ else
+ artworkUrl = QUrl( ":/addons/addon_default.svg" );
+ }
+
+public:
+ QString name;
+ QString summery;
+ QString description;
+ QString author;
+ QUrl sourceUrl;
+ QUrl artworkUrl;
+ AddonPtr entry;
+};
+
+} //namespace
+
+using SDItemPtr = std::shared_ptr<SDItem> ;
+using SDItemList = std::vector<SDItemPtr> ;
+
+// ListCache specialisation
+
+template<>
+bool ListCache<SDItemPtr>::compareItems(const SDItemPtr& a, const SDItemPtr& b)
{
+ //just compare the pointers here
+ return a == b;
}
-ServicesDiscoveryModel::~ServicesDiscoveryModel()
+// ServicesDiscoveryModelPrivate
+
+class ServicesDiscoveryModelPrivate
+ : public BaseModelPrivateT<SDItemPtr>
+ , public LocalListCacheLoader<SDItemPtr>::ModelSource
{
- if ( m_manager )
+public:
+ Q_DECLARE_PUBLIC(ServicesDiscoveryModel)
+
+public: //ctor/dtor
+ ServicesDiscoveryModelPrivate(ServicesDiscoveryModel* pub)
+ : BaseModelPrivateT<SDItemPtr>(pub)
{
- addons_manager_Delete( m_manager );
}
+
+ ~ServicesDiscoveryModelPrivate()
+ {
+ if ( m_manager )
+ addons_manager_Delete( m_manager );
+ }
+
+public:
+ const SDItem* getItemForRow(int row) const
+ {
+ const SDItemPtr* ref = item(row);
+ if (ref)
+ return ref->get();
+ return nullptr;
+ }
+
+public: //BaseModelPrivateT implementation
+ bool initializeModel() override;
+
+ bool loading() const override
+ {
+ return m_loading || BaseModelPrivateT<SDItemPtr>::loading();
+ }
+
+ std::unique_ptr<ListCacheLoader<SDItemPtr>> createLoader() const override
+ {
+ return std::make_unique<LocalListCacheLoader<SDItemPtr>>(
+ this, m_searchPattern,
+ getSortFunction()
+ );
+ }
+
+ LocalListCacheLoader<SDItemPtr>::ItemCompare getSortFunction() const
+ {
+ if (m_sortOrder == Qt::SortOrder::DescendingOrder)
+ return [](const SDItemPtr& a, const SDItemPtr& b){
+ return QString::compare(a->name, b->name) > 0;
+ };
+ else
+ return [](const SDItemPtr& a, const SDItemPtr& b) {
+ return QString::compare(a->name, b->name) < 0;
+ };
+ }
+
+
+
+public: //discovery callbacks
+ void addonFound( AddonPtr addon );
+ void addonChanged( AddonPtr addon );
+ void discoveryEnded();
+
+public: //LocalListCacheLoader implementation
+
+ virtual size_t getModelRevision() const override
+ {
+ return m_revision;
+ }
+
+ //return the data matching the pattern
+ virtual SDItemList getModelData(const QString& pattern) const override
+ {
+ if (pattern.isEmpty())
+ return m_items;
+
+ SDItemList items;
+ std::copy_if(
+ m_items.cbegin(), m_items.cend(),
+ std::back_inserter(items),
+ [&pattern](const SDItemPtr& item) {
+ return item->name.contains(pattern, Qt::CaseInsensitive);
+ });
+ return items;
+ }
+
+public: // data
+ bool m_loading = true;
+ addons_manager_t* m_manager = nullptr;
+
+ size_t m_revision = 0;
+ SDItemList m_items;
+};
+
+ServicesDiscoveryModel::ServicesDiscoveryModel( QObject* parent )
+ : BaseModel( new ServicesDiscoveryModelPrivate(this), parent )
+{
}
QVariant ServicesDiscoveryModel::data( const QModelIndex& index, int role ) const
{
+ Q_D(const ServicesDiscoveryModel);
if (!m_ctx)
return {};
- auto idx = index.row();
- if ( idx < 0 || (size_t)idx >= m_items.size() )
+
+ const SDItem* item = d->getItemForRow(index.row());
+ if (!item)
return {};
- const auto& item = m_items[idx];
+
switch ( role )
{
case Role::SERVICE_NAME:
- return item.name;
+ return item->name;
case Role::SERVICE_AUTHOR:
- return item.author;
+ return item->author;
case Role::SERVICE_SUMMARY:
- return item.summery;
+ return item->summery;
case Role::SERVICE_DESCRIPTION:
- return item.description;
+ return item->description;
case Role::SERVICE_DOWNLOADS:
- return QVariant::fromValue( item.entry->i_downloads );
+ return QVariant::fromValue( item->entry->i_downloads );
case Role::SERVICE_SCORE:
- return item.entry->i_score / 100;
+ return item->entry->i_score / 100;
case Role::SERVICE_STATE:
- return item.entry->e_state;
+ return item->entry->e_state;
case Role::SERVICE_ARTWORK:
- return item.artworkUrl;
+ return item->artworkUrl;
default:
return {};
}
@@ -84,62 +254,83 @@ QHash<int, QByteArray> ServicesDiscoveryModel::roleNames() const
};
}
-QMap<QString, QVariant> ServicesDiscoveryModel::getDataAt(int idx)
-{
- QMap<QString, QVariant> dataDict;
- QHash<int,QByteArray> roles = roleNames();
- for (auto role: roles.keys()) {
- dataDict[roles[role]] = data(index(idx), role);
- }
- return dataDict;
-}
-
void ServicesDiscoveryModel::installService(int idx)
{
- if ( idx < 0 || idx >= (int)m_items.size() )
+ Q_D(ServicesDiscoveryModel);
+
+ const SDItem* item = d->getItemForRow(idx);
+ if (!item)
return;
addon_uuid_t uuid;
- memcpy( uuid, m_items[idx].entry->uuid, sizeof( uuid ) );
- addons_manager_Install( m_manager, uuid );
+ memcpy( uuid, item->entry->uuid, sizeof( uuid ) );
+ addons_manager_Install( d->m_manager, uuid );
}
void ServicesDiscoveryModel::removeService(int idx)
{
- if ( idx < 0 || idx >= (int)m_items.size() )
+ Q_D(ServicesDiscoveryModel);
+
+ const SDItem* item = d->getItemForRow(idx);
+ if (!item)
return;
addon_uuid_t uuid;
- memcpy( uuid, m_items[idx].entry->uuid, sizeof( uuid ) );
- addons_manager_Remove( m_manager, uuid );
+ memcpy( uuid, item->entry->uuid, sizeof( uuid ) );
+ addons_manager_Remove( d->m_manager, uuid );
}
-int ServicesDiscoveryModel::rowCount(const QModelIndex& parent) const
+
+void ServicesDiscoveryModel::setCtx(MainCtx* ctx)
{
- if ( parent.isValid() )
- return 0;
- return getCount();
+ Q_D(ServicesDiscoveryModel);
+
+ if (ctx == m_ctx)
+ return;
+
+ assert(ctx);
+ m_ctx = ctx;
+ d->initializeModel();
+ emit ctxChanged();
}
-int ServicesDiscoveryModel::getCount() const
+static void addonFoundCallback( addons_manager_t *manager, addon_entry_t *entry )
{
- assert( m_items.size() < INT32_MAX );
- return static_cast<int>( m_items.size() );
+ if (entry->e_type != ADDON_SERVICE_DISCOVERY)
+ return;
+ ServicesDiscoveryModelPrivate* d = (ServicesDiscoveryModelPrivate*) manager->owner.sys;
+ QMetaObject::invokeMethod( d->q_func(), [d, entryPtr = AddonPtr(entry)]()
+ {
+ d->addonFound( std::move( entryPtr ) );
+ }, Qt::QueuedConnection);
}
-void ServicesDiscoveryModel::setCtx(MainCtx* ctx)
+static void addonsDiscoveryEndedCallback( addons_manager_t *manager )
{
- if (ctx) {
- m_ctx = ctx;
- }
- if (m_ctx) {
- initializeManager();
- }
- emit ctxChanged();
+ ServicesDiscoveryModelPrivate* d = (ServicesDiscoveryModelPrivate*) manager->owner.sys;
+ QMetaObject::invokeMethod( d->q_func(), [d]()
+ {
+ d->discoveryEnded();
+ }, Qt::QueuedConnection);
}
-void ServicesDiscoveryModel::initializeManager()
+static void addonChangedCallback( addons_manager_t *manager, addon_entry_t *entry )
{
+ if (entry->e_type != ADDON_SERVICE_DISCOVERY)
+ return;
+ ServicesDiscoveryModelPrivate* d = (ServicesDiscoveryModelPrivate*) manager->owner.sys;
+ QMetaObject::invokeMethod( d->q_func(), [d, entryPtr = AddonPtr(entry)]()
+ {
+ d->addonChanged( std::move( entryPtr ) );
+ }, Qt::QueuedConnection);
+}
+
+bool ServicesDiscoveryModelPrivate::initializeModel()
+{
+ Q_Q(ServicesDiscoveryModel);
+ if (m_qmlInitializing || !q->m_ctx)
+ return false;
+
if ( m_manager )
addons_manager_Delete( m_manager );
@@ -151,119 +342,42 @@ void ServicesDiscoveryModel::initializeManager()
addonChangedCallback,
};
- m_manager = addons_manager_New( VLC_OBJECT( m_ctx->getIntf() ), &owner );
+ m_manager = addons_manager_New( VLC_OBJECT( q->m_ctx->getIntf() ), &owner );
assert( m_manager );
m_loading = true;
- emit loadingChanged();
+ emit q->loadingChanged();
addons_manager_LoadCatalog( m_manager );
addons_manager_Gather( m_manager, "repo://" );
+ return true;
}
-void ServicesDiscoveryModel::addonFoundCallback( addons_manager_t *manager,
- addon_entry_t *entry )
-{
- if (entry->e_type != ADDON_SERVICE_DISCOVERY)
- return;
- ServicesDiscoveryModel *me = (ServicesDiscoveryModel *) manager->owner.sys;
- QMetaObject::invokeMethod( me, [me, entryPtr = AddonPtr(entry)]()
- {
- me->addonFound( std::move( entryPtr ) );
- }, Qt::QueuedConnection);
-}
-
-void ServicesDiscoveryModel::addonsDiscoveryEndedCallback( addons_manager_t *manager )
-{
- ServicesDiscoveryModel *me = (ServicesDiscoveryModel *) manager->owner.sys;
- QMetaObject::invokeMethod( me, [me]()
- {
- me->discoveryEnded();
- }, Qt::QueuedConnection);
-}
-
-void ServicesDiscoveryModel::addonChangedCallback( addons_manager_t *manager,
- addon_entry_t *entry )
-{
- if (entry->e_type != ADDON_SERVICE_DISCOVERY)
- return;
- ServicesDiscoveryModel *me = (ServicesDiscoveryModel *) manager->owner.sys;
- QMetaObject::invokeMethod( me, [me, entryPtr = AddonPtr(entry)]()
- {
- me->addonChanged( std::move( entryPtr ) );
- }, Qt::QueuedConnection);
-}
-void ServicesDiscoveryModel::addonFound( ServicesDiscoveryModel::AddonPtr addon )
+void ServicesDiscoveryModelPrivate::addonFound( AddonPtr addon )
{
- beginInsertRows( QModelIndex(), getCount(), getCount() );
- m_items.emplace_back(addon);
- endInsertRows();
- emit countChanged();
+ m_items.emplace_back(std::make_shared<SDItem>(addon));
+ m_revision++;
+ invalidateCache();
}
-void ServicesDiscoveryModel::addonChanged( ServicesDiscoveryModel::AddonPtr addon )
+void ServicesDiscoveryModelPrivate::addonChanged( AddonPtr addon )
{
- for ( int r = 0; r < getCount(); ++r )
+ for ( size_t r = 0; r < m_items.size(); ++r )
{
- if ( memcmp( m_items[r].entry->uuid, addon->uuid, sizeof( addon->uuid ) ) )
+ if ( memcmp( m_items[r]->entry->uuid, addon->uuid, sizeof( addon->uuid ) ) )
continue;
- m_items[r] = addon;
- emit dataChanged( index( r, 0 ), index( r, 0 ) );
+ m_items[r] = std::make_shared<SDItem>(addon);
+ break;
}
+ m_revision++;
+ invalidateCache();
}
-void ServicesDiscoveryModel::discoveryEnded()
+void ServicesDiscoveryModelPrivate::discoveryEnded()
{
+ Q_Q(ServicesDiscoveryModel);
assert( m_loading );
m_loading = false;
- emit loadingChanged();
-}
-
-ServicesDiscoveryModel::Item::Item( ServicesDiscoveryModel::AddonPtr addon )
-{
- *this = addon;
-}
-
-ServicesDiscoveryModel::Item &ServicesDiscoveryModel::Item::operator=( ServicesDiscoveryModel::AddonPtr addon )
-{
- name = qfu( addon->psz_name );
- summery = qfu( addon->psz_summary ).trimmed();
- description = qfu( addon->psz_description ).trimmed();
- author = qfu( addon->psz_author );
- sourceUrl = QUrl( addon->psz_source_uri );
- entry = addon;
-
- if ( addon->psz_image_data ) {
- char *cDir = config_GetUserDir( VLC_CACHE_DIR );
- if (likely(cDir != nullptr))
- {
- QDir dir( cDir );
- free(cDir);
- dir.mkdir("art");
- dir.cd("art");
- dir.mkdir("qt-addon-covers");
- dir.cd("qt-addon-covers");
-
- QString id = addons_uuid_to_psz( &addon->uuid );
- QString filename = QString("addon_thumbnail_%1.png").arg(id);
- QString absoluteFilePath = dir.absoluteFilePath(filename);
-
- if ( !QFileInfo::exists( absoluteFilePath )) {
- QPixmap pixmap;
- pixmap.loadFromData( QByteArray::fromBase64( QByteArray( addon->psz_image_data ) ),
- 0,
- Qt::AutoColor
- );
- pixmap.save(absoluteFilePath);
- }
- artworkUrl = QUrl::fromLocalFile( absoluteFilePath );
- }
- }
- else if ( addon->e_flags & ADDON_BROKEN )
- artworkUrl = QUrl( ":/addons/addon_broken.svg" );
- else
- artworkUrl = QUrl( ":/addons/addon_default.svg" );
-
- return *this;
+ emit q->loadingChanged();
}
=====================================
modules/gui/qt/network/servicesdiscoverymodel.hpp
=====================================
@@ -23,25 +23,17 @@
#include "config.h"
#endif
-#include <QAbstractListModel>
+#include "util/base_model.hpp"
+#include "maininterface/mainctx.hpp"
-#include <vlc_media_source.h>
-#include <vlc_addons.h>
-#include <vlc_cxx_helpers.hpp>
-
-#include <maininterface/mainctx.hpp>
-
-#include <memory>
-
-class ServicesDiscoveryModel : public QAbstractListModel
+struct ServicesDiscoveryModelPrivate;
+class ServicesDiscoveryModel : public BaseModel
{
Q_OBJECT
public:
Q_PROPERTY(MainCtx* ctx READ getCtx WRITE setCtx NOTIFY ctxChanged FINAL)
- Q_PROPERTY(bool loading READ isLoading NOTIFY loadingChanged FINAL)
- Q_PROPERTY(int count READ getCount NOTIFY countChanged FINAL)
enum State // equivalent to addon_state_t
{
@@ -65,58 +57,26 @@ public:
Q_ENUM(Role)
explicit ServicesDiscoveryModel(QObject* parent = nullptr);
- ~ServicesDiscoveryModel() override;
+public: //QAbstractListModel override
QVariant data(const QModelIndex& index, int role) const override;
QHash<int, QByteArray> roleNames() const override;
- int rowCount(const QModelIndex& parent = {}) const override;
-
- void setCtx(MainCtx* ctx);
- inline MainCtx* getCtx() const { return m_ctx; }
- inline bool isLoading() const { return m_loading; }
- int getCount() const;
-
- Q_INVOKABLE QMap<QString, QVariant> getDataAt(int idx);
+public: //invokable functions
Q_INVOKABLE void installService(int idx);
Q_INVOKABLE void removeService(int idx);
+public: // properties
+ void setCtx(MainCtx* ctx);
+ inline MainCtx* getCtx() const { return m_ctx; }
+
signals:
- void loadingChanged();
- void countChanged();
void ctxChanged();
private:
- using AddonPtr = vlc_shared_data_ptr_type(addon_entry_t,
- addon_entry_Hold, addon_entry_Release);
-
- void initializeManager();
- static void addonFoundCallback( addons_manager_t *, struct addon_entry_t * );
- static void addonsDiscoveryEndedCallback( addons_manager_t * );
- static void addonChangedCallback( addons_manager_t *, struct addon_entry_t * );
-
- void addonFound( AddonPtr addon );
- void addonChanged( AddonPtr addon );
- void discoveryEnded();
-
- struct Item
- {
- QString name;
- QString summery;
- QString description;
- QString author;
- QUrl sourceUrl;
- QUrl artworkUrl;
- AddonPtr entry;
-
- Item( AddonPtr addon );
- Item& operator =( AddonPtr addon );
- };
-
- std::vector<Item> m_items;
MainCtx* m_ctx = nullptr;
- addons_manager_t* m_manager = nullptr;
- bool m_loading = false;
+
+ Q_DECLARE_PRIVATE(ServicesDiscoveryModel);
};
#endif // MLServicesDiscoveryModel_HPP
=====================================
modules/gui/qt/util/sortfilterproxymodel.cpp deleted
=====================================
@@ -1,129 +0,0 @@
-/*****************************************************************************
- * Copyright (C) 2020 VLC authors and VideoLAN
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * ( at your option ) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-
-#include "sortfilterproxymodel.hpp"
-
-#include <QRegularExpression>
-
-#include <cassert>
-
-
-SortFilterProxyModel::SortFilterProxyModel( QObject *parent )
- : QSortFilterProxyModel( parent )
-{
- setFilterCaseSensitivity(Qt::CaseInsensitive);
-
- connect( this, &QAbstractListModel::rowsInserted, this, &SortFilterProxyModel::countChanged );
- connect( this, &QAbstractListModel::rowsRemoved, this, &SortFilterProxyModel::countChanged );
- connect( this, &QAbstractItemModel::modelReset, this, &SortFilterProxyModel::countChanged );
- connect( this, &QAbstractItemModel::layoutChanged, this, &SortFilterProxyModel::countChanged );
-
- connect( this, &QAbstractProxyModel::sourceModelChanged, this, &SortFilterProxyModel::updateFilterRole );
- connect( this, &QAbstractProxyModel::sourceModelChanged, this, &SortFilterProxyModel::updateSortRole );
-}
-
-QString SortFilterProxyModel::searchPattern() const
-{
- return filterRegularExpression().pattern();
-}
-
-void SortFilterProxyModel::setSearchPattern( const QString &searchPattern )
-{
- setFilterRegularExpression(searchPattern);
-}
-
-QByteArray SortFilterProxyModel::searchRole() const
-{
- return m_searchRole;
-}
-
-void SortFilterProxyModel::setSearchRole( const QByteArray &searchRole )
-{
- m_searchRole = searchRole;
- emit searchRoleChanged();
- updateFilterRole();
-}
-
-QString SortFilterProxyModel::sortCriteria() const
-{
- return m_sortCriteria;
-}
-
-void SortFilterProxyModel::setSortCriteria(const QString &sortCriteria)
-{
- if (m_sortCriteria == sortCriteria)
- return;
-
- m_sortCriteria = sortCriteria;
- updateSortRole();
- emit sortCriteriaChanged();
-}
-
-Qt::SortOrder SortFilterProxyModel::sortOrder() const
-{
- return QSortFilterProxyModel::sortOrder();
-}
-
-void SortFilterProxyModel::setSortOrder(Qt::SortOrder sortOrder)
-{
- if (this->sortOrder() == sortOrder)
- return;
-
- this->sort(0, sortOrder);
- emit sortOrderChanged();
-}
-
-int SortFilterProxyModel::count() const
-{
- return rowCount();
-}
-
-QMap<QString, QVariant> SortFilterProxyModel::getDataAt( int idx )
-{
- QMap<QString, QVariant> dataDict;
- QHash<int,QByteArray> roles = roleNames();
- for( const auto role: roles.keys() ) {
- dataDict[roles[role]] = data( index( idx, 0 ), role );
- }
- return dataDict;
-}
-
-QModelIndexList SortFilterProxyModel::mapIndexesToSource( const QModelIndexList &indexes )
-{
- QModelIndexList sourceIndexes;
- sourceIndexes.reserve( indexes.size() );
- for( const auto &proxyIndex : indexes ) {
- sourceIndexes.push_back( mapToSource(proxyIndex) );
- }
- return sourceIndexes;
-}
-
-int SortFilterProxyModel::mapIndexToSource(int idx)
-{
- return mapToSource( index( idx, 0 ) ).row();
-}
-
-void SortFilterProxyModel::updateFilterRole()
-{
- setFilterRole( roleNames().key( m_searchRole ) );
-}
-
-void SortFilterProxyModel::updateSortRole()
-{
- setSortRole( roleNames().key( m_sortCriteria.toUtf8() ) );
-}
=====================================
modules/gui/qt/util/sortfilterproxymodel.hpp deleted
=====================================
@@ -1,76 +0,0 @@
-/*****************************************************************************
- * Copyright (C) 2020 VLC authors and VideoLAN
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * ( at your option ) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-
-#ifndef SORT_FILTER_PROXY_MODEL
-#define SORT_FILTER_PROXY_MODEL
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "qt.hpp"
-#include <QSortFilterProxyModel>
-
-class SortFilterProxyModel : public QSortFilterProxyModel
-{
- Q_OBJECT
-
- Q_PROPERTY( QByteArray searchRole READ searchRole WRITE setSearchRole NOTIFY searchRoleChanged FINAL)
- Q_PROPERTY( QString searchPattern READ searchPattern WRITE setSearchPattern NOTIFY searchPatternChanged FINAL)
- Q_PROPERTY( QString sortCriteria READ sortCriteria WRITE setSortCriteria NOTIFY sortCriteriaChanged FINAL)
- Q_PROPERTY( Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder NOTIFY sortOrderChanged FINAL)
- Q_PROPERTY( int count READ count NOTIFY countChanged FINAL)
-
-public:
- SortFilterProxyModel( QObject * parent = nullptr );
-
- QString searchPattern() const;
- void setSearchPattern( const QString &searchPattern );
-
- QByteArray searchRole() const;
- void setSearchRole( const QByteArray &searchRole );
-
- QString sortCriteria() const;
- void setSortCriteria( const QString &sortCriteria );
-
- Qt::SortOrder sortOrder() const;
- void setSortOrder(Qt::SortOrder sortOrder);
-
- int count() const;
-
- Q_INVOKABLE QMap<QString, QVariant> getDataAt( int idx );
- Q_INVOKABLE QModelIndexList mapIndexesToSource( const QModelIndexList &indexes );
- Q_INVOKABLE int mapIndexToSource( int idx );
-
-signals:
- void searchPatternChanged();
- void searchRoleChanged();
- void countChanged();
- void sortCriteriaChanged();
- void sortOrderChanged();
-
-private slots:
- void updateFilterRole();
- void updateSortRole();
-
-private:
- QByteArray m_searchRole;
- QString m_sortCriteria;
-};
-
-#endif // SORT_FILTER_PROXY_MODEL
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/4ea5d3b9ac4cc7938cadd3a849303bf8bd5fcfda...dd683943be31612ea9c48d64a8b9f21e44ecb971
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/4ea5d3b9ac4cc7938cadd3a849303bf8bd5fcfda...dd683943be31612ea9c48d64a8b9f21e44ecb971
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