diff --git a/.clang-format b/.clang-format index 97f7f2b2347..e1dad0fa0c0 100644 --- a/.clang-format +++ b/.clang-format @@ -1,19 +1,3 @@ -# .clang-format for Qt Creator -# -# This is for clang-format >= 5.0. -# -# The configuration below follows the Qt Creator Coding Rules [1] as closely as -# possible. For documentation of the options, see [2]. -# -# Use ../../tests/manual/clang-format-for-qtc/test.cpp for documenting problems -# or testing changes. -# -# In case you update this configuration please also update the qtcStyle() in src\plugins\clangformat\clangformatutils.cpp -# -# [1] https://doc-snapshots.qt.io/qtcreator-extending/coding-style.html -# [2] https://clang.llvm.org/docs/ClangFormatStyleOptions.html -# ---- Language: Cpp AccessModifierOffset: -4 AlignAfterOpenBracket: Align @@ -90,12 +74,12 @@ NamespaceIndentation: None ObjCBlockIndentWidth: 4 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true -PenaltyBreakAssignment: 150 -PenaltyBreakBeforeFirstCallParameter: 300 +PenaltyBreakAssignment: 500 +PenaltyBreakBeforeFirstCallParameter: 150 PenaltyBreakComment: 500 PenaltyBreakFirstLessLess: 400 PenaltyBreakString: 600 -PenaltyExcessCharacter: 50 +PenaltyExcessCharacter: 7 PenaltyReturnTypeOnItsOwnLine: 300 PointerAlignment: Right ReflowComments: false diff --git a/src/libs/clangsupport/filesystem.cpp b/src/libs/clangsupport/filesystem.cpp index db79ddda312..e6c0ed34b70 100644 --- a/src/libs/clangsupport/filesystem.cpp +++ b/src/libs/clangsupport/filesystem.cpp @@ -61,4 +61,10 @@ long long FileSystem::lastModified(FilePathId filePathId) const return 0; } +void FileSystem::remove(const FilePathIds &filePathIds) +{ + for (FilePathId filePathId : filePathIds) + QFile::remove(QString{m_filePathCache.filePath(filePathId)}); +} + } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/filesystem.h b/src/libs/clangsupport/filesystem.h index ecf332de55c..827fdcfd0e8 100644 --- a/src/libs/clangsupport/filesystem.h +++ b/src/libs/clangsupport/filesystem.h @@ -41,6 +41,8 @@ public: FilePathIds directoryEntries(const QString &directoryPath) const override; long long lastModified(FilePathId filePathId) const override; + void remove(const FilePathIds &filePathIds) override; + private: FilePathCachingInterface &m_filePathCache; }; diff --git a/src/libs/clangsupport/filesysteminterface.h b/src/libs/clangsupport/filesysteminterface.h index 9d64d2a93a1..68be461657b 100644 --- a/src/libs/clangsupport/filesysteminterface.h +++ b/src/libs/clangsupport/filesysteminterface.h @@ -36,6 +36,7 @@ class FileSystemInterface public: virtual FilePathIds directoryEntries(const QString &directoryPath) const = 0; virtual long long lastModified(FilePathId filePathId) const = 0; + virtual void remove(const FilePathIds &filePathIds) = 0; protected: ~FileSystemInterface() = default; diff --git a/src/plugins/clangcodemodel/clangcodemodelunittestfiles.pri b/src/plugins/clangcodemodel/clangcodemodelunittestfiles.pri index a7ef679399d..07e55b9354b 100644 --- a/src/plugins/clangcodemodel/clangcodemodelunittestfiles.pri +++ b/src/plugins/clangcodemodel/clangcodemodelunittestfiles.pri @@ -1,22 +1,28 @@ INCLUDEPATH += $$PWD SOURCES += \ - $$PWD/clangactivationsequencecontextprocessor.cpp \ - $$PWD/clangactivationsequenceprocessor.cpp \ $$PWD/clangcompletionchunkstotextconverter.cpp \ - $$PWD/clangcompletioncontextanalyzer.cpp \ $$PWD/clangdiagnosticfilter.cpp \ $$PWD/clangfixitoperation.cpp \ $$PWD/clanghighlightingresultreporter.cpp \ $$PWD/clanguiheaderondiskmanager.cpp +!isEmpty(QTC_UNITTEST_BUILD_CPP_PARSER):SOURCES+= \ + $$PWD/clangactivationsequenceprocessor.cpp \ + $$PWD/clangactivationsequencecontextprocessor.cpp + HEADERS += \ - $$PWD/clangactivationsequencecontextprocessor.h \ - $$PWD/clangactivationsequenceprocessor.h \ $$PWD/clangcompletionchunkstotextconverter.h \ - $$PWD/clangcompletioncontextanalyzer.h \ $$PWD/clangdiagnosticfilter.h \ $$PWD/clangfixitoperation.h \ $$PWD/clanghighlightingresultreporter.h \ $$PWD/clangisdiagnosticrelatedtolocation.h \ $$PWD/clanguiheaderondiskmanager.h + +!isEmpty(QTC_UNITTEST_BUILD_CPP_PARSER):SOURCES+= \ + $$PWD/clangactivationsequencecontextprocessor.h \ + $$PWD/clangactivationsequenceprocessor.h \ + $$PWD/clangcompletioncontextanalyzer.cpp \ + $$PWD/clangcompletioncontextanalyzer.h + + diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index fba34728174..a7066e255bd 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -386,6 +386,13 @@ extend_qtc_plugin(QmlDesigner texteditorwidget.cpp texteditorwidget.h ) +extend_qtc_plugin(QmlDesigner + SOURCES_PREFIX components/listmodeleditor + SOURCES + listmodeleditordialog.cpp listmodeleditordialog.h + listmodeleditordialog.cpp listmodeleditormodel.h +) + extend_qtc_plugin(QmlDesigner SOURCES_PREFIX designercore SOURCES diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp index 53ec46697ee..3fbc85fd559 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp @@ -35,6 +35,7 @@ #include "utils/fileutils.h" #include "utils/outputformatter.h" +#include #include #include #include @@ -86,6 +87,15 @@ AssetExportDialog::AssetExportDialog(const Utils::FilePath &exportPath, Core::FileUtils::showInGraphicalShell(Core::ICore::mainWindow(), m_ui->exportPath->path()); }); + auto optionsWidget = new QWidget; + m_ui->advancedOptions->setSummaryText(tr("Advanced Options")); + m_ui->advancedOptions->setWidget(optionsWidget); + auto optionsLayout = new QHBoxLayout(optionsWidget); + optionsLayout->setMargin(8); + m_exportAssetsCheck = new QCheckBox(tr("Export assets"), this); + m_exportAssetsCheck->setChecked(true); + optionsLayout->addWidget(m_exportAssetsCheck); + m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); m_ui->stackedWidget->addWidget(m_filesView); @@ -97,6 +107,7 @@ AssetExportDialog::AssetExportDialog(const Utils::FilePath &exportPath, switchView(false); connect(m_ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, [this]() { + m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); m_assetExporter.cancel(); }); @@ -137,7 +148,8 @@ void AssetExportDialog::onExport() TaskHub::clearTasks(Constants::TASK_CATEGORY_ASSET_EXPORT); m_exportLogs->clear(); - m_assetExporter.exportQml(m_filePathModel.files(), m_ui->exportPath->fileName()); + m_assetExporter.exportQml(m_filePathModel.files(), m_ui->exportPath->fileName(), + m_exportAssetsCheck->isChecked()); } void AssetExportDialog::onExportStateChanged(AssetExporter::ParsingState newState) diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h index 7bf68b6a748..e452fff6bcb 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h @@ -34,6 +34,7 @@ QT_BEGIN_NAMESPACE class QPushButton; +class QCheckBox; class QListView; class QPlainTextEdit; QT_END_NAMESPACE @@ -74,6 +75,7 @@ private: FilePathModel &m_filePathModel; std::unique_ptr m_ui; QPushButton *m_exportBtn = nullptr; + QCheckBox *m_exportAssetsCheck = nullptr; QListView *m_filesView = nullptr; QPlainTextEdit *m_exportLogs = nullptr; Utils::OutputFormatter *m_outputFormatter = nullptr; diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui index 38c2152098c..a4e7ec91d9c 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui @@ -14,9 +14,6 @@ Export QML - - - @@ -30,10 +27,10 @@ - + - + 1000 @@ -43,13 +40,26 @@ - + + + + + 0 + 8 + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Close + + + @@ -59,6 +69,12 @@
utils/pathchooser.h
1 + + Utils::DetailsWidget + QWidget +
utils/detailswidget.h
+ 1 +
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp index 26e2d2c455b..1ea1e09e923 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp @@ -25,7 +25,6 @@ #include "assetexporter.h" #include "componentexporter.h" #include "exportnotification.h" -#include "assetexportpluginconstants.h" #include "rewriterview.h" #include "qmlitemnode.h" @@ -117,21 +116,26 @@ void AssetExporter::exportQml(const Utils::FilePaths &qmlFiles, const Utils::Fil ExportNotification::addInfo(tr("Exporting metadata at %1. Export assets: ") .arg(exportPath.toUserOutput()) .arg(exportAssets? tr("Yes") : tr("No"))); - // TODO Asset export notifyProgress(0.0); - Q_UNUSED(exportAssets); m_exportFiles = qmlFiles; + m_totalFileCount = m_exportFiles.count(); m_components = QJsonArray(); m_exportPath = exportPath; m_currentState.change(ParsingState::Parsing); triggerLoadNextFile(); - m_assetDumper = make_unique(); + if (exportAssets) + m_assetDumper = make_unique(); + else + m_assetDumper.reset(); } void AssetExporter::cancel() { - // TODO Cancel export - m_assetDumper.reset(); + if (!m_cancelled) { + ExportNotification::addInfo(tr("Cancelling export.")); + m_assetDumper.reset(); + m_cancelled = true; + } } bool AssetExporter::isBusy() const @@ -141,13 +145,13 @@ bool AssetExporter::isBusy() const m_currentState == AssetExporter::ParsingState::WritingJson; } -Utils::FilePath AssetExporter::exportAsset(const QmlObjectNode &node) +Utils::FilePath AssetExporter::exportAsset(const QmlObjectNode &node, const QString &uuid) { - // TODO: Use this hash as UUID and add to the node. - QByteArray hash = addNodeUUID(node.modelNode()); - Utils::FilePath assetPath = m_exportPath.pathAppended(QString("assets/%1.png") - .arg(QString::fromLatin1(hash))); - m_assetDumper->dumpAsset(node.toQmlItemNode().instanceRenderPixmap(), assetPath); + if (m_cancelled) + return {}; + Utils::FilePath assetPath = m_exportPath.pathAppended(QString("assets/%1.png").arg(uuid)); + if (m_assetDumper) + m_assetDumper->dumpAsset(node.toQmlItemNode().instanceRenderPixmap(), assetPath); return assetPath; } @@ -157,6 +161,7 @@ void AssetExporter::exportComponent(const ModelNode &rootNode) Component exporter(*this, rootNode); exporter.exportComponent(); m_components.append(exporter.json()); + notifyProgress((m_totalFileCount - m_exportFiles.count()) * 0.8 / m_totalFileCount); } void AssetExporter::notifyLoadError(AssetExporterView::LoadState state) @@ -194,19 +199,13 @@ void AssetExporter::onQmlFileLoaded() triggerLoadNextFile(); } -QByteArray AssetExporter::addNodeUUID(ModelNode node) +QByteArray AssetExporter::generateUuid(const ModelNode &node) { - QByteArray uuid = node.auxiliaryData(Constants::UuidTag).toByteArray(); - qDebug() << node.id() << "UUID" << uuid; - if (uuid.isEmpty()) { - // Assign a new hash. - do { - uuid = generateHash(node.id()); - } while (m_usedHashes.contains(uuid)); - m_usedHashes.insert(uuid); - node.setAuxiliaryData(Constants::UuidAuxTag, QString::fromLatin1(uuid)); - node.model()->rewriterView()->writeAuxiliaryData(); - } + QByteArray uuid; + do { + uuid = generateHash(node.id()); + } while (m_usedHashes.contains(uuid)); + m_usedHashes.insert(uuid); return uuid; } @@ -217,7 +216,7 @@ void AssetExporter::triggerLoadNextFile() void AssetExporter::loadNextFile() { - if (m_exportFiles.isEmpty()) { + if (m_cancelled || m_exportFiles.isEmpty()) { notifyProgress(0.8); m_currentState.change(ParsingState::ParsingFinished); writeMetadata(); @@ -233,6 +232,13 @@ void AssetExporter::loadNextFile() void AssetExporter::writeMetadata() const { + if (m_cancelled) { + notifyProgress(1.0); + ExportNotification::addInfo(tr("Export cancelled.")); + m_currentState.change(ParsingState::ExportingDone); + return; + } + Utils::FilePath metadataPath = m_exportPath.pathAppended(m_exportPath.fileName() + ".metadata"); ExportNotification::addInfo(tr("Writing metadata to file %1."). arg(metadataPath.toUserOutput())); @@ -253,7 +259,8 @@ void AssetExporter::writeMetadata() const } notifyProgress(1.0); ExportNotification::addInfo(tr("Export finished.")); - m_assetDumper->quitDumper(); + if (m_assetDumper) + m_assetDumper->quitDumper(); m_currentState.change(ParsingState::ExportingDone); } diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h index 2aa238fb327..01b125167be 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h @@ -70,7 +70,8 @@ public: void cancel(); bool isBusy() const; - Utils::FilePath exportAsset(const QmlObjectNode& node); + Utils::FilePath exportAsset(const QmlObjectNode& node, const QString &uuid); + QByteArray generateUuid(const ModelNode &node); signals: void stateChanged(ParsingState); @@ -87,8 +88,6 @@ private: void onQmlFileLoaded(); - QByteArray addNodeUUID(ModelNode node); - private: mutable class State { public: @@ -101,10 +100,12 @@ private: ProjectExplorer::Project *m_project = nullptr; AssetExporterView *m_view = nullptr; Utils::FilePaths m_exportFiles; + unsigned int m_totalFileCount = 0; Utils::FilePath m_exportPath; QJsonArray m_components; QSet m_usedHashes; std::unique_ptr m_assetDumper; + bool m_cancelled = false; }; QDebug operator<< (QDebug os, const QmlDesigner::AssetExporter::ParsingState& s); diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h index 1937c7126eb..a1c0e2181ca 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h @@ -61,6 +61,7 @@ const char AssetDataTag[] = "assetData"; const char AssetPathTag[] = "assetPath"; const char AssetBoundsTag[] = "assetBounds"; const char OpacityTag[] = "opacity"; +const char TypeNameTag[] = "qmlType"; const char TextDetailsTag[] = "textDetails"; const char FontFamilyTag[] = "fontFamily"; diff --git a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp index 819fa3d328f..059b6ecb161 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp @@ -23,6 +23,9 @@ ** ****************************************************************************/ #include "componentexporter.h" +#include "assetexporter.h" +#include "assetexportpluginconstants.h" +#include "exportnotification.h" #include "parsers/modelnodeparser.h" #include "model.h" @@ -73,6 +76,7 @@ void Component::exportComponent() { QTC_ASSERT(m_rootNode.isValid(), return); m_json = nodeToJson(m_rootNode); + addImports(); } ModelNodeParser *Component::createNodeParser(const ModelNode &node) const @@ -102,8 +106,18 @@ QJsonObject Component::nodeToJson(const ModelNode &node) { QJsonObject jsonObject; std::unique_ptr parser(createNodeParser(node)); - if (parser) + if (parser) { + if (parser->uuid().isEmpty()) { + // Assign an unique identifier to the node. + QByteArray uuid = m_exporter.generateUuid(node); + node.setAuxiliaryData(Constants::UuidAuxTag, QString::fromLatin1(uuid)); + node.model()->rewriterView()->writeAuxiliaryData(); + } jsonObject = parser->json(*this); + } else { + ExportNotification::addError(tr("Error exporting component %1. Parser unavailable.") + .arg(node.id())); + } QJsonArray children; for (const ModelNode &childnode : node.directSubModelNodes()) @@ -112,7 +126,17 @@ QJsonObject Component::nodeToJson(const ModelNode &node) if (!children.isEmpty()) jsonObject.insert("children", children); - return jsonObject; + return jsonObject; +} + +void Component::addImports() +{ + QJsonArray importsArray; + for (const Import &import : m_rootNode.model()->imports()) + importsArray.append(import.toString()); + + if (!importsArray.empty()) + m_json.insert(Constants::ImportsTag, importsArray); } diff --git a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.h b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.h index e2017b785c2..5e29e10bf0d 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.h +++ b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.h @@ -24,8 +24,9 @@ ****************************************************************************/ #pragma once -#include #include +#include +#include #include #include @@ -68,6 +69,8 @@ protected: class Component { + Q_DECLARE_TR_FUNCTIONS(Component); + public: Component(AssetExporter& exporter, const ModelNode &rootNode); @@ -84,6 +87,7 @@ public: private: ModelNodeParser* createNodeParser(const ModelNode &node) const; QJsonObject nodeToJson(const ModelNode &node); + void addImports(); private: AssetExporter& m_exporter; diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp index 159eccec460..a42b7300624 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp @@ -55,8 +55,7 @@ QJsonObject AssetNodeParser::json(Component &component) const QJsonObject jsonObject = ItemNodeParser::json(component); QPixmap asset = objectNode().toQmlItemNode().instanceRenderPixmap(); - Utils::FilePath assetPath = component.exporter().exportAsset(objectNode()); - + Utils::FilePath assetPath = component.exporter().exportAsset(objectNode(), uuid()); QJsonObject assetData; assetData.insert(AssetPathTag, assetPath.toString()); jsonObject.insert(AssetDataTag, assetData); diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp index 355983f2215..5104732e1c0 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp @@ -60,6 +60,10 @@ QJsonObject QmlDesigner::ItemNodeParser::json(QmlDesigner::Component &component) jsonObject.insert(WidthTag, size.width()); jsonObject.insert(HeightTag, size.height()); + jsonObject.insert(UuidTag, uuid()); + jsonObject.insert(ExportTypeTag, "child"); + jsonObject.insert(TypeNameTag, QString::fromLatin1(m_node.type())); + return jsonObject; } } diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp index 31787b83cc3..d9cb9b61781 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp @@ -23,6 +23,7 @@ ** ****************************************************************************/ #include "modelnodeparser.h" +#include "assetexportpluginconstants.h" namespace QmlDesigner { ModelNodeParser::ModelNodeParser(const QByteArrayList &lineage, const ModelNode &node) : @@ -38,4 +39,9 @@ QVariant ModelNodeParser::propertyValue(const PropertyName &name) const return m_objectNode.instanceValue(name); } +QString ModelNodeParser::uuid() const +{ + return m_node.auxiliaryData(Constants::UuidAuxTag).toString(); +} + } diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h index 4ca17746e8d..c91b2eae022 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h @@ -47,6 +47,7 @@ public: const QByteArrayList& lineage() const { return m_lineage; } const QmlObjectNode& objectNode() const { return m_objectNode; } QVariant propertyValue(const PropertyName &name) const; + QString uuid() const; protected: const ModelNode &m_node; diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index 2fa19a5c243..a8db25f4176 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -181,6 +181,9 @@ const char addFlowActionToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", const char fitRootToScreenToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fit the root element inside the available space."); const char fitSelectionToScreenToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fit the selected elements inside the available space."); +const char editListModelDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", + "Edit List Model..."); + const int priorityFirst = 280; const int prioritySelectionCategory = 220; const int priorityQmlPreviewCategory = 200; diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 479f40203cb..fb7d4ce2029 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -41,10 +41,14 @@ #include #include +#include +#include + #include #include #include +#include #include #include #include @@ -335,6 +339,64 @@ public: } }; +class EditListModelAction final : public ModelNodeContextMenuAction +{ +public: + EditListModelAction() + : ModelNodeContextMenuAction("EditListModel", + ComponentCoreConstants::editListModelDisplayName, + {}, + ComponentCoreConstants::rootCategory, + QKeySequence("Alt+e"), + 1001, + &openDialog, + &isListViewInBaseState, + &isListViewInBaseState) + {} + + static bool isListViewInBaseState(const SelectionContext &selectionState) + { + return selectionState.isInBaseState() && selectionState.singleNodeIsSelected() + && selectionState.currentSingleSelectedNode().metaInfo().isSubclassOf( + "QtQuick.ListView"); + } + + bool isEnabled(const SelectionContext &) const override { return true; } + + static ModelNode listModelNode(const ModelNode &listViewNode) + { + if (listViewNode.hasProperty("model")) { + if (listViewNode.hasBindingProperty("model")) + return listViewNode.bindingProperty("model").resolveToModelNode(); + else if (listViewNode.hasNodeProperty("model")) + return listViewNode.nodeProperty("model").modelNode(); + } + + ModelNode newModel = listViewNode.view()->createModelNode("QtQml.Models.ListModel", 2, 15); + listViewNode.nodeProperty("mode").reparentHere(newModel); + + return newModel; + } + + static void openDialog(const SelectionContext &selectionState) + { + ListModelEditorModel model; + + ModelNode targetNode = selectionState.targetNode(); + if (!targetNode.isValid()) + targetNode = selectionState.currentSingleSelectedNode(); + if (!targetNode.isValid()) + return; + + model.setListModel(listModelNode(targetNode)); + + ListModelEditorDialog dialog{Core::ICore::mainWindow()}; + dialog.setModel(&model); + + dialog.exec(); + } +}; + bool flowOptionVisible(const SelectionContext &context) { return QmlFlowViewNode::isValidQmlFlowViewNode(context.rootNode()); @@ -1217,6 +1279,8 @@ void DesignerActionManager::createDefaultDesignerActions() priorityGenericToolBar)); addDesignerAction(new ChangeStyleAction()); + + addDesignerAction(new EditListModelAction); } void DesignerActionManager::createDefaultAddResourceHandler() diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditor.pri b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditor.pri new file mode 100644 index 00000000000..cd6938aab4b --- /dev/null +++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditor.pri @@ -0,0 +1,7 @@ +SOURCES += \ + $$PWD/listmodeleditordialog.cpp \ + $$PWD/listmodeleditormodel.cpp + +HEADERS += \ + $$PWD/listmodeleditordialog.h \ + $$PWD/listmodeleditormodel.h diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.cpp b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.cpp new file mode 100644 index 00000000000..283acab78bb --- /dev/null +++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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 "listmodeleditordialog.h" +#include "listmodeleditormodel.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace QmlDesigner { + +namespace { +QIcon getIcon(Theme::Icon icon) +{ + const QString fontName = "qtds_propertyIconFont.ttf"; + + return Utils::StyleHelper::getIconFromIconFont(fontName, Theme::getIconUnicode(icon), 30, 30); +} +} // namespace + +ListModelEditorDialog::ListModelEditorDialog(QWidget *parent) + : QDialog(parent) +{ + resize((Core::ICore::mainWindow()->size() * 8) / 10); + + QVBoxLayout *mainLayout = new QVBoxLayout(this); + + QToolBar *toolBar = new QToolBar(); + toolBar->setIconSize({30, 30}); + mainLayout->addWidget(toolBar); + m_tableView = new QTableView; + mainLayout->addWidget(m_tableView); + + m_addRowAction = toolBar->addAction(getIcon(Theme::Icon::addRowAfter), tr("Add Row")); + m_removeRowsAction = toolBar->addAction(getIcon(Theme::Icon::deleteRow), tr("Remove Columns")); + m_addColumnAction = toolBar->addAction(getIcon(Theme::Icon::addColumnAfter), tr("Add Column")); + m_removeColumnsAction = toolBar->addAction(getIcon(Theme::Icon::deleteColumn), + tr("Remove Columns")); +} + +ListModelEditorDialog::~ListModelEditorDialog() = default; + +void ListModelEditorDialog::setModel(ListModelEditorModel *model) +{ + m_model = model; + + connect(m_addRowAction, &QAction::triggered, m_model, &ListModelEditorModel::addRow); + connect(m_addColumnAction, &QAction::triggered, this, &ListModelEditorDialog::openColumnDialog); + connect(m_removeRowsAction, &QAction::triggered, this, &ListModelEditorDialog::removeRows); + connect(m_removeColumnsAction, &QAction::triggered, this, &ListModelEditorDialog::removeColumns); + connect(m_tableView->horizontalHeader(), + &QHeaderView::sectionDoubleClicked, + this, + &ListModelEditorDialog::changeHeader); + + m_tableView->setModel(model); + m_tableView->horizontalHeader()->setMinimumSectionSize(60); + m_tableView->verticalHeader()->setMinimumSectionSize(25); + m_tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + m_tableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); +} + +void ListModelEditorDialog::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete) { + for (const QModelIndex index : m_tableView->selectionModel()->selectedIndexes()) + m_model->setData(index, QVariant(), Qt::EditRole); + } +} + +void ListModelEditorDialog::openColumnDialog() +{ + bool ok; + QString columnName = QInputDialog::getText( + this, tr("Add Property"), tr("Property Name:"), QLineEdit::Normal, "", &ok); + if (ok && !columnName.isEmpty()) + m_model->addColumn(columnName); +} + +void ListModelEditorDialog::removeRows() +{ + const QList indices = m_tableView->selectionModel()->selectedRows(); + std::vector rows; + rows.reserve(indices.size()); + + for (QModelIndex index : indices) + rows.push_back(index.row()); + + std::sort(rows.begin(), rows.end()); + + rows.erase(std::unique(rows.begin(), rows.end()), rows.end()); + + std::reverse(rows.begin(), rows.end()); + + for (int row : rows) + m_model->removeRow(row); +} + +void ListModelEditorDialog::removeColumns() +{ + const QList indices = m_tableView->selectionModel()->selectedColumns(); + std::vector columns; + columns.reserve(indices.size()); + + for (QModelIndex index : indices) + columns.push_back(index.column()); + + std::sort(columns.begin(), columns.end()); + + columns.erase(std::unique(columns.begin(), columns.end()), columns.end()); + + std::reverse(columns.begin(), columns.end()); + + for (int row : columns) + m_model->removeColumn(row); +} + +void ListModelEditorDialog::changeHeader(int column) +{ + const QString propertyName = QString::fromUtf8(m_model->propertyNames()[column]); + + bool ok; + QString newPropertyName = QInputDialog::getText( + this, tr("Change Propertry"), tr("Column Name:"), QLineEdit::Normal, propertyName, &ok); + + if (ok && !newPropertyName.isEmpty()) + m_model->renameColumn(column, newPropertyName); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.h b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.h new file mode 100644 index 00000000000..519d0869fae --- /dev/null +++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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 + +QT_BEGIN_NAMESPACE +class QAbstractItemModel; +class QTableView; +QT_END_NAMESPACE + +namespace Ui { +class ListModelEditorDialog; +} + +namespace QmlDesigner { + +class ListModelEditorModel; + +class ListModelEditorDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ListModelEditorDialog(QWidget *parent = nullptr); + ~ListModelEditorDialog(); + + void setModel(ListModelEditorModel *model); + +protected: + void keyPressEvent(QKeyEvent *) override; + +private: + void addRow(); + void openColumnDialog(); + void removeRows(); + void removeColumns(); + void changeHeader(int column); + +private: + ListModelEditorModel *m_model{}; + QAction *m_addRowAction{}; + QAction *m_removeRowsAction{}; + QAction *m_addColumnAction{}; + QAction *m_removeColumnsAction{}; + QTableView *m_tableView{}; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp new file mode 100644 index 00000000000..98722c3e8fb --- /dev/null +++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp @@ -0,0 +1,302 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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 "listmodeleditormodel.h" + +#include +#include +#include + +#include + +#include +#include +#include + +namespace QmlDesigner { + +class ListModelItem : public QStandardItem +{ +public: + ListModelItem(ModelNode node, PropertyName propertyName) + : node(std::move(node)) + , propertyName(propertyName) + { + setEditable(true); + } + + QVariant maybeConvertToNumber(const QVariant &value) + { + bool canConvert = false; + double convertedValue = value.toDouble(&canConvert); + if (canConvert) { + return convertedValue; + } + + return value; + } + + QVariant data(int role) const override + { + if (role == Qt::BackgroundColorRole && hasInvalidValue) + return QColor{Qt::darkYellow}; + + return QStandardItem::data(role); + } + + void setData(const QVariant &value, int role) override + { + if (role == Qt::DisplayRole || role == Qt::EditRole) + hasInvalidValue = !value.isValid(); + + if (role == Qt::EditRole) { + const QVariant &convertedValue = maybeConvertToNumber(value); + QStandardItem::setData(convertedValue, role); + if (value.isValid()) + node.variantProperty(propertyName).setValue(convertedValue); + else + node.removeProperty(propertyName); + } else { + QStandardItem::setData(value, role); + } + } + + void removeProperty() { node.removeProperty(propertyName); } + + void renameProperty(const PropertyName &newPropertyName) + { + if (node.hasProperty(propertyName)) { + node.removeProperty(propertyName); + node.variantProperty(newPropertyName).setValue(data(Qt::EditRole)); + } + propertyName = newPropertyName; + } + +public: + ModelNode node; + PropertyName propertyName; + bool hasInvalidValue = false; +}; + +namespace { +QList getPropertyNames(const ModelNode &listElementNode) +{ + auto properties = listElementNode.variantProperties(); + + QList names; + names.reserve(properties.size()); + + for (const auto &property : properties) + names.push_back(property.name()); + + std::sort(names.begin(), names.end()); + + return names; +} + +QList mergeProperyNames(const QList &first, + const QList &second) +{ + QList merged; + merged.reserve(first.size() + second.size()); + + std::set_union(first.begin(), + first.end(), + second.begin(), + second.end(), + std::back_inserter(merged)); + + return merged; +} + +std::unique_ptr createItem(const ModelNode &listElementNode, + const PropertyName &propertyName) +{ + auto item = std::make_unique(listElementNode, propertyName); + + QVariant value = listElementNode.variantProperty(propertyName).value(); + + item->setData(value, Qt::DisplayRole); + + return item; +} + +QList convertToStringList(const QList &propertyNames) +{ + QList names; + names.reserve(propertyNames.size()); + + for (const auto &propertyName : propertyNames) + names.push_back(QString::fromUtf8(propertyName)); + + return names; +} + +QList createProperyNames(const QList &listElementNodes) +{ + QList propertyNames; + propertyNames.reserve(10); + + for (const ModelNode &listElementNode : listElementNodes) + propertyNames = mergeProperyNames(getPropertyNames(listElementNode), propertyNames); + + return propertyNames; +} + +QList createColumnItems(const ModelNode &listModelNode, + const PropertyName &propertyName) +{ + QList items; + const auto listElementNodes = listModelNode.defaultNodeListProperty().toModelNodeList(); + + for (const ModelNode &listElementNode : listElementNodes) + items.push_back(createItem(listElementNode, propertyName).release()); + + return items; +} + +void renameProperties(const QStandardItemModel *model, + int columnIndex, + const PropertyName &newPropertyName) +{ + for (int rowIndex = 0; rowIndex < model->rowCount(); ++rowIndex) + static_cast(model->item(rowIndex, columnIndex))->renameProperty(newPropertyName); +} + +} // namespace + +void ListModelEditorModel::populateModel() +{ + const auto listElementNodes = m_listModelNode.defaultNodeListProperty().toModelNodeList(); + + m_propertyNames = createProperyNames(listElementNodes); + + setHorizontalHeaderLabels(convertToStringList(m_propertyNames)); + + createItems(listElementNodes); +} + +void ListModelEditorModel::createItems(const QList &listElementNodes) +{ + for (const ModelNode &listElementNode : listElementNodes) + appendItems(listElementNode); +} + +void ListModelEditorModel::appendItems(const ModelNode &listElementNode) +{ + QList row; + row.reserve(m_propertyNames.size()); + for (const PropertyName &propertyName : propertyNames()) + row.push_back(createItem(listElementNode, propertyName).release()); + + appendRow(row); +} + +void ListModelEditorModel::addRow() +{ + auto newElement = m_listModelNode.view()->createModelNode("QtQml.Models.ListElement", 2, 15); + m_listModelNode.defaultNodeListProperty().reparentHere(newElement); + + appendItems(newElement); +} + +void ListModelEditorModel::addColumn(const QString &columnName) +{ + PropertyName propertyName = columnName.toUtf8(); + + auto found = std::lower_bound(m_propertyNames.begin(), m_propertyNames.end(), propertyName); + + if (found != m_propertyNames.end() && *found == columnName) + return; + + int newIndex = static_cast(std::distance(m_propertyNames.begin(), found)); + + m_propertyNames.insert(found, propertyName); + + insertColumn(newIndex, createColumnItems(m_listModelNode, propertyName)); + + setHorizontalHeaderItem(newIndex, new QStandardItem(columnName)); +} + +bool ListModelEditorModel::setValue(int row, int column, QVariant value, Qt::ItemDataRole role) +{ + QModelIndex index = createIndex(row, column, invisibleRootItem()); + bool success = setData(index, value, role); + emit dataChanged(index, index); + + return success; +} + +void ListModelEditorModel::removeColumn(int column) +{ + QList columnItems = QStandardItemModel::takeColumn(column); + m_propertyNames.removeAt(column); + + for (QStandardItem *columnItem : columnItems) { + static_cast(columnItem)->removeProperty(); + delete columnItem; + } +} + +void ListModelEditorModel::removeRow(int row) +{ + QList rowItems = QStandardItemModel::takeRow(row); + + if (rowItems.size()) + static_cast(rowItems.front())->node.destroy(); + + qDeleteAll(rowItems); +} + +void ListModelEditorModel::renameColumn(int oldColumn, const QString &newColumnName) +{ + const PropertyName newPropertyName = newColumnName.toUtf8(); + + auto found = std::lower_bound(m_propertyNames.begin(), m_propertyNames.end(), newPropertyName); + + if (found != m_propertyNames.end() && *found == newPropertyName) + return; + + int newColumn = static_cast(std::distance(m_propertyNames.begin(), found)); + + if (oldColumn == newColumn) { + *found = newPropertyName; + renameProperties(this, newColumn, newPropertyName); + } else if (newColumn < oldColumn) { + m_propertyNames.insert(found, newPropertyName); + m_propertyNames.erase(std::next(m_propertyNames.begin(), oldColumn + 1)); + insertColumn(newColumn, takeColumn(oldColumn)); + renameProperties(this, newColumn, newPropertyName); + } else { + m_propertyNames.insert(found, newPropertyName); + m_propertyNames.erase(std::next(m_propertyNames.begin(), oldColumn)); + insertColumn(newColumn - 1, takeColumn(oldColumn)); + renameProperties(this, newColumn - 1, newPropertyName); + } + + setHorizontalHeaderLabels(convertToStringList(m_propertyNames)); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h new file mode 100644 index 00000000000..35d41bee68b --- /dev/null +++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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 + +namespace QmlDesigner { + +class ListModelEditorModel : public QStandardItemModel +{ + +public: + void setListModel(ModelNode node) + { + m_listModelNode = node; + populateModel(); + } + + void addRow(); + void addColumn(const QString &columnName); + + const QList &propertyNames() const { return m_propertyNames; } + + bool setValue(int row, int column, QVariant value, Qt::ItemDataRole role = Qt::EditRole); + + void removeColumn(int column); + void removeRow(int row); + void renameColumn(int column, const QString &newColumnName); + +private: + void populateModel(); + void createItems(const QList &listElementNodes); + void appendItems(const ModelNode &listElementNode); + +private: + ModelNode m_listModelNode; + QList m_propertyNames; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/exceptions/exception.cpp b/src/plugins/qmldesigner/designercore/exceptions/exception.cpp index 486e5bec2bd..66106387481 100644 --- a/src/plugins/qmldesigner/designercore/exceptions/exception.cpp +++ b/src/plugins/qmldesigner/designercore/exceptions/exception.cpp @@ -34,8 +34,8 @@ #include -#include #ifndef QMLDESIGNER_TEST +#include #include #endif @@ -160,8 +160,12 @@ QString Exception::description() const */ void Exception::showException(const QString &title) const { - QString composedTitle = title.isEmpty() ? QCoreApplication::translate("QmlDesigner", "Error") : title; + Q_UNUSED(title) +#ifndef QMLDESIGNER_TEST + QString composedTitle = title.isEmpty() ? QCoreApplication::translate("QmlDesigner", "Error") + : title; Core::AsynchronousMessageBox::warning(composedTitle, description()); +#endif } /*! diff --git a/src/plugins/qmldesigner/designercore/exceptions/exceptions.pri b/src/plugins/qmldesigner/designercore/exceptions/exceptions.pri index 848d7808fc2..6fbafdee3c3 100644 --- a/src/plugins/qmldesigner/designercore/exceptions/exceptions.pri +++ b/src/plugins/qmldesigner/designercore/exceptions/exceptions.pri @@ -1,2 +1,14 @@ -SOURCES += $$PWD/exception.cpp -SOURCES += $$PWD/invalidnodeinstanceexception.cpp +SOURCES += $$PWD/exception.cpp \ + $$PWD/invalidargumentexception.cpp \ + $$PWD/invalididexception.cpp \ + $$PWD/invalidmetainfoexception.cpp \ + $$PWD/invalidmodelnodeexception.cpp \ + $$PWD/invalidmodelstateexception.cpp \ + $$PWD/invalidpropertyexception.cpp \ + $$PWD/invalidqmlsourceexception.cpp \ + $$PWD/invalidreparentingexception.cpp \ + $$PWD/invalidslideindexexception.cpp \ + $$PWD/notimplementedexception.cpp \ + $$PWD/removebasestateexception.cpp \ + $$PWD/rewritingexception.cpp + diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h index 6f3d5eb6dfb..3197e746ab5 100644 --- a/src/plugins/qmldesigner/designercore/include/modelnode.h +++ b/src/plugins/qmldesigner/designercore/include/modelnode.h @@ -91,9 +91,12 @@ public: ModelNode(const Internal::InternalNodePointer &internalNode, Model *model, const AbstractView *view); ModelNode(const ModelNode &modelNode, AbstractView *view); ModelNode(const ModelNode &other); + ModelNode(ModelNode &&other); ~ModelNode(); - ModelNode& operator=(const ModelNode &other); + ModelNode &operator=(const ModelNode &other); + ModelNode &operator=(ModelNode &&other); + TypeName type() const; QString simplifiedTypeName() const; QString displayName() const; @@ -226,6 +229,15 @@ public: bool isSubclassOf(const TypeName &typeName, int majorVersion = -1, int minorVersion = -1) const; QIcon typeIcon() const; + friend void swap(ModelNode &first, ModelNode &second) + { + using std::swap; + + swap(first.m_internalNode, second.m_internalNode); + swap(first.m_model, second.m_model); + swap(first.m_view, second.m_view); + } + private: // functions Internal::InternalNodePointer internalNode() const; diff --git a/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h b/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h index 0872c547c83..552095a8d6d 100644 --- a/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h +++ b/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h @@ -31,10 +31,13 @@ // Unnecessary since core isn't a dll any more. #if defined(DESIGNER_CORE_LIBRARY) -# define QMLDESIGNERCORE_EXPORT Q_DECL_EXPORT +#define QMLDESIGNERCORE_EXPORT Q_DECL_EXPORT +#elif defined(DESIGNER_STATIC_CORE_LIBRARY) +#define QMLDESIGNERCORE_EXPORT #else -# define QMLDESIGNERCORE_EXPORT Q_DECL_IMPORT +#define QMLDESIGNERCORE_EXPORT Q_DECL_IMPORT #endif + namespace QmlDesigner { using PropertyName = QByteArray; using PropertyNameList = QList; diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 03ac27a5b0c..3be64be127d 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -42,6 +42,7 @@ #include #include +#include #include namespace QmlDesigner { diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index b4c21564c72..eb2d2859974 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -93,7 +93,25 @@ ModelNode::ModelNode(const ModelNode &modelNode, AbstractView *view) m_model(modelNode.model()), m_view(view) { +} +ModelNode::ModelNode(ModelNode &&other) + : m_internalNode(std::move(other.m_internalNode)) + , m_model(std::move(other.m_model)) + , m_view(std::move(other.m_view)) +{ + other.m_model = {}; + other.m_view = {}; +} + +ModelNode &ModelNode::operator=(ModelNode &&other) +{ + ModelNode newNode; + + swap(other, newNode); + swap(*this, newNode); + + return *this; } /*! \brief contructs a invalid model node @@ -103,7 +121,6 @@ ModelNode::ModelNode(const ModelNode &modelNode, AbstractView *view) ModelNode::ModelNode(): m_internalNode(new InternalNode) { - } ModelNode::ModelNode(const ModelNode &other) = default; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.pro b/src/plugins/qmldesigner/qmldesignerplugin.pro index bc0da990504..506b3d743b8 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.pro +++ b/src/plugins/qmldesigner/qmldesignerplugin.pro @@ -33,7 +33,7 @@ include(components/bindingeditor/bindingeditor.pri) include(components/annotationeditor/annotationeditor.pri) include(components/richtexteditor/richtexteditor.pri) include(components/transitioneditor/transitioneditor.pri) - +include(components/listmodeleditor/listmodeleditor.pri) BUILD_PUPPET_IN_CREATOR_BINPATH = $$(BUILD_PUPPET_IN_CREATOR_BINPATH) !isEmpty(BUILD_PUPPET_IN_CREATOR_BINPATH) { diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 6ff406e8979..021cf82dccd 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -59,6 +59,7 @@ Project { "components/stateseditor", "components/texteditor", "components/timelineeditor", + "compenents/listmodeleditor", ]) Properties { @@ -845,6 +846,10 @@ Project { "timelineeditor/timelineview.h", "timelineeditor/timelinewidget.cpp", "timelineeditor/timelinewidget.h", + "listmodeleditor/listmodeleditordialog.cpp ", + "listmodeleditor/listmodeleditordialog.h ", + "listmodeleditor/listmodeleditormodel.cpp ", + "listmodeleditor/listmodeleditordialog.h ", "transitioneditor/transitioneditorview.cpp", "transitioneditor/transitioneditorview.h", "transitioneditor/transitioneditorwidget.cpp", diff --git a/src/plugins/qmldesigner/qmldesignerunittestfiles.pri b/src/plugins/qmldesigner/qmldesignerunittestfiles.pri new file mode 100644 index 00000000000..cd4d52e8d3a --- /dev/null +++ b/src/plugins/qmldesigner/qmldesignerunittestfiles.pri @@ -0,0 +1,59 @@ +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/designercore/include +INCLUDEPATH += $$PWD/designercore +INCLUDEPATH += $$PWD/../../../share/qtcreator/qml/qmlpuppet/interfaces +INCLUDEPATH += $$PWD/../../../share/qtcreator/qml/qmlpuppet/types + +DEFINES += QMLDESIGNER_TEST DESIGNER_STATIC_CORE_LIBRARY + +include($$PWD/designercore/exceptions/exceptions.pri) + +SOURCES += \ + $$PWD/designercore/model/model.cpp \ + $$PWD/designercore/model/modelnode.cpp \ + $$PWD/designercore/model/import.cpp \ + $$PWD/designercore/model/abstractproperty.cpp \ + $$PWD/designercore/model/abstractview.cpp \ + $$PWD/designercore/model/internalproperty.cpp \ + $$PWD/designercore/model/internalbindingproperty.cpp \ + $$PWD/designercore/model/internalnodeabstractproperty.cpp \ + $$PWD/designercore/model/internalnodelistproperty.cpp \ + $$PWD/designercore/model/internalnodeproperty.cpp \ + $$PWD/designercore/model/internalsignalhandlerproperty.cpp \ + $$PWD/designercore/model/internalnode.cpp \ + $$PWD/designercore/model/internalvariantproperty.cpp \ + $$PWD/designercore/model/bindingproperty.cpp \ + $$PWD/designercore/model/nodeabstractproperty.cpp \ + $$PWD/designercore/model/nodelistproperty.cpp \ + $$PWD/designercore/model/nodeproperty.cpp \ + $$PWD/designercore/model/signalhandlerproperty.cpp \ + $$PWD/designercore/model/variantproperty.cpp\ + $$PWD/designercore/model/annotation.cpp \ + $$PWD/designercore/rewritertransaction.cpp \ + $$PWD/components/listmodeleditor/listmodeleditormodel.cpp + +HEADERS += \ + $$PWD/designercore/include/modelnode.h \ + $$PWD/designercore/include/model.h \ + $$PWD/../../../share/qtcreator/qml/qmlpuppet/interfaces/commondefines.h \ + $$PWD/designercore/include/import.h \ + $$PWD/designercore/include/abstractproperty.h \ + $$PWD/designercore/include/abstractview.h \ + $$PWD/designercore/model/model_p.h \ + $$PWD/designercore/include/qmldesignercorelib_global.h \ + $$PWD/designercore/model/internalbindingproperty.h \ + $$PWD/designercore/model/internalnode_p.h \ + $$PWD/designercore/model/internalnodeabstractproperty.h \ + $$PWD/designercore/model/internalnodelistproperty.h \ + $$PWD/designercore/model/internalnodeproperty.h \ + $$PWD/designercore/model/internalproperty.h \ + $$PWD/designercore/model/internalsignalhandlerproperty.h \ + $$PWD/designercore/model/internalvariantproperty.h \ + $$PWD/designercore/include/bindingproperty.h \ + $$PWD/designercore/include/nodeabstractproperty.h \ + $$PWD/designercore/include/nodelistproperty.h \ + $$PWD/designercore/include/nodeproperty.h \ + $$PWD/designercore/include/signalhandlerproperty.h \ + $$PWD/designercore/include/variantproperty.h \ + $$PWD/designercore/rewritertransaction.h \ + $$PWD/components/listmodeleditor/listmodeleditormodel.h diff --git a/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp b/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp index e91ca0159be..c7fe8e41384 100644 --- a/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp +++ b/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp @@ -213,7 +213,9 @@ struct Data // because we have a cycle dependency pchCreationProgressCounter, preCompiledHeaderStorage, database, - environment}; + environment, + fileSystem, + filePathCache}; ClangBackEnd::PchTasksMerger pchTaskMerger{pchTaskQueue}; ClangBackEnd::BuildDependenciesStorage<> buildDependencyStorage{database}; ClangBackEnd::BuildDependencyCollector buildDependencyCollector{filePathCache, diff --git a/src/tools/clangpchmanagerbackend/source/collectbuilddependencytoolaction.h b/src/tools/clangpchmanagerbackend/source/collectbuilddependencytoolaction.h index 88ed3d23448..47b86e22191 100644 --- a/src/tools/clangpchmanagerbackend/source/collectbuilddependencytoolaction.h +++ b/src/tools/clangpchmanagerbackend/source/collectbuilddependencytoolaction.h @@ -60,7 +60,6 @@ public: diagnosticConsumer); } -#if LLVM_VERSION_MAJOR >= 10 std::unique_ptr create() override { return std::make_unique( @@ -69,15 +68,6 @@ public: m_excludedIncludeUIDs, m_alreadyIncludedFileUIDs); } -#else - clang::FrontendAction *create() override - { - return new CollectBuildDependencyAction(m_buildDependency, - m_filePathCache, - m_excludedIncludeUIDs, - m_alreadyIncludedFileUIDs); - } -#endif std::vector generateExcludedIncludeFileUIDs(clang::FileManager &fileManager) const { @@ -86,16 +76,12 @@ public: for (const FilePath &filePath : m_excludedFilePaths) { NativeFilePath nativeFilePath{filePath}; - const clang::FileEntry *file = fileManager.getFile({nativeFilePath.path().data(), - nativeFilePath.path().size()}, - true) -#if LLVM_VERSION_MAJOR >= 10 - .get() -#endif - ; + auto file = fileManager.getFile({nativeFilePath.path().data(), + nativeFilePath.path().size()}, + true); if (file) - fileUIDs.push_back(file->getUID()); + fileUIDs.push_back(file.get()->getUID()); } std::sort(fileUIDs.begin(), fileUIDs.end()); diff --git a/src/tools/clangpchmanagerbackend/source/collectusedmacroactionfactory.h b/src/tools/clangpchmanagerbackend/source/collectusedmacroactionfactory.h index 473b8e7df8a..e9c7c4b32b9 100644 --- a/src/tools/clangpchmanagerbackend/source/collectusedmacroactionfactory.h +++ b/src/tools/clangpchmanagerbackend/source/collectusedmacroactionfactory.h @@ -61,7 +61,6 @@ public: diagnosticConsumer); } -#if LLVM_VERSION_MAJOR >= 10 std::unique_ptr create() override { return std::make_unique( @@ -71,16 +70,6 @@ public: m_sourceFiles, m_fileStatuses); } -#else - clang::FrontendAction *create() override - { - return new CollectUsedMacrosAction(m_usedMacros, - m_filePathCache, - m_sourceDependencies, - m_sourceFiles, - m_fileStatuses); - } -#endif private: UsedMacros &m_usedMacros; diff --git a/src/tools/clangpchmanagerbackend/source/collectusedmacrosandsourcespreprocessorcallbacks.h b/src/tools/clangpchmanagerbackend/source/collectusedmacrosandsourcespreprocessorcallbacks.h index 5dc630de182..70adc2dffb5 100644 --- a/src/tools/clangpchmanagerbackend/source/collectusedmacrosandsourcespreprocessorcallbacks.h +++ b/src/tools/clangpchmanagerbackend/source/collectusedmacrosandsourcespreprocessorcallbacks.h @@ -196,17 +196,15 @@ public: } void InclusionDirective(clang::SourceLocation hashLocation, - const clang::Token &/*includeToken*/, + const clang::Token & /*includeToken*/, llvm::StringRef /*fileName*/, bool /*isAngled*/, clang::CharSourceRange /*fileNameRange*/, const clang::FileEntry *file, llvm::StringRef /*searchPath*/, llvm::StringRef /*relativePath*/, - const clang::Module * /*imported*/ -#if LLVM_VERSION_MAJOR >= 7 - , clang::SrcMgr::CharacteristicKind /*fileType*/ -#endif + const clang::Module * /*imported*/, + clang::SrcMgr::CharacteristicKind /*fileType*/ ) override { if (!m_skipInclude && file) diff --git a/src/tools/clangpchmanagerbackend/source/generatepchactionfactory.h b/src/tools/clangpchmanagerbackend/source/generatepchactionfactory.h index f1e4c74457e..3993ccc6511 100644 --- a/src/tools/clangpchmanagerbackend/source/generatepchactionfactory.h +++ b/src/tools/clangpchmanagerbackend/source/generatepchactionfactory.h @@ -68,17 +68,10 @@ public: , m_fileContent(fileContent) {} -#if LLVM_VERSION_MAJOR >= 10 std::unique_ptr create() override { return std::make_unique(m_filePath, m_fileContent); } -#else - clang::FrontendAction *create() override - { - return new GeneratePCHAction{m_filePath, m_fileContent}; - } -#endif private: llvm::StringRef m_filePath; diff --git a/src/tools/clangpchmanagerbackend/source/pchtaskqueue.cpp b/src/tools/clangpchmanagerbackend/source/pchtaskqueue.cpp index 7a6e29c584c..8f1b53855a1 100644 --- a/src/tools/clangpchmanagerbackend/source/pchtaskqueue.cpp +++ b/src/tools/clangpchmanagerbackend/source/pchtaskqueue.cpp @@ -26,6 +26,8 @@ #include "pchtaskqueue.h" #include +#include +#include #include #include #include @@ -107,36 +109,67 @@ void PchTaskQueue::removePchTasks(const ProjectPartIds &projectsPartIds) removePchTasksByProjectPartId(projectsPartIds, m_projectPchTasks); } -void PchTaskQueue::processProjectPchTasks() +int PchTaskQueue::processProjectPchTasks() { - uint systemRunningTaskCount = m_systemPchTaskScheduler.slotUsage().used; + auto slotUsage = m_projectPchTaskScheduler.slotUsage(); + uint freeTaskCount = slotUsage.free; - if (!systemRunningTaskCount) { - uint freeTaskCount = m_projectPchTaskScheduler.slotUsage().free; + int newTaskCount = std::min(int(freeTaskCount), int(m_projectPchTasks.size())); - auto newEnd = std::prev(m_projectPchTasks.end(), - std::min(int(freeTaskCount), int(m_projectPchTasks.size()))); - m_projectPchTaskScheduler.addTasks(createProjectTasks( - {std::make_move_iterator(newEnd), std::make_move_iterator(m_projectPchTasks.end())})); - m_projectPchTasks.erase(newEnd, m_projectPchTasks.end()); - } + auto newEnd = std::prev(m_projectPchTasks.end(), newTaskCount); + m_projectPchTaskScheduler.addTasks(createProjectTasks( + {std::make_move_iterator(newEnd), std::make_move_iterator(m_projectPchTasks.end())})); + m_projectPchTasks.erase(newEnd, m_projectPchTasks.end()); + + return newTaskCount + slotUsage.used; } -void PchTaskQueue::processSystemPchTasks() +int PchTaskQueue::processSystemPchTasks() { - uint freeTaskCount = m_systemPchTaskScheduler.slotUsage().free; + auto slotUsage = m_systemPchTaskScheduler.slotUsage(); + uint freeTaskCount = slotUsage.free; - auto newEnd = std::prev(m_systemPchTasks.end(), - std::min(int(freeTaskCount), int(m_systemPchTasks.size()))); + int newTaskCount = std::min(int(freeTaskCount), int(m_systemPchTasks.size())); + + auto newEnd = std::prev(m_systemPchTasks.end(), newTaskCount); m_systemPchTaskScheduler.addTasks(createSystemTasks( {std::make_move_iterator(newEnd), std::make_move_iterator(m_systemPchTasks.end())})); m_systemPchTasks.erase(newEnd, m_systemPchTasks.end()); + + return newTaskCount + slotUsage.used; +} + +void PchTaskQueue::deleteUnusedPchs() +{ + FilePathIds existingPchFilePathIds = m_fileSystem.directoryEntries( + QString{m_environment.pchBuildDirectory()}); + FilePathIds notAnymoreUsedPchFilePathIds; + notAnymoreUsedPchFilePathIds.reserve(existingPchFilePathIds.size()); + + FilePathIds usedPchFilePathIds = m_filePathCache.filePathIds( + m_precompiledHeaderStorage.fetchAllPchPaths()); + std::sort(usedPchFilePathIds.begin(), usedPchFilePathIds.end()); + + std::set_difference(existingPchFilePathIds.begin(), + existingPchFilePathIds.end(), + usedPchFilePathIds.begin(), + usedPchFilePathIds.end(), + std::back_inserter(notAnymoreUsedPchFilePathIds)); + + m_fileSystem.remove(notAnymoreUsedPchFilePathIds); } void PchTaskQueue::processEntries() { - processSystemPchTasks(); - processProjectPchTasks(); + int projectTaskCount = 0; + int systemTaskCount = processSystemPchTasks(); + if (systemTaskCount == 0) + projectTaskCount = processProjectPchTasks(); + + int totalTaskCount = projectTaskCount + systemTaskCount; + + if (totalTaskCount == 0) + deleteUnusedPchs(); } std::vector PchTaskQueue::createProjectTasks(PchTasks &&pchTasks) const diff --git a/src/tools/clangpchmanagerbackend/source/pchtaskqueue.h b/src/tools/clangpchmanagerbackend/source/pchtaskqueue.h index f7be8ae012e..a7a455b3578 100644 --- a/src/tools/clangpchmanagerbackend/source/pchtaskqueue.h +++ b/src/tools/clangpchmanagerbackend/source/pchtaskqueue.h @@ -37,6 +37,8 @@ class PchCreatorInterface; class PrecompiledHeaderStorageInterface; class ProgressCounter; class Environment; +class FileSystemInterface; +class FilePathCachingInterface; class PchTaskQueue final : public PchTaskQueueInterface { @@ -48,13 +50,17 @@ public: ProgressCounter &progressCounter, PrecompiledHeaderStorageInterface &precompiledHeaderStorage, Sqlite::TransactionInterface &transactionsInterface, - const Environment &environment) + const Environment &environment, + FileSystemInterface &fileSystem, + FilePathCachingInterface &filePathCache) : m_systemPchTaskScheduler(systemPchTaskScheduler) , m_projectPchTaskScheduler(projectPchTaskScheduler) , m_precompiledHeaderStorage(precompiledHeaderStorage) , m_transactionsInterface(transactionsInterface) , m_progressCounter(progressCounter) , m_environment(environment) + , m_fileSystem(fileSystem) + , m_filePathCache(filePathCache) { Q_UNUSED(m_transactionsInterface) } @@ -74,8 +80,9 @@ public: private: void addPchTasks(PchTasks &&pchTasks, PchTasks &destination); void removePchTasksByProjectPartId(const ProjectPartIds &projectsPartIds, PchTasks &destination); - void processProjectPchTasks(); - void processSystemPchTasks(); + int processProjectPchTasks(); + int processSystemPchTasks(); + void deleteUnusedPchs(); private: PchTasks m_systemPchTasks; @@ -86,6 +93,8 @@ private: Sqlite::TransactionInterface &m_transactionsInterface; ProgressCounter &m_progressCounter; const Environment &m_environment; + FileSystemInterface &m_fileSystem; + FilePathCachingInterface &m_filePathCache; }; } // namespace ClangBackEnd diff --git a/src/tools/clangpchmanagerbackend/source/precompiledheaderstorage.h b/src/tools/clangpchmanagerbackend/source/precompiledheaderstorage.h index d781c96e529..16a6a669527 100644 --- a/src/tools/clangpchmanagerbackend/source/precompiledheaderstorage.h +++ b/src/tools/clangpchmanagerbackend/source/precompiledheaderstorage.h @@ -219,6 +219,22 @@ public: return {}; } + FilePaths fetchAllPchPaths() const + { + try { + Sqlite::DeferredTransaction transaction{database}; + + auto filePaths = fetchAllPchPathsStatement.template values(1024); + + transaction.commit(); + + return filePaths; + + } catch (const Sqlite::StatementIsBusy) { + return fetchAllPchPaths(); + } + } + public: Sqlite::ImmediateNonThrowingDestructorTransaction transaction; Database &database; @@ -262,6 +278,10 @@ public: "SELECT projectPchBuildTime, systemPchBuildTime FROM precompiledHeaders WHERE " "projectPartId = ?", database}; + mutable ReadStatement fetchAllPchPathsStatement{ + "SELECT DISTINCT systemPchPath FROM precompiledHeaders UNION ALL SELECT " + "DISTINCT projectPchPath FROM precompiledHeaders", + database}; }; } diff --git a/src/tools/clangpchmanagerbackend/source/precompiledheaderstorageinterface.h b/src/tools/clangpchmanagerbackend/source/precompiledheaderstorageinterface.h index 92b3604cdc4..fd954a0c8a6 100644 --- a/src/tools/clangpchmanagerbackend/source/precompiledheaderstorageinterface.h +++ b/src/tools/clangpchmanagerbackend/source/precompiledheaderstorageinterface.h @@ -60,6 +60,7 @@ public: virtual FilePath fetchPrecompiledHeader(ProjectPartId projectPartId) const = 0; virtual PchPaths fetchPrecompiledHeaders(ProjectPartId projectPartId) const = 0; virtual PrecompiledHeaderTimeStamps fetchTimeStamps(ProjectPartId projectPartId) const = 0; + virtual FilePaths fetchAllPchPaths() const = 0; protected: ~PrecompiledHeaderStorageInterface() = default; diff --git a/src/tools/clangrefactoringbackend/source/collectsymbolsaction.h b/src/tools/clangrefactoringbackend/source/collectsymbolsaction.h index 4f36adadf5f..bf592502bc6 100644 --- a/src/tools/clangrefactoringbackend/source/collectsymbolsaction.h +++ b/src/tools/clangrefactoringbackend/source/collectsymbolsaction.h @@ -48,11 +48,7 @@ class CollectSymbolsAction : public clang::WrapperFrontendAction public: CollectSymbolsAction(std::shared_ptr indexDataConsumer) : clang::WrapperFrontendAction( - clang::index::createIndexingAction(indexDataConsumer, createIndexingOptions() -#if LLVM_VERSION_MAJOR < 10 - , nullptr -#endif - )) + clang::index::createIndexingAction(indexDataConsumer, createIndexingOptions())) , m_indexDataConsumer(indexDataConsumer) {} diff --git a/src/tools/clangrefactoringbackend/source/indexdataconsumer.cpp b/src/tools/clangrefactoringbackend/source/indexdataconsumer.cpp index 5d21b30b9c2..abb30d61b91 100644 --- a/src/tools/clangrefactoringbackend/source/indexdataconsumer.cpp +++ b/src/tools/clangrefactoringbackend/source/indexdataconsumer.cpp @@ -118,11 +118,7 @@ bool IndexDataConsumer::isAlreadyParsed(clang::FileID fileId, SourcesManager &so return sourcesManager.alreadyParsed(filePathId(fileEntry), fileEntry->getModificationTime()); } -#if LLVM_VERSION_MAJOR >= 10 bool IndexDataConsumer::handleDeclOccurrence( -#else - bool IndexDataConsumer::handleDeclOccurence( -#endif const clang::Decl *declaration, clang::index::SymbolRoleSet symbolRoles, llvm::ArrayRef /*symbolRelations*/, @@ -180,11 +176,7 @@ SourceLocationKind macroSymbolType(clang::index::SymbolRoleSet roles) } // namespace -#if LLVM_VERSION_MAJOR >= 10 bool IndexDataConsumer::handleMacroOccurrence( -#else -bool IndexDataConsumer::handleMacroOccurence( -#endif const clang::IdentifierInfo *identifierInfo, const clang::MacroInfo *macroInfo, clang::index::SymbolRoleSet roles, diff --git a/src/tools/clangrefactoringbackend/source/indexdataconsumer.h b/src/tools/clangrefactoringbackend/source/indexdataconsumer.h index e1d3529806e..f3af3287444 100644 --- a/src/tools/clangrefactoringbackend/source/indexdataconsumer.h +++ b/src/tools/clangrefactoringbackend/source/indexdataconsumer.h @@ -57,22 +57,14 @@ public: IndexDataConsumer(const IndexDataConsumer &) = delete; IndexDataConsumer &operator=(const IndexDataConsumer &) = delete; -#if LLVM_VERSION_MAJOR >= 10 bool handleDeclOccurrence( -#else - bool handleDeclOccurence( -#endif const clang::Decl *declaration, clang::index::SymbolRoleSet symbolRoles, llvm::ArrayRef symbolRelations, clang::SourceLocation sourceLocation, ASTNodeInfo astNodeInfo) override; -#if LLVM_VERSION_MAJOR >= 10 bool handleMacroOccurrence( -#else - bool handleMacroOccurence( -#endif const clang::IdentifierInfo *identifierInfo, const clang::MacroInfo *macroInfo, clang::index::SymbolRoleSet roles, diff --git a/src/tools/clangrefactoringbackend/source/symbolscollector.cpp b/src/tools/clangrefactoringbackend/source/symbolscollector.cpp index dffd5838944..8e01869b1d9 100644 --- a/src/tools/clangrefactoringbackend/source/symbolscollector.cpp +++ b/src/tools/clangrefactoringbackend/source/symbolscollector.cpp @@ -74,11 +74,7 @@ std::unique_ptr newFrontendActionFactory( : m_action(consumerFactory) {} -#if LLVM_VERSION_MAJOR >= 10 std::unique_ptr create() override { return std::make_unique(m_action); } -#else - clang::FrontendAction *create() override { return new AdaptorAction(m_action); } -#endif private: class AdaptorAction : public clang::ASTFrontendAction diff --git a/tests/unit/mockup/coreplugin/helpitem.h b/tests/unit/mockup/coreplugin/helpitem.h new file mode 100644 index 00000000000..7942036c74e --- /dev/null +++ b/tests/unit/mockup/coreplugin/helpitem.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "core_global.h" + +#include + +namespace Core { + +class HelpItem +{ +public: + HelpItem() {} + HelpItem(const QString &) {} +}; + +} // namespace Core diff --git a/tests/unit/mockup/coreplugin/icontext.h b/tests/unit/mockup/coreplugin/icontext.h new file mode 100644 index 00000000000..95b55302fb7 --- /dev/null +++ b/tests/unit/mockup/coreplugin/icontext.h @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 + +namespace Core { + +class IContext +{ +public: + using HelpCallback = std::function; +}; + +} // namespace Core diff --git a/tests/unit/mockup/qmldesigner/designercore/include/documentmessage.h b/tests/unit/mockup/qmldesigner/designercore/include/documentmessage.h new file mode 100644 index 00000000000..c4edc8e07db --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/documentmessage.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "exception.h" + +#include + +#include +#include + +namespace QmlJS { +class DiagnosticMessage; +} + +namespace QmlDesigner { + +class DocumentMessage +{ +public: + enum Type { NoError = 0, InternalError = 1, ParseError = 2 }; + +public: + DocumentMessage() {} + DocumentMessage(const QString &) {} + + Type type() const { return m_type; } + + int line() const { return m_line; } + + int column() const { return m_column; } + + QString description() const { return m_description; } + + QUrl url() const { return m_url; } + + QString toString() const { return {}; } + +private: + Type m_type; + int m_line; + int m_column; + QString m_description; + QUrl m_url; +}; + +} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/itemlibraryinfo.h b/tests/unit/mockup/qmldesigner/designercore/include/itemlibraryinfo.h new file mode 100644 index 00000000000..ad854699b4e --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/itemlibraryinfo.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "qmldesignercorelib_global.h" + +#include + +namespace QmlDesigner { + +class ItemLibraryEntry +{ +public: + QString name() const { return {}; } + TypeName typeName() const { return {}; } + QIcon typeIcon() const { return {}; } + QString libraryEntryIconPath() const { return {}; } +}; + +class ItemLibraryInfo +{ +public: + QList entries() const { return {}; } + QList entriesForType(const QByteArray &typeName, + int majorVersion, + int minorVersion) const + { + return {}; + } + ItemLibraryEntry entry(const QString &name) const { return {}; } +}; + +} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/itemlibraryitem.h b/tests/unit/mockup/qmldesigner/designercore/include/itemlibraryitem.h new file mode 100644 index 00000000000..ccc06640eb8 --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/itemlibraryitem.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 + +#include "itemlibraryinfo.h" + +namespace QmlDesigner { + +class ItemLibraryItem : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QVariant itemLibraryEntry READ itemLibraryEntry FINAL) + Q_PROPERTY(QString itemName READ itemName FINAL) + Q_PROPERTY(QString itemLibraryIconPath READ itemLibraryIconPath FINAL) + Q_PROPERTY(bool itemVisible READ isVisible NOTIFY visibilityChanged FINAL) + +public: + ItemLibraryItem(QObject *) {} + ~ItemLibraryItem() override {} + + QString itemName() const { return {}; } + QString typeName() const { return {}; } + QString itemLibraryIconPath() const { return {}; } + + bool setVisible(bool) { return {}; } + bool isVisible() const { return {}; } + + void setItemLibraryEntry(const ItemLibraryEntry &) {} + QVariant itemLibraryEntry() const { return {}; } + +signals: + void visibilityChanged(); +}; + +} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/metainfo.h b/tests/unit/mockup/qmldesigner/designercore/include/metainfo.h new file mode 100644 index 00000000000..22e7c5762db --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/metainfo.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "qmldesignercorelib_global.h" + +#include +#include + +#include "itemlibraryinfo.h" +#include + +namespace QmlDesigner { + +class ModelNode; +class AbstractProperty; +class ItemLibraryInfo; + +inline bool operator==(const MetaInfo &first, const MetaInfo &second) +{ + return {}; +} +inline bool operator!=(const MetaInfo &first, const MetaInfo &second) +{ + return {}; +} + +class QMLDESIGNERCORE_EXPORT MetaInfo +{ +public: + ItemLibraryInfo *itemLibraryInfo() const { return {}; } + +public: + static MetaInfo global() { return {}; } + static void clearGlobal() {} + + static void setPluginPaths(const QStringList &paths) {} +}; + +} //namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/nodeinstanceview.h b/tests/unit/mockup/qmldesigner/designercore/include/nodeinstanceview.h new file mode 100644 index 00000000000..246d1ede406 --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/nodeinstanceview.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "qmldesignercorelib_global.h" +#include "abstractview.h" + +namespace QmlDesigner { + +class NodeInstanceView : public AbstractView +{ + Q_OBJECT + +public: + NodeInstanceView(QObject *parent) {} + ~NodeInstanceView() override {} + + void modelAttached(Model *model) override {} + void modelAboutToBeDetached(Model *model) override {} + void nodeCreated(const ModelNode &createdNode) override {} + void nodeRemoved(const ModelNode &removedNode, + const NodeAbstractProperty &parentProperty, + PropertyChangeFlags propertyChange) override + {} + void propertiesAboutToBeRemoved(const QList &propertyList) override {} + void propertiesRemoved(const QList &propertyList) override {} + void variantPropertiesChanged(const QList &propertyList, + PropertyChangeFlags propertyChange) override + {} + void bindingPropertiesChanged(const QList &propertyList, + PropertyChangeFlags propertyChange) override + {} + void signalHandlerPropertiesChanged(const QVector &propertyList, + PropertyChangeFlags propertyChange) override + {} + void nodeReparented(const ModelNode &node, + const NodeAbstractProperty &newPropertyParent, + const NodeAbstractProperty &oldPropertyParent, + AbstractView::PropertyChangeFlags propertyChange) override + {} + void nodeIdChanged(const ModelNode &node, const QString &newId, const QString &oldId) override + {} + void nodeOrderChanged(const NodeListProperty &listProperty, + const ModelNode &movedNode, + int oldIndex) override + {} + void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override {} + void nodeTypeChanged(const ModelNode &node, + const TypeName &type, + int majorVersion, + int minorVersion) override + {} + void customNotification(const AbstractView *view, + const QString &identifier, + const QList &nodeList, + const QList &data) override + {} + + void rewriterBeginTransaction() override {} + void rewriterEndTransaction() override {} + + void importsChanged(const QList &addedImports, const QList &removedImports) override + {} + + void sendToken(const QString &token, int number, const QVector &nodeVector) {} +}; + +} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/nodemetainfo.h b/tests/unit/mockup/qmldesigner/designercore/include/nodemetainfo.h new file mode 100644 index 00000000000..ed00f71d989 --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/nodemetainfo.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 + +#include "qmldesignercorelib_global.h" + +QT_BEGIN_NAMESPACE +class QDeclarativeContext; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class MetaInfo; +class Model; +class AbstractProperty; + +class NodeMetaInfo +{ +public: + NodeMetaInfo() {} + NodeMetaInfo(Model *, const TypeName &, int, int) {} + + bool isValid() const { return {}; } + bool isFileComponent() const { return {}; } + bool hasProperty(const PropertyName &) const { return {}; } + PropertyNameList propertyNames() const { return {}; } + PropertyNameList signalNames() const { return {}; } + PropertyNameList directPropertyNames() const { return {}; } + PropertyName defaultPropertyName() const { return "data"; } + bool hasDefaultProperty() const { return {}; } + TypeName propertyTypeName(const PropertyName &) const { return {}; } + bool propertyIsWritable(const PropertyName &) const { return {}; } + bool propertyIsListProperty(const PropertyName &) const { return {}; } + bool propertyIsEnumType(const PropertyName &) const { return {}; } + bool propertyIsPrivate(const PropertyName &) const { return {}; } + QString propertyEnumScope(const PropertyName &) const { return {}; } + QStringList propertyKeysForEnum(const PropertyName &) const { return {}; } + QVariant propertyCastedValue(const PropertyName &, const QVariant &) const { return {}; } + + QList classHierarchy() const { return {}; } + QList superClasses() const { return {}; } + NodeMetaInfo directSuperClass() const { return {}; } + + bool defaultPropertyIsComponent() const { return {}; } + + TypeName typeName() const { return {}; } + TypeName simplifiedTypeName() const { return {}; } + int majorVersion() const { return {}; } + int minorVersion() const { return {}; } + + QString componentSource() const { return {}; } + QString componentFileName() const { return {}; } + + bool hasCustomParser() const { return {}; } + + bool availableInVersion(int, int) const { return {}; } + bool isSubclassOf(const TypeName &, int = -1, int = -1) const { return {}; } + + bool isGraphicalItem() const { return {}; } + bool isLayoutable() const { return {}; } + bool isView() const { return {}; } + bool isTabView() const { return {}; } + + QString importDirectoryPath() const { return {}; } + + static void clearCache() {} +}; + +} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/qmlmodelnodefacade.h b/tests/unit/mockup/qmldesigner/designercore/include/qmlmodelnodefacade.h new file mode 100644 index 00000000000..3821943d8fd --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/qmlmodelnodefacade.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 + +namespace QmlDesigner { + +class AbstractView; +class NodeInstanceView; + +class QmlModelNodeFacade +{ +public: + operator ModelNode() const { return {}; } + ModelNode modelNode() { return {}; } + const ModelNode modelNode() const { return {}; } + bool hasModelNode() const { return {}; } + static bool isValidQmlModelNodeFacade(const ModelNode &modelNode) { return {}; } + virtual bool isValid() const { return {}; } + + AbstractView *view() const { return {}; } + static NodeInstanceView *nodeInstanceView(const ModelNode &modelNode) { return {}; } + NodeInstanceView *nodeInstanceView() const { return {}; } + bool isRootNode() const { return {}; } + + QmlModelNodeFacade(const ModelNode &) {} + QmlModelNodeFacade() {} + ~QmlModelNodeFacade(){}; +}; + +} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/qmlobjectnode.h b/tests/unit/mockup/qmldesigner/designercore/include/qmlobjectnode.h new file mode 100644 index 00000000000..7e2c118589a --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/qmlobjectnode.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "qmlmodelnodefacade.h" +#include + +#include + +namespace QmlDesigner { + +class QMLDESIGNERCORE_EXPORT QmlObjectNode : public QmlModelNodeFacade +{ +public: + QmlObjectNode() {} + QmlObjectNode(const ModelNode &modelNode){}; +}; + +} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/qmlstate.h b/tests/unit/mockup/qmldesigner/designercore/include/qmlstate.h new file mode 100644 index 00000000000..50e82596b50 --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/qmlstate.h @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "qmlmodelnodefacade.h" + +namespace QmlDesigner { + +class QmlModelState : public QmlModelNodeFacade +{ +public: + QmlModelState(); + QmlModelState(const ModelNode &) {} +}; + +} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/qmltimeline.h b/tests/unit/mockup/qmldesigner/designercore/include/qmltimeline.h new file mode 100644 index 00000000000..0d2c05b1b56 --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/qmltimeline.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "qmlmodelnodefacade.h" +#include + +namespace QmlDesigner { + +class QmlTimeline : public QmlModelNodeFacade +{ +public: + QmlTimeline() {} + QmlTimeline(const ModelNode &) {} + + bool isValid() const override { return {}; } + + void toogleRecording(bool b) const {} + + void resetGroupRecording() const {} +}; + +} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/rewriterview.h b/tests/unit/mockup/qmldesigner/designercore/include/rewriterview.h new file mode 100644 index 00000000000..39c19e4e2dd --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/rewriterview.h @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "qmldesignercorelib_global.h" +#include "abstractview.h" + +namespace QmlJS { +class Document; +class ScopeChain; +} + +namespace QmlDesigner { + +class TextModifier; + +namespace Internal { + +class TextToModelMerger; +class ModelToTextMerger; +class ModelNodePositionStorage; + +} //Internal + +struct CppTypeData +{ + QString superClassName; + QString importUrl; + QString versionString; + QString cppClassName; + QString typeName; + bool isSingleton = false; +}; + +class RewriterView : public AbstractView +{ + Q_OBJECT + +public: + enum DifferenceHandling { + Validate, + Amend + }; + +public: + RewriterView(DifferenceHandling, QObject *) {} + ~RewriterView() override {} + + void modelAttached(Model *) override {} + void modelAboutToBeDetached(Model *) override {} + void nodeCreated(const ModelNode &) override {} + void nodeRemoved(const ModelNode &, const NodeAbstractProperty &, PropertyChangeFlags) override + {} + void propertiesAboutToBeRemoved(const QList &) override {} + void propertiesRemoved(const QList &) override {} + void variantPropertiesChanged(const QList &, PropertyChangeFlags) override {} + void bindingPropertiesChanged(const QList &, PropertyChangeFlags) override {} + void signalHandlerPropertiesChanged(const QVector &, + PropertyChangeFlags) override + {} + void nodeReparented(const ModelNode &, + const NodeAbstractProperty &, + const NodeAbstractProperty &, + AbstractView::PropertyChangeFlags) override + {} + void nodeIdChanged(const ModelNode &, const QString &, const QString &) override {} + void nodeOrderChanged(const NodeListProperty &, const ModelNode &, int) override {} + void rootNodeTypeChanged(const QString &, int, int) override {} + void nodeTypeChanged(const ModelNode &, const TypeName &, int, int) override {} + void customNotification(const AbstractView *, + const QString &, + const QList &, + const QList &) override + {} + + void rewriterBeginTransaction() override {} + void rewriterEndTransaction() override {} + + void importsChanged(const QList &, const QList &) override {} + + TextModifier *textModifier() const { return {}; } + void setTextModifier(TextModifier *) {} + QString textModifierContent() const { return {}; } + + void reactivateTextMofifierChangeSignals() {} + void deactivateTextMofifierChangeSignals() {} + + void auxiliaryDataChanged(const ModelNode &node, const PropertyName &name, const QVariant &data) override + {} + + Internal::ModelNodePositionStorage *positionStorage() const {} + + QList warnings() const { return {}; } + QList errors() const { return {}; } + void clearErrorAndWarnings() {} + void setErrors(const QList &) {} + void setWarnings(const QList &) {} + void setIncompleteTypeInformation(bool) {} + bool hasIncompleteTypeInformation() const { return false; } + void addError(const DocumentMessage &) {} + + void enterErrorState(const QString &) {} + bool inErrorState() const { return false; } + void leaveErrorState() {} + void resetToLastCorrectQml() {} + + QMap extractText(const QList &) const; + int nodeOffset(const ModelNode &) const; + int nodeLength(const ModelNode &) const; + int firstDefinitionInsideOffset(const ModelNode &) const { return {}; } + int firstDefinitionInsideLength(const ModelNode &) const { return {}; } + bool modificationGroupActive() { return {}; } + ModelNode nodeAtTextCursorPosition(int) const { return {}; } + + bool renameId(const QString &, const QString &) { return {}; } + + const QmlJS::Document *document() const { return {}; } + const QmlJS::ScopeChain *scopeChain() const { return {}; } + + QString convertTypeToImportAlias(const QString &) const { return {}; } + + bool checkSemanticErrors() const { return {}; } + + void setCheckSemanticErrors(bool) {} + + QString pathForImport(const Import &) { return {}; } + + QStringList importDirectories() const { return {}; } + + QSet> qrcMapping() const { return {}; } + + void moveToComponent(const ModelNode &) {} + + QStringList autoComplete(const QString &, int, bool = true) { return {}; } + + QList getCppTypes() { return {}; } + + void setWidgetStatusCallback(std::function setWidgetStatusCallback); + + void qmlTextChanged() {} + void delayedSetup() {} + + void writeAuxiliaryData() {} + void restoreAuxiliaryData() {} + + QString getRawAuxiliaryData() const { return {}; } + QString auxiliaryDataAsQML() const { return {}; } + + ModelNode getNodeForCanonicalIndex(int) { return {}; } +}; + +} //QmlDesigner diff --git a/tests/unit/unittest/creator_dependency.pri b/tests/unit/unittest/creator_dependency.pri index 4aaac22a1bf..ef00624b957 100644 --- a/tests/unit/unittest/creator_dependency.pri +++ b/tests/unit/unittest/creator_dependency.pri @@ -16,6 +16,7 @@ include($$PWD/../../../src/plugins/clangpchmanager/clangpchmanager-source.pri) include($$PWD/../../../src/plugins/cpptools/cpptoolsunittestfiles.pri) include($$PWD/../../../src/plugins/debugger/debuggerunittestfiles.pri) include($$PWD/../../../src/plugins/compilationdatabaseprojectmanager/compilationdatabaseunittestfiles.pri) +include($$PWD/../../../src/plugins/qmldesigner/qmldesignerunittestfiles.pri) !isEmpty(QTC_UNITTEST_BUILD_CPP_PARSER):include(cplusplus.pri) !isEmpty(LLVM_VERSION) { include($$PWD/../../../src/plugins/clangtools/clangtoolsunittestfiles.pri) diff --git a/tests/unit/unittest/google-using-declarations.h b/tests/unit/unittest/google-using-declarations.h index 1a92ff33884..de524834039 100644 --- a/tests/unit/unittest/google-using-declarations.h +++ b/tests/unit/unittest/google-using-declarations.h @@ -65,6 +65,8 @@ using testing::Property; using testing::Return; using testing::ReturnRef; using testing::SafeMatcherCast; +using testing::SaveArg; +using testing::SaveArgPointee; using testing::Sequence; using testing::SizeIs; using testing::StrEq; diff --git a/tests/unit/unittest/gtest-creator-printing.cpp b/tests/unit/unittest/gtest-creator-printing.cpp index 704c9c6b90a..13fbe941301 100644 --- a/tests/unit/unittest/gtest-creator-printing.cpp +++ b/tests/unit/unittest/gtest-creator-printing.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,7 @@ #include #include #include +#include #include @@ -1450,6 +1452,25 @@ std::ostream &operator<<(std::ostream &out, const Diagnostic &diag) { } // namespace Internal } // namespace ClangTools +namespace QmlDesigner { + +std::ostream &operator<<(std::ostream &out, const ModelNode &node) +{ + if (!node.isValid()) + return out << "(invalid)"; + + return out << "(" << node.id() << ")"; +} +std::ostream &operator<<(std::ostream &out, const VariantProperty &property) +{ + if (!property.isValid()) + return out << "(invalid)"; + + return out << "(" << property.parentModelNode() << ", " << property.name() << ", " + << property.value() << ")"; +} +} // namespace QmlDesigner + void setFilePathCache(ClangBackEnd::FilePathCaching *cache) { filePathCache = cache; diff --git a/tests/unit/unittest/gtest-creator-printing.h b/tests/unit/unittest/gtest-creator-printing.h index e0cb55315f4..565479be03f 100644 --- a/tests/unit/unittest/gtest-creator-printing.h +++ b/tests/unit/unittest/gtest-creator-printing.h @@ -350,4 +350,12 @@ std::ostream &operator<<(std::ostream &out, const Diagnostic &diag); } // namespace Internal } // namespace CppTools +namespace QmlDesigner { +class ModelNode; +class VariantProperty; + +std::ostream &operator<<(std::ostream &out, const ModelNode &node); +std::ostream &operator<<(std::ostream &out, const VariantProperty &property); +} // namespace QmlDesigner + void setFilePathCache(ClangBackEnd::FilePathCaching *filePathCache); diff --git a/tests/unit/unittest/gtest-qt-printing.cpp b/tests/unit/unittest/gtest-qt-printing.cpp index cd97883b164..c097fd0b4cb 100644 --- a/tests/unit/unittest/gtest-qt-printing.cpp +++ b/tests/unit/unittest/gtest-qt-printing.cpp @@ -59,9 +59,11 @@ std::ostream &operator<<(std::ostream &out, const QVariant &variant) QString output; QDebug debug(&output); - debug << variant; + debug.noquote().nospace() << variant; - return out << output; + QByteArray utf8Text = output.toUtf8(); + + return out.write(utf8Text.data(), utf8Text.size()); } std::ostream &operator<<(std::ostream &out, const QTextCharFormat &format) @@ -88,4 +90,14 @@ void PrintTo(const QString &text, std::ostream *os) *os << text; } +void PrintTo(const QVariant &variant, std::ostream *os) +{ + *os << variant; +} + +void PrintTo(const QByteArray &text, std::ostream *os) +{ + *os << text; +} + QT_END_NAMESPACE diff --git a/tests/unit/unittest/gtest-qt-printing.h b/tests/unit/unittest/gtest-qt-printing.h index 424762273b3..ebaeb2c7851 100644 --- a/tests/unit/unittest/gtest-qt-printing.h +++ b/tests/unit/unittest/gtest-qt-printing.h @@ -35,10 +35,12 @@ class QVariant; class QString; class QTextCharFormat; -std::ostream &operator<<(std::ostream &out, const QVariant &variant); +std::ostream &operator<<(std::ostream &out, const QVariant &QVariant); std::ostream &operator<<(std::ostream &out, const QString &text); std::ostream &operator<<(std::ostream &out, const QByteArray &byteArray); std::ostream &operator<<(std::ostream &out, const QTextCharFormat &format); void PrintTo(const QString &text, std::ostream *os); +void PrintTo(const QVariant &variant, std::ostream *os); +void PrintTo(const QByteArray &text, std::ostream *os); QT_END_NAMESPACE diff --git a/tests/unit/unittest/listmodeleditor-test.cpp b/tests/unit/unittest/listmodeleditor-test.cpp new file mode 100644 index 00000000000..ca0913f8656 --- /dev/null +++ b/tests/unit/unittest/listmodeleditor-test.cpp @@ -0,0 +1,952 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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 "googletest.h" + +#include "mocklistmodeleditorview.h" + +#include +#include +#include +#include +#include + +namespace { + +using QmlDesigner::AbstractProperty; +using QmlDesigner::AbstractView; +using QmlDesigner::ModelNode; + +MATCHER_P2(HasItem, + name, + value, + std::string(negation ? "hasn't " : "has ") + "(" + name + ", " + value + ")") +{ + QStandardItem *item = arg; + + return item->data(Qt::UserRole).toString() == name && item->data(Qt::UserRole).toDouble() == value; +} + +MATCHER(IsInvalid, std::string(negation ? "isn't null" : "is null")) +{ + return !arg.isValid(); +} + +MATCHER_P3(IsVariantProperty, + node, + name, + value, + std::string(negation ? "isn't " : "is ") + "(" + name + ", " + PrintToString(value) + ")") +{ + const QmlDesigner::VariantProperty &property = arg; + + return property.parentModelNode() == node && property.name() == name && property.value() == value; +} + +MATCHER_P2(IsVariantProperty, + name, + value, + std::string(negation ? "isn't " : "is ") + "(" + name + ", " + PrintToString(value) + ")") +{ + const QmlDesigner::VariantProperty &property = arg; + + return property.name() == name && property.value() == value; +} + +MATCHER_P2(IsAbstractProperty, node, name, std::string(negation ? "isn't " : "is ") + "(" + name + ")") +{ + const QmlDesigner::AbstractProperty &property = arg; + + return property.parentModelNode() == node && property.name() == name; +} + +class ListModelEditor : public testing::Test +{ +public: + ListModelEditor() + { + designerModel->attachView(&mockView); + + emptyListModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15); + + listModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15); + mockView.rootModelNode().defaultNodeListProperty().reparentHere(listModelNode); + element1 = createElement({{"name", "foo"}, {"value", 1}, {"value2", 42}}); + element2 = createElement({{"value", 4}, {"name", "bar"}, {"image", "pic.png"}}); + element3 = createElement({{"image", "pic.png"}, {"name", "poo"}, {"value", 111}}); + } + + using Entry = std::pair; + + ModelNode createElement(std::initializer_list entries) + { + auto element = mockView.createModelNode("QtQml.Models/ListElement", 2, 15); + listModelNode.defaultNodeListProperty().reparentHere(element); + + for (const auto &entry : entries) { + element.variantProperty(entry.first).setValue(entry.second); + } + + return element; + } + + QList headerLabels(const QmlDesigner::ListModelEditorModel &model) const + { + QList labels; + labels.reserve(model.columnCount()); + + for (int i = 0; i < model.columnCount(); ++i) + labels.push_back(model.headerData(i, Qt::Horizontal).toString()); + + return labels; + } + + QList> displayValues() const + { + QList> rows; + + for (int rowIndex = 0; rowIndex < model.rowCount(); ++rowIndex) { + QList row; + + for (int columnIndex = 0; columnIndex < model.columnCount(); ++columnIndex) + row.push_back(model.data(model.index(rowIndex, columnIndex), Qt::DisplayRole)); + + rows.push_back(row); + } + + return rows; + } + + QList> backgroundColors() const + { + QList> rows; + + for (int rowIndex = 0; rowIndex < model.rowCount(); ++rowIndex) { + QList row; + + for (int columnIndex = 0; columnIndex < model.columnCount(); ++columnIndex) + row.push_back( + model.data(model.index(rowIndex, columnIndex), Qt::BackgroundColorRole) + .value()); + + rows.push_back(row); + } + + return rows; + } + + QList> properties() const + { + QList> properties; + properties.reserve(10); + + auto nodes = listModelNode.defaultNodeListProperty().toModelNodeList(); + + for (const ModelNode &node : nodes) + properties.push_back(node.variantProperties()); + + return properties; + } + +protected: + std::unique_ptr designerModel{QmlDesigner::Model::create("QtQuick.Item", 1, 1)}; + NiceMock mockView; + QmlDesigner::ListModelEditorModel model; + ModelNode listModelNode; + ModelNode emptyListModelNode; + ModelNode element1; + ModelNode element2; + ModelNode element3; +}; + +TEST_F(ListModelEditor, CreatePropertyNameSet) +{ + model.setListModel(listModelNode); + + ASSERT_THAT(model.propertyNames(), ElementsAre("image", "name", "value", "value2")); +} + +TEST_F(ListModelEditor, CreatePropertyNameSetForEmptyList) +{ + model.setListModel(emptyListModelNode); + + ASSERT_THAT(model.propertyNames(), IsEmpty()); +} + +TEST_F(ListModelEditor, HorizontalLabels) +{ + model.setListModel(listModelNode); + + ASSERT_THAT(headerLabels(model), ElementsAre("image", "name", "value", "value2")); +} + +TEST_F(ListModelEditor, HorizontalLabelsForEmptyList) +{ + model.setListModel(emptyListModelNode); + + ASSERT_THAT(headerLabels(model), IsEmpty()); +} + +TEST_F(ListModelEditor, DisplayValues) +{ + model.setListModel(listModelNode); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42), + ElementsAre("pic.png", "bar", 4, IsInvalid()), + ElementsAre("pic.png", "poo", 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, ChangeValueChangesDisplayValues) +{ + model.setListModel(listModelNode); + + model.setValue(0, 1, "hello"); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "hello", 1, 42), + ElementsAre("pic.png", "bar", 4, IsInvalid()), + ElementsAre("pic.png", "poo", 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, EditValueCallVariantPropertiesChanged) +{ + model.setListModel(listModelNode); + + EXPECT_CALL(mockView, + variantPropertiesChanged(ElementsAre(IsVariantProperty(element1, "name", "hello")), + Eq(AbstractView::NoAdditionalChanges))); + + model.setValue(0, 1, "hello"); +} + +TEST_F(ListModelEditor, ChangeDisplayValueCallsVariantPropertiesChanged) +{ + model.setListModel(listModelNode); + + EXPECT_CALL(mockView, + variantPropertiesChanged(ElementsAre(IsVariantProperty(element1, "name", "hello")), + Eq(AbstractView::NoAdditionalChanges))) + .Times(0); + + model.setValue(0, 1, "hello", Qt::DisplayRole); +} + +TEST_F(ListModelEditor, AddRowAddedInvalidRow) +{ + model.setListModel(listModelNode); + + model.addRow(); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42), + ElementsAre("pic.png", "bar", 4, IsInvalid()), + ElementsAre("pic.png", "poo", 111, IsInvalid()), + ElementsAre(IsInvalid(), IsInvalid(), IsInvalid(), IsInvalid()))); +} + +TEST_F(ListModelEditor, AddRowCreatesNewModelNodeAndReparents) +{ + model.setListModel(listModelNode); + + EXPECT_CALL(mockView, nodeCreated(Property(&ModelNode::type, Eq("QtQml.Models.ListElement")))); + EXPECT_CALL(mockView, + nodeReparented(Property(&ModelNode::type, Eq("QtQml.Models.ListElement")), + Property(&AbstractProperty::parentModelNode, Eq(listModelNode)), + _, + _)); + + model.addRow(); +} + +TEST_F(ListModelEditor, ChangeAddedRowPropery) +{ + model.setListModel(listModelNode); + model.addRow(); + + model.setValue(3, 2, 22); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42), + ElementsAre("pic.png", "bar", 4, IsInvalid()), + ElementsAre("pic.png", "poo", 111, IsInvalid()), + ElementsAre(IsInvalid(), IsInvalid(), 22, IsInvalid()))); +} + +TEST_F(ListModelEditor, ChangeAddedRowProperyCallsVariantPropertiesChanged) +{ + model.setListModel(listModelNode); + ModelNode element4; + ON_CALL(mockView, nodeReparented(_, _, _, _)).WillByDefault(SaveArg<0>(&element4)); + model.addRow(); + + EXPECT_CALL(mockView, + variantPropertiesChanged(ElementsAre(IsVariantProperty(element4, "value", 22)), + Eq(AbstractView::PropertiesAdded))); + + model.setValue(3, 2, 22); +} + +TEST_F(ListModelEditor, AddColumnInsertsPropertyName) +{ + model.setListModel(listModelNode); + + model.addColumn("other"); + + ASSERT_THAT(model.propertyNames(), ElementsAre("image", "name", "other", "value", "value2")); +} + +TEST_F(ListModelEditor, AddColumnInsertsPropertyNameToEmptyModel) +{ + model.setListModel(emptyListModelNode); + + model.addColumn("foo"); + + ASSERT_THAT(model.propertyNames(), ElementsAre("foo")); +} + +TEST_F(ListModelEditor, AddTwiceColumnInsertsPropertyNameToEmptyModel) +{ + model.setListModel(emptyListModelNode); + model.addColumn("foo"); + + model.addColumn("foo2"); + + ASSERT_THAT(model.propertyNames(), ElementsAre("foo", "foo2")); +} + +TEST_F(ListModelEditor, AddSameColumnInsertsPropertyName) +{ + model.setListModel(emptyListModelNode); + model.addColumn("foo"); + + model.addColumn("foo"); + + ASSERT_THAT(model.propertyNames(), ElementsAre("foo")); +} + +TEST_F(ListModelEditor, AddColumnInsertsHeaderLabel) +{ + model.setListModel(listModelNode); + + model.addColumn("other"); + + ASSERT_THAT(headerLabels(model), ElementsAre("image", "name", "other", "value", "value2")); +} + +TEST_F(ListModelEditor, AddColumnInsertsHeaderLabelToEmptyModel) +{ + model.setListModel(emptyListModelNode); + + model.addColumn("foo"); + + ASSERT_THAT(headerLabels(model), ElementsAre("foo")); +} + +TEST_F(ListModelEditor, AddTwiceColumnInsertsHeaderLabelToEmptyModel) +{ + model.setListModel(emptyListModelNode); + model.addColumn("foo"); + + model.addColumn("foo2"); + + ASSERT_THAT(headerLabels(model), ElementsAre("foo", "foo2")); +} + +TEST_F(ListModelEditor, AddSameColumnInsertsHeaderLabel) +{ + model.setListModel(emptyListModelNode); + model.addColumn("foo"); + + model.addColumn("foo"); + + ASSERT_THAT(headerLabels(model), ElementsAre("foo")); +} + +TEST_F(ListModelEditor, AddColumnInsertsDisplayValues) +{ + model.setListModel(listModelNode); + + model.addColumn("other"); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", IsInvalid(), 1, 42), + ElementsAre("pic.png", "bar", IsInvalid(), 4, IsInvalid()), + ElementsAre("pic.png", "poo", IsInvalid(), 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, ChangeAddColumnPropertyDisplayValue) +{ + model.setListModel(listModelNode); + model.addColumn("other"); + + model.setValue(1, 2, 22); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", IsInvalid(), 1, 42), + ElementsAre("pic.png", "bar", 22, 4, IsInvalid()), + ElementsAre("pic.png", "poo", IsInvalid(), 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, ChangeAddColumnPropertyCallsVariantPropertiesChanged) +{ + model.setListModel(listModelNode); + model.addColumn("other"); + + EXPECT_CALL(mockView, + variantPropertiesChanged(ElementsAre(IsVariantProperty(element2, "other", 434)), _)); + + model.setValue(1, 2, 434); +} + +TEST_F(ListModelEditor, RemoveColumnRemovesDisplayValues) +{ + model.setListModel(listModelNode); + + model.removeColumn(2); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 42), + ElementsAre("pic.png", "bar", IsInvalid()), + ElementsAre("pic.png", "poo", IsInvalid()))); +} + +TEST_F(ListModelEditor, RemoveColumnRemovesProperties) +{ + model.setListModel(listModelNode); + + EXPECT_CALL(mockView, propertiesRemoved(ElementsAre(IsAbstractProperty(element2, "image")))); + EXPECT_CALL(mockView, propertiesRemoved(ElementsAre(IsAbstractProperty(element3, "image")))); + + model.removeColumn(0); +} + +TEST_F(ListModelEditor, RemoveColumnRemovesPropertyName) +{ + model.setListModel(listModelNode); + + model.removeColumn(1); + + ASSERT_THAT(model.propertyNames(), ElementsAre("image", "value", "value2")); +} + +TEST_F(ListModelEditor, RemoveRowRemovesDisplayValues) +{ + model.setListModel(listModelNode); + + model.removeRow(1); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42), + ElementsAre("pic.png", "poo", 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, RemoveRowRemovesElementInListModel) +{ + model.setListModel(listModelNode); + + EXPECT_CALL(mockView, nodeRemoved(Eq(element2), _, _)); + + model.removeRow(1); +} + +TEST_F(ListModelEditor, ConvertStringFloatToFloat) +{ + model.setListModel(listModelNode); + + model.setValue(1, 1, "25.5"); + + ASSERT_THAT(element2.variantProperty("name").value().value(), 25.5); + ASSERT_THAT(element2.variantProperty("name").value().type(), QVariant::Double); +} + +TEST_F(ListModelEditor, ConvertStringIntegerToDouble) +{ + model.setListModel(listModelNode); + + model.setValue(1, 1, "25"); + + ASSERT_THAT(element2.variantProperty("name").value().value(), 25); + ASSERT_THAT(element2.variantProperty("name").value().type(), QVariant::Double); +} + +TEST_F(ListModelEditor, DontConvertStringToNumber) +{ + model.setListModel(listModelNode); + + model.setValue(1, 1, "hello"); + + ASSERT_THAT(element2.variantProperty("name").value().value(), "hello"); + ASSERT_THAT(element2.variantProperty("name").value().type(), QVariant::String); +} + +TEST_F(ListModelEditor, EmptyStringsRemovesProperty) +{ + model.setListModel(listModelNode); + + model.setValue(1, 1, ""); + + ASSERT_THAT(element2.variantProperty("name").value().value(), Eq("")); +} + +TEST_F(ListModelEditor, InvalidVariantRemovesProperty) +{ + model.setListModel(listModelNode); + + model.setValue(1, 1, QVariant()); + + ASSERT_FALSE(element2.hasProperty("name")); +} + +TEST_F(ListModelEditor, DispayValueIsChangedToDouble) +{ + model.setListModel(listModelNode); + + model.setValue(1, 1, "25.5"); + + ASSERT_THAT(displayValues()[1][1].type(), QVariant::Double); +} + +TEST_F(ListModelEditor, StringDispayValueIsNotChanged) +{ + model.setListModel(listModelNode); + + model.setValue(1, 1, "25.5a"); + + ASSERT_THAT(displayValues()[1][1].type(), QVariant::String); +} + +TEST_F(ListModelEditor, SetInvalidToDarkYellowBackgroundColor) +{ + model.setListModel(listModelNode); + + ASSERT_THAT( + backgroundColors(), + ElementsAre( + ElementsAre(Qt::darkYellow, Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow)), + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Qt::darkYellow), + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Qt::darkYellow))); +} + +TEST_F(ListModelEditor, SettingValueChangesBackgroundColor) +{ + model.setListModel(listModelNode); + + model.setValue(0, 0, "foo"); + + ASSERT_THAT( + backgroundColors(), + ElementsAre( + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow)), + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Qt::darkYellow), + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Qt::darkYellow))); +} + +TEST_F(ListModelEditor, SettingValueChangesByDisplayRoleBackgroundColor) +{ + model.setListModel(listModelNode); + + model.setValue(0, 0, "foo", Qt::DisplayRole); + + ASSERT_THAT( + backgroundColors(), + ElementsAre( + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow)), + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Qt::darkYellow), + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Qt::darkYellow))); +} + +TEST_F(ListModelEditor, ResettingValueChangesBackgroundColor) +{ + model.setListModel(listModelNode); + + model.setValue(1, 1, QVariant{}); + + ASSERT_THAT( + backgroundColors(), + ElementsAre( + ElementsAre(Qt::darkYellow, Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow)), + ElementsAre(Not(Qt::darkYellow), Qt::darkYellow, Not(Qt::darkYellow), Qt::darkYellow), + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Qt::darkYellow))); +} + +TEST_F(ListModelEditor, ResettingValueChangesByDisplayRoleBackgroundColor) +{ + model.setListModel(listModelNode); + + model.setValue(1, 1, QVariant{}, Qt::DisplayRole); + + ASSERT_THAT( + backgroundColors(), + ElementsAre( + ElementsAre(Qt::darkYellow, Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow)), + ElementsAre(Not(Qt::darkYellow), Qt::darkYellow, Not(Qt::darkYellow), Qt::darkYellow), + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Qt::darkYellow))); +} + +TEST_F(ListModelEditor, SettingNullValueChangesBackgroundColor) +{ + model.setListModel(listModelNode); + + model.setValue(0, 0, 0); + + ASSERT_THAT(backgroundColors(), + ElementsAre(ElementsAre(_, _, _, _), + ElementsAre(_, _, _, Qt::darkYellow), + ElementsAre(_, _, _, Qt::darkYellow))); +} + +TEST_F(ListModelEditor, DontRenamePropertyIfColumnNameExists) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "value2"); + + ASSERT_THAT(model.propertyNames(), ElementsAre("image", "name", "value", "value2")); +} + +TEST_F(ListModelEditor, DontRenameColumnIfColumnNameExists) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "value2"); + + ASSERT_THAT(headerLabels(model), ElementsAre("image", "name", "value", "value2")); +} + +TEST_F(ListModelEditor, DontRenameColumnIfColumnNameExistsDoesNotChangeDisplayValues) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "value2"); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42), + ElementsAre("pic.png", "bar", 4, IsInvalid()), + ElementsAre("pic.png", "poo", 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, DontRenameColumnIfColumnNameExistsDoesNotChangeProperties) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "value2"); + + ASSERT_THAT(properties(), + ElementsAre(UnorderedElementsAre(IsVariantProperty("name", "foo"), + IsVariantProperty("value", 1), + IsVariantProperty("value2", 42)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("name", "bar"), + IsVariantProperty("value", 4)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("name", "poo"), + IsVariantProperty("value", 111)))); +} + +TEST_F(ListModelEditor, RenamePropertyButDontChangeOrder) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "mood"); + + ASSERT_THAT(model.propertyNames(), ElementsAre("image", "mood", "value", "value2")); +} + +TEST_F(ListModelEditor, RenameColumnButDontChangeOrder) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "mood"); + + ASSERT_THAT(headerLabels(model), ElementsAre("image", "mood", "value", "value2")); +} + +TEST_F(ListModelEditor, RenameColumnButDontChangeOrderDisplayValues) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "mood"); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42), + ElementsAre("pic.png", "bar", 4, IsInvalid()), + ElementsAre("pic.png", "poo", 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, RenameColumnButDontChangeOrderProperies) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "mood"); + + ASSERT_THAT(properties(), + ElementsAre(UnorderedElementsAre(IsVariantProperty("mood", "foo"), + IsVariantProperty("value", 1), + IsVariantProperty("value2", 42)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("mood", "bar"), + IsVariantProperty("value", 4)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("mood", "poo"), + IsVariantProperty("value", 111)))); +} + +TEST_F(ListModelEditor, RemoveColumnAfterRenameColumn) +{ + model.setListModel(listModelNode); + model.renameColumn(1, "mood"); + + model.removeColumn(1); + + ASSERT_THAT(properties(), + ElementsAre(UnorderedElementsAre(IsVariantProperty("value", 1), + IsVariantProperty("value2", 42)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("value", 4)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("value", 111)))); +} + +TEST_F(ListModelEditor, ChangeValueAfterRenameColumn) +{ + model.setListModel(listModelNode); + model.renameColumn(1, "mood"); + + model.setValue(1, 1, "taaa"); + + ASSERT_THAT(properties(), + ElementsAre(UnorderedElementsAre(IsVariantProperty("mood", "foo"), + IsVariantProperty("value", 1), + IsVariantProperty("value2", 42)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("mood", "taaa"), + IsVariantProperty("value", 4)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("mood", "poo"), + IsVariantProperty("value", 111)))); +} + +TEST_F(ListModelEditor, RemovePropertyAfterRenameColumn) +{ + model.setListModel(listModelNode); + model.renameColumn(1, "mood"); + + model.setValue(1, 1, {}); + + ASSERT_THAT(properties(), + ElementsAre(UnorderedElementsAre(IsVariantProperty("mood", "foo"), + IsVariantProperty("value", 1), + IsVariantProperty("value2", 42)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("value", 4)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("mood", "poo"), + IsVariantProperty("value", 111)))); +} + +TEST_F(ListModelEditor, RenameToPrecedingProperty) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "alpha"); + + ASSERT_THAT(model.propertyNames(), ElementsAre("alpha", "image", "value", "value2")); +} + +TEST_F(ListModelEditor, RenameToPrecedingColumn) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "alpha"); + + ASSERT_THAT(headerLabels(model), ElementsAre("alpha", "image", "value", "value2")); +} + +TEST_F(ListModelEditor, RenameToPrecedingColumnDisplayValues) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "alpha"); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre("foo", IsInvalid(), 1, 42), + ElementsAre("bar", "pic.png", 4, IsInvalid()), + ElementsAre("poo", "pic.png", 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, RenameToPrecedingColumnProperties) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "alpha"); + + ASSERT_THAT(properties(), + ElementsAre(UnorderedElementsAre(IsVariantProperty("alpha", "foo"), + IsVariantProperty("value", 1), + IsVariantProperty("value2", 42)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("alpha", "bar"), + IsVariantProperty("value", 4)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("alpha", "poo"), + IsVariantProperty("value", 111)))); +} + +TEST_F(ListModelEditor, RenameToFollowingProperty) +{ + model.setListModel(listModelNode); + + model.renameColumn(2, "zoo"); + + ASSERT_THAT(model.propertyNames(), ElementsAre("image", "name", "value2", "zoo")); +} + +TEST_F(ListModelEditor, RenameToFollowingColumn) +{ + model.setListModel(listModelNode); + + model.renameColumn(2, "zoo"); + + ASSERT_THAT(headerLabels(model), ElementsAre("image", "name", "value2", "zoo")); +} + +TEST_F(ListModelEditor, RenameToFollowingColumnDisplayValues) +{ + model.setListModel(listModelNode); + + model.renameColumn(2, "zoo"); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 42, 1), + ElementsAre("pic.png", "bar", IsInvalid(), 4), + ElementsAre("pic.png", "poo", IsInvalid(), 111))); +} + +TEST_F(ListModelEditor, RenameToFollowingColumnProperties) +{ + model.setListModel(listModelNode); + + model.renameColumn(2, "zoo"); + + ASSERT_THAT(properties(), + ElementsAre(UnorderedElementsAre(IsVariantProperty("name", "foo"), + IsVariantProperty("zoo", 1), + IsVariantProperty("value2", 42)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("name", "bar"), + IsVariantProperty("zoo", 4)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("name", "poo"), + IsVariantProperty("zoo", 111)))); +} + +TEST_F(ListModelEditor, RenamePropertiesWithInvalidValue) +{ + model.setListModel(listModelNode); + + model.renameColumn(0, "mood"); + + ASSERT_THAT(properties(), + ElementsAre(UnorderedElementsAre(IsVariantProperty("name", "foo"), + IsVariantProperty("value", 1), + IsVariantProperty("value2", 42)), + UnorderedElementsAre(IsVariantProperty("mood", "pic.png"), + IsVariantProperty("name", "bar"), + IsVariantProperty("value", 4)), + UnorderedElementsAre(IsVariantProperty("mood", "pic.png"), + IsVariantProperty("name", "poo"), + IsVariantProperty("value", 111)))); +} + +TEST_F(ListModelEditor, ChangeValueAfterRenamePropertiesWithInvalidValue) +{ + model.setListModel(listModelNode); + model.renameColumn(0, "mood"); + + model.setValue(0, 0, "haaa"); + + ASSERT_THAT(properties(), + ElementsAre(UnorderedElementsAre(IsVariantProperty("mood", "haaa"), + IsVariantProperty("name", "foo"), + IsVariantProperty("value", 1), + IsVariantProperty("value2", 42)), + UnorderedElementsAre(IsVariantProperty("mood", "pic.png"), + IsVariantProperty("name", "bar"), + IsVariantProperty("value", 4)), + UnorderedElementsAre(IsVariantProperty("mood", "pic.png"), + IsVariantProperty("name", "poo"), + IsVariantProperty("value", 111)))); +} + +TEST_F(ListModelEditor, RemoveLastRow) +{ + model.setListModel(emptyListModelNode); + model.addColumn("mood"); + model.addRow(); + + model.removeRow(0); + + ASSERT_THAT(displayValues(), IsEmpty()); +} + +TEST_F(ListModelEditor, RemoveLastColumn) +{ + model.setListModel(emptyListModelNode); + model.addColumn("mood"); + model.addRow(); + + model.removeColumn(0); + + ASSERT_THAT(displayValues(), ElementsAre(IsEmpty())); +} + +TEST_F(ListModelEditor, RemoveLastEmptyColumn) +{ + model.setListModel(emptyListModelNode); + model.addColumn("mood"); + model.addRow(); + model.removeRow(0); + + model.removeColumn(0); + + ASSERT_THAT(displayValues(), IsEmpty()); +} + +TEST_F(ListModelEditor, RemoveLastEmptyRow) +{ + model.setListModel(emptyListModelNode); + model.addColumn("mood"); + model.addRow(); + model.removeColumn(0); + + model.removeRow(0); + + ASSERT_THAT(displayValues(), IsEmpty()); +} + +} // namespace diff --git a/tests/unit/unittest/mockfilesystem.h b/tests/unit/unittest/mockfilesystem.h index 688edbcae57..fff5758cc62 100644 --- a/tests/unit/unittest/mockfilesystem.h +++ b/tests/unit/unittest/mockfilesystem.h @@ -34,4 +34,5 @@ class MockFileSystem : public ClangBackEnd::FileSystemInterface public: MOCK_CONST_METHOD1(directoryEntries, ClangBackEnd::FilePathIds(const QString &directoryPath)); MOCK_CONST_METHOD1(lastModified, long long(ClangBackEnd::FilePathId filePathId)); + MOCK_METHOD1(remove, void(const ClangBackEnd::FilePathIds &filePathIds)); }; diff --git a/tests/unit/unittest/mocklistmodeleditorview.h b/tests/unit/unittest/mocklistmodeleditorview.h new file mode 100644 index 00000000000..6bec164f338 --- /dev/null +++ b/tests/unit/unittest/mocklistmodeleditorview.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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 + +class MockListModelEditorView : public QmlDesigner::AbstractView +{ +public: + MOCK_METHOD(void, + variantPropertiesChanged, + (const QList &propertyList, + PropertyChangeFlags propertyChange), + (override)); + MOCK_METHOD(void, nodeCreated, (const QmlDesigner::ModelNode &createdNode), (override)); + MOCK_METHOD(void, + nodeReparented, + (const QmlDesigner::ModelNode &node, + const QmlDesigner::NodeAbstractProperty &newPropertyParent, + const QmlDesigner::NodeAbstractProperty &oldPropertyParent, + AbstractView::PropertyChangeFlags propertyChange), + (override)); + MOCK_METHOD(void, + propertiesRemoved, + (const QList &propertyList), + (override)); + + MOCK_METHOD(void, + nodeRemoved, + (const QmlDesigner::ModelNode &removedNode, + const QmlDesigner::NodeAbstractProperty &parentProperty, + AbstractView::PropertyChangeFlags propertyChange), + (override)); +}; diff --git a/tests/unit/unittest/mockprecompiledheaderstorage.h b/tests/unit/unittest/mockprecompiledheaderstorage.h index de755e07eb7..5dddf97fce8 100644 --- a/tests/unit/unittest/mockprecompiledheaderstorage.h +++ b/tests/unit/unittest/mockprecompiledheaderstorage.h @@ -57,4 +57,5 @@ public: MOCK_CONST_METHOD1( fetchTimeStamps, ClangBackEnd::PrecompiledHeaderTimeStamps(ClangBackEnd::ProjectPartId projectPartId)); + MOCK_CONST_METHOD0(fetchAllPchPaths, ClangBackEnd::FilePaths()); }; diff --git a/tests/unit/unittest/mocksqlitereadstatement.cpp b/tests/unit/unittest/mocksqlitereadstatement.cpp index 1a27eea50f0..1f5bc53759b 100644 --- a/tests/unit/unittest/mocksqlitereadstatement.cpp +++ b/tests/unit/unittest/mocksqlitereadstatement.cpp @@ -107,6 +107,12 @@ FilePathIds MockSqliteReadStatement::values(std::size_ return valuesReturnFilePathIds(reserveSize, projectPartId); } +template<> +ClangBackEnd::FilePaths MockSqliteReadStatement::values(std::size_t reserveSize) +{ + return valuesReturnFilePaths(reserveSize); +} + template <> std::vector MockSqliteReadStatement::values(std::size_t reserveSize) { diff --git a/tests/unit/unittest/mocksqlitereadstatement.h b/tests/unit/unittest/mocksqlitereadstatement.h index 04e1f8effca..11a7b126e57 100644 --- a/tests/unit/unittest/mocksqlitereadstatement.h +++ b/tests/unit/unittest/mocksqlitereadstatement.h @@ -113,6 +113,8 @@ public: MOCK_METHOD1(valueReturnFilePath, Utils::optional(int)); + MOCK_METHOD1(valuesReturnFilePaths, ClangBackEnd::FilePaths(std::size_t)); + MOCK_METHOD1(valueReturnSmallString, Utils::optional(int)); @@ -233,6 +235,9 @@ template<> FilePathIds MockSqliteReadStatement::values(std::size_t reserveSize, const int &projectPartId); +template<> +ClangBackEnd::FilePaths MockSqliteReadStatement::values(std::size_t reserveSize); + template <> std::vector MockSqliteReadStatement::values(std::size_t reserveSize); diff --git a/tests/unit/unittest/pchtaskqueue-test.cpp b/tests/unit/unittest/pchtaskqueue-test.cpp index 598a2a5132a..6a4a2211d89 100644 --- a/tests/unit/unittest/pchtaskqueue-test.cpp +++ b/tests/unit/unittest/pchtaskqueue-test.cpp @@ -25,14 +25,18 @@ #include "googletest.h" +#include "mockfilesystem.h" #include "mockpchcreator.h" #include "mockprecompiledheaderstorage.h" #include "mocksqlitetransactionbackend.h" #include "mocktaskscheduler.h" #include "testenvironment.h" +#include #include #include +#include +#include namespace { @@ -45,9 +49,26 @@ using ClangBackEnd::SlotUsage; class PchTaskQueue : public testing::Test { protected: + ClangBackEnd::FilePathId filePathId(Utils::SmallStringView path) + { + return filePathCache.filePathId(ClangBackEnd::FilePathView{path}); + } + + ClangBackEnd::FilePathIds filePathIds(const Utils::PathStringVector &paths) + { + return filePathCache.filePathIds(Utils::transform(paths, [](const Utils::PathString &path) { + return ClangBackEnd::FilePathView(path); + })); + } + +protected: + Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; + ClangBackEnd::RefactoringDatabaseInitializer initializer{database}; + ClangBackEnd::FilePathCaching filePathCache{database}; NiceMock> mockSytemPchTaskScheduler; NiceMock> mockProjectPchTaskScheduler; NiceMock mockPrecompiledHeaderStorage; + NiceMock mockFileSystem; MockSqliteTransactionBackend mockSqliteTransactionBackend; NiceMock> mockSetProgressCallback; ClangBackEnd::ProgressCounter progressCounter{mockSetProgressCallback.AsStdFunction()}; @@ -57,7 +78,9 @@ protected: progressCounter, mockPrecompiledHeaderStorage, mockSqliteTransactionBackend, - testEnvironment}; + testEnvironment, + mockFileSystem, + filePathCache}; IncludeSearchPaths systemIncludeSearchPaths{ {"/includes", 1, IncludeSearchPathType::BuiltIn}, {"/other/includes", 2, IncludeSearchPathType::System}}; @@ -390,4 +413,91 @@ TEST_F(PchTaskQueue, DeleteSystemPchEntryInDatabaseIfNoPchIsGenerated) tasks.front()(mockPchCreator); } +TEST_F(PchTaskQueue, DontDeleteUnusedPchsIfSystemTaskAreProcessed) +{ + QString pchsDirectory{testEnvironment.pchBuildDirectory()}; + ON_CALL(mockSytemPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 1})); + ON_CALL(mockProjectPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 0})); + ON_CALL(mockFileSystem, directoryEntries(Eq(pchsDirectory))) + .WillByDefault(Return(filePathIds({"/tmp/foo", "/tmp/bar"}))); + ON_CALL(mockPrecompiledHeaderStorage, fetchAllPchPaths()) + .WillByDefault(Return(ClangBackEnd::FilePaths{"/tmp/foo", "/tmp/poo"})); + + EXPECT_CALL(mockFileSystem, remove(_)).Times(0); + + queue.processEntries(); +} + +TEST_F(PchTaskQueue, DontDeleteUnusedPchsIfProjectTaskAreProcessed) +{ + QString pchsDirectory{testEnvironment.pchBuildDirectory()}; + ON_CALL(mockSytemPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 0})); + ON_CALL(mockProjectPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 1})); + ON_CALL(mockFileSystem, directoryEntries(Eq(pchsDirectory))) + .WillByDefault(Return(filePathIds({"/tmp/foo", "/tmp/bar"}))); + ON_CALL(mockPrecompiledHeaderStorage, fetchAllPchPaths()) + .WillByDefault(Return(ClangBackEnd::FilePaths{"/tmp/foo", "/tmp/poo"})); + + EXPECT_CALL(mockFileSystem, remove(_)).Times(0); + + queue.processEntries(); +} + +TEST_F(PchTaskQueue, DontDeleteUnusedPchsIfSystemTaskIsAdded) +{ + QString pchsDirectory{testEnvironment.pchBuildDirectory()}; + ON_CALL(mockSytemPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 0})); + ON_CALL(mockProjectPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 0})); + ON_CALL(mockFileSystem, directoryEntries(Eq(pchsDirectory))) + .WillByDefault(Return(filePathIds({"/tmp/foo", "/tmp/bar"}))); + ON_CALL(mockPrecompiledHeaderStorage, fetchAllPchPaths()) + .WillByDefault(Return(ClangBackEnd::FilePaths{"/tmp/foo", "/tmp/poo"})); + queue.addSystemPchTasks({systemTask1}); + + EXPECT_CALL(mockFileSystem, remove(_)).Times(0); + + queue.processEntries(); +} + +TEST_F(PchTaskQueue, DontDeleteUnusedPchsIfProjectTaskIsAdded) +{ + QString pchsDirectory{testEnvironment.pchBuildDirectory()}; + ON_CALL(mockSytemPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 0})); + ON_CALL(mockProjectPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 0})); + ON_CALL(mockFileSystem, directoryEntries(Eq(pchsDirectory))) + .WillByDefault(Return(filePathIds({"/tmp/foo", "/tmp/bar"}))); + ON_CALL(mockPrecompiledHeaderStorage, fetchAllPchPaths()) + .WillByDefault(Return(ClangBackEnd::FilePaths{"/tmp/foo", "/tmp/poo"})); + queue.addProjectPchTasks({projectTask1}); + + EXPECT_CALL(mockFileSystem, remove(_)).Times(0); + + queue.processEntries(); +} + +TEST_F(PchTaskQueue, DeleteUnusedPchs) +{ + QString pchsDirectory{testEnvironment.pchBuildDirectory()}; + ON_CALL(mockSytemPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 0})); + ON_CALL(mockProjectPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 0})); + ON_CALL(mockFileSystem, directoryEntries(Eq(pchsDirectory))) + .WillByDefault(Return(filePathIds({ + "/tmp/foo", + "/tmp/bar", + "/tmp/hoo", + "/tmp/too", + }))); + ON_CALL(mockPrecompiledHeaderStorage, fetchAllPchPaths()) + .WillByDefault(Return(ClangBackEnd::FilePaths{ + "/tmp/foo", + "/tmp/poo", + "/tmp/too", + })); + + EXPECT_CALL(mockFileSystem, + remove(UnorderedElementsAre(filePathId("/tmp/bar"), filePathId("/tmp/hoo")))); + + queue.processEntries(); +} + } // namespace diff --git a/tests/unit/unittest/precompiledheaderstorage-test.cpp b/tests/unit/unittest/precompiledheaderstorage-test.cpp index 8b3727decd8..77574a28b91 100644 --- a/tests/unit/unittest/precompiledheaderstorage-test.cpp +++ b/tests/unit/unittest/precompiledheaderstorage-test.cpp @@ -53,6 +53,7 @@ protected: MockSqliteReadStatement &fetchPrecompiledHeaderStatement = storage.fetchPrecompiledHeaderStatement; MockSqliteReadStatement &fetchPrecompiledHeadersStatement = storage.fetchPrecompiledHeadersStatement; MockSqliteReadStatement &fetchTimeStampsStatement = storage.fetchTimeStampsStatement; + MockSqliteReadStatement &fetchAllPchPathsStatement = storage.fetchAllPchPathsStatement; }; TEST_F(PrecompiledHeaderStorage, UseTransaction) @@ -458,6 +459,32 @@ TEST_F(PrecompiledHeaderStorage, FetchTimeStampsBusy) storage.fetchTimeStamps(23); } +TEST_F(PrecompiledHeaderStorage, FetchAllPchPaths) +{ + InSequence s; + + EXPECT_CALL(database, deferredBegin()); + EXPECT_CALL(fetchAllPchPathsStatement, valuesReturnFilePaths(_)); + EXPECT_CALL(database, commit()); + + storage.fetchAllPchPaths(); +} + +TEST_F(PrecompiledHeaderStorage, FetchAllPchPathsIsBusy) +{ + InSequence s; + + EXPECT_CALL(database, deferredBegin()); + EXPECT_CALL(fetchAllPchPathsStatement, valuesReturnFilePaths(_)) + .WillOnce(Throw(Sqlite::StatementIsBusy{""})); + EXPECT_CALL(database, rollback()); + EXPECT_CALL(database, deferredBegin()); + EXPECT_CALL(fetchAllPchPathsStatement, valuesReturnFilePaths(_)); + EXPECT_CALL(database, commit()); + + storage.fetchAllPchPaths(); +} + class PrecompiledHeaderStorageSlowTest : public testing::Test { protected: @@ -478,4 +505,18 @@ TEST_F(PrecompiledHeaderStorageSlowTest, NoFetchTimeStamps) Field(&ClangBackEnd::PrecompiledHeaderTimeStamps::system, Eq(33)))); } +TEST_F(PrecompiledHeaderStorageSlowTest, FetchAllPchPaths) +{ + storage.insertProjectPrecompiledHeader(11, "/tmp/yi", 22); + storage.insertProjectPrecompiledHeader(12, "/tmp/er", 22); + storage.insertSystemPrecompiledHeaders({11, 12}, "/tmp/se", 33); + storage.insertSystemPrecompiledHeaders({13}, "/tmp/wu", 33); + storage.insertProjectPrecompiledHeader(13, "/tmp/san", 22); + + auto filePathIds = storage.fetchAllPchPaths(); + + ASSERT_THAT(filePathIds, + UnorderedElementsAre("/tmp/er", "/tmp/san", "/tmp/se", "/tmp/wu", "/tmp/yi")); +} + } // namespace diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index e9a96ace5f0..3917237d0bf 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -1,4 +1,5 @@ INCLUDEPATH += ../mockup +INCLUDEPATH += ../mockup/qmldesigner/designercore/include QT += core network testlib widgets CONFIG += console c++14 testcase @@ -38,8 +39,11 @@ CONFIG(release, debug|release):QMAKE_LFLAGS += -Wl,--strip-debug } gcc:!clang: QMAKE_CXXFLAGS += -Wno-noexcept-type -msvc: QMAKE_CXXFLAGS += /bigobj /wd4267 /wd4141 /wd4146 +msvc{ +QMAKE_CXXFLAGS += /bigobj /wd4267 /wd4141 /wd4146 /wd4624 +QMAKE_LFLAGS += /INCREMENTAL +} # create fake CppTools.json for the mime type definitions dependencyList = "\"Dependencies\" : []" cpptoolsjson.input = $$PWD/../../../src/plugins/cpptools/CppTools.json.in @@ -65,6 +69,7 @@ SOURCES += \ gtest-qt-printing.cpp \ lastchangedrowid-test.cpp \ lineprefixer-test.cpp \ + listmodeleditor-test.cpp \ locatorfilter-test.cpp \ mimedatabase-utilities.cpp \ pchmanagerclientserverinprocess-test.cpp \ @@ -132,18 +137,15 @@ SOURCES += \ sqlstatementbuilder-test.cpp \ createtablesqlstatementbuilder-test.cpp -!isEmpty(QTC_UNITTEST_BUILD_CPP_PARSER):matchingtext-test.cpp +!isEmpty(QTC_UNITTEST_BUILD_CPP_PARSER):SOURCES += matchingtext-test.cpp !isEmpty(LIBCLANG_LIBS) { SOURCES += \ - activationsequencecontextprocessor-test.cpp \ - activationsequenceprocessor-test.cpp \ chunksreportedmonitor.cpp \ clangasyncjob-base.cpp \ clangcodecompleteresults-test.cpp \ clangcodemodelserver-test.cpp \ clangcompletecodejob-test.cpp \ - clangcompletioncontextanalyzer-test.cpp \ clangdiagnosticfilter-test.cpp \ clangdocumentprocessors-test.cpp \ clangdocumentprocessor-test.cpp \ @@ -183,6 +185,12 @@ SOURCES += \ unsavedfile-test.cpp \ utf8positionfromlinecolumn-test.cpp \ readexporteddiagnostics-test.cpp + +!isEmpty(QTC_UNITTEST_BUILD_CPP_PARSER):SOURCE += \ + clangcompletioncontextanalyzer-test.cpp \ + activationsequencecontextprocessor-test.cpp \ + activationsequenceprocessor-test.cpp + } !isEmpty(LIBTOOLING_LIBS) { @@ -198,7 +206,6 @@ SOURCES += \ refactoringclientserverinprocess-test.cpp \ refactoringclient-test.cpp \ refactoringcompilationdatabase-test.cpp \ - refactoringengine-test.cpp \ refactoringserver-test.cpp \ sourcerangeextractor-test.cpp \ symbolindexing-test.cpp \ @@ -207,6 +214,9 @@ SOURCES += \ usedmacrocollector-test.cpp \ builddependencycollector-test.cpp \ tokenprocessor-test.cpp + +!isEmpty(QTC_UNITTEST_BUILD_CPP_PARSER):SOURCES += refactoringengine-test.cpp + } !isEmpty(CLANGFORMAT_LIBS) { @@ -237,6 +247,7 @@ HEADERS += \ mockclangpathwatcher.h \ mockclangpathwatchernotifier.h \ mockfilesystem.h \ + mocklistmodeleditorview.h \ mockpchcreator.h \ mockpchmanagerclient.h \ mockpchmanagernotifier.h \ @@ -288,7 +299,11 @@ HEADERS += \ mockbuilddependencygenerator.h \ mockpchtasksmerger.h \ mockpchtaskqueue.h \ - mockpchtaskgenerator.h + mockpchtaskgenerator.h \ + ../mockup/qmldesigner/designercore/include/nodeinstanceview.h \ + ../mockup/qmldesigner/designercore/include/rewriterview.h \ + ../mockup/qmldesigner/designercore/include/itemlibraryitem.h + !isEmpty(LIBCLANG_LIBS) { HEADERS += \