AssetExporter: Add basic infrastructure for the plugin

Adds export action, basic UI and workflow classes

Change-Id: If019a8fa48cacaf7e7665335c53b3adeeb257b07
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Vikas Pachdha
2020-06-04 22:31:32 +02:00
parent fe5636e53b
commit 818e263122
21 changed files with 1449 additions and 1 deletions

View File

@@ -0,0 +1,94 @@
/****************************************************************************
**
** Copyright (C) 2020 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 "assetexportdialog.h"
#include "ui_assetexportdialog.h"
#include "utils/fileutils.h"
#include <QPushButton>
#include <QDialogButtonBox>
#include <QMessageBox>
#include <algorithm>
namespace QmlDesigner {
AssetExportDialog::AssetExportDialog(const Utils::FilePath &exportPath,
AssetExporter &assetExporter, QWidget *parent) :
QDialog(parent),
m_assetExporter(assetExporter),
m_ui(new Ui::AssetExportDialog),
m_model(this)
{
m_ui->setupUi(this);
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
m_ui->filesView->setModel(&m_model);
m_exportBtn = m_ui->buttonBox->addButton(tr("Export"), QDialogButtonBox::AcceptRole);
m_exportBtn->setEnabled(false);
connect(m_exportBtn, &QPushButton::clicked, this, &AssetExportDialog::onExport);
m_ui->exportPathEdit->setText(exportPath.toString());
connect(&m_assetExporter, &AssetExporter::qmlFileResult, this, [this] (const Utils::FilePath &path) {
m_qmlFiles.append(path);
QStringList files = m_model.stringList();
files.append(path.toString());
m_model.setStringList(files);
});
connect(&m_assetExporter, &AssetExporter::stateChanged,
this, &AssetExportDialog::onExportStateChanged);
}
AssetExportDialog::~AssetExportDialog()
{
m_assetExporter.cancel();
}
void AssetExportDialog::onExport()
{
m_assetExporter.exportQml(m_qmlFiles,
Utils::FilePath::fromString(m_ui->exportPathEdit->text()));
}
void AssetExportDialog::onExportStateChanged(AssetExporter::ParsingState newState)
{
switch (newState) {
case AssetExporter::ParsingState::PreProcessing:
m_model.setStringList({});
break;
case AssetExporter::ParsingState::ExportingDone:
QMessageBox::information(this, tr("QML Export"), tr("Done"));
break;
default:
break;
}
m_exportBtn->setEnabled(newState == AssetExporter::ParsingState::PreProcessingFinished ||
newState == AssetExporter::ParsingState::ExportingDone);
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(m_assetExporter.isBusy());
}
}

View File

@@ -0,0 +1,66 @@
/****************************************************************************
**
** Copyright (C) 2020 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 "assetexporter.h"
#include <QDialog>
#include <QStringListModel>
#include "utils/fileutils.h"
#include <memory>
QT_BEGIN_NAMESPACE
class QPushButton;
QT_END_NAMESPACE
namespace Ui {
class AssetExportDialog;
}
namespace QmlDesigner {
class AssetExportDialog : public QDialog
{
Q_OBJECT
public:
explicit AssetExportDialog(const Utils::FilePath &exportPath, AssetExporter &assetExporter,
QWidget *parent = nullptr);
~AssetExportDialog();
private:
void onExport();
void onExportStateChanged(AssetExporter::ParsingState newState);
private:
AssetExporter &m_assetExporter;
std::unique_ptr<Ui::AssetExportDialog> m_ui;
QPushButton *m_exportBtn = nullptr;
QStringListModel m_model;
Utils::FilePaths m_qmlFiles;
};
}

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AssetExportDialog</class>
<widget class="QDialog" name="AssetExportDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>768</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>Export QML</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QListView" name="filesView"/>
</item>
<item row="0" column="0">
<widget class="QLineEdit" name="exportPathEdit"/>
</item>
<item row="2" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,255 @@
/****************************************************************************
**
** Copyright (C) 2020 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 "assetexporter.h"
#include "componentexporter.h"
#include "plaintexteditmodifier.h"
#include "rewriterview.h"
#include "projectexplorer/project.h"
#include "projectexplorer/projectnodes.h"
#include "utils/runextensions.h"
#include "utils/qtcassert.h"
#include <QJsonArray>
#include <QJsonDocument>
#include <QLoggingCategory>
#include <QMessageBox>
#include <QPlainTextEdit>
using namespace ProjectExplorer;
namespace {
Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.assetExporter", QtInfoMsg)
Q_LOGGING_CATEGORY(loggerWarn, "qtc.designer.assetExportPlugin.assetExporter", QtWarningMsg)
Q_LOGGING_CATEGORY(loggerError, "qtc.designer.assetExportPlugin.assetExporter", QtCriticalMsg)
void findQmlFiles(QFutureInterface<Utils::FilePath> &f, const Project *project)
{
if (!project && !f.isCanceled())
f.reportFinished({});
int index = 0;
Utils::FilePaths qmlFiles = project->files([&f, &index](const Node* node) ->bool {
if (f.isCanceled())
return false;
Utils::FilePath path = node->filePath();
bool isComponent = !path.fileName().isEmpty() && path.fileName().front().isUpper();
if (isComponent && node->filePath().endsWith(".ui.qml"))
f.reportResult(path, index++);
return true;
});
f.reportFinished();
}
//static QmlDesigner::Model* createModel(const Utils::FilePath &fileName)
//{
// QmlDesigner::Model *model = QmlDesigner::Model::create("Item", 2, 7);
// Utils::FileReader reader;
// QTC_ASSERT(reader.fetch(fileName.toString()), return nullptr);
// auto textEdit = new QPlainTextEdit;
// textEdit->setPlainText(QString::fromUtf8(reader.data()));
// auto modifier = new QmlDesigner::NotIndentingTextEditModifier(textEdit);
// modifier->setParent(model);
// auto rewriterView = new QmlDesigner::RewriterView(QmlDesigner::RewriterView::Validate, model);
// rewriterView->setCheckSemanticErrors(false);
// rewriterView->setTextModifier(modifier);
// model->attachView(rewriterView);
// QTC_ASSERT(rewriterView->rootModelNode().isValid(), return nullptr);
// return model;
//}
}
namespace QmlDesigner {
AssetExporter::AssetExporter(AssetExporterView *view, ProjectExplorer::Project *project, QObject *parent) :
QObject(parent),
m_currentState(*this),
m_project(project),
m_view(view)
{
connect(m_view, &AssetExporterView::loadingFinished, this, &AssetExporter::onQmlFileLoaded);
connect(m_view, &AssetExporterView::loadingError, this, &AssetExporter::notifyLoadError);
}
AssetExporter::~AssetExporter()
{
cancel();
}
bool AssetExporter::preProcessProject()
{
if (m_preprocessWatcher && !m_preprocessWatcher->isCanceled() &&
!m_preprocessWatcher->isFinished()) {
qCDebug(loggerInfo) << "Previous pre-processing not finished.";
return false;
}
m_currentState.change(ParsingState::PreProcessing);
m_preprocessWatcher.reset(new QFutureWatcher<Utils::FilePath>(this));
connect(m_preprocessWatcher.get(), &QFutureWatcher<Utils::FilePath>::resultReadyAt, this,
[this](int index) {
emit qmlFileResult(m_preprocessWatcher->resultAt(index));
});
connect(m_preprocessWatcher.get(), &QFutureWatcher<Utils::FilePath>::finished, this,
[this] () { m_currentState.change(ParsingState::PreProcessingFinished); });
QFuture<Utils::FilePath> f = Utils::runAsync(&findQmlFiles, m_project);
m_preprocessWatcher->setFuture(f);
return true;
}
void AssetExporter::exportQml(const Utils::FilePaths &qmlFiles, const Utils::FilePath &exportPath,
bool exportAssets)
{
// TODO Asset export
Q_UNUSED(exportAssets);
m_exportFiles = qmlFiles;
m_components = QJsonArray();
m_exportPath = exportPath;
m_currentState.change(ParsingState::Parsing);
triggerLoadNextFile();
}
void AssetExporter::cancel()
{
if (m_preprocessWatcher && !m_preprocessWatcher->isCanceled() &&
!m_preprocessWatcher->isFinished()) {
m_preprocessWatcher->cancel();
m_preprocessWatcher->waitForFinished();
}
}
bool AssetExporter::isBusy() const
{
return m_currentState == AssetExporter::ParsingState::PreProcessing ||
m_currentState == AssetExporter::ParsingState::Parsing ||
m_currentState == AssetExporter::ParsingState::ExportingAssets ||
m_currentState == AssetExporter::ParsingState::WritingJson;
}
void AssetExporter::exportComponent(const ModelNode &rootNode)
{
qCDebug(loggerInfo) << "Exporting component" << rootNode.id();
ComponentExporter exporter(rootNode);
QJsonObject json = exporter.exportComponent();
m_components.append(json);
}
void AssetExporter::notifyLoadError(AssetExporterView::LoadState state)
{
QString errorStr = tr("Unknown error.");
switch (state) {
case AssetExporterView::LoadState::Exausted:
errorStr = tr("Loading file is taking too long.");
break;
case AssetExporterView::LoadState::QmlErrorState:
errorStr = tr("Cannot parse. QML file has errors.");
break;
default:
return;
}
// TODO. Communicate errors to user
qCDebug(loggerError) << "QML load error:" << errorStr;
}
void AssetExporter::onQmlFileLoaded()
{
QTC_ASSERT(m_view && m_view->model(), qCDebug(loggerError) << "Null model"; return);
qCDebug(loggerInfo) << "Qml file load done" << m_view->model()->fileUrl();
exportComponent(m_view->rootModelNode());
triggerLoadNextFile();
}
void AssetExporter::triggerLoadNextFile()
{
QTimer::singleShot(0, this, &AssetExporter::loadNextFile);
}
void AssetExporter::loadNextFile()
{
if (m_exportFiles.isEmpty()) {
m_currentState.change(ParsingState::ParsingFinished);
writeMetadata();
return;
}
// Load the next pending file.
const Utils::FilePath file = m_exportFiles.takeFirst();
qCDebug(loggerInfo) << "Loading next file" << file;
m_view->loadQmlFile(file);
}
void AssetExporter::writeMetadata() const
{
qCDebug(loggerInfo) << "Writing metadata";
m_currentState.change(ParsingState::WritingJson);
QJsonObject jsonRoot; // TODO: Write plugin info to root
jsonRoot.insert("artboards", m_components);
QJsonDocument doc(jsonRoot);
if (doc.isNull() || doc.isEmpty()) {
qCDebug(loggerWarn) << "Empty JSON document";
} else {
Utils::FileSaver saver(m_exportPath.toString(), QIODevice::Text);
saver.write(doc.toJson(QJsonDocument::Indented));
if (!saver.finalize()) {
qCDebug(loggerError) << "Cannot write Metadata file: " << saver.errorString();
}
}
m_currentState.change(ParsingState::ExportingDone);
}
AssetExporter::State::State(AssetExporter &exporter) :
m_assetExporter(exporter)
{
}
void AssetExporter::State::change(const ParsingState &state)
{
qCDebug(loggerInfo()) << "Assetimporter State change: Old: " << m_state << "New: " << state;
if (m_state != state) {
m_state = state;
m_assetExporter.stateChanged(m_state);
}
}
QDebug operator<<(QDebug os, const AssetExporter::ParsingState &s)
{
os << static_cast<std::underlying_type<QmlDesigner::AssetExporter::ParsingState>::type>(s);
return os;
}
}

View File

@@ -0,0 +1,105 @@
/****************************************************************************
**
** Copyright (C) 2020 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 "assetexporterview.h"
#include "utils/fileutils.h"
#include <QFutureWatcher>
#include <QJsonArray>
#include <QJsonObject>
#include <memory>
QT_BEGIN_NAMESPACE
class QJsonArray;
QT_END_NAMESPACE
namespace ProjectExplorer {
class Project;
}
namespace QmlDesigner {
class AssetExporter : public QObject
{
Q_OBJECT
public:
enum class ParsingState {
Idle = 0,
PreProcessing,
PreProcessingFinished,
Parsing,
ParsingFinished,
ExportingAssets,
ExportingAssetsFinished,
WritingJson,
ExportingDone
};
AssetExporter(AssetExporterView *view, ProjectExplorer::Project *project,
QObject *parent = nullptr);
~AssetExporter();
bool preProcessProject();
void exportQml(const Utils::FilePaths &qmlFiles, const Utils::FilePath &exportPath, bool exportAssets = false);
void cancel();
bool isBusy() const;
signals:
void qmlFileResult(Utils::FilePath);
void stateChanged(ParsingState);
private:
ParsingState currentState() const { return m_currentState.m_state; }
void exportComponent(const ModelNode &rootNode);
void writeMetadata() const;
void notifyLoadError(AssetExporterView::LoadState state);
void triggerLoadNextFile();
void loadNextFile();
void onQmlFileLoaded();
private:
mutable class State {
public:
State(AssetExporter&);
void change(const ParsingState &state);
operator ParsingState() const { return m_state; }
AssetExporter &m_assetExporter;
ParsingState m_state = ParsingState::Idle;
} m_currentState;
ProjectExplorer::Project *m_project = nullptr;
AssetExporterView *m_view = nullptr;
Utils::FilePaths m_exportFiles;
Utils::FilePath m_exportPath;
QJsonArray m_components;
std::unique_ptr<QFutureWatcher<Utils::FilePath>> m_preprocessWatcher;
};
QDebug operator<< (QDebug os, const QmlDesigner::AssetExporter::ParsingState& s);
} // namespace QmlDesigner

View File

@@ -0,0 +1,124 @@
/****************************************************************************
**
** Copyright (C) 2020 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 "assetexporterplugin.h"
#include "assetexportpluginconstants.h"
#include "assetexportdialog.h"
#include "assetexporter.h"
#include "assetexporterview.h"
#include "componentexporter.h"
#include "parsers/modelitemnodeparser.h"
#include "coreplugin/actionmanager/actionmanager.h"
#include "coreplugin/actionmanager/actioncontainer.h"
#include "coreplugin/documentmanager.h"
#include "qmldesigner/qmldesignerplugin.h"
#include "projectexplorer/projectexplorerconstants.h"
#include "projectexplorer/session.h"
#include "projectexplorer/project.h"
#include "projectexplorer/session.h"
#include "extensionsystem/pluginmanager.h"
#include "extensionsystem/pluginspec.h"
#include "utils/algorithm.h"
#include <QCoreApplication>
#include <QAction>
#include <QLoggingCategory>
namespace QmlDesigner {
AssetExporterPlugin::AssetExporterPlugin() :
m_view(new AssetExporterView)
{
auto *designerPlugin = QmlDesigner::QmlDesignerPlugin::instance();
auto &viewManager = designerPlugin->viewManager();
viewManager.registerViewTakingOwnership(m_view);
// Add parsers templates for factory instantiation.
ComponentExporter::addNodeParser<ItemNodeParser>();
// Instantiate actions created by the plugin.
addActions();
connect(ProjectExplorer::SessionManager::instance(),
&ProjectExplorer::SessionManager::startupProjectChanged,
this, &AssetExporterPlugin::updateActions);
updateActions();
}
QString AssetExporterPlugin::pluginName() const
{
return QLatin1String("AssetExporterPlugin");
}
void AssetExporterPlugin::onExport()
{
auto startupProject = ProjectExplorer::SessionManager::startupProject();
if (!startupProject)
return;
auto exportDir = startupProject->projectFilePath().parentDir();
if (!exportDir.toFileInfo().isRoot())
exportDir = exportDir.parentDir();
auto defaultMetadataPath = exportDir.pathAppended(startupProject->displayName() + ".metadata");
AssetExporter assetExporter(m_view, startupProject);
AssetExportDialog assetExporterDialog(defaultMetadataPath, assetExporter);
assetExporter.preProcessProject();
assetExporterDialog.exec();
}
void AssetExporterPlugin::addActions()
{
auto exportAction = new QAction(tr("Export QML"));
exportAction->setToolTip(tr("Export QML code of the current project."));
connect(exportAction, &QAction::triggered, this, &AssetExporterPlugin::onExport);
Core::Command *cmd = Core::ActionManager::registerAction(exportAction, Constants::EXPORT_QML);
// Add action to build menu
Core::ActionContainer *buildMenu =
Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT);
buildMenu->addAction(cmd, ProjectExplorer::Constants::G_BUILD_RUN);
}
void AssetExporterPlugin::updateActions()
{
auto project = ProjectExplorer::SessionManager::startupProject();
QAction* const exportAction = Core::ActionManager::command(Constants::EXPORT_QML)->action();
exportAction->setEnabled(project && !project->needsConfiguration());
}
QString AssetExporterPlugin::metaInfo() const
{
return QLatin1String(":/assetexporterplugin/assetexporterplugin.metainfo");
}
} //QmlDesigner

View File

@@ -0,0 +1,56 @@
/****************************************************************************
**
** Copyright (C) 2020 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>
namespace QmlDesigner {
class AssetExporter;
class AssetExporterView;
class AssetExporterPlugin : public QObject, QmlDesigner::IWidgetPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QmlDesignerPlugin" FILE "assetexporterplugin.json")
Q_DISABLE_COPY(AssetExporterPlugin)
Q_INTERFACES(QmlDesigner::IWidgetPlugin)
public:
AssetExporterPlugin();
QString metaInfo() const;
QString pluginName() const;
private:
void onExport();
void addActions();
void updateActions();
AssetExporterView *m_view = nullptr;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,2 @@
MetaInfo {
}

View File

@@ -0,0 +1,29 @@
QT *= qml quick core widgets
VPATH += $$PWD
RESOURCES += assetexporterplugin.qrc
INCLUDEPATH += ./
HEADERS += \
assetexportdialog.h \
assetexporter.h \
assetexporterplugin.h \
assetexporterview.h \
assetexportpluginconstants.h \
componentexporter.h \
parsers/modelitemnodeparser.h \
parsers/modelnodeparser.h
SOURCES += \
assetexportdialog.cpp \
assetexporter.cpp \
assetexporterplugin.cpp \
assetexporterview.cpp \
componentexporter.cpp \
parsers/modelitemnodeparser.cpp \
parsers/modelnodeparser.cpp
FORMS += \
assetexportdialog.ui

View File

@@ -0,0 +1,17 @@
include (../../../../qtcreator.pri)
include (../plugindestdir.pri)
include (../designercore/iwidgetplugin.pri)
include (../qmldesigner_dependencies.pri)
include (assetexporterplugin.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)
TARGET = assetexporterplugin
TEMPLATE = lib
CONFIG += plugin

View File

@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/assetexporterplugin">
<file>assetexporterplugin.metainfo</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,149 @@
/****************************************************************************
**
** Copyright (C) 2020 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 "assetexporterview.h"
#include "qmlitemnode.h"
#include "rewriterview.h"
#include "coreplugin/editormanager/editormanager.h"
#include "coreplugin/editormanager/ieditor.h"
#include "coreplugin/modemanager.h"
#include "coreplugin/coreconstants.h"
#include <QLoggingCategory>
namespace {
Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.view", QtInfoMsg)
Q_LOGGING_CATEGORY(loggerWarn, "qtc.designer.assetExportPlugin.view", QtWarningMsg)
Q_LOGGING_CATEGORY(loggerError, "qtc.designer.assetExportPlugin.view", QtCriticalMsg)
static const int RetryIntervalMs = 500;
static const int MinRetry = 2;
}
namespace QmlDesigner {
AssetExporterView::AssetExporterView(QObject *parent) : AbstractView(parent),
m_timer(this)
{
m_timer.setInterval(RetryIntervalMs);
// We periodically check if file is loaded.
connect(&m_timer, &QTimer::timeout, this, &AssetExporterView::handleTimerTimeout);
}
bool AssetExporterView::loadQmlFile(const Utils::FilePath &path, uint timeoutSecs)
{
qCDebug(loggerInfo) << "Load file" << path;
if (loadingState() == LoadState::Busy)
return false;
setState(LoadState::Busy);
m_retryCount = std::max(MinRetry, static_cast<int>((timeoutSecs * 1000) / RetryIntervalMs));
Core::EditorManager::openEditor(path.toString(), Core::Id(),
Core::EditorManager::DoNotMakeVisible);
Core::ModeManager::activateMode(Core::Constants::MODE_DESIGN);
Core::ModeManager::setFocusToCurrentMode();
m_timer.start();
return true;
}
void AssetExporterView::modelAttached(Model *model)
{
if (model->rewriterView() && model->rewriterView()->inErrorState())
setState(LoadState::QmlErrorState);
AbstractView::modelAttached(model);
}
void AssetExporterView::
instanceInformationsChanged(const QMultiHash<ModelNode, InformationName> &informationChangeHash)
{
if (inErrorState() || loadingState() == LoadState::Loaded)
return; // Already reached error or connected state.
// We expect correct dimensions are available if the rootnode's
// information change message is received.
const auto nodes = informationChangeHash.keys();
bool hasRootNode = std::any_of(nodes.begin(), nodes.end(), [](const ModelNode &n) {
return n.isRootNode();
});
if (hasRootNode)
handleMaybeDone();
}
void AssetExporterView::instancesPreviewImageChanged(const QVector<ModelNode> &nodeList)
{
Q_UNUSED(nodeList);
emit previewChanged();
}
bool AssetExporterView::inErrorState() const
{
return m_state == LoadState::Exausted || m_state == LoadState::QmlErrorState;
}
bool AssetExporterView::isLoaded() const
{
return isAttached() && QmlItemNode(rootModelNode()).isValid();
}
void AssetExporterView::setState(AssetExporterView::LoadState state)
{
if (state != m_state) {
m_state = state;
qCDebug(loggerInfo) << "Loading state changed" << m_state;
if (inErrorState() || m_state == LoadState::Loaded) {
m_timer.stop();
if (m_state == LoadState::Loaded)
emit loadingFinished();
else
emit loadingError(m_state);
}
}
}
void AssetExporterView::handleMaybeDone()
{
if (isLoaded())
setState(LoadState::Loaded);
}
void AssetExporterView::handleTimerTimeout()
{
if (!inErrorState() && loadingState() != LoadState::Loaded)
handleMaybeDone();
if (--m_retryCount < 0)
setState(LoadState::Exausted);
}
}
QDebug operator<<(QDebug os, const QmlDesigner::AssetExporterView::LoadState &s)
{
os << static_cast<std::underlying_type<QmlDesigner::AssetExporterView::LoadState>::type>(s);
return os;
}

View File

@@ -0,0 +1,85 @@
/****************************************************************************
**
** Copyright (C) 2020 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 "abstractview.h"
#include "utils/fileutils.h"
#include <QObject>
#include <QTimer>
#include <memory>
namespace Core {
class IEditor;
}
namespace QmlDesigner {
class AssetExporterView : public AbstractView
{
Q_OBJECT
public:
enum class LoadState {
Idle = 1,
Busy,
Exausted,
QmlErrorState,
Loaded
};
AssetExporterView(QObject *parent = nullptr);
bool loadQmlFile(const Utils::FilePath &path, uint timeoutSecs = 10);
void modelAttached(Model *model) override;
void instanceInformationsChanged(const QMultiHash<ModelNode, InformationName> &informationChangeHash) override;
void instancesPreviewImageChanged(const QVector<ModelNode> &nodeList) override;
LoadState loadingState() const { return m_state; }
bool inErrorState() const;
signals:
void loadingFinished();
void loadingError(LoadState);
void previewChanged();
private:
bool isLoaded() const;
void setState(LoadState state);
void handleMaybeDone();
void handleTimerTimeout();
Core::IEditor *m_currentEditor = nullptr;
QTimer m_timer;
int m_retryCount = 0;
LoadState m_state = LoadState::Idle;
bool m_waitForPuppet = false;
};
}
QDebug operator<<(QDebug os, const QmlDesigner::AssetExporterView::LoadState &s);

View File

@@ -0,0 +1,33 @@
/****************************************************************************
**
** Copyright (C) 2020 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
namespace QmlDesigner {
namespace Constants {
const char EXPORT_QML[] = "Designer.ExportPlugin.ExportQml";
}
}

View File

@@ -0,0 +1,107 @@
/****************************************************************************
**
** Copyright (C) 2020 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 "componentexporter.h"
#include "parsers/modelnodeparser.h"
#include "model.h"
#include "nodeabstractproperty.h"
#include "rewriterview.h"
#include "utils/qtcassert.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QLoggingCategory>
namespace {
Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.modelExporter", QtInfoMsg)
static void populateLineage(const QmlDesigner::ModelNode &node, QByteArrayList &lineage)
{
if (!node.isValid() || node.type().isEmpty())
return;
lineage.append(node.type());
if (node.hasParentProperty())
populateLineage(node.parentProperty().parentModelNode(), lineage);
}
}
namespace QmlDesigner {
std::vector<std::unique_ptr<Internal::NodeParserCreatorBase>> ComponentExporter::m_readers;
ComponentExporter::ComponentExporter(const ModelNode &rootNode):
m_rootNode(rootNode)
{
}
QJsonObject ComponentExporter::exportComponent() const
{
QTC_ASSERT(m_rootNode.isValid(), return {});
return nodeToJson(m_rootNode);
}
ModelNodeParser *ComponentExporter::createNodeParser(const ModelNode &node) const
{
QByteArrayList lineage;
populateLineage(node, lineage);
std::unique_ptr<ModelNodeParser> reader;
for (auto &parserCreator: m_readers) {
std::unique_ptr<ModelNodeParser> r(parserCreator->instance(lineage, node));
if (r->isExportable()) {
if (reader) {
if (reader->priority() < r->priority())
reader = std::move(r);
} else {
reader = std::move(r);
}
}
}
if (!reader) {
qCDebug(loggerInfo()) << "No parser for node" << node;
}
return reader.release();
}
QJsonObject ComponentExporter::nodeToJson(const ModelNode &node) const
{
QJsonObject jsonObject;
std::unique_ptr<ModelNodeParser> parser(createNodeParser(node));
if (parser)
jsonObject = parser->json();
QJsonArray children;
for (const ModelNode &childnode : node.directSubModelNodes())
children.append(nodeToJson(childnode));
if (!children.isEmpty())
jsonObject.insert("children", children);
return jsonObject;
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,90 @@
/****************************************************************************
**
** Copyright (C) 2020 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 <QJsonValue>
#include <QPointer>
#include <QByteArrayList>
#include <vector>
#include <memory>
#include "utils/qtcassert.h"
QT_BEGIN_NAMESPACE
class QJsonArray;
QT_END_NAMESPACE
namespace QmlDesigner {
class Model;
class ModelNode;
class ComponentExporter;
class ModelNodeParser;
namespace Internal {
class NodeParserCreatorBase
{
public:
virtual ~NodeParserCreatorBase() {}
protected:
virtual ModelNodeParser *instance(const QByteArrayList &, const ModelNode &) const = 0;
friend class QmlDesigner::ComponentExporter;
};
template<class T>
class NodeParserCreator : public NodeParserCreatorBase
{
public:
NodeParserCreator() = default;
~NodeParserCreator() = default;
protected:
ModelNodeParser *instance(const QByteArrayList &lineage, const ModelNode &node) const {
return new T(lineage, node);
}
};
} //Internal
class ComponentExporter
{
public:
ComponentExporter(const ModelNode &rootNode);
QJsonObject exportComponent() const;
template<typename T> static void addNodeParser()
{
QTC_ASSERT((std::is_base_of<ModelNodeParser, T>::value), return);
m_readers.push_back(std::make_unique<Internal::NodeParserCreator<T>>());
}
private:
ModelNodeParser* createNodeParser(const ModelNode &node) const;
QJsonObject nodeToJson(const ModelNode &node) const;
private:
const ModelNode &m_rootNode;
static std::vector<std::unique_ptr<Internal::NodeParserCreatorBase>> m_readers;
};
}

View File

@@ -0,0 +1,66 @@
/****************************************************************************
**
** Copyright (C) 2020 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 "modelitemnodeparser.h"
#include "assetexportpluginconstants.h"
#include "modelnode.h"
#include "qmlitemnode.h"
#include "variantproperty.h"
namespace QmlDesigner {
ItemNodeParser::ItemNodeParser(const QByteArrayList &lineage, const ModelNode &node) :
ModelNodeParser(lineage, node)
{
}
bool QmlDesigner::ItemNodeParser::isExportable() const
{
return m_lineage.contains("QtQuick.Item");
}
QJsonObject QmlDesigner::ItemNodeParser::json() const
{
// TODO parse other relevant properties i.e. dimensions etc
QJsonObject jsonObject;
jsonObject.insert("qmlid", m_node.id());
QmlItemNode itemNode(m_node);
// Position relative to parent
QPointF pos = itemNode.instancePosition();
jsonObject.insert("x", pos.x());
jsonObject.insert("y", pos.y());
// size
QSizeF size = itemNode.instanceSize();
jsonObject.insert("width", size.width());
jsonObject.insert("height", size.height());
return jsonObject;
}
}

View File

@@ -0,0 +1,43 @@
/****************************************************************************
**
** Copyright (C) 2020 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 "modelnodeparser.h"
namespace QmlDesigner {
class ModelNode;
class ItemNodeParser : public ModelNodeParser
{
public:
ItemNodeParser(const QByteArrayList &lineage, const ModelNode &node);
~ItemNodeParser() override = default;
int priority() const override { return 100; }
bool isExportable() const override;
QJsonObject json() const override;
};
}

View File

@@ -0,0 +1,35 @@
/****************************************************************************
**
** Copyright (C) 2020 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 "modelnodeparser.h"
namespace QmlDesigner {
ModelNodeParser::ModelNodeParser(const QByteArrayList &lineage, const ModelNode &node) :
m_node(node),
m_lineage(lineage)
{
}
}

View File

@@ -0,0 +1,48 @@
/****************************************************************************
**
** Copyright (C) 2020 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 <QJsonObject>
#include <QByteArrayList>
namespace QmlDesigner {
class ModelNode;
class ModelNodeParser
{
public:
ModelNodeParser(const QByteArrayList &lineage, const ModelNode &node);
virtual ~ModelNodeParser() = default;
virtual int priority() const = 0;
virtual bool isExportable() const = 0;
virtual QJsonObject json() const = 0;
protected:
const ModelNode &m_node;
const QByteArrayList m_lineage;
};
}

View File

@@ -1,4 +1,9 @@
TEMPLATE = subdirs
CONFIG += ordered
SUBDIRS = qmldesignerplugin.pro qtquickplugin componentsplugin qmlpreviewplugin
SUBDIRS = \
qmldesignerplugin.pro \
qtquickplugin \
componentsplugin \
qmlpreviewplugin \
assetexporterplugin