QmlDesigner: Move 3D asset import to puppet

Moved 3D asset import to puppet to ensure import always uses the
correct version of the QtQuick3D.

Fixes: QDS-3154
Change-Id: I630a833e7231383b87bf8b7214d3545d12de15ab
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Miikka Heikkinen
2021-01-22 17:51:46 +02:00
parent f15fef7ee1
commit 99a3ddbd23
27 changed files with 507 additions and 251 deletions

View File

@@ -326,14 +326,6 @@ extend_qtc_plugin(QmlDesigner
itemlibraryiconimageprovider.cpp itemlibraryiconimageprovider.h
)
find_package(Qt5 COMPONENTS Quick3DAssetImport QUIET)
extend_qtc_plugin(QmlDesigner
CONDITION TARGET Qt5::Quick3DAssetImport
FEATURE_INFO "Qt Quick 3D asset import"
DEPENDS Qt5::Quick3DAssetImportPrivate
DEFINES IMPORT_QUICK3D_ASSETS
)
extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX components/navigator
SOURCES

View File

@@ -199,6 +199,15 @@ void DesignerActionManager::registerAddResourceHandler(const AddResourceHandler
m_addResourceHandler.append(handler);
}
void DesignerActionManager::unregisterAddResourceHandlers(const QString &category)
{
for (int i = m_addResourceHandler.size() - 1; i >= 0 ; --i) {
const AddResourceHandler &handler = m_addResourceHandler[i];
if (handler.category == category)
m_addResourceHandler.removeAt(i);
}
}
void DesignerActionManager::registerModelNodePreviewHandler(const ModelNodePreviewImageHandler &handler)
{
m_modelNodePreviewImageHandlers.append(handler);

View File

@@ -129,6 +129,7 @@ public:
QList<AddResourceHandler> addResourceHandler() const;
void registerAddResourceHandler(const AddResourceHandler &handler);
void unregisterAddResourceHandlers(const QString &category);
void registerModelNodePreviewHandler(const ModelNodePreviewImageHandler &handler);
bool hasModelNodePreviewHandler(const ModelNode &node) const;

View File

@@ -1,10 +1,5 @@
VPATH += $$PWD
qtHaveModule(quick3dassetimport) {
QT *= quick3dassetimport-private
DEFINES *= IMPORT_QUICK3D_ASSETS
}
# Input
HEADERS += itemlibraryview.h \
$$PWD/itemlibraryiconimageprovider.h \

View File

@@ -71,10 +71,13 @@ static const int rowHeight = 26;
}
ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(const QStringList &importFiles,
const QString &defaulTargetDirectory, QWidget *parent) :
QDialog(parent),
ui(new Ui::ItemLibraryAssetImportDialog),
m_importer(this)
const QString &defaulTargetDirectory,
const QVariantMap &supportedExts,
const QVariantMap &supportedOpts,
QWidget *parent) :
QDialog(parent)
, ui(new Ui::ItemLibraryAssetImportDialog)
, m_importer(this)
{
setModal(true);
ui->setupUi(this);
@@ -83,15 +86,24 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(const QStringList &im
m_outputFormatter->setPlainTextEdit(ui->plainTextEdit);
// Skip unsupported assets
bool skipSome = false;
QHash<QString, bool> supportMap;
for (const auto &file : importFiles) {
if (m_importer.isQuick3DAsset(file))
QString suffix = QFileInfo(file).suffix();
if (!supportMap.contains(suffix)) {
bool supported = false;
for (const auto &exts : supportedExts) {
if (exts.toStringList().contains(suffix)) {
supported = true;
break;
}
}
supportMap.insert(suffix, supported);
}
if (supportMap[suffix])
m_quick3DFiles << file;
else
skipSome = true;
}
if (skipSome)
if (m_quick3DFiles.size() != importFiles.size())
addWarning("Cannot import 3D and other assets simultaneously. Skipping non-3D assets.");
ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Import"));
@@ -153,14 +165,12 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(const QStringList &im
m_quick3DImportPath = candidatePath;
if (!m_quick3DFiles.isEmpty()) {
const QHash<QString, QVariantMap> allOptions = m_importer.allOptions();
const QHash<QString, QStringList> supportedExtensions = m_importer.supportedExtensions();
QVector<QJsonObject> groups;
auto optIt = allOptions.constBegin();
auto optIt = supportedOpts.constBegin();
int optIndex = 0;
while (optIt != allOptions.constEnd()) {
QJsonObject options = QJsonObject::fromVariantMap(optIt.value());
while (optIt != supportedOpts.constEnd()) {
QJsonObject options = QJsonObject::fromVariantMap(qvariant_cast<QVariantMap>(optIt.value()));
m_importOptions << options.value("options").toObject();
groups << options.value("groups").toObject();
const auto &exts = optIt.key().split(':');
@@ -173,10 +183,10 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(const QStringList &im
// Create tab for each supported extension group that also has files included in the import
QMap<QString, int> tabMap; // QMap used for alphabetical order
for (const auto &file : qAsConst(m_quick3DFiles)) {
auto extIt = supportedExtensions.constBegin();
auto extIt = supportedExts.constBegin();
QString ext = QFileInfo(file).suffix().toLower();
while (extIt != supportedExtensions.constEnd()) {
if (!tabMap.contains(extIt.key()) && extIt.value().contains(ext)) {
while (extIt != supportedExts.constEnd()) {
if (!tabMap.contains(extIt.key()) && extIt.value().toStringList().contains(ext)) {
tabMap.insert(extIt.key(), m_extToImportOptionsMap.value(ext));
break;
}

View File

@@ -46,7 +46,10 @@ class ItemLibraryAssetImportDialog : public QDialog
public:
explicit ItemLibraryAssetImportDialog(const QStringList &importFiles,
const QString &defaulTargetDirectory, QWidget *parent = nullptr);
const QString &defaulTargetDirectory,
const QVariantMap &supportedExts,
const QVariantMap &supportedOpts,
QWidget *parent = nullptr);
~ItemLibraryAssetImportDialog();
protected:

View File

@@ -30,18 +30,15 @@
#include "model.h"
#include "puppetcreator.h"
#include <QtCore/qdir.h>
#include <QtCore/qdiriterator.h>
#include <QtCore/qsavefile.h>
#include <QtCore/qfile.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qtemporarydir.h>
#include <QtWidgets/qapplication.h>
#include <QtWidgets/qmessagebox.h>
#ifdef IMPORT_QUICK3D_ASSETS
#include <QtQuick3DAssetImport/private/qssgassetimportmanager_p.h>
#endif
#include <QDir>
#include <QDirIterator>
#include <QSaveFile>
#include <QFile>
#include <QLoggingCategory>
#include <QTemporaryDir>
#include <QApplication>
#include <QMessageBox>
#include <QJsonDocument>
namespace
{
@@ -53,9 +50,6 @@ namespace QmlDesigner {
ItemLibraryAssetImporter::ItemLibraryAssetImporter(QObject *parent) :
QObject (parent)
{
#ifdef IMPORT_QUICK3D_ASSETS
m_quick3DAssetImporter.reset(new QSSGAssetImportManager);
#endif
}
ItemLibraryAssetImporter::~ItemLibraryAssetImporter() {
@@ -73,7 +67,6 @@ void ItemLibraryAssetImporter::importQuick3D(const QStringList &inputFiles,
reset();
m_isImporting = true;
#ifdef IMPORT_QUICK3D_ASSETS
if (!m_tempDir->isValid()) {
addError(tr("Could not create a temporary directory for import."));
notifyFinished();
@@ -85,24 +78,27 @@ void ItemLibraryAssetImporter::importQuick3D(const QStringList &inputFiles,
parseFiles(inputFiles, options, extToImportOptionsMap);
if (!isCancelled()) {
// Wait for icon generation processes to finish
const auto parseData = m_parseData;
for (const auto &pd : parseData) {
if (!startImportProcess(pd)) {
addError(tr("Failed to start import 3D asset process"),
pd.sourceInfo.absoluteFilePath());
m_parseData.remove(pd.importId);
}
}
}
if (!isCancelled()) {
// Wait for puppet processes to finish
if (m_qmlPuppetProcesses.empty()) {
finalizeQuick3DImport();
postImport();
} else {
m_qmlPuppetCount = static_cast<int>(m_qmlPuppetProcesses.size());
const QString progressTitle = tr("Generating icons.");
const QString progressTitle = tr("Importing 3D assets.");
addInfo(progressTitle);
notifyProgress(0, progressTitle);
}
}
#else
Q_UNUSED(inputFiles)
Q_UNUSED(importPath)
Q_UNUSED(options)
Q_UNUSED(extToImportOptionsMap)
addError(tr("Importing 3D assets requires building against Qt Quick 3D module."));
notifyFinished();
#endif
}
bool ItemLibraryAssetImporter::isImporting() const
@@ -135,67 +131,47 @@ void ItemLibraryAssetImporter::addInfo(const QString &infoMsg, const QString &sr
emit infoReported(infoMsg, srcPath);
}
bool ItemLibraryAssetImporter::isQuick3DAsset(const QString &fileName) const
void ItemLibraryAssetImporter::importProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
#ifdef IMPORT_QUICK3D_ASSETS
static QStringList quick3DExt;
if (quick3DExt.isEmpty()) {
const auto exts = m_quick3DAssetImporter->getSupportedExtensions();
for (const auto &ext : exts)
quick3DExt << ext;
Q_UNUSED(exitStatus)
++m_qmlImportFinishedCount;
m_qmlPuppetProcesses.erase(
std::remove_if(m_qmlPuppetProcesses.begin(), m_qmlPuppetProcesses.end(),
[&](const auto &entry) {
return !entry || entry->state() == QProcess::NotRunning;
}));
if (m_parseData.contains(-exitCode)) {
const ParseData pd = m_parseData.take(-exitCode);
addError(tr("Asset import process failed for: \"%1\"").arg(pd.sourceInfo.absoluteFilePath()));
}
if (m_qmlImportFinishedCount == m_qmlPuppetCount) {
notifyProgress(100);
QTimer::singleShot(0, this, &ItemLibraryAssetImporter::postImport);
} else {
notifyProgress(int(100. * (double(m_qmlImportFinishedCount) / double(m_qmlPuppetCount))));
}
return quick3DExt.contains(QFileInfo(fileName).suffix().toLower());
#else
Q_UNUSED(fileName)
return false;
#endif
}
QVariantMap ItemLibraryAssetImporter::supportedOptions(const QString &modelFile) const
{
#ifdef IMPORT_QUICK3D_ASSETS
return m_quick3DAssetImporter->getOptionsForFile(modelFile);
#else
Q_UNUSED(modelFile)
return {};
#endif
}
QHash<QString, QVariantMap> ItemLibraryAssetImporter::allOptions() const
{
#ifdef IMPORT_QUICK3D_ASSETS
return m_quick3DAssetImporter->getAllOptions();
#else
return {};
#endif
}
QHash<QString, QStringList> ItemLibraryAssetImporter::supportedExtensions() const
{
#ifdef IMPORT_QUICK3D_ASSETS
return m_quick3DAssetImporter->getSupportedExtensions();
#else
return {};
#endif
}
void ItemLibraryAssetImporter::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
void ItemLibraryAssetImporter::iconProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
Q_UNUSED(exitCode)
Q_UNUSED(exitStatus)
m_qmlPuppetProcesses.erase(
std::remove_if(m_qmlPuppetProcesses.begin(), m_qmlPuppetProcesses.end(), [&](const auto &entry) {
std::remove_if(m_qmlPuppetProcesses.begin(), m_qmlPuppetProcesses.end(),
[&](const auto &entry) {
return !entry || entry->state() == QProcess::NotRunning;
}));
const QString progressTitle = tr("Generating icons.");
if (m_qmlPuppetProcesses.empty()) {
notifyProgress(100, progressTitle);
finalizeQuick3DImport();
notifyProgress(100);
QTimer::singleShot(0, this, &ItemLibraryAssetImporter::finalizeQuick3DImport);
} else {
notifyProgress(int(100. * (1. - double(m_qmlPuppetCount) / double(m_qmlPuppetProcesses.size()))),
progressTitle);
notifyProgress(int(100. * (1. - (double(m_qmlPuppetProcesses.size()) / double(m_qmlPuppetCount)))));
}
}
@@ -210,14 +186,14 @@ void ItemLibraryAssetImporter::reset()
m_isImporting = false;
m_cancelled = false;
#ifdef IMPORT_QUICK3D_ASSETS
delete m_tempDir;
m_tempDir = new QTemporaryDir;
m_importFiles.clear();
m_overwrittenImports.clear();
m_qmlPuppetProcesses.clear();
m_qmlPuppetCount = 0;
#endif
m_qmlImportFinishedCount = 0;
m_parseData.clear();
}
void ItemLibraryAssetImporter::parseFiles(const QStringList &filePaths,
@@ -235,99 +211,86 @@ void ItemLibraryAssetImporter::parseFiles(const QStringList &filePaths,
notifyProgress(qRound(quota * (count + value)), progressTitle);
};
for (const QString &file : filePaths) {
if (isCancelled())
return;
if (isQuick3DAsset(file)) {
int index = extToImportOptionsMap.value(QFileInfo(file).suffix());
parseQuick3DAsset(file, options[index].toVariantMap());
int index = extToImportOptionsMap.value(QFileInfo(file).suffix());
ParseData pd;
pd.options = options[index];
if (preParseQuick3DAsset(file, pd)) {
pd.importId = ++m_importIdCounter;
m_parseData.insert(pd.importId, pd);
}
notifyProgress(qRound(++count * quota), progressTitle);
}
notifyProgress(100, progressTitle);
}
void ItemLibraryAssetImporter::parseQuick3DAsset(const QString &file, const QVariantMap &options)
bool ItemLibraryAssetImporter::preParseQuick3DAsset(const QString &file, ParseData &pd)
{
#ifdef IMPORT_QUICK3D_ASSETS
addInfo(tr("Parsing 3D Model"), file);
pd.targetDir = QDir(m_importPath);
pd.outDir = QDir(m_tempDir->path());
pd.sourceInfo = QFileInfo(file);
pd.assetName = pd.sourceInfo.completeBaseName();
QString errorString;
QDir targetDir(m_importPath);
QDir outDir(m_tempDir->path());
QFileInfo sourceInfo(file);
QString assetName = sourceInfo.completeBaseName();
if (!assetName.isEmpty()) {
if (!pd.assetName.isEmpty()) {
// Fix name so it plays nice with imports
for (QChar &currentChar : assetName) {
for (QChar &currentChar : pd.assetName) {
if (!currentChar.isLetter() && !currentChar.isDigit())
currentChar = QLatin1Char('_');
}
const QChar firstChar = assetName[0];
const QChar firstChar = pd.assetName[0];
if (firstChar.isDigit())
assetName[0] = QLatin1Char('_');
pd.assetName[0] = QLatin1Char('_');
if (firstChar.isLower())
assetName[0] = firstChar.toUpper();
pd.assetName[0] = firstChar.toUpper();
}
QString targetDirPath = targetDir.filePath(assetName);
pd.targetDirPath = pd.targetDir.filePath(pd.assetName);
if (outDir.exists(assetName)) {
addWarning(tr("Skipped import of duplicate asset: \"%1\"").arg(assetName));
return;
if (pd.outDir.exists(pd.assetName)) {
addWarning(tr("Skipped import of duplicate asset: \"%1\"").arg(pd.assetName));
return false;
}
QString originalAssetName = assetName;
if (targetDir.exists(assetName)) {
pd.originalAssetName = pd.assetName;
if (pd.targetDir.exists(pd.assetName)) {
// If we have a file system with case insensitive filenames, assetName may be
// different from the existing name. Modify assetName to ensure exact match to
// the overwritten old asset capitalization
const QStringList assetDirs = targetDir.entryList({assetName}, QDir::Dirs);
const QStringList assetDirs = pd.targetDir.entryList({pd.assetName}, QDir::Dirs);
if (assetDirs.size() == 1) {
assetName = assetDirs[0];
targetDirPath = targetDir.filePath(assetName);
pd.assetName = assetDirs[0];
pd.targetDirPath = pd.targetDir.filePath(pd.assetName);
}
if (!confirmAssetOverwrite(assetName)) {
addWarning(tr("Skipped import of existing asset: \"%1\"").arg(assetName));
return;
if (!confirmAssetOverwrite(pd.assetName)) {
addWarning(tr("Skipped import of existing asset: \"%1\"").arg(pd.assetName));
return false;
}
m_overwrittenImports << targetDirPath;
m_overwrittenImports << pd.targetDirPath;
}
outDir.mkpath(assetName);
pd.outDir.mkpath(pd.assetName);
if (!outDir.cd(assetName)) {
if (!pd.outDir.cd(pd.assetName)) {
addError(tr("Could not access temporary asset directory: \"%1\"")
.arg(outDir.filePath(assetName)));
return;
.arg(pd.outDir.filePath(pd.assetName)));
return false;
}
return true;
}
addInfo(tr("Generating 3D assets for: \"%1\"").arg(targetDir.absoluteFilePath(assetName)));
if (m_quick3DAssetImporter->importFile(
sourceInfo.absoluteFilePath(), outDir, options, &errorString)
!= QSSGAssetImportManager::ImportState::Success) {
addError(tr("Failed to import 3D asset with error: %1").arg(errorString),
sourceInfo.absoluteFilePath());
return;
}
// The importer is reset after every import to avoid issues with it caching various things
m_quick3DAssetImporter.reset(new QSSGAssetImportManager);
if (originalAssetName != assetName) {
void ItemLibraryAssetImporter::postParseQuick3DAsset(const ParseData &pd)
{
QDir outDir = pd.outDir;
if (pd.originalAssetName != pd.assetName) {
// Fix the generated qml file name
const QString assetQml = originalAssetName + ".qml";
const QString assetQml = pd.originalAssetName + ".qml";
if (outDir.exists(assetQml))
outDir.rename(assetQml, assetName + ".qml");
outDir.rename(assetQml, pd.assetName + ".qml");
}
QHash<QString, QString> assetFiles;
const int outDirPathSize = outDir.path().size();
auto insertAsset = [&](const QString &filePath) {
QString targetPath = filePath.mid(outDirPathSize);
targetPath.prepend(targetDirPath);
targetPath.prepend(pd.targetDirPath);
assetFiles.insert(filePath, targetPath);
};
@@ -348,7 +311,7 @@ void ItemLibraryAssetImporter::parseQuick3DAsset(const QString &file, const QVar
qmlInfo.append("module ");
qmlInfo.append(m_importPath.split('/').last());
qmlInfo.append(".");
qmlInfo.append(assetName);
qmlInfo.append(pd.assetName);
qmlInfo.append('\n');
while (qmlIt.hasNext()) {
qmlIt.next();
@@ -382,7 +345,7 @@ void ItemLibraryAssetImporter::parseQuick3DAsset(const QString &file, const QVar
out << "canBeDroppedInView3D: true" << Qt::endl;
file.close();
}
if (generateComponentIcon(24, iconFileName, qmlIt.filePath())) {
if (startIconProcess(24, iconFileName, qmlIt.filePath())) {
// Since icon is generated by external process, the file won't be
// ready for asset gathering below, so assume its generation succeeds
// and add it now.
@@ -395,7 +358,7 @@ void ItemLibraryAssetImporter::parseQuick3DAsset(const QString &file, const QVar
qmldirFile.write(qmlInfo.toUtf8());
qmldirFile.commit();
} else {
addError(tr("Failed to create qmldir file for asset: \"%1\"").arg(assetName));
addError(tr("Failed to create qmldir file for asset: \"%1\"").arg(pd.assetName));
}
}
}
@@ -408,19 +371,13 @@ void ItemLibraryAssetImporter::parseQuick3DAsset(const QString &file, const QVar
}
// Copy the original asset into a subdirectory
assetFiles.insert(sourceInfo.absoluteFilePath(),
targetDirPath + QStringLiteral("/source scene/") + sourceInfo.fileName());
assetFiles.insert(pd.sourceInfo.absoluteFilePath(),
pd.targetDirPath + QStringLiteral("/source scene/") + pd.sourceInfo.fileName());
m_importFiles.insert(assetFiles);
#else
Q_UNUSED(file)
Q_UNUSED(options)
#endif
}
void ItemLibraryAssetImporter::copyImportedFiles()
{
#ifdef IMPORT_QUICK3D_ASSETS
if (!m_overwrittenImports.isEmpty()) {
const QString progressTitle = tr("Removing old overwritten assets.");
addInfo(progressTitle);
@@ -461,15 +418,20 @@ void ItemLibraryAssetImporter::copyImportedFiles()
}
notifyProgress(100, progressTitle);
}
#endif
}
void ItemLibraryAssetImporter::notifyProgress(int value, const QString &text) const
void ItemLibraryAssetImporter::notifyProgress(int value, const QString &text)
{
emit progressChanged(value, text);
m_progressTitle = text;
emit progressChanged(value, m_progressTitle);
keepUiAlive();
}
void ItemLibraryAssetImporter::notifyProgress(int value)
{
notifyProgress(value, m_progressTitle);
}
void ItemLibraryAssetImporter::keepUiAlive() const
{
QApplication::processEvents();
@@ -484,8 +446,42 @@ bool ItemLibraryAssetImporter::confirmAssetOverwrite(const QString &assetName)
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes;
}
bool ItemLibraryAssetImporter::generateComponentIcon(int size, const QString &iconFile,
const QString &iconSource)
bool ItemLibraryAssetImporter::startImportProcess(const ParseData &pd)
{
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
Model *model = doc ? doc->currentModel() : nullptr;
if (model) {
PuppetCreator puppetCreator(doc->currentTarget(), model);
puppetCreator.createQml2PuppetExecutableIfMissing();
QStringList puppetArgs;
QJsonDocument optDoc(pd.options);
puppetArgs << "--import3dAsset" << pd.sourceInfo.absoluteFilePath()
<< pd.outDir.absolutePath() << QString::number(pd.importId)
<< QString::fromUtf8(optDoc.toJson());
QProcessUniquePointer process = puppetCreator.createPuppetProcess(
"custom",
{},
std::function<void()>(),
[&](int exitCode, QProcess::ExitStatus exitStatus) {
importProcessFinished(exitCode, exitStatus);
},
puppetArgs);
if (process->waitForStarted(5000)) {
m_qmlPuppetProcesses.push_back(std::move(process));
return true;
} else {
process.reset();
}
}
return false;
}
bool ItemLibraryAssetImporter::startIconProcess(int size, const QString &iconFile,
const QString &iconSource)
{
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
Model *model = doc ? doc->currentModel() : nullptr;
@@ -500,7 +496,7 @@ bool ItemLibraryAssetImporter::generateComponentIcon(int size, const QString &ic
{},
std::function<void()>(),
[&](int exitCode, QProcess::ExitStatus exitStatus) {
processFinished(exitCode, exitStatus);
iconProcessFinished(exitCode, exitStatus);
},
puppetArgs);
@@ -514,9 +510,30 @@ bool ItemLibraryAssetImporter::generateComponentIcon(int size, const QString &ic
return false;
}
void ItemLibraryAssetImporter::postImport()
{
Q_ASSERT(m_qmlPuppetProcesses.empty());
if (!isCancelled()) {
for (const auto &pd : qAsConst(m_parseData))
postParseQuick3DAsset(pd);
}
if (!isCancelled()) {
// Wait for icon generation processes to finish
if (m_qmlPuppetProcesses.empty()) {
finalizeQuick3DImport();
} else {
m_qmlPuppetCount = static_cast<int>(m_qmlPuppetProcesses.size());
const QString progressTitle = tr("Generating icons.");
addInfo(progressTitle);
notifyProgress(0, progressTitle);
}
}
}
void ItemLibraryAssetImporter::finalizeQuick3DImport()
{
#ifdef IMPORT_QUICK3D_ASSETS
if (!isCancelled()) {
// Don't allow cancel anymore as existing asset overwrites are not trivially recoverable.
// Also, on Windows at least you can't delete a subdirectory of a watched directory,
@@ -562,7 +579,6 @@ void ItemLibraryAssetImporter::finalizeQuick3DImport()
notifyFinished();
}
}
#endif
}
bool ItemLibraryAssetImporter::isCancelled() const

View File

@@ -29,11 +29,13 @@
#include <qprocessuniqueptr.h>
#include <QSet>
#include <QtCore/qhash.h>
#include <QtCore/qjsonobject.h>
#include <QtCore/qobject.h>
#include <QtCore/qprocess.h>
#include <QtCore/qstringlist.h>
#include <QHash>
#include <QJsonObject>
#include <QObject>
#include <QProcess>
#include <QStringList>
#include <QDir>
#include <QFileInfo>
QT_BEGIN_NAMESPACE
class QSSGAssetImportManager;
@@ -62,11 +64,6 @@ public:
void addWarning(const QString &warningMsg, const QString &srcPath = {}) const;
void addInfo(const QString &infoMsg, const QString &srcPath = {}) const;
bool isQuick3DAsset(const QString &fileName) const;
QVariantMap supportedOptions(const QString &modelFile) const;
QHash<QString, QVariantMap> allOptions() const;
QHash<QString, QStringList> supportedExtensions() const;
signals:
void errorReported(const QString &, const QString &) const;
void warningReported(const QString &, const QString &) const;
@@ -76,32 +73,49 @@ signals:
void importFinished();
private slots:
void processFinished(int exitCode, QProcess::ExitStatus exitStatus);
void importProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
void iconProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
private:
struct ParseData {
QJsonObject options;
QDir targetDir;
QDir outDir;
QString targetDirPath;
QFileInfo sourceInfo;
QString assetName;
QString originalAssetName;
int importId;
};
void notifyFinished();
void reset();
void parseFiles(const QStringList &filePaths, const QVector<QJsonObject> &options,
const QHash<QString, int> &extToImportOptionsMap);
void parseQuick3DAsset(const QString &file, const QVariantMap &options);
bool preParseQuick3DAsset(const QString &file, ParseData &pd);
void postParseQuick3DAsset(const ParseData &pd);
void copyImportedFiles();
void notifyProgress(int value, const QString &text) const;
void notifyProgress(int value, const QString &text);
void notifyProgress(int value);
void keepUiAlive() const;
bool confirmAssetOverwrite(const QString &assetName);
bool generateComponentIcon(int size, const QString &iconFile, const QString &iconSource);
bool startImportProcess(const ParseData &pd);
bool startIconProcess(int size, const QString &iconFile, const QString &iconSource);
void postImport();
void finalizeQuick3DImport();
#ifdef IMPORT_QUICK3D_ASSETS
QScopedPointer<QSSGAssetImportManager> m_quick3DAssetImporter;
QSet<QHash<QString, QString>> m_importFiles;
QSet<QString> m_overwrittenImports;
#endif
bool m_isImporting = false;
bool m_cancelled = false;
QString m_importPath;
QTemporaryDir *m_tempDir = nullptr;
std::vector<QProcessUniquePointer> m_qmlPuppetProcesses;
int m_qmlPuppetCount = 0;
int m_qmlImportFinishedCount = 0;
int m_importIdCounter = 1000000; // Use ids in range unlikely to clash with any normal process exit codes
QHash<int, ParseData> m_parseData;
QString m_progressTitle;
};
} // QmlDesigner

View File

@@ -25,6 +25,7 @@
#include "itemlibraryview.h"
#include "itemlibrarywidget.h"
#include "itemlibraryassetimportdialog.h"
#include "metainfo.h"
#include <asynchronousimagecache.h>
#include <bindingproperty.h>
@@ -216,4 +217,46 @@ void ItemLibraryView::updateImports()
m_widget->delayedUpdateModel();
}
void ItemLibraryView::updateImport3DSupport(const QVariantMap &supportMap)
{
QVariantMap extMap = qvariant_cast<QVariantMap>(supportMap.value("extensions"));
if (m_importableExtensions3DMap != extMap) {
DesignerActionManager *actionManager =
&QmlDesignerPlugin::instance()->viewManager().designerActionManager();
// All things importable by QSSGAssetImportManager are considered to be in the same category
// so we don't get multiple separate import dialogs when different file types are imported.
const QString category = tr("3D Assets");
if (!m_importableExtensions3DMap.isEmpty())
actionManager->unregisterAddResourceHandlers(category);
m_importableExtensions3DMap = extMap;
auto handle3DModel = [this](const QStringList &fileNames, const QString &defaultDir) -> bool {
auto importDlg = new ItemLibraryAssetImportDialog(fileNames, defaultDir,
m_importableExtensions3DMap,
m_importOptions3DMap,
Core::ICore::mainWindow());
importDlg->show();
return true;
};
auto add3DHandler = [&](const QString &category, const QString &ext) {
const QString filter = QStringLiteral("*.%1").arg(ext);
actionManager->registerAddResourceHandler(
AddResourceHandler(category, filter, handle3DModel, 10));
};
const auto groups = extMap.keys();
for (const auto &group : groups) {
const QStringList exts = extMap[group].toStringList();
for (const auto &ext : exts)
add3DHandler(category, ext);
}
}
m_importOptions3DMap = qvariant_cast<QVariantMap>(supportMap.value("options"));
}
} // namespace QmlDesigner

View File

@@ -53,6 +53,7 @@ public:
void modelAboutToBeDetached(Model *model) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
void documentMessagesChanged(const QList<DocumentMessage> &errors, const QList<DocumentMessage> &warnings) override;
void updateImport3DSupport(const QVariantMap &supportMap) override;
void setResourcePath(const QString &resourcePath);
@@ -66,6 +67,8 @@ private:
QPointer<ItemLibraryWidget> m_widget;
ImportManagerView *m_importManagerView;
bool m_hasErrors = false;
QVariantMap m_importableExtensions3DMap;
QVariantMap m_importOptions3DMap;
};
}

View File

@@ -26,7 +26,6 @@
#include "itemlibrarywidget.h"
#include "customfilesystemmodel.h"
#include "itemlibraryassetimportdialog.h"
#include "itemlibraryiconimageprovider.h"
#include <theme.h>
@@ -53,10 +52,6 @@
#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
#ifdef IMPORT_QUICK3D_ASSETS
#include <QtQuick3DAssetImport/private/qssgassetimportmanager_p.h>
#endif
#include <QApplication>
#include <QDrag>
#include <QFileDialog>
@@ -205,49 +200,6 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache,
addResources({});
});
#ifdef IMPORT_QUICK3D_ASSETS
DesignerActionManager *actionManager =
&QmlDesignerPlugin::instance()->viewManager().designerActionManager();
auto handle3DModel = [](const QStringList &fileNames, const QString &defaultDir) -> bool {
auto importDlg = new ItemLibraryAssetImportDialog(fileNames, defaultDir, Core::ICore::mainWindow());
importDlg->show();
return true;
};
auto add3DHandler = [&](const QString &category, const QString &ext) {
const QString filter = QStringLiteral("*.%1").arg(ext);
actionManager->registerAddResourceHandler(
AddResourceHandler(category, filter, handle3DModel, 10));
};
QSSGAssetImportManager importManager;
QHash<QString, QStringList> supportedExtensions = importManager.getSupportedExtensions();
// All things importable by QSSGAssetImportManager are considered to be in the same category
// so we don't get multiple separate import dialogs when different file types are imported.
const QString category = tr("3D Assets");
// Skip if 3D asset handlers have already been added
const QList<AddResourceHandler> handlers = actionManager->addResourceHandler();
bool categoryAlreadyAdded = false;
for (const auto &handler : handlers) {
if (handler.category == category) {
categoryAlreadyAdded = true;
break;
}
}
if (!categoryAlreadyAdded) {
const auto groups = supportedExtensions.keys();
for (const auto &group : groups) {
const auto extensions = supportedExtensions[group];
for (const auto &ext : extensions)
add3DHandler(category, ext);
}
}
#endif
const auto dropSupport = new Utils::DropSupport(
m_resourcesView.data(), [this](QDropEvent *event, Utils::DropSupport *) {
// Accept supported file types

View File

@@ -189,6 +189,7 @@ public:
void emitRenderImage3DChanged(const QImage &image);
void emitUpdateActiveScene3D(const QVariantMap &sceneState);
void emitModelNodelPreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap);
void emitImport3DSupportChanged(const QVariantMap &supportMap);
void sendTokenToInstances(const QString &token, int number, const QVector<ModelNode> &nodeVector);
@@ -248,6 +249,7 @@ public:
virtual void renderImage3DChanged(const QImage &image);
virtual void updateActiveScene3D(const QVariantMap &sceneState);
virtual void updateImport3DSupport(const QVariantMap &supportMap);
virtual void modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap);
void changeRootNodeType(const TypeName &type, int majorVersion, int minorVersion);

View File

@@ -1578,6 +1578,9 @@ void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand
updatePreviewImageForNode(node, image);
}
}
} else if (command.type() == PuppetToCreatorCommand::Import3DSupport) {
const QVariantMap supportMap = qvariant_cast<QVariantMap>(command.data());
emitImport3DSupportChanged(supportMap);
}
}

View File

@@ -386,6 +386,10 @@ void AbstractView::updateActiveScene3D(const QVariantMap & /*sceneState*/)
{
}
void AbstractView::updateImport3DSupport(const QVariantMap & /*supportMap*/)
{
}
void AbstractView::modelNodePreviewPixmapChanged(const ModelNode & /*node*/, const QPixmap & /*pixmap*/)
{
}
@@ -804,6 +808,12 @@ void AbstractView::emitModelNodelPreviewPixmapChanged(const ModelNode &node, con
model()->d->notifyModelNodePreviewPixmapChanged(node, pixmap);
}
void AbstractView::emitImport3DSupportChanged(const QVariantMap &supportMap)
{
if (model())
model()->d->notifyImport3DSupportChanged(supportMap);
}
void AbstractView::emitRewriterEndTransaction()
{
if (model())

View File

@@ -575,6 +575,11 @@ void ModelPrivate::notifyModelNodePreviewPixmapChanged(const ModelNode &node, co
[&](AbstractView *view) { view->modelNodePreviewPixmapChanged(node, pixmap); });
}
void ModelPrivate::notifyImport3DSupportChanged(const QVariantMap &supportMap)
{
notifyInstanceChanges([&](AbstractView *view) { view->updateImport3DSupport(supportMap); });
}
void ModelPrivate::notifyRewriterBeginTransaction()
{
notifyNodeInstanceViewLast([&](AbstractView *view) { view->rewriterBeginTransaction(); });

View File

@@ -176,6 +176,7 @@ public:
void notifyRenderImage3DChanged(const QImage &image);
void notifyUpdateActiveScene3D(const QVariantMap &sceneState);
void notifyModelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap);
void notifyImport3DSupportChanged(const QVariantMap &supportMap);
void setDocumentMessages(const QList<DocumentMessage> &errors, const QList<DocumentMessage> &warnings);