Extend copy/paste support to selections containing multiple nodes

This commit is contained in:
Candice Bentéjac 2022-08-24 17:51:05 +02:00
parent 08d502f4a6
commit e11452efdb
2 changed files with 43 additions and 24 deletions

View file

@ -755,23 +755,25 @@ class UIGraph(QObject):
self.hoveredNode = None self.hoveredNode = None
@Slot(result=str) @Slot(result=str)
def getSelectedNodeContent(self): def getSelectedNodesContent(self):
""" """
Return the content of the currently selected node in a string, formatted to JSON. Return the content of the currently selected nodes in a string, formatted to JSON.
If no node is currently selected, an empty string is returned. If no node is currently selected, an empty string is returned.
""" """
if self._selectedNode: if self._selectedNodes:
d = self._graph.toDict() d = self._graph.toDict()
node = d[self._selectedNode.name] selection = {}
return json.dumps(node, indent=4) for node in self._selectedNodes:
selection[node.name] = d[node.name]
return json.dumps(selection, indent=4)
return '' return ''
@Slot(str, QPoint) @Slot(str, QPoint)
def pasteNode(self, clipboardContent, position=None): def pasteNodes(self, clipboardContent, position=None):
""" """
Parse the content of the clipboard to see whether it contains Parse the content of the clipboard to see whether it contains
a valid node description. If that is the case, the node described valid node descriptions. If that is the case, the nodes described
in the clipboard is built with the available information. in the clipboard are built with the available information.
Otherwise, nothing is done. Otherwise, nothing is done.
This function does not need to be preceded by a call to "copyNodeContent". This function does not need to be preceded by a call to "copyNodeContent".
@ -780,7 +782,7 @@ class UIGraph(QObject):
can be used to generate a node. can be used to generate a node.
For example, it is enough to have: For example, it is enough to have:
{"nodeType":"CameraInit"} {"nodeName": {"nodeType":"CameraInit"}}
in the clipboard to create a default CameraInit node. in the clipboard to create a default CameraInit node.
""" """
if not clipboardContent: if not clipboardContent:
@ -794,18 +796,35 @@ class UIGraph(QObject):
if not isinstance(d, dict): if not isinstance(d, dict):
raise ValueError("The clipboard does not contain a valid node. Cannot paste it.") raise ValueError("The clipboard does not contain a valid node. Cannot paste it.")
nodeType = d.get("nodeType", None) finalPosition = None
if not nodeType: prevPosition = None
raise ValueError("The clipboard does not contain a valid node. Cannot paste it.")
attributes = {} for key in d:
attributes.update(d.get("inputs", {})) nodeDesc = d[key]
attributes.update(d.get("outputs", {})) nodeType = nodeDesc.get("nodeType", None)
if not nodeType:
pass
if isinstance(position, QPoint): attributes = {}
position = Position(position.x(), position.y()) attributes.update(nodeDesc.get("inputs", {}))
attributes.update(nodeDesc.get("outputs", {}))
self.push(commands.PasteNodeCommand(self._graph, nodeType, position=position, **attributes)) currentPosition = nodeDesc.get("position", None)
if isinstance(position, QPoint) and not finalPosition:
finalPosition = Position(position.x(), position.y())
else:
if prevPosition and currentPosition:
# if the nodes both have a position, recreate the distance between them from a different
# starting point
x = finalPosition.x + (currentPosition[0] - prevPosition[0])
y = finalPosition.y + (currentPosition[1] - prevPosition[1])
finalPosition = Position(x, y)
else:
# if either the current node or previous one lack a position, use a custom one
finalPosition = Position(finalPosition.x + self.layout.gridSpacing + self.layout.nodeWidth, finalPosition.y)
self.push(commands.PasteNodeCommand(self._graph, nodeType, position=finalPosition, **attributes))
prevPosition = currentPosition
undoStack = Property(QObject, lambda self: self._undoStack, constant=True) undoStack = Property(QObject, lambda self: self._undoStack, constant=True)
graphChanged = Signal() graphChanged = Signal()

View file

@ -77,9 +77,9 @@ Item {
} }
/// Copy node content to clipboard /// Copy node content to clipboard
function copyNode() function copyNodes()
{ {
var nodeContent = uigraph.getSelectedNodeContent() var nodeContent = uigraph.getSelectedNodesContent()
if (nodeContent !== '') { if (nodeContent !== '') {
Clipboard.clear() Clipboard.clear()
Clipboard.setText(nodeContent) Clipboard.setText(nodeContent)
@ -87,11 +87,11 @@ Item {
} }
/// Paste content of clipboard to graph editor and create new node if valid /// Paste content of clipboard to graph editor and create new node if valid
function pasteNode() function pasteNodes()
{ {
root.pastePosition = mapToItem(draggable, mouseArea.mouseX, mouseArea.mouseY) root.pastePosition = mapToItem(draggable, mouseArea.mouseX, mouseArea.mouseY)
var copiedContent = Clipboard.getText() var copiedContent = Clipboard.getText()
uigraph.pasteNode(copiedContent, root.pastePosition) uigraph.pasteNodes(copiedContent, root.pastePosition)
} }
Keys.onPressed: { Keys.onPressed: {
@ -107,10 +107,10 @@ Item {
if (event.key === Qt.Key_C) if (event.key === Qt.Key_C)
if (event.modifiers == Qt.ControlModifier) if (event.modifiers == Qt.ControlModifier)
copyNode() copyNodes()
if (event.key === Qt.Key_V) if (event.key === Qt.Key_V)
if (event.modifiers == Qt.ControlModifier) if (event.modifiers == Qt.ControlModifier)
pasteNode() pasteNodes()
} }
MouseArea { MouseArea {