QmlDesigner: Implement exporting an imported 3D component

Fixes: QDS-12904
Change-Id: If8c597f00864a08a4ce47d7c09239a5c70a5026a
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
This commit is contained in:
Mahmoud Badri
2024-06-06 13:49:01 +03:00
parent b9f28b957c
commit ddbb9937a1
2 changed files with 83 additions and 12 deletions

View File

@@ -19,6 +19,7 @@
#include <documentmanager.h> #include <documentmanager.h>
#include <enumeration.h> #include <enumeration.h>
#include <externaldependenciesinterface.h> #include <externaldependenciesinterface.h>
#include <model/modelutils.h>
#include <nodelistproperty.h> #include <nodelistproperty.h>
#include <qmldesignerconstants.h> #include <qmldesignerconstants.h>
#include <qmldesignerplugin.h> #include <qmldesignerplugin.h>
@@ -26,7 +27,6 @@
#include <uniquename.h> #include <uniquename.h>
#include <utils3d.h> #include <utils3d.h>
#include <variantproperty.h> #include <variantproperty.h>
#include <solutions/zip/zipwriter.h> #include <solutions/zip/zipwriter.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
@@ -379,7 +379,11 @@ void ContentLibraryView::customNotification(const AbstractView *view,
else else
addLib3DItem(nodeList.first()); addLib3DItem(nodeList.first());
} else if (identifier == "export_item_as_bundle") { } else if (identifier == "export_item_as_bundle") {
exportLib3DItem(nodeList.first()); // TODO: support exporting 2D items
if (nodeList.first().isComponent())
exportLib3DComponent(nodeList.first());
else
exportLib3DItem(nodeList.first());
} else if (identifier == "export_material_as_bundle") { } else if (identifier == "export_material_as_bundle") {
exportLib3DItem(nodeList.first(), data.first().value<QPixmap>()); exportLib3DItem(nodeList.first(), data.first().value<QPixmap>());
} }
@@ -676,14 +680,10 @@ void ContentLibraryView::addLibAssets(const QStringList &paths)
void ContentLibraryView::addLib3DComponent(const ModelNode &node) void ContentLibraryView::addLib3DComponent(const ModelNode &node)
{ {
auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
QString compBaseName = node.simplifiedTypeName(); QString compBaseName = node.simplifiedTypeName();
QString compFileName = compBaseName + ".qml"; QString compFileName = compBaseName + ".qml";
Utils::FilePath compDir = DocumentManager::currentProjectDirPath() auto compDir = Utils::FilePath::fromString(ModelUtils::componentFilePath(node)).parentDir();
.pathAppended(compUtils.import3dTypePath() + '/' + compBaseName);
auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/"); auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/");
// confirm overwrite if an item with same name exists // confirm overwrite if an item with same name exists
@@ -750,6 +750,71 @@ void ContentLibraryView::addLib3DComponent(const ModelNode &node)
m_widget->userModel()->add3DItem(compBaseName, compFileName, m_iconSavePath.toUrl(), filesList); m_widget->userModel()->add3DItem(compBaseName, compFileName, m_iconSavePath.toUrl(), filesList);
} }
void ContentLibraryView::exportLib3DComponent(const ModelNode &node)
{
QString exportPath = getExportPath(node);
if (exportPath.isEmpty())
return;
// targetPath is a temp path for collecting and zipping assets, actual export target is where
// the user chose to export (i.e. exportPath)
QTemporaryDir tempDir;
QTC_ASSERT(tempDir.isValid(), return);
auto targetPath = Utils::FilePath::fromString(tempDir.path());
m_zipWriter = std::make_unique<ZipWriter>(exportPath);
QString compBaseName = node.simplifiedTypeName();
QString compFileName = compBaseName + ".qml";
auto compDir = Utils::FilePath::fromString(ModelUtils::componentFilePath(node)).parentDir();
QString iconPath = QLatin1String("icons/%1").arg(UniqueName::generateId(compBaseName) + ".png");
const Utils::FilePaths sourceFiles = compDir.dirEntries({{}, QDir::Files, QDirIterator::Subdirectories});
const QStringList ignoreList {"_importdata.json", "qmldir", compBaseName + ".hints"};
QStringList filesList; // 3D component's assets (dependencies)
for (const Utils::FilePath &sourcePath : sourceFiles) {
Utils::FilePath relativePath = sourcePath.relativePathFrom(compDir);
if (ignoreList.contains(sourcePath.fileName()) || relativePath.startsWith("source scene"))
continue;
m_zipWriter->addFile(relativePath.toFSPathString(), sourcePath.fileContents().value_or(""));
if (sourcePath.fileName() != compFileName) // skip component file (only collect dependencies)
filesList.append(relativePath.path());
}
// add the item to the bundle json
QJsonObject jsonObj;
QJsonArray itemsArr;
itemsArr.append(QJsonObject {
{"name", node.simplifiedTypeName()},
{"qml", compFileName},
{"icon", iconPath},
{"files", QJsonArray::fromStringList(filesList)}
});
jsonObj["items"] = itemsArr;
Utils::FilePath jsonFilePath = targetPath.pathAppended(Constants::BUNDLE_JSON_FILENAME);
m_zipWriter->addFile(jsonFilePath.fileName(), QJsonDocument(jsonObj).toJson());
// add icon
m_iconSavePath = targetPath.pathAppended(iconPath);
m_iconSavePath.parentDir().ensureWritableDir();
getImageFromCache(compDir.pathAppended(compFileName).path(), [&](const QImage &image) {
QByteArray iconByteArray;
QBuffer buffer(&iconByteArray);
buffer.open(QIODevice::WriteOnly);
image.save(&buffer, "PNG");
m_zipWriter->addFile("icons/" + m_iconSavePath.fileName(), iconByteArray);
m_zipWriter->close();
});
}
void ContentLibraryView::addLib3DItem(const ModelNode &node) void ContentLibraryView::addLib3DItem(const ModelNode &node)
{ {
auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/"); auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/");
@@ -810,9 +875,8 @@ void ContentLibraryView::addLib3DItem(const ModelNode &node)
m_widget->userModel()->add3DItem(name, qml, m_iconSavePath.toUrl(), depAssetsList); m_widget->userModel()->add3DItem(name, qml, m_iconSavePath.toUrl(), depAssetsList);
} }
void ContentLibraryView::exportLib3DItem(const ModelNode &node, const QPixmap &iconPixmap) QString ContentLibraryView::getExportPath(const ModelNode &node)
{ {
// prompt and get the exported bundle path
QString defaultExportFileName = QLatin1String("%1.%2").arg(node.id(), Constants::BUNDLE_SUFFIX); QString defaultExportFileName = QLatin1String("%1.%2").arg(node.id(), Constants::BUNDLE_SUFFIX);
Utils::FilePath projectFP = DocumentManager::currentProjectDirPath(); Utils::FilePath projectFP = DocumentManager::currentProjectDirPath();
if (projectFP.isEmpty()) { if (projectFP.isEmpty()) {
@@ -822,9 +886,14 @@ void ContentLibraryView::exportLib3DItem(const ModelNode &node, const QPixmap &i
QString dialogTitle = node.metaInfo().isQtQuick3DMaterial() ? tr("Export Material") QString dialogTitle = node.metaInfo().isQtQuick3DMaterial() ? tr("Export Material")
: tr("Export Component"); : tr("Export Component");
QString exportPath = QFileDialog::getSaveFileName(m_widget, dialogTitle, return QFileDialog::getSaveFileName(m_widget, dialogTitle,
projectFP.pathAppended(defaultExportFileName).toFSPathString(), projectFP.pathAppended(defaultExportFileName).toFSPathString(),
tr("Qt Design Studio Bundle Files (*.%1)").arg(Constants::BUNDLE_SUFFIX)); tr("Qt Design Studio Bundle Files (*.%1)").arg(Constants::BUNDLE_SUFFIX));
}
void ContentLibraryView::exportLib3DItem(const ModelNode &node, const QPixmap &iconPixmap)
{
QString exportPath = getExportPath(node);
if (exportPath.isEmpty()) if (exportPath.isEmpty())
return; return;

View File

@@ -62,10 +62,12 @@ private:
void addLibMaterial(const ModelNode &node, const QPixmap &iconPixmap); void addLibMaterial(const ModelNode &node, const QPixmap &iconPixmap);
void addLibAssets(const QStringList &paths); void addLibAssets(const QStringList &paths);
void addLib3DComponent(const ModelNode &node); void addLib3DComponent(const ModelNode &node);
void exportLib3DComponent(const ModelNode &node);
void addLib3DItem(const ModelNode &node); void addLib3DItem(const ModelNode &node);
void exportLib3DItem(const ModelNode &node, const QPixmap &iconPixmap = {}); void exportLib3DItem(const ModelNode &node, const QPixmap &iconPixmap = {});
void getImageFromCache(const QString &qmlPath, void getImageFromCache(const QString &qmlPath,
std::function<void(const QImage &image)> successCallback); std::function<void(const QImage &image)> successCallback);
QString getExportPath(const ModelNode &node);
QPair<QString, QSet<QString>> modelNodeToQmlString(const ModelNode &node, int depth = 0); QPair<QString, QSet<QString>> modelNodeToQmlString(const ModelNode &node, int depth = 0);
#ifdef QDS_USE_PROJECTSTORAGE #ifdef QDS_USE_PROJECTSTORAGE