From af4a81608652ead5fd85c6191540b1cc00275c6f Mon Sep 17 00:00:00 2001 From: Rafal Andrusieczko Date: Wed, 18 Dec 2024 19:40:35 +0100 Subject: [PATCH] New functionalities implementation Create QML source as component (new file). Generate "in" ports dynamically based on metainfo (texture). Change-Id: I795e4ccb37eebec40e9716057b71a50073239ace Reviewed-by: Rafal Andrusieczko --- .../qmldesigner/nodegrapheditor/Main.qml | 32 ++-- .../nodegrapheditor/SaveAsDialog.qml | 24 +++ .../nodegrapheditor/imports/Nodes/Texture.qml | 18 ++ .../nodegrapheditor/nodegrapheditormodel.cpp | 159 ++++++++++++++++++ .../nodegrapheditor/nodegrapheditormodel.h | 5 + .../nodegrapheditor/nodegrapheditorwidget.cpp | 47 ++++++ .../nodegrapheditor/nodegrapheditorwidget.h | 1 + 7 files changed, 264 insertions(+), 22 deletions(-) diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/Main.qml b/share/qtcreator/qmldesigner/nodegrapheditor/Main.qml index b7aa9491794..004950eef3a 100644 --- a/share/qtcreator/qmldesigner/nodegrapheditor/Main.qml +++ b/share/qtcreator/qmldesigner/nodegrapheditor/Main.qml @@ -164,6 +164,9 @@ Item { anchors.centerIn: parent onSave: { + graphView.graph.clearGraph(); + graphView.graph.insertNode(Nodes.Components.material); + NodeGraphEditorBackend.nodeGraphEditorModel.createQmlComponent(graphView.graph); updateGraphData(); NodeGraphEditorBackend.nodeGraphEditorModel.saveFile(fileName); NodeGraphEditorBackend.nodeGraphEditorModel.openFileName(fileName); @@ -182,6 +185,8 @@ Item { onRejected: {} onSave: { if (NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName !== "") { + NodeGraphEditorBackend.nodeGraphEditorModel.createQmlComponent(graphView.graph); + /*Save current graph data to the backend*/ updateGraphData(); @@ -229,17 +234,7 @@ Item { tooltip: qsTr("Add a new graph node.") onClicked: () => { - newNodeGraphDialog.open(); - } - } - - HelperWidgets.AbstractButton { - buttonIcon: StudioTheme.Constants.remove_medium - style: StudioTheme.Values.viewBarButtonStyle - tooltip: qsTr("Clear graph.") - - onClicked: () => { - graphView.graph.clearGraph(); + saveAsDialog.open(); } } @@ -251,6 +246,7 @@ Item { onClicked: () => { if (NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName !== "") { + NodeGraphEditorBackend.nodeGraphEditorModel.createQmlComponent(graphView.graph); updateGraphData(); NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = false; NodeGraphEditorBackend.nodeGraphEditorModel.saveFile(NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName); @@ -260,16 +256,6 @@ Item { } } - HelperWidgets.AbstractButton { - buttonIcon: StudioTheme.Constants.saveAs_medium - style: StudioTheme.Values.viewBarButtonStyle - tooltip: qsTr("Save as ...") - - onClicked: () => { - saveAsDialog.open(); - } - } - Label { text: NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName } @@ -299,7 +285,9 @@ Item { } } onRightClicked: function (pos) { - contextMenu.popup(); + if (NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName !== "") { + contextMenu.popup(); + } } } diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/SaveAsDialog.qml b/share/qtcreator/qmldesigner/nodegrapheditor/SaveAsDialog.qml index 236643f953b..255bdf38f2a 100644 --- a/share/qtcreator/qmldesigner/nodegrapheditor/SaveAsDialog.qml +++ b/share/qtcreator/qmldesigner/nodegrapheditor/SaveAsDialog.qml @@ -60,6 +60,30 @@ StudioControls.Dialog { } } + Row { + Text { + anchors.verticalCenter: parent.verticalCenter + color: StudioTheme.Values.themeTextColor + text: qsTr("Type: ") + } + + StudioControls.ComboBox { + id: cbType + + actionIndicatorVisible: false + model: [ + { + value: "principled_material", + text: "Principled Material" + }, + ] + textRole: "text" + valueRole: "value" + + onActivated: () => {} + } + } + Text { id: emptyText diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Texture.qml b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Texture.qml index e3b2e1029d9..fb92b0e6a24 100644 --- a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Texture.qml +++ b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Texture.qml @@ -24,6 +24,7 @@ Base { Component.onCompleted: { node.label = "Texture"; + internal.configurePorts(root.graph); } onValueChanged: { NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = true; @@ -33,6 +34,23 @@ Base { anchors.centerIn: parent height: 96 source: root.value.source + // source: `image://qmldesigner_nodegrapheditor/${root.value.source}` width: 96 } + + QtObject { + id: internal + + function configurePorts(graph) { + const inPorts = NodeGraphEditorBackend.widget.createMetaData_inPorts("QtQuick3D.Texture"); + inPorts.forEach(data => { + const portItem = graph.insertPort(root.node, Qan.NodeItem.Left, Qan.PortItem.In, `${data.displayName} (${data.type})`, data.id); + portItem.dataName = data.displayName; + portItem.dataType = data.type; + }); + + const valuePort = graph.insertPort(root.node, Qan.NodeItem.Right, Qan.PortItem.Out, "OUT", "texture"); + valuePort.dataType = "Texture"; + } + } } diff --git a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditormodel.cpp b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditormodel.cpp index 67995e93b08..6f96339905d 100644 --- a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditormodel.cpp +++ b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditormodel.cpp @@ -7,9 +7,38 @@ #include "nodegrapheditorview.h" #include + +#include #include +#include +#include +#include + +#include + namespace QmlDesigner { +enum class FileType +{ + Binary, + Text +}; + +static bool writeToFile(const QByteArray &buf, const QString &filename, FileType fileType) +{ + QDir().mkpath(QFileInfo(filename).path()); + QFile f(filename); + QIODevice::OpenMode flags = QIODevice::WriteOnly | QIODevice::Truncate; + if (fileType == FileType::Text) + flags |= QIODevice::Text; + if (!f.open(flags)) { + qWarning() << "Failed to open file for writing:" << filename; + return false; + } + f.write(buf); + return true; +} + NodeGraphEditorModel::NodeGraphEditorModel(NodeGraphEditorView *nodeGraphEditorView) : QStandardItemModel(nodeGraphEditorView) , m_editorView(nodeGraphEditorView) @@ -125,4 +154,134 @@ void NodeGraphEditorModel::setHasUnsavedChanges(bool val) }; QString NodeGraphEditorModel::graphData(){return m_graphData;} + + +void NodeGraphEditorModel::createQmlComponent(qan::Graph* graph) +{ + qan::Node* root = nullptr; + for ( const auto& node : std::as_const(graph->get_nodes()) ) { + const QString className = QString::fromUtf8( node->getItem()->metaObject()->className() ); + if (className.startsWith("Material")) { + root = node; + break; + } + } + + qCritical() << "createQmlComponent" << root; + if (!root) + return; + + int indentation = 1; + int textureCounter = 0; + QMap translator; + QString s; + + const QList allowedTypes{ + "nge::BaseColor", + "nge::Metalness", + "nge::Roughness", + "Texture", + }; + + const QList requireQuotation{ + "QColor", + "QUrl", + }; + + const auto makeIndent = [](int v){ + QString s; + for (int i = 0; i < v; i++) { + s += " "; + } + return s; + }; + + const std::function scan = [&allowedTypes, &scan, &s, &translator, &requireQuotation, &textureCounter, &indentation, &makeIndent](qan::Node* node){ + for ( const auto& qi : std::as_const( node->getItem()->getPorts() ) ) { + const auto port = qobject_cast(qi); + if ( port->getInEdgeItems().size() > 0 ) { + const auto dataName = port->property("dataName").toString(); + const auto dataType = port->property("dataType").toString(); + const auto edge = port->getInEdgeItems().at(port->getInEdgeItems().size() - 1)->getEdge(); + + if (!edge) { + continue; + } + + if ( dataType == "nge::BaseColor" ) { + translator = { + {"color", "baseColor"}, + {"channel", "baseColorChannel"}, + {"map", "baseColorMap"}, + {"singleChannelEnabled", "baseColorSingleChannelEnabled"}, + }; + } + else if ( dataType == "nge::Metalness" ) { + translator = { + {"channel", "metalnessChannel"}, + {"map", "metalnessMap"}, + {"metalness", "metalness"}, + }; + } + else if ( dataType == "nge::Roughness" ) { + translator = { + {"channel", "roughnessChannel"}, + {"map", "roughnessMap"}, + {"roughness", "roughness"}, + }; + } + + if ( dataName != "" ) { + QObject* obj = edge->getDestination()->getItem()->property("value").value(); + const auto value = obj->property(dataName.toUtf8().constData()); + QString valueString = requireQuotation.contains(dataType) ? QString{"\"%1\""}.arg(value.toString()) : value.toString(); + + if (valueString.isEmpty() && dataType == "Texture") { + valueString=QString{"texture_%1"}.arg(textureCounter++); + } + + valueString.remove("image://qmldesigner_nodegrapheditor/"); + + const QString key = translator.contains(dataName) ? translator[dataName] : dataName; + s += QString{"%1%2: %3\n"}.arg(makeIndent(indentation), key, valueString); + + if ( dataType == "Texture" ) { + s += QString{"%1Texture {\n"}.arg(makeIndent(indentation++)); + s += QString{"%1id: %2\n"}.arg(makeIndent(indentation), valueString); + } + } + + if (allowedTypes.contains(dataType)) { + scan(edge->getSource()); + } + + if ( dataType == "Texture" ) { + s += QString{"%1}\n"}.arg(makeIndent(--indentation)); + } + } + } + }; + + s += QString{ +R"(import QtQuick +import QtQuick3D + +PrincipledMaterial { + id: root +)" + }; + + scan(root); + + s += QString{ +R"(} +)" + }; + + Utils::FilePath path = DocumentManager::currentResourcePath().pathAppended(m_currentFileName + ".qml"); + qCritical() << path.toString(); + writeToFile(s.toUtf8(), path.toString(), FileType::Text); +} + + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditormodel.h b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditormodel.h index 33e5a30e4ae..06c3a7239dc 100644 --- a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditormodel.h +++ b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditormodel.h @@ -6,6 +6,10 @@ #include #include +namespace qan { +class Graph; +} + namespace QmlDesigner { class NodeGraphEditorView; @@ -19,6 +23,7 @@ public: Q_INVOKABLE void openFile(QString filePath); Q_INVOKABLE void openFileName(QString filePath); Q_INVOKABLE void saveFile(QString fileName); + Q_INVOKABLE void createQmlComponent(qan::Graph* graph); private: QPointer m_editorView; diff --git a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorwidget.cpp b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorwidget.cpp index b0d2d234c8a..1188f706fa0 100644 --- a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorwidget.cpp +++ b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorwidget.cpp @@ -7,6 +7,7 @@ #include "nodegrapheditormodel.h" #include "nodegrapheditorview.h" +#include #include #include #include @@ -105,6 +106,52 @@ QString NodeGraphEditorWidget::qmlSourcesPath() return Core::ICore::resourcePath("qmldesigner/nodegrapheditor").toString(); } +static QList allowedTypes { + "bool", + "float", + "double", + "QColor", + "QUrl", + "Texture", +}; + +static QMap translateTypes { + { "float", "real" }, + { "double", "real" }, +}; + +static QList omitNames { +}; + +QList NodeGraphEditorWidget::createMetaData_inPorts(QByteArray typeName) +{ + QList result; + + const auto mi = m_editorView->model()->metaInfo(typeName); + if (mi.isValid()) { + for (const auto& property : mi.properties()) { + QString type = QString::fromUtf8(property.propertyType().simplifiedTypeName()); + const QString name = QString::fromUtf8(property.name()); + + if (!allowedTypes.contains(type)) + continue; + if (omitNames.contains(name)) + continue; + + if (translateTypes.contains(type)) + type = translateTypes[type]; + + result.append({ + { "id", name }, + { "displayName", name }, + { "type", type } + }); + } + } + + return result; +} + QString NodeGraphEditorWidget::generateUUID() const { return QUuid::createUuid().toString(); diff --git a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorwidget.h b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorwidget.h index f5a2ac6f6ef..5d7b614b9b7 100644 --- a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorwidget.h +++ b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorwidget.h @@ -30,6 +30,7 @@ public: NodeGraphEditorWidget(NodeGraphEditorView *nodeGraphEditorView, NodeGraphEditorModel *nodeGraphEditorModel); ~NodeGraphEditorWidget() override = default; + Q_INVOKABLE QList createMetaData_inPorts(QByteArray typeName); Q_INVOKABLE QString generateUUID() const; static QString qmlSourcesPath();