forked from qt-creator/qt-creator
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:
@@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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"}
|
||||
|
@@ -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
|
||||
|
@@ -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()
|
||||
|
@@ -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();
|
||||
|
@@ -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();
|
||||
|
@@ -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();
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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";
|
||||
|
Reference in New Issue
Block a user