From db42dde3b416215ad09332689bc996351ed97c90 Mon Sep 17 00:00:00 2001 From: Andrzej Biniek Date: Sat, 14 Dec 2024 14:57:45 +0100 Subject: [PATCH] Node graph save functionality Change-Id: Ic43649b68d6488a77a7b7ac055ef5377877d7646 Reviewed-by: spyro-adb --- .../assetsLibraryQmlSources/AssetDelegate.qml | 5 + .../assetsLibraryQmlSources/GridFile.qml | 10 +- .../qmldesigner/nodegrapheditor/Main.qml | 224 ++++++++++++++++++ .../nodegrapheditor/SaveAsDialog.qml | 106 +++++++++ .../nodegrapheditor/SaveChangesDialog.qml | 63 +++++ .../nodegrapheditor/imports/Editor/Graph.qml | 11 +- .../nodegrapheditor/imports/Editor/Port.qml | 1 + .../nodegrapheditor/imports/Nodes/Base.qml | 10 +- .../imports/Nodes/BaseColor.qml | 1 + .../imports/Nodes/CheckBox.qml | 11 +- .../nodegrapheditor/imports/Nodes/Color.qml | 7 + .../imports/Nodes/ColorEditorPopup.qml | 2 - .../imports/Nodes/ComboBox.qml | 6 + .../imports/Nodes/Material.qml | 1 + .../imports/Nodes/Metalness.qml | 1 + .../imports/Nodes/RealSpinBox.qml | 10 +- .../imports/Nodes/Roughness.qml | 1 + .../nodegrapheditor/imports/Nodes/Texture.qml | 5 + src/plugins/qmldesigner/CMakeLists.txt | 1 - .../assetslibraryiconprovider.cpp | 2 +- .../assetslibrary/assetslibrarywidget.cpp | 8 + .../assetslibrary/assetslibrarywidget.h | 1 + .../componentcore/modelnodeoperations.cpp | 8 + .../componentcore/modelnodeoperations.h | 1 + .../nodegrapheditor/nodegrapheditormodel.cpp | 112 +++++++++ .../nodegrapheditor/nodegrapheditormodel.h | 27 ++- .../nodegrapheditor/nodegrapheditorview.cpp | 13 +- .../nodegrapheditor/nodegrapheditorview.h | 3 + .../nodegrapheditor/nodegrapheditorwidget.cpp | 20 ++ .../nodegrapheditor/nodegrapheditorwidget.h | 7 +- .../libs/qmldesignerutils/asset.cpp | 14 ++ .../qmldesigner/libs/qmldesignerutils/asset.h | 3 + .../qmldesigner/qmldesignerconstants.h | 1 + 33 files changed, 682 insertions(+), 14 deletions(-) create mode 100644 share/qtcreator/qmldesigner/nodegrapheditor/SaveAsDialog.qml create mode 100644 share/qtcreator/qmldesigner/nodegrapheditor/SaveChangesDialog.qml diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml index 7128e8b09e0..21fd723f9df 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetDelegate.qml @@ -22,6 +22,7 @@ TreeViewDelegate { readonly property string suffix: model.fileName.substr(-4) readonly property bool isFont: root.suffix === ".ttf" || root.suffix === ".otf" readonly property bool isEffect: root.suffix === ".qep" + readonly property bool isNodeGraph: root.suffix === ".qng" property bool currFileSelected: false property int initialDepth: -1 property bool __isDirectory: assetsModel.isDirectory(model.filePath) @@ -202,6 +203,10 @@ TreeViewDelegate { AssetsLibraryBackend.tooltipBackend.hideTooltip() if (mouse.button === Qt.LeftButton && root.isEffect) AssetsLibraryBackend.rootView.openEffectComposer(filePath) + + if (mouse.button === Qt.LeftButton && root.isNodeGraph){ + AssetsLibraryBackend.rootView.openNodeGraphEditor(filePath) + } } ToolTip { diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/GridFile.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/GridFile.qml index 207edd4dbfc..c83d2002a64 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/GridFile.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/GridFile.qml @@ -12,6 +12,7 @@ GridItem { readonly property bool isFont: root.suffix === ".ttf" || root.suffix === ".otf" readonly property bool isEffect: root.suffix === ".qep" readonly property bool isMaterial: root.suffix === ".mat" + readonly property bool isNodeGraph: root.suffix === ".qng" icon.source: "image://qmldesigner_assets/" + model.filePath @@ -48,10 +49,15 @@ GridItem { mouseArea.onDoubleClicked: (mouse) => { if (mouse.button === Qt.LeftButton) { - if (root.isEffect) + if (root.isEffect) { root.rootView.openEffectComposer(filePath) - else if (root.isMaterial) + } + else if (root.isMaterial) { root.rootView.openMaterialEditor(filePath) + } + else if (root.isNodeGraph) { + root.rootView.openNodeGraphEditor(filePath) + } } } diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/Main.qml b/share/qtcreator/qmldesigner/nodegrapheditor/Main.qml index 0f50b1eff64..b7aa9491794 100644 --- a/share/qtcreator/qmldesigner/nodegrapheditor/Main.qml +++ b/share/qtcreator/qmldesigner/nodegrapheditor/Main.qml @@ -9,12 +9,195 @@ import StudioTheme as StudioTheme import QuickQanava as Qan import Editor as Editor +import Nodes as Nodes import NodeGraphEditorBackend Item { id: root + property string nodeGraphFileName + + // Invoked after save changes is done + property var onSaveChangesCallback: () => {} + + function initEdges(edges) { + for (let edge in edges) { + let outNodeUuid = edges[edge].outNodeUuid; + let inNodeUuid = edges[edge].inNodeUuid; + let n1, n2; + + for (let i = 0; i < graphView.graph.getNodeCount(); i++) { + if (graphView.graph.nodes.at(i).item.uuid == outNodeUuid) { + n1 = graphView.graph.nodes.at(i); + break; + } + } + + for (let i = 0; i < graphView.graph.getNodeCount(); i++) { + if (graphView.graph.nodes.at(i).item.uuid == inNodeUuid) { + n2 = graphView.graph.nodes.at(i); + break; + } + } + let e = graphView.graph.insertEdge(n1, n2); + let p1 = n1.item.findPort(edges[edge].outPortName); + let p2 = n2.item.findPort(edges[edge].inPortName); + + graphView.graph.bindEdge(e, p1, p2); + if (p2.dataBinding) { + p2.dataBinding(n1.item.value); + } else { + // TODO: change old implementation + n2.item.value[p2.dataName] = Qt.binding(() => { + if (n1.dataName !== "") + return n1.item.value[p1.dataName]; + return n1.item.value; + }); + } + } + } + + function initNodes(nodes) { + graphView.graph.clearGraph(); + let n = undefined; + for (let node in nodes) { + switch (nodes[node].type) { + case "Material": + n = graphView.graph.insertNode(Nodes.Components.material); + break; + case "Color": + n = graphView.graph.insertNode(Nodes.Components.color); + n.item.value.color = nodes[node].value.color; + break; + case "Metalness": + n = graphView.graph.insertNode(Nodes.Components.metalness); + break; + case "BaseColor": + n = graphView.graph.insertNode(Nodes.Components.baseColor); + break; + case "RealSpinBox": + n = graphView.graph.insertNode(Nodes.Components.realSpinBox); + n.item.value.floating = nodes[node].value.floating; + break; + case "Texture": + n = graphView.graph.insertNode(Nodes.Components.texture); + break; + case "CheckBox": + n = graphView.graph.insertNode(Nodes.Components.checkBox); + n.item.value.binary = nodes[node].value.binary; + break; + case "ComboBox": + n = graphView.graph.insertNode(Nodes.Components.comboBox); + n.item.value.text = nodes[node].value.text; + break; + case "Roughness": + n = graphView.graph.insertNode(Nodes.Components.roughness); + break; + } + n.item.x = nodes[node].x; + n.item.y = nodes[node].y; + n.item.uuid = nodes[node].uuid; + } + } + + // Invoked from C++ side when open node graph is requested and there are unsaved changes + function promptToSaveBeforeOpen(newNodeGraphName) { + nodeGraphFileName = newNodeGraphName; + saveChangesDialog.open(); + } + + function updateGraphData() { + let arr = { + nodes: [], + edges: [] + }; + + for (let i = 0; i < graphView.graph.nodes.length; i++) { + let node = { + uuid: graphView.graph.nodes.at(i).item.uuid, + x: graphView.graph.nodes.at(i).item.x, + y: graphView.graph.nodes.at(i).item.y, + type: graphView.graph.nodes.at(i).item.type + }; + + //Set the actual value only to the OUT nodes + if (node.type == "Color" || node.type == "RealSpinBox" || node.type == "CheckBox" || node.type == "ComboBox") { + node.value = graphView.graph.nodes.at(i).item.value; + } + arr["nodes"].push(node); + } + + for (let i = 0; i < graphView.graph.edges.length; i++) { + let edge = { + outNodeUuid: graphView.graph.edges.at(i).item.sourceItem.node.item.uuid, + inNodeUuid: graphView.graph.edges.at(i).item.destinationItem.node.item.uuid, + inPortName: graphView.graph.edges.at(i).item.destinationItem.dataId, + outPortName: graphView.graph.edges.at(i).item.sourceItem.dataId + }; + arr["edges"].push(edge); + } + let newGraphData = JSON.stringify(arr); + if (NodeGraphEditorBackend.nodeGraphEditorModel.graphData !== newGraphData) { + NodeGraphEditorBackend.nodeGraphEditorModel.graphData = newGraphData; + } + } + + Connections { + target: NodeGraphEditorBackend.nodeGraphEditorModel + + onNodeGraphLoaded: { + graphView.graph.clearGraph(); + let jsonString = NodeGraphEditorBackend.nodeGraphEditorModel.graphData; + let jsonObject = JSON.parse(jsonString); + let nodes = jsonObject.nodes; + let edges = jsonObject.edges; + initNodes(nodes); + initEdges(edges); + NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = false; + } + } + + SaveAsDialog { + id: saveAsDialog + + anchors.centerIn: parent + + onSave: { + updateGraphData(); + NodeGraphEditorBackend.nodeGraphEditorModel.saveFile(fileName); + NodeGraphEditorBackend.nodeGraphEditorModel.openFileName(fileName); + } + } + + SaveChangesDialog { + id: saveChangesDialog + + anchors.centerIn: parent + + onDiscard: { + NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName = nodeGraphFileName; + NodeGraphEditorBackend.nodeGraphEditorModel.openFileName(NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName); + } + onRejected: {} + onSave: { + if (NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName !== "") { + /*Save current graph data to the backend*/ + updateGraphData(); + + /*Save old data, before opening new node graph*/ + NodeGraphEditorBackend.nodeGraphEditorModel.saveFile(NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName); + NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName = root.nodeGraphFileName; + + /*Open new node graph*/ + NodeGraphEditorBackend.nodeGraphEditorModel.openFileName(NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName); + NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = false; + } else { + saveAsDialog.open(); + } + } + } + Editor.ContextMenu { id: contextMenu @@ -49,6 +232,47 @@ Item { newNodeGraphDialog.open(); } } + + HelperWidgets.AbstractButton { + buttonIcon: StudioTheme.Constants.remove_medium + style: StudioTheme.Values.viewBarButtonStyle + tooltip: qsTr("Clear graph.") + + onClicked: () => { + graphView.graph.clearGraph(); + } + } + + HelperWidgets.AbstractButton { + buttonIcon: StudioTheme.Constants.save_medium + enabled: NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges && NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName != "" + style: StudioTheme.Values.viewBarButtonStyle + tooltip: qsTr("Save.") + + onClicked: () => { + if (NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName !== "") { + updateGraphData(); + NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = false; + NodeGraphEditorBackend.nodeGraphEditorModel.saveFile(NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName); + } else { + saveAsDialog.open(); + } + } + } + + HelperWidgets.AbstractButton { + buttonIcon: StudioTheme.Constants.saveAs_medium + style: StudioTheme.Values.viewBarButtonStyle + tooltip: qsTr("Save as ...") + + onClicked: () => { + saveAsDialog.open(); + } + } + + Label { + text: NodeGraphEditorBackend.nodeGraphEditorModel.currentFileName + } } } diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/SaveAsDialog.qml b/share/qtcreator/qmldesigner/nodegrapheditor/SaveAsDialog.qml new file mode 100644 index 00000000000..236643f953b --- /dev/null +++ b/share/qtcreator/qmldesigner/nodegrapheditor/SaveAsDialog.qml @@ -0,0 +1,106 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme +import EffectComposerBackend + +StudioControls.Dialog { + id: root + + property alias fileName: nameText.text + + signal save(string fileName) + + closePolicy: Popup.CloseOnEscape + implicitHeight: 160 + implicitWidth: 350 + modal: true + title: qsTr("Save node graph") + + contentItem: Item { + Column { + spacing: 2 + + Row { + id: row + + Text { + anchors.verticalCenter: parent.verticalCenter + color: StudioTheme.Values.themeTextColor + text: qsTr("Node graph name: ") + } + + StudioControls.TextField { + id: nameText + + actionIndicator.visible: false + translationIndicator.visible: false + + Keys.onEnterPressed: btnSave.onClicked() + Keys.onEscapePressed: root.reject() + Keys.onReturnPressed: btnSave.onClicked() + onTextChanged: { + let errMsg = ""; + if (/[^A-Za-z0-9_]+/.test(text)) + errMsg = qsTr("Name contains invalid characters."); + // if (!/^[A-Z]/.test(text)) + // errMsg = qsTr("Name must start with a capital letter.") + if (text.length < 1) + errMsg = qsTr("Name must have at least 1 character."); + else if (/\s/.test(text)) + errMsg = qsTr("Name cannot contain white space."); + + emptyText.text = errMsg; + btnSave.enabled = errMsg.length === 0; + } + } + } + + Text { + id: emptyText + + anchors.right: row.right + color: StudioTheme.Values.themeError + } + } + + Row { + anchors.bottom: parent.bottom + anchors.right: parent.right + spacing: 2 + + HelperWidgets.Button { + id: btnSave + + enabled: nameText.text !== "" + text: qsTr("Save") + + onClicked: { + if (!enabled) // needed since this event handler can be triggered from keyboard events + return; + root.save(nameText.text); + root.accept(); // TODO: confirm before overriding node graph with same name + } + } + + HelperWidgets.Button { + text: qsTr("Cancel") + + onClicked: { + root.reject(); + } + } + } + } + + onOpened: { + nameText.text = ""; + nameText.selectAll(); + nameText.forceActiveFocus(); + emptyText.text = ""; + } +} diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/SaveChangesDialog.qml b/share/qtcreator/qmldesigner/nodegrapheditor/SaveChangesDialog.qml new file mode 100644 index 00000000000..3c265b30dfe --- /dev/null +++ b/share/qtcreator/qmldesigner/nodegrapheditor/SaveChangesDialog.qml @@ -0,0 +1,63 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme +import EffectComposerBackend + +StudioControls.Dialog { + id: root + + signal discard + signal save + + closePolicy: Popup.CloseOnEscape + implicitHeight: 130 + implicitWidth: 300 + modal: true + title: qsTr("Save Changes") + + contentItem: Item { + Text { + color: StudioTheme.Values.themeTextColor + text: qsTr("Current node graph has unsaved changes.") + } + + HelperWidgets.Button { + anchors.bottom: parent.bottom + text: qsTr("Cancel") + width: 60 + + onClicked: root.reject() + } + + Row { + anchors.bottom: parent.bottom + anchors.right: parent.right + spacing: 2 + + HelperWidgets.Button { + text: qsTr("Save") + width: 50 + + onClicked: { + root.save(); + root.accept(); + } + } + + HelperWidgets.Button { + text: qsTr("Discard Changes") + width: 110 + + onClicked: { + root.discard(); + root.accept(); + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Editor/Graph.qml b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Editor/Graph.qml index 9907c3bb9a9..172ac31c24e 100644 --- a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Editor/Graph.qml +++ b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Editor/Graph.qml @@ -4,6 +4,7 @@ import QtQuick import QuickQanava as Qan +import NodeGraphEditorBackend Qan.Graph { id: root @@ -34,7 +35,7 @@ Qan.Graph { }); } } - onNodeRemoved: node => {} + onNodeRemoved: node => {NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = true;} onOnEdgeRemoved: edge => { const srcNode = edge.getSource(); const dstNode = edge.getDestination(); @@ -43,5 +44,13 @@ Qan.Graph { // TODO: add reset binding function dstNode.item.value[dstPortItem.dataName] = dstNode.item.reset[dstPortItem.dataName]; + NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = true; + } + + onEdgeInserted: { + NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = true; + } + onNodeInserted: { + NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = true; } } diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Editor/Port.qml b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Editor/Port.qml index e75fa9ecf47..328edb7ba6b 100644 --- a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Editor/Port.qml +++ b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Editor/Port.qml @@ -11,4 +11,5 @@ Qan.Port { property var dataBinding: null property string dataName: "" property string dataType: "" + property string dataId: "" } diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Base.qml b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Base.qml index e0f0a892bbf..c27147c3e41 100644 --- a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Base.qml +++ b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Base.qml @@ -23,7 +23,8 @@ Qan.NodeItem { property var pin: [] property var pout: [] } - readonly property string uuid: NodeGraphEditorBackend.widget.generateUUID() + property string type + property string uuid: NodeGraphEditorBackend.widget.generateUUID() Layout.preferredHeight: 60 Layout.preferredWidth: 100 @@ -34,6 +35,12 @@ Qan.NodeItem { Component.onCompleted: { internal.configurePorts(root.graph); } + onXChanged: { + NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = true; + } + onYChanged: { + NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = true; + } Qan.RectNodeTemplate { anchors.fill: parent @@ -56,6 +63,7 @@ Qan.NodeItem { } portItem.dataName = data.alias; portItem.dataType = data.type; + portItem.dataId = data.id; }; root.portsMetaData.pin.forEach(data => { diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/BaseColor.qml b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/BaseColor.qml index c9e261af529..33638b0ee6e 100644 --- a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/BaseColor.qml +++ b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/BaseColor.qml @@ -24,6 +24,7 @@ Base { Layout.preferredHeight: 150 Layout.preferredWidth: 150 + type: "BaseColor" portsMetaData: QtObject { property var pin: [ diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/CheckBox.qml b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/CheckBox.qml index 20422b30149..fc48d19cb0b 100644 --- a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/CheckBox.qml +++ b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/CheckBox.qml @@ -5,14 +5,17 @@ import QtQuick import QtQuick.Layouts import StudioControls as StudioControls +import NodeGraphEditorBackend Base { id: root readonly property QtObject value: QtObject { - property bool binary: checkBox.checked + property bool binary } + type: "CheckBox" + portsMetaData: QtObject { property var pin: [] property var pout: [ @@ -34,5 +37,11 @@ Base { actionIndicatorVisible: false anchors.centerIn: parent + checked: root.value.binary + + onCheckedChanged: { + NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = true; + root.value.binary = checked; + } } } diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Color.qml b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Color.qml index 290e5c0e2e2..91822527e9a 100644 --- a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Color.qml +++ b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Color.qml @@ -3,14 +3,21 @@ import QtQuick import QtQuick.Layouts +import NodeGraphEditorBackend Base { id: root readonly property QtObject value: QtObject { property color color + + onColorChanged: { + NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = true; + } } + type: "Color" + portsMetaData: QtObject { property var pin: [] property var pout: [ diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/ColorEditorPopup.qml b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/ColorEditorPopup.qml index e89d74dc30d..6ecdc191729 100644 --- a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/ColorEditorPopup.qml +++ b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/ColorEditorPopup.qml @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick - import HelperWidgets as HelperWidgets import StudioControls as StudioControls @@ -18,7 +17,6 @@ StudioControls.PopupDialog { function open(showItem) { loader.ensureActive(); colorPopup.show(showItem); - loader.updateOriginalColor(); } diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/ComboBox.qml b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/ComboBox.qml index e555ecc26e4..7e8195329a0 100644 --- a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/ComboBox.qml +++ b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/ComboBox.qml @@ -6,15 +6,21 @@ import QtQuick.Layouts import HelperWidgets as HelperWidgets import StudioControls as StudioControls +import NodeGraphEditorBackend Base { id: root property QtObject value: QtObject { property url text: `image://qmldesigner_nodegrapheditor/${comboBox.currentValue}` + + onTextChanged: { + NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = true; + } } Layout.preferredWidth: 175 + type: "ComboBox" portsMetaData: QtObject { property var pin: [] diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Material.qml b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Material.qml index b33c4380ccb..774d26858b2 100644 --- a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Material.qml +++ b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Material.qml @@ -19,6 +19,7 @@ Base { Layout.preferredHeight: 150 Layout.preferredWidth: 150 + type: "Material" portsMetaData: QtObject { property var pin: [ diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Metalness.qml b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Metalness.qml index a8262e9cf66..7f84d0e3ed1 100644 --- a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Metalness.qml +++ b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Metalness.qml @@ -22,6 +22,7 @@ Base { Layout.preferredHeight: 150 Layout.preferredWidth: 150 + type: "Metalness" portsMetaData: QtObject { property var pin: [ diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/RealSpinBox.qml b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/RealSpinBox.qml index cca15c19312..bb13bdc9fc5 100644 --- a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/RealSpinBox.qml +++ b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/RealSpinBox.qml @@ -5,15 +5,17 @@ import QtQuick import QtQuick.Layouts import StudioControls as StudioControls +import NodeGraphEditorBackend Base { id: root readonly property QtObject value: QtObject { - property real floating: realSpinBox.realValue + property real floating } Layout.preferredWidth: 175 + type: "RealSpinBox" portsMetaData: QtObject { property var pin: [] @@ -37,5 +39,11 @@ Base { actionIndicatorVisible: false anchors.centerIn: parent decimals: 2 + realValue: root.value.floating + + onRealValueChanged: { + NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = true; + root.value.floating = realValue; + } } } diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Roughness.qml b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Roughness.qml index 4d673dcb1f0..6b1f4d9d59b 100644 --- a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Roughness.qml +++ b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Roughness.qml @@ -22,6 +22,7 @@ Base { Layout.preferredHeight: 150 Layout.preferredWidth: 150 + type: "Roughness" portsMetaData: QtObject { property var pin: [ diff --git a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Texture.qml b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Texture.qml index 2473fe61121..e3b2e1029d9 100644 --- a/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Texture.qml +++ b/share/qtcreator/qmldesigner/nodegrapheditor/imports/Nodes/Texture.qml @@ -7,6 +7,7 @@ import QtQuick3D as QtQuick3D import QuickQanava as Qan +import NodeGraphEditorBackend import NodeGraphEditorBackend Base { @@ -19,10 +20,14 @@ Base { Layout.preferredHeight: 150 Layout.preferredWidth: 150 + type: "Texture" Component.onCompleted: { node.label = "Texture"; } + onValueChanged: { + NodeGraphEditorBackend.nodeGraphEditorModel.hasUnsavedChanges = true; + } Image { anchors.centerIn: parent diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 13a44c69e0e..a829f5ef263 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -33,7 +33,6 @@ option(ENABLE_METAINFO_TRACING "Enable meta info tracing" ${ENV_QTC_ENABLE_METAI add_feature_info("Meta info tracing" ${ENABLE_METAINFO_TRACING} "") add_subdirectory(libs) - add_qtc_plugin(QmlDesigner PLUGIN_RECOMMENDS QmlPreview CONDITION TARGET QmlDesignerCore AND TARGET Qt::QuickWidgets AND TARGET Qt::Svg diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp index b1c92ea108d..15a01c6dbdb 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp @@ -124,7 +124,7 @@ QPair AssetsLibraryIconProvider::fetchPixmap(const QString &id, type = "sound"; else if (asset.isVideo()) type = "video"; - else if (asset.isEffect()) + else if (asset.isEffect() || asset.isNodeGraph()) type = QmlDesigner::ModelNodeOperations::getEffectIcon(id); QString pathTemplate = QString(":/AssetsLibrary/images/asset_%1%2.png").arg(type); diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 8fda0a44482..de4fe633587 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -612,6 +612,11 @@ QSet AssetsLibraryWidget::supportedAssetSuffixes(bool complex) return suffixes; } +void AssetsLibraryWidget::openNodeGraphEditor(const QString &filePath) +{ + ModelNodeOperations::openNodeGraphEditor(filePath); +} + void AssetsLibraryWidget::openEffectComposer(const QString &filePath) { ModelNodeOperations::openEffectComposer(filePath); @@ -695,6 +700,9 @@ QPair AssetsLibraryWidget::getAssetTypeAndData(const QStrin } else if (asset.isEffect()) { // Data: Effect Composer format (suffix) return {Constants::MIME_TYPE_ASSET_EFFECT, asset.suffix().toUtf8()}; + } else if (asset.isNodeGraph()) { + // Data: Node Grpah Editor format (suffix) + return {Constants::MIME_TYPE_ASSET_NODEGRAPH, asset.suffix().toUtf8()}; } } return {}; diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h index 2493c401e0d..f9112bcc92e 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h @@ -85,6 +85,7 @@ public: const QString &targetDirPath = {}); Q_INVOKABLE QSet supportedAssetSuffixes(bool complex); Q_INVOKABLE void openEffectComposer(const QString &filePath); + Q_INVOKABLE void openNodeGraphEditor(const QString &filePath); Q_INVOKABLE int qtVersion() const; Q_INVOKABLE void invalidateThumbnail(const QString &id); Q_INVOKABLE QSize imageSize(const QString &id); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 2922fbe532a..58508381e8e 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -2213,6 +2213,13 @@ void openEffectComposer(const QString &filePath) } } +void openNodeGraphEditor(const QString &filePath) +{ + QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("NodeGraphEditor", true); + QmlDesignerPlugin::instance()->viewManager() + .emitCustomNotification("open_nodegrapheditor_graph", {}, {filePath}); +} + void openOldEffectMaker(const QString &filePath) { const ProjectExplorer::Target *target = ProjectExplorer::ProjectTree::currentTarget(); @@ -2282,6 +2289,7 @@ QString getEffectsDefaultDirectory(const QString &defaultDir) return getAssetDefaultDirectory("effects", defaultDir); } + QString getEffectIcon(const QString &effectPath) { Utils::FilePath effectFile = QmlDesignerPlugin::instance()->documentManager() diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index f54bb72d209..e2bc8980421 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -212,6 +212,7 @@ void editInEffectComposer(const SelectionContext &selectionContext); QMLDESIGNERCOMPONENTS_EXPORT Utils::FilePath getEffectsImportDirectory(); QMLDESIGNERCOMPONENTS_EXPORT QString getEffectsDefaultDirectory(const QString &defaultDir = {}); void openEffectComposer(const QString &filePath); +void openNodeGraphEditor(const QString &filePath); void openOldEffectMaker(const QString &filePath); QString getEffectIcon(const QString &effectPath); bool useLayerEffect(); diff --git a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditormodel.cpp b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditormodel.cpp index 9cbe4630041..67995e93b08 100644 --- a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditormodel.cpp +++ b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditormodel.cpp @@ -5,6 +5,9 @@ #include "nodegrapheditorview.h" +#include "nodegrapheditorview.h" +#include +#include namespace QmlDesigner { NodeGraphEditorModel::NodeGraphEditorModel(NodeGraphEditorView *nodeGraphEditorView) @@ -13,4 +16,113 @@ NodeGraphEditorModel::NodeGraphEditorModel(NodeGraphEditorView *nodeGraphEditorV { } +void NodeGraphEditorModel::saveFile(QString fileName){ + auto directory = saveDirectory(); + auto saveFile = QFile(directory + '/' + fileName + ".qng"); + if (!saveFile.open(QIODevice::WriteOnly)) { + QString error = QString("Error: Couldn't save node graph file"); + qCritical() << error; + return; + } + auto str = m_graphData.toStdString(); + saveFile.write(str.c_str()); + saveFile.close(); +} + + +void NodeGraphEditorModel::openFile(QString filePath){ + const QString nodeGraphName = QFileInfo(filePath).baseName(); + setCurrentFileName(nodeGraphName); + QFile nodeGraphFile(filePath); + if (!nodeGraphFile.open(QIODevice::ReadOnly)) { + QString error = QString("Couldn't open node graph file: '%1'").arg(filePath); + qWarning() << qPrintable(error); + return; + } + QByteArray data = nodeGraphFile.readAll(); + m_graphData = QString::fromStdString(data.toStdString()); + graphDataChanged(); + nodeGraphLoaded(); + + nodeGraphFile.close(); + if (data.isEmpty()) + return; +} +QString NodeGraphEditorModel::saveDirectory(){ + if (m_saveDirectory!="") { + return m_saveDirectory; + } else { + const QString assetDir = "nodegraphs"; + QString targetDir = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath().toString(); + QString adjustedDefaultDirectory = targetDir; + Utils::FilePath contentPath = QmlDesignerPlugin::instance()->documentManager().currentResourcePath(); + Utils::FilePath assetPath = contentPath.pathAppended(assetDir); + if (!assetPath.exists()) + assetPath.createDir(); + + if (assetPath.exists() && assetPath.isDir()) + adjustedDefaultDirectory = assetPath.toString(); + m_saveDirectory = adjustedDefaultDirectory; + } + return m_saveDirectory; +} + +void NodeGraphEditorModel::openFileName(QString fileName){ + auto directory = saveDirectory(); + + const QString nodeGraphName = QFileInfo(fileName).baseName(); + setCurrentFileName(nodeGraphName); + QFile nodeGraphFile(directory + "/" + fileName + ".qng"); + if (!nodeGraphFile.open(QIODevice::ReadOnly)) { + QString error = QString("Couldn't open node graph file: '%1'").arg(nodeGraphFile.fileName()); + qWarning() << qPrintable(error); + return; + } + QByteArray data = nodeGraphFile.readAll(); + m_graphData = QString::fromStdString(data.toStdString()); + graphDataChanged(); + nodeGraphLoaded(); + + nodeGraphFile.close(); + if (data.isEmpty()) + return; +} + + +bool NodeGraphEditorModel::hasUnsavedChanges() const +{ + return m_hasUnsavedChanges; +} + +void NodeGraphEditorModel::setHasUnsavedChanges(bool val) +{ + if (m_hasUnsavedChanges == val) + return; + + m_hasUnsavedChanges = val; + emit hasUnsavedChangesChanged(); +} + + + QString NodeGraphEditorModel::currentFileName() const{ + return m_currentFileName; + } + + void NodeGraphEditorModel::setCurrentFileName(const QString &newCurrentFileName){ + if (m_currentFileName == newCurrentFileName) + return; + + m_currentFileName = newCurrentFileName; + emit currentFileNameChanged(); + } + + void NodeGraphEditorModel::setGraphData(QString val){ + if (m_graphData == val) + return; + + m_graphData = val; + emit graphDataChanged(); + }; + + QString NodeGraphEditorModel::graphData(){return m_graphData;} } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditormodel.h b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditormodel.h index d14c9122500..33e5a30e4ae 100644 --- a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditormodel.h +++ b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditormodel.h @@ -16,9 +16,34 @@ class NodeGraphEditorModel : public QStandardItemModel public: NodeGraphEditorModel(NodeGraphEditorView *nodeGraphEditorView); - + Q_INVOKABLE void openFile(QString filePath); + Q_INVOKABLE void openFileName(QString filePath); + Q_INVOKABLE void saveFile(QString fileName); private: QPointer m_editorView; + + +Q_PROPERTY(bool hasUnsavedChanges MEMBER m_hasUnsavedChanges WRITE setHasUnsavedChanges NOTIFY hasUnsavedChangesChanged) + Q_PROPERTY(QString currentFileName READ currentFileName WRITE setCurrentFileName NOTIFY currentFileNameChanged) + Q_PROPERTY(QString graphData READ graphData WRITE setGraphData NOTIFY graphDataChanged) + signals: +void graphDataChanged(); +void nodeGraphLoaded(); + void hasUnsavedChangesChanged(); + void currentFileNameChanged(); + public: + void setGraphData(QString val); + QString graphData(); + bool hasUnsavedChanges() const; + void setHasUnsavedChanges(bool val); + QString currentFileName() const; + void setCurrentFileName(const QString &newCurrentFileName); +private: + QString saveDirectory(); + QString m_graphData{""}; + QString m_currentFileName{""}; + QString m_saveDirectory{""}; + bool m_hasUnsavedChanges{false}; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorview.cpp b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorview.cpp index 1a279d34887..5278a88e96c 100644 --- a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorview.cpp +++ b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorview.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "nodegrapheditorview.h" - #include "nodegrapheditormodel.h" #include "nodegrapheditorwidget.h" @@ -31,4 +30,16 @@ WidgetInfo NodeGraphEditorView::widgetInfo() tr("Node Graph")); } +void NodeGraphEditorView::customNotification([[maybe_unused]] const AbstractView *view, + const QString &identifier, + [[maybe_unused]] const QList &nodeList, + const QList &data) +{ + if (data.size() < 1) + return; + if (identifier == "open_nodegrapheditor_graph") { + const QString nodeGraphPath = data[0].toString(); + m_editorWidget->openNodeGraph(nodeGraphPath); + } +} } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorview.h b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorview.h index 1158dc9b38b..a02e17a4f31 100644 --- a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorview.h +++ b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorview.h @@ -21,8 +21,11 @@ public: WidgetInfo widgetInfo() override; private: + void customNotification(const AbstractView *view, const QString &identifier, + const QList &nodeList, const QList &data) override; QPointer m_editorModel; QPointer m_editorWidget; + }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorwidget.cpp b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorwidget.cpp index 0f52097e0db..b0d2d234c8a 100644 --- a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorwidget.cpp +++ b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorwidget.cpp @@ -41,9 +41,28 @@ static Utils::FilePath materialsPath() return DocumentManager::currentResourcePath().pathAppended("materials"); } +void NodeGraphEditorWidget::doOpenNodeGraph(){ + m_model->openFile(m_filePath); +} + +void NodeGraphEditorWidget::openNodeGraph(const QString &path) +{ + m_filePath = path; + if (m_model->hasUnsavedChanges()){ + /*Pass new path to update if saved*/ + auto newFile = QFileInfo(path).baseName(); + QMetaObject::invokeMethod(quickWidget()->rootObject(), "promptToSaveBeforeOpen",Q_ARG(QVariant, newFile)); + } + else{ + doOpenNodeGraph(); + } +} + + NodeGraphEditorWidget::NodeGraphEditorWidget(NodeGraphEditorView *nodeGraphEditorView, NodeGraphEditorModel *nodeGraphEditorModel) : m_editorView(nodeGraphEditorView) + , m_model(nodeGraphEditorModel) , m_imageProvider(nullptr) , m_qmlSourceUpdateShortcut(nullptr) { @@ -64,6 +83,7 @@ NodeGraphEditorWidget::NodeGraphEditorWidget(NodeGraphEditorView *nodeGraphEdito auto map = registerPropertyMap("NodeGraphEditorBackend"); map->setProperties({{"nodeGraphEditorModel", QVariant::fromValue(nodeGraphEditorModel)}}); map->setProperties({{"widget", QVariant::fromValue(this)}}); + connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, &NodeGraphEditorWidget::reloadQmlSource); Theme::setupTheme(engine()); diff --git a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorwidget.h b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorwidget.h index a0e491dd4a6..f5a2ac6f6ef 100644 --- a/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorwidget.h +++ b/src/plugins/qmldesigner/components/nodegrapheditor/nodegrapheditorwidget.h @@ -30,9 +30,11 @@ public: NodeGraphEditorWidget(NodeGraphEditorView *nodeGraphEditorView, NodeGraphEditorModel *nodeGraphEditorModel); ~NodeGraphEditorWidget() override = default; - static QString qmlSourcesPath(); Q_INVOKABLE QString generateUUID() const; + static QString qmlSourcesPath(); + Q_INVOKABLE void doOpenNodeGraph(); + void openNodeGraph(const QString &path); protected: void showEvent(QShowEvent *) override; @@ -41,12 +43,13 @@ protected: private: void reloadQmlSource(); - private: QPointer m_editorView; + QPointer m_model; Internal::NodeGraphEditorImageProvider *m_imageProvider; QShortcut *m_qmlSourceUpdateShortcut; QElapsedTimer m_usageTimer; + QString m_filePath; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/libs/qmldesignerutils/asset.cpp b/src/plugins/qmldesigner/libs/qmldesignerutils/asset.cpp index 2db4708d9ac..ec511329209 100644 --- a/src/plugins/qmldesigner/libs/qmldesignerutils/asset.cpp +++ b/src/plugins/qmldesigner/libs/qmldesignerutils/asset.cpp @@ -85,6 +85,13 @@ const QStringList &Asset::supportedEffectComposerSuffixes() return retList; } +const QStringList &Asset::supportedNodeGraphSuffixes() +{ + // These are file types only supported by Node Graph Editor + static QStringList retList {"*.qng"}; + return retList; +} + const QStringList &Asset::supportedMaterialSuffixes() { static QStringList retList {"*.mat"}; @@ -112,6 +119,7 @@ const QSet &Asset::supportedSuffixes() insertSuffixes(supportedVideoSuffixes()); insertSuffixes(supportedTexture3DSuffixes()); insertSuffixes(supportedEffectComposerSuffixes()); + insertSuffixes(supportedNodeGraphSuffixes()); insertSuffixes(supportedMaterialSuffixes()); insertSuffixes(supportedImported3dSuffixes()); } @@ -196,6 +204,10 @@ bool Asset::isEffect() const return m_type == Asset::Type::Effect; } +bool Asset::isNodeGraph() const +{ + return m_type == Asset::Type::NodeGraph; +} bool Asset::isMaterial() const { return m_type == Asset::Type::Material; @@ -252,6 +264,8 @@ void Asset::resolveType() m_type = Asset::Type::Texture3D; else if (supportedEffectComposerSuffixes().contains(m_suffix)) m_type = Asset::Type::Effect; + else if (supportedNodeGraphSuffixes().contains(m_suffix)) + m_type = Asset::Type::NodeGraph; else if (supportedMaterialSuffixes().contains(m_suffix)) m_type = Asset::Type::Material; else if (supportedImported3dSuffixes().contains(m_suffix)) diff --git a/src/plugins/qmldesigner/libs/qmldesignerutils/asset.h b/src/plugins/qmldesigner/libs/qmldesignerutils/asset.h index 7ca7d04c5aa..dbb2305a1ba 100644 --- a/src/plugins/qmldesigner/libs/qmldesignerutils/asset.h +++ b/src/plugins/qmldesigner/libs/qmldesignerutils/asset.h @@ -26,6 +26,7 @@ public: Video, Texture3D, Effect, + NodeGraph, Material, Imported3D }; @@ -41,6 +42,7 @@ public: static const QStringList &supportedVideoSuffixes(); static const QStringList &supportedTexture3DSuffixes(); static const QStringList &supportedEffectComposerSuffixes(); + static const QStringList &supportedNodeGraphSuffixes(); static const QStringList &supportedMaterialSuffixes(); static const QStringList &supportedImported3dSuffixes(); static const QSet &supportedSuffixes(); @@ -64,6 +66,7 @@ public: bool isHdrFile() const; bool isKtxFile() const; bool isEffect() const; + bool isNodeGraph() const; bool isMaterial() const; bool isImported3D() const; bool isSupported() const; diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index bc8ef76d5a5..8a10fcd2a4b 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -88,6 +88,7 @@ inline constexpr char MIME_TYPE_ASSET_TEXTURE3D[] = "application/vnd.qtdesignstudio.asset.texture3d"; inline constexpr char MIME_TYPE_MODELNODE_LIST[] = "application/vnd.qtdesignstudio.modelnode.list"; inline constexpr char MIME_TYPE_ASSET_EFFECT[] = "application/vnd.qtdesignstudio.asset.effect"; +inline constexpr char MIME_TYPE_ASSET_NODEGRAPH[] = "application/vnd.qtdesignstudio.asset.nodegraph"; // Menus inline constexpr char M_VIEW_WORKSPACES[] = "QmlDesigner.Menu.View.Workspaces";