Merge remote-tracking branch 'origin/4.11'

Change-Id: I6389fd3583ca0638fa6b94c03d2442a584b604bc
This commit is contained in:
Eike Ziller
2019-09-30 10:38:53 +02:00
32 changed files with 1177 additions and 92 deletions

View File

@@ -19,9 +19,13 @@ you can check out from the public Git repository. For example:
* Fixed that text moved around when resizing and zooming (QTCREATORBUG-4756)
## All Projects
* Fixed `Qt Creator Plugin` wizard (QTCREATORBUG-22945)
## Debugging
* Fixed more layout restoration issues (QTCREATORBUG-22286, QTCREATORBUG-22938)
* Fixed more layout restoration issues (QTCREATORBUG-22286, QTCREATORBUG-22415, QTCREATORBUG-22938)
### LLDB
@@ -36,7 +40,13 @@ you can check out from the public Git repository. For example:
### macOS
* Fixed debugging with Xcode 11 (QTCREATORBUG-22955)
* Fixed window stacking order after closing file dialog (QTCREATORBUG-22906)
* Fixed window size after exiting fullscreen
### QNX
* Fixed that QNX compiler could not be selected for C
## Credits for these changes go to:
@@ -44,7 +54,9 @@ Aleksei German
Alexander Akulich
Andre Hartmann
André Pönitz
Christian Kandeler
Christian Stenger
Cristian Adam
David Schulz
Eike Ziller
Knud Dollereder
@@ -53,5 +65,5 @@ Lisandro Damián Nicanor Pérez Meyer
Nikolai Kosjar
Orgad Shaneh
Richard Weickelt
Sergey Belyashov
Thomas Hartmann

View File

@@ -136,8 +136,6 @@ class PlainDumper:
printer = self.printer.gen_printer(value.nativeValue)
except:
printer = self.printer.invoke(value.nativeValue)
lister = getattr(printer, 'children', None)
children = [] if lister is None else list(lister())
d.putType(value.nativeValue.type.name)
val = printer.to_string()
if isinstance(val, str):
@@ -149,11 +147,20 @@ class PlainDumper:
d.putCharArrayValue(val.address, val.length,
val.type.target().sizeof)
d.putNumChild(len(children))
if d.isExpanded():
with Children(d):
for child in children:
d.putSubItem(child[0], d.fromNativeValue(gdb.Value(child[1])))
lister = getattr(printer, 'children', None)
if lister is None:
d.putNumChild(0)
else:
if d.isExpanded():
children = lister()
with Children(d):
i = 0
for (name, child) in children:
d.putSubItem(name, d.fromNativeValue(child))
i += 1
if i > 1000:
break
d.putNumChild(1)
def importPlainDumpers(args):
if args == 'off':

View File

@@ -44,8 +44,8 @@ QString FakeMetaEnum::name() const
void FakeMetaEnum::setName(const QString &name)
{ m_name = name; }
void FakeMetaEnum::addKey(const QString &key, int value)
{ m_keys.append(key); m_values.append(value); }
void FakeMetaEnum::addKey(const QString &key)
{ m_keys.append(key); }
QString FakeMetaEnum::key(int index) const
{ return m_keys.at(index); }
@@ -71,10 +71,6 @@ void FakeMetaEnum::addToHash(QCryptographicHash &hash) const
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar));
}
len = m_values.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
foreach (int value, m_values)
hash.addData(reinterpret_cast<const char *>(&value), sizeof(value));
}
QString FakeMetaEnum::describe(int baseIndent) const
@@ -82,16 +78,14 @@ QString FakeMetaEnum::describe(int baseIndent) const
QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
QString res = QLatin1String("Enum ");
res += name();
res += QLatin1String(":{");
res += QLatin1String(": [");
for (int i = 0; i < keyCount(); ++i) {
res += newLine;
res += QLatin1String(" ");
res += key(i);
res += QLatin1String(": ");
res += QString::number(m_values.value(i, -1));
}
res += newLine;
res += QLatin1Char('}');
res += QLatin1Char(']');
return res;
}

View File

@@ -43,7 +43,6 @@ namespace LanguageUtils {
class LANGUAGEUTILS_EXPORT FakeMetaEnum {
QString m_name;
QStringList m_keys;
QList<int> m_values;
public:
FakeMetaEnum();
@@ -54,7 +53,7 @@ public:
QString name() const;
void setName(const QString &name);
void addKey(const QString &key, int value);
void addKey(const QString &key);
QString key(int index) const;
int keyCount() const;
QStringList keys() const;

View File

@@ -737,7 +737,7 @@ static LanguageUtils::FakeMetaObject::Ptr buildFakeMetaObject(
Symbol *enumMember = e->memberAt(j);
if (!enumMember->name())
continue;
metaEnum.addKey(namePrinter.prettyName(enumMember->name()), 0);
metaEnum.addKey(namePrinter.prettyName(enumMember->name()));
}
fmo->addEnum(metaEnum);
}

View File

@@ -649,39 +649,34 @@ void TypeDescriptionReader::readEnumValues(AST::UiScriptBinding *ast, LanguageUt
return;
}
ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
auto *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
if (!expStmt) {
addError(ast->statement->firstSourceLocation(), tr("Expected object literal after colon."));
addError(ast->statement->firstSourceLocation(), tr("Expected expression after colon."));
return;
}
ObjectPattern *objectLit = AST::cast<ObjectPattern *>(expStmt->expression);
if (!objectLit) {
addError(expStmt->firstSourceLocation(), tr("Expected object literal after colon."));
return;
}
for (PatternPropertyList *it = objectLit->properties; it; it = it->next) {
PatternProperty *assignement = AST::cast<PatternProperty *>(it->property);
if (assignement) {
StringLiteralPropertyName *propName = AST::cast<StringLiteralPropertyName *>(assignement->name);
NumericLiteral *value = AST::cast<NumericLiteral *>(assignement->initializer);
UnaryMinusExpression *minus = AST::cast<UnaryMinusExpression *>(assignement->initializer);
if (minus)
value = AST::cast<NumericLiteral *>(minus->expression);
if (!propName || !value) {
addError(objectLit->firstSourceLocation(), tr("Expected object literal to contain only 'string: number' elements."));
continue;
if (auto *objectLit = AST::cast<ObjectPattern *>(expStmt->expression)) {
for (PatternPropertyList *it = objectLit->properties; it; it = it->next) {
if (PatternProperty *assignement = it->property) {
if (auto *name = AST::cast<StringLiteralPropertyName *>(assignement->name)) {
fme->addKey(name->id.toString());
continue;
}
}
double v = value->value;
if (minus)
v = -v;
fme->addKey(propName->id.toString(), v);
continue;
addError(it->firstSourceLocation(), tr("Expected strings as enum keys."));
}
PatternPropertyList *getterSetter = AST::cast<PatternPropertyList *>(it->next);
if (getterSetter)
addError(objectLit->firstSourceLocation(), tr("Enum should not contain getter and setters, but only 'string: number' elements."));
} else if (auto *arrayLit = AST::cast<ArrayPattern *>(expStmt->expression)) {
for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
if (PatternElement *element = it->element) {
if (auto *name = AST::cast<StringLiteral *>(element->initializer)) {
fme->addKey(name->value.toString());
continue;
}
}
addError(it->firstSourceLocation(), tr("Expected strings as enum keys."));
}
} else {
addError(ast->statement->firstSourceLocation(),
tr("Expected either array or object literal as enum definition."));
}
}

View File

@@ -381,27 +381,41 @@ QList<ToolChain *> SdccToolChainFactory::autoDetect(const QList<ToolChain *> &al
if (Utils::HostOsInfo::isWindowsHost()) {
#ifdef Q_OS_WIN64
static const char kRegistryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\SDCC";
#else
static const char kRegistryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\SDCC";
#endif
QSettings registry(kRegistryNode, QSettings::NativeFormat);
QString compilerPath = registry.value("Default").toString();
if (!compilerPath.isEmpty()) {
// Tries to detect the candidate from the 32-bit
// or 64-bit system registry format.
auto probeCandidate = [](QSettings::Format format) {
QSettings registry("HKEY_LOCAL_MACHINE\\SOFTWARE\\SDCC",
format);
QString compilerPath = registry.value("Default").toString();
if (compilerPath.isEmpty())
return Candidate{};
// Build full compiler path.
compilerPath += "\\bin\\sdcc.exe";
const FilePath fn = FilePath::fromString(
QFileInfo(compilerPath).absoluteFilePath());
if (compilerExists(fn)) {
// Build compiler version.
const QString version = QString("%1.%2.%3").arg(
registry.value("VersionMajor").toString(),
registry.value("VersionMinor").toString(),
registry.value("VersionRevision").toString());
candidates.push_back({fn, version});
}
if (!compilerExists(fn))
return Candidate{};
// Build compiler version.
const QString version = QString("%1.%2.%3").arg(
registry.value("VersionMajor").toString(),
registry.value("VersionMinor").toString(),
registry.value("VersionRevision").toString());
return Candidate{fn, version};
};
const QSettings::Format allowedFormats[] = {
QSettings::NativeFormat,
#ifdef Q_OS_WIN
QSettings::Registry32Format,
QSettings::Registry64Format
#endif
};
for (const QSettings::Format format : allowedFormats) {
const auto candidate = probeCandidate(format);
if (candidate.compilerPath.isEmpty() || candidates.contains(candidate))
continue;
candidates.push_back(candidate);
}
}

View File

@@ -33,8 +33,9 @@
#include <projectexplorer/project.h>
#include <projectexplorer/target.h>
#include <remotelinux/remotelinuxcheckforfreediskspacestep.h>
#include <remotelinux/genericdirectuploadstep.h>
#include <remotelinux/makeinstallstep.h>
#include <remotelinux/remotelinuxcheckforfreediskspacestep.h>
#include <remotelinux/remotelinuxdeployconfiguration.h>
using namespace ProjectExplorer;
@@ -51,6 +52,11 @@ QdbDeployConfigurationFactory::QdbDeployConfigurationFactory()
"Deploy to Boot2Qt target"));
setUseDeploymentDataView();
addInitialStep(RemoteLinux::MakeInstallStep::stepId(), [](Target *target) {
const Project * const prj = target->project();
return prj->deploymentKnowledge() == DeploymentKnowledge::Bad
&& prj->hasMakeInstallEquivalent();
});
addInitialStep(RemoteLinuxCheckForFreeDiskSpaceStep::stepId());
addInitialStep(QdbStopApplicationStep::stepId());
addInitialStep(GenericDirectUploadStep::stepId());

View File

@@ -46,6 +46,7 @@
#include <remotelinux/remotelinuxcheckforfreediskspacestep.h>
#include <remotelinux/genericdirectuploadstep.h>
#include <remotelinux/makeinstallstep.h>
#include <utils/hostosinfo.h>
#include <utils/fileutils.h>
@@ -175,8 +176,8 @@ public:
QdbDeployStepFactory<RemoteLinux::RemoteLinuxCheckForFreeDiskSpaceStep>
m_checkForFreeDiskSpaceStepFactory;
QdbDeployStepFactory<RemoteLinux::GenericDirectUploadStep>
m_directUploadStepFactory;
QdbDeployStepFactory<RemoteLinux::GenericDirectUploadStep> m_directUploadStepFactory;
QdbDeployStepFactory<RemoteLinux::MakeInstallStep> m_makeInstallStepFactory;
const QList<Core::Id> supportedRunConfigs {
m_runConfigFactory.id(),

View File

@@ -2285,8 +2285,8 @@ void DebuggerEngine::openDisassemblerView(const Location &location)
void DebuggerEngine::raiseWatchersWindow()
{
if (d->m_watchersView) {
if (auto dock = qobject_cast<QDockWidget *>(d->m_watchersView->parentWidget())) {
if (d->m_watchersView && d->m_watchersWindow) {
if (auto dock = qobject_cast<QDockWidget *>(d->m_watchersWindow->parentWidget())) {
if (QAction *act = dock->toggleViewAction()) {
if (!act->isChecked())
QTimer::singleShot(1, act, [act] { act->trigger(); });

View File

@@ -45,11 +45,13 @@
#include <texteditor/texteditoractionhandler.h>
#include <texteditor/texteditorconstants.h>
#include <utils/executeondestruction.h>
#include <utils/qtcassert.h>
#include <utils/synchronousprocess.h>
#include <QCoreApplication>
#include <QRegularExpression>
#include <QTimer>
using namespace ProjectExplorer;
using namespace Utils;
@@ -58,6 +60,7 @@ namespace Python {
namespace Internal {
static constexpr char startPylsInfoBarId[] = "PythonEditor::StartPyls";
static constexpr char installPylsInfoBarId[] = "PythonEditor::InstallPyls";
struct PythonForProject
{
@@ -193,6 +196,90 @@ static LanguageClient::Client *registerLanguageServer(const PythonForProject &py
return LanguageClient::LanguageClientManager::clientForSetting(settings).value(0);
}
class PythonLSInstallHelper : public QObject
{
Q_OBJECT
public:
PythonLSInstallHelper(const PythonForProject &python, QPointer<TextEditor::TextDocument> document)
: m_python(python)
, m_document(document)
{}
void run()
{
auto killTimer = new QTimer(&m_process);
connect(&m_process,
QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this,
&PythonLSInstallHelper::installFinished);
connect(&m_process,
&QProcess::readyReadStandardError,
this,
&PythonLSInstallHelper::errorAvailable);
connect(&m_process,
&QProcess::readyReadStandardOutput,
this,
&PythonLSInstallHelper::outputAvailable);
connect(killTimer, &QTimer::timeout, [this]() {
SynchronousProcess::stopProcess(m_process);
Core::MessageManager::write(tr("The Python language server installation timed out."));
});
// on windows the pyls 0.28.3 crashes with pylint so just install the pyflakes linter
const QString &pylsVersion = HostOsInfo::isWindowsHost()
? QString{"python-language-server[pyflakes]"}
: QString{"python-language-server[all]"};
m_process.start(m_python.path.toString(),
{"-m", "pip", "install", pylsVersion});
Core::MessageManager::write(tr("Running '%1 %2' to install python language server")
.arg(m_process.program(), m_process.arguments().join(' ')));
killTimer->start(5 /*minutes*/ * 60 * 1000);
}
private:
void installFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
if (exitStatus == QProcess::NormalExit && exitCode == 0) {
if (LanguageClient::Client *client = registerLanguageServer(m_python))
LanguageClient::LanguageClientManager::reOpenDocumentWithClient(m_document, client);
} else {
Core::MessageManager::write(
tr("Installing the Python language server failed with exit code %1").arg(exitCode));
}
deleteLater();
}
void outputAvailable()
{
const QString &stdOut = QString::fromLocal8Bit(m_process.readAllStandardOutput().trimmed());
if (!stdOut.isEmpty())
Core::MessageManager::write(stdOut);
}
void errorAvailable()
{
const QString &stdErr = QString::fromLocal8Bit(m_process.readAllStandardError().trimmed());
if (!stdErr.isEmpty())
Core::MessageManager::write(stdErr);
}
QProcess m_process;
const PythonForProject m_python;
QPointer<TextEditor::TextDocument> m_document;
};
static void installPythonLanguageServer(const PythonForProject &python,
QPointer<TextEditor::TextDocument> document)
{
document->infoBar()->removeInfo(installPylsInfoBarId);
auto install = new PythonLSInstallHelper(python, document);
install->run();
}
static void setupPythonLanguageServer(const PythonForProject &python,
QPointer<TextEditor::TextDocument> document)
{
@@ -206,13 +293,25 @@ static void updateEditorInfoBar(const PythonForProject &python, TextEditor::Text
const PythonLanguageServerState &lsState = checkPythonLanguageServer(python.path, document);
if (lsState.state == PythonLanguageServerState::CanNotBeInstalled
|| lsState.state == PythonLanguageServerState::AlreadyConfigured
|| lsState.state == PythonLanguageServerState::CanBeInstalled /* TODO */) {
|| lsState.state == PythonLanguageServerState::AlreadyConfigured) {
return;
}
Core::InfoBar *infoBar = document->infoBar();
if (lsState.state == PythonLanguageServerState::AlreadyInstalled
if (lsState.state == PythonLanguageServerState::CanBeInstalled
&& infoBar->canInfoBeAdded(installPylsInfoBarId)) {
auto message
= PythonEditorFactory::tr(
"Install and set up Python language server for %1 (%2). "
"The language server provides Python specific completions and annotations.")
.arg(python.name(), python.path.toUserOutput());
Core::InfoBarEntry info(installPylsInfoBarId,
message,
Core::InfoBarEntry::GlobalSuppression::Enabled);
info.setCustomButtonInfo(TextEditor::BaseTextEditor::tr("Install"),
[=]() { installPythonLanguageServer(python, document); });
infoBar->addInfo(info);
} else if (lsState.state == PythonLanguageServerState::AlreadyInstalled
&& infoBar->canInfoBeAdded(startPylsInfoBarId)) {
auto message = PythonEditorFactory::tr("Found a Python language server for %1 (%2). "
"Should this one be set up for this document?")
@@ -264,3 +363,5 @@ PythonEditorFactory::PythonEditorFactory()
} // namespace Internal
} // namespace Python
#include "pythoneditor.moc"

View File

@@ -267,6 +267,17 @@ extend_qtc_plugin(QmlDesigner
itemlibrarysectionmodel.cpp itemlibrarysectionmodel.h
itemlibraryview.cpp itemlibraryview.h
itemlibrarywidget.cpp itemlibrarywidget.h
itemlibraryassetimportdialog.cpp itemlibraryassetimportdialog.h
itemlibraryassetimportdialog.ui
itemlibraryassetimporter.cpp itemlibraryassetimporter.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

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

View File

@@ -1,5 +1,10 @@
VPATH += $$PWD
qtHaveModule(quick3dassetimport) {
QT *= quick3dassetimport-private
DEFINES *= IMPORT_QUICK3D_ASSETS
}
# Input
HEADERS += itemlibraryview.h \
itemlibrarywidget.h \
@@ -9,6 +14,8 @@ HEADERS += itemlibraryview.h \
itemlibrarysectionmodel.h \
itemlibraryitem.h \
itemlibrarysection.h \
itemlibraryassetimportdialog.h \
itemlibraryassetimporter.h \
customfilesystemmodel.h
SOURCES += itemlibraryview.cpp \
@@ -19,6 +26,9 @@ SOURCES += itemlibraryview.cpp \
itemlibrarysectionmodel.cpp \
itemlibraryitem.cpp \
itemlibrarysection.cpp \
itemlibraryassetimportdialog.cpp \
itemlibraryassetimporter.cpp \
customfilesystemmodel.cpp
RESOURCES += itemlibrary.qrc
FORMS += itemlibraryassetimportdialog.ui

View File

@@ -3,5 +3,7 @@
<file>images/item-default-icon.png</file>
<file>images/item-invalid-icon.png</file>
<file>images/item-default-icon@2x.png</file>
<file>images/item-3D_model-icon.png</file>
<file>images/item-3D_model-icon@2x.png</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,229 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "itemlibraryassetimportdialog.h"
#include "ui_itemlibraryassetimportdialog.h"
#include "qmldesignerplugin.h"
#include "qmldesignerconstants.h"
#include "model.h"
#include "utils/outputformatter.h"
#include <QtCore/qfileinfo.h>
#include <QtCore/qdir.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qtimer.h>
#include <QtWidgets/qpushbutton.h>
#include <QtWidgets/qformlayout.h>
#include <QtWidgets/qlabel.h>
#include <QtWidgets/qscrollbar.h>
namespace QmlDesigner {
namespace {
static void addFormattedMessage(Utils::OutputFormatter *formatter, const QString &str,
const QString &srcPath, Utils::OutputFormat format) {
if (!formatter)
return;
QString msg = str;
if (!srcPath.isEmpty())
msg += QStringLiteral(": \"%1\"").arg(srcPath);
msg += QLatin1Char('\n');
formatter->appendMessage(msg, format);
formatter->plainTextEdit()->verticalScrollBar()->setValue(
formatter->plainTextEdit()->verticalScrollBar()->maximum());
}
}
ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(const QStringList &importFiles,
const QString &defaulTargetDirectory, QWidget *parent) :
QDialog(parent),
ui(new Ui::ItemLibraryAssetImportDialog),
m_importer(this)
{
setModal(true);
ui->setupUi(this);
m_outputFormatter = new Utils::OutputFormatter;
m_outputFormatter->setPlainTextEdit(ui->plainTextEdit);
// Skip unsupported assets
bool skipSome = false;
for (const auto &file : importFiles) {
if (m_importer.isQuick3DAsset(file))
m_quick3DFiles << file;
else
skipSome = true;
}
if (skipSome)
addWarning("Cannot import 3D and other assets simultaneously. Skipping non-3D assets.");
// Import button will be used in near future when we add import options. Hide for now.
ui->buttonBox->button(QDialogButtonBox::Ok)->hide();
ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Import"));
connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked,
this, &ItemLibraryAssetImportDialog::onImport);
ui->buttonBox->button(QDialogButtonBox::Close)->setDefault(true);
// Import is always done under known folder. The order of preference for folder is:
// 1) An existing QUICK_3D_ASSETS_FOLDER under DEFAULT_ASSET_IMPORT_FOLDER project import path
// 2) An existing QUICK_3D_ASSETS_FOLDER under any project import path
// 3) New QUICK_3D_ASSETS_FOLDER under DEFAULT_ASSET_IMPORT_FOLDER project import path
// 4) New QUICK_3D_ASSETS_FOLDER under any project import path
// 5) New QUICK_3D_ASSETS_FOLDER under new DEFAULT_ASSET_IMPORT_FOLDER under project
const QString defaultAssetFolder = QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER);
const QString quick3DFolder = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER);
QString candidatePath = defaulTargetDirectory + defaultAssetFolder + quick3DFolder;
int candidatePriority = 5;
QStringList importPaths;
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
if (doc) {
Model *model = doc->currentModel();
if (model)
importPaths = model->importPaths();
}
for (auto importPath : qAsConst(importPaths)) {
if (importPath.startsWith(defaulTargetDirectory)) {
const bool isDefaultFolder = importPath.endsWith(defaultAssetFolder);
const QString assetFolder = importPath + quick3DFolder;
const bool exists = QFileInfo(assetFolder).exists();
if (exists) {
if (isDefaultFolder) {
// Priority one location, stop looking
candidatePath = assetFolder;
break;
} else if (candidatePriority > 2) {
candidatePriority = 2;
candidatePath = assetFolder;
}
} else {
if (candidatePriority > 3 && isDefaultFolder) {
candidatePriority = 3;
candidatePath = assetFolder;
} else if (candidatePriority > 4) {
candidatePriority = 4;
candidatePath = assetFolder;
}
}
}
}
m_quick3DImportPath = candidatePath;
// Queue import immediately until we have some options
QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::onImport);
connect(ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked,
this, &ItemLibraryAssetImportDialog::onClose);
connect(&m_importer, &ItemLibraryAssetImporter::errorReported,
this, &ItemLibraryAssetImportDialog::addError);
connect(&m_importer, &ItemLibraryAssetImporter::warningReported,
this, &ItemLibraryAssetImportDialog::addWarning);
connect(&m_importer, &ItemLibraryAssetImporter::infoReported,
this, &ItemLibraryAssetImportDialog::addInfo);
connect(&m_importer, &ItemLibraryAssetImporter::importFinished,
this, &ItemLibraryAssetImportDialog::onImportFinished);
connect(&m_importer, &ItemLibraryAssetImporter::progressChanged,
this, &ItemLibraryAssetImportDialog::setImportProgress);
}
ItemLibraryAssetImportDialog::~ItemLibraryAssetImportDialog()
{
delete ui;
}
void ItemLibraryAssetImportDialog::setImportUiState(bool importing)
{
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!importing);
ui->buttonBox->button(QDialogButtonBox::Close)->setText(importing ? tr("Cancel") : tr("Close"));
}
void ItemLibraryAssetImportDialog::addError(const QString &error, const QString &srcPath)
{
addFormattedMessage(m_outputFormatter, error, srcPath, Utils::StdErrFormat);
}
void ItemLibraryAssetImportDialog::addWarning(const QString &warning, const QString &srcPath)
{
addFormattedMessage(m_outputFormatter, warning, srcPath, Utils::StdOutFormat);
}
void ItemLibraryAssetImportDialog::addInfo(const QString &info, const QString &srcPath)
{
addFormattedMessage(m_outputFormatter, info, srcPath, Utils::NormalMessageFormat);
}
void ItemLibraryAssetImportDialog::onImport()
{
setImportUiState(true);
ui->progressBar->setValue(0);
ui->plainTextEdit->clear();
if (!m_quick3DFiles.isEmpty())
m_importer.importQuick3D(m_quick3DFiles, m_quick3DImportPath);
}
void ItemLibraryAssetImportDialog::setImportProgress(int value, const QString &text)
{
ui->progressLabel->setText(text);
if (value < 0)
ui->progressBar->setRange(0, 0);
else
ui->progressBar->setRange(0, 100);
ui->progressBar->setValue(value);
}
void ItemLibraryAssetImportDialog::onImportFinished()
{
setImportUiState(false);
if (m_importer.isCancelled()) {
QString interruptStr = tr("Import interrupted.");
addError(interruptStr);
setImportProgress(0, interruptStr);
} else {
QString doneStr = tr("Import done.");
addInfo(doneStr);
setImportProgress(100, doneStr);
}
}
void ItemLibraryAssetImportDialog::onClose()
{
if (m_importer.isImporting()) {
addInfo(tr("Canceling import."));
m_importer.cancelImport();
} else {
reject();
}
close();
deleteLater();
}
}

View File

@@ -0,0 +1,71 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "itemlibraryassetimporter.h"
#include <QtWidgets/qdialog.h>
namespace Utils {
class OutputFormatter;
}
namespace QmlDesigner {
class ItemLibraryAssetImporter;
namespace Ui {
class ItemLibraryAssetImportDialog;
}
class ItemLibraryAssetImportDialog : public QDialog
{
Q_OBJECT
public:
explicit ItemLibraryAssetImportDialog(const QStringList &importFiles,
const QString &defaulTargetDirectory, QWidget *parent = nullptr);
~ItemLibraryAssetImportDialog();
private slots:
void addError(const QString &error, const QString &srcPath = {});
void addWarning(const QString &warning, const QString &srcPath = {});
void addInfo(const QString &info, const QString &srcPath = {});
private:
void setImportUiState(bool importing);
void onImport();
void setImportProgress(int value, const QString &text);
void onImportFinished();
void onClose();
Ui::ItemLibraryAssetImportDialog *ui = nullptr;
Utils::OutputFormatter *m_outputFormatter = nullptr;
QStringList m_quick3DFiles;
QString m_quick3DImportPath;
ItemLibraryAssetImporter m_importer;
};
}

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QmlDesigner::ItemLibraryAssetImportDialog</class>
<widget class="QDialog" name="QmlDesigner::ItemLibraryAssetImportDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>Asset Import</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPlainTextEdit" name="plainTextEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="progressLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,322 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "itemlibraryassetimporter.h"
#include "qmldesignerplugin.h"
#include "qmldesignerconstants.h"
#include "rewriterview.h"
#include "model.h"
#include <QtCore/qdir.h>
#include <QtCore/qsavefile.h>
#include <QtCore/qloggingcategory.h>
#include <QtWidgets/qapplication.h>
#include <QtWidgets/qmessagebox.h>
#ifdef IMPORT_QUICK3D_ASSETS
#include <QtQuick3DAssetImport/private/qssgassetimportmanager_p.h>
#endif
namespace
{
Q_LOGGING_CATEGORY(importerLog, "qtc.itemlibrary.assetImporter", QtWarningMsg)
}
namespace QmlDesigner {
ItemLibraryAssetImporter::ItemLibraryAssetImporter(QObject *parent) :
QObject (parent)
{
}
ItemLibraryAssetImporter::~ItemLibraryAssetImporter() {
cancelImport();
};
void ItemLibraryAssetImporter::importQuick3D(const QStringList &inputFiles,
const QString &importPath)
{
if (m_isImporting)
cancelImport();
reset();
m_isImporting = true;
#ifdef IMPORT_QUICK3D_ASSETS
if (!QDir().mkpath(importPath)) {
addError(tr("Cannot create import directory."), importPath);
notifyFinished();
return;
}
m_importPath = importPath;
parseFiles(inputFiles);
if (!isCancelled()) {
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
Model *model = doc ? doc->currentModel() : nullptr;
if (model && !m_quick3DAddedImports.isEmpty()) {
const QString progressTitle = tr("Updating data model.");
addInfo(progressTitle);
notifyProgress(0, progressTitle);
// Trigger underlying qmljs snapshot update by making a non-change to the doc
model->rewriterView()->textModifier()->replace(0, 0, {});
// There is a inbuilt delay before rewriter change actually updates the data model,
// so we need to wait for a moment to allow the change to take effect.
// Otherwise subsequent subcomponent manager update won't detect new imports properly.
QTimer *timer = new QTimer(parent());
static int counter;
counter = 0;
timer->callOnTimeout([this, timer, progressTitle, doc]() {
if (!isCancelled()) {
notifyProgress(++counter * 10, progressTitle);
if (counter >= 10) {
doc->updateSubcomponentManager();
timer->stop();
notifyFinished();
}
} else {
timer->stop();
}
});
timer->start(100);
} else {
notifyFinished();
}
}
#else
Q_UNUSED(inputFiles)
Q_UNUSED(importPath)
addError(tr("Importing 3D assets requires building against Qt Quick 3D module."));
notifyFinished();
#endif
}
bool ItemLibraryAssetImporter::isImporting() const
{
return m_isImporting;
}
void ItemLibraryAssetImporter::cancelImport()
{
m_cancelled = true;
}
void ItemLibraryAssetImporter::addError(const QString &errMsg, const QString &srcPath) const
{
qCDebug(importerLog) << "Error: "<< errMsg << srcPath;
emit errorReported(errMsg, srcPath);
}
void ItemLibraryAssetImporter::addWarning(const QString &warningMsg, const QString &srcPath) const
{
qCDebug(importerLog) << "Warning: " << warningMsg << srcPath;
emit warningReported(warningMsg, srcPath);
}
void ItemLibraryAssetImporter::addInfo(const QString &infoMsg, const QString &srcPath) const
{
qCDebug(importerLog) << "Info: " << infoMsg << srcPath;
emit infoReported(infoMsg, srcPath);
}
bool ItemLibraryAssetImporter::isQuick3DAsset(const QString &fileName) const
{
static QStringList quick3DExt;
if (quick3DExt.isEmpty()) {
quick3DExt << QString(Constants::FbxExtension)
<< QString(Constants::ColladaExtension)
<< QString(Constants::ObjExtension)
<< QString(Constants::BlenderExtension)
<< QString(Constants::GltfExtension);
}
return quick3DExt.contains(QFileInfo(fileName).suffix());
}
void ItemLibraryAssetImporter::notifyFinished()
{
m_isImporting = false;
emit importFinished();
}
void ItemLibraryAssetImporter::reset()
{
m_isImporting = false;
m_cancelled = false;
#ifdef IMPORT_QUICK3D_ASSETS
m_quick3DAddedImports.clear();
#endif
}
void ItemLibraryAssetImporter::parseFiles(const QStringList &filePaths)
{
if (isCancelled())
return;
const QString progressTitle = tr("Parsing files.");
addInfo(progressTitle);
notifyProgress(0, progressTitle);
uint count = 0;
double quota = 100.0 / filePaths.count();
std::function<void(double)> progress = [this, quota, &count, &progressTitle](double value) {
notifyProgress(qRound(quota * (count + value)), progressTitle);
};
for (const QString &file : filePaths) {
if (isCancelled())
return;
if (isQuick3DAsset(file))
parseQuick3DAsset(file);
notifyProgress(qRound(++count * quota), progressTitle);
}
notifyProgress(100, progressTitle);
}
void ItemLibraryAssetImporter::parseQuick3DAsset(const QString &file)
{
#ifdef IMPORT_QUICK3D_ASSETS
addInfo(tr("Parsing 3D Model"), file);
if (!m_quick3DAssetImporter)
m_quick3DAssetImporter.reset(new QSSGAssetImportManager);
QString errorString;
QDir rootDir(m_importPath);
QFileInfo sourceInfo(file);
QString assetName = sourceInfo.completeBaseName();
if (!assetName.isEmpty()) {
// Fix name so it plays nice with imports
for (QChar &currentChar : assetName) {
if (!currentChar.isLetter() && !currentChar.isDigit())
currentChar = QLatin1Char('_');
}
QCharRef firstChar = assetName[0];
if (firstChar.isDigit())
firstChar = QLatin1Char('_');
if (firstChar.isLower())
firstChar = firstChar.toUpper();
}
QDir outDir = rootDir;
if (outDir.exists(assetName)) {
if (confirmAssetOverwrite(assetName)) {
if (outDir.cd(assetName)) {
outDir.removeRecursively();
outDir.cdUp();
outDir.mkpath(assetName);
} // If cd fails here, it will fail below, too, so no error handling here
} else {
addWarning(tr("Skipped import of existing asset: \"%1\"").arg(assetName));
return;
}
} else {
outDir.mkpath(assetName);
}
if (!outDir.cd(assetName)) {
addError(tr("Could not access asset directory: \"%1\"").arg(outDir.filePath(assetName)));
return;
}
addInfo(tr("Generating 3D assets into: \"%1\"").arg(outDir.absolutePath()));
if (m_quick3DAssetImporter->importFile(
sourceInfo.absoluteFilePath(), outDir,
&errorString) != QSSGAssetImportManager::ImportState::Success) {
addError(tr("Failed to import 3D asset with error: %1").arg(errorString),
sourceInfo.absoluteFilePath());
return;
}
// Generate qmldir file
outDir.setNameFilters({QStringLiteral("*.qml")});
const QFileInfoList qmlFiles = outDir.entryInfoList(QDir::Files);
if (!qmlFiles.isEmpty()) {
QString qmldirFileName = outDir.absoluteFilePath(QStringLiteral("qmldir"));
QSaveFile qmldirFile(qmldirFileName);
QString version = QStringLiteral("1.0");
if (qmldirFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
for (const auto &fi : qmlFiles) {
QString qmlInfo;
qmlInfo.append(fi.baseName());
qmlInfo.append(QLatin1Char(' '));
qmlInfo.append(version);
qmlInfo.append(QLatin1Char(' '));
qmlInfo.append(fi.fileName());
qmldirFile.write(qmlInfo.toUtf8());
}
QString assetFolder = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER);
assetFolder = assetFolder.mid(assetFolder.lastIndexOf(QLatin1Char('/')) + 1);
m_quick3DAddedImports.insert(
qmldirFileName,
Import::createLibraryImport(
QStringLiteral("%1.%2").arg(assetFolder).arg(assetName), version));
qmldirFile.commit();
} else {
addError(tr("Failed to create qmldir file for asset: \"%1\"").arg(assetName));
}
}
// Copy the original asset into a subdirectory
QString origAssetDirName = QStringLiteral("source model");
QDir origAssetDir = outDir;
origAssetDir.mkpath(origAssetDirName);
origAssetDir.cd(origAssetDirName);
QFile::copy(sourceInfo.absoluteFilePath(), origAssetDir.filePath(sourceInfo.fileName()));
#else
Q_UNUSED(file)
#endif
}
void ItemLibraryAssetImporter::notifyProgress(int value, const QString &text) const
{
emit progressChanged(value, text);
keepUiAlive();
}
void ItemLibraryAssetImporter::keepUiAlive() const
{
QApplication::processEvents();
}
bool ItemLibraryAssetImporter::confirmAssetOverwrite(const QString &assetName)
{
const QString title = tr("Overwrite Existing Asset?");
const QString question = tr("Asset already exists. Overwrite?\n\"%1\"").arg(assetName);
return QMessageBox::question(qobject_cast<QWidget *>(parent()),
title, question,
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes;
}
bool ItemLibraryAssetImporter::isCancelled() const
{
keepUiAlive();
return m_cancelled;
}
} // QmlDesigner

View File

@@ -0,0 +1,84 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <QtCore/qobject.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qhash.h>
#include "import.h"
QT_BEGIN_NAMESPACE
class QSSGAssetImportManager;
QT_END_NAMESPACE
namespace QmlDesigner {
class ItemLibraryAssetImporter : public QObject
{
Q_OBJECT
public:
ItemLibraryAssetImporter(QObject *parent = nullptr);
~ItemLibraryAssetImporter();
void importQuick3D(const QStringList &inputFiles, const QString &importPath);
bool isImporting() const;
void cancelImport();
bool isCancelled() const;
void addError(const QString &errMsg, const QString &srcPath = {}) const;
void addWarning(const QString &warningMsg, const QString &srcPath = {}) const;
void addInfo(const QString &infoMsg, const QString &srcPath = {}) const;
bool isQuick3DAsset(const QString &fileName) const;
signals:
void errorReported(const QString &, const QString &) const;
void warningReported(const QString &, const QString &) const;
void infoReported(const QString &, const QString &) const;
void progressChanged(int value, const QString &text) const;
void importFinished();
private:
void notifyFinished();
void reset();
void parseFiles(const QStringList &filePaths);
void parseQuick3DAsset(const QString &file);
void notifyProgress(int value, const QString &text) const;
void keepUiAlive() const;
bool confirmAssetOverwrite(const QString &assetName);
#ifdef IMPORT_QUICK3D_ASSETS
QScopedPointer<QSSGAssetImportManager> m_quick3DAssetImporter;
QHash<QString, Import> m_quick3DAddedImports;
#endif
bool m_isImporting = false;
bool m_cancelled = false;
QString m_importPath;
};
} // QmlDesigner

View File

@@ -26,6 +26,7 @@
#include "itemlibrarywidget.h"
#include "customfilesystemmodel.h"
#include "itemlibraryassetimportdialog.h"
#include <theme.h>
@@ -36,6 +37,8 @@
#include <model.h>
#include <rewritingexception.h>
#include <qmldesignerplugin.h>
#include <qmldesignerconstants.h>
#include <designeractionmanager.h>
#include <utils/algorithm.h>
#include <utils/flowlayout.h>
@@ -183,6 +186,43 @@ ItemLibraryWidget::ItemLibraryWidget(QWidget *parent) :
flowLayout->addWidget(button);
connect(button, &QToolButton::clicked, this, &ItemLibraryWidget::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);
importDlg->show();
return true;
};
const QString category = tr("3D Models");
auto add3DHandler = [&actionManager, &handle3DModel, &category](const char *ext) {
const QString filter = QStringLiteral("*.%1").arg(QString::fromLatin1(ext));
actionManager->registerAddResourceHandler(
AddResourceHandler(category, filter, handle3DModel, 10));
};
// Skip if 3D model 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) {
add3DHandler(Constants::FbxExtension);
add3DHandler(Constants::ColladaExtension);
add3DHandler(Constants::ObjExtension);
add3DHandler(Constants::BlenderExtension);
add3DHandler(Constants::GltfExtension);
}
#endif
// init the first load of the QML UI elements
reloadQmlSource();
}
@@ -192,13 +232,19 @@ void ItemLibraryWidget::setItemLibraryInfo(ItemLibraryInfo *itemLibraryInfo)
if (m_itemLibraryInfo.data() == itemLibraryInfo)
return;
if (m_itemLibraryInfo)
if (m_itemLibraryInfo) {
disconnect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged,
this, &ItemLibraryWidget::delayedUpdateModel);
disconnect(m_itemLibraryInfo.data(), &ItemLibraryInfo::importTagsChanged,
this, &ItemLibraryWidget::delayedUpdateModel);
}
m_itemLibraryInfo = itemLibraryInfo;
if (itemLibraryInfo)
if (itemLibraryInfo) {
connect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged,
this, &ItemLibraryWidget::delayedUpdateModel);
connect(m_itemLibraryInfo.data(), &ItemLibraryInfo::importTagsChanged,
this, &ItemLibraryWidget::delayedUpdateModel);
}
delayedUpdateModel();
}
@@ -383,7 +429,22 @@ void ItemLibraryWidget::addPossibleImport(const QString &name)
QTC_ASSERT(m_model, return);
const Import import = m_model->highestPossibleImport(name);
try {
m_model->changeImports({Import::createLibraryImport(name, import.version())}, {});
QList<Import> addedImports = {Import::createLibraryImport(name, import.version())};
// Special case for adding an import for 3D asset - also add QtQuick3D import
const QString asset3DPrefix = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER + 1)
+ QLatin1Char('.');
if (name.startsWith(asset3DPrefix)) {
const QString q3Dlib = QLatin1String(Constants::QT_QUICK_3D_MODULE_NAME);
Import q3DImport = m_model->highestPossibleImport(q3Dlib);
if (q3DImport.url() == q3Dlib)
addedImports.prepend(Import::createLibraryImport(q3Dlib, q3DImport.version()));
}
RewriterTransaction transaction
= m_model->rewriterView()->beginRewriterTransaction(
QByteArrayLiteral("ItemLibraryWidget::addPossibleImport"));
m_model->changeImports(addedImports, {});
transaction.commit();
}
catch (const RewritingException &e) {
e.showException();

View File

@@ -114,6 +114,7 @@ public:
signals:
void entriesChanged();
void importTagsChanged();
private: // functions
ItemLibraryInfo(QObject *parent = nullptr);

View File

@@ -65,6 +65,8 @@ private: // functions
void registerQmlFile(const QFileInfo &fileInfo, const QString &qualifier, bool addToLibrary);
Model *model() const;
QStringList importPaths() const;
void parseQuick3DAssetDir(const QString &assetPath);
QStringList quick3DAssetPaths() const;
private: // variables
QFileSystemWatcher m_watcher;

View File

@@ -356,7 +356,10 @@ void ItemLibraryInfo::addBlacklistImports(const QStringList &list)
void ItemLibraryInfo::addShowTagsForImports(const QStringList &list)
{
m_showTagsForImports.append(list);
if (!list.isEmpty()) {
m_showTagsForImports.append(list);
emit importTagsChanged();
}
}
void ItemLibraryInfo::setBaseInfo(ItemLibraryInfo *baseInfo)

View File

@@ -85,8 +85,7 @@ void SubComponentManager::addImport(int pos, const Import &import)
url.replace(QLatin1Char('.'), QLatin1Char('/'));
foreach (const QString &path, importPaths()) {
url = path + QLatin1Char('/') + url;
QFileInfo dirInfo = QFileInfo(url);
QFileInfo dirInfo = QFileInfo(path + QLatin1Char('/') + url);
if (dirInfo.exists() && dirInfo.isDir()) {
const QString canonicalDirPath = dirInfo.canonicalFilePath();
m_watcher.addPath(canonicalDirPath);
@@ -126,14 +125,19 @@ void SubComponentManager::parseDirectories()
if (!m_filePath.isEmpty()) {
const QString file = m_filePath.toLocalFile();
QFileInfo dirInfo = QFileInfo(QFileInfo(file).path());
const QString canonicalPath = dirInfo.canonicalFilePath();
if (dirInfo.exists() && dirInfo.isDir())
parseDirectory(dirInfo.canonicalFilePath());
parseDirectory(canonicalPath);
foreach (const QString &subDir, QDir(QFileInfo(file).path()).entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot)) {
parseDirectory(dirInfo.canonicalFilePath() + QLatin1String("/") + subDir, true, subDir.toUtf8());
parseDirectory(canonicalPath + QLatin1Char('/') + subDir, true, subDir.toUtf8());
}
}
const QStringList assetPaths = quick3DAssetPaths();
for (const auto &assetPath : assetPaths)
parseDirectory(assetPath);
foreach (const Import &import, m_imports) {
if (import.isFileImport()) {
QFileInfo dirInfo = QFileInfo(m_filePath.resolved(import.file()).toLocalFile());
@@ -169,6 +173,11 @@ void SubComponentManager::parseDirectory(const QString &canonicalDirPath, bool a
if (!model() || !model()->rewriterView())
return;
if (canonicalDirPath.endsWith(QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER))) {
parseQuick3DAssetDir(canonicalDirPath);
return;
}
QDir designerDir(canonicalDirPath + QLatin1String(Constants::QML_DESIGNER_SUBFOLDER));
if (designerDir.exists()) {
QStringList filter;
@@ -325,7 +334,7 @@ void SubComponentManager::registerQmlFile(const QFileInfo &fileInfo, const QStri
ItemLibraryEntry itemLibraryEntry;
itemLibraryEntry.setType(componentName.toUtf8());
itemLibraryEntry.setName(baseComponentName);
itemLibraryEntry.setCategory(QLatin1String("My QML Components"));
itemLibraryEntry.setCategory(tr("My QML Components"));
if (!qualifier.isEmpty()) {
itemLibraryEntry.setRequiredImport(fixedQualifier);
}
@@ -348,6 +357,75 @@ QStringList SubComponentManager::importPaths() const
return QStringList();
}
void SubComponentManager::parseQuick3DAssetDir(const QString &assetPath)
{
QDir assetDir(assetPath);
const QString assetImportRoot = assetPath.mid(assetPath.lastIndexOf(QLatin1Char('/')) + 1);
QStringList assets = assetDir.entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot);
for (QString &asset : assets)
asset.prepend(assetImportRoot + QLatin1Char('.'));
QStringList newFlowTags;
const QStringList flowTags = model()->metaInfo().itemLibraryInfo()->showTagsForImports();
const QString quick3Dlib = QLatin1String(Constants::QT_QUICK_3D_MODULE_NAME);
const QList<Import> possibleImports = model()->possibleImports();
auto isPossibleImport = [&possibleImports](const QString &asset) {
for (const Import &import : possibleImports) {
if (import.url() == asset)
return true;
}
return false;
};
// If there are 3D assets in import path, add a flow tag for QtQuick3D
if (!assets.isEmpty() && !flowTags.contains(quick3Dlib) && isPossibleImport(quick3Dlib))
newFlowTags << quick3Dlib;
// Create item library entries for Quick3D assets that are imported by document
const QString iconPath = QStringLiteral(":/ItemLibrary/images/item-3D_model-icon.png");
for (auto &import : qAsConst(m_imports)) {
if (import.isLibraryImport() && assets.contains(import.url())) {
assets.removeOne(import.url());
ItemLibraryEntry itemLibraryEntry;
const QString name = import.url().mid(import.url().indexOf(QLatin1Char('.')) + 1);
const QString type = import.url() + QLatin1Char('.') + name;
// For now we assume version is always 1.0 as that's what importer hardcodes
itemLibraryEntry.setType(type.toUtf8(), 1, 0);
itemLibraryEntry.setName(name);
itemLibraryEntry.setCategory(tr("My Quick3D Components"));
itemLibraryEntry.setRequiredImport(import.url());
itemLibraryEntry.setLibraryEntryIconPath(iconPath);
itemLibraryEntry.setTypeIcon(QIcon(iconPath));
if (!model()->metaInfo().itemLibraryInfo()->containsEntry(itemLibraryEntry))
model()->metaInfo().itemLibraryInfo()->addEntries({itemLibraryEntry});
}
}
// Create flow tags for the rest, if they are possible imports
if (!assets.isEmpty()) {
for (const QString &asset : qAsConst(assets)) {
if (!flowTags.contains(asset) && isPossibleImport(asset))
newFlowTags << asset;
}
}
if (!newFlowTags.isEmpty())
model()->metaInfo().itemLibraryInfo()->addShowTagsForImports(newFlowTags);
}
QStringList SubComponentManager::quick3DAssetPaths() const
{
const auto impPaths = importPaths();
QStringList retPaths;
for (const auto &impPath : impPaths) {
const QString assetPath = impPath + QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER);
if (QFileInfo(assetPath).exists())
retPaths << assetPath;
}
return retPaths;
}
/*!
\class SubComponentManager
@@ -392,6 +470,14 @@ void SubComponentManager::update(const QUrl &filePath, const QList<Import> &impo
m_dirToQualifier.remove(oldDir.canonicalFilePath(), QString());
if (!m_dirToQualifier.contains(oldDir.canonicalFilePath()))
m_watcher.removePath(oldDir.filePath());
// Remove old watched asset paths
const QStringList watchPaths = m_watcher.directories();
const QString &quick3DAssetFolder = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER);
for (const auto &watchPath : watchPaths) {
if (watchPath.endsWith(quick3DAssetFolder))
m_watcher.removePath(watchPath);
}
}
if (!newDir.filePath().isEmpty())
@@ -417,7 +503,13 @@ void SubComponentManager::update(const QUrl &filePath, const QList<Import> &impo
addImport(ii, imports.at(ii));
}
m_watcher.addPath(newDir.absoluteFilePath());
const QString newPath = newDir.absoluteFilePath();
m_watcher.addPath(newPath);
// Watch existing asset paths, including a global ones if they exist
const auto assetPaths = quick3DAssetPaths();
for (const auto &assetPath : assetPaths)
m_watcher.addPath(assetPath);
parseDirectories();
}

View File

@@ -50,6 +50,15 @@ const char GO_INTO_COMPONENT[] = "QmlDesigner.GoIntoComponent";
const char EXPORT_AS_IMAGE[] = "QmlDesigner.ExportAsImage";
const char QML_DESIGNER_SUBFOLDER[] = "/designer/";
const char QUICK_3D_ASSETS_FOLDER[] = "/Quick3DAssets";
const char DEFAULT_ASSET_IMPORT_FOLDER[] = "/asset_imports";
const char QT_QUICK_3D_MODULE_NAME[] = "QtQuick3D";
const char FbxExtension[] = "fbx";
const char ColladaExtension[] = "dae";
const char ObjExtension[] = "obj";
const char BlenderExtension[] = "blend";
const char GltfExtension[] = "gltf";
namespace Internal {
enum { debug = 0 };

View File

@@ -524,6 +524,11 @@ Project {
"integration/utilitypanelcontroller.cpp",
"integration/utilitypanelcontroller.h",
"itemlibrary/itemlibrary.qrc",
"itemlibrary/itemlibraryassetimportdialog.cpp",
"itemlibrary/itemlibraryassetimportdialog.h",
"itemlibrary/itemlibraryassetimportdialog.ui",
"itemlibrary/itemlibraryassetimporter.cpp",
"itemlibrary/itemlibraryassetimporter.h",
"itemlibrary/itemlibraryimageprovider.cpp",
"itemlibrary/itemlibraryimageprovider.h",
"itemlibrary/itemlibraryitem.cpp",

View File

@@ -55,6 +55,7 @@
#include <projectexplorer/toolchain.h>
#include <remotelinux/genericdirectuploadstep.h>
#include <remotelinux/makeinstallstep.h>
#include <remotelinux/remotelinuxcheckforfreediskspacestep.h>
#include <qtsupport/qtkitinformation.h>
@@ -90,6 +91,11 @@ public:
addSupportedTargetDeviceType(Constants::QNX_QNX_OS_TYPE);
setUseDeploymentDataView();
addInitialStep(RemoteLinux::MakeInstallStep::stepId(), [](Target *target) {
const Project * const prj = target->project();
return prj->deploymentKnowledge() == DeploymentKnowledge::Bad
&& prj->hasMakeInstallEquivalent();
});
addInitialStep(DeviceCheckBuildStep::stepId());
addInitialStep(RemoteLinux::RemoteLinuxCheckForFreeDiskSpaceStep::stepId());
addInitialStep(RemoteLinux::GenericDirectUploadStep::stepId());
@@ -110,6 +116,7 @@ public:
QnxDeployConfigurationFactory deployConfigFactory;
GenericQnxDeployStepFactory<RemoteLinux::GenericDirectUploadStep> directUploadDeployFactory;
GenericQnxDeployStepFactory<RemoteLinux::RemoteLinuxCheckForFreeDiskSpaceStep> checkForFreeDiskSpaceDeployFactory;
GenericQnxDeployStepFactory<RemoteLinux::MakeInstallStep> makeInstallDeployFactory;
GenericQnxDeployStepFactory<DeviceCheckBuildStep> checkBuildDeployFactory;
QnxRunConfigurationFactory runConfigFactory;
QnxSettingsPage settingsPage;

View File

@@ -45,7 +45,6 @@ using namespace ProjectExplorer;
using namespace Utils;
namespace RemoteLinux {
namespace Internal {
const char MakeAspectId[] = "RemoteLinux.MakeInstall.Make";
const char InstallRootAspectId[] = "RemoteLinux.MakeInstall.InstallRoot";
@@ -228,5 +227,4 @@ bool MakeInstallStep::fromMap(const QVariantMap &map)
return true;
}
} // namespace Internal
} // namespace RemoteLinux

View File

@@ -25,15 +25,16 @@
#pragma once
#include "remotelinux_export.h"
#include <projectexplorer/deploymentdata.h>
#include <projectexplorer/makestep.h>
namespace Utils { class FilePath; }
namespace RemoteLinux {
namespace Internal {
class MakeInstallStep : public ProjectExplorer::MakeStep
class REMOTELINUX_EXPORT MakeInstallStep : public ProjectExplorer::MakeStep
{
Q_OBJECT
public:
@@ -62,5 +63,4 @@ private:
bool m_isCmakeProject = false;
};
} // namespace Internal
} // namespace RemoteLinux