diff --git a/src/plugins/qmlprojectmanager/CMakeLists.txt b/src/plugins/qmlprojectmanager/CMakeLists.txt index ab8f29bc7fb..ee8fa21c4f1 100644 --- a/src/plugins/qmlprojectmanager/CMakeLists.txt +++ b/src/plugins/qmlprojectmanager/CMakeLists.txt @@ -1,12 +1,13 @@ add_qtc_plugin(QmlProjectManager PLUGIN_CLASS QmlProjectPlugin - DEPENDS QmlJS + DEPENDS QmlJS Qt5::QuickWidgets PLUGIN_DEPENDS Core ProjectExplorer QtSupport SOURCES fileformat/filefilteritems.cpp fileformat/filefilteritems.h fileformat/qmlprojectfileformat.cpp fileformat/qmlprojectfileformat.h fileformat/qmlprojectitem.cpp fileformat/qmlprojectitem.h projectfilecontenttools.cpp projectfilecontenttools.h + qdslandingpage.cpp qdslandingpage.h qmlmainfileaspect.cpp qmlmainfileaspect.h qmlmultilanguageaspect.cpp qmlmultilanguageaspect.h qmlproject.cpp qmlproject.h diff --git a/src/plugins/qmlprojectmanager/qdslandingpage.cpp b/src/plugins/qmlprojectmanager/qdslandingpage.cpp new file mode 100644 index 00000000000..5a70036b824 --- /dev/null +++ b/src/plugins/qmlprojectmanager/qdslandingpage.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2022 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 "qdslandingpage.h" +#include "utils/algorithm.h" + +#include + +#include +#include + +namespace QmlProjectManager { +namespace Internal { + +const char QMLRESOURCEPATH[] = "qmldesigner/propertyEditorQmlSources/imports"; +const char LANDINGPAGEPATH[] = "qmldesigner/landingpage"; + +QdsLandingPage::QdsLandingPage(QWidget *parent) + : m_dialog{new QQuickWidget(parent)} +{ + setParent(m_dialog); + + const QString resourcePath = Core::ICore::resourcePath(QMLRESOURCEPATH).toString(); + const QString landingPath = Core::ICore::resourcePath(LANDINGPAGEPATH).toString(); + + qmlRegisterSingletonInstance("LandingPageApi", 1, 0, "LandingPageApi", this); + m_dialog->setResizeMode(QQuickWidget::SizeRootObjectToView); + m_dialog->engine()->addImportPath(landingPath + "/imports"); + m_dialog->engine()->addImportPath(resourcePath); + m_dialog->setSource(QUrl::fromLocalFile(landingPath + "/main.qml")); + + connect(m_dialog->rootObject(), SIGNAL(openQtc(bool)), this, SIGNAL(openCreator(bool))); + connect(m_dialog->rootObject(), SIGNAL(openQds(bool)), this, SIGNAL(openDesigner(bool))); + connect(m_dialog->rootObject(), SIGNAL(installQds()), this, SIGNAL(installDesigner())); + connect(m_dialog->rootObject(), SIGNAL(generateCmake()), this, SIGNAL(generateCmake())); + connect(m_dialog->rootObject(), SIGNAL(generateProjectFile()), this, SIGNAL(generateProjectFile())); + m_dialog->hide(); +} + +QWidget* QdsLandingPage::dialog() +{ + return m_dialog; +} + +void QdsLandingPage::show() +{ + m_dialog->rootObject()->setProperty("qdsInstalled", m_qdsInstalled); + m_dialog->rootObject()->setProperty("projectFileExists", m_projectFileExists); + m_dialog->rootObject()->setProperty("qtVersion", m_qtVersion); + m_dialog->rootObject()->setProperty("qdsVersion", m_qdsVersion); + m_dialog->rootObject()->setProperty("cmakeLists", m_cmakeResources); + m_dialog->rootObject()->setProperty("rememberSelection", Qt::Unchecked); + m_dialog->show(); +} + +void QdsLandingPage::hide() +{ + m_dialog->hide(); +} + +bool QdsLandingPage::qdsInstalled() const +{ + return m_qdsInstalled; +} + +void QdsLandingPage::setQdsInstalled(bool installed) +{ + m_qdsInstalled = installed; +} + +bool QdsLandingPage::projectFileExists() const +{ + return m_projectFileExists; +} + +void QdsLandingPage::setProjectFileExists(bool exists) +{ + m_projectFileExists = exists; +} + +const QString QdsLandingPage::qtVersion() const +{ + return m_qtVersion; +} + +void QdsLandingPage::setQtVersion(const QString &version) +{ + m_qtVersion = version; +} + +const QString QdsLandingPage::qdsVersion() const +{ + return m_qdsVersion; +} + +void QdsLandingPage::setQdsVersion(const QString &version) +{ + m_qdsVersion = version; +} + +const QStringList QdsLandingPage::cmakeResources() const +{ + return m_cmakeResources; +} + +void QdsLandingPage::setCmakeResources(const Utils::FilePaths &resources) +{ + QStringList strings = Utils::transform(resources, + [](const Utils::FilePath &path) + { return path.fileName(); }); + setCmakeResources(strings); +} + +void QdsLandingPage::setCmakeResources(const QStringList &resources) +{ + m_cmakeResources = resources; +} + +} // namespace Internal +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/qdslandingpage.h b/src/plugins/qmlprojectmanager/qdslandingpage.h new file mode 100644 index 00000000000..e1c277a75b2 --- /dev/null +++ b/src/plugins/qmlprojectmanager/qdslandingpage.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2022 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 + +namespace QmlProjectManager { +namespace Internal { + +class DesignModeContext : public Core::IContext +{ + Q_OBJECT + +public: + DesignModeContext(QWidget *widget) { setWidget(widget); } +}; + +class QdsLandingPage : public QObject +{ + Q_OBJECT + +public: + Q_PROPERTY(bool qdsInstalled MEMBER m_qdsInstalled READ qdsInstalled WRITE setQdsInstalled) + Q_PROPERTY(bool projectFileExists MEMBER m_projectFileExists READ projectFileExists WRITE setProjectFileExists) + Q_PROPERTY(QString qtVersion MEMBER m_qtVersion READ qtVersion WRITE setQtVersion) + Q_PROPERTY(QString qdsVersion MEMBER m_qdsVersion READ qdsVersion WRITE setQdsVersion) + +public: + QdsLandingPage(QWidget *parent = nullptr); + + QWidget *dialog(); + void show(); + void hide(); + + bool qdsInstalled() const; + void setQdsInstalled(bool installed); + bool projectFileExists() const; + void setProjectFileExists(bool exists); + const QString qtVersion() const; + void setQtVersion(const QString &version); + const QString qdsVersion() const; + void setQdsVersion(const QString &version); + const QStringList cmakeResources() const; + void setCmakeResources(const Utils::FilePaths &resources); + void setCmakeResources(const QStringList &resources); + +signals: + void doNotShowChanged(bool doNotShow); + void openCreator(bool rememberSelection); + void openDesigner(bool rememberSelection); + void installDesigner(); + void generateCmake(); + void generateProjectFile(); + +private: + QQuickWidget *m_dialog = nullptr; + + bool m_qdsInstalled = false; + bool m_projectFileExists = false; + Qt::CheckState m_doNotShow = Qt::Unchecked; + QString m_qtVersion; + QString m_qdsVersion; + QStringList m_cmakeResources; +}; + +} // namespace Internal +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index 67a9ab3dc03..780946e55d5 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -108,25 +108,7 @@ QmlProject::QmlProject(const Utils::FilePath &fileName) setNeedsBuildConfigurations(false); setBuildSystemCreator([](Target *t) { return new QmlBuildSystem(t); }); - if (!QmlProject::isQtDesignStudio()) { - if (QmlProjectPlugin::qdsInstallationExists()) { - auto lambda = [fileName]() { - if (Core::ICore::infoBar()->canInfoBeAdded(openInQDSAppSetting)) { - Utils::InfoBarEntry - info(openInQDSAppSetting, - tr("Would you like to open the project in Qt Design Studio?"), - Utils::InfoBarEntry::GlobalSuppression::Disabled); - info.addCustomButton(tr("Open in Qt Design Studio"), [&, fileName] { - Core::ICore::infoBar()->removeInfo(openInQDSAppSetting); - QmlProjectPlugin::openQDS(fileName); - }); - Core::ICore::infoBar()->addInfo(info); - } - }; - - QTimer::singleShot(0, this, lambda); - } - } else { + if (QmlProject::isQtDesignStudio()) { m_openFileConnection = connect(this, &QmlProject::anyParsingFinished, diff --git a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs index 0822c8ae11a..3990491c5c0 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs +++ b/src/plugins/qmlprojectmanager/qmlprojectmanager.qbs @@ -16,6 +16,7 @@ QtcPlugin { name: "General" files: [ "projectfilecontenttools.cpp", "projectfilecontenttools.h", + "qdslandingpage.cpp", "qdslandingpage.h", "qmlmainfileaspect.cpp", "qmlmainfileaspect.h", "qmlmultilanguageaspect.cpp", "qmlmultilanguageaspect.h", "qmlproject.cpp", "qmlproject.h", diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp index 44f8bfad9af..32b9cb1594d 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp @@ -23,16 +23,19 @@ ** ****************************************************************************/ +#include "qdslandingpage.h" #include "qmlprojectplugin.h" #include "qmlproject.h" #include "qmlprojectconstants.h" #include "qmlprojectrunconfiguration.h" #include "projectfilecontenttools.h" -#include +#include +#include #include #include #include +#include #include #include @@ -40,14 +43,18 @@ #include +#include +#include + #include #include #include -#include +#include #include +#include #include #include #include @@ -58,8 +65,8 @@ using namespace ProjectExplorer; namespace QmlProjectManager { namespace Internal { -const char openInQDSAppInfoBar[] = "OpenInQDSAppUiQml"; -const char alwaysOpenUiQmlInQDS[] = "J.QtQuick/QmlJSEditor.openUiQmlFilesInQDS"; +const char alwaysOpenUiQmlMode[] = "J.QtQuick/QmlJSEditor.openUiQmlMode"; +const char installQdsUrl[] = "https://www.qt.io/product/ui-design-tools"; static bool isQmlDesigner(const ExtensionSystem::PluginSpec *spec) { @@ -76,14 +83,19 @@ static bool qmlDesignerEnabled() return it != plugins.end() && (*it)->plugin(); } -static bool alwaysOpenUiQmlfileInQds() +static QString alwaysOpenWithMode() { - return Core::ICore::settings()->value(alwaysOpenUiQmlInQDS, false).toBool(); + return Core::ICore::settings()->value(alwaysOpenUiQmlMode, "").toString(); } -static void enableAlwaysOpenUiQmlfileInQds() +static void setAlwaysOpenWithMode(const QString &mode) { - return Core::ICore::settings()->setValue(alwaysOpenUiQmlInQDS, true); + Core::ICore::settings()->setValue(alwaysOpenUiQmlMode, mode); +} + +static void clearAlwaysOpenWithMode() +{ + Core::ICore::settings()->remove(alwaysOpenUiQmlMode); } class QmlProjectPluginPrivate @@ -94,12 +106,15 @@ public: {ProjectExplorer::Constants::NORMAL_RUN_MODE}, {runConfigFactory.runConfigurationId()}}; QPointer lastMessageBox; + QdsLandingPage *landingPage = nullptr; }; QmlProjectPlugin::~QmlProjectPlugin() { if (d->lastMessageBox) d->lastMessageBox->deleteLater(); + if (d->landingPage) + d->landingPage->deleteLater(); delete d; } @@ -119,6 +134,8 @@ void QmlProjectPlugin::openQDS(const Utils::FilePath &fileName) QMessageBox::warning(Core::ICore::dialogParent(), fileName.fileName(), QObject::tr("Failed to start Qt Design Studio.")); + if (alwaysOpenWithMode() == Core::Constants::MODE_DESIGN) + clearAlwaysOpenWithMode(); } } @@ -135,15 +152,30 @@ bool QmlProjectPlugin::qdsInstallationExists() return qdsInstallationEntry().exists(); } -Utils::FilePath findQmlProject(const Utils::FilePath &folder) +bool QmlProjectPlugin::checkIfEditorIsuiQml(Core::IEditor *editor) { - QDir dir = folder.toDir(); - for (const QString &file : dir.entryList({"*.qmlproject"})) - return Utils::FilePath::fromString(folder.toString() + "/" + file); - return {}; + if (editor + && (editor->document()->id() == QmlJSEditor::Constants::C_QMLJSEDITOR_ID + || editor->document()->id() == QmlJSEditor::Constants::C_QTQUICKDESIGNEREDITOR_ID)) { + QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance(); + QmlJS::Document::Ptr document = + modelManager->ensuredGetDocumentForPath(editor->document()->filePath().toString()); + if (!document.isNull()) + return document->language() == QmlJS::Dialect::QmlQtQuick2Ui; + } + return false; } -Utils::FilePath findQmlProjectUpwards(const Utils::FilePath &folder) +const Utils::FilePath findQmlProject(const Utils::FilePath &folder) +{ + const Utils::FilePaths files = folder.dirEntries({QStringList("*.qmlproject"), QDir::Files}); + if (files.isEmpty()) + return {}; + + return files.constFirst(); +} + +const Utils::FilePath findQmlProjectUpwards(const Utils::FilePath &folder) { auto ret = findQmlProject(folder); if (ret.exists()) @@ -205,55 +237,7 @@ bool QmlProjectPlugin::initialize(const QStringList &, QString *errorMessage) d = new QmlProjectPluginPrivate; if (!qmlDesignerEnabled()) { - connect(Core::EditorManager::instance(), - &Core::EditorManager::currentEditorChanged, - [this](Core::IEditor *editor) { - QmlJS::ModelManagerInterface *modelManager - = QmlJS::ModelManagerInterface::instance(); - - if (!editor || !modelManager) - return; - - if (d->lastMessageBox) - return; - auto filePath = editor->document()->filePath(); - QmlJS::Document::Ptr document = modelManager->ensuredGetDocumentForPath( - filePath.toString()); - if (!document.isNull() - && document->language() == QmlJS::Dialect::QmlQtQuick2Ui) { - - const QString description = tr("Files of the type ui.qml are intended for Qt Design Studio."); - - if (!qdsInstallationExists()) { - if (Core::ICore::infoBar()->canInfoBeAdded(openInQDSAppInfoBar)) { - Utils::InfoBarEntry - info(openInQDSAppInfoBar, - description + tr(" Learn more about Qt Design Studio here: ") - + "Qt Design Studio", - Utils::InfoBarEntry::GlobalSuppression::Disabled); - Core::ICore::infoBar()->addInfo(info); - } - return; - } - - - if (alwaysOpenUiQmlfileInQds()) { - openInQDSWithProject(filePath); - } else if (Core::ICore::infoBar()->canInfoBeAdded(openInQDSAppInfoBar)) { - Utils::InfoBarEntry - info(openInQDSAppInfoBar, - description + "\n" + tr("Do you want to open this file always in Qt Design Studio?"), - Utils::InfoBarEntry::GlobalSuppression::Disabled); - info.addCustomButton(tr("Always open in Qt Design Studio"), [filePath] { - Core::ICore::infoBar()->removeInfo(openInQDSAppInfoBar); - - enableAlwaysOpenUiQmlfileInQds(); - openInQDSWithProject(filePath); - }); - Core::ICore::infoBar()->addInfo(info); - } - } - }); + initializeQmlLandingPage(); } ProjectManager::registerProjectType(QmlJSTools::Constants::QMLPROJECT_MIMETYPE); @@ -262,5 +246,112 @@ bool QmlProjectPlugin::initialize(const QStringList &, QString *errorMessage) return true; } +void QmlProjectPlugin::initializeQmlLandingPage() +{ + d->landingPage = new QdsLandingPage(); + connect(d->landingPage, &QdsLandingPage::openCreator, this, &QmlProjectPlugin::openQtc); + connect(d->landingPage, &QdsLandingPage::openDesigner, this, &QmlProjectPlugin::openQds); + connect(d->landingPage, &QdsLandingPage::installDesigner, this, &QmlProjectPlugin::installQds); + connect(d->landingPage, &QdsLandingPage::generateCmake, this, &QmlProjectPlugin::generateCmake); + connect(d->landingPage, &QdsLandingPage::generateProjectFile, this, &QmlProjectPlugin::generateProjectFile); + + auto dialog = d->landingPage->dialog(); + + const QStringList mimeTypes = {QmlJSTools::Constants::QMLUI_MIMETYPE}; + auto context = new Internal::DesignModeContext(dialog); + Core::ICore::addContextObject(context); + + Core::DesignMode::registerDesignWidget(dialog, mimeTypes, context->context()); + + connect(Core::ModeManager::instance(), &Core::ModeManager::currentModeChanged, + this, &QmlProjectPlugin::editorModeChanged); +} + +void QmlProjectPlugin::displayQmlLandingPage() +{ + const QString qtVersionString = ProjectFileContentTools::qtVersion(projectFilePath()); + const QString qdsVersionString = ProjectFileContentTools::qdsVersion(projectFilePath()); + + d->landingPage->setQdsInstalled(qdsInstallationExists()); + d->landingPage->setProjectFileExists(projectFilePath().exists()); + d->landingPage->setCmakeResources(ProjectFileContentTools::rootCmakeFiles()); + d->landingPage->setQtVersion(qtVersionString); + d->landingPage->setQdsVersion(qdsVersionString); + d->landingPage->show(); +} + +void QmlProjectPlugin::hideQmlLandingPage() +{ + d->landingPage->hide(); +} + +static bool isDesignerMode(Utils::Id mode) +{ + return mode == Core::Constants::MODE_DESIGN; +} + +void QmlProjectPlugin::editorModeChanged(Utils::Id newMode, Utils::Id oldMode) +{ + Core::IEditor *currentEditor = Core::EditorManager::currentEditor(); + if (checkIfEditorIsuiQml(currentEditor)) { + if (isDesignerMode(newMode)) { + if (alwaysOpenWithMode() == Core::Constants::MODE_DESIGN) + openQds(); + else if (alwaysOpenWithMode() == Core::Constants::MODE_EDIT) + openQtc(); + else + displayQmlLandingPage(); + } else if (isDesignerMode(oldMode)) { + hideQmlLandingPage(); + } + } +} + +void QmlProjectPlugin::openQtc(bool permanent) +{ + if (permanent) + setAlwaysOpenWithMode(Core::Constants::MODE_EDIT); + + hideQmlLandingPage(); + Core::ModeManager::activateMode(Core::Constants::MODE_EDIT); +} + +void QmlProjectPlugin::openQds(bool permanent) +{ + if (permanent) + setAlwaysOpenWithMode(Core::Constants::MODE_DESIGN); + + hideQmlLandingPage(); + auto editor = Core::EditorManager::currentEditor(); + if (editor) + openInQDSWithProject(editor->document()->filePath()); +} + +void QmlProjectPlugin::installQds() +{ + QDesktopServices::openUrl(QUrl(installQdsUrl)); + hideQmlLandingPage(); +} + +void QmlProjectPlugin::generateCmake() +{ + qWarning() << "TODO generate cmake"; +} + +void QmlProjectPlugin::generateProjectFile() +{ + qWarning() << "TODO generate .qmlproject"; +} + +Utils::FilePath QmlProjectPlugin::projectFilePath() +{ + auto project = ProjectExplorer::SessionManager::startupProject(); + const QmlProjectManager::QmlProject *qmlProject = qobject_cast(project); + if (qmlProject) + return qmlProject->projectFilePath(); + + return {}; +} + } // namespace Internal } // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.h b/src/plugins/qmlprojectmanager/qmlprojectplugin.h index e793cee74b8..2e8f97b061a 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectplugin.h +++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.h @@ -25,6 +25,7 @@ #pragma once +#include #include #include @@ -43,11 +44,28 @@ public: static void openQDS(const Utils::FilePath &fileName); static Utils::FilePath qdsInstallationEntry(); static bool qdsInstallationExists(); + static bool checkIfEditorIsuiQml(Core::IEditor *editor); + static Utils::FilePath projectFilePath(); + static Utils::FilePaths rootCmakeFiles(); + static QString qtVersion(const Utils::FilePath &projectFilePath); + static QString qdsVersion(const Utils::FilePath &projectFilePath); + +public slots: + void editorModeChanged(Utils::Id newMode, Utils::Id oldMode); + void openQtc(bool permanent = false); + void openQds(bool permanent = false); + void installQds(); + void generateCmake(); + void generateProjectFile(); private: static void openInQDSWithProject(const Utils::FilePath &filePath); + static const QString readFileContents(const Utils::FilePath &filePath); bool initialize(const QStringList &arguments, QString *errorString) final; + void initializeQmlLandingPage(); + void displayQmlLandingPage(); + void hideQmlLandingPage(); class QmlProjectPluginPrivate *d = nullptr; };