Add flow view reordering via connection breaking

When a connection is broken in the flow view:
1. The node that lost its input moves to the end of the chain
2. The remaining nodes reconnect automatically
3. Tool model updates to reflect new order
4. Dependency validation warns about broken references

Flow: Input → A → B → C → Output
Break B's input: Input → A → C → B → Output

Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
rob
2026-01-15 23:50:18 -04:00
co-authored by Claude Opus 4.5
parent 260ebf1b2f
commit d1f0c2f893
2 changed files with 84 additions and 0 deletions
+54
View File
@@ -125,6 +125,9 @@ class FlowGraphWidget(QWidget):
# Emitted when steps are deleted from flow view (list of step indices)
steps_deleted = Signal(list)
# Emitted when steps are reordered (new order as list of step indices)
steps_reordered = Signal(list)
def __init__(self, parent=None):
super().__init__(parent)
self._tool: Optional[Tool] = None
@@ -153,6 +156,7 @@ class FlowGraphWidget(QWidget):
# Connect signals
self._graph.node_double_clicked.connect(self._on_node_double_clicked)
self._graph.nodes_deleted.connect(self._on_nodes_deleted)
self._graph.port_disconnected.connect(self._on_port_disconnected)
# Add graph widget
layout.addWidget(self._graph.widget, 1)
@@ -427,6 +431,56 @@ class FlowGraphWidget(QWidget):
self.steps_deleted.emit(deleted_indices)
self.flow_changed.emit()
def _on_port_disconnected(self, input_port, output_port):
"""Handle port disconnection - reorder steps.
When a connection is broken:
1. The node that lost its input moves to the end
2. The chain reconnects (previous node → next node)
3. Disconnected node attaches at the end before Output
"""
if not self._tool or not self._tool.steps:
return
# Get the nodes involved
# input_port belongs to the node that lost its input
# output_port belongs to the node that was providing the input
disconnected_node = input_port.node()
source_node = output_port.node()
# Only handle step nodes (not Input/Output)
if not hasattr(disconnected_node, '_step_index') or disconnected_node._step_index < 0:
return
disconnected_idx = disconnected_node._step_index
# Find the node that the disconnected node was connected to (its output target)
next_node = None
if disconnected_node.output_ports():
out_port = disconnected_node.output_ports()[0]
connected_ports = out_port.connected_ports()
if connected_ports:
next_node = connected_ports[0].node()
# Build new order: move disconnected step to end
old_order = list(range(len(self._tool.steps)))
old_order.remove(disconnected_idx)
old_order.append(disconnected_idx)
# Reconnect the graph visually
# Connect source to next (if next is a step node)
if next_node and hasattr(next_node, '_step_index') and next_node._step_index >= 0:
if source_node.output_ports() and next_node.input_ports():
source_node.output_ports()[0].connect_to(next_node.input_ports()[0])
# Connect the last step (before disconnected) to disconnected node
# and disconnected node to output
# This will be handled by the rebuild after reorder
# Emit the new order
self.steps_reordered.emit(old_order)
self.flow_changed.emit()
def refresh(self):
"""Refresh the graph from current tool data."""
self._rebuild_graph()