diff --git a/src/plugins/extensionmanager/extensionmanager_test.qrc b/src/plugins/extensionmanager/extensionmanager_test.qrc index f8a11b4e084..e7e934e5316 100644 --- a/src/plugins/extensionmanager/extensionmanager_test.qrc +++ b/src/plugins/extensionmanager/extensionmanager_test.qrc @@ -1,5 +1,6 @@ + testdata/augmentedplugindata.json testdata/defaultpacks.json testdata/thirdpartyplugins.json testdata/varieddata.json diff --git a/src/plugins/extensionmanager/extensionsbrowser.cpp b/src/plugins/extensionmanager/extensionsbrowser.cpp index cd4c591097d..bfe4fb102f8 100644 --- a/src/plugins/extensionmanager/extensionsbrowser.cpp +++ b/src/plugins/extensionmanager/extensionsbrowser.cpp @@ -381,7 +381,7 @@ void ExtensionsBrowser::fetchExtensions() const auto onQueryDone = [this](const NetworkQuery &query, DoneWith result) { if (result != DoneWith::Success) { #ifdef WITH_TESTS - // Available test sets: "defaultpacks", "varieddata", "thirdpartyplugins" + // Available: "augmentedplugindata", "defaultpacks", "varieddata", "thirdpartyplugins" d->model->setExtensionsJson(testData("defaultpacks")); #endif // WITH_TESTS return; diff --git a/src/plugins/extensionmanager/extensionsmodel.cpp b/src/plugins/extensionmanager/extensionsmodel.cpp index 56fcc54ad8b..702e3019f75 100644 --- a/src/plugins/extensionmanager/extensionsmodel.cpp +++ b/src/plugins/extensionmanager/extensionsmodel.cpp @@ -36,8 +36,8 @@ using Dependencies = QList; struct Plugin { - Dependencies dependencies; QString copyright; + Dependencies dependencies; bool isInternal = false; QString name; QString packageUrl; @@ -69,23 +69,29 @@ struct Extension { }; using Extensions = QList; +static const Dependencies dependenciesFromJson(const QJsonObject &obj) +{ + const QJsonArray dependenciesArray = obj.value("Dependencies").toArray(); + Dependencies dependencies; + for (const QJsonValueConstRef &dependencyVal : dependenciesArray) { + const QJsonObject dependencyObj = dependencyVal.toObject(); + const QJsonObject metaDataObj = dependencyObj.value("meta_data").toObject(); + dependencies.append({ + .name = metaDataObj.value("Name").toString(), + .version = metaDataObj.value("Version").toString(), + }); + } + + return dependencies; +} + static Plugin pluginFromJson(const QJsonObject &obj) { const QJsonObject metaDataObj = obj.value("meta_data").toObject(); - const QJsonArray dependenciesArray = metaDataObj.value("Dependencies").toArray(); - Dependencies dependencies; - for (const QJsonValueConstRef &dependencyVal : dependenciesArray) { - const QJsonObject dependencyObj = dependencyVal.toObject(); - dependencies.append(Dependency{ - .name = dependencyObj.value("Name").toString(), - .version = dependencyObj.value("Version").toString(), - }); - } - return { - .dependencies = dependencies, .copyright = metaDataObj.value("Copyright").toString(), + .dependencies = dependenciesFromJson(metaDataObj), .isInternal = obj.value("is_internal").toBool(false), .name = metaDataObj.value("Name").toString(), .packageUrl = obj.value("url").toString(), @@ -192,30 +198,78 @@ static Extensions parseExtensionsRepoReply(const QByteArray &jsonData) return parsedExtensions; } +static Extension extensionFromPluginSpec(const PluginSpec *pluginSpec) +{ + const Dependencies dependencies = transform(pluginSpec->dependencies(), + [](const PluginDependency &pd) -> Dependency { + return { + .name = pd.name, + .version = pd.version, + }; + }); + const Plugin plugin = { + .copyright = pluginSpec->copyright(), + .dependencies = dependencies, + .name = pluginSpec->name(), + .packageUrl = {}, + .vendor = pluginSpec->vendor(), + .version = pluginSpec->version(), + }; + + const QStringList lines = pluginSpec->description().split('\n', Qt::SkipEmptyParts) + + pluginSpec->longDescription().split('\n', Qt::SkipEmptyParts); + const TextData text = {{ pluginSpec->name(), lines }}; + LinksData links; + if (const QString url = pluginSpec->url(); !url.isEmpty()) + links.append({{}, url}); + const Description description = { + .images = {}, + .links = links, + .text = text, + }; + + const QString platformsPattern = pluginSpec->platformSpecification().pattern(); + const QStringList platforms = platformsPattern.isEmpty() + ? QStringList({"macOS", "Windows", "Linux"}) + : QStringList(platformsPattern); + + const Extension extension = { + .copyright = pluginSpec->copyright(), + .description = description, + .id = {}, + .license = pluginSpec->license(), + .name = pluginSpec->name(), + .platforms = platforms, + .plugins = {plugin}, + .tags = {}, + .type = ItemTypeExtension, + .vendor = pluginSpec->vendor(), + .version = pluginSpec->version(), + }; + return extension; +} + class ExtensionsModelPrivate { public: void setExtensions(const Extensions &extensions); - void removeLocalExtensions(); + void addUnlistedLocalExtensions(); - Extensions allExtensions; // Original, complete extensions entries - Extensions absentExtensions; // All packs + plugin extensions that are not (yet) installed + Extensions extensions; }; void ExtensionsModelPrivate::setExtensions(const Extensions &extensions) { - allExtensions = extensions; - removeLocalExtensions(); + this->extensions = extensions; + addUnlistedLocalExtensions(); } -void ExtensionsModelPrivate::removeLocalExtensions() +void ExtensionsModelPrivate::addUnlistedLocalExtensions() { - const QStringList installedPlugins = transform(PluginManager::plugins(), &PluginSpec::name); - absentExtensions.clear(); - for (const Extension &extension : allExtensions) { - if (extension.type == ItemTypePack || !installedPlugins.contains(extension.name)) - absentExtensions.append(extension); - } + const QStringList listedModelExtensions = transform(extensions, &Extension::name); + for (const PluginSpec *plugin : PluginManager::plugins()) + if (!listedModelExtensions.contains(plugin->name())) + extensions.append(extensionFromPluginSpec(plugin)); } ExtensionsModel::ExtensionsModel(QObject *parent) @@ -231,66 +285,7 @@ ExtensionsModel::~ExtensionsModel() int ExtensionsModel::rowCount([[maybe_unused]] const QModelIndex &parent) const { - const int remoteExtnsionsCount = d->absentExtensions.count(); - const int installedPluginsCount = PluginManager::plugins().count(); - return remoteExtnsionsCount + installedPluginsCount; -} - -static QVariant dataFromPluginSpec(const PluginSpec *pluginSpec, int role) -{ - switch (role) { - case Qt::DisplayRole: - case RoleName: - return pluginSpec->name(); - case RoleCopyright: - return pluginSpec->copyright(); - case RoleDependencies: { - QStringList dependencies = transform(pluginSpec->dependencies(), - &PluginDependency::toString); - dependencies.sort(); - return dependencies; - } - case RoleDescriptionImages: - break; - case RoleDescriptionLinks: { - const QString url = pluginSpec->url(); - if (!url.isEmpty()) { - const LinksData links = {{{}, url}}; - return QVariant::fromValue(links); - } - break; - } - case RoleDescriptionText: { - QStringList lines = pluginSpec->description().split('\n', Qt::SkipEmptyParts); - lines.append(pluginSpec->longDescription().split('\n', Qt::SkipEmptyParts)); - const TextData text = {{ pluginSpec->name(), lines }}; - return QVariant::fromValue(text); - } - case RoleItemType: - return ItemTypeExtension; - case RoleLicense: - return pluginSpec->license(); - case RoleLocation: - return pluginSpec->filePath().toVariant(); - case RolePlatforms: { - const QString pattern = pluginSpec->platformSpecification().pattern(); - const QStringList platforms = pattern.isEmpty() - ? QStringList({"macOS", "Windows", "Linux"}) - : QStringList(pattern); - return platforms; - } - case RoleSize: - return pluginSpec->filePath().fileSize(); - case RoleTags: - break; - case RoleVendor: - return pluginSpec->vendor(); - case RoleVersion: - return pluginSpec->version(); - default: - break; - } - return {}; + return d->extensions.count(); } static QStringList dependenciesFromExtension(const Extension &extension) @@ -299,7 +294,7 @@ static QStringList dependenciesFromExtension(const Extension &extension) for (const Plugin &plugin : extension.plugins) { for (const Dependency &dependency : plugin.dependencies) { const QString withVersion = QString::fromLatin1("%1 (%2)").arg(dependency.name) - .arg(dependency.version); + .arg(dependency.version); dependencies.append(withVersion); } } @@ -371,27 +366,18 @@ QVariant ExtensionsModel::data(const QModelIndex &index, int role) const if (role == RoleSearchText) return searchText(index); - const bool itemIsLocalPlugin = index.row() >= d->absentExtensions.count(); - if (itemIsLocalPlugin) { - const PluginSpecs &pluginSpecs = PluginManager::plugins(); - const int pluginIndex = index.row() - d->absentExtensions.count(); - QTC_ASSERT(pluginIndex >= 0 && pluginIndex <= pluginSpecs.size(), return {}); - const PluginSpec *plugin = pluginSpecs.at(pluginIndex); - return dataFromPluginSpec(plugin, role); - } else { - const Extension &extension = d->absentExtensions.at(index.row()); - const QVariant extensionData = dataFromExtension(extension, role); - - // If data is unavailable, retrieve it from the first contained plugin - if (extensionData.isNull() && !extension.plugins.isEmpty()) { - const PluginSpec *pluginSpec = ExtensionsModel::pluginSpecForName( - extension.plugins.constFirst().name); - if (pluginSpec) - return dataFromPluginSpec(pluginSpec, role); - } - return extensionData; + const Extension &extension = d->extensions.at(index.row()); + const QVariant extensionData = dataFromExtension(extension, role); + // If data is unavailable, retrieve it from the first contained plugin + if (extensionData.isNull() && !extension.plugins.isEmpty()) { + const QString firstPluginName = extension.plugins.constFirst().name; + const Extension firstPluginExtension = + findOrDefault(d->extensions, Utils::equal(&Extension::name, firstPluginName)); + if (firstPluginExtension.name.isEmpty()) + return {}; + return dataFromExtension(firstPluginExtension, role); } - return {}; + return extensionData; } void ExtensionsModel::setExtensionsJson(const QByteArray &json) diff --git a/src/plugins/extensionmanager/testdata/augmentedplugindata.json b/src/plugins/extensionmanager/testdata/augmentedplugindata.json new file mode 100644 index 00000000000..32efe15b708 --- /dev/null +++ b/src/plugins/extensionmanager/testdata/augmentedplugindata.json @@ -0,0 +1,71 @@ +{ + "items": [ + { + "name": "ScreenRecorder", + "description": { + "paragraphs": [ + { + "header": "Screen Recorder plugin", + "text": [ + "With FFmpeg, you can record your screens and save the recordings as animated images or videos.", + "To record screens:", + "", + "- Select Tools > Screen Recording.", + "- Select to select the screen to record from and to set the recorded screen area.", + "- Select to start recording.", + "- Select when you are done recording.", + "- Select Crop and Trim to edit the recording.", + "- Select Export to save the recording as an animated image or a video." + ] + }, + { + "header": "Set the screen and area to record", + "text": [ + "Set the screen and the area to record in the Screen Recording Options dialog.", + "To select a screen and area:", + "", + "- In Display, select the display to record.", + "- In Recorded screen area, drag the guides to set the x and y coordinates of the starting point for the recording area, as well as the width and height of the area.", + "- Select OK to return to the Record Screen dialog." + ] + } + ], + "images": [ + { + "image_label": "Create animated imges like this", + "url": "https://bugreports.qt.io/secure/attachment/156058/156058_DragAndCopyOnLinux.gif" + } + ], + "links": [ + { + "link_text": "Documentation", + "url": "https://doc.qt.io/qtcreator/creator-how-to-record-screens.html" + }, + { + "link_text": "Homepage", + "url": "https://www.qt.io/" + } + ] + }, + "is_pack": false, + "plugins": [ + { + "meta_data": { + "Name": "ScreenRecorder", + "Dependencies": [ + { + "meta_data": { + "Name": "Core", + "Version": "13.0.2" + } + } + ], + "Version": "13.0.2" + } + } + ], + "tags": [ "Utility", "Docs" ], + "vendor": "The Qt Company Ltd" + } + ] +} diff --git a/src/plugins/extensionmanager/testdata/defaultpacks.json b/src/plugins/extensionmanager/testdata/defaultpacks.json index 0323b08d27e..d26b0a42c60 100644 --- a/src/plugins/extensionmanager/testdata/defaultpacks.json +++ b/src/plugins/extensionmanager/testdata/defaultpacks.json @@ -121,41 +121,6 @@ "plugins": [ { "meta_data": { "Name": "Designer" } } ] - }, - - { - "name": "SpellChecker", - "tags": [ "Editor" ], - "platforms": [ "macOS", "Windows", "Linux" ], - "license": "os", - "is_pack": false, - "description": { - "paragraphs": [ - { - "text": [ - "Spellcheck comments in source files." - ], - "header": "Get started" - } - ], - "links": [ - { - "url": "https://github.com/CJCombrink/SpellChecker-Plugin", - "link_text": "GitHub page" - } - ] - }, - "plugins": [ - { - "meta_data": { - "Name": "SpellChecker", - "Copyright": "(C) 2015 - 2024 Carel Combrink" - }, - "url": "https://github.com/CJCombrink/SpellChecker-Plugin/releases/download/v3.6.0/SpellChecker-Plugin_QtC13.0.0_macos_x64.tar.gz" - } - ], - "vendor": "Carel Combrink", - "copyright": "(C) 2015 - 2024 Carel Combrink" } ] }