QmlPreview: Pimpl plugin

This is closer in structure and timing to what others do.

Properties are left in the plugin itself in case someone uses them.

Change-Id: Ic43aea1da7e3bc56e9678144f37f5ad40ce1b20f
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
hjk
2019-08-13 10:57:37 +02:00
parent 20e7c1443e
commit 847108d4f4
2 changed files with 127 additions and 88 deletions

View File

@@ -99,14 +99,58 @@ static void defaultFpsHandler(quint16 frames[8])
Core::MessageManager::write(QString::fromLatin1("QML preview: %1 fps").arg(frames[0])); Core::MessageManager::write(QString::fromLatin1("QML preview: %1 fps").arg(frames[0]));
} }
bool QmlPreviewPlugin::initialize(const QStringList &arguments, QString *errorString) class QmlPreviewPluginPrivate : public QObject
{ {
Q_UNUSED(arguments) public:
Q_UNUSED(errorString) explicit QmlPreviewPluginPrivate(QmlPreviewPlugin *parent);
setFileLoader(&defaultFileLoader); void previewCurrentFile();
setFileClassifier(&defaultFileClassifier); void onEditorChanged(Core::IEditor *editor);
setFpsHandler(&defaultFpsHandler); void onEditorAboutToClose(Core::IEditor *editor);
void setDirty();
void addPreview(ProjectExplorer::RunControl *preview);
void removePreview(ProjectExplorer::RunControl *preview);
void attachToEditor();
void checkEditor();
void checkFile(const QString &fileName);
void triggerPreview(const QString &changedFile, const QByteArray &contents);
QString previewedFile() const;
void setPreviewedFile(const QString &previewedFile);
QmlPreviewRunControlList runningPreviews() const;
QmlPreviewFileClassifier fileClassifier() const;
void setFileClassifier(QmlPreviewFileClassifier fileClassifer);
float zoomFactor() const;
void setZoomFactor(float zoomFactor);
QmlPreview::QmlPreviewFpsHandler fpsHandler() const;
void setFpsHandler(QmlPreview::QmlPreviewFpsHandler fpsHandler);
QString locale() const;
void setLocale(const QString &locale);
QmlPreviewPlugin *q = nullptr;
QThread m_parseThread;
QString m_previewedFile;
QmlPreviewFileLoader m_fileLoader = nullptr;
Core::IEditor *m_lastEditor = nullptr;
QmlPreviewRunControlList m_runningPreviews;
bool m_dirty = false;
QmlPreview::QmlPreviewFileClassifier m_fileClassifer = nullptr;
float m_zoomFactor = -1.0;
QmlPreview::QmlPreviewFpsHandler m_fpsHandler = nullptr;
QString m_locale;
std::unique_ptr<ProjectExplorer::RunWorkerFactory> m_runWorkerFactory;
};
QmlPreviewPluginPrivate::QmlPreviewPluginPrivate(QmlPreviewPlugin *parent)
: q(parent)
{
m_fileLoader = &defaultFileLoader;
m_fileClassifer = &defaultFileClassifier;
m_fpsHandler = &defaultFpsHandler;
m_runWorkerFactory.reset(new RunWorkerFactory{ m_runWorkerFactory.reset(new RunWorkerFactory{
RunWorkerFactory::make<LocalQmlPreviewSupport>(), RunWorkerFactory::make<LocalQmlPreviewSupport>(),
@@ -117,7 +161,7 @@ bool QmlPreviewPlugin::initialize(const QStringList &arguments, QString *errorSt
Core::ActionContainer *menu = Core::ActionManager::actionContainer( Core::ActionContainer *menu = Core::ActionManager::actionContainer(
Constants::M_BUILDPROJECT); Constants::M_BUILDPROJECT);
QAction *action = new QAction(tr("QML Preview"), this); QAction *action = new QAction(QmlPreviewPlugin::tr("QML Preview"), this);
action->setToolTip(QLatin1String("Preview changes to QML code live in your application.")); action->setToolTip(QLatin1String("Preview changes to QML code live in your application."));
action->setEnabled(SessionManager::startupProject() != nullptr); action->setEnabled(SessionManager::startupProject() != nullptr);
connect(SessionManager::instance(), &SessionManager::startupProjectChanged, action, connect(SessionManager::instance(), &SessionManager::startupProjectChanged, action,
@@ -132,11 +176,11 @@ bool QmlPreviewPlugin::initialize(const QStringList &arguments, QString *errorSt
menu = Core::ActionManager::actionContainer(Constants::M_FILECONTEXT); menu = Core::ActionManager::actionContainer(Constants::M_FILECONTEXT);
action = new QAction(tr("Preview File"), this); action = new QAction(tr("Preview File"), this);
action->setEnabled(false); action->setEnabled(false);
connect(this, &QmlPreviewPlugin::runningPreviewsChanged, connect(q, &QmlPreviewPlugin::runningPreviewsChanged,
action, [action](const QmlPreviewRunControlList &previews) { action, [action](const QmlPreviewRunControlList &previews) {
action->setEnabled(!previews.isEmpty()); action->setEnabled(!previews.isEmpty());
}); });
connect(action, &QAction::triggered, this, &QmlPreviewPlugin::previewCurrentFile); connect(action, &QAction::triggered, this, &QmlPreviewPluginPrivate::previewCurrentFile);
menu->addAction(Core::ActionManager::registerAction(action, "QmlPreview.Preview", menu->addAction(Core::ActionManager::registerAction(action, "QmlPreview.Preview",
projectTreeContext), projectTreeContext),
Constants::G_FILE_OTHER); Constants::G_FILE_OTHER);
@@ -151,23 +195,23 @@ bool QmlPreviewPlugin::initialize(const QStringList &arguments, QString *errorSt
QmlPreviewParser *parser = new QmlPreviewParser; QmlPreviewParser *parser = new QmlPreviewParser;
parser->moveToThread(&m_parseThread); parser->moveToThread(&m_parseThread);
connect(this, &QObject::destroyed, parser, &QObject::deleteLater); connect(this, &QObject::destroyed, parser, &QObject::deleteLater);
connect(this, &QmlPreviewPlugin::checkDocument, parser, &QmlPreviewParser::parse); connect(q, &QmlPreviewPlugin::checkDocument, parser, &QmlPreviewParser::parse);
connect(this, &QmlPreviewPlugin::previewedFileChanged, this, &QmlPreviewPlugin::checkFile); connect(q, &QmlPreviewPlugin::previewedFileChanged, this, &QmlPreviewPluginPrivate::checkFile);
connect(parser, &QmlPreviewParser::success, this, &QmlPreviewPlugin::triggerPreview); connect(parser, &QmlPreviewParser::success, this, &QmlPreviewPluginPrivate::triggerPreview);
RunControl::registerWorkerCreator(Constants::QML_PREVIEW_RUN_MODE, RunControl::registerWorkerCreator(Constants::QML_PREVIEW_RUN_MODE,
[this](RunControl *runControl) { [this](RunControl *runControl) {
QmlPreviewRunner *runner = new QmlPreviewRunner(runControl, m_fileLoader, m_fileClassifer, QmlPreviewRunner *runner = new QmlPreviewRunner(runControl, m_fileLoader, m_fileClassifer,
m_fpsHandler, m_zoomFactor, m_locale); m_fpsHandler, m_zoomFactor, m_locale);
QObject::connect(this, &QmlPreviewPlugin::updatePreviews, QObject::connect(q, &QmlPreviewPlugin::updatePreviews,
runner, &QmlPreviewRunner::loadFile); runner, &QmlPreviewRunner::loadFile);
QObject::connect(this, &QmlPreviewPlugin::rerunPreviews, QObject::connect(q, &QmlPreviewPlugin::rerunPreviews,
runner, &QmlPreviewRunner::rerun); runner, &QmlPreviewRunner::rerun);
QObject::connect(runner, &QmlPreviewRunner::ready, QObject::connect(runner, &QmlPreviewRunner::ready,
this, &QmlPreviewPlugin::previewCurrentFile); this, &QmlPreviewPluginPrivate::previewCurrentFile);
QObject::connect(this, &QmlPreviewPlugin::zoomFactorChanged, QObject::connect(q, &QmlPreviewPlugin::zoomFactorChanged,
runner, &QmlPreviewRunner::zoom); runner, &QmlPreviewRunner::zoom);
QObject::connect(this, &QmlPreviewPlugin::localeChanged, QObject::connect(q, &QmlPreviewPlugin::localeChanged,
runner, &QmlPreviewRunner::language); runner, &QmlPreviewRunner::language);
QObject::connect(runner, &RunWorker::started, this, [this, runControl]() { QObject::connect(runner, &RunWorker::started, this, [this, runControl]() {
@@ -181,6 +225,20 @@ bool QmlPreviewPlugin::initialize(const QStringList &arguments, QString *errorSt
}); });
attachToEditor(); attachToEditor();
}
QmlPreviewPlugin::~QmlPreviewPlugin()
{
delete d;
}
bool QmlPreviewPlugin::initialize(const QStringList &arguments, QString *errorString)
{
Q_UNUSED(arguments)
Q_UNUSED(errorString)
d = new QmlPreviewPluginPrivate(this);
return true; return true;
} }
@@ -190,8 +248,8 @@ void QmlPreviewPlugin::extensionsInitialized()
ExtensionSystem::IPlugin::ShutdownFlag QmlPreviewPlugin::aboutToShutdown() ExtensionSystem::IPlugin::ShutdownFlag QmlPreviewPlugin::aboutToShutdown()
{ {
m_parseThread.quit(); d->m_parseThread.quit();
m_parseThread.wait(); d->m_parseThread.wait();
return SynchronousShutdown; return SynchronousShutdown;
} }
@@ -207,94 +265,94 @@ QVector<QObject *> QmlPreviewPlugin::createTestObjects() const
QString QmlPreviewPlugin::previewedFile() const QString QmlPreviewPlugin::previewedFile() const
{ {
return m_previewedFile; return d->m_previewedFile;
} }
void QmlPreviewPlugin::setPreviewedFile(const QString &previewedFile) void QmlPreviewPlugin::setPreviewedFile(const QString &previewedFile)
{ {
if (m_previewedFile == previewedFile) if (d->m_previewedFile == previewedFile)
return; return;
m_previewedFile = previewedFile; d->m_previewedFile = previewedFile;
emit previewedFileChanged(m_previewedFile); emit previewedFileChanged(d->m_previewedFile);
} }
QmlPreviewRunControlList QmlPreviewPlugin::runningPreviews() const QmlPreviewRunControlList QmlPreviewPlugin::runningPreviews() const
{ {
return m_runningPreviews; return d->m_runningPreviews;
} }
QmlPreviewFileLoader QmlPreviewPlugin::fileLoader() const QmlPreviewFileLoader QmlPreviewPlugin::fileLoader() const
{ {
return m_fileLoader; return d->m_fileLoader;
} }
QmlPreviewFileClassifier QmlPreviewPlugin::fileClassifier() const QmlPreviewFileClassifier QmlPreviewPlugin::fileClassifier() const
{ {
return m_fileClassifer; return d->m_fileClassifer;
} }
void QmlPreviewPlugin::setFileClassifier(QmlPreviewFileClassifier fileClassifer) void QmlPreviewPlugin::setFileClassifier(QmlPreviewFileClassifier fileClassifer)
{ {
if (m_fileClassifer == fileClassifer) if (d->m_fileClassifer == fileClassifer)
return; return;
m_fileClassifer = fileClassifer; d->m_fileClassifer = fileClassifer;
emit fileClassifierChanged(m_fileClassifer); emit fileClassifierChanged(d->m_fileClassifer);
} }
float QmlPreviewPlugin::zoomFactor() const float QmlPreviewPlugin::zoomFactor() const
{ {
return m_zoomFactor; return d->m_zoomFactor;
} }
void QmlPreviewPlugin::setZoomFactor(float zoomFactor) void QmlPreviewPlugin::setZoomFactor(float zoomFactor)
{ {
if (m_zoomFactor == zoomFactor) if (d->m_zoomFactor == zoomFactor)
return; return;
m_zoomFactor = zoomFactor; d->m_zoomFactor = zoomFactor;
emit zoomFactorChanged(m_zoomFactor); emit zoomFactorChanged(d->m_zoomFactor);
} }
QmlPreviewFpsHandler QmlPreviewPlugin::fpsHandler() const QmlPreviewFpsHandler QmlPreviewPlugin::fpsHandler() const
{ {
return m_fpsHandler; return d->m_fpsHandler;
} }
void QmlPreviewPlugin::setFpsHandler(QmlPreviewFpsHandler fpsHandler) void QmlPreviewPlugin::setFpsHandler(QmlPreviewFpsHandler fpsHandler)
{ {
if (m_fpsHandler == fpsHandler) if (d->m_fpsHandler == fpsHandler)
return; return;
m_fpsHandler = fpsHandler; d->m_fpsHandler = fpsHandler;
emit fpsHandlerChanged(m_fpsHandler); emit fpsHandlerChanged(d->m_fpsHandler);
} }
QString QmlPreviewPlugin::locale() const QString QmlPreviewPlugin::locale() const
{ {
return m_locale; return d->m_locale;
} }
void QmlPreviewPlugin::setLocale(const QString &locale) void QmlPreviewPlugin::setLocale(const QString &locale)
{ {
if (m_locale == locale) if (d->m_locale == locale)
return; return;
m_locale = locale; d->m_locale = locale;
emit localeChanged(m_locale); emit localeChanged(d->m_locale);
} }
void QmlPreviewPlugin::setFileLoader(QmlPreviewFileLoader fileLoader) void QmlPreviewPlugin::setFileLoader(QmlPreviewFileLoader fileLoader)
{ {
if (m_fileLoader == fileLoader) if (d->m_fileLoader == fileLoader)
return; return;
m_fileLoader = fileLoader; d->m_fileLoader = fileLoader;
emit fileLoaderChanged(m_fileLoader); emit fileLoaderChanged(d->m_fileLoader);
} }
void QmlPreviewPlugin::previewCurrentFile() void QmlPreviewPluginPrivate::previewCurrentFile()
{ {
const Node *currentNode = ProjectTree::currentNode(); const Node *currentNode = ProjectTree::currentNode();
if (!currentNode || !currentNode->asFileNode() if (!currentNode || !currentNode->asFileNode()
@@ -303,16 +361,16 @@ void QmlPreviewPlugin::previewCurrentFile()
const QString file = currentNode->filePath().toString(); const QString file = currentNode->filePath().toString();
if (file != m_previewedFile) if (file != m_previewedFile)
setPreviewedFile(file); q->setPreviewedFile(file);
else else
checkFile(file); checkFile(file);
} }
void QmlPreviewPlugin::onEditorChanged(Core::IEditor *editor) void QmlPreviewPluginPrivate::onEditorChanged(Core::IEditor *editor)
{ {
if (m_lastEditor) { if (m_lastEditor) {
Core::IDocument *doc = m_lastEditor->document(); Core::IDocument *doc = m_lastEditor->document();
disconnect(doc, &Core::IDocument::contentsChanged, this, &QmlPreviewPlugin::setDirty); disconnect(doc, &Core::IDocument::contentsChanged, this, &QmlPreviewPluginPrivate::setDirty);
if (m_dirty) { if (m_dirty) {
m_dirty = false; m_dirty = false;
checkEditor(); checkEditor();
@@ -323,11 +381,11 @@ void QmlPreviewPlugin::onEditorChanged(Core::IEditor *editor)
if (m_lastEditor) { if (m_lastEditor) {
// Handle new editor // Handle new editor
connect(m_lastEditor->document(), &Core::IDocument::contentsChanged, connect(m_lastEditor->document(), &Core::IDocument::contentsChanged,
this, &QmlPreviewPlugin::setDirty); this, &QmlPreviewPluginPrivate::setDirty);
} }
} }
void QmlPreviewPlugin::onEditorAboutToClose(Core::IEditor *editor) void QmlPreviewPluginPrivate::onEditorAboutToClose(Core::IEditor *editor)
{ {
if (m_lastEditor != editor) if (m_lastEditor != editor)
return; return;
@@ -335,7 +393,7 @@ void QmlPreviewPlugin::onEditorAboutToClose(Core::IEditor *editor)
// Oh no our editor is going to be closed // Oh no our editor is going to be closed
// get the content first // get the content first
Core::IDocument *doc = m_lastEditor->document(); Core::IDocument *doc = m_lastEditor->document();
disconnect(doc, &Core::IDocument::contentsChanged, this, &QmlPreviewPlugin::setDirty); disconnect(doc, &Core::IDocument::contentsChanged, this, &QmlPreviewPluginPrivate::setDirty);
if (m_dirty) { if (m_dirty) {
m_dirty = false; m_dirty = false;
checkEditor(); checkEditor();
@@ -343,7 +401,7 @@ void QmlPreviewPlugin::onEditorAboutToClose(Core::IEditor *editor)
m_lastEditor = nullptr; m_lastEditor = nullptr;
} }
void QmlPreviewPlugin::setDirty() void QmlPreviewPluginPrivate::setDirty()
{ {
m_dirty = true; m_dirty = true;
QTimer::singleShot(1000, this, [this](){ QTimer::singleShot(1000, this, [this](){
@@ -354,28 +412,28 @@ void QmlPreviewPlugin::setDirty()
}); });
} }
void QmlPreviewPlugin::addPreview(ProjectExplorer::RunControl *preview) void QmlPreviewPluginPrivate::addPreview(ProjectExplorer::RunControl *preview)
{ {
m_runningPreviews.append(preview); m_runningPreviews.append(preview);
emit runningPreviewsChanged(m_runningPreviews); emit q->runningPreviewsChanged(m_runningPreviews);
} }
void QmlPreviewPlugin::removePreview(ProjectExplorer::RunControl *preview) void QmlPreviewPluginPrivate::removePreview(ProjectExplorer::RunControl *preview)
{ {
m_runningPreviews.removeOne(preview); m_runningPreviews.removeOne(preview);
emit runningPreviewsChanged(m_runningPreviews); emit q->runningPreviewsChanged(m_runningPreviews);
} }
void QmlPreviewPlugin::attachToEditor() void QmlPreviewPluginPrivate::attachToEditor()
{ {
Core::EditorManager *editorManager = Core::EditorManager::instance(); Core::EditorManager *editorManager = Core::EditorManager::instance();
connect(editorManager, &Core::EditorManager::currentEditorChanged, connect(editorManager, &Core::EditorManager::currentEditorChanged,
this, &QmlPreviewPlugin::onEditorChanged); this, &QmlPreviewPluginPrivate::onEditorChanged);
connect(editorManager, &Core::EditorManager::editorAboutToClose, connect(editorManager, &Core::EditorManager::editorAboutToClose,
this, &QmlPreviewPlugin::onEditorAboutToClose); this, &QmlPreviewPluginPrivate::onEditorAboutToClose);
} }
void QmlPreviewPlugin::checkEditor() void QmlPreviewPluginPrivate::checkEditor()
{ {
QmlJS::Dialect::Enum dialect = QmlJS::Dialect::AnyLanguage; QmlJS::Dialect::Enum dialect = QmlJS::Dialect::AnyLanguage;
Core::IDocument *doc = m_lastEditor->document(); Core::IDocument *doc = m_lastEditor->document();
@@ -401,10 +459,10 @@ void QmlPreviewPlugin::checkEditor()
dialect = QmlJS::Dialect::QmlQtQuick2Ui; dialect = QmlJS::Dialect::QmlQtQuick2Ui;
else else
dialect = QmlJS::Dialect::NoLanguage; dialect = QmlJS::Dialect::NoLanguage;
emit checkDocument(doc->filePath().toString(), doc->contents(), dialect); emit q->checkDocument(doc->filePath().toString(), doc->contents(), dialect);
} }
void QmlPreviewPlugin::checkFile(const QString &fileName) void QmlPreviewPluginPrivate::checkFile(const QString &fileName)
{ {
if (!m_fileLoader) if (!m_fileLoader)
return; return;
@@ -413,17 +471,17 @@ void QmlPreviewPlugin::checkFile(const QString &fileName)
const QByteArray contents = m_fileLoader(fileName, &success); const QByteArray contents = m_fileLoader(fileName, &success);
if (success) { if (success) {
emit checkDocument(fileName, contents, emit q->checkDocument(fileName, contents,
QmlJS::ModelManagerInterface::guessLanguageOfFile(fileName).dialect()); QmlJS::ModelManagerInterface::guessLanguageOfFile(fileName).dialect());
} }
} }
void QmlPreviewPlugin::triggerPreview(const QString &changedFile, const QByteArray &contents) void QmlPreviewPluginPrivate::triggerPreview(const QString &changedFile, const QByteArray &contents)
{ {
if (m_previewedFile.isEmpty()) if (m_previewedFile.isEmpty())
previewCurrentFile(); previewCurrentFile();
else else
emit updatePreviews(m_previewedFile, changedFile, contents); emit q->updatePreviews(m_previewedFile, changedFile, contents);
} }
QmlPreviewParser::QmlPreviewParser() QmlPreviewParser::QmlPreviewParser()

View File

@@ -61,6 +61,8 @@ class QmlPreviewPlugin : public ExtensionSystem::IPlugin
Q_PROPERTY(QString locale READ locale WRITE setLocale NOTIFY localeChanged) Q_PROPERTY(QString locale READ locale WRITE setLocale NOTIFY localeChanged)
public: public:
~QmlPreviewPlugin() override;
bool initialize(const QStringList &arguments, QString *errorString) override; bool initialize(const QStringList &arguments, QString *errorString) override;
void extensionsInitialized() override; void extensionsInitialized() override;
ShutdownFlag aboutToShutdown() override; ShutdownFlag aboutToShutdown() override;
@@ -101,28 +103,7 @@ signals:
void localeChanged(const QString &locale); void localeChanged(const QString &locale);
private: private:
void previewCurrentFile(); class QmlPreviewPluginPrivate *d = nullptr;
void onEditorChanged(Core::IEditor *editor);
void onEditorAboutToClose(Core::IEditor *editor);
void setDirty();
void addPreview(ProjectExplorer::RunControl *preview);
void removePreview(ProjectExplorer::RunControl *preview);
void attachToEditor();
void checkEditor();
void checkFile(const QString &fileName);
void triggerPreview(const QString &changedFile, const QByteArray &contents);
QThread m_parseThread;
QString m_previewedFile;
QmlPreviewFileLoader m_fileLoader = nullptr;
Core::IEditor *m_lastEditor = nullptr;
QmlPreviewRunControlList m_runningPreviews;
bool m_dirty = false;
QmlPreview::QmlPreviewFileClassifier m_fileClassifer = nullptr;
float m_zoomFactor = -1.0;
QmlPreview::QmlPreviewFpsHandler m_fpsHandler = nullptr;
QString m_locale;
std::unique_ptr<ProjectExplorer::RunWorkerFactory> m_runWorkerFactory;
}; };
} // namespace Internal } // namespace Internal