Fixed dragging corners

This commit is contained in:
2026-07-20 13:24:37 +02:00
parent 4ab74f64e2
commit 6a3e9f01d0
4 changed files with 271 additions and 58 deletions

View File

@@ -217,9 +217,6 @@ class InterfaceTerminalItem(QGraphicsObject):
super().mousePressEvent(event)
def contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent) -> None: # noqa: N802
if self.index < 0:
event.accept()
return
menu = QMenu()
options_action = menu.addAction(f"{self.direction.title()} Options…")
if menu.exec(event.screenPos()) is options_action:
@@ -362,6 +359,20 @@ class WaypointHandle(QGraphicsEllipseItem):
| QGraphicsItem.GraphicsItemFlag.ItemSendsGeometryChanges
)
self.setCursor(Qt.CursorShape.SizeAllCursor)
self.drag_offset = QPointF()
def mousePressEvent(self, event: QGraphicsSceneMouseEvent) -> None: # noqa: N802
scene = self.scene()
if isinstance(scene, GraphScene):
owner = scene.route_graphics_item(self.item_kind, self.item_id)
if owner is not None:
owner.setSelected(True)
self.drag_offset = self.pos() - event.scenePos()
event.accept()
def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent) -> None: # noqa: N802
self.setPos(_snapped(event.scenePos() + self.drag_offset))
event.accept()
def itemChange(self, change, value): # noqa: N802
if change == QGraphicsItem.GraphicsItemChange.ItemPositionChange:
@@ -373,12 +384,15 @@ class WaypointHandle(QGraphicsEllipseItem):
return super().itemChange(change, value)
def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None: # noqa: N802
super().mouseReleaseEvent(event)
scene = self.scene()
if isinstance(scene, GraphScene):
scene.move_route_node(self.item_kind, self.item_id, self.index, self.pos())
event.accept()
def contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent) -> None: # noqa: N802
if self.index < 0:
event.accept()
return
menu = QMenu()
delete_action = menu.addAction("Delete Node")
if menu.exec(event.screenPos()) is delete_action:
@@ -388,6 +402,40 @@ class WaypointHandle(QGraphicsEllipseItem):
event.accept()
class AnnotationResizeHandle(QGraphicsEllipseItem):
CURSORS = {
"n": Qt.CursorShape.SizeVerCursor,
"s": Qt.CursorShape.SizeVerCursor,
"e": Qt.CursorShape.SizeHorCursor,
"w": Qt.CursorShape.SizeHorCursor,
"nw": Qt.CursorShape.SizeFDiagCursor,
"se": Qt.CursorShape.SizeFDiagCursor,
"ne": Qt.CursorShape.SizeBDiagCursor,
"sw": Qt.CursorShape.SizeBDiagCursor,
}
def __init__(self, owner: "AnnotationGraphicsItem", role: str) -> None:
super().__init__(-4, -4, 8, 8, owner)
self.owner, self.role = owner, role
self.setBrush(QColor("#ffffff"))
self.setPen(QPen(QColor("#2563eb"), 1.5))
self.setCursor(self.CURSORS[role])
self.setZValue(1000)
def mousePressEvent(self, event: QGraphicsSceneMouseEvent) -> None: # noqa: N802
self.owner.setSelected(True)
self.owner.begin_resize()
event.accept()
def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent) -> None: # noqa: N802
self.owner.resize_from_handle(self.role, event.scenePos())
event.accept()
def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None: # noqa: N802
self.owner.finish_resize()
event.accept()
class AnnotationGraphicsItem(QGraphicsObject):
def __init__(self, annotation: Annotation, controller: DocumentController) -> None:
super().__init__()
@@ -395,6 +443,7 @@ class AnnotationGraphicsItem(QGraphicsObject):
self.annotation_id = annotation.id
self.controller = controller
self.drag_start = QPointF(annotation.x, annotation.y)
self.resize_start: dict[str, float] | None = None
self.setPos(annotation.x, annotation.y)
self.setZValue(annotation.layer)
self.setFlags(
@@ -402,6 +451,15 @@ class AnnotationGraphicsItem(QGraphicsObject):
| QGraphicsItem.GraphicsItemFlag.ItemIsSelectable
| QGraphicsItem.GraphicsItemFlag.ItemSendsGeometryChanges
)
roles = ("n", "ne", "e", "se", "s", "sw", "w", "nw")
self.resize_handles = (
[AnnotationResizeHandle(self, role) for role in roles]
if annotation.kind == "box"
else []
)
self._position_resize_handles()
for handle in self.resize_handles:
handle.hide()
def boundingRect(self) -> QRectF: # noqa: N802
width, height = self.annotation.width, self.annotation.height
@@ -457,8 +515,75 @@ class AnnotationGraphicsItem(QGraphicsObject):
def itemChange(self, change, value): # noqa: N802
if change == QGraphicsItem.GraphicsItemChange.ItemPositionChange:
return _snapped(value)
if change == QGraphicsItem.GraphicsItemChange.ItemSelectedHasChanged:
for handle in self.resize_handles:
handle.setVisible(bool(value))
return super().itemChange(change, value)
def _position_resize_handles(self) -> None:
width, height = self.annotation.width, self.annotation.height
positions = {
"n": QPointF(width / 2, 0),
"ne": QPointF(width, 0),
"e": QPointF(width, height / 2),
"se": QPointF(width, height),
"s": QPointF(width / 2, height),
"sw": QPointF(0, height),
"w": QPointF(0, height / 2),
"nw": QPointF(0, 0),
}
for handle in self.resize_handles:
handle.setPos(positions[handle.role])
def begin_resize(self) -> None:
self.resize_start = {
"x": self.annotation.x,
"y": self.annotation.y,
"width": self.annotation.width,
"height": self.annotation.height,
}
def resize_from_handle(self, role: str, scene_position: QPointF) -> None:
if self.resize_start is None:
self.begin_resize()
start = self.resize_start
edges = {
"left": start["x"],
"top": start["y"],
"right": start["x"] + start["width"],
"bottom": start["y"] + start["height"],
}
point = _snapped(scene_position)
minimum = _graph_snap_size()
if "w" in role:
edges["left"] = min(point.x(), edges["right"] - minimum)
if "e" in role:
edges["right"] = max(point.x(), edges["left"] + minimum)
if "n" in role:
edges["top"] = min(point.y(), edges["bottom"] - minimum)
if "s" in role:
edges["bottom"] = max(point.y(), edges["top"] + minimum)
self.prepareGeometryChange()
self.annotation.x, self.annotation.y = edges["left"], edges["top"]
self.annotation.width = edges["right"] - edges["left"]
self.annotation.height = edges["bottom"] - edges["top"]
self.setPos(self.annotation.x, self.annotation.y)
self._position_resize_handles()
self.update()
def finish_resize(self) -> None:
if self.resize_start is None:
return
old = self.resize_start
new = {
"x": self.annotation.x,
"y": self.annotation.y,
"width": self.annotation.width,
"height": self.annotation.height,
}
self.resize_start = None
self.controller.set_annotation_geometry(self.annotation_id, old, new)
def contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent) -> None: # noqa: N802
scene = self.scene()
if not isinstance(scene, GraphScene):
@@ -655,6 +780,7 @@ class GraphScene(QGraphicsScene):
elif isinstance(graphics, AnnotationGraphicsItem):
graphics.prepareGeometryChange()
graphics.setPos(annotation.x, annotation.y)
graphics._position_resize_handles()
graphics.update()
def edit_annotation_options(self, annotation_id: str) -> None:
@@ -951,6 +1077,13 @@ class GraphScene(QGraphicsScene):
]
return item, points
def route_graphics_item(self, item_kind: str, item_id: str) -> QGraphicsItem | None:
return (
self.connection_items.get(item_id)
if item_kind == "connection"
else self.annotation_items.get(item_id)
)
def add_route_node(self, item_kind: str, item_id: str, position: QPointF) -> None:
item, points = self._route_values(item_kind, item_id)
if item is None:
@@ -986,6 +1119,8 @@ class GraphScene(QGraphicsScene):
)
points.insert(insertion, snapped)
routing = "angled" if item.properties.get("routing") == "direct" else None
if routing == "angled" or item.properties.get("routing") == "angled":
points = self._orthogonal_points(start, end, points)[1:-1]
self.controller.set_route_waypoints(item_kind, item_id, points, routing)
def move_route_node(self, item_kind: str, item_id: str, index: int, position: QPointF) -> None:
@@ -1012,6 +1147,14 @@ class GraphScene(QGraphicsScene):
return
if index < len(points):
points[index] = position
if item.properties.get("routing") == "angled":
if item_kind == "annotation":
start = QPointF(item.x, item.y)
end = QPointF(item.x + item.width, item.y + item.height)
else:
graphics = self.connection_items[item_id]
start, end = graphics.start, graphics.end
points = self._orthogonal_points(start, end, points)[1:-1]
self.controller.set_route_waypoints(item_kind, item_id, points)
def preview_route_node(