forked from qt-creator/qt-creator
Marketplace: Remove plugin
Change-Id: I44a753ad3638a8a858de6a256f60f3a0a6a9efac Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -8,7 +8,6 @@ add_subdirectory(serialterminal)
|
|||||||
add_subdirectory(extensionmanager)
|
add_subdirectory(extensionmanager)
|
||||||
add_subdirectory(helloworld)
|
add_subdirectory(helloworld)
|
||||||
add_subdirectory(imageviewer)
|
add_subdirectory(imageviewer)
|
||||||
add_subdirectory(marketplace)
|
|
||||||
add_subdirectory(screenrecorder)
|
add_subdirectory(screenrecorder)
|
||||||
add_subdirectory(updateinfo)
|
add_subdirectory(updateinfo)
|
||||||
add_subdirectory(welcome)
|
add_subdirectory(welcome)
|
||||||
|
@@ -1,10 +0,0 @@
|
|||||||
add_qtc_plugin(Marketplace
|
|
||||||
PLUGIN_DEPENDS Core
|
|
||||||
SOURCES
|
|
||||||
marketplaceplugin.cpp
|
|
||||||
marketplacetr.h
|
|
||||||
productlistmodel.cpp
|
|
||||||
productlistmodel.h
|
|
||||||
qtmarketplacewelcomepage.cpp
|
|
||||||
qtmarketplacewelcomepage.h
|
|
||||||
)
|
|
@@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
"Id" : "marketplace",
|
|
||||||
"DisplayName" : "Qt Marketplace",
|
|
||||||
"Name" : "Marketplace",
|
|
||||||
"Version" : "${IDE_VERSION}",
|
|
||||||
"CompatVersion" : "${IDE_VERSION_COMPAT}",
|
|
||||||
"DisabledByDefault" : true,
|
|
||||||
"VendorId" : "theqtcompany",
|
|
||||||
"Vendor" : "The Qt Company Ltd",
|
|
||||||
"Copyright" : "${IDE_COPYRIGHT}",
|
|
||||||
"License" : [ "Commercial Usage",
|
|
||||||
"",
|
|
||||||
"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.",
|
|
||||||
"",
|
|
||||||
"GNU General Public License Usage",
|
|
||||||
"",
|
|
||||||
"Alternatively, this file may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this file. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html."
|
|
||||||
],
|
|
||||||
"Description" : "Install applications from Qt Marketplace.",
|
|
||||||
"LongDescription" : [],
|
|
||||||
"Url" : "https://www.qt.io",
|
|
||||||
"DocumentationUrl" : "https://marketplace.qt.io",
|
|
||||||
${IDE_PLUGIN_DEPENDENCIES}
|
|
||||||
}
|
|
@@ -1,20 +0,0 @@
|
|||||||
import qbs
|
|
||||||
|
|
||||||
QtcPlugin {
|
|
||||||
name: "Marketplace"
|
|
||||||
|
|
||||||
Depends { name: "Core" }
|
|
||||||
Depends { name: "Utils" }
|
|
||||||
|
|
||||||
Depends { name: "Qt.widgets" }
|
|
||||||
Depends { name: "Qt.network" }
|
|
||||||
|
|
||||||
files: [
|
|
||||||
"marketplaceplugin.cpp",
|
|
||||||
"marketplacetr.h",
|
|
||||||
"productlistmodel.cpp",
|
|
||||||
"productlistmodel.h",
|
|
||||||
"qtmarketplacewelcomepage.cpp",
|
|
||||||
"qtmarketplacewelcomepage.h",
|
|
||||||
]
|
|
||||||
}
|
|
@@ -1,23 +0,0 @@
|
|||||||
// Copyright (C) 2019 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#include "qtmarketplacewelcomepage.h"
|
|
||||||
|
|
||||||
#include <extensionsystem/iplugin.h>
|
|
||||||
|
|
||||||
namespace Marketplace::Internal {
|
|
||||||
|
|
||||||
class MarketplacePlugin final : public ExtensionSystem::IPlugin
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Marketplace.json")
|
|
||||||
|
|
||||||
void initialize() final
|
|
||||||
{
|
|
||||||
setupQtMarketPlaceWelcomePage(this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // Marketplace::Internal
|
|
||||||
|
|
||||||
#include "marketplaceplugin.moc"
|
|
@@ -1,15 +0,0 @@
|
|||||||
// Copyright (C) 2022 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QCoreApplication>
|
|
||||||
|
|
||||||
namespace Marketplace {
|
|
||||||
|
|
||||||
struct Tr
|
|
||||||
{
|
|
||||||
Q_DECLARE_TR_FUNCTIONS(QtC::Marketplace)
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Marketplace
|
|
@@ -1,306 +0,0 @@
|
|||||||
// Copyright (C) 2019 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#include "productlistmodel.h"
|
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
|
||||||
#include <utils/networkaccessmanager.h>
|
|
||||||
#include <utils/qtcassert.h>
|
|
||||||
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QDesktopServices>
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QJsonArray>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QNetworkAccessManager>
|
|
||||||
#include <QNetworkReply>
|
|
||||||
#include <QNetworkRequest>
|
|
||||||
#include <QPixmapCache>
|
|
||||||
#include <QRegularExpression>
|
|
||||||
#include <QScopeGuard>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QUrl>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
|
|
||||||
using namespace Core;
|
|
||||||
|
|
||||||
namespace Marketplace {
|
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
class ProductItemDelegate : public Core::ListItemDelegate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void clickAction(const Core::ListItem *item) const override
|
|
||||||
{
|
|
||||||
QTC_ASSERT(item, return);
|
|
||||||
auto productItem = static_cast<const ProductItem *>(item);
|
|
||||||
const QUrl url(QString("https://marketplace.qt.io/products/").append(productItem->handle));
|
|
||||||
QDesktopServices::openUrl(url);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const QNetworkRequest constructRequest(const QString &collection)
|
|
||||||
{
|
|
||||||
QString url("https://marketplace.qt.io");
|
|
||||||
if (collection.isEmpty())
|
|
||||||
url.append("/collections.json");
|
|
||||||
else
|
|
||||||
url.append("/collections/").append(collection).append("/products.json");
|
|
||||||
|
|
||||||
return QNetworkRequest(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const QString plainTextFromHtml(const QString &original)
|
|
||||||
{
|
|
||||||
QString plainText(original);
|
|
||||||
static const QRegularExpression breakReturn("<\\s*br/?\\s*>",
|
|
||||||
QRegularExpression::CaseInsensitiveOption);
|
|
||||||
static const QRegularExpression allTags("<[^>]*>");
|
|
||||||
static const QRegularExpression moreThan2NewLines("\n{3,}");
|
|
||||||
plainText.replace(breakReturn, "\n"); // "translate" <br/> into newline
|
|
||||||
plainText.remove(allTags); // remove all tags
|
|
||||||
plainText = plainText.trimmed();
|
|
||||||
plainText.replace(moreThan2NewLines, "\n\n"); // consolidate some newlines
|
|
||||||
|
|
||||||
// FIXME the description text is usually too long and needs to get elided sensibly
|
|
||||||
return (plainText.length() > 157) ? plainText.left(157).append("...") : plainText;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int priority(const QString &collection)
|
|
||||||
{
|
|
||||||
if (collection == "featured")
|
|
||||||
return 10;
|
|
||||||
if (collection == "from-qt-partners")
|
|
||||||
return 20;
|
|
||||||
return 50;
|
|
||||||
}
|
|
||||||
|
|
||||||
SectionedProducts::SectionedProducts(QWidget *parent)
|
|
||||||
: SectionedGridView(parent)
|
|
||||||
, m_productDelegate(new ProductItemDelegate)
|
|
||||||
{
|
|
||||||
setItemDelegate(m_productDelegate);
|
|
||||||
setPixmapFunction([this](const QString &url) -> QPixmap {
|
|
||||||
queueImageForDownload(url);
|
|
||||||
return {};
|
|
||||||
});
|
|
||||||
connect(m_productDelegate,
|
|
||||||
&ProductItemDelegate::tagClicked,
|
|
||||||
this,
|
|
||||||
&SectionedProducts::onTagClicked);
|
|
||||||
}
|
|
||||||
|
|
||||||
SectionedProducts::~SectionedProducts()
|
|
||||||
{
|
|
||||||
delete m_productDelegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SectionedProducts::updateCollections()
|
|
||||||
{
|
|
||||||
emit toggleProgressIndicator(true);
|
|
||||||
QNetworkReply *reply = Utils::NetworkAccessManager::instance()->get(constructRequest({}));
|
|
||||||
connect(reply, &QNetworkReply::finished,
|
|
||||||
this, [this, reply]() { onFetchCollectionsFinished(reply); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void SectionedProducts::onFetchCollectionsFinished(QNetworkReply *reply)
|
|
||||||
{
|
|
||||||
QTC_ASSERT(reply, return);
|
|
||||||
const QScopeGuard cleanup([reply] { reply->deleteLater(); });
|
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NoError) {
|
|
||||||
const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
|
|
||||||
if (doc.isNull())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const QJsonArray collections = doc.object().value("collections").toArray();
|
|
||||||
for (int i = 0, end = collections.size(); i < end; ++i) {
|
|
||||||
const QJsonObject obj = collections.at(i).toObject();
|
|
||||||
const auto handle = obj.value("handle").toString();
|
|
||||||
const int productsCount = obj.value("products_count").toInt();
|
|
||||||
|
|
||||||
if (productsCount > 0 && handle != "all-products" && handle != "qt-education-1") {
|
|
||||||
m_collectionTitles.insert(handle, obj.value("title").toString());
|
|
||||||
m_pendingCollections.append(handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!m_pendingCollections.isEmpty())
|
|
||||||
fetchCollectionsContents();
|
|
||||||
} else {
|
|
||||||
QVariant status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
|
||||||
if (status.isValid() && status.toInt() == 430)
|
|
||||||
QTimer::singleShot(30000, this, &SectionedProducts::updateCollections);
|
|
||||||
else
|
|
||||||
emit errorOccurred(reply->error(), reply->errorString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SectionedProducts::onFetchSingleCollectionFinished(QNetworkReply *reply)
|
|
||||||
{
|
|
||||||
emit toggleProgressIndicator(false);
|
|
||||||
|
|
||||||
QTC_ASSERT(reply, return);
|
|
||||||
const QScopeGuard cleanup([reply] { reply->deleteLater(); });
|
|
||||||
|
|
||||||
QList<Core::ListItem *> productsForCollection;
|
|
||||||
if (reply->error() == QNetworkReply::NoError) {
|
|
||||||
const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
|
|
||||||
if (doc.isNull())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QString collectionHandle = reply->url().path();
|
|
||||||
if (QTC_GUARD(collectionHandle.endsWith("/products.json"))) {
|
|
||||||
collectionHandle.chop(14);
|
|
||||||
collectionHandle = collectionHandle.mid(collectionHandle.lastIndexOf('/') + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const QList<Core::ListItem *> presentItems = items();
|
|
||||||
const QJsonArray products = doc.object().value("products").toArray();
|
|
||||||
for (int i = 0, end = products.size(); i < end; ++i) {
|
|
||||||
const QJsonObject obj = products.at(i).toObject();
|
|
||||||
const QString handle = obj.value("handle").toString();
|
|
||||||
|
|
||||||
bool foundItem = Utils::findOrDefault(presentItems, [handle](const Core::ListItem *it) {
|
|
||||||
return static_cast<const ProductItem *>(it)->handle == handle;
|
|
||||||
});
|
|
||||||
if (foundItem)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ProductItem *product = new ProductItem;
|
|
||||||
product->name = obj.value("title").toString();
|
|
||||||
product->description = plainTextFromHtml(obj.value("body_html").toString());
|
|
||||||
|
|
||||||
product->handle = handle;
|
|
||||||
const QJsonArray tags = obj.value("tags").toArray();
|
|
||||||
for (const auto &val : tags)
|
|
||||||
product->tags.append(val.toString());
|
|
||||||
|
|
||||||
const auto images = obj.value("images").toArray();
|
|
||||||
if (!images.isEmpty()) {
|
|
||||||
auto imgObj = images.first().toObject();
|
|
||||||
const QJsonValue imageSrc = imgObj.value("src");
|
|
||||||
if (imageSrc.isString())
|
|
||||||
product->imageUrl = imageSrc.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
productsForCollection.append(product);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!productsForCollection.isEmpty()) {
|
|
||||||
Section section{m_collectionTitles.value(collectionHandle), priority(collectionHandle)};
|
|
||||||
addNewSection(section, productsForCollection);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// bad.. but we still might be able to fetch another collection
|
|
||||||
qWarning() << "Failed to fetch collection:" << reply->errorString() << reply->error();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_pendingCollections.isEmpty()) // more collections? go ahead..
|
|
||||||
fetchCollectionsContents();
|
|
||||||
else if (m_productModels.isEmpty())
|
|
||||||
emit errorOccurred(0, "Failed to fetch any collection.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SectionedProducts::fetchCollectionsContents()
|
|
||||||
{
|
|
||||||
QTC_ASSERT(!m_pendingCollections.isEmpty(), return);
|
|
||||||
const QString collection = m_pendingCollections.dequeue();
|
|
||||||
|
|
||||||
QNetworkReply *reply
|
|
||||||
= Utils::NetworkAccessManager::instance()->get(constructRequest(collection));
|
|
||||||
connect(reply, &QNetworkReply::finished,
|
|
||||||
this, [this, reply]() { onFetchSingleCollectionFinished(reply); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void SectionedProducts::queueImageForDownload(const QString &url)
|
|
||||||
{
|
|
||||||
m_pendingImages.insert(url);
|
|
||||||
if (!m_isDownloadingImage)
|
|
||||||
fetchNextImage();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void updateModelIndexesForUrl(ListModel *model, const QString &url)
|
|
||||||
{
|
|
||||||
const QList<ListItem *> items = model->items();
|
|
||||||
for (int row = 0, end = items.size(); row < end; ++row) {
|
|
||||||
if (items.at(row)->imageUrl == url) {
|
|
||||||
const QModelIndex index = model->index(row);
|
|
||||||
emit model->dataChanged(index, index, {ListModel::ItemImageRole, Qt::DisplayRole});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SectionedProducts::fetchNextImage()
|
|
||||||
{
|
|
||||||
if (m_pendingImages.isEmpty()) {
|
|
||||||
m_isDownloadingImage = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto it = m_pendingImages.constBegin();
|
|
||||||
const QString nextUrl = *it;
|
|
||||||
m_pendingImages.erase(it);
|
|
||||||
|
|
||||||
if (QPixmapCache::find(nextUrl, nullptr)) {
|
|
||||||
// this image is already cached it might have been added while downloading
|
|
||||||
for (ListModel *model : std::as_const(m_productModels))
|
|
||||||
updateModelIndexesForUrl(model, nextUrl);
|
|
||||||
fetchNextImage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_isDownloadingImage = true;
|
|
||||||
QNetworkReply *reply = Utils::NetworkAccessManager::instance()->get(QNetworkRequest(nextUrl));
|
|
||||||
connect(reply, &QNetworkReply::finished,
|
|
||||||
this, [this, reply]() { onImageDownloadFinished(reply); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void SectionedProducts::onImageDownloadFinished(QNetworkReply *reply)
|
|
||||||
{
|
|
||||||
QTC_ASSERT(reply, return);
|
|
||||||
const QScopeGuard cleanup([reply] { reply->deleteLater(); });
|
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NoError) {
|
|
||||||
const QByteArray data = reply->readAll();
|
|
||||||
QPixmap pixmap;
|
|
||||||
const QUrl imageUrl = reply->request().url();
|
|
||||||
const QString imageFormat = QFileInfo(imageUrl.fileName()).suffix();
|
|
||||||
if (pixmap.loadFromData(data, imageFormat.toLatin1())) {
|
|
||||||
const QString url = imageUrl.toString();
|
|
||||||
const int dpr = qApp->devicePixelRatio();
|
|
||||||
pixmap = pixmap.scaled(WelcomePageHelpers::WelcomeThumbnailSize * dpr,
|
|
||||||
Qt::KeepAspectRatio,
|
|
||||||
Qt::SmoothTransformation);
|
|
||||||
pixmap.setDevicePixelRatio(dpr);
|
|
||||||
QPixmapCache::insert(url, pixmap);
|
|
||||||
for (ListModel *model : std::as_const(m_productModels))
|
|
||||||
updateModelIndexesForUrl(model, url);
|
|
||||||
}
|
|
||||||
} // handle error not needed - it's okay'ish to have no images as long as the rest works
|
|
||||||
|
|
||||||
fetchNextImage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SectionedProducts::addNewSection(const Section §ion, const QList<Core::ListItem *> &items)
|
|
||||||
{
|
|
||||||
QTC_ASSERT(!items.isEmpty(), return);
|
|
||||||
m_productModels.append(addSection(section, items));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SectionedProducts::onTagClicked(const QString &tag)
|
|
||||||
{
|
|
||||||
setCurrentIndex(1 /* search */);
|
|
||||||
emit tagClicked(tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<Core::ListItem *> SectionedProducts::items()
|
|
||||||
{
|
|
||||||
QList<Core::ListItem *> result;
|
|
||||||
for (const ListModel *model : std::as_const(m_productModels))
|
|
||||||
result.append(model->items());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Internal
|
|
||||||
} // namespace Marketplace
|
|
@@ -1,65 +0,0 @@
|
|||||||
// Copyright (C) 2019 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <coreplugin/welcomepagehelper.h>
|
|
||||||
|
|
||||||
#include <QQueue>
|
|
||||||
#include <QStackedWidget>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QNetworkReply;
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
namespace Marketplace {
|
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
class ProductGridView;
|
|
||||||
class ProductItemDelegate;
|
|
||||||
|
|
||||||
class ProductItem : public Core::ListItem
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QString handle;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SectionedProducts : public Core::SectionedGridView
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit SectionedProducts(QWidget *parent);
|
|
||||||
~SectionedProducts() override;
|
|
||||||
void updateCollections();
|
|
||||||
void queueImageForDownload(const QString &url);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void errorOccurred(int errorCode, const QString &errorString);
|
|
||||||
void toggleProgressIndicator(bool show);
|
|
||||||
void tagClicked(const QString &tag);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void onFetchCollectionsFinished(QNetworkReply *reply);
|
|
||||||
void onFetchSingleCollectionFinished(QNetworkReply *reply);
|
|
||||||
void fetchCollectionsContents();
|
|
||||||
|
|
||||||
void fetchNextImage();
|
|
||||||
void onImageDownloadFinished(QNetworkReply *reply);
|
|
||||||
void addNewSection(const Core::Section §ion, const QList<Core::ListItem *> &items);
|
|
||||||
void onTagClicked(const QString &tag);
|
|
||||||
|
|
||||||
QList<Core::ListItem *> items();
|
|
||||||
|
|
||||||
QQueue<QString> m_pendingCollections;
|
|
||||||
QSet<QString> m_pendingImages;
|
|
||||||
QMap<QString, QString> m_collectionTitles;
|
|
||||||
QList<Core::ListModel *> m_productModels;
|
|
||||||
ProductItemDelegate *m_productDelegate = nullptr;
|
|
||||||
bool m_isDownloadingImage = false;
|
|
||||||
int m_columnCount = 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Internal
|
|
||||||
} // namespace Marketplace
|
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(Marketplace::Internal::ProductItem *)
|
|
@@ -1,129 +0,0 @@
|
|||||||
// Copyright (C) 2019 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#include "qtmarketplacewelcomepage.h"
|
|
||||||
|
|
||||||
#include "marketplacetr.h"
|
|
||||||
#include "productlistmodel.h"
|
|
||||||
|
|
||||||
#include <coreplugin/welcomepagehelper.h>
|
|
||||||
|
|
||||||
#include <utils/fancylineedit.h>
|
|
||||||
#include <utils/layoutbuilder.h>
|
|
||||||
#include <utils/progressindicator.h>
|
|
||||||
#include <utils/qtcassert.h>
|
|
||||||
#include <utils/theme/theme.h>
|
|
||||||
|
|
||||||
#include <QDesktopServices>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QLineEdit>
|
|
||||||
#include <QShowEvent>
|
|
||||||
|
|
||||||
using namespace Core;
|
|
||||||
using namespace Utils;
|
|
||||||
|
|
||||||
namespace Marketplace::Internal {
|
|
||||||
|
|
||||||
class QtMarketplacePageWidget final : public QWidget
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QtMarketplacePageWidget()
|
|
||||||
{
|
|
||||||
m_searcher = new Core::SearchBox(this);
|
|
||||||
m_searcher->setPlaceholderText(Tr::tr("Search in Marketplace..."));
|
|
||||||
|
|
||||||
m_errorLabel = new QLabel(this);
|
|
||||||
m_errorLabel->setVisible(false);
|
|
||||||
|
|
||||||
m_sectionedProducts = new SectionedProducts(this);
|
|
||||||
auto progressIndicator = new Utils::ProgressIndicator(ProgressIndicatorSize::Large, this);
|
|
||||||
progressIndicator->attachToWidget(m_sectionedProducts);
|
|
||||||
progressIndicator->hide();
|
|
||||||
|
|
||||||
using namespace StyleHelper::SpacingTokens;
|
|
||||||
|
|
||||||
using namespace Layouting;
|
|
||||||
Column {
|
|
||||||
Row {
|
|
||||||
m_searcher,
|
|
||||||
m_errorLabel,
|
|
||||||
customMargins(0, 0, ExVPaddingGapXl, 0),
|
|
||||||
},
|
|
||||||
m_sectionedProducts,
|
|
||||||
spacing(ExVPaddingGapXl),
|
|
||||||
customMargins(ExVPaddingGapXl, ExVPaddingGapXl, 0, 0),
|
|
||||||
}.attachTo(this);
|
|
||||||
|
|
||||||
connect(m_sectionedProducts, &SectionedProducts::toggleProgressIndicator,
|
|
||||||
progressIndicator, &Utils::ProgressIndicator::setVisible);
|
|
||||||
connect(m_sectionedProducts, &SectionedProducts::errorOccurred, this,
|
|
||||||
[this, progressIndicator](int, const QString &message) {
|
|
||||||
progressIndicator->hide();
|
|
||||||
progressIndicator->deleteLater();
|
|
||||||
m_errorLabel->setAlignment(Qt::AlignHCenter);
|
|
||||||
QFont f(m_errorLabel->font());
|
|
||||||
f.setPixelSize(20);
|
|
||||||
m_errorLabel->setFont(f);
|
|
||||||
const QString txt
|
|
||||||
= Tr::tr(
|
|
||||||
"<p>Could not fetch data from Qt Marketplace.</p><p>Try with your browser "
|
|
||||||
"instead: <a href='https://marketplace.qt.io'>https://marketplace.qt.io</a>"
|
|
||||||
"</p><br/><p><small><i>Error: %1</i></small></p>").arg(message);
|
|
||||||
m_errorLabel->setText(txt);
|
|
||||||
m_errorLabel->setVisible(true);
|
|
||||||
m_searcher->setVisible(false);
|
|
||||||
connect(m_errorLabel, &QLabel::linkActivated,
|
|
||||||
this, []() { QDesktopServices::openUrl(QUrl("https://marketplace.qt.io")); });
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(m_searcher,
|
|
||||||
&QLineEdit::textChanged,
|
|
||||||
m_sectionedProducts,
|
|
||||||
&SectionedProducts::setSearchStringDelayed);
|
|
||||||
connect(m_sectionedProducts, &SectionedProducts::tagClicked,
|
|
||||||
this, &QtMarketplacePageWidget::onTagClicked);
|
|
||||||
}
|
|
||||||
|
|
||||||
void showEvent(QShowEvent *event) final
|
|
||||||
{
|
|
||||||
if (!m_initialized) {
|
|
||||||
m_initialized = true;
|
|
||||||
m_sectionedProducts->updateCollections();
|
|
||||||
}
|
|
||||||
QWidget::showEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onTagClicked(const QString &tag)
|
|
||||||
{
|
|
||||||
const QString text = m_searcher->text();
|
|
||||||
m_searcher->setText((text.startsWith("tag:\"") ? text.trimmed() + " " : QString())
|
|
||||||
+ QString("tag:\"%1\" ").arg(tag));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
SectionedProducts *m_sectionedProducts = nullptr;
|
|
||||||
QLabel *m_errorLabel = nullptr;
|
|
||||||
QLineEdit *m_searcher = nullptr;
|
|
||||||
bool m_initialized = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// QtMarketplaceWelcomePage
|
|
||||||
|
|
||||||
class QtMarketplaceWelcomePage final : public IWelcomePage
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QtMarketplaceWelcomePage() = default;
|
|
||||||
|
|
||||||
QString title() const final { return Tr::tr("Marketplace"); }
|
|
||||||
int priority() const final { return 60; }
|
|
||||||
Utils::Id id() const final { return "Marketplace"; }
|
|
||||||
QWidget *createWidget() const final { return new QtMarketplacePageWidget; }
|
|
||||||
};
|
|
||||||
|
|
||||||
void setupQtMarketPlaceWelcomePage(QObject *guard)
|
|
||||||
{
|
|
||||||
auto page = new QtMarketplaceWelcomePage;
|
|
||||||
page->setParent(guard);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Marketplace::Internal
|
|
@@ -1,12 +0,0 @@
|
|||||||
// Copyright (C) 2019 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
namespace Marketplace::Internal {
|
|
||||||
|
|
||||||
void setupQtMarketPlaceWelcomePage(QObject *guard);
|
|
||||||
|
|
||||||
} // namespace Marketplace::Internal
|
|
@@ -56,7 +56,6 @@ Project {
|
|||||||
"languageclient/lualanguageclient/lualanguageclient.qbs",
|
"languageclient/lualanguageclient/lualanguageclient.qbs",
|
||||||
"lua/lua.qbs",
|
"lua/lua.qbs",
|
||||||
"macros/macros.qbs",
|
"macros/macros.qbs",
|
||||||
"marketplace/marketplace.qbs",
|
|
||||||
"mcusupport/mcusupport.qbs",
|
"mcusupport/mcusupport.qbs",
|
||||||
"mercurial/mercurial.qbs",
|
"mercurial/mercurial.qbs",
|
||||||
"modeleditor/modeleditor.qbs",
|
"modeleditor/modeleditor.qbs",
|
||||||
|
Reference in New Issue
Block a user