QmlProjectManager: Integrate QDS landing page

Task-number: QDS-6564
Change-Id: Ia1a4188ba1bb561bbfeadf5fb29bcba75d05b5ce
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
Tapani Mattila
2022-05-06 14:27:58 +03:00
parent 515845b815
commit 8bb1a862a0
7 changed files with 412 additions and 83 deletions

View File

@@ -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

View File

@@ -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 <coreplugin/icore.h>
#include <QtQml/QQmlEngine>
#include <QQuickItem>
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<QdsLandingPage>("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

View File

@@ -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 <coreplugin/icontext.h>
#include <utils/filepath.h>
#include <QtQuickWidgets/QQuickWidget>
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

View File

@@ -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,

View File

@@ -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",

View File

@@ -23,16 +23,19 @@
**
****************************************************************************/
#include "qdslandingpage.h"
#include "qmlprojectplugin.h"
#include "qmlproject.h"
#include "qmlprojectconstants.h"
#include "qmlprojectrunconfiguration.h"
#include "projectfilecontenttools.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/designmode.h>
#include <coreplugin/fileiconprovider.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
#include <coreplugin/modemanager.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/runcontrol.h>
@@ -40,14 +43,18 @@
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmljseditor/qmljseditor.h>
#include <qmljseditor/qmljseditorconstants.h>
#include <qmljstools/qmljstoolsconstants.h>
#include <extensionsystem/pluginmanager.h>
#include <extensionsystem/pluginspec.h>
#include <utils/infobar.h>
#include <utils/fileutils.h>
#include <utils/qtcprocess.h>
#include <QDesktopServices>
#include <QMessageBox>
#include <QPushButton>
#include <QTimer>
@@ -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<QMessageBox> 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: ")
+ "<a href='https://www.qt.io/product/ui-design-tools'>Qt Design Studio</a>",
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<QmlProject>(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<const QmlProjectManager::QmlProject*>(project);
if (qmlProject)
return qmlProject->projectFilePath();
return {};
}
} // namespace Internal
} // namespace QmlProjectManager

View File

@@ -25,6 +25,7 @@
#pragma once
#include <coreplugin/editormanager/editormanager.h>
#include <extensionsystem/iplugin.h>
#include <utils/filepath.h>
@@ -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;
};