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

View File

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

View File

@@ -44,8 +44,8 @@ QString FakeMetaEnum::name() const
void FakeMetaEnum::setName(const QString &name) void FakeMetaEnum::setName(const QString &name)
{ m_name = name; } { m_name = name; }
void FakeMetaEnum::addKey(const QString &key, int value) void FakeMetaEnum::addKey(const QString &key)
{ m_keys.append(key); m_values.append(value); } { m_keys.append(key); }
QString FakeMetaEnum::key(int index) const QString FakeMetaEnum::key(int index) const
{ return m_keys.at(index); } { 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 *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar)); 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 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 newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
QString res = QLatin1String("Enum "); QString res = QLatin1String("Enum ");
res += name(); res += name();
res += QLatin1String(":{"); res += QLatin1String(": [");
for (int i = 0; i < keyCount(); ++i) { for (int i = 0; i < keyCount(); ++i) {
res += newLine; res += newLine;
res += QLatin1String(" "); res += QLatin1String(" ");
res += key(i); res += key(i);
res += QLatin1String(": ");
res += QString::number(m_values.value(i, -1));
} }
res += newLine; res += newLine;
res += QLatin1Char('}'); res += QLatin1Char(']');
return res; return res;
} }

View File

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

View File

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

View File

@@ -649,39 +649,34 @@ void TypeDescriptionReader::readEnumValues(AST::UiScriptBinding *ast, LanguageUt
return; return;
} }
ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement); auto *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
if (!expStmt) { if (!expStmt) {
addError(ast->statement->firstSourceLocation(), tr("Expected object literal after colon.")); addError(ast->statement->firstSourceLocation(), tr("Expected expression after colon."));
return; return;
} }
ObjectPattern *objectLit = AST::cast<ObjectPattern *>(expStmt->expression); if (auto *objectLit = AST::cast<ObjectPattern *>(expStmt->expression)) {
if (!objectLit) { for (PatternPropertyList *it = objectLit->properties; it; it = it->next) {
addError(expStmt->firstSourceLocation(), tr("Expected object literal after colon.")); if (PatternProperty *assignement = it->property) {
return; if (auto *name = AST::cast<StringLiteralPropertyName *>(assignement->name)) {
} fme->addKey(name->id.toString());
continue;
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;
} }
addError(it->firstSourceLocation(), tr("Expected strings as enum keys."));
double v = value->value;
if (minus)
v = -v;
fme->addKey(propName->id.toString(), v);
continue;
} }
PatternPropertyList *getterSetter = AST::cast<PatternPropertyList *>(it->next); } else if (auto *arrayLit = AST::cast<ArrayPattern *>(expStmt->expression)) {
if (getterSetter) for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
addError(objectLit->firstSourceLocation(), tr("Enum should not contain getter and setters, but only 'string: number' elements.")); 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()) { if (Utils::HostOsInfo::isWindowsHost()) {
#ifdef Q_OS_WIN64 // Tries to detect the candidate from the 32-bit
static const char kRegistryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\SDCC"; // or 64-bit system registry format.
#else auto probeCandidate = [](QSettings::Format format) {
static const char kRegistryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\SDCC"; QSettings registry("HKEY_LOCAL_MACHINE\\SOFTWARE\\SDCC",
#endif format);
QString compilerPath = registry.value("Default").toString();
QSettings registry(kRegistryNode, QSettings::NativeFormat); if (compilerPath.isEmpty())
QString compilerPath = registry.value("Default").toString(); return Candidate{};
if (!compilerPath.isEmpty()) {
// Build full compiler path. // Build full compiler path.
compilerPath += "\\bin\\sdcc.exe"; compilerPath += "\\bin\\sdcc.exe";
const FilePath fn = FilePath::fromString( const FilePath fn = FilePath::fromString(
QFileInfo(compilerPath).absoluteFilePath()); QFileInfo(compilerPath).absoluteFilePath());
if (compilerExists(fn)) { if (!compilerExists(fn))
// Build compiler version. return Candidate{};
const QString version = QString("%1.%2.%3").arg( // Build compiler version.
registry.value("VersionMajor").toString(), const QString version = QString("%1.%2.%3").arg(
registry.value("VersionMinor").toString(), registry.value("VersionMajor").toString(),
registry.value("VersionRevision").toString()); registry.value("VersionMinor").toString(),
candidates.push_back({fn, version}); 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/project.h>
#include <projectexplorer/target.h> #include <projectexplorer/target.h>
#include <remotelinux/remotelinuxcheckforfreediskspacestep.h>
#include <remotelinux/genericdirectuploadstep.h> #include <remotelinux/genericdirectuploadstep.h>
#include <remotelinux/makeinstallstep.h>
#include <remotelinux/remotelinuxcheckforfreediskspacestep.h>
#include <remotelinux/remotelinuxdeployconfiguration.h> #include <remotelinux/remotelinuxdeployconfiguration.h>
using namespace ProjectExplorer; using namespace ProjectExplorer;
@@ -51,6 +52,11 @@ QdbDeployConfigurationFactory::QdbDeployConfigurationFactory()
"Deploy to Boot2Qt target")); "Deploy to Boot2Qt target"));
setUseDeploymentDataView(); setUseDeploymentDataView();
addInitialStep(RemoteLinux::MakeInstallStep::stepId(), [](Target *target) {
const Project * const prj = target->project();
return prj->deploymentKnowledge() == DeploymentKnowledge::Bad
&& prj->hasMakeInstallEquivalent();
});
addInitialStep(RemoteLinuxCheckForFreeDiskSpaceStep::stepId()); addInitialStep(RemoteLinuxCheckForFreeDiskSpaceStep::stepId());
addInitialStep(QdbStopApplicationStep::stepId()); addInitialStep(QdbStopApplicationStep::stepId());
addInitialStep(GenericDirectUploadStep::stepId()); addInitialStep(GenericDirectUploadStep::stepId());

View File

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

View File

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

View File

@@ -45,11 +45,13 @@
#include <texteditor/texteditoractionhandler.h> #include <texteditor/texteditoractionhandler.h>
#include <texteditor/texteditorconstants.h> #include <texteditor/texteditorconstants.h>
#include <utils/executeondestruction.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/synchronousprocess.h> #include <utils/synchronousprocess.h>
#include <QCoreApplication> #include <QCoreApplication>
#include <QRegularExpression> #include <QRegularExpression>
#include <QTimer>
using namespace ProjectExplorer; using namespace ProjectExplorer;
using namespace Utils; using namespace Utils;
@@ -58,6 +60,7 @@ namespace Python {
namespace Internal { namespace Internal {
static constexpr char startPylsInfoBarId[] = "PythonEditor::StartPyls"; static constexpr char startPylsInfoBarId[] = "PythonEditor::StartPyls";
static constexpr char installPylsInfoBarId[] = "PythonEditor::InstallPyls";
struct PythonForProject struct PythonForProject
{ {
@@ -193,6 +196,90 @@ static LanguageClient::Client *registerLanguageServer(const PythonForProject &py
return LanguageClient::LanguageClientManager::clientForSetting(settings).value(0); 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, static void setupPythonLanguageServer(const PythonForProject &python,
QPointer<TextEditor::TextDocument> document) QPointer<TextEditor::TextDocument> document)
{ {
@@ -206,13 +293,25 @@ static void updateEditorInfoBar(const PythonForProject &python, TextEditor::Text
const PythonLanguageServerState &lsState = checkPythonLanguageServer(python.path, document); const PythonLanguageServerState &lsState = checkPythonLanguageServer(python.path, document);
if (lsState.state == PythonLanguageServerState::CanNotBeInstalled if (lsState.state == PythonLanguageServerState::CanNotBeInstalled
|| lsState.state == PythonLanguageServerState::AlreadyConfigured || lsState.state == PythonLanguageServerState::AlreadyConfigured) {
|| lsState.state == PythonLanguageServerState::CanBeInstalled /* TODO */) {
return; return;
} }
Core::InfoBar *infoBar = document->infoBar(); 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)) { && infoBar->canInfoBeAdded(startPylsInfoBarId)) {
auto message = PythonEditorFactory::tr("Found a Python language server for %1 (%2). " auto message = PythonEditorFactory::tr("Found a Python language server for %1 (%2). "
"Should this one be set up for this document?") "Should this one be set up for this document?")
@@ -264,3 +363,5 @@ PythonEditorFactory::PythonEditorFactory()
} // namespace Internal } // namespace Internal
} // namespace Python } // namespace Python
#include "pythoneditor.moc"

View File

@@ -267,6 +267,17 @@ extend_qtc_plugin(QmlDesigner
itemlibrarysectionmodel.cpp itemlibrarysectionmodel.h itemlibrarysectionmodel.cpp itemlibrarysectionmodel.h
itemlibraryview.cpp itemlibraryview.h itemlibraryview.cpp itemlibraryview.h
itemlibrarywidget.cpp itemlibrarywidget.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 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 VPATH += $$PWD
qtHaveModule(quick3dassetimport) {
QT *= quick3dassetimport-private
DEFINES *= IMPORT_QUICK3D_ASSETS
}
# Input # Input
HEADERS += itemlibraryview.h \ HEADERS += itemlibraryview.h \
itemlibrarywidget.h \ itemlibrarywidget.h \
@@ -9,6 +14,8 @@ HEADERS += itemlibraryview.h \
itemlibrarysectionmodel.h \ itemlibrarysectionmodel.h \
itemlibraryitem.h \ itemlibraryitem.h \
itemlibrarysection.h \ itemlibrarysection.h \
itemlibraryassetimportdialog.h \
itemlibraryassetimporter.h \
customfilesystemmodel.h customfilesystemmodel.h
SOURCES += itemlibraryview.cpp \ SOURCES += itemlibraryview.cpp \
@@ -19,6 +26,9 @@ SOURCES += itemlibraryview.cpp \
itemlibrarysectionmodel.cpp \ itemlibrarysectionmodel.cpp \
itemlibraryitem.cpp \ itemlibraryitem.cpp \
itemlibrarysection.cpp \ itemlibrarysection.cpp \
itemlibraryassetimportdialog.cpp \
itemlibraryassetimporter.cpp \
customfilesystemmodel.cpp customfilesystemmodel.cpp
RESOURCES += itemlibrary.qrc RESOURCES += itemlibrary.qrc
FORMS += itemlibraryassetimportdialog.ui

View File

@@ -3,5 +3,7 @@
<file>images/item-default-icon.png</file> <file>images/item-default-icon.png</file>
<file>images/item-invalid-icon.png</file> <file>images/item-invalid-icon.png</file>
<file>images/item-default-icon@2x.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> </qresource>
</RCC> </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 "itemlibrarywidget.h"
#include "customfilesystemmodel.h" #include "customfilesystemmodel.h"
#include "itemlibraryassetimportdialog.h"
#include <theme.h> #include <theme.h>
@@ -36,6 +37,8 @@
#include <model.h> #include <model.h>
#include <rewritingexception.h> #include <rewritingexception.h>
#include <qmldesignerplugin.h> #include <qmldesignerplugin.h>
#include <qmldesignerconstants.h>
#include <designeractionmanager.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/flowlayout.h> #include <utils/flowlayout.h>
@@ -183,6 +186,43 @@ ItemLibraryWidget::ItemLibraryWidget(QWidget *parent) :
flowLayout->addWidget(button); flowLayout->addWidget(button);
connect(button, &QToolButton::clicked, this, &ItemLibraryWidget::addResources); 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 // init the first load of the QML UI elements
reloadQmlSource(); reloadQmlSource();
} }
@@ -192,13 +232,19 @@ void ItemLibraryWidget::setItemLibraryInfo(ItemLibraryInfo *itemLibraryInfo)
if (m_itemLibraryInfo.data() == itemLibraryInfo) if (m_itemLibraryInfo.data() == itemLibraryInfo)
return; return;
if (m_itemLibraryInfo) if (m_itemLibraryInfo) {
disconnect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged, disconnect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged,
this, &ItemLibraryWidget::delayedUpdateModel); this, &ItemLibraryWidget::delayedUpdateModel);
disconnect(m_itemLibraryInfo.data(), &ItemLibraryInfo::importTagsChanged,
this, &ItemLibraryWidget::delayedUpdateModel);
}
m_itemLibraryInfo = itemLibraryInfo; m_itemLibraryInfo = itemLibraryInfo;
if (itemLibraryInfo) if (itemLibraryInfo) {
connect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged, connect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged,
this, &ItemLibraryWidget::delayedUpdateModel); this, &ItemLibraryWidget::delayedUpdateModel);
connect(m_itemLibraryInfo.data(), &ItemLibraryInfo::importTagsChanged,
this, &ItemLibraryWidget::delayedUpdateModel);
}
delayedUpdateModel(); delayedUpdateModel();
} }
@@ -383,7 +429,22 @@ void ItemLibraryWidget::addPossibleImport(const QString &name)
QTC_ASSERT(m_model, return); QTC_ASSERT(m_model, return);
const Import import = m_model->highestPossibleImport(name); const Import import = m_model->highestPossibleImport(name);
try { 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) { catch (const RewritingException &e) {
e.showException(); e.showException();

View File

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

View File

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

View File

@@ -356,7 +356,10 @@ void ItemLibraryInfo::addBlacklistImports(const QStringList &list)
void ItemLibraryInfo::addShowTagsForImports(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) void ItemLibraryInfo::setBaseInfo(ItemLibraryInfo *baseInfo)

View File

@@ -85,8 +85,7 @@ void SubComponentManager::addImport(int pos, const Import &import)
url.replace(QLatin1Char('.'), QLatin1Char('/')); url.replace(QLatin1Char('.'), QLatin1Char('/'));
foreach (const QString &path, importPaths()) { foreach (const QString &path, importPaths()) {
url = path + QLatin1Char('/') + url; QFileInfo dirInfo = QFileInfo(path + QLatin1Char('/') + url);
QFileInfo dirInfo = QFileInfo(url);
if (dirInfo.exists() && dirInfo.isDir()) { if (dirInfo.exists() && dirInfo.isDir()) {
const QString canonicalDirPath = dirInfo.canonicalFilePath(); const QString canonicalDirPath = dirInfo.canonicalFilePath();
m_watcher.addPath(canonicalDirPath); m_watcher.addPath(canonicalDirPath);
@@ -126,14 +125,19 @@ void SubComponentManager::parseDirectories()
if (!m_filePath.isEmpty()) { if (!m_filePath.isEmpty()) {
const QString file = m_filePath.toLocalFile(); const QString file = m_filePath.toLocalFile();
QFileInfo dirInfo = QFileInfo(QFileInfo(file).path()); QFileInfo dirInfo = QFileInfo(QFileInfo(file).path());
const QString canonicalPath = dirInfo.canonicalFilePath();
if (dirInfo.exists() && dirInfo.isDir()) 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)) { 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) { foreach (const Import &import, m_imports) {
if (import.isFileImport()) { if (import.isFileImport()) {
QFileInfo dirInfo = QFileInfo(m_filePath.resolved(import.file()).toLocalFile()); 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()) if (!model() || !model()->rewriterView())
return; return;
if (canonicalDirPath.endsWith(QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER))) {
parseQuick3DAssetDir(canonicalDirPath);
return;
}
QDir designerDir(canonicalDirPath + QLatin1String(Constants::QML_DESIGNER_SUBFOLDER)); QDir designerDir(canonicalDirPath + QLatin1String(Constants::QML_DESIGNER_SUBFOLDER));
if (designerDir.exists()) { if (designerDir.exists()) {
QStringList filter; QStringList filter;
@@ -325,7 +334,7 @@ void SubComponentManager::registerQmlFile(const QFileInfo &fileInfo, const QStri
ItemLibraryEntry itemLibraryEntry; ItemLibraryEntry itemLibraryEntry;
itemLibraryEntry.setType(componentName.toUtf8()); itemLibraryEntry.setType(componentName.toUtf8());
itemLibraryEntry.setName(baseComponentName); itemLibraryEntry.setName(baseComponentName);
itemLibraryEntry.setCategory(QLatin1String("My QML Components")); itemLibraryEntry.setCategory(tr("My QML Components"));
if (!qualifier.isEmpty()) { if (!qualifier.isEmpty()) {
itemLibraryEntry.setRequiredImport(fixedQualifier); itemLibraryEntry.setRequiredImport(fixedQualifier);
} }
@@ -348,6 +357,75 @@ QStringList SubComponentManager::importPaths() const
return QStringList(); 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 \class SubComponentManager
@@ -392,6 +470,14 @@ void SubComponentManager::update(const QUrl &filePath, const QList<Import> &impo
m_dirToQualifier.remove(oldDir.canonicalFilePath(), QString()); m_dirToQualifier.remove(oldDir.canonicalFilePath(), QString());
if (!m_dirToQualifier.contains(oldDir.canonicalFilePath())) if (!m_dirToQualifier.contains(oldDir.canonicalFilePath()))
m_watcher.removePath(oldDir.filePath()); 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()) if (!newDir.filePath().isEmpty())
@@ -417,7 +503,13 @@ void SubComponentManager::update(const QUrl &filePath, const QList<Import> &impo
addImport(ii, imports.at(ii)); 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(); parseDirectories();
} }

View File

@@ -50,6 +50,15 @@ const char GO_INTO_COMPONENT[] = "QmlDesigner.GoIntoComponent";
const char EXPORT_AS_IMAGE[] = "QmlDesigner.ExportAsImage"; const char EXPORT_AS_IMAGE[] = "QmlDesigner.ExportAsImage";
const char QML_DESIGNER_SUBFOLDER[] = "/designer/"; 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 { namespace Internal {
enum { debug = 0 }; enum { debug = 0 };

View File

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

View File

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

View File

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

View File

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