diff options
author | Tim Jenssen <tim.jenssen@qt.io> | 2020-06-26 15:13:07 +0200 |
---|---|---|
committer | Tim Jenssen <tim.jenssen@qt.io> | 2020-06-26 16:51:22 +0200 |
commit | b8cd87dea027f54957373a10c77a506ec2509b10 (patch) | |
tree | ab7b00b4be9a46c1a0c89afaab7016444a257633 /src/plugins/qmldesigner/assetexporterplugin | |
parent | 97ac63b4012ab55def11a58ee6bd7b6878dc37e1 (diff) | |
parent | 5a06305ffe054287f55cba4c5d860ea73fccf684 (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')
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; +}; + +} |