diff options
5 files changed, 276 insertions, 51 deletions
diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp b/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp index ce54df3d8d..f385cc7466 100644 --- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp +++ b/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp @@ -91,6 +91,10 @@ QmlProjectItem *QmlProjectFileFormat::parseProjectFile(const Utils::FilePath &fi if (mainFileProperty.isValid()) projectItem->setMainFile(mainFileProperty.value.toString()); + const auto mainUiFileProperty = rootNode->property(QLatin1String("mainUiFile")); + if (mainUiFileProperty.isValid()) + projectItem->setMainUiFile(mainUiFileProperty.value.toString()); + const auto importPathsProperty = rootNode->property(QLatin1String("importPaths")); if (importPathsProperty.isValid()) { QStringList list = importPathsProperty.value.toStringList(); diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h index 02916555a7..d437db6114 100644 --- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h +++ b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h @@ -81,6 +81,9 @@ public: QString mainFile() const { return m_mainFile; } void setMainFile(const QString &mainFilePath) { m_mainFile = mainFilePath; } + QString mainUiFile() const { return m_mainUiFile; } + void setMainUiFile(const QString &mainUiFilePath) { m_mainUiFile = mainUiFilePath; } + bool widgetApp() const { return m_widgetApp; } void setWidgetApp(bool widgetApp) { m_widgetApp = widgetApp; } @@ -107,6 +110,7 @@ protected: QStringList m_supportedLanguages; QString m_primaryLanguage; QString m_mainFile; + QString m_mainUiFile; Utils::EnvironmentItems m_environment; QVector<QmlProjectContentItem *> m_content; // content property bool m_forceFreeType = false; diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index 37fb78f033..dc58282e0d 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -135,21 +135,38 @@ QmlProject::QmlProject(const Utils::FilePath &fileName) disconnect(m_openFileConnection); if (target && success) { - Utils::FilePaths uiFiles = getUiQmlFilesForFolder(projectDirectory() - + "/content"); - if (uiFiles.isEmpty()) - uiFiles = getUiQmlFilesForFolder(projectDirectory()); - - if (!uiFiles.isEmpty()) { - Utils::FilePath currentFile; - if (auto cd = Core::EditorManager::currentDocument()) - currentFile = cd->filePath(); - - if (currentFile.isEmpty() || !isKnownFile(currentFile)) - QTimer::singleShot(1000, [uiFiles]() { - Core::EditorManager::openEditor(uiFiles.first(), + + auto target = activeTarget(); + if (!target) + return; + + auto qmlBuildSystem = qobject_cast<QmlProjectManager::QmlBuildSystem *>( + target->buildSystem()); + + const Utils::FilePath mainUiFile = qmlBuildSystem->mainUiFilePath(); + + if (mainUiFile.completeSuffix() == "qi.qml" && mainUiFile.exists()) { + QTimer::singleShot(1000, [mainUiFile]() { + Core::EditorManager::openEditor(mainUiFile, Utils::Id()); - }); + }); + } else { + Utils::FilePaths uiFiles = getUiQmlFilesForFolder(projectDirectory() + + "/content"); + if (uiFiles.isEmpty()) + uiFiles = getUiQmlFilesForFolder(projectDirectory()); + + if (!uiFiles.isEmpty()) { + Utils::FilePath currentFile; + if (auto cd = Core::EditorManager::currentDocument()) + currentFile = cd->filePath(); + + if (currentFile.isEmpty() || !isKnownFile(currentFile)) + QTimer::singleShot(1000, [uiFiles]() { + Core::EditorManager::openEditor(uiFiles.first(), + Utils::Id()); + }); + } } } }); @@ -253,6 +270,58 @@ void QmlBuildSystem::parseProject(RefreshOptions options) } } +bool QmlBuildSystem::setFileSettingInProjectFile(const QString &setting, const Utils::FilePath &mainFilePath, const QString &oldFile) +{ + // make sure to change it also in the qmlproject file + const Utils::FilePath qmlProjectFilePath = project()->projectFilePath(); + Core::FileChangeBlocker fileChangeBlocker(qmlProjectFilePath); + const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForFilePath(qmlProjectFilePath); + TextEditor::TextDocument *document = nullptr; + if (!editors.isEmpty()) { + document = qobject_cast<TextEditor::TextDocument*>(editors.first()->document()); + if (document && document->isModified()) + if (!Core::DocumentManager::saveDocument(document)) + return false; + } + + QString fileContent; + QString error; + Utils::TextFileFormat textFileFormat; + const QTextCodec *codec = QTextCodec::codecForName("UTF-8"); // qml files are defined to be utf-8 + if (Utils::TextFileFormat::readFile(qmlProjectFilePath, codec, &fileContent, &textFileFormat, &error) + != Utils::TextFileFormat::ReadSuccess) { + qWarning() << "Failed to read file" << qmlProjectFilePath << ":" << error; + } + + const QString settingQmlCode = setting + ":"; + + QDir projectDir = project()->projectFilePath().toDir(); + projectDir.cdUp(); + const QString relativePath = projectDir.relativeFilePath(mainFilePath.toString()); + + if (fileContent.indexOf(settingQmlCode) < 0) { + QString addedText = QString("\n %1 \"%2\"\n").arg(settingQmlCode).arg(relativePath); + auto index = fileContent.lastIndexOf("}"); + fileContent.insert(index, addedText); + } else { + QString originalFileName = oldFile; + originalFileName.replace(".", "\\."); + const QRegularExpression expression(QString("%1\\s*\"(%2)\"").arg(settingQmlCode).arg(originalFileName)); + + const QRegularExpressionMatch match = expression.match(fileContent); + + fileContent.replace(match.capturedStart(1), + match.capturedLength(1), + relativePath); + } + + if (!textFileFormat.writeFile(qmlProjectFilePath, fileContent, &error)) + qWarning() << "Failed to write file" << qmlProjectFilePath << ":" << error; + + refresh(Everything); + return true; +} + void QmlBuildSystem::refresh(RefreshOptions options) { ParseGuard guard = guardParsingRun(); @@ -283,11 +352,68 @@ QString QmlBuildSystem::mainFile() const return QString(); } +QString QmlBuildSystem::mainUiFile() const +{ + if (m_projectItem) + return m_projectItem->mainUiFile(); + return QString(); +} + Utils::FilePath QmlBuildSystem::mainFilePath() const { return projectDirectory().pathAppended(mainFile()); } +Utils::FilePath QmlBuildSystem::mainUiFilePath() const +{ + return projectDirectory().pathAppended(mainUiFile()); +} + +bool QmlBuildSystem::setMainFileInProjectFile(const Utils::FilePath &newMainFilePath) +{ + + return setFileSettingInProjectFile("mainFile", newMainFilePath, mainFile()); +} + +bool QmlBuildSystem::setMainUiFileInProjectFile(const Utils::FilePath &newMainUiFilePath) +{ + return setMainUiFileInMainFile(newMainUiFilePath) + && setFileSettingInProjectFile("mainUiFile", newMainUiFilePath, mainUiFile()); +} + +bool QmlBuildSystem::setMainUiFileInMainFile(const Utils::FilePath &newMainUiFilePath) +{ + Core::FileChangeBlocker fileChangeBlocker(mainFilePath()); + const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForFilePath(mainFilePath()); + TextEditor::TextDocument *document = nullptr; + if (!editors.isEmpty()) { + document = qobject_cast<TextEditor::TextDocument*>(editors.first()->document()); + if (document && document->isModified()) + if (!Core::DocumentManager::saveDocument(document)) + return false; + } + + QString fileContent; + QString error; + Utils::TextFileFormat textFileFormat; + const QTextCodec *codec = QTextCodec::codecForName("UTF-8"); // qml files are defined to be utf-8 + if (Utils::TextFileFormat::readFile(mainFilePath(), codec, &fileContent, &textFileFormat, &error) + != Utils::TextFileFormat::ReadSuccess) { + qWarning() << "Failed to read file" << mainFilePath() << ":" << error; + } + + const QString currentMain = QString("%1 {").arg(mainUiFilePath().baseName()); + const QString newMain = QString("%1 {").arg(newMainUiFilePath.baseName()); + + if (fileContent.contains(currentMain)) + fileContent.replace(currentMain, newMain); + + if (!textFileFormat.writeFile(mainFilePath(), fileContent, &error)) + qWarning() << "Failed to write file" << mainFilePath() << ":" << error; + + return true; +} + bool QmlBuildSystem::qtForMCUs() const { if (m_projectItem) @@ -663,43 +789,10 @@ bool QmlBuildSystem::deleteFiles(Node *context, const FilePaths &filePaths) bool QmlBuildSystem::renameFile(Node * context, const FilePath &oldFilePath, const FilePath &newFilePath) { if (dynamic_cast<QmlProjectNode *>(context)) { - if (oldFilePath.endsWith(mainFile())) { - setMainFile(newFilePath.toString()); - - // make sure to change it also in the qmlproject file - const Utils::FilePath qmlProjectFilePath = project()->projectFilePath(); - Core::FileChangeBlocker fileChangeBlocker(qmlProjectFilePath); - const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForFilePath(qmlProjectFilePath); - TextEditor::TextDocument *document = nullptr; - if (!editors.isEmpty()) { - document = qobject_cast<TextEditor::TextDocument*>(editors.first()->document()); - if (document && document->isModified()) - if (!Core::DocumentManager::saveDocument(document)) - return false; - } - - QString fileContent; - QString error; - Utils::TextFileFormat textFileFormat; - const QTextCodec *codec = QTextCodec::codecForName("UTF-8"); // qml files are defined to be utf-8 - if (Utils::TextFileFormat::readFile(qmlProjectFilePath, codec, &fileContent, &textFileFormat, &error) - != Utils::TextFileFormat::ReadSuccess) { - qWarning() << "Failed to read file" << qmlProjectFilePath << ":" << error; - } - - // find the mainFile and do the file name with brackets in a capture group and mask the . with \. - QString originalFileName = oldFilePath.fileName(); - originalFileName.replace(".", "\\."); - const QRegularExpression expression(QString("mainFile:\\s*\"(%1)\"").arg(originalFileName)); - const QRegularExpressionMatch match = expression.match(fileContent); - - fileContent.replace(match.capturedStart(1), match.capturedLength(1), newFilePath.fileName()); - - if (!textFileFormat.writeFile(qmlProjectFilePath, fileContent, &error)) - qWarning() << "Failed to write file" << qmlProjectFilePath << ":" << error; - - refresh(Everything); - } + if (oldFilePath.endsWith(mainFile())) + return setMainFileInProjectFile(newFilePath); + if (oldFilePath.endsWith(mainUiFile())) + return setMainUiFileInProjectFile(newFilePath); return true; } diff --git a/src/plugins/qmlprojectmanager/qmlproject.h b/src/plugins/qmlprojectmanager/qmlproject.h index 78a30187f6..a5ce067be6 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.h +++ b/src/plugins/qmlprojectmanager/qmlproject.h @@ -77,7 +77,13 @@ public: Utils::FilePath canonicalProjectDir() const; QString mainFile() const; + QString mainUiFile() const; Utils::FilePath mainFilePath() const; + Utils::FilePath mainUiFilePath() const; + + bool setMainFileInProjectFile(const Utils::FilePath &newMainFilePath); + bool setMainUiFileInProjectFile(const Utils::FilePath &newMainUiFilePath); + bool setMainUiFileInMainFile(const Utils::FilePath &newMainUiFilePath); bool qtForMCUs() const; bool qt6Project() const; @@ -116,6 +122,10 @@ public: void parseProject(RefreshOptions options); private: + bool setFileSettingInProjectFile(const QString &setting, + const Utils::FilePath &mainFilePath, + const QString &oldFile); + std::unique_ptr<QmlProjectItem> m_projectItem; Utils::FilePath m_canonicalProjectDir; bool m_blockFilesUpdate = false; diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp index bc7b6f3bde..ff58349dc0 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp @@ -28,14 +28,20 @@ #include "qmlprojectconstants.h" #include "qmlprojectrunconfiguration.h" +#include <coreplugin/actionmanager/actionmanager.h> +#include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/fileiconprovider.h> #include <coreplugin/icore.h> #include <coreplugin/messagebox.h> +#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmanager.h> +#include <projectexplorer/projectnodes.h> +#include <projectexplorer/projecttree.h> #include <projectexplorer/runcontrol.h> #include <projectexplorer/session.h> +#include <projectexplorer/target.h> #include <qmljs/qmljsmodelmanagerinterface.h> @@ -47,6 +53,7 @@ #include <utils/infobar.h> #include <utils/qtcprocess.h> +#include <QAction> #include <QMessageBox> #include <QPushButton> #include <QTimer> @@ -197,6 +204,23 @@ void QmlProjectPlugin::openInQDSWithProject(const Utils::FilePath &filePath) } } +static QmlBuildSystem *qmlBuildSystemforFileNode(const FileNode *fileNode) +{ + if (!fileNode) + return nullptr; + + if (QmlProject *qmlProject = qobject_cast<QmlProject*>(fileNode->getProject())) { + auto target = qmlProject->activeTarget(); + if (!target) + return nullptr; + + return qobject_cast<QmlProjectManager::QmlBuildSystem *>(target->buildSystem()); + + } + + return nullptr; +} + bool QmlProjectPlugin::initialize(const QStringList &, QString *errorMessage) { Q_UNUSED(errorMessage) @@ -206,6 +230,7 @@ bool QmlProjectPlugin::initialize(const QStringList &, QString *errorMessage) if (!qmlDesignerEnabled()) { connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged, + this, [this](Core::IEditor *editor) { QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance(); @@ -258,6 +283,95 @@ bool QmlProjectPlugin::initialize(const QStringList &, QString *errorMessage) ProjectManager::registerProjectType<QmlProject>(QmlJSTools::Constants::QMLPROJECT_MIMETYPE); Core::FileIconProvider::registerIconOverlayForSuffix(":/qmlproject/images/qmlproject.png", "qmlproject"); + + if (QmlProject::isQtDesignStudio()) { + Core::ActionContainer *menu = Core::ActionManager::actionContainer( + ProjectExplorer::Constants::M_FILECONTEXT); + QAction *mainfileAction = new QAction(tr("Set as main .qml file"), this); + mainfileAction->setEnabled(false); + + connect(mainfileAction, &QAction::triggered, this, []() { + const Node *currentNode = ProjectTree::currentNode(); + if (!currentNode || !currentNode->asFileNode() + || currentNode->asFileNode()->fileType() != FileType::QML) + return; + + const Utils::FilePath file = currentNode->filePath(); + + QmlBuildSystem *buildSystem = qmlBuildSystemforFileNode(currentNode->asFileNode()); + if (buildSystem) + buildSystem->setMainFileInProjectFile(file); + }); + + menu->addAction(Core::ActionManager::registerAction( + mainfileAction, + "QmlProject.setMainFile", + Core::Context(ProjectExplorer::Constants::C_PROJECT_TREE)), + ProjectExplorer::Constants::G_FILE_OTHER); + mainfileAction->setVisible(false); + connect(ProjectTree::instance(), + &ProjectTree::currentNodeChanged, + mainfileAction, + [mainfileAction](Node *node) { + const FileNode *fileNode = node ? node->asFileNode() : nullptr; + + const bool isVisible = fileNode && fileNode->fileType() == FileType::QML + && fileNode->filePath().completeSuffix() == "qml"; + + mainfileAction->setVisible(isVisible); + + if (!isVisible) + return; + + QmlBuildSystem *buildSystem = qmlBuildSystemforFileNode(fileNode); + + if (buildSystem) + mainfileAction->setEnabled(buildSystem->mainFilePath() + != fileNode->filePath()); + }); + + QAction *mainUifileAction = new QAction(tr("Set as main .ui.qml file"), this); + mainUifileAction->setEnabled(false); + + connect(mainUifileAction, &QAction::triggered, this, []() { + const Node *currentNode = ProjectTree::currentNode(); + if (!currentNode || !currentNode->asFileNode() + || currentNode->asFileNode()->fileType() != FileType::QML) + return; + + const Utils::FilePath file = currentNode->filePath(); + + QmlBuildSystem *buildSystem = qmlBuildSystemforFileNode(currentNode->asFileNode()); + if (buildSystem) + buildSystem->setMainUiFileInProjectFile(file); + }); + + menu->addAction(Core::ActionManager::registerAction( + mainUifileAction, + "QmlProject.setMainUIFile", + Core::Context(ProjectExplorer::Constants::C_PROJECT_TREE)), + ProjectExplorer::Constants::G_FILE_OTHER); + mainUifileAction->setVisible(false); + connect(ProjectTree::instance(), + &ProjectTree::currentNodeChanged, + mainUifileAction, + [mainUifileAction](Node *node) { + const FileNode *fileNode = node ? node->asFileNode() : nullptr; + const bool isVisible = fileNode && fileNode->fileType() == FileType::QML + && fileNode->filePath().completeSuffix() == "ui.qml"; + + mainUifileAction->setVisible(isVisible); + + if (!isVisible) + return; + + QmlBuildSystem *buildSystem = qmlBuildSystemforFileNode(fileNode); + if (buildSystem) + mainUifileAction->setEnabled(buildSystem->mainUiFilePath() + != fileNode->filePath()); + }); + } + return true; } // namespace Internal |