aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/qmldesigner/assetexporterplugin
diff options
context:
space:
mode:
authorTim Jenssen <tim.jenssen@qt.io>2020-06-26 15:13:07 +0200
committerTim Jenssen <tim.jenssen@qt.io>2020-06-26 16:51:22 +0200
commitb8cd87dea027f54957373a10c77a506ec2509b10 (patch)
treeab7b00b4be9a46c1a0c89afaab7016444a257633 /src/plugins/qmldesigner/assetexporterplugin
parent97ac63b4012ab55def11a58ee6bd7b6878dc37e1 (diff)
parent5a06305ffe054287f55cba4c5d860ea73fccf684 (diff)
Merge remote-tracking branch 'origin/qds-1.59' into 4.13
Conflicts: src/plugins/clangformat/clangformatplugin.cpp src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp src/plugins/qmldesigner/qmldesigner.qbs Change-Id: Ie4a0beeb9fd32ac9683f4e8769988a9c3f3e369a
Diffstat (limited to 'src/plugins/qmldesigner/assetexporterplugin')
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp190
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h82
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui65
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp370
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporter.h111
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp130
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.h56
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.json6
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.metainfo2
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri37
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pro17
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs59
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qrc5
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporterview.cpp161
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexporterview.h86
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h78
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp119
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/componentexporter.h93
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/exportnotification.cpp58
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/exportnotification.h33
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp163
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/filepathmodel.h60
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp66
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.h42
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp65
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.h44
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp41
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h58
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp87
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.h43
30 files changed, 2427 insertions, 0 deletions
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp
new file mode 100644
index 0000000000..53ec46697e
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** 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 "assetexportpluginconstants.h"
+#include "filepathmodel.h"
+
+#include "coreplugin/fileutils.h"
+#include "coreplugin/icore.h"
+#include "projectexplorer/task.h"
+#include "projectexplorer/taskhub.h"
+#include "utils/fileutils.h"
+#include "utils/outputformatter.h"
+
+#include <QPushButton>
+#include <QListView>
+#include <QPlainTextEdit>
+#include <QDialogButtonBox>
+#include <QMessageBox>
+#include <QScrollBar>
+
+#include <algorithm>
+#include <cmath>
+
+namespace {
+static void addFormattedMessage(Utils::OutputFormatter *formatter, const QString &str,
+ Utils::OutputFormat format) {
+ if (!formatter)
+ return;
+
+ QPlainTextEdit *edit = formatter->plainTextEdit();
+ QScrollBar *scroll = edit->verticalScrollBar();
+ bool isAtBottom = scroll && scroll->value() == scroll->maximum();
+
+ QString msg = str + "\n";
+ formatter->appendMessage(msg, format);
+
+ if (isAtBottom)
+ scroll->setValue(scroll->maximum());
+}
+}
+
+using namespace ProjectExplorer;
+
+namespace QmlDesigner {
+AssetExportDialog::AssetExportDialog(const Utils::FilePath &exportPath,
+ AssetExporter &assetExporter, FilePathModel &model,
+ QWidget *parent) :
+ QDialog(parent),
+ m_assetExporter(assetExporter),
+ m_filePathModel(model),
+ m_ui(new Ui::AssetExportDialog),
+ m_filesView(new QListView),
+ m_exportLogs(new QPlainTextEdit),
+ m_outputFormatter(new Utils::OutputFormatter())
+{
+ m_ui->setupUi(this);
+
+ m_ui->exportPath->setFileName(exportPath);
+ m_ui->exportPath->setPromptDialogTitle(tr("Choose Export Path"));
+ m_ui->exportPath->lineEdit()->setReadOnly(true);
+ m_ui->exportPath->addButton(tr("Open"), this, [this]() {
+ Core::FileUtils::showInGraphicalShell(Core::ICore::mainWindow(), m_ui->exportPath->path());
+ });
+
+ m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
+
+ m_ui->stackedWidget->addWidget(m_filesView);
+ m_filesView->setModel(&m_filePathModel);
+
+ m_exportLogs->setReadOnly(true);
+ m_outputFormatter->setPlainTextEdit(m_exportLogs);
+ m_ui->stackedWidget->addWidget(m_exportLogs);
+ switchView(false);
+
+ connect(m_ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, [this]() {
+ m_assetExporter.cancel();
+ });
+
+ m_exportBtn = m_ui->buttonBox->addButton(tr("Export"), QDialogButtonBox::AcceptRole);
+ m_exportBtn->setEnabled(false);
+ connect(m_exportBtn, &QPushButton::clicked, this, &AssetExportDialog::onExport);
+ connect(&m_filePathModel, &FilePathModel::modelReset, this, [this]() {
+ m_ui->exportProgress->setRange(0, 1000);
+ m_ui->exportProgress->setValue(0);
+ m_exportBtn->setEnabled(true);
+ });
+
+ connect(m_ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, [this]() {
+ close();
+ });
+ m_ui->buttonBox->button(QDialogButtonBox::Close)->setVisible(false);
+
+ connect(&m_assetExporter, &AssetExporter::stateChanged,
+ this, &AssetExportDialog::onExportStateChanged);
+ connect(&m_assetExporter, &AssetExporter::exportProgressChanged,
+ this, &AssetExportDialog::updateExportProgress);
+
+ connect(TaskHub::instance(), &TaskHub::taskAdded, this, &AssetExportDialog::onTaskAdded);
+
+ m_ui->exportProgress->setRange(0,0);
+}
+
+AssetExportDialog::~AssetExportDialog()
+{
+ m_assetExporter.cancel();
+}
+
+void AssetExportDialog::onExport()
+{
+ switchView(true);
+
+ updateExportProgress(0.0);
+ TaskHub::clearTasks(Constants::TASK_CATEGORY_ASSET_EXPORT);
+ m_exportLogs->clear();
+
+ m_assetExporter.exportQml(m_filePathModel.files(), m_ui->exportPath->fileName());
+}
+
+void AssetExportDialog::onExportStateChanged(AssetExporter::ParsingState newState)
+{
+ switch (newState) {
+ case AssetExporter::ParsingState::ExportingDone:
+ m_exportBtn->setVisible(false);
+ m_ui->buttonBox->button(QDialogButtonBox::Close)->setVisible(true);
+ break;
+ default:
+ break;
+ }
+
+ m_exportBtn->setEnabled(newState == AssetExporter::ParsingState::ExportingDone);
+ m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(m_assetExporter.isBusy());
+}
+
+void AssetExportDialog::updateExportProgress(double value)
+{
+ value = std::max(0.0, std::min(1.0, value));
+ m_ui->exportProgress->setValue(std::round(value * 1000));
+}
+
+void AssetExportDialog::switchView(bool showExportView)
+{
+ if (showExportView)
+ m_ui->stackedWidget->setCurrentWidget(m_exportLogs);
+ else
+ m_ui->stackedWidget->setCurrentWidget(m_filesView);
+}
+
+void AssetExportDialog::onTaskAdded(const ProjectExplorer::Task &task)
+{
+ Utils::OutputFormat format = Utils::NormalMessageFormat;
+ if (task.category == Constants::TASK_CATEGORY_ASSET_EXPORT) {
+ switch (task.type) {
+ case ProjectExplorer::Task::Error:
+ format = Utils::StdErrFormat;
+ break;
+ case ProjectExplorer::Task::Warning:
+ format = Utils::StdOutFormat;
+ break;
+ default:
+ format = Utils::NormalMessageFormat;
+ }
+ addFormattedMessage(m_outputFormatter, task.description(), format);
+ }
+}
+
+}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h
new file mode 100644
index 0000000000..7bf68b6a74
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** 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;
+class QListView;
+class QPlainTextEdit;
+QT_END_NAMESPACE
+
+namespace Ui {
+class AssetExportDialog;
+}
+
+namespace Utils {
+class OutputFormatter;
+}
+
+namespace ProjectExplorer {
+class Task;
+}
+
+namespace QmlDesigner {
+class FilePathModel;
+
+class AssetExportDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit AssetExportDialog(const Utils::FilePath &exportPath, AssetExporter &assetExporter,
+ FilePathModel& model, QWidget *parent = nullptr);
+ ~AssetExportDialog();
+
+private:
+ void onExport();
+ void onExportStateChanged(AssetExporter::ParsingState newState);
+ void updateExportProgress(double value);
+ void switchView(bool showExportView);
+ void onTaskAdded(const ProjectExplorer::Task &task);
+
+private:
+ AssetExporter &m_assetExporter;
+ FilePathModel &m_filePathModel;
+ std::unique_ptr<Ui::AssetExportDialog> m_ui;
+ QPushButton *m_exportBtn = nullptr;
+ QListView *m_filesView = nullptr;
+ QPlainTextEdit *m_exportLogs = nullptr;
+ Utils::OutputFormatter *m_outputFormatter = nullptr;
+};
+
+}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui
new file mode 100644
index 0000000000..38c2152098
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui
@@ -0,0 +1,65 @@
+<?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="0" column="1">
+ <widget class="Utils::PathChooser" name="exportPath" native="true"/>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Export path:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <widget class="QStackedWidget" name="stackedWidget"/>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="QProgressBar" name="exportProgress">
+ <property name="maximum">
+ <number>1000</number>
+ </property>
+ <property name="value">
+ <number>0</number>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" colspan="2">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Close</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>Utils::PathChooser</class>
+ <extends>QWidget</extends>
+ <header location="global">utils/pathchooser.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp
new file mode 100644
index 0000000000..26e2d2c455
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp
@@ -0,0 +1,370 @@
+/****************************************************************************
+**
+** 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 "exportnotification.h"
+#include "assetexportpluginconstants.h"
+
+#include "rewriterview.h"
+#include "qmlitemnode.h"
+#include "qmlobjectnode.h"
+#include "utils/qtcassert.h"
+#include "utils/runextensions.h"
+#include "variantproperty.h"
+
+#include <QCryptographicHash>
+#include <QDir>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QLoggingCategory>
+#include <QWaitCondition>
+
+#include <random>
+#include <queue>
+
+using namespace ProjectExplorer;
+using namespace std;
+namespace {
+bool makeParentPath(const Utils::FilePath &path)
+{
+ QDir d;
+ return d.mkpath(path.toFileInfo().absolutePath());
+}
+
+QByteArray generateHash(const QString &token) {
+ static uint counter = 0;
+ std::mt19937 gen(std::random_device().operator()());
+ std::uniform_int_distribution<> distribution(1, 99999);
+ QByteArray data = QString("%1%2%3").arg(token).arg(++counter).arg(distribution(gen)).toLatin1();
+ return QCryptographicHash::hash(data, QCryptographicHash::Md5).toHex();
+}
+
+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)
+}
+
+namespace QmlDesigner {
+
+class AssetDumper
+{
+public:
+ AssetDumper();
+ ~AssetDumper();
+
+ void dumpAsset(const QPixmap &p, const Utils::FilePath &path);
+
+ /* Keeps on dumping until all assets are dumped, then quits */
+ void quitDumper();
+
+ /* Aborts dumping */
+ void abortDumper();
+
+private:
+ void addAsset(const QPixmap &p, const Utils::FilePath &path);
+ void doDumping(QFutureInterface<void> &fi);
+ void savePixmap(const QPixmap &p, Utils::FilePath &path) const;
+
+ QFuture<void> m_dumpFuture;
+ QMutex m_queueMutex;
+ QWaitCondition m_queueCondition;
+ std::queue<std::pair<QPixmap, Utils::FilePath>> m_assets;
+ std::atomic<bool> m_quitDumper;
+};
+
+
+
+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();
+}
+
+void AssetExporter::exportQml(const Utils::FilePaths &qmlFiles, const Utils::FilePath &exportPath,
+ bool exportAssets)
+{
+ ExportNotification::addInfo(tr("Exporting metadata at %1. Export assets: ")
+ .arg(exportPath.toUserOutput())
+ .arg(exportAssets? tr("Yes") : tr("No")));
+ // TODO Asset export
+ notifyProgress(0.0);
+ Q_UNUSED(exportAssets);
+ m_exportFiles = qmlFiles;
+ m_components = QJsonArray();
+ m_exportPath = exportPath;
+ m_currentState.change(ParsingState::Parsing);
+ triggerLoadNextFile();
+ m_assetDumper = make_unique<AssetDumper>();
+}
+
+void AssetExporter::cancel()
+{
+ // TODO Cancel export
+ m_assetDumper.reset();
+}
+
+bool AssetExporter::isBusy() const
+{
+ return m_currentState == AssetExporter::ParsingState::Parsing ||
+ m_currentState == AssetExporter::ParsingState::ExportingAssets ||
+ m_currentState == AssetExporter::ParsingState::WritingJson;
+}
+
+Utils::FilePath AssetExporter::exportAsset(const QmlObjectNode &node)
+{
+ // TODO: Use this hash as UUID and add to the node.
+ QByteArray hash = addNodeUUID(node.modelNode());
+ Utils::FilePath assetPath = m_exportPath.pathAppended(QString("assets/%1.png")
+ .arg(QString::fromLatin1(hash)));
+ m_assetDumper->dumpAsset(node.toQmlItemNode().instanceRenderPixmap(), assetPath);
+ return assetPath;
+}
+
+void AssetExporter::exportComponent(const ModelNode &rootNode)
+{
+ qCDebug(loggerInfo) << "Exporting component" << rootNode.id();
+ Component exporter(*this, rootNode);
+ exporter.exportComponent();
+ m_components.append(exporter.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;
+ }
+ qCDebug(loggerError) << "QML load error:" << errorStr;
+ ExportNotification::addError(tr("Loading QML failed. %1").arg(errorStr));
+}
+
+void AssetExporter::notifyProgress(double value) const
+{
+ emit exportProgressChanged(value);
+}
+
+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());
+ QString error;
+ if (!m_view->saveQmlFile(&error)) {
+ ExportNotification::addError(tr("Error saving QML file. %1")
+ .arg(error.isEmpty()? tr("Unknown") : error));
+ }
+ triggerLoadNextFile();
+}
+
+QByteArray AssetExporter::addNodeUUID(ModelNode node)
+{
+ QByteArray uuid = node.auxiliaryData(Constants::UuidTag).toByteArray();
+ qDebug() << node.id() << "UUID" << uuid;
+ if (uuid.isEmpty()) {
+ // Assign a new hash.
+ do {
+ uuid = generateHash(node.id());
+ } while (m_usedHashes.contains(uuid));
+ m_usedHashes.insert(uuid);
+ node.setAuxiliaryData(Constants::UuidAuxTag, QString::fromLatin1(uuid));
+ node.model()->rewriterView()->writeAuxiliaryData();
+ }
+ return uuid;
+}
+
+void AssetExporter::triggerLoadNextFile()
+{
+ QTimer::singleShot(0, this, &AssetExporter::loadNextFile);
+}
+
+void AssetExporter::loadNextFile()
+{
+ if (m_exportFiles.isEmpty()) {
+ notifyProgress(0.8);
+ m_currentState.change(ParsingState::ParsingFinished);
+ writeMetadata();
+ return;
+ }
+
+ // Load the next pending file.
+ const Utils::FilePath file = m_exportFiles.takeFirst();
+ ExportNotification::addInfo(tr("Exporting file %1.").arg(file.toUserOutput()));
+ qCDebug(loggerInfo) << "Loading next file" << file;
+ m_view->loadQmlFile(file);
+}
+
+void AssetExporter::writeMetadata() const
+{
+ Utils::FilePath metadataPath = m_exportPath.pathAppended(m_exportPath.fileName() + ".metadata");
+ ExportNotification::addInfo(tr("Writing metadata to file %1.").
+ arg(metadataPath.toUserOutput()));
+ makeParentPath(metadataPath);
+ 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()) {
+ ExportNotification::addError(tr("Empty JSON document."));
+ } else {
+ Utils::FileSaver saver(metadataPath.toString(), QIODevice::Text);
+ saver.write(doc.toJson(QJsonDocument::Indented));
+ if (!saver.finalize()) {
+ ExportNotification::addError(tr("Writing metadata failed. %1").
+ arg(saver.errorString()));
+ }
+ }
+ notifyProgress(1.0);
+ ExportNotification::addInfo(tr("Export finished."));
+ m_assetDumper->quitDumper();
+ 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;
+}
+
+AssetDumper::AssetDumper():
+ m_quitDumper(false)
+{
+ m_dumpFuture = Utils::runAsync(&AssetDumper::doDumping, this);
+}
+
+AssetDumper::~AssetDumper()
+{
+ abortDumper();
+}
+
+void AssetDumper::dumpAsset(const QPixmap &p, const Utils::FilePath &path)
+{
+ addAsset(p, path);
+}
+
+void AssetDumper::quitDumper()
+{
+ m_quitDumper = true;
+ m_queueCondition.wakeAll();
+ if (!m_dumpFuture.isFinished())
+ m_dumpFuture.waitForFinished();
+}
+
+void AssetDumper::abortDumper()
+{
+ if (!m_dumpFuture.isFinished()) {
+ m_dumpFuture.cancel();
+ m_queueCondition.wakeAll();
+ m_dumpFuture.waitForFinished();
+ }
+}
+
+void AssetDumper::addAsset(const QPixmap &p, const Utils::FilePath &path)
+{
+ QMutexLocker locker(&m_queueMutex);
+ qDebug() << "Save Asset:" << path;
+ m_assets.push({p, path});
+}
+
+void AssetDumper::doDumping(QFutureInterface<void> &fi)
+{
+ auto haveAsset = [this] (std::pair<QPixmap, Utils::FilePath> *asset) {
+ QMutexLocker locker(&m_queueMutex);
+ if (m_assets.empty())
+ return false;
+ *asset = m_assets.front();
+ m_assets.pop();
+ return true;
+ };
+
+ forever {
+ std::pair<QPixmap, Utils::FilePath> asset;
+ if (haveAsset(&asset)) {
+ if (fi.isCanceled())
+ break;
+ savePixmap(asset.first, asset.second);
+ } else {
+ if (m_quitDumper)
+ break;
+ QMutexLocker locker(&m_queueMutex);
+ m_queueCondition.wait(&m_queueMutex);
+ }
+
+ if (fi.isCanceled())
+ break;
+ }
+ fi.reportFinished();
+}
+
+void AssetDumper::savePixmap(const QPixmap &p, Utils::FilePath &path) const
+{
+ if (p.isNull()) {
+ qCDebug(loggerWarn) << "Dumping null pixmap" << path;
+ return;
+ }
+
+ if (!makeParentPath(path)) {
+ ExportNotification::addError(AssetExporter::tr("Error creating asset directory. %1")
+ .arg(path.fileName()));
+ return;
+ }
+
+ if (!p.save(path.toString())) {
+ ExportNotification::addError(AssetExporter::tr("Error saving asset. %1")
+ .arg(path.fileName()));
+ }
+}
+
+}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h
new file mode 100644
index 0000000000..2aa238fb32
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** 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 <QJsonArray>
+#include <QJsonObject>
+#include <QSet>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+class QJsonArray;
+QT_END_NAMESPACE
+
+namespace ProjectExplorer {
+class Project;
+}
+
+namespace QmlDesigner {
+class AssetDumper;
+
+class AssetExporter : public QObject
+{
+ Q_OBJECT
+
+public:
+
+ enum class ParsingState {
+ Idle = 0,
+ Parsing,
+ ParsingFinished,
+ ExportingAssets,
+ ExportingAssetsFinished,
+ WritingJson,
+ ExportingDone
+ };
+
+ AssetExporter(AssetExporterView *view, ProjectExplorer::Project *project,
+ QObject *parent = nullptr);
+ ~AssetExporter();
+
+ void exportQml(const Utils::FilePaths &qmlFiles, const Utils::FilePath &exportPath,
+ bool exportAssets = false);
+
+ void cancel();
+ bool isBusy() const;
+
+ Utils::FilePath exportAsset(const QmlObjectNode& node);
+
+signals:
+ void stateChanged(ParsingState);
+ void exportProgressChanged(double) const;
+
+private:
+ ParsingState currentState() const { return m_currentState.m_state; }
+ void exportComponent(const ModelNode &rootNode);
+ void writeMetadata() const;
+ void notifyLoadError(AssetExporterView::LoadState state);
+ void notifyProgress(double value) const;
+ void triggerLoadNextFile();
+ void loadNextFile();
+
+ void onQmlFileLoaded();
+
+ QByteArray addNodeUUID(ModelNode node);
+
+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;
+ QSet<QByteArray> m_usedHashes;
+ std::unique_ptr<AssetDumper> m_assetDumper;
+};
+QDebug operator<< (QDebug os, const QmlDesigner::AssetExporter::ParsingState& s);
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp
new file mode 100644
index 0000000000..e45e48ca0d
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** 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 "filepathmodel.h"
+#include "componentexporter.h"
+
+#include "parsers/modelitemnodeparser.h"
+#include "parsers/textnodeparser.h"
+#include "parsers/assetnodeparser.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 "projectexplorer/taskhub.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)
+{
+ ProjectExplorer::TaskHub::addCategory( Constants::TASK_CATEGORY_ASSET_EXPORT,
+ tr("Asset Export"), false);
+
+ auto *designerPlugin = QmlDesigner::QmlDesignerPlugin::instance();
+ auto &viewManager = designerPlugin->viewManager();
+ viewManager.registerViewTakingOwnership(m_view);
+
+ // Add parsers templates for factory instantiation.
+ Component::addNodeParser<ItemNodeParser>();
+ Component::addNodeParser<TextNodeParser>();
+ Component::addNodeParser<AssetNodeParser>();
+
+ // 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;
+
+ FilePathModel model(startupProject);
+ QString exportDirName = startupProject->displayName() + "_export";
+ auto exportDir = startupProject->projectFilePath().parentDir().pathAppended(exportDirName);
+ AssetExporter assetExporter(m_view, startupProject);
+ AssetExportDialog assetExporterDialog(exportDir, assetExporter, model);
+ 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
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.h b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.h
new file mode 100644
index 0000000000..0615cdb217
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.h
@@ -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
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.json b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.json
new file mode 100644
index 0000000000..a925eaca8e
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.json
@@ -0,0 +1,6 @@
+{
+ "Vendor" : "The Qt Company Ltd",
+ "Category" : "Qt Quick",
+ "Description" : "Plugin for exporting assets and QML from QmlDesigner",
+ "Url" : "http://www.qt.io"
+}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.metainfo b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.metainfo
new file mode 100644
index 0000000000..5bfe70cffd
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.metainfo
@@ -0,0 +1,2 @@
+MetaInfo {
+}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri
new file mode 100644
index 0000000000..713ab1184f
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri
@@ -0,0 +1,37 @@
+QT *= qml quick core widgets
+
+VPATH += $$PWD
+
+RESOURCES += assetexporterplugin.qrc
+
+INCLUDEPATH += ./
+
+HEADERS += \
+ assetexportdialog.h \
+ assetexporter.h \
+ assetexporterplugin.h \
+ assetexporterview.h \
+ assetexportpluginconstants.h \
+ componentexporter.h \
+ exportnotification.h \
+ filepathmodel.h \
+ parsers/assetnodeparser.h \
+ parsers/modelitemnodeparser.h \
+ parsers/modelnodeparser.h \
+ parsers/textnodeparser.h
+
+SOURCES += \
+ assetexportdialog.cpp \
+ assetexporter.cpp \
+ assetexporterplugin.cpp \
+ assetexporterview.cpp \
+ componentexporter.cpp \
+ exportnotification.cpp \
+ filepathmodel.cpp \
+ parsers/assetnodeparser.cpp \
+ parsers/modelitemnodeparser.cpp \
+ parsers/modelnodeparser.cpp \
+ parsers/textnodeparser.cpp
+
+FORMS += \
+ assetexportdialog.ui
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pro b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pro
new file mode 100644
index 0000000000..2612b06e0e
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pro
@@ -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
+
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs
new file mode 100644
index 0000000000..e847525324
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs
@@ -0,0 +1,59 @@
+import qbs
+
+QtcProduct {
+ name: "assetexporterplugin"
+ type: ["dynamiclibrary"]
+ installDir: qtc.ide_plugin_path + '/' + installDirName
+ property string installDirName: qbs.targetOS.contains("macos") ? "QmlDesigner" : "qmldesigner"
+
+ Depends { name: "Core" }
+ Depends { name: "ProjectExplorer" }
+ Depends { name: "QmlDesigner" }
+ Depends { name: "Utils" }
+
+ cpp.includePaths: base.concat([
+ "./",
+ "../designercore/include",
+ "../../../../share/qtcreator/qml/qmlpuppet/interfaces",
+ "../../../../share/qtcreator/qml/qmlpuppet/types"
+ ])
+
+ Properties {
+ condition: qbs.targetOS.contains("unix")
+ cpp.internalVersion: ""
+ }
+
+ Group {
+ name: "plugin metadata"
+ files: ["assetexporterplugin.json"]
+ fileTags: ["qt_plugin_metadata"]
+ }
+
+ files: [
+ "assetexportdialog.cpp",
+ "assetexportdialog.h",
+ "assetexportdialog.ui",
+ "assetexporter.cpp",
+ "assetexporter.h",
+ "assetexporterplugin.cpp",
+ "assetexporterplugin.h",
+ "assetexporterplugin.qrc",
+ "assetexporterview.cpp",
+ "assetexporterview.h",
+ "assetexportpluginconstants.h",
+ "componentexporter.cpp",
+ "componentexporter.h",
+ "exportnotification.cpp",
+ "exportnotification.h",
+ "filepathmodel.cpp",
+ "filepathmodel.h",
+ "parsers/assetnodeparser.cpp",
+ "parsers/assetnodeparser.h",
+ "parsers/modelitemnodeparser.cpp",
+ "parsers/modelitemnodeparser.h",
+ "parsers/modelnodeparser.cpp",
+ "parsers/modelnodeparser.h",
+ "parsers/textnodeparser.cpp",
+ "parsers/textnodeparser.h"
+ ]
+}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qrc b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qrc
new file mode 100644
index 0000000000..8db1e0adaf
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/assetexporterplugin">
+ <file>assetexporterplugin.metainfo</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.cpp
new file mode 100644
index 0000000000..fbb4173249
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.cpp
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** 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));
+ m_currentEditor = 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;
+}
+
+bool AssetExporterView::saveQmlFile(QString *error) const
+{
+ if (!m_currentEditor) {
+ qCDebug(loggerWarn) << "Saving QML file failed. No editor.";
+ return false;
+ }
+ return m_currentEditor->document()->save(error);
+}
+
+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();
+ // TODO: Send the loaded signal with a delay. The assumption that model attached and a
+ // valid root object is enough to declare a QML file is ready is incorrect. A ideal
+ // solution would be that the puppet notifies file ready signal.
+ if (m_state == LoadState::Loaded)
+ QTimer::singleShot(2000, this, &AssetExporterView::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;
+}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.h b/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.h
new file mode 100644
index 0000000000..46c2c77071
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterview.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** 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);
+ bool saveQmlFile(QString *error) const;
+
+ 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);
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h
new file mode 100644
index 0000000000..1937c7126e
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** 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";
+
+const char TASK_CATEGORY_ASSET_EXPORT[] = "AssetExporter.Export";
+const char UuidAuxTag[] = "uuid";
+
+//***************************************************************************
+// Metadata tags
+//***************************************************************************
+// Plugin info tags
+const char PluginInfoTag[] = "pluginInfo";
+const char MetadataVersionTag[] = "metadataVersion";
+
+const char DocumentInfoTag[] = "documentInfo";
+const char DocumentNameTag[] = "name";
+
+// Layer data tags
+const char ArtboardListTag[] = "artboards";
+
+const char XPosTag[] = "x";
+const char YPosTag[] = "y";
+const char WidthTag[] = "width";
+const char HeightTag[] = "height";
+
+
+const char QmlIdTag[] = "qmlId";
+const char ExportTypeTag[] = "exportType";
+const char QmlPropertiesTag[] = "qmlProperties";
+const char ImportsTag[] = "extraImports";
+const char UuidTag[] = "uuid";
+const char ClipTag[] = "clip";
+const char AssetDataTag[] = "assetData";
+const char AssetPathTag[] = "assetPath";
+const char AssetBoundsTag[] = "assetBounds";
+const char OpacityTag[] = "opacity";
+
+const char TextDetailsTag[] = "textDetails";
+const char FontFamilyTag[] = "fontFamily";
+const char FontSizeTag[] = "fontSize";
+const char FontStyleTag[] = "fontStyle";
+const char LetterSpacingTag[] = "kerning";
+const char TextColorTag[] = "textColor";
+const char TextContentTag[] = "contents";
+const char IsMultilineTag[] = "multiline";
+const char LineHeightTag[] = "lineHeight";
+const char HAlignTag[] = "horizontalAlignment";
+const char VAlignTag[] = "verticalAlignment";
+
+}
+}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp
new file mode 100644
index 0000000000..819fa3d328
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** 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>> Component::m_readers;
+Component::Component(AssetExporter &exporter, const ModelNode &rootNode):
+ m_exporter(exporter),
+ m_rootNode(rootNode)
+{
+
+}
+
+QJsonObject Component::json() const
+{
+ return m_json;
+}
+
+AssetExporter &Component::exporter()
+{
+ return m_exporter;
+}
+
+void Component::exportComponent()
+{
+ QTC_ASSERT(m_rootNode.isValid(), return);
+ m_json = nodeToJson(m_rootNode);
+}
+
+ModelNodeParser *Component::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 Component::nodeToJson(const ModelNode &node)
+{
+ QJsonObject jsonObject;
+ std::unique_ptr<ModelNodeParser> parser(createNodeParser(node));
+ if (parser)
+ jsonObject = parser->json(*this);
+
+ QJsonArray children;
+ for (const ModelNode &childnode : node.directSubModelNodes())
+ children.append(nodeToJson(childnode));
+
+ if (!children.isEmpty())
+ jsonObject.insert("children", children);
+
+ return jsonObject;
+}
+
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.h b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.h
new file mode 100644
index 0000000000..3668f372bf
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** 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>
+
+#include <memory>
+
+#include "utils/qtcassert.h"
+
+QT_BEGIN_NAMESPACE
+class QJsonArray;
+QT_END_NAMESPACE
+
+namespace QmlDesigner {
+class AssetExporter;
+class ModelNode;
+class Component;
+class ModelNodeParser;
+
+namespace Internal {
+class NodeParserCreatorBase
+{
+public:
+ virtual ~NodeParserCreatorBase() {}
+protected:
+ virtual ModelNodeParser *instance(const QByteArrayList &, const ModelNode &) const = 0;
+ friend class QmlDesigner::Component;
+};
+
+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 Component
+{
+public:
+ Component(AssetExporter& exporter, const ModelNode &rootNode);
+
+ void exportComponent();
+ QJsonObject json() const;
+
+ AssetExporter &exporter();
+
+ 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);
+
+private:
+ AssetExporter& m_exporter;
+ const ModelNode &m_rootNode;
+ QJsonObject m_json;
+ static std::vector<std::unique_ptr<Internal::NodeParserCreatorBase>> m_readers;
+};
+}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/exportnotification.cpp b/src/plugins/qmldesigner/assetexporterplugin/exportnotification.cpp
new file mode 100644
index 0000000000..d4259c3fe2
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/exportnotification.cpp
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** This file is part of the Qt Asset Importer module.
+**
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://www.qt.io/contact-us
+**
+******************************************************************************/
+#include "exportnotification.h"
+#include "assetexportpluginconstants.h"
+
+#include "projectexplorer/taskhub.h"
+
+#include <QLoggingCategory>
+
+namespace {
+Q_LOGGING_CATEGORY(loggerDebug, "qtc.designer.assetExportPlugin.exportNotification", QtDebugMsg)
+}
+
+using namespace ProjectExplorer;
+namespace {
+static void addTask(Task::TaskType type, const QString &desc)
+{
+ qCDebug(loggerDebug) << desc;
+ Task task(type, desc, {}, -1, QmlDesigner::Constants::TASK_CATEGORY_ASSET_EXPORT);
+ TaskHub::addTask(task);
+}
+}
+
+namespace QmlDesigner {
+
+void ExportNotification::addError(const QString &errMsg)
+{
+ addTask(Task::Error, errMsg);
+}
+
+void ExportNotification::addWarning(const QString &warningMsg)
+{
+ addTask(Task::Warning, warningMsg);
+}
+
+void ExportNotification::addInfo(const QString &infoMsg)
+{
+ addTask(Task::Unknown, infoMsg);
+}
+} // QmlDesigner
diff --git a/src/plugins/qmldesigner/assetexporterplugin/exportnotification.h b/src/plugins/qmldesigner/assetexporterplugin/exportnotification.h
new file mode 100644
index 0000000000..23fab1b8fa
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/exportnotification.h
@@ -0,0 +1,33 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** This file is part of the Qt Asset Importer module.
+**
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://www.qt.io/contact-us
+**
+******************************************************************************/
+#pragma once
+
+#include <QString>
+
+namespace QmlDesigner {
+class ExportNotification
+{
+public:
+ static void addError(const QString &errMsg);
+ static void addWarning(const QString &warningMsg);
+ static void addInfo(const QString &infoMsg);
+};
+} // QmlDesigner
diff --git a/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp b/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp
new file mode 100644
index 0000000000..36c175414b
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** 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 "filepathmodel.h"
+
+#include "exportnotification.h"
+
+#include "projectexplorer/project.h"
+#include "projectexplorer/projectnodes.h"
+#include "utils/runextensions.h"
+
+#include <QLoggingCategory>
+#include <QTimer>
+
+using namespace ProjectExplorer;
+
+namespace {
+Q_LOGGING_CATEGORY(loggerError, "qtc.designer.assetExportPlugin.filePathModel", QtCriticalMsg)
+Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.filePathModel", QtInfoMsg)
+
+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();
+}
+}
+
+namespace QmlDesigner {
+
+FilePathModel::FilePathModel(ProjectExplorer::Project *project, QObject *parent)
+ : QAbstractListModel(parent),
+ m_project(project)
+{
+ QTimer::singleShot(0, this, &FilePathModel::processProject);
+}
+
+FilePathModel::~FilePathModel()
+{
+ if (m_preprocessWatcher && !m_preprocessWatcher->isCanceled() &&
+ !m_preprocessWatcher->isFinished()) {
+ ExportNotification::addInfo(tr("Canceling QML files preparation."));
+ m_preprocessWatcher->cancel();
+ m_preprocessWatcher->waitForFinished();
+ qCDebug(loggerInfo) << "Canceling QML files preparation done.";
+ }
+}
+
+Qt::ItemFlags FilePathModel::flags(const QModelIndex &index) const
+{
+ Qt::ItemFlags itemFlags = QAbstractListModel::flags(index);
+ if (index.isValid())
+ itemFlags |= Qt::ItemIsUserCheckable;
+ return itemFlags;
+}
+
+int FilePathModel::rowCount(const QModelIndex &parent) const
+{
+ if (!parent.isValid())
+ return m_files.count();
+ return 0;
+}
+
+QVariant FilePathModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return {};
+
+ switch (role) {
+ case Qt::DisplayRole:
+ return m_files[index.row()].toUserOutput();
+ case Qt::CheckStateRole:
+ return m_skipped.count(m_files[index.row()]) ? Qt::Unchecked : Qt::Checked;
+ default:
+ break;
+ }
+
+ return {};
+}
+
+bool FilePathModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (!index.isValid() || role != Qt::CheckStateRole)
+ return false;
+
+ const Utils::FilePath path = m_files[index.row()];
+ if (value == Qt::Checked)
+ m_skipped.erase(path);
+ else
+ m_skipped.insert(path);
+
+ emit dataChanged(index, index);
+ return true;
+}
+
+Utils::FilePaths FilePathModel::files() const
+{
+ Utils::FilePaths selectedPaths;
+ std::copy_if(m_files.begin(), m_files.end(), std::back_inserter(selectedPaths),
+ [this](const Utils::FilePath &path) {
+ return !m_skipped.count(path);
+ });
+ return selectedPaths;
+}
+
+void FilePathModel::processProject()
+{
+ if (m_preprocessWatcher && !m_preprocessWatcher->isCanceled() &&
+ !m_preprocessWatcher->isFinished()) {
+ qCDebug(loggerError) << "Previous model load not finished.";
+ return;
+ }
+
+ beginResetModel();
+ m_preprocessWatcher.reset(new QFutureWatcher<Utils::FilePath>(this));
+ connect(m_preprocessWatcher.get(), &QFutureWatcher<Utils::FilePath>::resultReadyAt, this,
+ [this](int resultIndex) {
+ beginInsertRows(index(0, 0) , m_files.count(), m_files.count());
+ m_files.append(m_preprocessWatcher->resultAt(resultIndex));
+ endInsertRows();
+ });
+
+ connect(m_preprocessWatcher.get(), &QFutureWatcher<Utils::FilePath>::finished,
+ this, &FilePathModel::endResetModel);
+
+ QFuture<Utils::FilePath> f = Utils::runAsync(&findQmlFiles, m_project);
+ m_preprocessWatcher->setFuture(f);
+}
+
+
+}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.h b/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.h
new file mode 100644
index 0000000000..91a800c036
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** 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 <QAbstractListModel>
+#include <QFutureWatcher>
+
+#include "utils/fileutils.h"
+
+#include <memory>
+#include <unordered_set>
+
+namespace ProjectExplorer {
+class Project;
+}
+
+namespace QmlDesigner {
+class FilePathModel : public QAbstractListModel
+{
+public:
+ FilePathModel(ProjectExplorer::Project *project, QObject *parent = nullptr);
+ ~FilePathModel() override;
+
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ bool setData(const QModelIndex &index, const QVariant &value, int role) override;
+
+ Utils::FilePaths files() const;
+private:
+ void processProject();
+
+ ProjectExplorer::Project *m_project = nullptr;
+ std::unique_ptr<QFutureWatcher<Utils::FilePath>> m_preprocessWatcher;
+ std::unordered_set<Utils::FilePath> m_skipped;
+ Utils::FilePaths m_files;
+};
+
+}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp
new file mode 100644
index 0000000000..159eccec46
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp
@@ -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 "assetnodeparser.h"
+#include "assetexportpluginconstants.h"
+#include "assetexporter.h"
+
+#include "qmlitemnode.h"
+#include "componentexporter.h"
+
+#include "utils/fileutils.h"
+
+#include <QPixmap>
+
+namespace QmlDesigner {
+using namespace Constants;
+AssetNodeParser::AssetNodeParser(const QByteArrayList &lineage, const ModelNode &node) :
+ ItemNodeParser(lineage, node)
+{
+
+}
+
+bool AssetNodeParser::isExportable() const
+{
+ auto hasType = [this](const QByteArray &type) {
+ return lineage().contains(type);
+ };
+ return hasType("QtQuick.Image") || hasType("QtQuick.Rectangle");
+}
+
+QJsonObject AssetNodeParser::json(Component &component) const
+{
+ QJsonObject jsonObject = ItemNodeParser::json(component);
+
+ QPixmap asset = objectNode().toQmlItemNode().instanceRenderPixmap();
+ Utils::FilePath assetPath = component.exporter().exportAsset(objectNode());
+
+ QJsonObject assetData;
+ assetData.insert(AssetPathTag, assetPath.toString());
+ jsonObject.insert(AssetDataTag, assetData);
+ return jsonObject;
+}
+}
+
diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.h b/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.h
new file mode 100644
index 0000000000..be764b17ec
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.h
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** 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 "modelitemnodeparser.h"
+
+namespace QmlDesigner {
+class Component;
+
+class AssetNodeParser : public ItemNodeParser
+{
+public:
+ AssetNodeParser(const QByteArrayList &lineage, const ModelNode &node);
+ ~AssetNodeParser() override = default;
+
+ bool isExportable() const override;
+ int priority() const override { return 200; }
+ QJsonObject json(Component &component) const override;
+};
+}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp
new file mode 100644
index 0000000000..355983f221
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** 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 "qmlitemnode.h"
+
+namespace QmlDesigner {
+using namespace Constants;
+ItemNodeParser::ItemNodeParser(const QByteArrayList &lineage,
+ const ModelNode &node) :
+ ModelNodeParser(lineage, node)
+{
+
+}
+
+bool QmlDesigner::ItemNodeParser::isExportable() const
+{
+ return lineage().contains("QtQuick.Item");
+}
+
+QJsonObject QmlDesigner::ItemNodeParser::json(QmlDesigner::Component &component) const
+{
+ Q_UNUSED(component);
+ const QmlObjectNode &qmlObjectNode = objectNode();
+ QJsonObject jsonObject;
+ jsonObject.insert(QmlIdTag, qmlObjectNode.id());
+ QmlItemNode itemNode = qmlObjectNode.toQmlItemNode();
+
+ // Position relative to parent
+ QPointF pos = itemNode.instancePosition();
+ jsonObject.insert(XPosTag, pos.x());
+ jsonObject.insert(YPosTag, pos.y());
+
+ // size
+ QSizeF size = itemNode.instanceSize();
+ jsonObject.insert(WidthTag, size.width());
+ jsonObject.insert(HeightTag, size.height());
+
+ return jsonObject;
+}
+}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.h b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.h
new file mode 100644
index 0000000000..503fb4c2e9
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.h
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** 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 Component;
+
+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(Component &component) const override;
+};
+}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp
new file mode 100644
index 0000000000..31787b83cc
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp
@@ -0,0 +1,41 @@
+/****************************************************************************
+**
+** 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_objectNode(node),
+ m_lineage(lineage)
+{
+
+}
+
+QVariant ModelNodeParser::propertyValue(const PropertyName &name) const
+{
+ return m_objectNode.instanceValue(name);
+}
+
+}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h
new file mode 100644
index 0000000000..4ca17746e8
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** 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 "qmlobjectnode.h"
+
+#include <QJsonObject>
+#include <QByteArrayList>
+
+namespace QmlDesigner {
+class Component;
+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(Component& component) const = 0;
+
+ const QByteArrayList& lineage() const { return m_lineage; }
+ const QmlObjectNode& objectNode() const { return m_objectNode; }
+ QVariant propertyValue(const PropertyName &name) const;
+
+protected:
+ const ModelNode &m_node;
+
+private:
+ QmlObjectNode m_objectNode;
+ QByteArrayList m_lineage;
+};
+}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp
new file mode 100644
index 0000000000..12b73c4506
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** 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 "textnodeparser.h"
+#include "assetexportpluginconstants.h"
+
+#include <QColor>
+#include <QHash>
+
+namespace {
+const QHash<QString, QString> AlignMapping{
+ {"AlignRight", "RIGHT"},
+ {"AlignHCenter", "CENTER"},
+ {"AlignJustify", "JUSTIFIED"},
+ {"AlignLeft", "LEFT"},
+ {"AlignTop", "TOP"},
+ {"AlignVCenter", "CENTER"},
+ {"AlignBottom", "BOTTOM"}
+};
+
+QString toJsonAlignEnum(QString value) {
+ if (value.isEmpty() || !AlignMapping.contains(value))
+ return "";
+ return AlignMapping[value];
+}
+}
+
+
+namespace QmlDesigner {
+using namespace Constants;
+TextNodeParser::TextNodeParser(const QByteArrayList &lineage, const ModelNode &node) :
+ ItemNodeParser(lineage, node)
+{
+
+}
+
+bool TextNodeParser::isExportable() const
+{
+ return lineage().contains("QtQuick.Text");
+}
+
+QJsonObject TextNodeParser::json(Component &component) const
+{
+ Q_UNUSED(component);
+ QJsonObject jsonObject = ItemNodeParser::json(component);
+
+ QJsonObject textDetails;
+ textDetails.insert(TextContentTag, propertyValue("text").toString());
+ textDetails.insert(FontFamilyTag, propertyValue("font.family").toString());
+ textDetails.insert(FontStyleTag, propertyValue("font.styleName").toString());
+ textDetails.insert(FontSizeTag, propertyValue("font.pixelSize").toInt());
+ textDetails.insert(LetterSpacingTag, propertyValue("font.letterSpacing").toFloat());
+
+ QColor fontColor(propertyValue("font.color").toString());
+ textDetails.insert(TextColorTag, fontColor.name(QColor::HexArgb));
+
+ textDetails.insert(HAlignTag, toJsonAlignEnum(propertyValue("horizontalAlignment").toString()));
+ textDetails.insert(VAlignTag, toJsonAlignEnum(propertyValue("verticalAlignment").toString()));
+
+ textDetails.insert(IsMultilineTag, propertyValue("wrapMode").toString().compare("NoWrap") != 0);
+
+ jsonObject.insert(TextDetailsTag, textDetails);
+ return jsonObject;
+}
+}
diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.h b/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.h
new file mode 100644
index 0000000000..c05d5c8f88
--- /dev/null
+++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.h
@@ -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 "modelitemnodeparser.h"
+
+namespace QmlDesigner {
+class Component;
+
+class TextNodeParser : public ItemNodeParser
+{
+public:
+ TextNodeParser(const QByteArrayList &lineage, const ModelNode &node);
+ ~TextNodeParser() override = default;
+
+ bool isExportable() const override;
+ int priority() const override { return 200; }
+ QJsonObject json(Component &component) const override;
+};
+
+}