forked from qt-creator/qt-creator
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 <rnd@spyro-soft.com>
This commit is contained in:
@@ -164,6 +164,9 @@ Item {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
||||||
onSave: {
|
onSave: {
|
||||||
|
graphView.graph.clearGraph();
|
||||||
|
graphView.graph.insertNode(Nodes.Components.material);
|
||||||
|
NodeGraphEditorBackend.nodeGraphEditorModel.createQmlComponent(graphView.graph);
|
||||||
updateGraphData();
|
updateGraphData();
|
||||||
NodeGraphEditorBackend.nodeGraphEditorModel.saveFile(fileName);
|
NodeGraphEditorBackend.nodeGraphEditorModel.saveFile(fileName);
|
||||||
NodeGraphEditorBackend.nodeGraphEditorModel.openFileName(fileName);
|
NodeGraphEditorBackend.nodeGraphEditorModel.openFileName(fileName);
|
||||||
@@ -182,6 +185,8 @@ Item {
|
|||||||
onRejected: {}
|
onRejected: {}
|
||||||
onSave: {
|
onSave: {
|
||||||
if (NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName !== "") {
|
if (NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName !== "") {
|
||||||
|
NodeGraphEditorBackend.nodeGraphEditorModel.createQmlComponent(graphView.graph);
|
||||||
|
|
||||||
/*Save current graph data to the backend*/
|
/*Save current graph data to the backend*/
|
||||||
updateGraphData();
|
updateGraphData();
|
||||||
|
|
||||||
@@ -229,17 +234,7 @@ Item {
|
|||||||
tooltip: qsTr("Add a new graph node.")
|
tooltip: qsTr("Add a new graph node.")
|
||||||
|
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
newNodeGraphDialog.open();
|
saveAsDialog.open();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HelperWidgets.AbstractButton {
|
|
||||||
buttonIcon: StudioTheme.Constants.remove_medium
|
|
||||||
style: StudioTheme.Values.viewBarButtonStyle
|
|
||||||
tooltip: qsTr("Clear graph.")
|
|
||||||
|
|
||||||
onClicked: () => {
|
|
||||||
graphView.graph.clearGraph();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,6 +246,7 @@ Item {
|
|||||||
|
|
||||||
onClicked: () => {
|
onClicked: () => {
|
||||||
if (NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName !== "") {
|
if (NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName !== "") {
|
||||||
|
NodeGraphEditorBackend.nodeGraphEditorModel.createQmlComponent(graphView.graph);
|
||||||
updateGraphData();
|
updateGraphData();
|
||||||
NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = false;
|
NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = false;
|
||||||
NodeGraphEditorBackend.nodeGraphEditorModel.saveFile(NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName);
|
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 {
|
Label {
|
||||||
text: NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName
|
text: NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName
|
||||||
}
|
}
|
||||||
@@ -299,7 +285,9 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
onRightClicked: function (pos) {
|
onRightClicked: function (pos) {
|
||||||
contextMenu.popup();
|
if (NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName !== "") {
|
||||||
|
contextMenu.popup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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 {
|
Text {
|
||||||
id: emptyText
|
id: emptyText
|
||||||
|
|
||||||
|
@@ -24,6 +24,7 @@ Base {
|
|||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
node.label = "Texture";
|
node.label = "Texture";
|
||||||
|
internal.configurePorts(root.graph);
|
||||||
}
|
}
|
||||||
onValueChanged: {
|
onValueChanged: {
|
||||||
NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = true;
|
NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = true;
|
||||||
@@ -33,6 +34,23 @@ Base {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
height: 96
|
height: 96
|
||||||
source: root.value.source
|
source: root.value.source
|
||||||
|
// source: `image://qmldesigner_nodegrapheditor/${root.value.source}`
|
||||||
width: 96
|
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";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,9 +7,38 @@
|
|||||||
|
|
||||||
#include "nodegrapheditorview.h"
|
#include "nodegrapheditorview.h"
|
||||||
#include <qmldesignerplugin.h>
|
#include <qmldesignerplugin.h>
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QList>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include <QuickQanava>
|
||||||
|
|
||||||
namespace QmlDesigner {
|
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)
|
NodeGraphEditorModel::NodeGraphEditorModel(NodeGraphEditorView *nodeGraphEditorView)
|
||||||
: QStandardItemModel(nodeGraphEditorView)
|
: QStandardItemModel(nodeGraphEditorView)
|
||||||
, m_editorView(nodeGraphEditorView)
|
, m_editorView(nodeGraphEditorView)
|
||||||
@@ -125,4 +154,134 @@ void NodeGraphEditorModel::setHasUnsavedChanges(bool val)
|
|||||||
};
|
};
|
||||||
|
|
||||||
QString NodeGraphEditorModel::graphData(){return m_graphData;}
|
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<QString, QString> translator;
|
||||||
|
QString s;
|
||||||
|
|
||||||
|
const QList<QString> allowedTypes{
|
||||||
|
"nge::BaseColor",
|
||||||
|
"nge::Metalness",
|
||||||
|
"nge::Roughness",
|
||||||
|
"Texture",
|
||||||
|
};
|
||||||
|
|
||||||
|
const QList<QString> requireQuotation{
|
||||||
|
"QColor",
|
||||||
|
"QUrl",
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto makeIndent = [](int v){
|
||||||
|
QString s;
|
||||||
|
for (int i = 0; i < v; i++) {
|
||||||
|
s += " ";
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::function<void(qan::Node* node)> 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<qan::PortItem*>(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<QObject*>();
|
||||||
|
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
|
} // namespace QmlDesigner
|
||||||
|
@@ -6,6 +6,10 @@
|
|||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
|
|
||||||
|
namespace qan {
|
||||||
|
class Graph;
|
||||||
|
}
|
||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
|
|
||||||
class NodeGraphEditorView;
|
class NodeGraphEditorView;
|
||||||
@@ -19,6 +23,7 @@ public:
|
|||||||
Q_INVOKABLE void openFile(QString filePath);
|
Q_INVOKABLE void openFile(QString filePath);
|
||||||
Q_INVOKABLE void openFileName(QString filePath);
|
Q_INVOKABLE void openFileName(QString filePath);
|
||||||
Q_INVOKABLE void saveFile(QString fileName);
|
Q_INVOKABLE void saveFile(QString fileName);
|
||||||
|
Q_INVOKABLE void createQmlComponent(qan::Graph* graph);
|
||||||
private:
|
private:
|
||||||
QPointer<NodeGraphEditorView> m_editorView;
|
QPointer<NodeGraphEditorView> m_editorView;
|
||||||
|
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
#include "nodegrapheditormodel.h"
|
#include "nodegrapheditormodel.h"
|
||||||
#include "nodegrapheditorview.h"
|
#include "nodegrapheditorview.h"
|
||||||
|
|
||||||
|
#include <nodemetainfo.h>
|
||||||
#include <qmldesignerconstants.h>
|
#include <qmldesignerconstants.h>
|
||||||
#include <qmldesignerplugin.h>
|
#include <qmldesignerplugin.h>
|
||||||
#include <theme.h>
|
#include <theme.h>
|
||||||
@@ -105,6 +106,52 @@ QString NodeGraphEditorWidget::qmlSourcesPath()
|
|||||||
return Core::ICore::resourcePath("qmldesigner/nodegrapheditor").toString();
|
return Core::ICore::resourcePath("qmldesigner/nodegrapheditor").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QList<QString> allowedTypes {
|
||||||
|
"bool",
|
||||||
|
"float",
|
||||||
|
"double",
|
||||||
|
"QColor",
|
||||||
|
"QUrl",
|
||||||
|
"Texture",
|
||||||
|
};
|
||||||
|
|
||||||
|
static QMap<QString, QString> translateTypes {
|
||||||
|
{ "float", "real" },
|
||||||
|
{ "double", "real" },
|
||||||
|
};
|
||||||
|
|
||||||
|
static QList<QString> omitNames {
|
||||||
|
};
|
||||||
|
|
||||||
|
QList<QVariantMap> NodeGraphEditorWidget::createMetaData_inPorts(QByteArray typeName)
|
||||||
|
{
|
||||||
|
QList<QVariantMap> 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
|
QString NodeGraphEditorWidget::generateUUID() const
|
||||||
{
|
{
|
||||||
return QUuid::createUuid().toString();
|
return QUuid::createUuid().toString();
|
||||||
|
@@ -30,6 +30,7 @@ public:
|
|||||||
NodeGraphEditorWidget(NodeGraphEditorView *nodeGraphEditorView, NodeGraphEditorModel *nodeGraphEditorModel);
|
NodeGraphEditorWidget(NodeGraphEditorView *nodeGraphEditorView, NodeGraphEditorModel *nodeGraphEditorModel);
|
||||||
~NodeGraphEditorWidget() override = default;
|
~NodeGraphEditorWidget() override = default;
|
||||||
|
|
||||||
|
Q_INVOKABLE QList<QVariantMap> createMetaData_inPorts(QByteArray typeName);
|
||||||
|
|
||||||
Q_INVOKABLE QString generateUUID() const;
|
Q_INVOKABLE QString generateUUID() const;
|
||||||
static QString qmlSourcesPath();
|
static QString qmlSourcesPath();
|
||||||
|
Reference in New Issue
Block a user