QmlDesigner: Allow adding a folder to content library

Change-Id: If44fdc0f0a7c59011854fd358f0542ce35ac1079
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Ali Kianian <ali.kianian@qt.io>
This commit is contained in:
Mahmoud Badri
2025-03-24 17:01:24 +02:00
parent 88b8d5ea6c
commit ee1a725da1
10 changed files with 430 additions and 189 deletions

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick.Layouts
import Qt.labs.qmlmodels
import HelperWidgets as HelperWidgets
import StudioControls as StudioControls
@@ -79,9 +80,34 @@ Item {
}
}
ColumnLayout {
id: col
anchors.fill: parent
spacing: 0
Rectangle {
id: toolbar
width: parent.width
height: StudioTheme.Values.toolbarHeight
color: StudioTheme.Values.themeToolbarBackground
HelperWidgets.AbstractButton {
style: StudioTheme.Values.viewBarButtonStyle
buttonIcon: StudioTheme.Constants.add_medium
enabled: hasMaterial && hasModelSelection && hasQuick3DImport && hasMaterialLibrary
tooltip: qsTr("Add a custom bundle folder.")
onClicked: ContentLibraryBackend.rootView.browseBundleFolder()
x: 5 // left margin
}
}
HelperWidgets.ScrollView {
id: scrollView
anchors.fill: parent
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
interactive: !ctxMenuItem.opened && !ctxMenuTexture.opened
@@ -106,12 +132,20 @@ Item {
caption: categoryTitle
dropEnabled: true
category: "ContentLib_User"
showCloseButton: section.isCustomCat
closeButtonToolTip: qsTr("Remove folder")
closeButtonIcon: StudioTheme.Constants.deletepermanently_small
onCloseButtonClicked: {
ContentLibraryBackend.userModel.removeBundleDir(index)
}
function expandSection() {
section.expanded = true
}
property alias count: repeater.count
property bool isCustomCat: !["Textures", "Materials", "3D"].includes(section.caption);
onCountChanged: root.assignMaxCount()
@@ -125,6 +159,7 @@ Item {
drag.accepted = (categoryTitle === "Textures" && hasTexture)
|| (categoryTitle === "Materials" && drag.formats[0] === "application/vnd.qtdesignstudio.material")
|| (categoryTitle === "3D" && has3DNode)
|| (section.isCustomCat && hasTexture)
section.highlight = drag.accepted
}
@@ -147,6 +182,11 @@ Item {
ContentLibraryBackend.rootView.acceptMaterialDrop(drag.getDataAsString(drag.formats[0]))
} else if (categoryTitle === "3D") {
ContentLibraryBackend.rootView.accept3DDrop(drag.getDataAsArrayBuffer(drag.formats[0]))
} else { // custom bundle folder
if (drag.formats[0] === "application/vnd.qtdesignstudio.assets")
ContentLibraryBackend.rootView.acceptTexturesDrop(drag.urls, categoryBundlePath)
else if (drag.formats[0] === "application/vnd.qtdesignstudio.texture")
ContentLibraryBackend.rootView.acceptTextureDrop(drag.getDataAsString(drag.formats[0]), categoryBundlePath)
}
}
@@ -157,6 +197,7 @@ Item {
Repeater {
id: repeater
model: categoryItems
delegate: DelegateChooser {
@@ -215,17 +256,19 @@ Item {
: categoryTitle.toLowerCase()
if (!ContentLibraryBackend.rootView.isQt6Project) {
qsTr("<b>Content Library</b> is not supported in Qt5 projects.")
} else if (!ContentLibraryBackend.rootView.hasQuick3DImport && categoryTitle !== "Textures") {
} else if (!ContentLibraryBackend.rootView.hasQuick3DImport
&& categoryTitle !== "Textures" && !section.isCustomCat) {
qsTr('To use %1, add the <b>QtQuick3D</b> module and the <b>View3D</b>
component in the <b>Components</b> view, or click
<a href=\"#add_import\"><span style=\"text-decoration:none;color:%2\">
here</span></a>.')
.arg(categoryName)
.arg(StudioTheme.Values.themeInteraction)
} else if (!ContentLibraryBackend.rootView.hasMaterialLibrary && categoryTitle !== "Textures") {
} else if (!ContentLibraryBackend.rootView.hasMaterialLibrary
&& categoryTitle !== "Textures" && !section.isCustomCat) {
qsTr("<b>Content Library</b> is disabled inside a non-visual component.")
} else if (categoryEmpty) {
qsTr("There are no "+ categoryName + " in the <b>User Assets</b>.")
qsTr("There are no items in this category.")
} else {
""
}
@@ -250,4 +293,5 @@ Item {
}
}
}
}
}

View File

@@ -9,6 +9,7 @@
#include "contentlibrarytexture.h"
#include "contentlibrarywidget.h"
#include <asset.h>
#include <bundleimporter.h>
#include <designerpaths.h>
#include <imageutils.h>
@@ -16,6 +17,7 @@
#include <qmldesignerplugin.h>
#include <utils/algorithm.h>
#include <utils/filesystemwatcher.h>
#include <utils/qtcassert.h>
#include <QFileInfo>
@@ -28,10 +30,18 @@ namespace QmlDesigner {
ContentLibraryUserModel::ContentLibraryUserModel(ContentLibraryWidget *parent)
: QAbstractListModel(parent)
, m_widget(parent)
, m_fileWatcher(Utils::makeUniqueObjectPtr<Utils::FileSystemWatcher>(parent))
{
createCategories();
connect(m_fileWatcher.get(), &Utils::FileSystemWatcher::directoryChanged, this,
[this] (const QString &dirPath) {
reloadTextureCategory(Utils::FilePath::fromString(dirPath));
});
}
ContentLibraryUserModel::~ContentLibraryUserModel() = default;
int ContentLibraryUserModel::rowCount(const QModelIndex &) const
{
return m_userCategories.size();
@@ -44,17 +54,22 @@ QVariant ContentLibraryUserModel::data(const QModelIndex &index, int role) const
UserCategory *currCat = m_userCategories.at(index.row());
if (role == TitleRole)
switch (role) {
case TitleRole:
return currCat->title();
if (role == ItemsRole)
case BundlePathRole:
return currCat->bundlePath().toVariant();
case ItemsRole:
return QVariant::fromValue(currCat->items());
if (role == NoMatchRole)
case NoMatchRole:
return currCat->noMatch();
if (role == EmptyRole)
case EmptyRole:
return currCat->isEmpty();
}
return {};
}
@@ -76,6 +91,71 @@ void ContentLibraryUserModel::createCategories()
compUtils.user3DBundleId()};
m_userCategories << catMaterial << catTexture << cat3D;
loadCustomCategories(userBundlePath);
}
void ContentLibraryUserModel::loadCustomCategories(const Utils::FilePath &userBundlePath)
{
auto jsonFilePath = userBundlePath.pathAppended(Constants::CUSTOM_BUNDLES_JSON_FILENAME);
if (!jsonFilePath.exists()) {
const QString jsonContent = QStringLiteral(R"({ "version": "%1", "items": {}})")
.arg(CUSTOM_BUNDLES_JSON_FILE_VERSION);
Utils::expected_str<qint64> res = jsonFilePath.writeFileContents(jsonContent.toLatin1());
QTC_ASSERT_EXPECTED(res, return);
}
Utils::expected_str<QByteArray> jsonContents = jsonFilePath.fileContents();
QTC_ASSERT_EXPECTED(jsonContents, return);
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonContents.value());
QTC_ASSERT(!jsonDoc.isNull(), return);
m_customCatsRootObj = jsonDoc.object();
m_customCatsObj = m_customCatsRootObj.value("items").toObject();
for (auto it = m_customCatsObj.constBegin(); it != m_customCatsObj.constEnd(); ++it) {
auto dirPath = Utils::FilePath::fromString(it.key());
if (!dirPath.exists())
continue;
addBundleDir(dirPath);
}
}
bool ContentLibraryUserModel::bundleDirExists(const QString &dirPath) const
{
return m_customCatsObj.contains(dirPath);
}
void ContentLibraryUserModel::addBundleDir(const Utils::FilePath &dirPath)
{
QTC_ASSERT(!dirPath.isEmpty(), return);
// TODO: detect if a bundle exists in the dir, determine its type, and create a matching category.
// For now we consider a custom folder as a texture bundle
auto newCat = new UserTextureCategory{dirPath.fileName(), dirPath};
newCat->loadBundle();
beginInsertRows({}, m_userCategories.size(), m_userCategories.size());
m_userCategories << newCat;
endInsertRows();
m_fileWatcher->addDirectory(dirPath, Utils::FileSystemWatcher::WatchAllChanges);
// add the folder to custom bundles json file if it is missing
const QString dirPathStr = dirPath.toFSPathString();
if (!m_customCatsObj.contains(dirPathStr)) {
m_customCatsObj.insert(dirPathStr, QJsonObject{});
m_customCatsRootObj["items"] = m_customCatsObj;
auto userBundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User");
auto jsonFilePath = userBundlePath.pathAppended(Constants::CUSTOM_BUNDLES_JSON_FILENAME);
auto result = jsonFilePath.writeFileContents(QJsonDocument(m_customCatsRootObj).toJson());
QTC_ASSERT_EXPECTED(result,);
}
}
void ContentLibraryUserModel::addItem(const QString &bundleId, const QString &name,
@@ -102,22 +182,36 @@ void ContentLibraryUserModel::refreshSection(const QString &bundleId)
updateIsEmpty();
}
void ContentLibraryUserModel::addTextures(const Utils::FilePaths &paths)
void ContentLibraryUserModel::addTextures(const Utils::FilePaths &paths, const Utils::FilePath &bundlePath)
{
auto texCat = qobject_cast<UserTextureCategory *>(m_userCategories[TexturesSectionIdx]);
int catIdx = bundlePathToIndex(bundlePath);
UserTextureCategory *texCat = qobject_cast<UserTextureCategory *>(m_userCategories.at(catIdx));
QTC_ASSERT(texCat, return);
texCat->addItems(paths);
emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx), {ItemsRole, EmptyRole});
emit dataChanged(index(catIdx), index(catIdx), {ItemsRole, EmptyRole});
updateIsEmpty();
}
void ContentLibraryUserModel::removeTextures(const QStringList &fileNames)
void ContentLibraryUserModel::reloadTextureCategory(const Utils::FilePath &dirPath)
{
int catIdx = bundlePathToIndex(dirPath);
UserTextureCategory *texCat = qobject_cast<UserTextureCategory *>(m_userCategories.at(catIdx));
QTC_ASSERT(texCat, return);
const Utils::FilePaths &paths = dirPath.dirEntries({Asset::supportedImageSuffixes(), QDir::Files});
texCat->clearItems();
addTextures(paths, dirPath);
}
void ContentLibraryUserModel::removeTextures(const QStringList &fileNames, const Utils::FilePath &bundlePath)
{
// note: this method doesn't refresh the model after textures removal
auto texCat = qobject_cast<UserTextureCategory *>(m_userCategories[TexturesSectionIdx]);
int catIdx = bundlePathToIndex(bundlePath);
UserTextureCategory *texCat = qobject_cast<UserTextureCategory *>(m_userCategories.at(catIdx));
QTC_ASSERT(texCat, return);
const QObjectList items = texCat->items();
@@ -137,13 +231,28 @@ void ContentLibraryUserModel::removeTexture(ContentLibraryTexture *tex, bool ref
Utils::FilePath::fromString(tex->iconPath()).removeFile();
// remove from model
m_userCategories[TexturesSectionIdx]->removeItem(tex);
UserTextureCategory *itemCat = qobject_cast<UserTextureCategory *>(tex->parent());
QTC_ASSERT(itemCat, return);
itemCat->removeItem(tex);
// update model
if (refresh) {
emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx));
int catIdx = bundlePathToIndex(itemCat->bundlePath());
emit dataChanged(index(catIdx), index(catIdx));
updateIsEmpty();
}
const QString bundlePathStr = itemCat->bundlePath().toFSPathString();
if (m_customCatsObj.contains(bundlePathStr)) {
m_customCatsObj.remove(bundlePathStr);
m_customCatsRootObj["items"] = m_customCatsObj;
auto userBundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User");
auto jsonFilePath = userBundlePath.pathAppended(Constants::CUSTOM_BUNDLES_JSON_FILENAME);
auto result = jsonFilePath.writeFileContents(QJsonDocument(m_customCatsRootObj).toJson());
QTC_ASSERT_EXPECTED(result,);
}
}
void ContentLibraryUserModel::removeFromContentLib(QObject *item)
@@ -154,6 +263,30 @@ void ContentLibraryUserModel::removeFromContentLib(QObject *item)
removeItem(castedItem);
}
void ContentLibraryUserModel::removeBundleDir(int catIdx)
{
auto texCat = qobject_cast<UserTextureCategory *>(m_userCategories.at(catIdx));
QTC_ASSERT(texCat, return);
QString dirPath = texCat->bundlePath().toFSPathString();
// remove from json
QTC_ASSERT(m_customCatsObj.contains(dirPath), return);
m_customCatsObj.remove(dirPath);
m_customCatsRootObj["items"] = m_customCatsObj;
auto userBundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User");
auto jsonFilePath = userBundlePath.pathAppended(Constants::CUSTOM_BUNDLES_JSON_FILENAME);
auto result = jsonFilePath.writeFileContents(QJsonDocument(m_customCatsRootObj).toJson());
QTC_ASSERT_EXPECTED(result, return);
// remove from model
beginRemoveRows({}, catIdx, catIdx);
delete texCat;
m_userCategories.removeAt(catIdx);
endRemoveRows();
}
void ContentLibraryUserModel::removeItemByName(const QString &qmlFileName, const QString &bundleId)
{
ContentLibraryItem *itemToRemove = nullptr;
@@ -239,10 +372,27 @@ ContentLibraryUserModel::SectionIndex ContentLibraryUserModel::bundleIdToSection
return {};
}
int ContentLibraryUserModel::bundlePathToIndex(const QString &bundlePath) const
{
return bundlePathToIndex(Utils::FilePath::fromString(bundlePath));
}
int ContentLibraryUserModel::bundlePathToIndex(const Utils::FilePath &bundlePath) const
{
for (int i = 0; i < m_userCategories.size(); ++i) {
if (m_userCategories.at(i)->bundlePath() == bundlePath)
return i;
}
qWarning() << __FUNCTION__ << "Invalid bundlePath:" << bundlePath;
return -1;
}
QHash<int, QByteArray> ContentLibraryUserModel::roleNames() const
{
static const QHash<int, QByteArray> roles {
{TitleRole, "categoryTitle"},
{BundlePathRole, "categoryBundlePath"},
{EmptyRole, "categoryEmpty"},
{ItemsRole, "categoryItems"},
{NoMatchRole, "categoryNoMatch"}

View File

@@ -6,10 +6,15 @@
#include "usercategory.h"
#include <utils/filepath.h>
#include <utils/uniqueobjectptr.h>
#include <QAbstractListModel>
#include <QJsonObject>
namespace Utils {
class FileSystemWatcher;
}
QT_FORWARD_DECLARE_CLASS(QUrl)
namespace QmlDesigner {
@@ -28,6 +33,7 @@ class ContentLibraryUserModel : public QAbstractListModel
public:
ContentLibraryUserModel(ContentLibraryWidget *parent = nullptr);
~ContentLibraryUserModel();
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
@@ -50,8 +56,9 @@ public:
void addItem(const QString &bundleId, const QString &name, const QString &qml,const QUrl &icon,
const QStringList &files);
void refreshSection(const QString &bundleId);
void addTextures(const Utils::FilePaths &paths);
void removeTextures(const QStringList &fileNames);
void addTextures(const Utils::FilePaths &paths, const Utils::FilePath &bundlePath);
void reloadTextureCategory(const Utils::FilePath &dirPath);
void removeTextures(const QStringList &fileNames, const Utils::FilePath &bundlePath);
void removeItemByName(const QString &qmlFileName, const QString &bundleId);
@@ -59,12 +66,15 @@ public:
QJsonObject &bundleObjectRef(const QString &bundleId);
void loadBundles(bool force = false);
void addBundleDir(const Utils::FilePath &dirPath);
bool bundleDirExists(const QString &dirPath) const;
Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryItem *mat, bool add = false);
Q_INVOKABLE void addToProject(ContentLibraryItem *item);
Q_INVOKABLE void removeFromProject(QObject *item);
Q_INVOKABLE void removeTexture(QmlDesigner::ContentLibraryTexture *tex, bool refresh = true);
Q_INVOKABLE void removeFromContentLib(QObject *item);
Q_INVOKABLE void removeBundleDir(int catIdx);
signals:
void hasRequiredQuick3DImportChanged();
@@ -79,14 +89,20 @@ private:
EffectsSectionIdx };
void createCategories();
void loadCustomCategories(const Utils::FilePath &userBundlePath);
void loadMaterialBundle();
void load3DBundle();
void loadTextureBundle();
void removeItem(ContentLibraryItem *item);
SectionIndex bundleIdToSectionIndex(const QString &bundleId) const;
int bundlePathToIndex(const QString &bundlePath) const;
int bundlePathToIndex(const Utils::FilePath &bundlePath) const;
ContentLibraryWidget *m_widget = nullptr;
QJsonObject m_customCatsRootObj;
QJsonObject m_customCatsObj;
QString m_searchText;
Utils::UniqueObjectPtr<Utils::FileSystemWatcher> m_fileWatcher;
QList<UserCategory *> m_userCategories;
@@ -95,7 +111,9 @@ private:
int m_quick3dMajorVersion = -1;
int m_quick3dMinorVersion = -1;
enum Roles { TitleRole = Qt::UserRole + 1, ItemsRole, EmptyRole, NoMatchRole };
enum Roles { TitleRole = Qt::UserRole + 1, BundlePathRole, ItemsRole, EmptyRole, NoMatchRole };
static constexpr char CUSTOM_BUNDLES_JSON_FILE_VERSION[] = "1.0";
};
} // namespace QmlDesigner

View File

@@ -91,7 +91,7 @@ WidgetInfo ContentLibraryView::widgetInfo()
});
connect(m_widget, &ContentLibraryWidget::acceptTextureDrop, this,
[this](const QString &internalId) {
[this](const QString &internalId, const QString &bundlePath) {
ModelNode texNode = QmlDesignerPlugin::instance()->viewManager()
.view()->modelNodeForInternalId(internalId.toInt());
auto [qmlString, depAssets] = m_bundleHelper->modelNodeToQmlString(texNode);
@@ -104,11 +104,11 @@ WidgetInfo ContentLibraryView::widgetInfo()
paths.append(path);
}
addLibAssets(paths);
addLibAssets(paths, bundlePath);
});
connect(m_widget, &ContentLibraryWidget::acceptTexturesDrop, this,
[this](const QList<QUrl> &urls) {
[this](const QList<QUrl> &urls, const QString &bundlePath) {
QStringList paths;
for (const QUrl &url : urls) {
@@ -117,7 +117,7 @@ WidgetInfo ContentLibraryView::widgetInfo()
if (Asset(path).isValidTextureSource())
paths.append(path);
}
addLibAssets(paths);
addLibAssets(paths, bundlePath);
});
connect(m_widget, &ContentLibraryWidget::acceptMaterialDrop, this,
@@ -569,14 +569,16 @@ void ContentLibraryView::applyBundleMaterialToDropTarget(const ModelNode &bundle
}
#endif
void ContentLibraryView::addLibAssets(const QStringList &paths)
void ContentLibraryView::addLibAssets(const QStringList &paths, const QString &bundlePath)
{
auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/textures");
auto fullBundlePath = Utils::FilePath::fromString(bundlePath.isEmpty()
? Paths::bundlesPathSetting() + "/User/textures"
: bundlePath);
Utils::FilePaths sourcePathsToAdd;
Utils::FilePaths targetPathsToAdd;
QStringList fileNamesToRemove;
const QStringList existingAssetsFileNames = Utils::transform(bundlePath.dirEntries(QDir::Files),
const QStringList existingAssetsFileNames = Utils::transform(fullBundlePath.dirEntries(QDir::Files),
&Utils::FilePath::fileName);
for (const QString &path : paths) {
@@ -598,21 +600,13 @@ void ContentLibraryView::addLibAssets(const QStringList &paths)
}
// remove the to-be-overwritten resources from target bundle path
m_widget->userModel()->removeTextures(fileNamesToRemove);
m_widget->userModel()->removeTextures(fileNamesToRemove, fullBundlePath);
// copy resources to target bundle path
for (const Utils::FilePath &sourcePath : sourcePathsToAdd) {
Utils::FilePath targetPath = bundlePath.pathAppended(sourcePath.fileName());
Utils::FilePath targetPath = fullBundlePath.pathAppended(sourcePath.fileName());
Asset asset{sourcePath.toFSPathString()};
// save icon
QString iconSavePath = bundlePath.pathAppended("icons/" + sourcePath.baseName() + ".png")
.toFSPathString();
QPixmap icon = asset.pixmap({120, 120});
bool iconSaved = icon.save(iconSavePath);
if (!iconSaved)
qWarning() << __FUNCTION__ << "icon save failed";
// save asset
auto result = sourcePath.copyFile(targetPath);
QTC_ASSERT_EXPECTED(result,);
@@ -620,7 +614,7 @@ void ContentLibraryView::addLibAssets(const QStringList &paths)
targetPathsToAdd.append(targetPath);
}
m_widget->userModel()->addTextures(targetPathsToAdd);
m_widget->userModel()->addTextures(targetPathsToAdd, fullBundlePath);
}
// TODO: combine this method with BundleHelper::exportComponent()

View File

@@ -63,7 +63,7 @@ private:
bool isItemBundle(const QString &bundleId) const;
void active3DSceneChanged(qint32 sceneId);
void updateBundlesQuick3DVersion();
void addLibAssets(const QStringList &paths);
void addLibAssets(const QStringList &paths, const QString &bundlePath = {});
void addLib3DComponent(const ModelNode &node);
void addLibItem(const ModelNode &node, const QPixmap &iconPixmap = {});
void importBundleToContentLib();

View File

@@ -190,6 +190,21 @@ ContentLibraryWidget::~ContentLibraryWidget()
{
}
void ContentLibraryWidget::browseBundleFolder()
{
DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument();
QTC_ASSERT(document, return);
const QString currentDir = document->fileName().parentDir().toUrlishString();
QString dir = QFileDialog::getExistingDirectory(Core::ICore::dialogParent(),
tr("Choose Directory"),
currentDir,
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if (!dir.isEmpty() && !m_userModel->bundleDirExists(dir))
m_userModel->addBundleDir(Utils::FilePath::fromString(dir));
}
void ContentLibraryWidget::createImporter()
{
m_importer = new BundleImporter();

View File

@@ -104,6 +104,7 @@ public:
Q_INVOKABLE bool has3DNode(const QByteArray &data) const;
Q_INVOKABLE bool hasTexture(const QString &format, const QVariant &data) const;
Q_INVOKABLE void addQtQuick3D();
Q_INVOKABLE void browseBundleFolder();
QSize sizeHint() const override;
@@ -127,8 +128,8 @@ signals:
void hasModelSelectionChanged();
void importBundle();
void requestTab(int tabIndex);
void acceptTexturesDrop(const QList<QUrl> &urls);
void acceptTextureDrop(const QString &internalId);
void acceptTexturesDrop(const QList<QUrl> &urls, const QString &bundlePath = {});
void acceptTextureDrop(const QString &internalId, const QString &bundlePath = {});
void acceptMaterialDrop(const QString &internalId);
void accept3DDrop(const QByteArray &internalIds);
void importQtQuick3D();

View File

@@ -5,7 +5,7 @@
#include "contentlibrarytexture.h"
#include <designerpaths.h>
#include <asset.h>
#include <imageutils.h>
namespace QmlDesigner {
@@ -27,7 +27,7 @@ void UserTextureCategory::loadBundle(bool force)
m_bundlePath.ensureWritableDir();
m_bundlePath.pathAppended("icons").ensureWritableDir();
addItems(m_bundlePath.dirEntries(QDir::Files));
addItems(m_bundlePath.dirEntries({Asset::supportedImageSuffixes(), QDir::Files}));
m_bundleLoaded = true;
}
@@ -55,6 +55,14 @@ void UserTextureCategory::addItems(const Utils::FilePaths &paths)
QSize imgDims = info.first;
qint64 imgFileSize = info.second;
if (!iconFileInfo.exists()) { // generate an icon if one doesn't exist
Asset asset{filePath.toFSPathString()};
QPixmap icon = asset.pixmap({120, 120});
bool iconSaved = icon.save(iconFileInfo.filePath());
if (!iconSaved)
qWarning() << __FUNCTION__ << "icon save failed";
}
auto tex = new ContentLibraryTexture(this, iconFileInfo, dirPath, suffix, imgDims, imgFileSize);
m_items.append(tex);
}
@@ -63,4 +71,13 @@ void UserTextureCategory::addItems(const Utils::FilePaths &paths)
emit itemsChanged();
}
void UserTextureCategory::clearItems()
{
qDeleteAll(m_items);
m_items.clear();
setIsEmpty(true);
emit itemsChanged();
}
} // namespace QmlDesigner

View File

@@ -16,10 +16,11 @@ class UserTextureCategory : public UserCategory
public:
UserTextureCategory(const QString &title, const Utils::FilePath &bundlePath);
void loadBundle(bool force) override;
void loadBundle(bool force = false) override;
void filter(const QString &searchText) override;
void addItems(const Utils::FilePaths &paths);
void clearItems();
};
} // namespace QmlDesigner

View File

@@ -59,6 +59,7 @@ inline constexpr char EDIT3D_SNAP_CONFIG[] = "QmlDesigner.Editor3D.SnapConfig";
inline constexpr char EDIT3D_CAMERA_SPEED_CONFIG[] = "QmlDesigner.Editor3D.CameraSpeedConfig";
inline constexpr char BUNDLE_JSON_FILENAME[] = "bundle.json";
inline constexpr char CUSTOM_BUNDLES_JSON_FILENAME[] = "custom_bundles.json";
inline constexpr char BUNDLE_SUFFIX[] = "qdsbundle";
inline constexpr char COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE[] = "Effects";
inline constexpr char COMPONENT_BUNDLES_ASSET_REF_FILE[] = "_asset_ref.json";