// Copyright (C) 2020 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "componentexporter.h" #include "assetexporter.h" #include "assetexportpluginconstants.h" #include "exportnotification.h" #include "dumpers/nodedumper.h" #include "model.h" #include "nodeabstractproperty.h" #include "nodemetainfo.h" #include "qmlitemnode.h" #include "rewriterview.h" #include "utils/qtcassert.h" #include #include #include #include namespace { Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.modelExporter", QtInfoMsg) static QByteArrayList populateLineage(const QmlDesigner::ModelNode &node) { QByteArrayList lineage; if (!node.isValid() || node.type().isEmpty()) return {}; for (auto &info : node.metaInfo().superClasses()) lineage.append(info.typeName()); return lineage; } } namespace QmlDesigner { using namespace Constants; std::vector> Component::m_readers; Component::Component(AssetExporter &exporter, const ModelNode &rootNode): m_exporter(exporter), m_rootNode(rootNode) { m_name = m_rootNode.id(); if (m_name.isEmpty()) m_name = QString::fromUtf8(m_rootNode.type()); } 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); // Change the export type to component QJsonObject metadata = m_json.value(MetadataTag).toObject(); metadata.insert(ExportTypeTag, ExportTypeComponent); addReferenceAsset(metadata); m_json.insert(MetadataTag, metadata); addImports(); } const QString &Component::name() const { return m_name; } NodeDumper *Component::createNodeDumper(const ModelNode &node) const { QByteArrayList lineage = populateLineage(node); std::unique_ptr reader; for (auto &dumperCreator: m_readers) { std::unique_ptr r(dumperCreator->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 dumper for node" << node; return reader.release(); } QJsonObject Component::nodeToJson(const ModelNode &node) { QJsonObject jsonObject; // Don't export States, Connection, Timeline etc nodes. if (!node.metaInfo().isQtQuickItem()) return {}; std::unique_ptr dumper(createNodeDumper(node)); if (dumper) { jsonObject = dumper->json(*this); } else { ExportNotification::addError(tr("Error exporting node %1. Cannot parse type %2.") .arg(node.id()).arg(QString::fromUtf8(node.type()))); } QJsonArray children; for (const ModelNode &childnode : node.directSubModelNodes()) { const QJsonObject childJson = nodeToJson(childnode); if (!childJson.isEmpty()) children.append(childJson); } if (!children.isEmpty()) jsonObject.insert(ChildrenTag, children); return jsonObject; } void Component::addReferenceAsset(QJsonObject &metadataObject) const { QPixmap refAsset = m_exporter.generateAsset(m_rootNode); stichChildrendAssets(m_rootNode, refAsset); Utils::FilePath refAssetPath = m_exporter.assetPath(m_rootNode, this, "_ref"); m_exporter.exportAsset(refAsset, refAssetPath); QJsonObject assetData; if (metadataObject.contains(AssetDataTag)) assetData = metadataObject[AssetDataTag].toObject(); assetData.insert(ReferenceAssetTag, refAssetPath.toString()); metadataObject.insert(AssetDataTag, assetData); } void Component::stichChildrendAssets(const ModelNode &node, QPixmap &parentPixmap) const { if (!node.hasAnySubModelNodes()) return; QPainter painter(&parentPixmap); for (const ModelNode &child : node.directSubModelNodes()) { QPixmap childPixmap = m_exporter.generateAsset(child); if (childPixmap.isNull()) continue; stichChildrendAssets(child, childPixmap); QTransform cTransform = QmlObjectNode(child).toQmlItemNode().instanceTransform(); painter.setTransform(cTransform); painter.drawPixmap(QPoint(0, 0), childPixmap); } painter.end(); } void Component::addImports() { QJsonArray importsArray; for (const Import &import : m_rootNode.model()->imports()) importsArray.append(import.toString()); if (!importsArray.empty()) m_json.insert(Constants::ImportsTag, importsArray); } } // namespace QmlDesigner