QmlDesigner: Take content library texture metadata file in use

Metadata file texture_bundle.json will be included with bundle icons,
and it contains details about the texture (format, dimensions, and size)
that are useful to know before downloading the texture.

Metadata is used to generate proper tooltip for non-downloaded textures
in content library.

Also removed default suffix from displayed filename in case metadata is
missing, as it was misleading.

Fixes: QDS-9230
Change-Id: Icbe0bbb7f1e663e0adc41d379231b7f41dc79e31
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
Miikka Heikkinen
2023-03-03 14:02:55 +02:00
parent eb16c66552
commit 9a55bb0fd0
10 changed files with 111 additions and 58 deletions

View File

@@ -238,7 +238,7 @@ Item {
onFinishedChanged: { onFinishedChanged: {
mouseArea.enabled = true mouseArea.enabled = true
modelData.setDownloaded() modelData.setDownloaded()
root.downloadState = "downloaded" root.downloadState = modelData.isDownloaded() ? "downloaded" : "failed"
root.downloadStateChanged() root.downloadStateChanged()
rootView.markNoTextureDownloading() rootView.markNoTextureDownloading()

View File

@@ -12,16 +12,20 @@
namespace QmlDesigner { namespace QmlDesigner {
ContentLibraryTexture::ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo, ContentLibraryTexture::ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo,
const QString &downloadPath, const QUrl &icon, const QString &webUrl) const QString &downloadPath, const QUrl &icon,
const QString &webUrl, const QString &fileExt,
const QSize &dimensions, const qint64 sizeInBytes)
: QObject(parent) : QObject(parent)
, m_iconPath(iconFileInfo.filePath()) , m_iconPath(iconFileInfo.filePath())
, m_downloadPath(downloadPath) , m_downloadPath(downloadPath)
, m_webUrl(webUrl) , m_webUrl(webUrl)
, m_baseName{iconFileInfo.baseName()} , m_baseName{iconFileInfo.baseName()}
, m_fileExt(fileExt)
, m_icon(icon) , m_icon(icon)
, m_dimensions(dimensions)
, m_sizeInBytes(sizeInBytes)
{ {
m_fileExt = resolveFileExt(); doSetDownloaded();
m_toolTip = resolveToolTipText();
} }
bool ContentLibraryTexture::filter(const QString &searchText) bool ContentLibraryTexture::filter(const QString &searchText)
@@ -61,49 +65,56 @@ QString ContentLibraryTexture::resolveFileExt()
}); });
} }
return QString{"."} + textureFiles.at(0).completeSuffix(); return '.' + textureFiles.at(0).completeSuffix();
} }
QString ContentLibraryTexture::resolveToolTipText() QString ContentLibraryTexture::resolveToolTipText()
{ {
QString fileName;
QString imageInfo;
if (m_fileExt.isEmpty()) { if (m_fileExt.isEmpty()) {
imageInfo = ImageUtils::imageInfo(m_iconPath, false); // No supplied or resolved extension means we have just the icon and no other data
fileName = m_baseName + m_defaultExt; return m_baseName;
} else {
fileName = m_baseName + m_fileExt;
QString fullDownloadPath = m_downloadPath + "/" + fileName;
imageInfo = ImageUtils::imageInfo(fullDownloadPath, true);
} }
return QLatin1String("%1\n%2").arg(fileName, imageInfo); QString fileName = m_baseName + m_fileExt;
QString imageInfo;
if (!m_isDownloaded && m_sizeInBytes > 0 && !m_dimensions.isNull()) {
imageInfo = ImageUtils::imageInfo(m_dimensions, m_sizeInBytes);
} else {
QString fullDownloadPath = m_downloadPath + '/' + fileName;
imageInfo = ImageUtils::imageInfo(fullDownloadPath);
}
return QStringLiteral("%1\n%2").arg(fileName, imageInfo);
} }
bool ContentLibraryTexture::isDownloaded() const bool ContentLibraryTexture::isDownloaded() const
{ {
if (m_fileExt.isEmpty()) return m_isDownloaded;
return false;
QString fullPath = realTexturePath();
return QFileInfo(fullPath).isFile();
} }
QString ContentLibraryTexture::realTexturePath() const QString ContentLibraryTexture::downloadedTexturePath() const
{ {
return m_downloadPath + "/" + m_baseName + m_fileExt; return m_downloadPath + '/' + m_baseName + m_fileExt;
} }
void ContentLibraryTexture::setDownloaded() void ContentLibraryTexture::setDownloaded()
{ {
m_fileExt = resolveFileExt(); QString toolTip = m_toolTip;
QString toolTip = resolveToolTipText();
if (toolTip != m_toolTip) { doSetDownloaded();
m_toolTip = toolTip;
if (toolTip != m_toolTip)
emit textureToolTipChanged(); emit textureToolTipChanged();
} }
void ContentLibraryTexture::doSetDownloaded()
{
if (m_fileExt.isEmpty())
m_fileExt = resolveFileExt();
m_isDownloaded = QFileInfo::exists(downloadedTexturePath());
m_toolTip = resolveToolTipText();
} }
QString ContentLibraryTexture::parentDirPath() const QString ContentLibraryTexture::parentDirPath() const

View File

@@ -5,6 +5,7 @@
#include <QFileInfo> #include <QFileInfo>
#include <QObject> #include <QObject>
#include <QSize>
#include <QUrl> #include <QUrl>
namespace QmlDesigner { namespace QmlDesigner {
@@ -22,7 +23,8 @@ class ContentLibraryTexture : public QObject
public: public:
ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo, ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo,
const QString &downloadPath, const QUrl &icon, const QString &webUrl); const QString &downloadPath, const QUrl &icon, const QString &webUrl,
const QString &fileExt, const QSize &dimensions, const qint64 sizeInBytes);
Q_INVOKABLE bool isDownloaded() const; Q_INVOKABLE bool isDownloaded() const;
Q_INVOKABLE void setDownloaded(); Q_INVOKABLE void setDownloaded();
@@ -31,7 +33,7 @@ public:
QUrl icon() const; QUrl icon() const;
QString iconPath() const; QString iconPath() const;
QString realTexturePath() const; QString downloadedTexturePath() const;
QString parentDirPath() const; QString parentDirPath() const;
signals: signals:
@@ -39,9 +41,9 @@ signals:
void textureToolTipChanged(); void textureToolTipChanged();
private: private:
inline static const QString m_defaultExt = ".png";
QString resolveFileExt(); QString resolveFileExt();
QString resolveToolTipText(); QString resolveToolTipText();
void doSetDownloaded();
QString m_iconPath; QString m_iconPath;
QString m_downloadPath; QString m_downloadPath;
@@ -50,6 +52,9 @@ private:
QString m_baseName; QString m_baseName;
QString m_fileExt; QString m_fileExt;
QUrl m_icon; QUrl m_icon;
QSize m_dimensions;
qint64 m_sizeInBytes = -1;
bool m_isDownloaded = false;
bool m_visible = true; bool m_visible = true;
}; };

View File

@@ -13,11 +13,13 @@ ContentLibraryTexturesCategory::ContentLibraryTexturesCategory(QObject *parent,
: QObject(parent), m_name(name) {} : QObject(parent), m_name(name) {}
void ContentLibraryTexturesCategory::addTexture(const QFileInfo &tex, const QString &downloadPath, void ContentLibraryTexturesCategory::addTexture(const QFileInfo &tex, const QString &downloadPath,
const QString &webUrl) const QString &webUrl, const QString &fileExt,
const QSize &dimensions, const qint64 sizeInBytes)
{ {
QUrl icon = QUrl::fromLocalFile(tex.absoluteFilePath()); QUrl icon = QUrl::fromLocalFile(tex.absoluteFilePath());
m_categoryTextures.append(new ContentLibraryTexture(this, tex, downloadPath, icon, webUrl)); m_categoryTextures.append(new ContentLibraryTexture(this, tex, downloadPath, icon, webUrl,
fileExt, dimensions, sizeInBytes));
} }
bool ContentLibraryTexturesCategory::filter(const QString &searchText) bool ContentLibraryTexturesCategory::filter(const QString &searchText)

View File

@@ -7,6 +7,7 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QFileInfo; class QFileInfo;
class QSize;
QT_END_NAMESPACE QT_END_NAMESPACE
namespace QmlDesigner { namespace QmlDesigner {
@@ -26,7 +27,8 @@ class ContentLibraryTexturesCategory : public QObject
public: public:
ContentLibraryTexturesCategory(QObject *parent, const QString &name); ContentLibraryTexturesCategory(QObject *parent, const QString &name);
void addTexture(const QFileInfo &tex, const QString &subPath, const QString &webUrl); void addTexture(const QFileInfo &tex, const QString &subPath, const QString &webUrl,
const QString &fileExt, const QSize &dimensions, const qint64 sizeInBytes);
bool filter(const QString &searchText); bool filter(const QString &searchText);
QString name() const; QString name() const;

View File

@@ -13,15 +13,18 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QDir> #include <QDir>
#include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QUrl> #include <QUrl>
#include <QQmlEngine> #include <QQmlEngine>
#include <QSize>
#include <QStandardPaths> #include <QStandardPaths>
namespace QmlDesigner { namespace QmlDesigner {
ContentLibraryTexturesModel::ContentLibraryTexturesModel(const QString &bundleSubpath, QObject *parent) ContentLibraryTexturesModel::ContentLibraryTexturesModel(const QString &bundleSubPath, QObject *parent)
: QAbstractListModel(parent) : QAbstractListModel(parent)
, m_bundleSubPath(bundleSubPath)
{ {
qmlRegisterType<QmlDesigner::FileDownloader>("WebFetcher", 1, 0, "FileDownloader"); qmlRegisterType<QmlDesigner::FileDownloader>("WebFetcher", 1, 0, "FileDownloader");
qmlRegisterType<QmlDesigner::FileExtractor>("WebFetcher", 1, 0, "FileExtractor"); qmlRegisterType<QmlDesigner::FileExtractor>("WebFetcher", 1, 0, "FileExtractor");
@@ -30,7 +33,7 @@ ContentLibraryTexturesModel::ContentLibraryTexturesModel(const QString &bundleSu
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)
+ "/QtDesignStudio/bundles"; + "/QtDesignStudio/bundles";
m_downloadPath = baseDownloadPath + "/" + bundleSubpath; m_downloadPath = baseDownloadPath + '/' + bundleSubPath;
} }
int ContentLibraryTexturesModel::rowCount(const QModelIndex &) const int ContentLibraryTexturesModel::rowCount(const QModelIndex &) const
@@ -96,7 +99,8 @@ QHash<int, QByteArray> ContentLibraryTexturesModel::roleNames() const
return roles; return roles;
} }
void ContentLibraryTexturesModel::loadTextureBundle(const QString &bundlePath, const QString &baseUrl) void ContentLibraryTexturesModel::loadTextureBundle(const QString &bundlePath, const QString &baseUrl,
const QVariantMap &metaData)
{ {
QDir bundleDir = QDir(bundlePath); QDir bundleDir = QDir(bundlePath);
if (!bundleDir.exists()) { if (!bundleDir.exists()) {
@@ -112,9 +116,22 @@ void ContentLibraryTexturesModel::loadTextureBundle(const QString &bundlePath, c
auto category = new ContentLibraryTexturesCategory(this, dir.fileName()); auto category = new ContentLibraryTexturesCategory(this, dir.fileName());
const QFileInfoList texFiles = QDir(dir.filePath() + "/icon").entryInfoList(QDir::Files); const QFileInfoList texFiles = QDir(dir.filePath() + "/icon").entryInfoList(QDir::Files);
for (const QFileInfo &tex : texFiles) { for (const QFileInfo &tex : texFiles) {
QString urlPath = baseUrl + "/" + dir.fileName() + "/" + tex.baseName() + ".zip"; QString zipPath = '/' + dir.fileName() + '/' + tex.baseName() + ".zip";
QString downloadPath = m_downloadPath + "/" + dir.fileName(); QString urlPath = baseUrl + zipPath;
category->addTexture(tex, downloadPath, urlPath); QString downloadPath = m_downloadPath + '/' + dir.fileName();
QString fullZipPath = m_bundleSubPath + zipPath;
QString fileExt;
QSize dimensions;
qint64 sizeInBytes = -1;
if (metaData.contains(fullZipPath)) {
QVariantMap dataMap = metaData[fullZipPath].toMap();
fileExt = '.' + dataMap.value("format").toString();
dimensions = QSize(dataMap.value("width").toInt(), dataMap.value("height").toInt());
sizeInBytes = dataMap.value("size").toLongLong();
}
category->addTexture(tex, downloadPath, urlPath, fileExt, dimensions, sizeInBytes);
} }
m_bundleCategories.append(category); m_bundleCategories.append(category);
} }

View File

@@ -33,7 +33,8 @@ public:
void setHasSceneEnv(bool b); void setHasSceneEnv(bool b);
void resetModel(); void resetModel();
void loadTextureBundle(const QString &bundlePath, const QString &baseUrl); void loadTextureBundle(const QString &bundlePath, const QString &baseUrl,
const QVariantMap &metaData);
signals: signals:
void isEmptyChanged(); void isEmptyChanged();
@@ -46,6 +47,7 @@ private:
QString m_searchText; QString m_searchText;
QString m_downloadPath; QString m_downloadPath;
QString m_bundleSubPath;
QList<ContentLibraryTexturesCategory *> m_bundleCategories; QList<ContentLibraryTexturesCategory *> m_bundleCategories;
bool m_isEmpty = true; bool m_isEmpty = true;

View File

@@ -18,6 +18,7 @@
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QJsonDocument>
#include <QMimeData> #include <QMimeData>
#include <QMouseEvent> #include <QMouseEvent>
#include <QQmlContext> #include <QQmlContext>
@@ -69,10 +70,11 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event)
if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 20 if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 20
&& m_textureToDrag->isDownloaded()) { && m_textureToDrag->isDownloaded()) {
QMimeData *mimeData = new QMimeData; QMimeData *mimeData = new QMimeData;
mimeData->setData(Constants::MIME_TYPE_BUNDLE_TEXTURE, {m_textureToDrag->realTexturePath().toUtf8()}); mimeData->setData(Constants::MIME_TYPE_BUNDLE_TEXTURE,
{m_textureToDrag->downloadedTexturePath().toUtf8()});
// Allows standard file drag-n-drop. As of now needed to drop on Assets view // Allows standard file drag-n-drop. As of now needed to drop on Assets view
mimeData->setUrls({QUrl::fromLocalFile(m_textureToDrag->realTexturePath())}); mimeData->setUrls({QUrl::fromLocalFile(m_textureToDrag->downloadedTexturePath())});
emit bundleTextureDragStarted(m_textureToDrag); emit bundleTextureDragStarted(m_textureToDrag);
model->startDrag(mimeData, m_textureToDrag->icon().toLocalFile()); model->startDrag(mimeData, m_textureToDrag->icon().toLocalFile());
@@ -106,8 +108,16 @@ ContentLibraryWidget::ContentLibraryWidget()
QString baseUrl = QmlDesignerPlugin::settings() QString baseUrl = QmlDesignerPlugin::settings()
.value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL) .value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL)
.toString(); .toString();
m_texturesModel->loadTextureBundle(textureBundlePath + "/Textures", baseUrl + "/Textures");
m_environmentsModel->loadTextureBundle(textureBundlePath + "/Environments", baseUrl + "/Environments"); QVariantMap metaData;
QFile jsonFile(textureBundlePath + "/texture_bundle.json");
if (jsonFile.open(QIODevice::ReadOnly | QIODevice::Text))
metaData = QJsonDocument::fromJson(jsonFile.readAll()).toVariant().toMap();
m_texturesModel->loadTextureBundle(textureBundlePath + "/Textures",
baseUrl + "/Textures", metaData);
m_environmentsModel->loadTextureBundle(textureBundlePath + "/Environments",
baseUrl + "/Environments", metaData);
m_quickWidget->rootContext()->setContextProperties({ m_quickWidget->rootContext()->setContextProperties({
{"rootView", QVariant::fromValue(this)}, {"rootView", QVariant::fromValue(this)},
@@ -271,7 +281,7 @@ void ContentLibraryWidget::addImage(ContentLibraryTexture *tex)
if (!tex->isDownloaded()) if (!tex->isDownloaded())
return; return;
emit addTextureRequested(tex->realTexturePath(), AddTextureMode::Image); emit addTextureRequested(tex->downloadedTexturePath(), AddTextureMode::Image);
} }
void ContentLibraryWidget::addTexture(ContentLibraryTexture *tex) void ContentLibraryWidget::addTexture(ContentLibraryTexture *tex)
@@ -279,7 +289,7 @@ void ContentLibraryWidget::addTexture(ContentLibraryTexture *tex)
if (!tex->isDownloaded()) if (!tex->isDownloaded())
return; return;
emit addTextureRequested(tex->realTexturePath(), AddTextureMode::Texture); emit addTextureRequested(tex->downloadedTexturePath(), AddTextureMode::Texture);
} }
void ContentLibraryWidget::addLightProbe(ContentLibraryTexture *tex) void ContentLibraryWidget::addLightProbe(ContentLibraryTexture *tex)
@@ -287,7 +297,7 @@ void ContentLibraryWidget::addLightProbe(ContentLibraryTexture *tex)
if (!tex->isDownloaded()) if (!tex->isDownloaded())
return; return;
emit addTextureRequested(tex->realTexturePath(), AddTextureMode::LightProbe); emit addTextureRequested(tex->downloadedTexturePath(), AddTextureMode::LightProbe);
} }
void ContentLibraryWidget::updateSceneEnvState() void ContentLibraryWidget::updateSceneEnvState()

View File

@@ -11,15 +11,21 @@
namespace QmlDesigner { namespace QmlDesigner {
QString QmlDesigner::ImageUtils::imageInfo(const QString &path, bool fetchSizeInfo) QString ImageUtils::imageInfo(const QSize &dimensions, qint64 sizeInBytes)
{
return QLatin1String("%1 x %2\n%3")
.arg(QString::number(dimensions.width()),
QString::number(dimensions.height()),
QLocale::system().formattedDataSize(
sizeInBytes, 2, QLocale::DataSizeTraditionalFormat));
}
QString QmlDesigner::ImageUtils::imageInfo(const QString &path)
{ {
QFileInfo info(path); QFileInfo info(path);
if (!info.exists()) if (!info.exists())
return {}; return {};
if (!fetchSizeInfo)
return QLatin1String("(%1)").arg(info.suffix());
int width = 0; int width = 0;
int height = 0; int height = 0;
const QString suffix = info.suffix(); const QString suffix = info.suffix();
@@ -43,14 +49,10 @@ QString QmlDesigner::ImageUtils::imageInfo(const QString &path, bool fetchSizeIn
height = size.height(); height = size.height();
} }
if (width == 0 && height == 0) if (width <= 0 || height <= 0)
return {}; return {};
return QLatin1String("%1 x %2\n%3 (%4)") return imageInfo(QSize(width, height), info.size());
.arg(QString::number(width),
QString::number(height),
QLocale::system().formattedDataSize(info.size(), 2, QLocale::DataSizeTraditionalFormat),
info.suffix());
} }
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#pragma once #pragma once
#include <QSize>
#include <QString> #include <QString>
namespace QmlDesigner { namespace QmlDesigner {
@@ -11,7 +12,8 @@ class ImageUtils
public: public:
ImageUtils(); ImageUtils();
static QString imageInfo(const QString &path, bool sizeInfo = true); static QString imageInfo(const QSize &dimensions, qint64 sizeInBytes);
static QString imageInfo(const QString &path);
}; };
} // namespace QmlDesigner } // namespace QmlDesigner