From bcc84223de3a71b57fb098e8e90aad4cabb6622d Mon Sep 17 00:00:00 2001 From: samamou Date: Thu, 6 Feb 2025 10:15:56 -0500 Subject: [PATCH 1/6] Add hardcoded categories --- .../PackageManager/PluginItemModel.cpp | 1 + .../PackageManager/PluginItemModel.hpp | 2 +- .../PackageManager/View.cpp | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/plugins/score-plugin-packagemanager/PackageManager/PluginItemModel.cpp b/src/plugins/score-plugin-packagemanager/PackageManager/PluginItemModel.cpp index d896665b0e..2127bb6fc1 100644 --- a/src/plugins/score-plugin-packagemanager/PackageManager/PluginItemModel.cpp +++ b/src/plugins/score-plugin-packagemanager/PackageManager/PluginItemModel.cpp @@ -246,6 +246,7 @@ std::optional Package::fromJson(const QJsonObject& obj) noexcept {"long", [&](QJsonValue v) { add.longDescription = v.toString(); }}, {"small", [&](QJsonValue v) { add.smallImagePath = v.toString(); }}, {"large", [&](QJsonValue v) { add.largeImagePath = v.toString(); }}, + {"category", [&](QJsonValue v) { add.category = v.toString(); }}, {"size", [&](QJsonValue v) { add.size = v.toString(); }}, {"key", [&](QJsonValue v) { add.key = UuidKey::fromString(v.toString()); diff --git a/src/plugins/score-plugin-packagemanager/PackageManager/PluginItemModel.hpp b/src/plugins/score-plugin-packagemanager/PackageManager/PluginItemModel.hpp index 7ecf2f6140..f82043b671 100644 --- a/src/plugins/score-plugin-packagemanager/PackageManager/PluginItemModel.hpp +++ b/src/plugins/score-plugin-packagemanager/PackageManager/PluginItemModel.hpp @@ -35,7 +35,7 @@ struct Package std::vector files; // URL to a file containing the current version. QMap> arch_files; // if there are per-architecture files QString url; // Link to the homepage of the package if any - + QString category; QString shortDescription; QString longDescription; QString smallImagePath; diff --git a/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp b/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp index 2279e699e8..ad19bbc5b4 100644 --- a/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp +++ b/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -109,10 +111,22 @@ PluginSettingsView::PluginSettingsView() auto vlay = new QVBoxLayout{side_widget}; grid->addWidget(side_widget, 0, 1, 2, 1); + auto categoryLabel = new QLabel("Select Category:"); + vlay->addWidget(categoryLabel); + + auto categoryComboBox = new QComboBox; + categoryComboBox->addItem("All"); + categoryComboBox->addItem("Media"); + categoryComboBox->addItem("AI Models"); + vlay->addWidget(categoryComboBox); + vlay->addSpacing(20); + vlay->addWidget(m_link); vlay->addWidget(m_uninstall); m_install->setVisible(false); vlay->addWidget(m_install); + vlay->addSpacing(20); + vlay->addWidget(m_update); vlay->addWidget(m_updateAll); vlay->addStretch(); From f742a77b1d012b7a0cd8296542615152961f1e58 Mon Sep 17 00:00:00 2001 From: samamou Date: Thu, 6 Feb 2025 19:30:38 -0500 Subject: [PATCH 2/6] Add tooltip and external link icon and adjust some widget text --- .../PackageManager/View.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp b/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp index ad19bbc5b4..8daaf59db6 100644 --- a/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp +++ b/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp @@ -4,6 +4,7 @@ #include #include +#include #include @@ -95,7 +96,7 @@ PluginSettingsView::PluginSettingsView() local_widget->setLayout(local_layout); local_layout->addWidget(m_addonsOnSystem); - tab_widget->addTab(local_widget, tr("Local")); + tab_widget->addTab(local_widget, tr("Local packages")); } { @@ -104,7 +105,7 @@ PluginSettingsView::PluginSettingsView() remote_widget->setLayout(remote_layout); remote_layout->addWidget(m_remoteAddons); - tab_widget->addTab(remote_widget, tr("Browse")); + tab_widget->addTab(remote_widget, tr("Available packages")); } auto side_widget = new QWidget; @@ -121,7 +122,16 @@ PluginSettingsView::PluginSettingsView() vlay->addWidget(categoryComboBox); vlay->addSpacing(20); + m_link->setToolTip(tr("Open external package link in default browser.")); + auto icon = makeIcons( + QStringLiteral(":/icons/undock_on.png"), QStringLiteral(":/icons/undock_off.png"), + QStringLiteral(":/icons/undock_off.png")); + m_link->setIcon(icon); + vlay->addWidget(m_link); + + vlay->addSpacing(20); + vlay->addWidget(m_uninstall); m_install->setVisible(false); vlay->addWidget(m_install); @@ -236,7 +246,8 @@ void PluginSettingsView::refresh() if(qEnvironmentVariableIsSet("SCORE_SANITIZE_SKIP_CHECKS")) return; QNetworkRequest rqst{ - QUrl("https://raw.githubusercontent.com/ossia/score-addons/master/addons.json")}; + QUrl("https://raw.githubusercontent.com/ossia/score-addons/refs/heads/" + "add-ai-models/addons.json")}; mgr.get(rqst); } From 5b5f4ca9251bade1a4803da59e28eb3b9b8342c1 Mon Sep 17 00:00:00 2001 From: samamou Date: Fri, 7 Feb 2025 17:39:41 -0500 Subject: [PATCH 3/6] one step closer to filtering by "kind" --- .../PackageManager/PluginItemModel.cpp | 11 +++-- .../PackageManager/PluginItemModel.hpp | 8 ++-- .../PackageManager/View.cpp | 48 +++++++++++++++++-- .../PackageManager/View.hpp | 2 + 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/plugins/score-plugin-packagemanager/PackageManager/PluginItemModel.cpp b/src/plugins/score-plugin-packagemanager/PackageManager/PluginItemModel.cpp index 2127bb6fc1..6d9db01424 100644 --- a/src/plugins/score-plugin-packagemanager/PackageManager/PluginItemModel.cpp +++ b/src/plugins/score-plugin-packagemanager/PackageManager/PluginItemModel.cpp @@ -68,6 +68,9 @@ PackagesModel::headerData(int section, Qt::Orientation orientation, int role) co case Column::ShortDesc: return tr("Description"); break; + case Column::Kind: + return tr("Kind"); + break; case Column::Version: return tr("Version"); break; @@ -108,12 +111,15 @@ QVariant PackagesModel::data(const QModelIndex& index, int role) const case Column::ShortDesc: return addon.shortDescription; break; - case Column::Version: - return addon.version; + case Column::Kind: + return addon.kind; break; case Column::Size: return addon.size; break; + case Column::Version: + return addon.version; + break; default: break; } @@ -246,7 +252,6 @@ std::optional Package::fromJson(const QJsonObject& obj) noexcept {"long", [&](QJsonValue v) { add.longDescription = v.toString(); }}, {"small", [&](QJsonValue v) { add.smallImagePath = v.toString(); }}, {"large", [&](QJsonValue v) { add.largeImagePath = v.toString(); }}, - {"category", [&](QJsonValue v) { add.category = v.toString(); }}, {"size", [&](QJsonValue v) { add.size = v.toString(); }}, {"key", [&](QJsonValue v) { add.key = UuidKey::fromString(v.toString()); diff --git a/src/plugins/score-plugin-packagemanager/PackageManager/PluginItemModel.hpp b/src/plugins/score-plugin-packagemanager/PackageManager/PluginItemModel.hpp index f82043b671..f95c6c85d9 100644 --- a/src/plugins/score-plugin-packagemanager/PackageManager/PluginItemModel.hpp +++ b/src/plugins/score-plugin-packagemanager/PackageManager/PluginItemModel.hpp @@ -34,8 +34,7 @@ struct Package kind; // what kind of package it is (for now: "addon", "sdk", "library", "media", "presets") std::vector files; // URL to a file containing the current version. QMap> arch_files; // if there are per-architecture files - QString url; // Link to the homepage of the package if any - QString category; + QString url; // Link to the homepage of the package if any QString shortDescription; QString longDescription; QString smallImagePath; @@ -66,9 +65,10 @@ class PackagesModel : public QAbstractItemModel Name, Version, Size, - ShortDesc + ShortDesc, + Kind }; - static constexpr const int ColumnCount = 4; + static constexpr const int ColumnCount = 5; QModelIndex index(int row, int column, const QModelIndex& parent) const override; QModelIndex parent(const QModelIndex& child) const override; diff --git a/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp b/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp index 8daaf59db6..cb826368e3 100644 --- a/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp +++ b/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp @@ -112,13 +112,10 @@ PluginSettingsView::PluginSettingsView() auto vlay = new QVBoxLayout{side_widget}; grid->addWidget(side_widget, 0, 1, 2, 1); - auto categoryLabel = new QLabel("Select Category:"); + auto categoryLabel = new QLabel("Select Kind:"); vlay->addWidget(categoryLabel); - auto categoryComboBox = new QComboBox; - categoryComboBox->addItem("All"); - categoryComboBox->addItem("Media"); - categoryComboBox->addItem("AI Models"); + categoryComboBox = new QComboBox(m_widget); vlay->addWidget(categoryComboBox); vlay->addSpacing(20); @@ -172,6 +169,7 @@ PluginSettingsView::PluginSettingsView() m_progress->setValue(0); refresh(); + updateCategoryComboBox(1); } else // Local { @@ -179,6 +177,7 @@ PluginSettingsView::PluginSettingsView() m_install->setVisible(false); m_update->setVisible(true); m_updateAll->setVisible(true); + updateCategoryComboBox(0); } }); @@ -331,6 +330,11 @@ void PluginSettingsView::on_message(QNetworkReply* rep) } rep->deleteLater(); + if(m_install->isVisible()) + updateCategoryComboBox(1); + else + updateCategoryComboBox(0); + if(!m_firstTimeCheck) { m_firstTimeCheck = true; @@ -716,4 +720,38 @@ void PluginSettingsView::progress_from_bytes(qint64 bytesReceived, qint64 bytesT m_progress->setValue(((bytesReceived / 1024.) / (bytesTotal / 1024.)) * 100); } +void PluginSettingsView::updateCategoryComboBox(int tabIndex) +{ + categoryComboBox->clear(); + categoryComboBox->addItem("All"); + + PackagesModel* model = nullptr; + if(tabIndex == 0) // Local packages tab + { + model = static_cast(m_addonsOnSystem->model()); + } + else if(tabIndex == 1) // Remote packages tab + { + model = static_cast(m_remoteAddons->model()); + } + + // If the model exists, add uniqueKind + if(model) + { + QList uniqueKinds; + + for(const auto& addon : model->addons()) + { + if(!uniqueKinds.contains(addon.kind)) + { + uniqueKinds.append(addon.kind); + } + } + + for(const QString& kind : uniqueKinds) + { + categoryComboBox->addItem(kind); + } + } +} } diff --git a/src/plugins/score-plugin-packagemanager/PackageManager/View.hpp b/src/plugins/score-plugin-packagemanager/PackageManager/View.hpp index 60dfe6bde1..5380885f55 100644 --- a/src/plugins/score-plugin-packagemanager/PackageManager/View.hpp +++ b/src/plugins/score-plugin-packagemanager/PackageManager/View.hpp @@ -66,6 +66,7 @@ class PluginSettingsView : public score::GlobalSettingsView void set_info(); void reset_progress(); void progress_from_bytes(qint64 bytesReceived, qint64 bytesTotal); + void updateCategoryComboBox(int tabIndex); QWidget* m_widget{new QWidget}; @@ -84,6 +85,7 @@ class PluginSettingsView : public score::GlobalSettingsView QStorageInfo storage; QLabel* m_storage{new QLabel}; + QComboBox* categoryComboBox = nullptr; bool m_firstTimeCheck{false}; }; From a365ffbb41b2b8497f10f66b36a126e9e465d164 Mon Sep 17 00:00:00 2001 From: samamou Date: Mon, 10 Feb 2025 23:58:32 -0500 Subject: [PATCH 4/6] Refactor PackageManager to include sorting + remove QComboBox --- .../PackageManager/Presenter.cpp | 27 +- .../PackageManager/View.cpp | 1076 ++++++++--------- .../PackageManager/View.hpp | 5 + 3 files changed, 534 insertions(+), 574 deletions(-) diff --git a/src/plugins/score-plugin-packagemanager/PackageManager/Presenter.cpp b/src/plugins/score-plugin-packagemanager/PackageManager/Presenter.cpp index 93dc18c97c..7dba13f9b9 100644 --- a/src/plugins/score-plugin-packagemanager/PackageManager/Presenter.cpp +++ b/src/plugins/score-plugin-packagemanager/PackageManager/Presenter.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -37,20 +38,34 @@ PluginSettingsPresenter::PluginSettingsPresenter( auto& ps_model = static_cast(model); auto& ps_view = static_cast(view); - ps_view.localView()->setModel(&ps_model.localPlugins); + ps_view.m_remoteModel = &ps_model.remotePlugins; + ps_view.m_localModel = &ps_model.localPlugins; + + QSortFilterProxyModel* localProxyModel = new QSortFilterProxyModel(this); + localProxyModel->setSourceModel(&ps_model.localPlugins); + localProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); + + QSortFilterProxyModel* remoteProxyModel = new QSortFilterProxyModel(this); + remoteProxyModel->setSourceModel(&ps_model.remotePlugins); + remoteProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); + + ps_view.localView()->setModel(localProxyModel); ps_view.localView()->setColumnWidth(0, 200); ps_view.localView()->setColumnWidth(1, 40); ps_view.localView()->setColumnWidth(2, 40); + ps_view.localView()->setColumnWidth(3, 300); + ps_view.localView()->setColumnWidth(4, 30); ps_view.localView()->horizontalHeader()->setStretchLastSection(true); - ps_view.remoteView()->setModel(&ps_model.remotePlugins); + ps_view.remoteView()->setModel(remoteProxyModel); ps_view.remoteView()->setColumnWidth(0, 200); - ps_view.remoteView()->setColumnWidth(1, 40); - ps_view.remoteView()->setColumnWidth(2, 40); + ps_view.remoteView()->setColumnWidth(1, 50); + ps_view.remoteView()->setColumnWidth(2, 50); + ps_view.remoteView()->setColumnWidth(3, 300); + ps_view.remoteView()->setColumnWidth(4, 30); ps_view.remoteView()->horizontalHeader()->setStretchLastSection(true); - ps_view.remoteView()->setSelectionModel(&ps_model.remoteSelection); - + // ps_view.remoteView()->setSelectionModel(&ps_model.remoteSelection); connect( &ps_model.remoteSelection, &QItemSelectionModel::currentRowChanged, this, [&](const QModelIndex& current, const QModelIndex& previous) { diff --git a/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp b/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp index cb826368e3..e9b35f02c5 100644 --- a/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp +++ b/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -37,721 +38,660 @@ W_OBJECT_IMPL(PM::PluginSettingsView) namespace PM { -namespace zip_helper -{ + namespace zip_helper + { -QString get_path(const QString& str) -{ - auto idx = str.lastIndexOf('/'); - if(idx != -1) + QString get_path(const QString& str) { - return str.mid(0, idx); + auto idx = str.lastIndexOf('/'); + if(idx != -1) + { + return str.mid(0, idx); + } + return ""; } - return ""; -} - -QString slash_path(const QString& str) -{ - return {}; -} -QString relative_path(const QString& base, const QString& filename) -{ - return filename; -} + QString slash_path(const QString& str) + { + return {}; + } -QString combine_path(const QString& path, const QString& filename) -{ - return path + "/" + filename; -} + QString relative_path(const QString& base, const QString& filename) + { + return filename; + } -bool make_folder(const QString& str) -{ - QDir d; - return d.mkpath(str); -} + QString combine_path(const QString& path, const QString& filename) + { + return path + "/" + filename; + } -} + bool make_folder(const QString& str) + { + QDir d; + return d.mkpath(str); + } -PluginSettingsView::PluginSettingsView() -{ - storage = QStorageInfo( - score::AppContext().settings().getPackagesPath()); + } - m_progress->setMinimum(0); - m_progress->setMaximum(100); - m_progress->setHidden(true); + PluginSettingsView::PluginSettingsView() + { + storage = QStorageInfo( + score::AppContext().settings().getPackagesPath()); - auto grid = new QGridLayout{m_widget}; - grid->setContentsMargins(0, 0, 0, 0); - m_widget->setLayout(grid); + m_progress->setMinimum(0); + m_progress->setMaximum(100); + m_progress->setHidden(true); - auto tab_widget = new QTabWidget; - grid->addWidget(tab_widget, 0, 0); - grid->addWidget(m_progress, 1, 0); + auto grid = new QGridLayout{m_widget}; + grid->setContentsMargins(0, 0, 0, 0); + m_widget->setLayout(grid); - { - auto local_widget = new QWidget; - auto local_layout = new QVBoxLayout{local_widget}; - local_widget->setLayout(local_layout); - local_layout->addWidget(m_addonsOnSystem); + auto tab_widget = new QTabWidget; + grid->addWidget(tab_widget, 0, 0); + grid->addWidget(m_progress, 1, 0); - tab_widget->addTab(local_widget, tr("Local packages")); - } + { + auto local_widget = new QWidget; + auto local_layout = new QVBoxLayout{local_widget}; + local_widget->setLayout(local_layout); + local_layout->addWidget(m_addonsOnSystem); - { - auto remote_widget = new QWidget; - auto remote_layout = new QVBoxLayout{remote_widget}; - remote_widget->setLayout(remote_layout); - remote_layout->addWidget(m_remoteAddons); + tab_widget->addTab(local_widget, tr("Local packages")); + } - tab_widget->addTab(remote_widget, tr("Available packages")); - } + { + auto remote_widget = new QWidget; + auto remote_layout = new QVBoxLayout{remote_widget}; + remote_widget->setLayout(remote_layout); + remote_layout->addWidget(m_remoteAddons); - auto side_widget = new QWidget; - auto vlay = new QVBoxLayout{side_widget}; - grid->addWidget(side_widget, 0, 1, 2, 1); + tab_widget->addTab(remote_widget, tr("Available packages")); + } - auto categoryLabel = new QLabel("Select Kind:"); - vlay->addWidget(categoryLabel); + auto side_widget = new QWidget; + auto vlay = new QVBoxLayout{side_widget}; + grid->addWidget(side_widget, 0, 1, 2, 1); - categoryComboBox = new QComboBox(m_widget); - vlay->addWidget(categoryComboBox); - vlay->addSpacing(20); + vlay->addSpacing(20); - m_link->setToolTip(tr("Open external package link in default browser.")); - auto icon = makeIcons( + m_link->setToolTip(tr("Open external package link in default browser.")); + auto icon = makeIcons( QStringLiteral(":/icons/undock_on.png"), QStringLiteral(":/icons/undock_off.png"), - QStringLiteral(":/icons/undock_off.png")); - m_link->setIcon(icon); + QStringLiteral(":/icons/undock_off.png")); + m_link->setIcon(icon); - vlay->addWidget(m_link); + vlay->addWidget(m_link); - vlay->addSpacing(20); + vlay->addSpacing(20); - vlay->addWidget(m_uninstall); - m_install->setVisible(false); - vlay->addWidget(m_install); - vlay->addSpacing(20); + vlay->addWidget(m_uninstall); + m_install->setVisible(false); + vlay->addWidget(m_install); + vlay->addSpacing(20); - vlay->addWidget(m_update); - vlay->addWidget(m_updateAll); - vlay->addStretch(); + vlay->addWidget(m_update); + vlay->addWidget(m_updateAll); + vlay->addStretch(); - set_info(); - vlay->addWidget(m_storage); - - for(QTableView* v : {m_addonsOnSystem, m_remoteAddons}) - { - v->verticalHeader()->hide(); - v->verticalHeader()->sectionResizeMode(QHeaderView::Fixed); - v->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); - v->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); - v->setSelectionBehavior(QAbstractItemView::SelectRows); - v->setEditTriggers(QAbstractItemView::NoEditTriggers); - v->setSelectionMode(QAbstractItemView::SingleSelection); - v->setShowGrid(false); - } + set_info(); + vlay->addWidget(m_storage); - connect(tab_widget, &QTabWidget::tabBarClicked, this, [this](int i) { - if(i == 1) // Remote + for(QTableView* v : {m_addonsOnSystem, m_remoteAddons}) { - m_uninstall->setVisible(false); - m_install->setVisible(true); - m_update->setVisible(false); - m_updateAll->setVisible(false); + v->verticalHeader()->hide(); + v->verticalHeader()->sectionResizeMode(QHeaderView::Fixed); + v->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + v->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + v->setSelectionBehavior(QAbstractItemView::SelectRows); + v->setEditTriggers(QAbstractItemView::NoEditTriggers); + v->setSelectionMode(QAbstractItemView::SingleSelection); + v->setShowGrid(false); + } - RemotePackagesModel* model - = static_cast(m_remoteAddons->model()); - model->clear(); + connect(tab_widget, &QTabWidget::tabBarClicked, this, [this](int i) { + if(i == 1) // Remote + { + m_uninstall->setVisible(false); + m_install->setVisible(true); + m_update->setVisible(false); + m_updateAll->setVisible(false); - m_progress->setVisible(true); - m_progress->setValue(0); + m_remoteModel->clear(); - refresh(); - updateCategoryComboBox(1); - } - else // Local - { - m_uninstall->setVisible(true); - m_install->setVisible(false); - m_update->setVisible(true); - m_updateAll->setVisible(true); - updateCategoryComboBox(0); - } - }); + m_progress->setVisible(true); + m_progress->setValue(0); - connect(m_link, &QPushButton::pressed, this, &PluginSettingsView::openLink); + refresh(); + } + else // Local + { + m_uninstall->setVisible(true); + m_install->setVisible(false); + m_update->setVisible(true); + m_updateAll->setVisible(true); + } + }); - connect(m_uninstall, &QPushButton::pressed, this, &PluginSettingsView::uninstall); + connect(m_link, &QPushButton::pressed, this, &PluginSettingsView::openLink); - connect(m_install, &QPushButton::pressed, this, &PluginSettingsView::install); + connect(m_uninstall, &QPushButton::pressed, this, &PluginSettingsView::uninstall); - connect(m_update, &QPushButton::pressed, this, &PluginSettingsView::update); + connect(m_install, &QPushButton::pressed, this, &PluginSettingsView::install); - connect(m_updateAll, &QPushButton::pressed, this, &PluginSettingsView::updateAll); + connect(m_update, &QPushButton::pressed, this, &PluginSettingsView::update); - connect(&mgr, &QNetworkAccessManager::finished, this, &PluginSettingsView::on_message); + connect(m_updateAll, &QPushButton::pressed, this, &PluginSettingsView::updateAll); - refresh(); -} + connect( + &mgr, &QNetworkAccessManager::finished, this, &PluginSettingsView::on_message); -QWidget* PluginSettingsView::getWidget() -{ - return m_widget; -} + m_addonsOnSystem->setSortingEnabled(true); -void PluginSettingsView::firstTimeLibraryDownload() -{ - const auto& lib = score::GUIAppContext().settings(); - const QString lib_folder = lib.getPackagesPath() + "/default"; - const QString lib_info = lib_folder + "/package.json"; - if(QFile file{lib_info}; !file.exists()) + m_remoteAddons->setSortingEnabled(true); + + refresh(); + } + + QWidget* PluginSettingsView::getWidget() { - auto dl = score::question( - qApp->activeWindow(), tr("Download the user library ?"), - tr("The user library has not been found. \n" - "Do you want to download it from the internet ? \n\n" - "Note: you can always download it later from : \n" - "https://github.com/ossia/score-user-library")); - - if(dl == QMessageBox::Yes) + return m_widget; + } + + void PluginSettingsView::firstTimeLibraryDownload() + { + const auto& lib = score::GUIAppContext().settings(); + const QString lib_folder = lib.getPackagesPath() + "/default"; + const QString lib_info = lib_folder + "/package.json"; + if(QFile file{lib_info}; !file.exists()) { - zdl::download_and_extract( - QUrl{"https://github.com/ossia/score-user-library/archive/master.zip"}, - lib.getPackagesPath(), - [](const auto&) mutable { - auto& lib = score::GUIAppContext().settings(); - QDir packages_dir{lib.getPackagesPath()}; - packages_dir.rename("score-user-library-master", "default"); - - lib.rescanLibrary(); - }, - [](qint64 bytesReceived, qint64 bytesTotal) { - qDebug() << (((bytesReceived / 1024.) / (bytesTotal / 1024.)) * 100) - << "% downloaded"; - }, - [] {}); + auto dl = score::question( + qApp->activeWindow(), tr("Download the user library ?"), + tr("The user library has not been found. \n" + "Do you want to download it from the internet ? \n\n" + "Note: you can always download it later from : \n" + "https://github.com/ossia/score-user-library")); + + if(dl == QMessageBox::Yes) + { + zdl::download_and_extract( + QUrl{"https://github.com/ossia/score-user-library/archive/master.zip"}, + lib.getPackagesPath(), + [](const auto&) mutable { + auto& lib = score::GUIAppContext().settings(); + QDir packages_dir{lib.getPackagesPath()}; + packages_dir.rename("score-user-library-master", "default"); + + lib.rescanLibrary(); + }, + [](qint64 bytesReceived, qint64 bytesTotal) { + qDebug() << (((bytesReceived / 1024.) / (bytesTotal / 1024.)) * 100) + << "% downloaded"; + }, + [] {}); + } + } + else + { + checkAll(); } } - else + + void PluginSettingsView::refresh() { - checkAll(); + if(qEnvironmentVariableIsSet("SCORE_SANITIZE_SKIP_CHECKS")) + return; + QNetworkRequest rqst{ + QUrl("https://raw.githubusercontent.com/ossia/score-addons/refs/heads/" + "add-ai-models/addons.json")}; + mgr.get(rqst); } -} - -void PluginSettingsView::refresh() -{ - if(qEnvironmentVariableIsSet("SCORE_SANITIZE_SKIP_CHECKS")) - return; - QNetworkRequest rqst{ - QUrl("https://raw.githubusercontent.com/ossia/score-addons/refs/heads/" - "add-ai-models/addons.json")}; - mgr.get(rqst); -} -void PluginSettingsView::handleAddonList(const QJsonObject& obj) -{ - m_progress->setVisible(true); - auto arr = obj["addons"].toArray(); - m_addonsToRetrieve = arr.size(); - int delay = 0; - for(QJsonValue elt : arr) + void PluginSettingsView::handleAddonList(const QJsonObject& obj) { - QTimer::singleShot(delay, this, [this, url = QUrl(elt.toString())] { - QNetworkRequest rqst{url}; - mgr.get(rqst); - }); - delay += 16; + m_progress->setVisible(true); + auto arr = obj["addons"].toArray(); + m_addonsToRetrieve = arr.size(); + int delay = 0; + for(QJsonValue elt : arr) + { + QTimer::singleShot(delay, this, [this, url = QUrl(elt.toString())] { + QNetworkRequest rqst{url}; + mgr.get(rqst); + }); + delay += 16; + } } -} - -void PluginSettingsView::handleAddon(const QJsonObject& obj) -{ - RemotePackagesModel* model - = static_cast(m_remoteAddons->model()); - - if(m_addonsToRetrieve == std::ssize(model->m_vec)) - reset_progress(); - else - m_progress->setValue(m_progress->value() + (100.0 / m_addonsToRetrieve)); - - auto addon = Package::fromJson(obj); - if(!addon) - return; - - auto& add = *addon; - // Load images - if(!add.smallImagePath.isEmpty()) + void PluginSettingsView::handleAddon(const QJsonObject& obj) { - // c.f. https://wiki.qt.io/Download_Data_from_URL - auto dl = new score::FileDownloader{QUrl{add.smallImagePath}}; - connect(dl, &score::FileDownloader::downloaded, this, [=](QByteArray arr) { - model->updateAddon( - add.key, [=](Package& add) { add.smallImage.loadFromData(arr); }); - dl->deleteLater(); - }); - } + if(m_addonsToRetrieve == std::ssize(m_remoteModel->m_vec)) + reset_progress(); + else + m_progress->setValue(m_progress->value() + (100.0 / m_addonsToRetrieve)); - if(!add.largeImagePath.isEmpty()) - { - // c.f. https://wiki.qt.io/Download_Data_from_URL - auto dl = new score::FileDownloader{QUrl{add.largeImagePath}}; - connect(dl, &score::FileDownloader::downloaded, this, [=](QByteArray arr) { - model->updateAddon( - add.key, [=](Package& add) { add.largeImage.loadFromData(arr); }); + auto addon = Package::fromJson(obj); + if(!addon) + return; - dl->deleteLater(); - }); - } + auto& add = *addon; - model->addAddon(std::move(add)); -} + // Load images + if(!add.smallImagePath.isEmpty()) + { + // c.f. https://wiki.qt.io/Download_Data_from_URL + auto dl = new score::FileDownloader{QUrl{add.smallImagePath}}; + connect(dl, &score::FileDownloader::downloaded, this, [=](QByteArray arr) { + m_remoteModel->updateAddon( + add.key, [=](Package& add) { add.smallImage.loadFromData(arr); }); + + dl->deleteLater(); + }); + } -void PluginSettingsView::on_message(QNetworkReply* rep) -{ - auto res = rep->readAll(); - auto json = QJsonDocument::fromJson(res).object(); + if(!add.largeImagePath.isEmpty()) + { + // c.f. https://wiki.qt.io/Download_Data_from_URL + auto dl = new score::FileDownloader{QUrl{add.largeImagePath}}; + connect(dl, &score::FileDownloader::downloaded, this, [=](QByteArray arr) { + m_remoteModel->updateAddon( + add.key, [=](Package& add) { add.largeImage.loadFromData(arr); }); + + dl->deleteLater(); + }); + } - if(json.contains("addons")) - { - handleAddonList(json); + m_remoteModel->addAddon(std::move(add)); } - else if(json.contains("name")) - { - handleAddon(json); - } - else - { - qDebug() << rep->request().url().toString() << ' ' << res; - reset_progress(); - } - rep->deleteLater(); - if(m_install->isVisible()) - updateCategoryComboBox(1); - else - updateCategoryComboBox(0); - - if(!m_firstTimeCheck) + void PluginSettingsView::on_message(QNetworkReply * rep) { - m_firstTimeCheck = true; - QTimer::singleShot(3000, this, &PluginSettingsView::firstTimeLibraryDownload); - } -} + auto res = rep->readAll(); + auto json = QJsonDocument::fromJson(res).object(); -// the install button set to visible means we are browsing -PackagesModel* PluginSettingsView::getCurrentModel() -{ - if(m_install->isVisible()) - return static_cast(m_remoteAddons->model()); - else - return static_cast(m_addonsOnSystem->model()); -} + if(json.contains("addons")) + { + handleAddonList(json); + } + else if(json.contains("name")) + { + handleAddon(json); + } + else + { + qDebug() << rep->request().url().toString() << ' ' << res; + reset_progress(); + } + rep->deleteLater(); -int PluginSettingsView::getCurrentRow(const QTableView* t = nullptr) -{ - QModelIndexList rows{}; + if(!m_firstTimeCheck) + { + m_firstTimeCheck = true; + QTimer::singleShot(3000, this, &PluginSettingsView::firstTimeLibraryDownload); + } + } - if(t) - rows = t->selectionModel()->selectedRows(0); - else + // the install button set to visible means we are browsing + PackagesModel* PluginSettingsView::getCurrentModel() { if(m_install->isVisible()) - rows = m_remoteAddons->selectionModel()->selectedRows(0); + return m_remoteModel; else - rows = m_addonsOnSystem->selectionModel()->selectedRows(0); + return m_localModel; } - if(rows.isEmpty()) - return -1; - - return rows.first().row(); -} - -Package PluginSettingsView::selectedPackage(const PackagesModel* model, int row) -{ - if(row == -1) - return {}; - - SCORE_ASSERT(int(model->addons().size()) > row); - - return model->addons().at(row); -} - -void PluginSettingsView::openLink() -{ - const auto& addon = selectedPackage(getCurrentModel(), getCurrentRow()); - - QDesktopServices::openUrl(addon.url); -} - -void PluginSettingsView::install_package(const Package& addon) -{ - if(addon.kind == "addon" || addon.kind == "nodes") - installAddon(addon); - else if(addon.kind == "sdk") - installSDK(); - else if(addon.kind == "media") - installLibrary(addon); - else if(addon.kind == "presets") - installLibrary(addon); -} + int PluginSettingsView::getCurrentRow(const QTableView* t = nullptr) + { + QModelIndexList rows{}; -void PluginSettingsView::install() -{ - const auto& addon = selectedPackage( - static_cast(m_remoteAddons->model()), - getCurrentRow(m_remoteAddons)); + if(t) + rows = t->selectionModel()->selectedRows(0); + else + { + if(m_install->isVisible()) + rows = m_remoteAddons->selectionModel()->selectedRows(0); + else + rows = m_addonsOnSystem->selectionModel()->selectedRows(0); + } - m_progress->setVisible(true); + if(rows.isEmpty()) + return -1; - install_package(addon); -} + return rows.first().row(); + } -void PluginSettingsView::uninstall() -{ - const auto& addon = selectedPackage( - static_cast(m_addonsOnSystem->model()), - getCurrentRow(m_addonsOnSystem)); + Package PluginSettingsView::selectedPackage(const PackagesModel* model, int row) + { + if(row == -1) + return {}; - bool success{false}; + SCORE_ASSERT(int(model->addons().size()) > row); - const auto& library{score::AppContext().settings()}; + return model->addons().at(row); + } - if(addon.kind == "addon" || addon.kind == "nodes" || addon.kind == "media") + void PluginSettingsView::openLink() { - success = QDir{library.getPackagesPath() + '/' + addon.raw_name}.removeRecursively(); + const auto& addon = selectedPackage(getCurrentModel(), getCurrentRow()); + + QDesktopServices::openUrl(addon.url); } - else if(addon.kind == "sdk") + + void PluginSettingsView::install_package(const Package& addon) { - success = QDir{library.getSDKPath()}.removeRecursively(); + if(addon.kind == "addon" || addon.kind == "nodes") + installAddon(addon); + else if(addon.kind == "sdk") + installSDK(); + else if(addon.kind == "media") + installLibrary(addon); + else if(addon.kind == "presets") + installLibrary(addon); } - if(success) + void PluginSettingsView::install() { - const auto& localPlugins - = static_cast(m_addonsOnSystem->model()); + const auto& addon = selectedPackage(m_remoteModel, getCurrentRow(m_remoteAddons)); - localPlugins->removeAddon(addon); - set_info(); + m_progress->setVisible(true); + + install_package(addon); } -} -void PluginSettingsView::update() -{ - auto local_model = static_cast(m_addonsOnSystem->model()); - auto remote_model = static_cast(m_remoteAddons->model()); - const auto& addon = selectedPackage(local_model, getCurrentRow(m_addonsOnSystem)); - - auto key = addon.key; - auto it = ossia::find_if( - remote_model->addons(), [&](auto& pkg) { return pkg.key == addon.key; }); - if(it == remote_model->addons().end()) + void PluginSettingsView::uninstall() { - qDebug() << "Addon " << addon.name << "not found on the server!"; - return; - } + const auto& addon = selectedPackage(m_localModel, getCurrentRow(m_addonsOnSystem)); - m_progress->setVisible(true); - install_package(*it); -} + bool success{false}; -void PluginSettingsView::checkAll() -{ - auto local_model = static_cast(m_addonsOnSystem->model()); - auto remote_model = static_cast(m_remoteAddons->model()); + const auto& library{score::AppContext().settings()}; - std::vector to_update; - for(auto& addon : local_model->addons()) - { - auto key = addon.key; - auto it = ossia::find_if( - remote_model->addons(), [&](auto& pkg) { return pkg.key == addon.key; }); - if(it == remote_model->addons().end()) + if(addon.kind == "addon" || addon.kind == "nodes" || addon.kind == "media") { - qDebug() << "Addon " << addon.name << "not found on the server!"; - continue; + success + = QDir{library.getPackagesPath() + '/' + addon.raw_name}.removeRecursively(); + } + else if(addon.kind == "sdk") + { + success = QDir{library.getSDKPath()}.removeRecursively(); } - if(it->version <= addon.version) - continue; - - to_update.push_back(&*it); - } - - if(!to_update.empty()) - { - QString s = tr("Some installed packages are out-of-date: \n"); - for(auto pkg : to_update) + if(success) { - s += tr("- %1 (version %3)\n").arg(pkg->name).arg(pkg->version); + m_localModel->removeAddon(addon); + set_info(); } - s += tr("Head to Settings > Packages to update them"); - score::information(qApp->activeWindow(), tr("Packages can be updated"), s); } -} -void PluginSettingsView::updateAll() -{ - auto local_model = static_cast(m_addonsOnSystem->model()); - auto remote_model = static_cast(m_remoteAddons->model()); - - for(auto& addon : local_model->addons()) + void PluginSettingsView::update() { + const auto& addon = selectedPackage(m_localModel, getCurrentRow(m_addonsOnSystem)); + auto key = addon.key; auto it = ossia::find_if( - remote_model->addons(), [&](auto& pkg) { return pkg.key == addon.key; }); - if(it == remote_model->addons().end()) + m_remoteModel->addons(), [&](auto& pkg) { return pkg.key == addon.key; }); + if(it == m_remoteModel->addons().end()) { qDebug() << "Addon " << addon.name << "not found on the server!"; - continue; + return; } - if(it->version <= addon.version) - continue; - m_progress->setVisible(true); install_package(*it); } -} -void PluginSettingsView::installAddon(const Package& addon) -{ - if(addon.files.empty()) + void PluginSettingsView::checkAll() { - reset_progress(); - return; + + std::vector to_update; + for(auto& addon : m_localModel->addons()) + { + auto key = addon.key; + auto it = ossia::find_if( + m_remoteModel->addons(), [&](auto& pkg) { return pkg.key == addon.key; }); + if(it == m_remoteModel->addons().end()) + { + qDebug() << "Addon " << addon.name << "not found on the server!"; + continue; + } + + if(it->version <= addon.version) + continue; + + to_update.push_back(&*it); + } + + if(!to_update.empty()) + { + QString s = tr("Some installed packages are out-of-date: \n"); + for(auto pkg : to_update) + { + s += tr("- %1 (version %3)\n").arg(pkg->name).arg(pkg->version); + } + s += tr("Head to Settings > Packages to update them"); + score::information(qApp->activeWindow(), tr("Packages can be updated"), s); + } } - const QString& installPath - = score::AppContext().settings().getPackagesPath(); - for(auto f : addon.files) - zdl::download_and_extract( - f, QDir{installPath}.absolutePath(), - [this, installPath, addon](const std::vector& res) { - reset_progress(); - if(res.empty()) - return; - // We want the extracted folder to have the name of the addon + void PluginSettingsView::updateAll() + { + for(auto& addon : m_localModel->addons()) + { + auto key = addon.key; + auto it = ossia::find_if( + m_remoteModel->addons(), [&](auto& pkg) { return pkg.key == addon.key; }); + if(it == m_remoteModel->addons().end()) { - QDir addons_dir{installPath}; - QFileInfo a_file(res[0]); - auto d = a_file.dir(); - auto old_d = d; - while(d.cdUp() && !d.isRoot()) + qDebug() << "Addon " << addon.name << "not found on the server!"; + continue; + } + + if(it->version <= addon.version) + continue; + + m_progress->setVisible(true); + install_package(*it); + } + } + + void PluginSettingsView::installAddon(const Package& addon) + { + if(addon.files.empty()) + { + reset_progress(); + return; + } + + const QString& installPath + = score::AppContext().settings().getPackagesPath(); + for(auto f : addon.files) + zdl::download_and_extract( + f, QDir{installPath}.absolutePath(), + [this, installPath, addon](const std::vector& res) { + reset_progress(); + if(res.empty()) + return; + // We want the extracted folder to have the name of the addon { - if(d == addons_dir) + QDir addons_dir{installPath}; + QFileInfo a_file(res[0]); + auto d = a_file.dir(); + auto old_d = d; + while(d.cdUp() && !d.isRoot()) { - addons_dir.rename(old_d.dirName(), addon.raw_name); - break; + if(d == addons_dir) + { + addons_dir.rename(old_d.dirName(), addon.raw_name); + break; + } + old_d = d; } - old_d = d; } - } - QMessageBox::information( - m_widget, tr("Addon downloaded"), - tr("The addon %1 has been successfully installed in :\n" - "%2\n\n" - "It will be built and enabled shortly.\nCheck the message " - "console for errors if nothing happens.") - .arg(addon.name) - .arg(QFileInfo(installPath).absoluteFilePath())); + QMessageBox::information( + m_widget, tr("Addon downloaded"), + tr("The addon %1 has been successfully installed in :\n" + "%2\n\n" + "It will be built and enabled shortly.\nCheck the message " + "console for errors if nothing happens.") + .arg(addon.name) + .arg(QFileInfo(installPath).absoluteFilePath())); }, [this](qint64 received, qint64 total) { progress_from_bytes(received, total); }, - [this, addon] { - reset_progress(); - QMessageBox::warning( - m_widget, tr("Download failed"), - tr("The package %1 could not be downloaded.").arg(addon.name)); - }); -} + [this, addon] { + reset_progress(); + QMessageBox::warning( + m_widget, tr("Download failed"), + tr("The package %1 could not be downloaded.").arg(addon.name)); + }); + } -void PluginSettingsView::installSDK() -{ - constexpr const char* platform = + void PluginSettingsView::installSDK() + { + constexpr const char* platform = #if defined(_WIN32) - "windows" + "windows" #elif defined(__APPLE__) - "mac" + "mac" #else - "linux" + "linux" #endif - ; + ; - const QString sdk_path{ - score::AppContext().settings().getSDKPath() + '/' - + SCORE_TAG_NO_V}; - QDir{}.mkpath(sdk_path); + const QString sdk_path{ + score::AppContext().settings().getSDKPath() + '/' + + SCORE_TAG_NO_V}; + QDir{}.mkpath(sdk_path); - const QUrl sdk_url - = QString("https://github.com/ossia/score/releases/download/%1/%2-sdk.zip") - .arg(SCORE_TAG) - .arg(platform); + const QUrl sdk_url + = QString("https://github.com/ossia/score/releases/download/%1/%2-sdk.zip") + .arg(SCORE_TAG) + .arg(platform); - zdl::download_and_extract( - sdk_url, QFileInfo{sdk_path}.absoluteFilePath(), - [this](const std::vector& res) { - reset_progress(); - if(res.empty()) - return; + zdl::download_and_extract( + sdk_url, QFileInfo{sdk_path}.absoluteFilePath(), + [this](const std::vector& res) { + reset_progress(); + if(res.empty()) + return; - QMessageBox::information( - m_widget, tr("SDK downloaded"), - tr("The SDK has been successfully installed in the user library.")); + QMessageBox::information( + m_widget, tr("SDK downloaded"), + tr("The SDK has been successfully installed in the user library.")); - set_info(); - }, - [this](qint64 received, qint64 total) { progress_from_bytes(received, total); }, - [this] { - reset_progress(); - QMessageBox::warning( - m_widget, tr("Download failed"), tr("The SDK could not be downloaded.")); - }); -} + set_info(); + }, + [this](qint64 received, qint64 total) { progress_from_bytes(received, total); }, + [this] { + reset_progress(); + QMessageBox::warning( + m_widget, tr("Download failed"), tr("The SDK could not be downloaded.")); + }); + } -void PluginSettingsView::installLibrary(const Package& addon) -{ - const QString destination{ - score::AppContext().settings().getPackagesPath() + "/" - + addon.raw_name}; + void PluginSettingsView::installLibrary(const Package& addon) + { + const QString destination{ + score::AppContext().settings().getPackagesPath() + "/" + + addon.raw_name}; - if(QDir dest{destination}; dest.exists()) - dest.removeRecursively(); + if(QDir dest{destination}; dest.exists()) + dest.removeRecursively(); - QDir{}.mkpath(destination); + QDir{}.mkpath(destination); - for(auto f : addon.files) - zdl::download_and_extract( - f, QFileInfo{destination}.absoluteFilePath(), - [this, addon, destination](const std::vector& res) { - on_packageInstallSuccess(addon, destination, res); + for(auto f : addon.files) + zdl::download_and_extract( + f, QFileInfo{destination}.absoluteFilePath(), + [this, addon, destination](const std::vector& res) { + on_packageInstallSuccess(addon, destination, res); }, [this](qint64 received, qint64 total) { progress_from_bytes(received, total); }, - [this, addon] { on_packageInstallFailure(addon); }); -} + [this, addon] { on_packageInstallFailure(addon); }); + } -void PluginSettingsView::on_packageInstallSuccess( - const Package& addon, const QDir& destination, const std::vector& res) -{ - reset_progress(); - if(res.empty()) - return; - - // Often zip files contain a single, empty directory. - // In that case, we move everything up a level to make the library cleaner. - QDir dir{destination}; - auto files = dir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); - if(files.size() == 1) + void PluginSettingsView::on_packageInstallSuccess( + const Package& addon, const QDir& destination, const std::vector& res) { - auto child = files[0]; - QFileInfo info{dir.absoluteFilePath(child)}; - if(info.isDir()) - { - dir.rename(child, "___score_tmp___"); - QDir subdir{dir.absoluteFilePath("___score_tmp___")}; + reset_progress(); + if(res.empty()) + return; - for(auto& entry : - subdir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) + // Often zip files contain a single, empty directory. + // In that case, we move everything up a level to make the library cleaner. + QDir dir{destination}; + auto files = dir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); + if(files.size() == 1) + { + auto child = files[0]; + QFileInfo info{dir.absoluteFilePath(child)}; + if(info.isDir()) { - dir.rename( - QString{"___score_tmp___%1%2"}.arg(QDir::separator()).arg(entry), entry); - } + dir.rename(child, "___score_tmp___"); + QDir subdir{dir.absoluteFilePath("___score_tmp___")}; - subdir.removeRecursively(); + for(auto& entry : + subdir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) + { + dir.rename( + QString{"___score_tmp___%1%2"}.arg(QDir::separator()).arg(entry), entry); + } + + subdir.removeRecursively(); + } } - } - { - QFile f{dir.absoluteFilePath("package.json")}; - if(f.open(QIODevice::WriteOnly)) { - QJsonObject obj; - obj["name"] = addon.name; - obj["raw_name"] = addon.raw_name; - obj["version"] = addon.version; - obj["kind"] = addon.kind; - obj["url"] = addon.url; - obj["short"] = addon.shortDescription; - obj["long"] = addon.longDescription; - obj["size"] = addon.size; - // TODO images - obj["key"] = QString{score::uuids::toByteArray(addon.key.impl())}; - - f.write(QJsonDocument{obj}.toJson()); + QFile f{dir.absoluteFilePath("package.json")}; + if(f.open(QIODevice::WriteOnly)) + { + QJsonObject obj; + obj["name"] = addon.name; + obj["raw_name"] = addon.raw_name; + obj["version"] = addon.version; + obj["kind"] = addon.kind; + obj["url"] = addon.url; + obj["short"] = addon.shortDescription; + obj["long"] = addon.longDescription; + obj["size"] = addon.size; + // TODO images + obj["key"] = QString{score::uuids::toByteArray(addon.key.impl())}; + + f.write(QJsonDocument{obj}.toJson()); + } } - } - - QMessageBox::information( - m_widget, tr("Package downloaded"), - tr("The package %1 has been successfully installed in the user library.") - .arg(addon.name)); - - auto& localPlugins = *static_cast(m_addonsOnSystem->model()); - - localPlugins.registerAddon(dir.absolutePath()); - set_info(); -} - -void PluginSettingsView::on_packageInstallFailure(const Package& addon) -{ - reset_progress(); - QMessageBox::warning( - m_widget, tr("Download failed"), - tr("The package %1 could not be downloaded.").arg(addon.name)); -} - -void PluginSettingsView::set_info() -{ - m_storage->setText( - QString::number(storage.bytesAvailable() / 1024.0 / 1024.0 / 1024) + " G\n" - + tr("available on volume")); -}; - -void PluginSettingsView::reset_progress() -{ - m_progress->setHidden(true); - m_progress->setValue(0); -} - -void PluginSettingsView::progress_from_bytes(qint64 bytesReceived, qint64 bytesTotal) -{ - m_progress->setValue(((bytesReceived / 1024.) / (bytesTotal / 1024.)) * 100); -} -void PluginSettingsView::updateCategoryComboBox(int tabIndex) -{ - categoryComboBox->clear(); - categoryComboBox->addItem("All"); + QMessageBox::information( + m_widget, tr("Package downloaded"), + tr("The package %1 has been successfully installed in the user library.") + .arg(addon.name)); - PackagesModel* model = nullptr; - if(tabIndex == 0) // Local packages tab - { - model = static_cast(m_addonsOnSystem->model()); + m_localModel->registerAddon(dir.absolutePath()); + set_info(); } - else if(tabIndex == 1) // Remote packages tab + + void PluginSettingsView::on_packageInstallFailure(const Package& addon) { - model = static_cast(m_remoteAddons->model()); + reset_progress(); + QMessageBox::warning( + m_widget, tr("Download failed"), + tr("The package %1 could not be downloaded.").arg(addon.name)); } - // If the model exists, add uniqueKind - if(model) + void PluginSettingsView::set_info() { - QList uniqueKinds; + m_storage->setText( + QString::number(storage.bytesAvailable() / 1024.0 / 1024.0 / 1024) + " G\n" + + tr("available on volume")); + }; - for(const auto& addon : model->addons()) - { - if(!uniqueKinds.contains(addon.kind)) - { - uniqueKinds.append(addon.kind); - } - } + void PluginSettingsView::reset_progress() + { + m_progress->setHidden(true); + m_progress->setValue(0); + } - for(const QString& kind : uniqueKinds) - { - categoryComboBox->addItem(kind); - } + void PluginSettingsView::progress_from_bytes(qint64 bytesReceived, qint64 bytesTotal) + { + m_progress->setValue(((bytesReceived / 1024.) / (bytesTotal / 1024.)) * 100); } } -} diff --git a/src/plugins/score-plugin-packagemanager/PackageManager/View.hpp b/src/plugins/score-plugin-packagemanager/PackageManager/View.hpp index 5380885f55..f200560d4f 100644 --- a/src/plugins/score-plugin-packagemanager/PackageManager/View.hpp +++ b/src/plugins/score-plugin-packagemanager/PackageManager/View.hpp @@ -22,6 +22,8 @@ class QObject; namespace PM { class PluginSettingsPresenter; +class LocalPackagesModel; +class RemotePackagesModel; class PluginSettingsView : public score::GlobalSettingsView { W_OBJECT(PluginSettingsView) @@ -35,6 +37,9 @@ class PluginSettingsView : public score::GlobalSettingsView QWidget* getWidget() override; + RemotePackagesModel* m_remoteModel{}; + LocalPackagesModel* m_localModel{}; + private: void firstTimeLibraryDownload(); From f8d6551380d55411714722b5426d7badd4865942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Tue, 11 Feb 2025 10:56:28 -0500 Subject: [PATCH 5/6] selection: fix row selection when sorted --- .../PackageManager/Model.cpp | 1 - .../PackageManager/Model.hpp | 3 --- .../PackageManager/Presenter.cpp | 14 +++++++++----- .../PackageManager/View.cpp | 1 - 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/plugins/score-plugin-packagemanager/PackageManager/Model.cpp b/src/plugins/score-plugin-packagemanager/PackageManager/Model.cpp index 16b6d769a2..ef321d7bd9 100644 --- a/src/plugins/score-plugin-packagemanager/PackageManager/Model.cpp +++ b/src/plugins/score-plugin-packagemanager/PackageManager/Model.cpp @@ -21,7 +21,6 @@ PluginSettingsModel::PluginSettingsModel( QSettings& set, const score::ApplicationContext& ctx) : score::SettingsDelegateModel{} , localPlugins{ctx} - , remoteSelection{&remotePlugins} { } diff --git a/src/plugins/score-plugin-packagemanager/PackageManager/Model.hpp b/src/plugins/score-plugin-packagemanager/PackageManager/Model.hpp index 7912f7303d..220fed0fd6 100644 --- a/src/plugins/score-plugin-packagemanager/PackageManager/Model.hpp +++ b/src/plugins/score-plugin-packagemanager/PackageManager/Model.hpp @@ -1,8 +1,6 @@ #pragma once #include -#include - #include class QAbstractItemModel; @@ -19,6 +17,5 @@ class PluginSettingsModel : public score::SettingsDelegateModel LocalPackagesModel localPlugins; RemotePackagesModel remotePlugins; - QItemSelectionModel remoteSelection; }; } diff --git a/src/plugins/score-plugin-packagemanager/PackageManager/Presenter.cpp b/src/plugins/score-plugin-packagemanager/PackageManager/Presenter.cpp index 7dba13f9b9..5a3983293e 100644 --- a/src/plugins/score-plugin-packagemanager/PackageManager/Presenter.cpp +++ b/src/plugins/score-plugin-packagemanager/PackageManager/Presenter.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -65,14 +66,17 @@ PluginSettingsPresenter::PluginSettingsPresenter( ps_view.remoteView()->setColumnWidth(4, 30); ps_view.remoteView()->horizontalHeader()->setStretchLastSection(true); - // ps_view.remoteView()->setSelectionModel(&ps_model.remoteSelection); + auto selection = new QItemSelectionModel{remoteProxyModel, this}; + ps_view.remoteView()->setSelectionModel(selection); connect( - &ps_model.remoteSelection, &QItemSelectionModel::currentRowChanged, this, - [&](const QModelIndex& current, const QModelIndex& previous) { - Package& addon = ps_model.remotePlugins.addons().at(current.row()); + selection, &QItemSelectionModel::currentRowChanged, this, + [remoteProxyModel, &ps_model, + &ps_view](const QModelIndex& current, const QModelIndex& previous) { + auto selected = remoteProxyModel->mapToSource(current); + Package& addon = ps_model.remotePlugins.addons().at(selected.row()); ps_view.installButton().setEnabled(!addon.files.empty() || addon.kind == "sdk"); - }); + }); ps_view.installButton().setEnabled(false); diff --git a/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp b/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp index e9b35f02c5..c70e107c3f 100644 --- a/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp +++ b/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp @@ -443,7 +443,6 @@ namespace PM void PluginSettingsView::checkAll() { - std::vector to_update; for(auto& addon : m_localModel->addons()) { From abedc99f3edb468073d8057773b34e3aa5c266ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Tue, 11 Feb 2025 11:24:53 -0500 Subject: [PATCH 6/6] packages view: further fix for filtered and sorted view --- .../PackageManager/View.cpp | 44 +++++++++++-------- .../PackageManager/View.hpp | 4 +- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp b/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp index c70e107c3f..a6bda4ca8c 100644 --- a/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp +++ b/src/plugins/score-plugin-packagemanager/PackageManager/View.cpp @@ -333,32 +333,36 @@ namespace PM } // the install button set to visible means we are browsing - PackagesModel* PluginSettingsView::getCurrentModel() + std::pair + PluginSettingsView::getCurrentModelAndTable() const noexcept { if(m_install->isVisible()) - return m_remoteModel; + return {m_remoteModel, m_remoteAddons}; else - return m_localModel; + return {m_localModel, m_addonsOnSystem}; } - int PluginSettingsView::getCurrentRow(const QTableView* t = nullptr) + int PluginSettingsView::getCurrentRow( + QAbstractItemModel* sourceModel, const QTableView* t) { QModelIndexList rows{}; + SCORE_ASSERT(sourceModel); + SCORE_ASSERT(t); - if(t) - rows = t->selectionModel()->selectedRows(0); - else - { - if(m_install->isVisible()) - rows = m_remoteAddons->selectionModel()->selectedRows(0); - else - rows = m_addonsOnSystem->selectionModel()->selectedRows(0); - } + // What did we select on the table + rows = t->selectionModel()->selectedRows(0); if(rows.isEmpty()) return -1; - return rows.first().row(); + auto row = rows.first(); + // Map from the view's model (QSortFilter...) to the actual data model (Remote or LocalModel) + auto sfpm = static_cast(t->model()); + auto index_in_filter = sfpm->mapToSource(row); + if(!index_in_filter.isValid()) + return -1; + + return index_in_filter.row(); } Package PluginSettingsView::selectedPackage(const PackagesModel* model, int row) @@ -373,7 +377,8 @@ namespace PM void PluginSettingsView::openLink() { - const auto& addon = selectedPackage(getCurrentModel(), getCurrentRow()); + auto [model, table] = getCurrentModelAndTable(); + const auto& addon = selectedPackage(model, getCurrentRow(model, table)); QDesktopServices::openUrl(addon.url); } @@ -392,7 +397,8 @@ namespace PM void PluginSettingsView::install() { - const auto& addon = selectedPackage(m_remoteModel, getCurrentRow(m_remoteAddons)); + const auto& addon + = selectedPackage(m_remoteModel, getCurrentRow(m_remoteModel, m_remoteAddons)); m_progress->setVisible(true); @@ -401,7 +407,8 @@ namespace PM void PluginSettingsView::uninstall() { - const auto& addon = selectedPackage(m_localModel, getCurrentRow(m_addonsOnSystem)); + const auto& addon + = selectedPackage(m_localModel, getCurrentRow(m_localModel, m_addonsOnSystem)); bool success{false}; @@ -426,7 +433,8 @@ namespace PM void PluginSettingsView::update() { - const auto& addon = selectedPackage(m_localModel, getCurrentRow(m_addonsOnSystem)); + const auto& addon + = selectedPackage(m_localModel, getCurrentRow(m_localModel, m_addonsOnSystem)); auto key = addon.key; auto it = ossia::find_if( diff --git a/src/plugins/score-plugin-packagemanager/PackageManager/View.hpp b/src/plugins/score-plugin-packagemanager/PackageManager/View.hpp index f200560d4f..a2102cd6a5 100644 --- a/src/plugins/score-plugin-packagemanager/PackageManager/View.hpp +++ b/src/plugins/score-plugin-packagemanager/PackageManager/View.hpp @@ -46,8 +46,8 @@ class PluginSettingsView : public score::GlobalSettingsView void handleAddonList(const QJsonObject&); void handleAddon(const QJsonObject&); - PackagesModel* getCurrentModel(); - int getCurrentRow(const QTableView* t); + std::pair getCurrentModelAndTable() const noexcept; + int getCurrentRow(QAbstractItemModel* sourceModel, const QTableView* t); Package selectedPackage(const PackagesModel* model, int row); void installAddon(const Package& addon);