From 2081d1c0bc5aa865b68f0252e4a09f1608e55cfc Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Thu, 23 Mar 2023 20:50:12 +0200 Subject: [PATCH] Download icons and metadata of textures at startup Task-number: QDS-9397 Change-Id: If1bb4a5500896b70b4a9dbb82d2122c1232ee744 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../contentlibrarymaterialsmodel.cpp | 2 +- .../contentlibrarytexturesmodel.cpp | 24 ++-- .../contentlibrarytexturesmodel.h | 3 +- .../contentlibrary/contentlibrarywidget.cpp | 134 +++++++++++++++++- .../contentlibrary/contentlibrarywidget.h | 9 ++ .../qmldesigner/utils/filedownloader.cpp | 4 +- 6 files changed, 150 insertions(+), 26 deletions(-) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index 42b5112e1c8..937152603a6 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -114,7 +114,7 @@ bool ContentLibraryMaterialsModel::fetchBundleIcons(const QDir &bundleDir) QString iconsPath = bundleDir.filePath("icons"); QDir iconsDir(iconsPath); - if (iconsDir.exists() && iconsDir.entryList().length() > 0) + if (iconsDir.exists() && iconsDir.entryList(QDir::NoDotAndDotDot).length() > 0) return true; QString zipFileUrl = m_baseUrl + "/icons.zip"; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp index 56197ca5b54..639d0154def 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp @@ -6,9 +6,6 @@ #include "contentlibrarytexturescategory.h" #include "qmldesignerplugin.h" -#include "utils/filedownloader.h" -#include "utils/fileextractor.h" - #include #include @@ -18,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -28,9 +26,6 @@ namespace QmlDesigner { ContentLibraryTexturesModel::ContentLibraryTexturesModel(const QString &category, QObject *parent) : QAbstractListModel(parent) { - qmlRegisterType("WebFetcher", 1, 0, "FileDownloader"); - qmlRegisterType("WebFetcher", 1, 0, "FileExtractor"); - m_category = category; // textures main category (ex: Textures, Environments) } @@ -103,28 +98,24 @@ QHash ContentLibraryTexturesModel::roleNames() const * @param bundlePath local path to the bundle folder and icons * @param metaData bundle textures metadata */ -void ContentLibraryTexturesModel::loadTextureBundle(const QString &bundlePath, const QVariantMap &metaData) +void ContentLibraryTexturesModel::loadTextureBundle(const QString &remoteUrl, const QString &bundleIconPath, + const QVariantMap &metaData) { if (!m_bundleCategories.isEmpty()) return; - QDir bundleDir = QString("%1/%2").arg(bundlePath, m_category); + QDir bundleDir = QString("%1/%2").arg(bundleIconPath, m_category); if (!bundleDir.exists()) { - qWarning() << __FUNCTION__ << "textures bundle folder doesn't exist." << bundlePath; + qWarning() << __FUNCTION__ << "textures bundle folder doesn't exist." << bundleDir.absolutePath(); return; } - QString remoteBaseUrl = QmlDesignerPlugin::settings() - .value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL).toString() - + "/textures/" + m_category; - const QFileInfoList dirs = bundleDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); for (const QFileInfo &dir : dirs) { auto category = new ContentLibraryTexturesCategory(this, dir.fileName()); - const QFileInfoList texFiles = QDir(dir.filePath() + "/icon").entryInfoList(QDir::Files); + const QFileInfoList texFiles = QDir(dir.filePath()).entryInfoList(QDir::Files); for (const QFileInfo &tex : texFiles) { - - QString fullRemoteUrl = QString("%1/%2/%3.zip").arg(remoteBaseUrl, dir.fileName(), + QString fullRemoteUrl = QString("%1/%2/%3.zip").arg(remoteUrl, dir.fileName(), tex.baseName()); QString localDownloadPath = QString("%1/%2/%3").arg(QmlDesignerBasePlugin::bundlesPathSetting(), m_category, dir.fileName()); @@ -145,6 +136,7 @@ void ContentLibraryTexturesModel::loadTextureBundle(const QString &bundlePath, c m_bundleCategories.append(category); } + resetModel(); updateIsEmpty(); } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h index 1f8efb5675a..3f7e4d48688 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h @@ -33,7 +33,8 @@ public: void setHasSceneEnv(bool b); void resetModel(); - void loadTextureBundle(const QString &bundlePath, const QVariantMap &metaData); + void loadTextureBundle(const QString &remoteUrl, const QString &bundlePath, + const QVariantMap &metaData); signals: void isEmptyChanged(); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index 00aa613dae7..8b3868d38e6 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -8,6 +8,9 @@ #include "contentlibrarytexture.h" #include "contentlibrarytexturesmodel.h" +#include "utils/filedownloader.h" +#include "utils/fileextractor.h" + #include #include #include @@ -19,6 +22,7 @@ #include #include +#include #include #include #include @@ -32,6 +36,8 @@ namespace QmlDesigner { +constexpr int TextureBundleMetadataVersion = 1; + static QString propertyEditorResourcesPath() { #ifdef SHARE_QML_PATH @@ -98,6 +104,9 @@ ContentLibraryWidget::ContentLibraryWidget() , m_texturesModel(new ContentLibraryTexturesModel("Textures", this)) , m_environmentsModel(new ContentLibraryTexturesModel("Environments", this)) { + qmlRegisterType("WebFetcher", 1, 0, "FileDownloader"); + qmlRegisterType("WebFetcher", 1, 0, "FileExtractor"); + setWindowTitle(tr("Content Library", "Title of content library widget")); setMinimumWidth(120); @@ -106,15 +115,17 @@ ContentLibraryWidget::ContentLibraryWidget() m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); m_quickWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground)); - QString textureBundlePath = findTextureBundlePath(); + m_baseUrl = QmlDesignerPlugin::settings() + .value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL).toString() + + "/textures/v1"; - QVariantMap metaData; - QFile jsonFile(textureBundlePath + "/texture_bundle.json"); - if (jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) - metaData = QJsonDocument::fromJson(jsonFile.readAll()).toVariant().toMap(); + m_texturesUrl = m_baseUrl + "/Textures"; + m_environmentsUrl = m_baseUrl + "/Environments"; - m_texturesModel->loadTextureBundle(textureBundlePath, metaData); - m_environmentsModel->loadTextureBundle(textureBundlePath, metaData); + m_downloadPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + + "/QtDesignStudio/bundles"; + + loadTextureBundle(); Theme::setupTheme(m_quickWidget->engine()); m_quickWidget->quickWidget()->installEventFilter(this); @@ -144,6 +155,115 @@ ContentLibraryWidget::ContentLibraryWidget() reloadQmlSource(); } +QVariantMap ContentLibraryWidget::readBundleMetadata() +{ + QVariantMap metaData; + QFile jsonFile(m_downloadPath + "/texture_bundle.json"); + if (jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) + metaData = QJsonDocument::fromJson(jsonFile.readAll()).toVariant().toMap(); + + int version = metaData["version"].toInt(); + if (version != TextureBundleMetadataVersion) { + qWarning() << "Unrecognized texture metadata file version: " << version; + return {}; + } + + return metaData; +} + +void ContentLibraryWidget::loadTextureBundle() +{ + QDir bundleDir{m_downloadPath}; + + if (fetchTextureBundleMetadata(bundleDir) && fetchTextureBundleIcons(bundleDir)) { + QString bundleIconPath = m_downloadPath + "/TextureBundleIcons"; + QVariantMap metaData = readBundleMetadata(); + m_texturesModel->loadTextureBundle(m_texturesUrl, bundleIconPath, metaData); + m_environmentsModel->loadTextureBundle(m_environmentsUrl, bundleIconPath, metaData); + } +} + +bool ContentLibraryWidget::fetchTextureBundleMetadata(const QDir &bundleDir) +{ + QString filePath = bundleDir.filePath("texture_bundle.json"); + + QFileInfo fi(filePath); + if (fi.exists() && fi.size() > 0) + return true; + + QString metaFileUrl = m_baseUrl + "/texture_bundle.zip"; + FileDownloader *downloader = new FileDownloader(this); + downloader->setUrl(metaFileUrl); + downloader->setProbeUrl(false); + downloader->setDownloadEnabled(true); + + QObject::connect(downloader, &FileDownloader::finishedChanged, this, [=]() { + FileExtractor *extractor = new FileExtractor(this); + extractor->setArchiveName(downloader->completeBaseName()); + extractor->setSourceFile(downloader->outputFile()); + extractor->setTargetPath(bundleDir.absolutePath()); + extractor->setAlwaysCreateDir(false); + extractor->setClearTargetPathContents(false); + + QObject::connect(extractor, &FileExtractor::finishedChanged, this, [=]() { + downloader->deleteLater(); + extractor->deleteLater(); + + if (fetchTextureBundleIcons(bundleDir)) { + QString bundleIconPath = m_downloadPath + "/TextureBundleIcons"; + QVariantMap metaData = readBundleMetadata(); + m_texturesModel->loadTextureBundle(m_texturesUrl, bundleIconPath, metaData); + m_environmentsModel->loadTextureBundle(m_environmentsUrl, bundleIconPath, metaData); + } + }); + + extractor->extract(); + }); + + downloader->start(); + return false; +} + +bool ContentLibraryWidget::fetchTextureBundleIcons(const QDir &bundleDir) +{ + QString iconsPath = bundleDir.filePath("TextureBundleIcons"); + + QDir iconsDir(iconsPath); + if (iconsDir.exists() && iconsDir.entryList(QDir::NoDotAndDotDot).length() > 0) + return true; + + QString zipFileUrl = m_baseUrl + "/icons.zip"; + + FileDownloader *downloader = new FileDownloader(this); + downloader->setUrl(zipFileUrl); + downloader->setProbeUrl(false); + downloader->setDownloadEnabled(true); + + QObject::connect(downloader, &FileDownloader::finishedChanged, this, [=]() { + FileExtractor *extractor = new FileExtractor(this); + extractor->setArchiveName(downloader->completeBaseName()); + extractor->setSourceFile(downloader->outputFile()); + extractor->setTargetPath(bundleDir.absolutePath()); + extractor->setAlwaysCreateDir(false); + extractor->setClearTargetPathContents(false); + + QObject::connect(extractor, &FileExtractor::finishedChanged, this, [=]() { + downloader->deleteLater(); + extractor->deleteLater(); + + QString bundleIconPath = m_downloadPath + "/TextureBundleIcons"; + QVariantMap metaData = readBundleMetadata(); + m_texturesModel->loadTextureBundle(m_texturesUrl, bundleIconPath, metaData); + m_environmentsModel->loadTextureBundle(m_environmentsUrl, bundleIconPath, metaData); + }); + + extractor->extract(); + }); + + downloader->start(); + return false; +} + QList ContentLibraryWidget::createToolBarWidgets() { return {}; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h index 0837613977d..519d6fcf780 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h @@ -9,6 +9,7 @@ #include QT_BEGIN_NAMESPACE +class QDir; class QShortcut; class QToolButton; QT_END_NAMESPACE @@ -80,6 +81,10 @@ private: void updateSearch(); void setIsDragging(bool val); QString findTextureBundlePath(); + void loadTextureBundle(); + QVariantMap readBundleMetadata(); + bool fetchTextureBundleMetadata(const QDir &bundleDir); + bool fetchTextureBundleIcons(const QDir &bundleDir); QScopedPointer m_quickWidget; QPointer m_materialsModel; @@ -98,6 +103,10 @@ private: bool m_hasQuick3DImport = false; bool m_isDragging = false; bool m_anyTextureBeingDownloaded = false; + QString m_baseUrl; + QString m_texturesUrl; + QString m_environmentsUrl; + QString m_downloadPath; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/utils/filedownloader.cpp b/src/plugins/qmldesigner/utils/filedownloader.cpp index b5aaab59d80..b5c735d87a8 100644 --- a/src/plugins/qmldesigner/utils/filedownloader.cpp +++ b/src/plugins/qmldesigner/utils/filedownloader.cpp @@ -8,6 +8,7 @@ #include #include +#include namespace QmlDesigner { @@ -41,7 +42,8 @@ void FileDownloader::start() { emit downloadStarting(); - QString tempFileName = QDir::tempPath() + "/.qds_download_" + url().fileName(); + auto uniqueText = QByteArray::number(QRandomGenerator::global()->generate(), 16); + QString tempFileName = QDir::tempPath() + "/.qds_" + uniqueText + "_download_" + url().fileName(); m_outputFile.setFileName(tempFileName); m_outputFile.open(QIODevice::WriteOnly);