1 /****************************************************************************
2 **
3 ** Copyright (C) 2020 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 #include "componentexporter.h"
26 #include "assetexporter.h"
27 #include "assetexportpluginconstants.h"
28 #include "exportnotification.h"
29 #include "dumpers/nodedumper.h"
30
31 #include "model.h"
32 #include "nodeabstractproperty.h"
33 #include "nodemetainfo.h"
34 #include "qmlitemnode.h"
35 #include "rewriterview.h"
36
37 #include "utils/qtcassert.h"
38
39 #include <QJsonArray>
40 #include <QJsonObject>
41 #include <QLoggingCategory>
42 #include <QPainter>
43
44 namespace {
45 Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.modelExporter", QtInfoMsg)
46
populateLineage(const QmlDesigner::ModelNode & node)47 static QByteArrayList populateLineage(const QmlDesigner::ModelNode &node)
48 {
49 QByteArrayList lineage;
50 if (!node.isValid() || node.type().isEmpty())
51 return {};
52
53 for (auto &info : node.metaInfo().superClasses())
54 lineage.append(info.typeName());
55
56 return lineage;
57 }
58
59 }
60
61 namespace QmlDesigner {
62 using namespace Constants;
63
64 std::vector<std::unique_ptr<Internal::NodeDumperCreatorBase>> Component::m_readers;
Component(AssetExporter & exporter,const ModelNode & rootNode)65 Component::Component(AssetExporter &exporter, const ModelNode &rootNode):
66 m_exporter(exporter),
67 m_rootNode(rootNode)
68 {
69 m_name = m_rootNode.id();
70 if (m_name.isEmpty())
71 m_name = QString::fromUtf8(m_rootNode.type());
72 }
73
json() const74 QJsonObject Component::json() const
75 {
76 return m_json;
77 }
78
exporter()79 AssetExporter &Component::exporter()
80 {
81 return m_exporter;
82 }
83
exportComponent()84 void Component::exportComponent()
85 {
86 QTC_ASSERT(m_rootNode.isValid(), return);
87 m_json = nodeToJson(m_rootNode);
88 // Change the export type to component
89 QJsonObject metadata = m_json.value(MetadataTag).toObject();
90 metadata.insert(ExportTypeTag, ExportTypeComponent);
91 addReferenceAsset(metadata);
92 m_json.insert(MetadataTag, metadata);
93 addImports();
94 }
95
name() const96 const QString &Component::name() const
97 {
98 return m_name;
99 }
100
createNodeDumper(const ModelNode & node) const101 NodeDumper *Component::createNodeDumper(const ModelNode &node) const
102 {
103 QByteArrayList lineage = populateLineage(node);
104 std::unique_ptr<NodeDumper> reader;
105 for (auto &dumperCreator: m_readers) {
106 std::unique_ptr<NodeDumper> r(dumperCreator->instance(lineage, node));
107 if (r->isExportable()) {
108 if (reader) {
109 if (reader->priority() < r->priority())
110 reader = std::move(r);
111 } else {
112 reader = std::move(r);
113 }
114 }
115 }
116
117 if (!reader)
118 qCDebug(loggerInfo()) << "No dumper for node" << node;
119
120 return reader.release();
121 }
122
nodeToJson(const ModelNode & node)123 QJsonObject Component::nodeToJson(const ModelNode &node)
124 {
125 QJsonObject jsonObject;
126
127 // Don't export States, Connection, Timeline etc nodes.
128 if (!node.isSubclassOf("QtQuick.Item"))
129 return {};
130
131 std::unique_ptr<NodeDumper> dumper(createNodeDumper(node));
132 if (dumper) {
133 jsonObject = dumper->json(*this);
134 } else {
135 ExportNotification::addError(tr("Error exporting node %1. Cannot parse type %2.")
136 .arg(node.id()).arg(QString::fromUtf8(node.type())));
137 }
138
139 QJsonArray children;
140 for (const ModelNode &childnode : node.directSubModelNodes()) {
141 const QJsonObject childJson = nodeToJson(childnode);
142 if (!childJson.isEmpty())
143 children.append(childJson);
144 }
145
146 if (!children.isEmpty())
147 jsonObject.insert(ChildrenTag, children);
148
149 return jsonObject;
150 }
151
addReferenceAsset(QJsonObject & metadataObject) const152 void Component::addReferenceAsset(QJsonObject &metadataObject) const
153 {
154 QPixmap refAsset = m_exporter.generateAsset(m_rootNode);
155 stichChildrendAssets(m_rootNode, refAsset);
156 Utils::FilePath refAssetPath = m_exporter.assetPath(m_rootNode, this, "_ref");
157 m_exporter.exportAsset(refAsset, refAssetPath);
158 QJsonObject assetData;
159 if (metadataObject.contains(AssetDataTag))
160 assetData = metadataObject[AssetDataTag].toObject();
161 assetData.insert(ReferenceAssetTag, refAssetPath.toString());
162 metadataObject.insert(AssetDataTag, assetData);
163 }
164
stichChildrendAssets(const ModelNode & node,QPixmap & parentPixmap) const165 void Component::stichChildrendAssets(const ModelNode &node, QPixmap &parentPixmap) const
166 {
167 if (!node.hasAnySubModelNodes())
168 return;
169
170 QPainter painter(&parentPixmap);
171 for (const ModelNode &child : node.directSubModelNodes()) {
172 QPixmap childPixmap = m_exporter.generateAsset(child);
173 if (childPixmap.isNull())
174 continue;
175 stichChildrendAssets(child, childPixmap);
176 QTransform cTransform = QmlObjectNode(child).toQmlItemNode().instanceTransform();
177 painter.setTransform(cTransform);
178 painter.drawPixmap(QPoint(0, 0), childPixmap);
179 }
180 painter.end();
181 }
182
addImports()183 void Component::addImports()
184 {
185 QJsonArray importsArray;
186 for (const Import &import : m_rootNode.model()->imports())
187 importsArray.append(import.toString());
188
189 if (!importsArray.empty())
190 m_json.insert(Constants::ImportsTag, importsArray);
191 }
192
193
194 } // namespace QmlDesigner
195