diff --git a/README.md b/README.md index 1a3456cdb2d..55ce5919e68 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Prerequisites: * CMake * Ninja (recommended) -The installed toolchains have to match the one Qt was compiled with. +The used toolchain has to be compatible with the one Qt was compiled with. ### Linux and macOS diff --git a/doc/qtdesignstudio/images/qtquick-text-editor.png b/doc/qtdesignstudio/images/qtquick-text-editor.png index ad1cb7a9ae6..0d1acf87f0f 100644 Binary files a/doc/qtdesignstudio/images/qtquick-text-editor.png and b/doc/qtdesignstudio/images/qtquick-text-editor.png differ diff --git a/doc/qtdesignstudio/src/views/qtquick-text-editor.qdoc b/doc/qtdesignstudio/src/views/qtquick-text-editor.qdoc index f60821ca9aa..4140f3956b5 100644 --- a/doc/qtdesignstudio/src/views/qtquick-text-editor.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-text-editor.qdoc @@ -43,6 +43,10 @@ You can also view and modify other types of text files. + To open the search dialog in \uicontrol {Text Editor}, go to + \uicontrol Edit > \uicontrol {Find/Replace} > \uicontrol {Find/Replace}. + You can also select \key Ctrl + \key {F}. + You can use a subset of the functions available in the \l{Writing Code}{Edit mode}: diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.cpp index c1cbca6f5ee..5f448b90da5 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.cpp @@ -29,18 +29,21 @@ #include #endif +#include +#include +#include #include #include -#include -#include #include -#include +#include +#include namespace Import3D { -void import3D(const QString &sourceAsset, const QString &outDir, int exitId, const QString &options) +void import3D(const QString &sourceAsset, const QString &outDir, const QString &options) { + QString errorStr; #ifdef IMPORT_QUICK3D_ASSETS QScopedPointer importer {new QSSGAssetImportManager}; @@ -48,32 +51,35 @@ void import3D(const QString &sourceAsset, const QString &outDir, int exitId, con QJsonDocument optDoc = QJsonDocument::fromJson(options.toUtf8(), &error); if (!optDoc.isNull() && optDoc.isObject()) { - QString errorStr; QJsonObject optObj = optDoc.object(); if (importer->importFile(sourceAsset, outDir, optObj.toVariantMap(), &errorStr) != QSSGAssetImportManager::ImportState::Success) { - qWarning() << __FUNCTION__ << "Failed to import 3D asset" - << sourceAsset << "with error:" << errorStr; - } else { - // Allow little time for file operations to finish - QTimer::singleShot(2000, nullptr, [exitId]() { - qApp->exit(exitId); - }); - return; } } else { - qWarning() << __FUNCTION__ << "Failed to parse import options:" << error.errorString(); + errorStr = QObject::tr("Failed to parse import options: %1").arg(error.errorString()); } #else + errorStr = QObject::tr("QtQuick3D is not available."); Q_UNUSED(sourceAsset) Q_UNUSED(outDir) - Q_UNUSED(exitId) Q_UNUSED(options) - qWarning() << __FUNCTION__ << "Failed to parse import options, Quick3DAssetImport not available"; #endif - QTimer::singleShot(0, nullptr, [exitId]() { - // Negative exitId means import failure - qApp->exit(-exitId); + if (!errorStr.isEmpty()) { + qWarning() << __FUNCTION__ << "Failed to import asset:" << errorStr << outDir; + + // Write the error into a file in outDir to pass it to creator side + QString errorFileName = outDir + "/__error.log"; + QFile file(errorFileName); + if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + QTextStream out(&file); + out << errorStr; + file.close(); + } + } + + // Allow little time for file operations to finish + QTimer::singleShot(2000, nullptr, []() { + qApp->exit(0); }); } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.h index eb2e4731724..3f53ef33c79 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.h @@ -29,5 +29,5 @@ namespace Import3D { - void import3D(const QString &sourceAsset, const QString &outDir, int id, const QString &options); + void import3D(const QString &sourceAsset, const QString &outDir, const QString &options); }; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp index 8b6135c6222..5a7e0d7dd8b 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp @@ -228,10 +228,9 @@ int internalMain(QGuiApplication *application) if (application->arguments().at(1) == "--import3dAsset") { QString sourceAsset = application->arguments().at(2); QString outDir = application->arguments().at(3); - int exitId = application->arguments().at(4).toInt(); - QString options = application->arguments().at(5); + QString options = application->arguments().at(4); - Import3D::import3D(sourceAsset, outDir, exitId, options); + Import3D::import3D(sourceAsset, outDir, options); return application->exec(); } diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/NewProjectView.qml b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/NewProjectView.qml index e8bb58cfa76..d0544c2f67a 100644 --- a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/NewProjectView.qml +++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/NewProjectView.qml @@ -101,6 +101,9 @@ ScrollView { height: DialogValues.presetItemHeight onClicked: delegate.GridView.view.currentIndex = index + onDoubleClicked: { + BackendApi.accept() + } background: Rectangle { id: delegateBackground diff --git a/src/plugins/clangcodemodel/clangutils.cpp b/src/plugins/clangcodemodel/clangutils.cpp index e906d6baf6f..466a062644b 100644 --- a/src/plugins/clangcodemodel/clangutils.cpp +++ b/src/plugins/clangcodemodel/clangutils.cpp @@ -379,6 +379,8 @@ GenerateCompilationDbResult generateCompilationDB(const CppEditor::ProjectInfo:: { QTC_ASSERT(!baseDir.isEmpty(), return GenerateCompilationDbResult(QString(), QCoreApplication::translate("ClangUtils", "Could not retrieve build directory."))); + QTC_ASSERT(projectInfo, return GenerateCompilationDbResult(QString(), + "Could not retrieve project info.")); QTC_CHECK(baseDir.ensureWritableDir()); QFile compileCommandsFile(baseDir.toString() + "/compile_commands.json"); const bool fileOpened = compileCommandsFile.open(QIODevice::WriteOnly | QIODevice::Truncate); diff --git a/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp b/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp index 0629273d968..093e5f24ac7 100644 --- a/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp +++ b/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp @@ -128,7 +128,12 @@ mergeTools(std::vector> &sdkTools, std::unique_ptr userTool = std::move(userTools[0]); userTools.erase(std::begin(userTools)); - int userToolIndex = Utils::indexOf(result, Utils::equal(&CMakeTool::id, userTool->id())); + int userToolIndex = Utils::indexOf(result, [&userTool](const std::unique_ptr &tool) { + // Id should be sufficient, but we have older "mis-registered" docker based items. + // Make sure that these don't override better new values from the sdk by + // also checking the actual executable. + return userTool->id() == tool->id() && userTool->cmakeExecutable() == tool->cmakeExecutable(); + }); if (userToolIndex >= 0) { // Replace the sdk tool with the user tool, so any user changes do not get lost result[userToolIndex] = std::move(userTool); diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp index a6b94660f6f..d4a4cdbe6f5 100644 --- a/src/plugins/coreplugin/manhattanstyle.cpp +++ b/src/plugins/coreplugin/manhattanstyle.cpp @@ -189,6 +189,14 @@ QRect ManhattanStyle::subElementRect(SubElement element, const QStyleOption *opt QRect ManhattanStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option, SubControl subControl, const QWidget *widget) const { +#if QT_VERSION < QT_VERSION_CHECK(6, 2, 5) + // Workaround for QTBUG-101581, can be removed when building with Qt 6.2.5 or higher + if (control == CC_ScrollBar) { + const auto scrollbar = qstyleoption_cast(option); + if (scrollbar && qint64(scrollbar->maximum) - scrollbar->minimum > INT_MAX) + return QRect(); // breaks the scrollbar, but avoids the crash + } +#endif return QProxyStyle::subControlRect(control, option, subControl, widget); } diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index b0848827634..cfa4f54f65d 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -345,12 +345,12 @@ public: searchDirsComboBox->addItem(tr("Search in PATH")); searchDirsComboBox->addItem(tr("Search in Selected Directories")); - auto searchDirsLineEdit = new QLineEdit; - searchDirsLineEdit->setText("/usr/bin;/opt"); + auto searchDirsLineEdit = new FancyLineEdit; searchDirsLineEdit->setToolTip( tr("Select the paths in the docker image that should be scanned for kit entries.")); + searchDirsLineEdit->setHistoryCompleter("DockerMounts", true); - auto searchPaths = [this, searchDirsComboBox, searchDirsLineEdit, dockerDevice] { + auto searchPaths = [searchDirsComboBox, searchDirsLineEdit, dockerDevice] { FilePaths paths; if (searchDirsComboBox->currentIndex() == 0) { paths = dockerDevice->systemEnvironment().path(); @@ -418,8 +418,10 @@ public: }.attachTo(this); searchDirsLineEdit->setVisible(false); - auto updateDirectoriesLineEdit = [this, searchDirsLineEdit](int index) { + auto updateDirectoriesLineEdit = [searchDirsLineEdit](int index) { searchDirsLineEdit->setVisible(index == 1); + if (index == 1) + searchDirsLineEdit->setFocus(); }; QObject::connect(searchDirsComboBox, qOverload(&QComboBox::activated), this, updateDirectoriesLineEdit); diff --git a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp index f55f4807d91..439faec59d1 100644 --- a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp +++ b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp @@ -419,7 +419,7 @@ void LayoutInGridLayout::fillEmptyCells() newItemNode.setVariantProperty("y", yPos); newItemNode.setVariantProperty("width", 14); newItemNode.setVariantProperty("height", 14); - newItemNode.setId(m_selectionContext.view()->generateNewId("spacer")); + newItemNode.setId(m_selectionContext.view()->model()->generateNewId("spacer")); } m_layoutedNodes.append(m_spacerNodes); } diff --git a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp index 36d30713032..402023b8289 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp @@ -241,7 +241,7 @@ void BackendModel::addNewBackend() if (!model->hasImport(import)) model->changeImports({import}, {}); - QString propertyName = m_connectionView->generateNewId(typeName); + QString propertyName = m_connectionView->model()->generateNewId(typeName); NodeMetaInfo metaInfo = model->metaInfo(typeName.toUtf8()); diff --git a/src/plugins/qmldesigner/components/eventlist/nodelistview.cpp b/src/plugins/qmldesigner/components/eventlist/nodelistview.cpp index a322d56ebc9..8569477bb5f 100644 --- a/src/plugins/qmldesigner/components/eventlist/nodelistview.cpp +++ b/src/plugins/qmldesigner/components/eventlist/nodelistview.cpp @@ -141,7 +141,7 @@ QString NodeListView::setNodeId(int internalId, const QString &id) { ModelNode node = modelNodeForInternalId(internalId); if (node.isValid()) { - QString newId = generateNewId(id); + QString newId = model()->generateNewId(id); node.setIdWithRefactoring(newId); return newId; } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index 1ff31aad632..7e22efe016f 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -142,9 +142,10 @@ void ItemLibraryAssetImporter::addInfo(const QString &infoMsg, const QString &sr emit infoReported(infoMsg, srcPath); } -void ItemLibraryAssetImporter::importProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) +void ItemLibraryAssetImporter::importProcessFinished(int exitCode, QProcess::ExitStatus exitStatus, + int importId) { - Q_UNUSED(exitStatus) + Q_UNUSED(exitCode) ++m_qmlImportFinishedCount; @@ -154,9 +155,32 @@ void ItemLibraryAssetImporter::importProcessFinished(int exitCode, QProcess::Exi return !entry || entry->state() == QProcess::NotRunning; })); - if (m_parseData.contains(-exitCode)) { - const ParseData pd = m_parseData.take(-exitCode); - addError(tr("Asset import process failed for: \"%1\".").arg(pd.sourceInfo.absoluteFilePath())); + if (m_parseData.contains(importId)) { + const ParseData &pd = m_parseData[importId]; + QString errStr; + if (exitStatus == QProcess::ExitStatus::CrashExit) { + errStr = tr("Import process crashed."); + } else { + bool unknownFail = !pd.outDir.exists() || pd.outDir.isEmpty(); + if (!unknownFail) { + QFile errorLog(pd.outDir.filePath("__error.log")); + if (errorLog.exists()) { + if (errorLog.open(QIODevice::ReadOnly)) + errStr = QString::fromUtf8(errorLog.readAll()); + else + unknownFail = true; + } + } + if (unknownFail) + errStr = tr("Import failed for unknown reason."); + } + + if (!errStr.isEmpty()) { + addError(tr("Asset import process failed: \"%1\".") + .arg(pd.sourceInfo.absoluteFilePath())); + addError(errStr); + m_parseData.remove(importId); + } } if (m_qmlImportFinishedCount == m_qmlPuppetCount) { @@ -565,15 +589,14 @@ bool ItemLibraryAssetImporter::startImportProcess(const ParseData &pd) QJsonDocument optDoc(pd.options); puppetArgs << "--import3dAsset" << pd.sourceInfo.absoluteFilePath() - << pd.outDir.absolutePath() << QString::number(pd.importId) - << QString::fromUtf8(optDoc.toJson()); + << pd.outDir.absolutePath() << QString::fromUtf8(optDoc.toJson()); QProcessUniquePointer process = puppetCreator.createPuppetProcess( "custom", {}, [&] {}, [&](int exitCode, QProcess::ExitStatus exitStatus) { - importProcessFinished(exitCode, exitStatus); + importProcessFinished(exitCode, exitStatus, pd.importId); }, puppetArgs); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h index 9abc315fbe0..83be9af5f4e 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h @@ -74,7 +74,7 @@ signals: void importFinished(); private slots: - void importProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); + void importProcessFinished(int exitCode, QProcess::ExitStatus exitStatus, int importId); void iconProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); private: @@ -125,7 +125,7 @@ private: std::vector m_qmlPuppetProcesses; int m_qmlPuppetCount = 0; int m_qmlImportFinishedCount = 0; - int m_importIdCounter = 1000000; // Use ids in range unlikely to clash with any normal process exit codes + int m_importIdCounter = 0; QHash m_parseData; QString m_progressTitle; QList m_requiredImports; diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 654a18dfc9e..735df196afb 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -910,8 +910,8 @@ ModelNode NavigatorTreeModel::handleItemLibraryShaderDrop(const QString &shaderP // Rename the node based on shader source QFileInfo fi(relPath); - newModelNode.setIdWithoutRefactoring(m_view->generateNewId(fi.baseName(), - "shader")); + newModelNode.setIdWithoutRefactoring( + m_view->model()->generateNewId(fi.baseName(), "shader")); // Passes can't have children, so move shader node under parent if (targetProperty.parentModelNode().isSubclassOf("QtQuick3D.Pass")) { BindingProperty listProp = targetNode.bindingProperty("shaders"); @@ -956,9 +956,9 @@ ModelNode NavigatorTreeModel::handleItemLibrarySoundDrop(const QString &soundPat // Rename the node based on source QFileInfo fi(relPath); - newModelNode.setIdWithoutRefactoring(m_view->generateNewId(fi.baseName(), - "soundEffect")); - } + newModelNode.setIdWithoutRefactoring( + m_view->model()->generateNewId(fi.baseName(), "soundEffect")); + } return newModelNode; } @@ -1073,7 +1073,8 @@ ModelNode NavigatorTreeModel::createTextureNode(const NodeAbstractProperty &targ // Rename the node based on source image QFileInfo fi(imagePath); - newModelNode.setIdWithoutRefactoring(m_view->generateNewId(fi.baseName(), "textureImage")); + newModelNode.setIdWithoutRefactoring( + m_view->model()->generateNewId(fi.baseName(), "textureImage")); return newModelNode; } return {}; diff --git a/src/plugins/qmldesigner/componentsplugin/addtabdesigneraction.cpp b/src/plugins/qmldesigner/componentsplugin/addtabdesigneraction.cpp index 4f08a6eb830..12ba1d433a0 100644 --- a/src/plugins/qmldesigner/componentsplugin/addtabdesigneraction.cpp +++ b/src/plugins/qmldesigner/componentsplugin/addtabdesigneraction.cpp @@ -131,7 +131,7 @@ void AddTabDesignerAction::addNewTab() tabViewModelNode.majorVersion(), tabViewModelNode.minorVersion(), propertyList); - newTabModelNode.setIdWithRefactoring(newTabModelNode.view()->generateNewId(tabName)); + newTabModelNode.setIdWithRefactoring(newTabModelNode.model()->generateNewId(tabName)); tabViewModelNode.defaultNodeAbstractProperty().reparentHere(newTabModelNode); } } diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index 89ccb2845fd..920689857a5 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -136,8 +136,6 @@ public: ModelNode modelNodeForId(const QString &id); bool hasId(const QString &id) const; - QString generateNewId(const QString &prefixName) const; - QString generateNewId(const QString &prefixName, const QString &fallbackPrefix) const; ModelNode modelNodeForInternalId(qint32 internalId) const; bool hasModelNodeForInternalId(qint32 internalId) const; diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index cb453a57566..48700376b9d 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -124,6 +124,11 @@ public: void clearMetaInfoCache(); + bool hasId(const QString &id) const; + + QString generateNewId(const QString &prefixName) const; + QString generateNewId(const QString &prefixName, const QString &fallbackPrefix) const; + protected: Model(); diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 8d02354a448..e7e5636d233 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -42,7 +42,6 @@ #include #include -#include #include #include @@ -517,52 +516,7 @@ ModelNode AbstractView::modelNodeForId(const QString &id) bool AbstractView::hasId(const QString &id) const { - return model()->d->hasId(id); -} - -QString firstCharToLower(const QString &string) -{ - QString resultString = string; - - if (!resultString.isEmpty()) - resultString[0] = resultString.at(0).toLower(); - - return resultString; -} - -QString AbstractView::generateNewId(const QString &prefixName, const QString &fallbackPrefix) const -{ - // First try just the prefixName without number as postfix, then continue with 2 and further - // as postfix until id does not already exist. - // Properties of the root node are not allowed for ids, because they are available in the - // complete context without qualification. - - int counter = 0; - - QString newBaseId = QString(QStringLiteral("%1")).arg(firstCharToLower(prefixName)); - newBaseId.remove(QRegularExpression(QStringLiteral("[^a-zA-Z0-9_]"))); - - if (!newBaseId.isEmpty()) { - QChar firstChar = newBaseId.at(0); - if (firstChar.isDigit()) - newBaseId.prepend('_'); - } else { - newBaseId = fallbackPrefix; - } - - QString newId = newBaseId; - - while (!ModelNode::isValidId(newId) || hasId(newId) || rootModelNode().hasProperty(newId.toUtf8())) { - ++counter; - newId = QString(QStringLiteral("%1%2")).arg(firstCharToLower(newBaseId)).arg(counter); - } - - return newId; -} - -QString AbstractView::generateNewId(const QString &prefixName) const -{ - return generateNewId(prefixName, QStringLiteral("element")); + return model()->hasId(id); } ModelNode AbstractView::modelNodeForInternalId(qint32 internalId) const diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 39b24186232..64a45e0a894 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -67,6 +67,8 @@ #include +#include + /*! \defgroup CoreModel */ @@ -1444,6 +1446,57 @@ bool Model::hasImport(const Import &import, bool ignoreAlias, bool allowHigherVe return false; } +bool Model::hasId(const QString &id) const +{ + return d->hasId(id); +} + +static QString firstCharToLower(const QString &string) +{ + QString resultString = string; + + if (!resultString.isEmpty()) + resultString[0] = resultString.at(0).toLower(); + + return resultString; +} + +QString Model::generateNewId(const QString &prefixName, const QString &fallbackPrefix) const +{ + // First try just the prefixName without number as postfix, then continue with 2 and further + // as postfix until id does not already exist. + // Properties of the root node are not allowed for ids, because they are available in the + // complete context without qualification. + + int counter = 0; + + QString newBaseId = QString(QStringLiteral("%1")).arg(firstCharToLower(prefixName)); + newBaseId.remove(QRegularExpression(QStringLiteral("[^a-zA-Z0-9_]"))); + + if (!newBaseId.isEmpty()) { + QChar firstChar = newBaseId.at(0); + if (firstChar.isDigit()) + newBaseId.prepend('_'); + } else { + newBaseId = fallbackPrefix; + } + + QString newId = newBaseId; + + while (!ModelNode::isValidId(newId) || hasId(newId) + || d->rootNode()->hasProperty(newId.toUtf8())) { + ++counter; + newId = QString(QStringLiteral("%1%2")).arg(firstCharToLower(newBaseId)).arg(counter); + } + + return newId; +} + +QString Model::generateNewId(const QString &prefixName) const +{ + return generateNewId(prefixName, QStringLiteral("element")); +} + bool Model::isImportPossible(const Import &import, bool ignoreAlias, bool allowHigherVersion) const { if (imports().contains(import)) diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index 288d9dadc4f..c6851548759 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -25,7 +25,6 @@ #include "modelnode.h" #include -#include #include #include #include "internalnode_p.h" @@ -144,7 +143,7 @@ QString ModelNode::id() const QString ModelNode::validId() { if (id().isEmpty()) - setIdWithRefactoring(view()->generateNewId(simplifiedTypeName())); + setIdWithRefactoring(model()->generateNewId(simplifiedTypeName())); return id(); } @@ -270,7 +269,8 @@ void ModelNode::setIdWithoutRefactoring(const QString &id) if (id == m_internalNode->id()) return; - if (view()->hasId(id)) + + if (model()->hasId(id)) throw InvalidIdException(__LINE__, __FUNCTION__, __FILE__, id.toUtf8(), InvalidIdException::DuplicateId); m_model.data()->d->changeNodeId(internalNode(), id); diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index bc5089f5305..844240f0b92 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp @@ -117,7 +117,7 @@ QmlItemNode QmlItemNode::createQmlItemNodeFromImage(AbstractView *view, const QS parentproperty.reparentHere(newQmlItemNode); QFileInfo fi(relativeImageName); - newQmlItemNode.setId(view->generateNewId(fi.baseName(), "image")); + newQmlItemNode.setId(view->model()->generateNewId(fi.baseName(), "image")); newQmlItemNode.modelNode().variantProperty("fillMode").setEnumeration("Image.PreserveAspectFit"); @@ -168,7 +168,7 @@ QmlItemNode QmlItemNode::createQmlItemNodeFromFont(AbstractView *view, metaInfo.minorVersion(), propertyPairList)); parentproperty.reparentHere(newQmlItemNode); - newQmlItemNode.setId(view->generateNewId("text", "text")); + newQmlItemNode.setId(view->model()->generateNewId("text", "text")); Q_ASSERT(newQmlItemNode.isValid()); }; diff --git a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp index 6a78e11cdba..d6f417407c1 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp @@ -335,7 +335,7 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view, if (!newQmlObjectNode.isValid()) return; - newQmlObjectNode.setId(view->generateNewId(itemLibraryEntry.name())); + newQmlObjectNode.setId(view->model()->generateNewId(itemLibraryEntry.name())); for (const auto &propertyBindingEntry : propertyBindingList) newQmlObjectNode.modelNode().bindingProperty(propertyBindingEntry.first).setExpression(propertyBindingEntry.second); diff --git a/src/plugins/qmlpreview/CMakeLists.txt b/src/plugins/qmlpreview/CMakeLists.txt index 532dd8999f2..4b537c4cf97 100644 --- a/src/plugins/qmlpreview/CMakeLists.txt +++ b/src/plugins/qmlpreview/CMakeLists.txt @@ -21,20 +21,22 @@ extend_qtc_plugin(QmlPreview tests/qmlpreviewplugin_test.cpp tests/qmlpreviewplugin_test.h ) -# check if Qt version have_qml_debug_translation_protocol -# will be introduced in Qt 6.2, but there are users -# who needs it in older but special built Qt versions aswell -string(REGEX MATCH "^[0-9]*" QT_VERSION_MAJOR ${Qt5_VERSION}) -get_target_property(qmldebugprivate_include_directories - Qt${QT_VERSION_MAJOR}::QmlPrivate - INTERFACE_INCLUDE_DIRECTORIES -) -find_file(have_qml_debug_translation_protocol - NAMES private/qqmldebugtranslationprotocol_p.h - PATHS ${qmldebugprivate_include_directories} -) -extend_qtc_plugin(QmlPreview - CONDITION have_qml_debug_translation_protocol - PUBLIC_DEPENDS Qt5::QmlPrivate - PUBLIC_DEFINES "FOUND_QML_DEBUG_TRANSLATION_PROTOCOL" -) +if(TARGET Qt${QT_VERSION_MAJOR}::QmlPrivate) + # check if Qt version have_qml_debug_translation_protocol + # will be introduced in Qt 6.2, but there are users + # who needs it in older but special built Qt versions aswell + string(REGEX MATCH "^[0-9]*" QT_VERSION_MAJOR ${Qt5_VERSION}) + get_target_property(qmldebugprivate_include_directories + Qt${QT_VERSION_MAJOR}::QmlPrivate + INTERFACE_INCLUDE_DIRECTORIES + ) + find_file(have_qml_debug_translation_protocol + NAMES private/qqmldebugtranslationprotocol_p.h + PATHS ${qmldebugprivate_include_directories} + ) + extend_qtc_plugin(QmlPreview + CONDITION have_qml_debug_translation_protocol + PUBLIC_DEPENDS Qt5::QmlPrivate + PUBLIC_DEFINES "FOUND_QML_DEBUG_TRANSLATION_PROTOCOL" + ) +endif()