diff --git a/src/libs/utils/infobar.cpp b/src/libs/utils/infobar.cpp index 83c8918eef6..230473b31bb 100644 --- a/src/libs/utils/infobar.cpp +++ b/src/libs/utils/infobar.cpp @@ -278,6 +278,7 @@ void InfoBarDisplay::update() QLabel *infoWidgetLabel = new QLabel(info.m_infoText); infoWidgetLabel->setWordWrap(true); + infoWidgetLabel->setOpenExternalLinks(true); hbox->addWidget(infoWidgetLabel, 1); if (info.m_detailsWidgetCreator) { diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index 7ecf1a19990..c1b8c6dc430 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -27,10 +27,11 @@ #include "fileformat/qmlprojectfileformat.h" #include "fileformat/qmlprojectitem.h" -#include "qmlprojectrunconfiguration.h" #include "qmlprojectconstants.h" #include "qmlprojectmanagerconstants.h" #include "qmlprojectnodes.h" +#include "qmlprojectplugin.h" +#include "qmlprojectrunconfiguration.h" #include #include @@ -94,23 +95,6 @@ static int preferedQtTarget(Target *target) const char openInQDSAppSetting[] = "OpenInQDSApp"; -static void openQDS(const QString &qdsPath, const Utils::FilePath &fileName) -{ - bool qdsStarted = false; - //-a and -client arguments help to append project to open design studio application - if (Utils::HostOsInfo::isMacHost()) - qdsStarted = Utils::QtcProcess::startDetached({"/usr/bin/open", {"-a", qdsPath, fileName.toString()}}); - else - //ToDo change qdsPath type to FilePath - qdsStarted = Utils::QtcProcess::startDetached({Utils::FilePath::fromString(qdsPath), {"-client", fileName.toString()}}); - - if (!qdsStarted) { - QMessageBox::warning(Core::ICore::dialogParent(), - fileName.fileName(), - QObject::tr("Failed to start Qt Design Studio.")); - } -} - QmlProject::QmlProject(const Utils::FilePath &fileName) : Project(QString::fromLatin1(Constants::QMLPROJECT_MIMETYPE), fileName) { @@ -121,23 +105,17 @@ QmlProject::QmlProject(const Utils::FilePath &fileName) setNeedsBuildConfigurations(false); setBuildSystemCreator([](Target *t) { return new QmlBuildSystem(t); }); - QSettings *settings = Core::ICore::settings(); - const QString qdsInstallationEntry = "QML/Designer/DesignStudioInstallation"; //set in installer - if (!isQtDesignStudio()) { - const QString qdsPath = settings->value(qdsInstallationEntry).toString(); - const bool foundQDS = Utils::FilePath::fromString(qdsPath).exists(); - - if (foundQDS) { - auto lambda = [fileName, qdsPath]() { + 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::Enabled); - info.setCustomButtonInfo(tr("Open in Qt Design Studio"), [&, qdsPath, fileName] { + info.setCustomButtonInfo(tr("Open in Qt Design Studio"), [&, fileName] { Core::ICore::infoBar()->removeInfo(openInQDSAppSetting); - openQDS(qdsPath, fileName); + QmlProjectPlugin::openQDS(fileName); }); Core::ICore::infoBar()->addInfo(info); } diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp index a9780c06c7d..c2fd4b45763 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp @@ -27,45 +27,212 @@ #include "qmlproject.h" #include "qmlprojectrunconfiguration.h" +#include #include #include +#include #include #include +#include + +#include #include +#include +#include + +#include + +#include +#include +#include +#include + using namespace ProjectExplorer; namespace QmlProjectManager { namespace Internal { +const char openInQDSAppSetting[] = "OpenInQDSAppUiQml"; + +static bool isQmlDesigner(const ExtensionSystem::PluginSpec *spec) +{ + if (!spec) + return false; + + return spec->name().contains("QmlDesigner"); +} + +static bool qmlDesignerEnabled() +{ + const auto plugins = ExtensionSystem::PluginManager::plugins(); + const auto it = std::find_if(plugins.begin(), plugins.end(), &isQmlDesigner); + return it != plugins.end() && (*it)->plugin(); +} + class QmlProjectPluginPrivate { public: QmlProjectRunConfigurationFactory runConfigFactory; - RunWorkerFactory runWorkerFactory{ - RunWorkerFactory::make(), - {ProjectExplorer::Constants::NORMAL_RUN_MODE}, - {runConfigFactory.runConfigurationId()} - }; + RunWorkerFactory runWorkerFactory{RunWorkerFactory::make(), + {ProjectExplorer::Constants::NORMAL_RUN_MODE}, + {runConfigFactory.runConfigurationId()}}; + QPointer lastMessageBox; }; QmlProjectPlugin::~QmlProjectPlugin() { + if (d->lastMessageBox) + d->lastMessageBox->deleteLater(); delete d; } +void QmlProjectPlugin::openQDS(const Utils::FilePath &fileName) +{ + const QString &qdsPath = QmlProjectPlugin::qdsInstallationEntry(); + bool qdsStarted = false; + //-a and -client arguments help to append project to open design studio application + if (Utils::HostOsInfo::isMacHost()) + qdsStarted = QProcess::startDetached("/usr/bin/open", {"-a", qdsPath, fileName.toString()}); + else + qdsStarted = QProcess::startDetached(qdsPath, {"-client", fileName.toString()}); + + if (!qdsStarted) { + QMessageBox::warning(Core::ICore::dialogParent(), + fileName.fileName(), + QObject::tr("Failed to start Qt Design Studio.")); + } +} + +QString QmlProjectPlugin::qdsInstallationEntry() +{ + QSettings *settings = Core::ICore::settings(); + const QString qdsInstallationEntry = "QML/Designer/DesignStudioInstallation"; //set in installer + + return settings->value(qdsInstallationEntry).toString(); +} + +bool QmlProjectPlugin::qdsInstallationExists() +{ + return Utils::FilePath::fromString(qdsInstallationEntry()).exists(); +} + +Utils::FilePath findQmlProject(const Utils::FilePath &folder) +{ + QDir dir = folder.toDir(); + for (const QString &file : dir.entryList({"*.qmlproject"})) + return Utils::FilePath::fromString(folder.toString() + "/" + file); + return {}; +} + +Utils::FilePath findQmlProjectUpwards(const Utils::FilePath &folder) +{ + auto ret = findQmlProject(folder); + if (ret.exists()) + return ret; + + QDir dir = folder.toDir(); + if (dir.cdUp()) + return findQmlProjectUpwards(Utils::FilePath::fromString(dir.absolutePath())); + return {}; +} + +static bool findAndOpenProject(const Utils::FilePath &filePath) +{ + + ProjectExplorer::Project *project + = ProjectExplorer::SessionManager::projectForFile(filePath); + + if (project) { + if (project->projectFilePath().suffix() == "qmlproject") { + QmlProjectPlugin::openQDS(project->projectFilePath()); + return true; + } else { + auto projectFolder = project->rootProjectDirectory(); + auto qmlProjectFile = findQmlProject(projectFolder); + if (qmlProjectFile.exists()) { + QmlProjectPlugin::openQDS(qmlProjectFile); + return true; + } + } + } + + auto qmlProjectFile = findQmlProjectUpwards(filePath); + if (qmlProjectFile.exists()) { + QmlProjectPlugin::openQDS(qmlProjectFile); + return true; + } + return false; +} + bool QmlProjectPlugin::initialize(const QStringList &, QString *errorMessage) { Q_UNUSED(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) + 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(openInQDSAppSetting)) { + Utils::InfoBarEntry + info(openInQDSAppSetting, + description + tr(" Learn more about Qt Design Studio here: ") + + "Qt Design Studio", + Utils::InfoBarEntry::GlobalSuppression::Enabled); + Core::ICore::infoBar()->addInfo(info); + } + return; + } + + if (Core::ICore::infoBar()->canInfoBeAdded(openInQDSAppSetting)) { + Utils::InfoBarEntry + info(openInQDSAppSetting, + description + "\n" + tr("Do you want to open this file in Qt Design Studio?"), + Utils::InfoBarEntry::GlobalSuppression::Enabled); + info.setCustomButtonInfo(tr("Open in Qt Design Studio"), [filePath] { + Core::ICore::infoBar()->removeInfo(openInQDSAppSetting); + + if (findAndOpenProject(filePath)) { + openQDS(filePath); + //The first one might be ignored when QDS is starting up + QTimer::singleShot(4000, [filePath] { openQDS(filePath); }); + } else { + Core::AsynchronousMessageBox::warning(tr("Qt Design Studio"), + tr("No project file (*.qmlproject) found for Qt Design Studio.")); + } + }); + Core::ICore::infoBar()->addInfo(info); + } + } + }); + } + ProjectManager::registerProjectType(QmlJSTools::Constants::QMLPROJECT_MIMETYPE); - Core::FileIconProvider::registerIconOverlayForSuffix(":/qmlproject/images/qmlproject.png", "qmlproject"); + Core::FileIconProvider::registerIconOverlayForSuffix(":/qmlproject/images/qmlproject.png", + "qmlproject"); return true; -} +} // namespace Internal } // namespace Internal } // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.h b/src/plugins/qmlprojectmanager/qmlprojectplugin.h index 9337688a1e5..260512c5df3 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectplugin.h +++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.h @@ -26,6 +26,7 @@ #pragma once #include +#include namespace QmlProjectManager { namespace Internal { @@ -39,6 +40,10 @@ public: QmlProjectPlugin() = default; ~QmlProjectPlugin() final; + static void openQDS(const Utils::FilePath &fileName); + static QString qdsInstallationEntry(); + static bool qdsInstallationExists(); + private: bool initialize(const QStringList &arguments, QString *errorString) final;