aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/qmldesigner/qmlpreviewplugin
diff options
context:
space:
mode:
authorThomas Hartmann <thomas.hartmann@qt.io>2019-06-04 16:45:28 +0200
committerThomas Hartmann <thomas.hartmann@qt.io>2019-06-05 13:37:26 +0000
commit7016aa5c02411d3ac313324f34ce39f86107fae3 (patch)
treefe817b21c6730bd04bd97d3222b4616c02013ec7 /src/plugins/qmldesigner/qmlpreviewplugin
parent218c1e37699423cca7131ea32d97209bbc63980d (diff)
QmlDesigner: Add live preview
Task-number: QDS-376 Change-Id: I38d110c414b14d6a75a66a5c989f63d57d2cdf02 Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
Diffstat (limited to 'src/plugins/qmldesigner/qmlpreviewplugin')
-rw-r--r--src/plugins/qmldesigner/qmlpreviewplugin/images/live_preview.pngbin0 -> 198 bytes
-rw-r--r--src/plugins/qmldesigner/qmlpreviewplugin/images/live_preview@2x.pngbin0 -> 424 bytes
-rw-r--r--src/plugins/qmldesigner/qmlpreviewplugin/qmlpreview.metainfo2
-rw-r--r--src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp303
-rw-r--r--src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.h135
-rw-r--r--src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp173
-rw-r--r--src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.h69
-rw-r--r--src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.pri10
-rw-r--r--src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.pro18
-rw-r--r--src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.qrc7
10 files changed, 717 insertions, 0 deletions
diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/images/live_preview.png b/src/plugins/qmldesigner/qmlpreviewplugin/images/live_preview.png
new file mode 100644
index 0000000000..b56467d4ea
--- /dev/null
+++ b/src/plugins/qmldesigner/qmlpreviewplugin/images/live_preview.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/images/live_preview@2x.png b/src/plugins/qmldesigner/qmlpreviewplugin/images/live_preview@2x.png
new file mode 100644
index 0000000000..93c49a04eb
--- /dev/null
+++ b/src/plugins/qmldesigner/qmlpreviewplugin/images/live_preview@2x.png
Binary files differ
diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreview.metainfo b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreview.metainfo
new file mode 100644
index 0000000000..5bfe70cffd
--- /dev/null
+++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreview.metainfo
@@ -0,0 +1,2 @@
+MetaInfo {
+}
diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp
new file mode 100644
index 0000000000..1fe99825c3
--- /dev/null
+++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp
@@ -0,0 +1,303 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "qmlpreviewplugin.h"
+#include "qmlpreviewactions.h"
+
+#include <zoomaction.h>
+
+#include <utils/utilsicons.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/session.h>
+#include <projectexplorer/project.h>
+
+#include <QLabel>
+#include <QComboBox>
+#include <QPointer>
+
+namespace QmlDesigner {
+
+using namespace ProjectExplorer;
+
+const Utils::Icon previewIcon({
+ {":/qmlpreviewplugin/images/live_preview.png", Utils::Theme::IconsBaseColor}});
+
+static void handleAction(const SelectionContext &context)
+{
+ if (context.view()->isAttached()) {
+ if (context.toggled()) {
+ ProjectExplorerPlugin::runStartupProject(Constants::QML_PREVIEW_RUN_MODE);
+ QmlPreviewPlugin::setQmlFile();
+ } else {
+ QmlPreviewPlugin::stopAllRunControls();
+ }
+ }
+}
+
+QmlPreviewAction::QmlPreviewAction() : ModelNodeAction("LivePreview",
+ "Live Preview",
+ previewIcon.icon(),
+ QmlPreviewPlugin::tr("Show Live Preview"),
+ ComponentCoreConstants::qmlPreviewCategory,
+ QKeySequence("Alt+p"),
+ 20,
+ &handleAction,
+ &SelectionContextFunctors::always)
+{
+ if (!QmlPreviewPlugin::getPreviewPlugin())
+ defaultAction()->setVisible(false);
+
+ defaultAction()->setCheckable(true);
+}
+
+void QmlPreviewAction::updateContext()
+{
+ if (selectionContext().view()->isAttached())
+ QmlPreviewPlugin::setQmlFile();
+
+ defaultAction()->setSelectionContext(selectionContext());
+}
+
+ActionInterface::Type QmlPreviewAction::type() const
+{
+ return ToolBarAction;
+}
+
+ZoomPreviewAction::ZoomPreviewAction()
+ : m_zoomAction(new ZoomAction(nullptr))
+{
+ QObject::connect(m_zoomAction.get(), &ZoomAction::zoomLevelChanged, [=](float d) {
+ QmlPreviewPlugin::setZoomFactor(d);
+ });
+ if (!QmlPreviewPlugin::getPreviewPlugin())
+ m_zoomAction->setVisible(false);
+}
+
+ZoomPreviewAction::~ZoomPreviewAction()
+= default;
+
+QAction *ZoomPreviewAction::action() const
+{
+ return m_zoomAction.get();
+}
+
+QByteArray ZoomPreviewAction::category() const
+{
+ return ComponentCoreConstants::qmlPreviewCategory;
+}
+
+QByteArray ZoomPreviewAction::menuId() const
+{
+ return QByteArray();
+}
+
+int ZoomPreviewAction::priority() const
+{
+ return 19;
+}
+
+ActionInterface::Type ZoomPreviewAction::type() const
+{
+ return ToolBarAction;
+}
+
+void ZoomPreviewAction::currentContextChanged(const SelectionContext &)
+{}
+
+quint16 FpsLabelAction::lastValidFrames = 0;
+QList<QPointer<QLabel>> FpsLabelAction::fpsHandlerLabelList;
+
+FpsLabelAction::FpsLabelAction(QObject *parent)
+ : QWidgetAction(parent)
+{
+}
+
+void FpsLabelAction::fpsHandler(quint16 fpsValues[8])
+{
+ quint16 frames = fpsValues[0];
+ if (frames != 0)
+ lastValidFrames = frames;
+ QString fpsText("%1 FPS");
+ if (lastValidFrames == 0 || (frames == 0 && lastValidFrames < 2))
+ fpsText = fpsText.arg("--");
+ else
+ fpsText = fpsText.arg(lastValidFrames);
+ for (QPointer<QLabel> label : fpsHandlerLabelList) {
+ if (label)
+ label->setText(fpsText);
+ }
+}
+
+void FpsLabelAction::cleanFpsCounter()
+{
+ lastValidFrames = 0;
+ quint16 nullInitialized[8] = {0};
+ fpsHandler(nullInitialized);
+}
+
+QWidget *FpsLabelAction::createWidget(QWidget *parent)
+{
+ auto label = new QLabel(parent);
+ auto originList = fpsHandlerLabelList;
+ fpsHandlerLabelList.clear();
+ fpsHandlerLabelList.append(label);
+ for (const auto &labelPointer : originList) {
+ if (labelPointer)
+ fpsHandlerLabelList.append(labelPointer);
+ }
+
+ return label;
+}
+
+void FpsLabelAction::refreshFpsLabel(quint16 frames)
+{
+ for (const auto &labelPointer : fpsHandlerLabelList) {
+ if (labelPointer)
+ labelPointer->setText(QString("%1 FPS").arg(frames));
+ }
+}
+
+FpsAction::FpsAction() : m_fpsLabelAction(new FpsLabelAction(nullptr))
+{}
+
+QAction *FpsAction::action() const
+{
+ return m_fpsLabelAction.get();
+}
+
+QByteArray FpsAction::category() const
+{
+ return ComponentCoreConstants::qmlPreviewCategory;
+}
+
+QByteArray FpsAction::menuId() const
+{
+ return QByteArray();
+}
+
+int FpsAction::priority() const
+{
+ return 19;
+}
+
+ActionInterface::Type FpsAction::type() const
+{
+ return ToolBarAction;
+}
+
+void FpsAction::currentContextChanged(const SelectionContext &)
+{}
+
+SwitchLanguageComboboxAction::SwitchLanguageComboboxAction(QObject *parent)
+ : QWidgetAction(parent)
+{
+ connect(ProjectExplorer::SessionManager::instance(),
+ &ProjectExplorer::SessionManager::startupProjectChanged,
+ this,
+ &SwitchLanguageComboboxAction::refreshProjectLocales);
+}
+
+QWidget *SwitchLanguageComboboxAction::createWidget(QWidget *parent)
+{
+ QPointer<QComboBox> comboBox = new QComboBox(parent);
+ comboBox->setToolTip(tr("Switch the language used by preview."));
+ connect(comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), [this, comboBox](int index) {
+ if (index == 0)
+ emit currentLocaleChanged("");
+ else
+ emit currentLocaleChanged(comboBox->currentText());
+ });
+
+ auto refreshComboBoxFunction = [this, comboBox] (ProjectExplorer::Project *project) {
+ if (comboBox) {
+ refreshProjectLocales(project);
+ comboBox->clear();
+ comboBox->addItem(tr("Default"));
+ comboBox->addItems(m_localeStrings);
+ }
+ };
+ connect(ProjectExplorer::SessionManager::instance(),
+ &ProjectExplorer::SessionManager::startupProjectChanged,
+ refreshComboBoxFunction);
+
+ if (auto project = SessionManager::startupProject())
+ refreshComboBoxFunction(project);
+
+ return comboBox;
+}
+
+void SwitchLanguageComboboxAction::refreshProjectLocales(Project *project)
+{
+ if (!project)
+ return;
+ m_localeStrings.clear();
+ const auto projectDirectory = project->rootProjectDirectory().toFileInfo().absoluteFilePath();
+ const QDir languageDirectory(projectDirectory + "/i18n");
+ const auto qmFiles = languageDirectory.entryList({"qml_*.qm"});
+ m_localeStrings = Utils::transform(qmFiles, [](const QString &qmFile) {
+ const int localeStartPosition = qmFile.lastIndexOf("_") + 1;
+ const int localeEndPosition = qmFile.size() - QString(".qm").size();
+ const QString locale = qmFile.left(localeEndPosition).mid(localeStartPosition);
+ return locale;
+ });
+}
+
+SwitchLanguageAction::SwitchLanguageAction()
+ : m_switchLanguageAction(new SwitchLanguageComboboxAction(nullptr))
+{
+ QObject::connect(m_switchLanguageAction.get(), &SwitchLanguageComboboxAction::currentLocaleChanged,
+ &QmlPreviewPlugin::setLanguageLocale);
+}
+
+QAction *SwitchLanguageAction::action() const
+{
+ return m_switchLanguageAction.get();
+}
+
+QByteArray SwitchLanguageAction::category() const
+{
+ return ComponentCoreConstants::qmlPreviewCategory;
+}
+
+QByteArray SwitchLanguageAction::menuId() const
+{
+ return QByteArray();
+}
+
+int SwitchLanguageAction::priority() const
+{
+ return 10;
+}
+
+ActionInterface::Type SwitchLanguageAction::type() const
+{
+ return ToolBarAction;
+}
+
+void SwitchLanguageAction::currentContextChanged(const SelectionContext &)
+{}
+
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.h b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.h
new file mode 100644
index 0000000000..8a71ee07c2
--- /dev/null
+++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.h
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 <actioninterface.h>
+#include <modelnodecontextmenu_helper.h>
+
+#include <QByteArray>
+
+#include <QWidgetAction>
+#include <memory>
+
+QT_FORWARD_DECLARE_CLASS(QAction)
+QT_FORWARD_DECLARE_CLASS(QLabel)
+
+
+namespace QmlPreview {
+using QmlPreviewFpsHandler = void (*)(quint16 *);
+}
+
+namespace ProjectExplorer {
+class Project;
+}
+
+namespace QmlDesigner {
+class ZoomAction;
+
+class QmlPreviewAction : public ModelNodeAction
+{
+public:
+ QmlPreviewAction();
+
+ void updateContext() override;
+
+ Type type() const override;
+};
+
+class ZoomPreviewAction : public ActionInterface
+{
+public:
+ ZoomPreviewAction();
+ ~ZoomPreviewAction() override;
+ QAction *action() const override;
+ QByteArray category() const override;
+ QByteArray menuId() const override;
+ int priority() const override;
+ Type type() const override;
+ void currentContextChanged(const SelectionContext &) override;
+
+private:
+ std::unique_ptr<ZoomAction> m_zoomAction;
+};
+
+class FpsLabelAction : public QWidgetAction
+{
+public:
+ explicit FpsLabelAction(QObject *parent = nullptr);
+ static void fpsHandler(quint16 fpsValues[8]);
+ static void cleanFpsCounter();
+protected:
+ QWidget *createWidget(QWidget *parent) override;
+private:
+ static void refreshFpsLabel(quint16 frames);
+ static QPointer<QLabel> m_labelInstance;
+ static QList<QPointer<QLabel>> fpsHandlerLabelList;
+ static quint16 lastValidFrames;
+};
+
+class FpsAction : public ActionInterface
+{
+public:
+ FpsAction();
+ QAction *action() const override;
+ QByteArray category() const override;
+ QByteArray menuId() const override;
+ int priority() const override;
+ Type type() const override;
+ void currentContextChanged(const SelectionContext &) override;
+private:
+ std::unique_ptr<FpsLabelAction> m_fpsLabelAction;
+};
+
+class SwitchLanguageComboboxAction : public QWidgetAction
+{
+ Q_OBJECT
+public:
+ explicit SwitchLanguageComboboxAction(QObject *parent = nullptr);
+signals:
+ void currentLocaleChanged(const QString& string);
+protected:
+ QWidget *createWidget(QWidget *parent) override;
+private:
+ void refreshProjectLocales(ProjectExplorer::Project *project);
+ QStringList m_localeStrings;
+};
+
+class SwitchLanguageAction : public ActionInterface
+{
+public:
+ SwitchLanguageAction();
+ QAction *action() const override;
+ QByteArray category() const override;
+ QByteArray menuId() const override;
+ int priority() const override;
+ Type type() const override;
+ void currentContextChanged(const SelectionContext &) override;
+private:
+ std::unique_ptr<SwitchLanguageComboboxAction> m_switchLanguageAction;
+};
+
+} // namespace QmlDesigner
+Q_DECLARE_METATYPE(QmlPreview::QmlPreviewFpsHandler);
diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp
new file mode 100644
index 0000000000..5a3c1c4e09
--- /dev/null
+++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.cpp
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "qmlpreviewplugin.h"
+#include "qmlpreviewactions.h"
+
+#include <modelnodecontextmenu_helper.h>
+#include <componentcore_constants.h>
+#include <qmldesignerplugin.h>
+#include <viewmanager.h>
+#include <actioninterface.h>
+#include <zoomaction.h>
+
+#include <extensionsystem/pluginmanager.h>
+#include <extensionsystem/pluginspec.h>
+
+#include <utils/algorithm.h>
+#include <utils/qtcassert.h>
+#include <utils/utilsicons.h>
+
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/runconfiguration.h>
+#include <projectexplorer/runcontrol.h>
+
+namespace QmlPreview {
+using QmlPreviewRunControlList = QList<ProjectExplorer::RunControl *>;
+}
+
+Q_DECLARE_METATYPE(QmlPreview::QmlPreviewRunControlList)
+
+namespace QmlDesigner {
+static QObject *s_previewPlugin = nullptr;
+
+QmlPreviewPlugin::QmlPreviewPlugin()
+{
+ DesignerActionManager &designerActionManager =
+ QmlDesignerPlugin::instance()->designerActionManager();
+ auto previewAction = new QmlPreviewAction();
+ designerActionManager.addDesignerAction(new ActionGroup(
+ QString(),
+ ComponentCoreConstants::qmlPreviewCategory,
+ ComponentCoreConstants::priorityQmlPreviewCategory,
+ &SelectionContextFunctors::always));
+ s_previewPlugin = getPreviewPlugin();
+
+ if (s_previewPlugin) {
+ bool connected = connect(s_previewPlugin, SIGNAL(runningPreviewsChanged(const QmlPreviewRunControlList &)),
+ this, SLOT(handleRunningPreviews()));
+ QTC_ASSERT(connected, qWarning() << "something wrong with the runningPreviewsChanged signal");
+ }
+
+ designerActionManager.addDesignerAction(previewAction);
+
+ auto zoomAction = new ZoomPreviewAction;
+ designerActionManager.addDesignerAction(zoomAction);
+
+ auto separator = new SeperatorDesignerAction(ComponentCoreConstants::qmlPreviewCategory, 0);
+ designerActionManager.addDesignerAction(separator);
+
+ m_previewToggleAction = previewAction->defaultAction();
+
+ if (s_previewPlugin) {
+ auto fpsAction = new FpsAction;
+ designerActionManager.addDesignerAction(fpsAction);
+ s_previewPlugin->setProperty("fpsHandler", QVariant::fromValue<QmlPreview::QmlPreviewFpsHandler>(FpsLabelAction::fpsHandler));
+ auto switchLanguageAction = new SwitchLanguageAction;
+ designerActionManager.addDesignerAction(switchLanguageAction);
+ }
+}
+
+QString QmlPreviewPlugin::pluginName() const
+{
+ return QLatin1String("QmlPreviewPlugin");
+}
+
+void QmlPreviewPlugin::stopAllRunControls()
+{
+ QTC_ASSERT(s_previewPlugin, return);
+
+ const QVariant variant = s_previewPlugin->property("runningPreviews");
+ auto runControls = variant.value<QmlPreview::QmlPreviewRunControlList>();
+
+ for (ProjectExplorer::RunControl *runControl : runControls)
+ runControl->initiateStop();
+
+}
+
+void QmlPreviewPlugin::handleRunningPreviews()
+{
+ QTC_ASSERT(s_previewPlugin, return);
+
+ const QVariant variant = s_previewPlugin->property("runningPreviews");
+ if (variant.isValid()) {
+ // the QmlPreview::QmlPreviewRunControlList type have to be available and used in the qmlpreview plugin
+ QTC_ASSERT(variant.canConvert<QmlPreview::QmlPreviewRunControlList>(), return);
+ auto runControls = variant.value<QmlPreview::QmlPreviewRunControlList>();
+ m_previewToggleAction->setChecked(!runControls.isEmpty());
+ if (runControls.isEmpty())
+ FpsLabelAction::cleanFpsCounter();
+ }
+}
+
+QString QmlPreviewPlugin::metaInfo() const
+{
+ return QLatin1String(":/qmlpreviewplugin/qmlpreview.metainfo");
+}
+
+void QmlPreviewPlugin::setQmlFile()
+{
+ if (s_previewPlugin) {
+ const Utils::FileName qmlFileName =
+ QmlDesignerPlugin::instance()->currentDesignDocument()->fileName();
+ s_previewPlugin->setProperty("previewedFile", qmlFileName.toString());
+ }
+}
+
+float QmlPreviewPlugin::zoomFactor()
+{
+ QVariant zoomFactorVariant = 1.0;
+ if (s_previewPlugin && !s_previewPlugin->property("zoomFactor").isNull())
+ zoomFactorVariant = s_previewPlugin->property("zoomFactor");
+ return zoomFactorVariant.toFloat();
+}
+
+void QmlPreviewPlugin::setZoomFactor(float zoomFactor)
+{
+ if (s_previewPlugin)
+ s_previewPlugin->setProperty("zoomFactor", zoomFactor);
+}
+
+void QmlPreviewPlugin::setLanguageLocale(const QString &locale)
+{
+ if (s_previewPlugin)
+ s_previewPlugin->setProperty("locale", locale);
+}
+
+QObject *QmlPreviewPlugin::getPreviewPlugin()
+{
+ auto pluginIt = std::find_if(ExtensionSystem::PluginManager::plugins().begin(),
+ ExtensionSystem::PluginManager::plugins().end(),
+ [](const ExtensionSystem::PluginSpec *p) {
+ return p->name() == "QmlPreview";
+ });
+
+ if (pluginIt != ExtensionSystem::PluginManager::plugins().constEnd())
+ return (*pluginIt)->plugin();
+
+ return nullptr;
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.h b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.h
new file mode 100644
index 0000000000..7f50d76696
--- /dev/null
+++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 <iwidgetplugin.h>
+
+#include <QMetaType>
+
+QT_FORWARD_DECLARE_CLASS(QAction)
+
+namespace QmlDesigner {
+
+class QmlPreviewPlugin : public QObject, QmlDesigner::IWidgetPlugin
+{
+ Q_OBJECT
+
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QmlDesignerPlugin" FILE "qmlpreviewplugin.json")
+
+ Q_DISABLE_COPY(QmlPreviewPlugin)
+ Q_INTERFACES(QmlDesigner::IWidgetPlugin)
+
+public:
+ QmlPreviewPlugin();
+ ~QmlPreviewPlugin() override = default;
+
+ QString metaInfo() const override;
+ QString pluginName() const override;
+
+ static void stopAllRunControls();
+ static void setQmlFile();
+ static QObject *getPreviewPlugin();
+
+ static float zoomFactor();
+ static void setZoomFactor(float zoomFactor);
+ static void setLanguageLocale(const QString &locale);
+signals:
+ void fpsChanged(quint16 frames);
+
+private slots:
+ void handleRunningPreviews();
+
+private:
+ QAction *m_previewToggleAction = nullptr;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.pri b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.pri
new file mode 100644
index 0000000000..1808b69266
--- /dev/null
+++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.pri
@@ -0,0 +1,10 @@
+QT *= qml quick core
+
+VPATH += $$PWD
+
+SOURCES += qmlpreviewplugin.cpp
+HEADERS += qmlpreviewplugin.h
+SOURCES += qmlpreviewactions.cpp
+HEADERS += qmlpreviewactions.h
+
+RESOURCES += qmlpreviewplugin.qrc
diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.pro b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.pro
new file mode 100644
index 0000000000..56eadb58a5
--- /dev/null
+++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.pro
@@ -0,0 +1,18 @@
+TARGET = qmlpreviewplugin
+TEMPLATE = lib
+CONFIG += plugin
+
+include(../../../../qtcreator.pri)\
+
+include (../designercore/iwidgetplugin.pri)
+include (../qmldesigner_dependencies.pri)
+
+LIBS += -L$$IDE_PLUGIN_PATH
+LIBS += -l$$qtLibraryName(QmlDesigner)
+LIBS += -l$$qtLibraryName(Extensionsystem)
+LIBS += -l$$qtLibraryName(Core)
+LIBS += -l$$qtLibraryName(ProjectExplorer)
+LIBS += -l$$qtLibraryName(Utils)
+
+include(qmlpreviewplugin.pri)
+include(../plugindestdir.pri)
diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.qrc b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.qrc
new file mode 100644
index 0000000000..3f8cc2a077
--- /dev/null
+++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewplugin.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/qmlpreviewplugin">
+ <file>qmlpreview.metainfo</file>
+ <file>images/live_preview.png</file>
+ <file>images/live_preview@2x.png</file>
+ </qresource>
+</RCC>