[ui] GraphEditor: use entire node selection when handling '_ from here' operations

This commit is contained in:
ChemicalXandco 2021-04-18 14:33:17 +01:00
parent e8a5491178
commit fdfabf0066
3 changed files with 56 additions and 39 deletions

View file

@ -1132,11 +1132,6 @@ class Graph(BaseObject):
for node in self.nodes: for node in self.nodes:
node.clearSubmittedChunks() node.clearSubmittedChunks()
@Slot(Node)
def clearDataFrom(self, startNode):
for node in self.dfsOnDiscover(startNodes=[startNode], reverse=True, dependenciesOnly=True)[0]:
node.clearData()
def iterChunksByStatus(self, status): def iterChunksByStatus(self, status):
""" Iterate over NodeChunks with the given status """ """ Iterate over NodeChunks with the given status """
for node in self.nodes: for node in self.nodes:

View file

@ -541,21 +541,19 @@ class UIGraph(QObject):
for node in nodes: for node in nodes:
self.push(commands.RemoveNodeCommand(self._graph, node)) self.push(commands.RemoveNodeCommand(self._graph, node))
@Slot(Node) @Slot(QObject)
def removeNodesFrom(self, startNode): def removeNodesFrom(self, nodes):
""" """
Remove all nodes starting from 'startNode' to graph leaves. Remove all nodes starting from 'startNode' to graph leaves.
Args: Args:
startNode (Node): the node to start from. startNode (Node): the node to start from.
""" """
if not startNode: with self.groupedGraphModification("Remove Nodes From Selected Nodes"):
return nodesToRemove, _ = self._graph.dfsOnDiscover(startNodes=nodes, reverse=True, dependenciesOnly=True)
with self.groupedGraphModification("Remove Nodes from {}".format(startNode.name)):
nodes, _ = self._graph.dfsOnDiscover(startNodes=[startNode], reverse=True, dependenciesOnly=True)
# Perform nodes removal from leaves to start node so that edges # Perform nodes removal from leaves to start node so that edges
# can be re-created in correct order on redo. # can be re-created in correct order on redo.
self.removeNodes(list(reversed(nodes))) self.removeNodes(list(reversed(nodesToRemove)))
@Slot(QObject, result="QVariantList") @Slot(QObject, result="QVariantList")
def duplicateNodes(self, nodes): def duplicateNodes(self, nodes):
@ -579,29 +577,38 @@ class UIGraph(QObject):
self.moveNode(n, Position(n.x, bbox[3] + self.layout.gridSpacing + n.y)) self.moveNode(n, Position(n.x, bbox[3] + self.layout.gridSpacing + n.y))
return duplicates return duplicates
@Slot(Node, result="QVariantList") @Slot(QObject, result="QVariantList")
def duplicateNodesFrom(self, startNode): def duplicateNodesFrom(self, nodes):
""" """
Duplicate all nodes starting from 'startNode' to graph leaves. Duplicate all nodes starting from 'nodes' to graph leaves.
Args: Args:
startNode (Node): the node to start from. nodes (list[Node]): the nodes to start from.
Returns: Returns:
list[Node]: the list of duplicated nodes list[Node]: the list of duplicated nodes
""" """
if not startNode: with self.groupedGraphModification("Duplicate Nodes From Selected Nodes"):
return nodesToDuplicate, _ = self._graph.dfsOnDiscover(startNodes=nodes, reverse=True, dependenciesOnly=True)
with self.groupedGraphModification("Duplicate Nodes from {}".format(startNode.name)): duplicates = self.duplicateNodes(nodesToDuplicate)
nodes, _ = self._graph.dfsOnDiscover(startNodes=[startNode], reverse=True, dependenciesOnly=True)
duplicates = self.duplicateNodes(nodes)
return duplicates return duplicates
@Slot(QObject) @Slot(QObject)
def clearData(self, nodes): def clearData(self, nodes):
""" Clear data from 'nodes'. """
nodes = self.filterNodes(nodes) nodes = self.filterNodes(nodes)
for n in nodes: for n in nodes:
n.clearData() n.clearData()
@Slot(QObject)
def clearDataFrom(self, nodes):
"""
Clear data from all nodes starting from 'nodes' to graph leaves.
Args:
nodes (list[Node]): the nodes to start from.
"""
self.clearData(self._graph.dfsOnDiscover(startNodes=nodes, reverse=True, dependenciesOnly=True)[0])
@Slot(Attribute, Attribute) @Slot(Attribute, Attribute)
def addEdge(self, src, dst): def addEdge(self, src, dst):
if isinstance(dst, ListAttribute) and not isinstance(src, ListAttribute): if isinstance(dst, ListAttribute) and not isinstance(src, ListAttribute):
@ -664,22 +671,37 @@ class UIGraph(QObject):
@Slot(Node) @Slot(Node)
def appendSelection(self, node): def appendSelection(self, node):
""" Append 'node' to the selection if it is not already part of the selection. """
if not self._selectedNodes.contains(node): if not self._selectedNodes.contains(node):
self._selectedNodes.append(node) self._selectedNodes.append(node)
@Slot("QVariantList")
def selectNodes(self, nodes):
""" Append 'nodes' to the selection. """
for node in nodes:
self.appendSelection(node)
self.selectedNodesChanged.emit()
@Slot(Node) @Slot(Node)
def selectFollowing(self, node): def selectFollowing(self, node):
for n in self._graph.dfsOnDiscover(startNodes=[node], reverse=True, dependenciesOnly=True)[0]: """ Select all the nodes the depend on 'node'. """
self.appendSelection(n) self.selectNodes(self._graph.dfsOnDiscover(startNodes=[node], reverse=True, dependenciesOnly=True)[0])
self.selectedNodesChanged.emit()
@Slot(QObject, QObject) @Slot(QObject, QObject)
def boxSelect(self, selection, draggable): def boxSelect(self, selection, draggable):
"""
Select nodes that overlap with 'selection'.
Takes into account the zoom and position of 'draggable'.
Args:
selection: the rectangle selection widget.
draggable: the parent widget that has position and scale data.
"""
x = selection.x() - draggable.x() x = selection.x() - draggable.x()
y = selection.y() - draggable.y() y = selection.y() - draggable.y()
otherX = x + selection.width() otherX = x + selection.width()
otherY = y + selection.height() otherY = y + selection.height()
x, y, otherX, otherY = [ i / j for i, j in zip([x, y, otherX, otherY], [draggable.scale()] * 4) ] x, y, otherX, otherY = [ i / draggable.scale() for i in [x, y, otherX, otherY] ]
if x == otherX or y == otherY: if x == otherX or y == otherY:
return return
for n in self._graph.nodes: for n in self._graph.nodes:
@ -692,7 +714,7 @@ class UIGraph(QObject):
@Slot() @Slot()
def clearNodeSelection(self): def clearNodeSelection(self):
""" Clear all node selection. """ """ Clear all node selection. """
self.selectedNode = None self._selectedNode = None
self._selectedNodes.clear() self._selectedNodes.clear()
self.selectedNodeChanged.emit() self.selectedNodeChanged.emit()
self.selectedNodesChanged.emit() self.selectedNodesChanged.emit()
@ -718,11 +740,11 @@ class UIGraph(QObject):
lockedChanged = Signal() lockedChanged = Signal()
selectedNodeChanged = Signal() selectedNodeChanged = Signal()
# Currently selected node # Current main selected node
selectedNode = makeProperty(QObject, "_selectedNode", selectedNodeChanged, resetOnDestroy=True) selectedNode = makeProperty(QObject, "_selectedNode", selectedNodeChanged, resetOnDestroy=True)
selectedNodesChanged = Signal() selectedNodesChanged = Signal()
# Currently selected nodes to drag # Currently selected nodes
selectedNodes = makeProperty(QObject, "_selectedNodes", selectedNodesChanged, resetOnDestroy=True) selectedNodes = makeProperty(QObject, "_selectedNodes", selectedNodesChanged, resetOnDestroy=True)
hoveredNodeChanged = Signal() hoveredNodeChanged = Signal()

View file

@ -62,15 +62,15 @@ Item {
} }
/// Duplicate a node and optionnally all the following ones /// Duplicate a node and optionnally all the following ones
function duplicateNode(node, duplicateFollowingNodes) { function duplicateNode(duplicateFollowingNodes) {
if (duplicateFollowingNodes) { if (duplicateFollowingNodes) {
var nodes = uigraph.duplicateNodesFrom(node) var nodes = uigraph.duplicateNodesFrom(uigraph.selectedNodes)
} else { } else {
var nodes = uigraph.duplicateNodes(uigraph.selectedNodes) var nodes = uigraph.duplicateNodes(uigraph.selectedNodes)
} }
uigraph.clearNodeSelection() uigraph.clearNodeSelection()
selectNode(nodes[0]) uigraph.selectedNode = nodes[0]
uigraph.selectFollowing(nodes[0]) uigraph.selectNodes(nodes)
} }
@ -79,11 +79,11 @@ Item {
fit() fit()
if(event.key === Qt.Key_Delete) if(event.key === Qt.Key_Delete)
if(event.modifiers == Qt.AltModifier) if(event.modifiers == Qt.AltModifier)
uigraph.removeNodesFrom(uigraph.selectedNode) uigraph.removeNodesFrom(uigraph.selectedNodes)
else else
uigraph.removeNodes(uigraph.selectedNodes) uigraph.removeNodes(uigraph.selectedNodes)
if(event.key === Qt.Key_D) if(event.key === Qt.Key_D)
duplicateNode(uigraph.selectedNode, event.modifiers == Qt.AltModifier) duplicateNode(event.modifiers == Qt.AltModifier)
} }
MouseArea { MouseArea {
@ -338,14 +338,14 @@ Item {
MenuItem { MenuItem {
text: "Duplicate Node(s)" + (duplicateFollowingButton.hovered ? " From Here" : "") text: "Duplicate Node(s)" + (duplicateFollowingButton.hovered ? " From Here" : "")
enabled: true enabled: true
onTriggered: duplicateNode(nodeMenu.currentNode, false) onTriggered: duplicateNode(false)
MaterialToolButton { MaterialToolButton {
id: duplicateFollowingButton id: duplicateFollowingButton
height: parent.height height: parent.height
anchors { right: parent.right; rightMargin: parent.padding } anchors { right: parent.right; rightMargin: parent.padding }
text: MaterialIcons.fast_forward text: MaterialIcons.fast_forward
onClicked: { onClicked: {
duplicateNode(nodeMenu.currentNode, true); duplicateNode(true);
nodeMenu.close(); nodeMenu.close();
} }
} }
@ -360,7 +360,7 @@ Item {
anchors { right: parent.right; rightMargin: parent.padding } anchors { right: parent.right; rightMargin: parent.padding }
text: MaterialIcons.fast_forward text: MaterialIcons.fast_forward
onClicked: { onClicked: {
uigraph.removeNodesFrom(nodeMenu.currentNode); uigraph.removeNodesFrom(uigraph.selectedNodes);
nodeMenu.close(); nodeMenu.close();
} }
} }
@ -419,7 +419,7 @@ Item {
onAccepted: { onAccepted: {
if(deleteFollowing) if(deleteFollowing)
graph.clearDataFrom(node); uigraph.clearDataFrom(uigraph.selectedNodes);
else else
uigraph.clearData(uigraph.selectedNodes); uigraph.clearData(uigraph.selectedNodes);
} }