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
|
||||
|
||||
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,9 +285,11 @@ Item {
|
||||
}
|
||||
}
|
||||
onRightClicked: function (pos) {
|
||||
if (NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName !== "") {
|
||||
contextMenu.popup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Editor.NewNodeGraphDialog {
|
||||
id: newNodeGraphDialog
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -7,9 +7,38 @@
|
||||
|
||||
#include "nodegrapheditorview.h"
|
||||
#include <qmldesignerplugin.h>
|
||||
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
|
||||
#include <QuickQanava>
|
||||
|
||||
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<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
|
||||
|
@@ -6,6 +6,10 @@
|
||||
#include <QStandardItemModel>
|
||||
#include <QPointer>
|
||||
|
||||
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<NodeGraphEditorView> m_editorView;
|
||||
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include "nodegrapheditormodel.h"
|
||||
#include "nodegrapheditorview.h"
|
||||
|
||||
#include <nodemetainfo.h>
|
||||
#include <qmldesignerconstants.h>
|
||||
#include <qmldesignerplugin.h>
|
||||
#include <theme.h>
|
||||
@@ -105,6 +106,52 @@ QString NodeGraphEditorWidget::qmlSourcesPath()
|
||||
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
|
||||
{
|
||||
return QUuid::createUuid().toString();
|
||||
|
@@ -30,6 +30,7 @@ public:
|
||||
NodeGraphEditorWidget(NodeGraphEditorView *nodeGraphEditorView, NodeGraphEditorModel *nodeGraphEditorModel);
|
||||
~NodeGraphEditorWidget() override = default;
|
||||
|
||||
Q_INVOKABLE QList<QVariantMap> createMetaData_inPorts(QByteArray typeName);
|
||||
|
||||
Q_INVOKABLE QString generateUUID() const;
|
||||
static QString qmlSourcesPath();
|
||||
|
Reference in New Issue
Block a user