From d5f3335c5e9f812c98d837f4065d2294dee91e2d Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Thu, 26 Sep 2019 14:27:41 +0200 Subject: [PATCH 01/10] RemoteLinux: Use the "install to local dir" step in derived plugins That is, Boot2Qt and Qnx. Task-number: QTCREATORBUG-22822 Change-Id: Id48069c6ee2bc857a94be173b6c1de9bc69404a8 Reviewed-by: hjk --- src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp | 8 +++++++- src/plugins/boot2qt/qdbplugin.cpp | 5 +++-- src/plugins/qnx/qnxplugin.cpp | 7 +++++++ src/plugins/remotelinux/makeinstallstep.cpp | 2 -- src/plugins/remotelinux/makeinstallstep.h | 6 +++--- 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp b/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp index c033b6b1019..7e973d86e18 100644 --- a/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp +++ b/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp @@ -33,8 +33,9 @@ #include #include -#include #include +#include +#include #include 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()); diff --git a/src/plugins/boot2qt/qdbplugin.cpp b/src/plugins/boot2qt/qdbplugin.cpp index 8a07dbba6c7..81a2fc80d05 100644 --- a/src/plugins/boot2qt/qdbplugin.cpp +++ b/src/plugins/boot2qt/qdbplugin.cpp @@ -46,6 +46,7 @@ #include #include +#include #include #include @@ -175,8 +176,8 @@ public: QdbDeployStepFactory m_checkForFreeDiskSpaceStepFactory; - QdbDeployStepFactory - m_directUploadStepFactory; + QdbDeployStepFactory m_directUploadStepFactory; + QdbDeployStepFactory m_makeInstallStepFactory; const QList supportedRunConfigs { m_runConfigFactory.id(), diff --git a/src/plugins/qnx/qnxplugin.cpp b/src/plugins/qnx/qnxplugin.cpp index a0780561369..c9f124b31a8 100644 --- a/src/plugins/qnx/qnxplugin.cpp +++ b/src/plugins/qnx/qnxplugin.cpp @@ -55,6 +55,7 @@ #include #include +#include #include #include @@ -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 directUploadDeployFactory; GenericQnxDeployStepFactory checkForFreeDiskSpaceDeployFactory; + GenericQnxDeployStepFactory makeInstallDeployFactory; GenericQnxDeployStepFactory checkBuildDeployFactory; QnxRunConfigurationFactory runConfigFactory; QnxSettingsPage settingsPage; diff --git a/src/plugins/remotelinux/makeinstallstep.cpp b/src/plugins/remotelinux/makeinstallstep.cpp index 3c2b798ca50..ccc19418fe3 100644 --- a/src/plugins/remotelinux/makeinstallstep.cpp +++ b/src/plugins/remotelinux/makeinstallstep.cpp @@ -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 diff --git a/src/plugins/remotelinux/makeinstallstep.h b/src/plugins/remotelinux/makeinstallstep.h index 78f9029a9e6..9cc27c3435d 100644 --- a/src/plugins/remotelinux/makeinstallstep.h +++ b/src/plugins/remotelinux/makeinstallstep.h @@ -25,15 +25,16 @@ #pragma once +#include "remotelinux_export.h" + #include #include 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 From 76dea6c61f51128e1d01a33d41cac04d49189c9b Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 18 Sep 2019 17:12:45 +0300 Subject: [PATCH 02/10] QmlDesigner: Support importing 3D assets into Quick3D components Added import dialog for importing 3D assets. Generated Quick3D components are placed into a hardcoded path under an existing import directory, preferring an import path that ends with "asset_imports" folder. Import statements for imported assets are not automatically added to the current document. Instead, flow tags are generated for the available imports. If QtQuick3D import is missing, a flow tag for that is also generated. Item library entries for imported assets are created and grouped in their own group. Task-number: QDS-1051 Task-number: QDS-1053 Change-Id: I89cef9c0dc8b5a71a5cf25a6a184cef49c1090dc Reviewed-by: Mahmoud Badri Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/CMakeLists.txt | 11 + .../itemlibrary/images/item-3D_model-icon.png | Bin 0 -> 492 bytes .../images/item-3D_model-icon@2x.png | Bin 0 -> 942 bytes .../components/itemlibrary/itemlibrary.pri | 12 +- .../components/itemlibrary/itemlibrary.qrc | 2 + .../itemlibraryassetimportdialog.cpp | 229 +++++++++++++ .../itemlibraryassetimportdialog.h | 71 ++++ .../itemlibraryassetimportdialog.ui | 49 +++ .../itemlibrary/itemlibraryassetimporter.cpp | 322 ++++++++++++++++++ .../itemlibrary/itemlibraryassetimporter.h | 84 +++++ .../itemlibrary/itemlibrarywidget.cpp | 50 ++- .../designercore/include/itemlibraryinfo.h | 1 + .../include/subcomponentmanager.h | 2 + .../designercore/metainfo/itemlibraryinfo.cpp | 5 +- .../metainfo/subcomponentmanager.cpp | 104 +++++- .../qmldesigner/qmldesignerconstants.h | 9 + src/plugins/qmldesigner/qmldesignerplugin.qbs | 5 + 17 files changed, 946 insertions(+), 10 deletions(-) create mode 100644 src/plugins/qmldesigner/components/itemlibrary/images/item-3D_model-icon.png create mode 100644 src/plugins/qmldesigner/components/itemlibrary/images/item-3D_model-icon@2x.png create mode 100644 src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp create mode 100644 src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h create mode 100644 src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui create mode 100644 src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp create mode 100644 src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 99b82116bfb..3d8f5ce3cb2 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -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 diff --git a/src/plugins/qmldesigner/components/itemlibrary/images/item-3D_model-icon.png b/src/plugins/qmldesigner/components/itemlibrary/images/item-3D_model-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1a69afa777c1bfc6338e778ec7e57a26c492d7fd GIT binary patch literal 492 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuJoH+#A`hIn+|ooeq9nkaFs zKGVrLA==rDWoqz+AYI+AXy+}q0l~p9L%dW}1)s=I$!K0d8hK3{%r@AB2Ue;@EX|NGhI!@m3F=NPk+e#?9?S(WekD`ffRwT*8dA22D{ zI-!cU$2;outXC2@?0D|5x-~jS9^|{F-m68*?+XAYE!7syty$egu0b(7&8^Lmxs zP^lYoO!FAhy;5&|mfWG6SvbA%t=$9Gf|bq>mE;?wCtcgU>B_$k)kgo5TozA~+Rc-5 zpQUbzs_UM~s{Stj%f5W~n3do6f@1@N4rBAAI41SRlNMc}OYVpicm=(Z=jd&{uw+NG z$*i`w-yP;JDf!}ee5uwawinY&_$|b2dmmM-o;Jtta@X7R1LmIB8$KU6UZLmVW&S(+ z%9@b52eg`9%ng{<+>&FSrW)(xFXenBZ1FPd*L-|w6X(Pw@JVc|Ro}GvK3c^10Qynbqq**BXECzaxL==-+$A_deU)KlAfk<@qzI zB@J&^H~1b{d|>tg?MZd-lh%as-k8T`!}5kDZMxHa=IDm+s()vfzG=S3b0conzRU;T zbSma0A9y^8FKwdS%i9fa858&m^2KE4`8TMm)SBjWcP~k2-j>fWkNFzo^mJagbb$)? zim*#F4UH43)iy9zaP3gbzAz`gG4??Aq<=5YG`zj??cj_B+f#XeJy-c)vLXIb{aUFF zdnGDFcW85OJ0F;^c**(SW(n1D8>DyGbC;cWKal2m++AwrwT8E^8*(=aoIN4cBU^vm z=||I&`LEpe9%y*0?exX?z}CpuA0IzYVXAPx6u*@%$6jFj={uY%drp}jm^{f&$8P>K z)0rtTR@(z+dRlj`_E|MQ@OI|I8!9h7wqA6JJdtx>cI(kck2Zf&*~e~?Q@#JK<C}heb=`wN4((F;1@z6LEUosO=s9 zV#2$JJR*NMa@g-p`r@xznSML-_T@(RAf6rjZf)#rY+Ao~(ytv-8M_y3td;*@WMKdL zYI|eT`J_elfyM>#s!QKmFZw@e&2FF1R1ntG%Rtv?aR(#; ze^qo%N<-4~xc2Nu)8rbb>3&b{F~?2%k}IZbxTulo_V#0s=dbG(IaKcTORxLdg%^tP z?QT-s`Y&D@W_P<)iN#&G-kf*aeEH|O7I(6OX3X#1RMfvDIC2Z)+C5nbzn57gN}Tmj zw3(YSColQUw)k%n=R95?;@(wtcbUN^t-T8`wSFjO_|JSSKA3sU=lWF)3=9mOu6{1- HoD!Mimages/item-default-icon.png images/item-invalid-icon.png images/item-default-icon@2x.png + images/item-3D_model-icon.png + images/item-3D_model-icon@2x.png diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp new file mode 100644 index 00000000000..0dd80c3c9af --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include + +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(); +} +} diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h new file mode 100644 index 00000000000..93fcc58fc67 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h @@ -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 + +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; +}; +} diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui new file mode 100644 index 00000000000..7cccc494144 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui @@ -0,0 +1,49 @@ + + + QmlDesigner::ItemLibraryAssetImportDialog + + + + 0 + 0 + 800 + 480 + + + + Asset Import + + + + + + true + + + + + + + + + + + + + + 0 + + + + + + + QDialogButtonBox::Close|QDialogButtonBox::Ok + + + + + + + + diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp new file mode 100644 index 00000000000..9aa4ea0262e --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -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 +#include +#include +#include +#include + +#ifdef IMPORT_QUICK3D_ASSETS +#include +#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 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 ¤tChar : 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(parent()), + title, question, + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes; +} + +bool ItemLibraryAssetImporter::isCancelled() const +{ + keepUiAlive(); + return m_cancelled; +} + +} // QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h new file mode 100644 index 00000000000..a8e0a5b1a92 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h @@ -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 +#include +#include + +#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 m_quick3DAssetImporter; + QHash m_quick3DAddedImports; +#endif + bool m_isImporting = false; + bool m_cancelled = false; + QString m_importPath; +}; +} // QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index b37bbdfa365..9477871bdf0 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -26,6 +26,7 @@ #include "itemlibrarywidget.h" #include "customfilesystemmodel.h" +#include "itemlibraryassetimportdialog.h" #include @@ -36,6 +37,8 @@ #include #include #include +#include +#include #include #include @@ -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 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(); } diff --git a/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h b/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h index aed724e50fe..6c91d4917c2 100644 --- a/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h +++ b/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h @@ -114,6 +114,7 @@ public: signals: void entriesChanged(); + void importTagsChanged(); private: // functions ItemLibraryInfo(QObject *parent = nullptr); diff --git a/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h b/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h index da21c17cd92..674f7dff494 100644 --- a/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h +++ b/src/plugins/qmldesigner/designercore/include/subcomponentmanager.h @@ -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; diff --git a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp index 4251dd96c30..32c023464a9 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp @@ -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) diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index 85a84af9a3f..0c707ff837b 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -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 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 &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 &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(); } diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 85350917f03..0f75d535819 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -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 }; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 1e82b1354db..662b38ac3a4 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -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", From 38292de68a707eeaa2e5ece5a16084c00f8e4bef Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 18 Sep 2019 13:37:20 +0300 Subject: [PATCH 03/10] BareMetal: Fix auto detection of SDCC toolchain on Windows The SDCC toolchain package can be provided as 32-bit or as 64-bit installer. If the SDCC 64-bit package will be installed on the 32-bit Windows, then it will not be found in the system registry, because we use the QSettings::NativeFormat. So, we need to check the data for the 32-bit and 64-bit registry sequentially. Change-Id: I1e7711bdde173eff21a7ba84f221d505a21709ca Reviewed-by: Christian Kandeler --- src/plugins/baremetal/sdcctoolchain.cpp | 48 ++++++++++++++++--------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/src/plugins/baremetal/sdcctoolchain.cpp b/src/plugins/baremetal/sdcctoolchain.cpp index b839a68fe8e..a292a575d96 100644 --- a/src/plugins/baremetal/sdcctoolchain.cpp +++ b/src/plugins/baremetal/sdcctoolchain.cpp @@ -395,27 +395,41 @@ QList SdccToolChainFactory::autoDetect(const QList &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); } } From 716ec39ae09eb8078583025daec2d744bd023698 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Thu, 26 Sep 2019 14:37:54 +0200 Subject: [PATCH 04/10] Debugger: Fix automatic raising of Expressions When adding a new expression automatically raise the Expressions widget if it is not shown already. Change-Id: If89e4e4fbf8cbe57e0f08478cd2d3b9f4797f5ad Reviewed-by: hjk --- src/plugins/debugger/debuggerengine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 6b94e5025d1..dc067e65b47 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -2260,8 +2260,8 @@ void DebuggerEngine::openDisassemblerView(const Location &location) void DebuggerEngine::raiseWatchersWindow() { - if (d->m_watchersView) { - if (auto dock = qobject_cast(d->m_watchersView->parentWidget())) { + if (d->m_watchersView && d->m_watchersWindow) { + if (auto dock = qobject_cast(d->m_watchersWindow->parentWidget())) { if (QAction *act = dock->toggleViewAction()) { if (!act->isChecked()) QTimer::singleShot(1, act, [act] { act->trigger(); }); From a0a0ee0b5f50ddbb2502023531614c3549a31ed2 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Fri, 27 Sep 2019 15:54:11 +0200 Subject: [PATCH 05/10] More change log for 4.10.1 Change-Id: Iae2927d24e021ef8b2e2f4849963fde4c22f55c0 Reviewed-by: Leena Miettinen --- dist/changes-4.10.1.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/dist/changes-4.10.1.md b/dist/changes-4.10.1.md index 6e5d0db956d..7d942ba68ab 100644 --- a/dist/changes-4.10.1.md +++ b/dist/changes-4.10.1.md @@ -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 - From 3e2e0c6edc3bde3538106b27eb270dff776b826f Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 27 Sep 2019 15:40:35 +0200 Subject: [PATCH 06/10] Debugger: Use GDB's own pretty printers more cautiously Looks like there is no attempt made on the GDB/libstdc++ side to recognize uninitialized objects, so we cannot convert "all 932 million children, I swear" to a list. Task-number: QTCREATORBUG-22887 Change-Id: I3bf7e53c8b11ece3e1182d8446e959a66a41665a Reviewed-by: Christian Stenger --- share/qtcreator/debugger/gdbbridge.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index 2f056c7a932..027560f333a 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -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': From 24dd7be6dbc150131db62921e2b5b922e136b8aa Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Mon, 30 Sep 2019 08:44:12 +0200 Subject: [PATCH 07/10] Update qbs submodule To HEAD of 1.14 branch. Change-Id: I16a799316afe1d1be3410ef3084c0724de931c42 Reviewed-by: Eike Ziller --- src/shared/qbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/qbs b/src/shared/qbs index 94fe404a5a6..aec975a3f95 160000 --- a/src/shared/qbs +++ b/src/shared/qbs @@ -1 +1 @@ -Subproject commit 94fe404a5a6d7cf91926bcfbd026953994b25815 +Subproject commit aec975a3f95f905b2d63ea1500ace28eddea7b9e From 90963249a40536783f7f1b82fc714b6b0ca70b01 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Mon, 23 Sep 2019 12:10:09 +0200 Subject: [PATCH 08/10] Python: Add info bar to install python language server If the detected python for the current document does not have an installed language server, check whether pip is installed, try to install the language server and if that was successful set it up. Change-Id: Ib6cf3bacdcf3489728990cace5417862b7c78be5 Reviewed-by: Christian Stenger --- src/plugins/python/pythoneditor.cpp | 107 +++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 3 deletions(-) diff --git a/src/plugins/python/pythoneditor.cpp b/src/plugins/python/pythoneditor.cpp index 434ec9e1cc1..8b0c3b84542 100644 --- a/src/plugins/python/pythoneditor.cpp +++ b/src/plugins/python/pythoneditor.cpp @@ -45,11 +45,13 @@ #include #include +#include #include #include #include #include +#include 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 document) + : m_python(python) + , m_document(document) + {} + + void run() + { + auto killTimer = new QTimer(&m_process); + + connect(&m_process, + QOverload::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 m_document; +}; + +static void installPythonLanguageServer(const PythonForProject &python, + QPointer document) +{ + document->infoBar()->removeInfo(installPylsInfoBarId); + + auto install = new PythonLSInstallHelper(python, document); + install->run(); +} + static void setupPythonLanguageServer(const PythonForProject &python, QPointer 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" From 1bf23a6c74c18b1f89d17cbc9bfc2f93056020bc Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 27 Sep 2019 15:41:46 +0300 Subject: [PATCH 09/10] QmlDesigner: When adding 3D asset import, also add missing QtQuick3D When adding 3D asset import via a flow tag, check if QtQuick3D import is missing. If it is, add it automatically. Change-Id: Ia2e67180faa88e10b185a42a7ce426dc29d9ce23 Fixes: QDS-1067 Reviewed-by: Thomas Hartmann --- .../itemlibrary/itemlibrarywidget.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 9477871bdf0..f69c00b94d0 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -429,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 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(); From b5b1367b0b37133c9666a5d20c95d53e5e6d6a43 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 29 Aug 2019 10:38:36 +0200 Subject: [PATCH 10/10] Accept arrays as enum descriptions in qmltypes files We don't use the values at all and they are hard to determine statically. Qt >= 5.15 will generated arrays instead of objects. Change-Id: I3b3bbd427c49e649ca3f9cef51c89b32e830eb66 Reviewed-by: Fabian Kosmale Reviewed-by: Thomas Hartmann --- src/libs/languageutils/fakemetaobject.cpp | 14 ++--- src/libs/languageutils/fakemetaobject.h | 3 +- src/libs/qmljs/qmljsfindexportedcpptypes.cpp | 2 +- src/libs/qmljs/qmljstypedescriptionreader.cpp | 51 +++++++++---------- 4 files changed, 29 insertions(+), 41 deletions(-) diff --git a/src/libs/languageutils/fakemetaobject.cpp b/src/libs/languageutils/fakemetaobject.cpp index 1a6579b6e33..3d8866972b4 100644 --- a/src/libs/languageutils/fakemetaobject.cpp +++ b/src/libs/languageutils/fakemetaobject.cpp @@ -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(&len), sizeof(len)); hash.addData(reinterpret_cast(key.constData()), len * sizeof(QChar)); } - len = m_values.size(); - hash.addData(reinterpret_cast(&len), sizeof(len)); - foreach (int value, m_values) - hash.addData(reinterpret_cast(&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; } diff --git a/src/libs/languageutils/fakemetaobject.h b/src/libs/languageutils/fakemetaobject.h index 543d80b45c9..a629ebedf8f 100644 --- a/src/libs/languageutils/fakemetaobject.h +++ b/src/libs/languageutils/fakemetaobject.h @@ -43,7 +43,6 @@ namespace LanguageUtils { class LANGUAGEUTILS_EXPORT FakeMetaEnum { QString m_name; QStringList m_keys; - QList 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; diff --git a/src/libs/qmljs/qmljsfindexportedcpptypes.cpp b/src/libs/qmljs/qmljsfindexportedcpptypes.cpp index 7ba890e17a2..7b3749ab519 100644 --- a/src/libs/qmljs/qmljsfindexportedcpptypes.cpp +++ b/src/libs/qmljs/qmljsfindexportedcpptypes.cpp @@ -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); } diff --git a/src/libs/qmljs/qmljstypedescriptionreader.cpp b/src/libs/qmljs/qmljstypedescriptionreader.cpp index 895922a1eb8..152432ca7be 100644 --- a/src/libs/qmljs/qmljstypedescriptionreader.cpp +++ b/src/libs/qmljs/qmljstypedescriptionreader.cpp @@ -649,39 +649,34 @@ void TypeDescriptionReader::readEnumValues(AST::UiScriptBinding *ast, LanguageUt return; } - ExpressionStatement *expStmt = AST::cast(ast->statement); + auto *expStmt = AST::cast(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(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(it->property); - if (assignement) { - StringLiteralPropertyName *propName = AST::cast(assignement->name); - NumericLiteral *value = AST::cast(assignement->initializer); - UnaryMinusExpression *minus = AST::cast(assignement->initializer); - if (minus) - value = AST::cast(minus->expression); - if (!propName || !value) { - addError(objectLit->firstSourceLocation(), tr("Expected object literal to contain only 'string: number' elements.")); - continue; + if (auto *objectLit = AST::cast(expStmt->expression)) { + for (PatternPropertyList *it = objectLit->properties; it; it = it->next) { + if (PatternProperty *assignement = it->property) { + if (auto *name = AST::cast(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(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(expStmt->expression)) { + for (PatternElementList *it = arrayLit->elements; it; it = it->next) { + if (PatternElement *element = it->element) { + if (auto *name = AST::cast(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.")); } }