From dd5730d2a3cec20649563ca1dc777ee32c9882a7 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 21 Apr 2023 16:36:03 +0200 Subject: [PATCH] QmlDesigner: Simplify conditional import adding Change-Id: I78e110a81f8117b711968b6be5b4241f96763c41 Reviewed-by: Miikka Heikkinen --- src/plugins/qmldesigner/CMakeLists.txt | 2 + .../components/componentcore/viewmanager.h | 2 +- .../components/edit3d/edit3dview.cpp | 27 ++---- .../itemlibrary/itemlibraryassetimporter.cpp | 82 ++++++---------- .../itemlibrary/itemlibraryassetimporter.h | 2 +- .../itemlibrary/itemlibrarywidget.cpp | 32 +++---- .../navigator/navigatortreemodel.cpp | 41 ++++---- .../designercore/model/modelutils.cpp | 93 +++++++++++++++++++ .../designercore/model/modelutils.h | 24 +++++ 9 files changed, 184 insertions(+), 121 deletions(-) create mode 100644 src/plugins/qmldesigner/designercore/model/modelutils.cpp create mode 100644 src/plugins/qmldesigner/designercore/model/modelutils.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 992f46b68bd..b9459aeb1b5 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -343,6 +343,8 @@ extend_qtc_library(QmlDesignerCore modelnodepositionstorage.cpp modeltotextmerger.cpp modeltotextmerger.h + modelutils.cpp + modelutils.h nodeabstractproperty.cpp nodelistproperty.cpp nodeproperty.cpp diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.h b/src/plugins/qmldesigner/components/componentcore/viewmanager.h index c54cf139e78..4201065f99f 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.h +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.h @@ -67,7 +67,7 @@ public: void disableWidgets(); void enableWidgets(); - void pushFileOnCrumbleBar(const Utils::FilePath &fileName); + void pushFileOnCrumbleBar(const ::Utils::FilePath &fileName); void pushInFileComponentOnCrumbleBar(const ModelNode &modelNode); void nextFileIsCalledInternally(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 9dcc2d7abc8..10d62a5dc21 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -23,6 +23,8 @@ #include "seekerslider.h" #include "theme.h" +#include + #include #include @@ -919,25 +921,12 @@ Edit3DBakeLightsAction *Edit3DView::bakeLightsAction() const void Edit3DView::addQuick3DImport() { DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); - if (document && !document->inFileComponentModelActive() && model()) { - Import qtQuick3DImport; - differenceCall(model()->possibleImports(), model()->imports(), [&](const auto &import) { - if (import.url() == "QtQuick3D") - qtQuick3DImport = import; - }); - if (!qtQuick3DImport.isEmpty()) { - if (!qtQuick3DImport.version().isEmpty() && qtQuick3DImport.majorVersion() >= 6) { - // Prefer empty version number in Qt6 and beyond - model()->changeImports({Import::createLibraryImport(qtQuick3DImport.url(), - {}, - qtQuick3DImport.alias(), - qtQuick3DImport.importPaths())}, - {}); - } else { - model()->changeImports({qtQuick3DImport}, {}); - } - return; - } + if (document && !document->inFileComponentModelActive() && model() + && Utils::addImportWithCheck( + "QtQuick3D", + [](const Import &import) { return !import.hasVersion() || import.majorVersion() >= 6; }, + model())) { + return; } Core::AsynchronousMessageBox::warning(tr("Failed to Add Import"), tr("Could not add QtQuick3D import to project.")); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index e7002ff2503..3bbe60a0f65 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -14,6 +14,8 @@ #include "rewritingexception.h" #include "viewmanager.h" +#include + #include #include @@ -300,7 +302,7 @@ bool ItemLibraryAssetImporter::preParseQuick3DAsset(const QString &file, ParseDa if (exitVal == QDialog::Accepted) overwriteFiles = dlg.selectedFiles(); if (!overwriteFiles.isEmpty()) { - overwriteFiles.append(Utils::toList(alwaysOverwrite)); + overwriteFiles.append(::Utils::toList(alwaysOverwrite)); m_overwrittenImports.insert(pd.targetDirPath, overwriteFiles); } else { addWarning(tr("No files selected for overwrite, skipping import: \"%1\".").arg(pd.assetName)); @@ -359,9 +361,8 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd) qmlInfo.append("."); qmlInfo.append(pd.assetName); qmlInfo.append('\n'); - m_requiredImports.append(Import::createLibraryImport( - QStringLiteral("%1.%2").arg(pd.targetDir.dirName(), - pd.assetName), version)); + m_requiredImports.append( + QStringLiteral("%1.%2").arg(pd.targetDir.dirName(), pd.assetName)); while (qmlIt.hasNext()) { qmlIt.next(); QFileInfo fi = QFileInfo(qmlIt.filePath()); @@ -417,11 +418,8 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd) } // Add quick3D import unless it is already added - if (impVersionMajor > 0 - && m_requiredImports.first().url() != "QtQuick3D") { - m_requiredImports.prepend(Import::createLibraryImport( - "QtQuick3D", impVersionStr)); - } + if (impVersionMajor > 0 && m_requiredImports.first() != "QtQuick3D") + m_requiredImports.prepend("QtQuick3D"); } if (impVersionMajor > 0 && impVersionMajor < 6) { pd.iconFile = iconFileName; @@ -683,66 +681,42 @@ void ItemLibraryAssetImporter::finalizeQuick3DImport() QFuture result; if (modelManager) { QmlJS::PathsAndLanguages pathToScan; - pathToScan.maybeInsert(Utils::FilePath::fromString(m_importPath)); - result = Utils::runAsync(&QmlJS::ModelManagerInterface::importScan, - modelManager->workingCopy(), pathToScan, - modelManager, true, true, true); + pathToScan.maybeInsert(::Utils::FilePath::fromString(m_importPath)); + result = ::Utils::runAsync(&QmlJS::ModelManagerInterface::importScan, + modelManager->workingCopy(), + pathToScan, + modelManager, + true, + true, + true); } // First we have to wait a while to ensure qmljs detects new files and updates its - // internal model. Then we make a non-change to the document to trigger qmljs snapshot - // update. There is an inbuilt delay before rewriter change actually updates the data - // model, so we need to wait for another moment to allow the change to take effect. - // Otherwise subsequent subcomponent manager update won't detect new imports properly. + // internal model. Then we force amend on rewriter to trigger qmljs snapshot update. QTimer *timer = new QTimer(parent()); static int counter; counter = 0; timer->callOnTimeout([this, timer, progressTitle, model, result]() { if (!isCancelled()) { - notifyProgress(++counter, progressTitle); - if (counter < 50) { + notifyProgress(++counter * 2, progressTitle); + if (counter < 49) { if (result.isCanceled() || result.isFinished()) - counter = 49; // skip to next step - } else if (counter == 50) { + counter = 48; // skip to next step + } else if (counter == 49) { QmlDesignerPlugin::instance()->documentManager().resetPossibleImports(); - model->rewriterView()->textModifier()->replace(0, 0, {}); - } else if (counter < 100) { + model->rewriterView()->forceAmend(); try { - const Imports posImports = model->possibleImports(); - const Imports currentImports = model->imports(); - Imports newImportsToAdd; - - for (auto &imp : std::as_const(m_requiredImports)) { - const bool isPos = Utils::contains(posImports, [imp](const Import &posImp) { - return posImp.url() == imp.url(); - }); - const bool isCur = Utils::contains(currentImports, [imp](const Import &curImp) { - return curImp.url() == imp.url(); - }); - if (!(isPos || isCur)) - return; - // Check again with 'contains' to ensure we insert latest version - if (!currentImports.contains(imp)) - newImportsToAdd.append(imp); - } - if (counter == 99) + RewriterTransaction transaction = model->rewriterView()->beginRewriterTransaction( + QByteArrayLiteral("ItemLibraryAssetImporter::finalizeQuick3DImport")); + bool success = Utils::addImportsWithCheck(m_requiredImports, model); + if (!success) addError(tr("Failed to insert import statement into qml document.")); - else - counter = 99; - if (!newImportsToAdd.isEmpty()) { - RewriterTransaction transaction - = model->rewriterView()->beginRewriterTransaction( - QByteArrayLiteral("ItemLibraryAssetImporter::finalizeQuick3DImport")); - - model->changeImports(newImportsToAdd, {}); - transaction.commit(); - } + transaction.commit(); } catch (const RewritingException &e) { addError(tr("Failed to update imports: %1").arg(e.description())); - counter = 99; } - } else if (counter >= 100) { + } else if (counter >= 50) { if (!m_overwrittenImports.isEmpty()) model->rewriterView()->emitCustomNotification("asset_import_update"); timer->stop(); @@ -752,7 +726,7 @@ void ItemLibraryAssetImporter::finalizeQuick3DImport() timer->stop(); } }); - timer->start(50); + timer->start(100); } else { notifyFinished(); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h index 94413af392e..a53f0c9de1b 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h @@ -107,7 +107,7 @@ private: int m_currentImportId = 0; QHash m_parseData; QString m_progressTitle; - Imports m_requiredImports; + QStringList m_requiredImports; QList m_puppetQueue; }; } // QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 127bd4af534..f98284f61d6 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -59,7 +60,7 @@ namespace QmlDesigner { static QString propertyEditorResourcesPath() { #ifdef SHARE_QML_PATH - if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + if (::Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources"; #endif return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); @@ -80,26 +81,16 @@ bool ItemLibraryWidget::eventFilter(QObject *obj, QEvent *event) ItemLibraryEntry entry = m_itemToDrag.value(); // For drag to be handled correctly, we must have the component properly imported // beforehand, so we import the module immediately when the drag starts - if (!entry.requiredImport().isEmpty()) { - // We don't know if required import is library of file import, so try both. - Import libImport = Import::createLibraryImport(entry.requiredImport()); - Import fileImport = Import::createFileImport(entry.requiredImport()); - if (!m_model->hasImport(libImport, true, true) - && !m_model->hasImport(fileImport, true, true)) { - const Imports possImports = m_model->possibleImports(); - for (const auto &possImport : possImports) { - if ((!possImport.url().isEmpty() && possImport.url() == libImport.url()) - || (!possImport.file().isEmpty() && possImport.file() == fileImport.file())) { - m_model->changeImports({possImport}, {}); - break; - } - } - } + if (!entry.requiredImport().isEmpty() + && !Utils::addImportWithCheck(entry.requiredImport(), m_model)) { + qWarning() << __FUNCTION__ << "Required import adding failed:" + << entry.requiredImport(); } if (model) { model->startDrag(m_itemLibraryModel->getMimeData(entry), - Utils::StyleHelper::dpiSpecificImageFile(entry.libraryEntryIconPath())); + ::Utils::StyleHelper::dpiSpecificImageFile( + entry.libraryEntryIconPath())); } m_itemToDrag = {}; @@ -154,7 +145,7 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache) updateSearch(); setStyleSheet(Theme::replaceCssColors( - QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); + QString::fromUtf8(::Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F5), this); connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, &ItemLibraryWidget::reloadQmlSource); @@ -176,10 +167,9 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache) {"itemLibraryIconHeight", m_itemIconSize.height()}, {"rootView", QVariant::fromValue(this)}, {"widthLimit", HORIZONTAL_LAYOUT_WIDTH_LIMIT}, - {"highlightColor", Utils::StyleHelper::notTooBrightHighlightColor()}, + {"highlightColor", ::Utils::StyleHelper::notTooBrightHighlightColor()}, {"tooltipBackend", QVariant::fromValue(m_previewTooltipBackend.get())}}); - reloadQmlSource(); } @@ -303,7 +293,7 @@ void ItemLibraryWidget::setModel(Model *model) QString ItemLibraryWidget::qmlSourcesPath() { #ifdef SHARE_QML_PATH - if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + if (::Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) return QLatin1String(SHARE_QML_PATH) + "/itemLibraryQmlSources"; #endif return Core::ICore::resourcePath("qmldesigner/itemLibraryQmlSources").toString(); diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 9f664429039..16dd2242863 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -9,23 +9,24 @@ #include "qmldesignerplugin.h" #include "assetslibrarywidget.h" +#include #include +#include +#include #include +#include +#include +#include +#include +#include #include #include #include #include -#include -#include -#include -#include -#include #include +#include #include #include -#include -#include -#include #include @@ -210,7 +211,7 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const return modelNode.displayName(); } else if (role == Qt::DecorationRole) { if (currentQmlObjectNode.hasError()) - return Utils::Icons::WARNING.icon(); + return ::Utils::Icons::WARNING.icon(); return modelNode.typeIcon(); @@ -314,14 +315,14 @@ QList NavigatorTreeModel::filteredList(const NodeListProperty &proper if (m_nameFilter.isEmpty()) { nameFilteredList = propertyNodes; } else { - nameFilteredList.append(Utils::filtered(propertyNodes, [&] (const ModelNode &arg){ + nameFilteredList.append(::Utils::filtered(propertyNodes, [&](const ModelNode &arg) { const bool value = m_nameFilteredList.contains(arg); return value; })); } if (filter) { - list.append(Utils::filtered(nameFilteredList, [] (const ModelNode &arg) { + list.append(::Utils::filtered(nameFilteredList, [](const ModelNode &arg) { const bool value = (QmlItemNode::isValidQmlItemNode(arg) || NodeHints::fromModelNode(arg).visibleInNavigator()) && arg.id() != Constants::MATERIAL_LIB_ID; return value; @@ -899,18 +900,8 @@ ModelNode NavigatorTreeModel::handleItemLibraryFontDrop(const QString &fontFamil void NavigatorTreeModel::addImport(const QString &importName) { - Import import = Import::createLibraryImport(importName); - if (!m_view->model()->hasImport(import, true, true)) { - const Imports possImports = difference(m_view->model()->possibleImports(), - m_view->model()->imports()); - for (const auto &possImport : possImports) { - if (possImport.url() == import.url()) { - import = possImport; - m_view->model()->changeImports({import}, {}); - break; - } - } - } + if (!Utils::addImportWithCheck(importName, m_view->model())) + qWarning() << __FUNCTION__ << "Adding import failed:" << importName; } bool QmlDesigner::NavigatorTreeModel::moveNodeToParent(const NodeAbstractProperty &targetProperty, @@ -1269,12 +1260,12 @@ static QList collectParents(const QList &modelNodes) } } - return Utils::toList(parents); + return ::Utils::toList(parents); } QList NavigatorTreeModel::nodesToPersistentIndex(const QList &modelNodes) { - return Utils::transform(modelNodes, [this](const ModelNode &modelNode) { + return ::Utils::transform(modelNodes, [this](const ModelNode &modelNode) { return QPersistentModelIndex(indexForModelNode(modelNode)); }); } diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.cpp b/src/plugins/qmldesigner/designercore/model/modelutils.cpp new file mode 100644 index 00000000000..2e454527b88 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/model/modelutils.cpp @@ -0,0 +1,93 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "modelutils.h" + +#include + +#include + +namespace QmlDesigner::Utils { + +namespace { + +enum class ImportError { EmptyImportName, HasAlreadyImport, NoModule }; + +::Utils::expected findImport(const QString &importName, + const std::function &predicate, + const Imports &imports, + const Imports &modules) +{ + if (importName.isEmpty()) + return ::Utils::make_unexpected(ImportError::EmptyImportName); + + auto hasName = [&](const auto &import) { + return import.url() == importName || import.file() == importName; + }; + + bool hasImport = std::any_of(imports.begin(), imports.end(), hasName); + + if (hasImport) + return ::Utils::make_unexpected(ImportError::HasAlreadyImport); + + auto foundModule = std::find_if(modules.begin(), modules.end(), [&](const Import &import) { + return hasName(import) && predicate(import); + }); + + if (foundModule == modules.end()) + return ::Utils::make_unexpected(ImportError::NoModule); + + return *foundModule; +} + +} // namespace + +bool addImportWithCheck(const QString &importName, + const std::function &predicate, + Model *model) +{ + return addImportsWithCheck({importName}, predicate, model); +} + +bool addImportWithCheck(const QString &importName, Model *model) +{ + return addImportWithCheck( + importName, [](const Import &) { return true; }, model); +} + +bool addImportsWithCheck(const QStringList &importNames, Model *model) +{ + return addImportsWithCheck( + importNames, [](const Import &) { return true; }, model); +} + +bool addImportsWithCheck(const QStringList &importNames, + const std::function &predicate, + Model *model) +{ + const Imports &imports = model->imports(); + const Imports &modules = model->possibleImports(); + + Imports importsToAdd; + importsToAdd.reserve(importNames.size()); + + for (const QString &importName : importNames) { + auto import = findImport(importName, predicate, imports, modules); + + if (import) { + importsToAdd.push_back(*import); + } else { + if (import.error() == ImportError::NoModule) + return false; + else + continue; + } + } + + if (!importsToAdd.isEmpty()) + model->changeImports(std::move(importsToAdd), {}); + + return true; +} + +} // namespace QmlDesigner::Utils diff --git a/src/plugins/qmldesigner/designercore/model/modelutils.h b/src/plugins/qmldesigner/designercore/model/modelutils.h new file mode 100644 index 00000000000..58055f39065 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/model/modelutils.h @@ -0,0 +1,24 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "qmldesignercorelib_global.h" + +#include +#include + +#include + +namespace QmlDesigner::Utils { + +QMLDESIGNERCORE_EXPORT bool addImportsWithCheck(const QStringList &importNames, + const std::function &predicate, + Model *model); +QMLDESIGNERCORE_EXPORT bool addImportsWithCheck(const QStringList &importNames, Model *model); +QMLDESIGNERCORE_EXPORT bool addImportWithCheck(const QString &importName, + const std::function &predicate, + Model *model); +QMLDESIGNERCORE_EXPORT bool addImportWithCheck(const QString &importName, Model *model); + +} // namespace QmlDesigner::Utils