From 829d0d19fce72f80a1d21cd5c46e7953ead039c3 Mon Sep 17 00:00:00 2001 From: Rebecca Breu Date: Tue, 6 Apr 2021 11:23:42 +0200 Subject: [PATCH] Fix scale handle cursor shapes for rotated items --- beeref/selection.py | 43 +++++++++++++++++---------------- beeref/view.py | 2 +- tests/test_selection.py | 53 +++++++++++++++++++++++++++-------------- 3 files changed, 59 insertions(+), 39 deletions(-) diff --git a/beeref/selection.py b/beeref/selection.py index 20bc1e2..9d58bcc 100644 --- a/beeref/selection.py +++ b/beeref/selection.py @@ -229,15 +229,20 @@ class SelectableMixin(BaseItemMixin): return for corner in self.corners: - # See if we need to change the cursor for scale areas + # See if we need to change the cursor for interactable areas if self.get_scale_bounds(corner).contains(event.pos()): - direction = self.get_corner_direction(corner) - if direction.x() == direction.y(): + self.event_anchor = self.center_scene_coords + angle = self.get_rotate_angle(self.mapToScene(corner)) + if abs(angle) >= 157.5 or abs(angle) <= 22.5: + self.setCursor(Qt.CursorShape.SizeVerCursor) + elif 112.5 <= angle <= 157.5 or -67.5 <= angle <= -22.5: self.setCursor(Qt.CursorShape.SizeFDiagCursor) + elif 67.5 <= abs(angle) <= 112.5: + self.setCursor(Qt.CursorShape.SizeHorCursor) else: self.setCursor(Qt.CursorShape.SizeBDiagCursor) return - if self.get_rotate_bounds(corner).contains(event.pos()): + elif self.get_rotate_bounds(corner).contains(event.pos()): self.setCursor(BeeAssets().cursor_rotate) return @@ -257,11 +262,10 @@ class SelectableMixin(BaseItemMixin): # Start scale action for this corner self.scale_active = True self.event_start = event.scenePos() - self.event_direction = self.get_mouse_event_direction( - event) + self.event_direction = self.get_direction_from_center( + event.scenePos()) self.event_anchor = self.mapToScene( self.get_scale_anchor(corner)) - self.scale_direction = self.get_corner_direction(corner) for item in self.selection_action_items(): item.scale_orig_factor = item.scale() event.accept() @@ -271,7 +275,8 @@ class SelectableMixin(BaseItemMixin): # Start rotate action self.rotate_active = True self.event_anchor = self.center_scene_coords - self.rotate_start_angle = self.get_rotate_angle(event) + self.rotate_start_angle = self.get_rotate_angle( + event.scenePos()) for item in self.selection_action_items(): item.rotate_orig_degrees = item.rotation() event.accept() @@ -298,23 +303,21 @@ class SelectableMixin(BaseItemMixin): return QtCore.QPointF(1 if corner.x() > 0 else -1, 1 if corner.y() > 0 else -1) - def get_mouse_event_direction(self, event): - """The direction of a mouse event in relation to the item's center. - """ - diff = event.scenePos() - self.center_scene_coords + def get_direction_from_center(self, pos): + """The direction of a point in relation to the item's center.""" + diff = pos - self.center_scene_coords length = math.sqrt(QtCore.QPointF.dotProduct(diff, diff)) return diff / length - def get_rotate_angle(self, event): - """Get the angle of the current mouse position towards the - scale center.""" + def get_rotate_angle(self, pos): + """Get the angle of the given position towards the event anchor.""" - diff = event.scenePos() - self.event_anchor + diff = pos - self.event_anchor return -math.degrees(math.atan2(diff.x(), diff.y())) - def get_rotate_delta(self, event): + def get_rotate_delta(self, pos): """Get the rotate delta for the current mouse movement""" - return self.get_rotate_angle(event) - self.rotate_start_angle + return self.get_rotate_angle(pos) - self.rotate_start_angle def mouseMoveEvent(self, event): if self.scale_active: @@ -324,7 +327,7 @@ class SelectableMixin(BaseItemMixin): item.mapFromScene(self.event_anchor)) event.accept() elif self.rotate_active: - delta = self.get_rotate_delta(event) + delta = self.get_rotate_delta(event.scenePos()) for item in self.selection_action_items(): item.setRotation(item.rotate_orig_degrees + delta, item.mapFromScene(self.event_anchor)) @@ -346,7 +349,7 @@ class SelectableMixin(BaseItemMixin): self.scene().undo_stack.push( commands.RotateItemsBy( self.selection_action_items(), - self.get_rotate_delta(event), + self.get_rotate_delta(event.scenePos()), self.event_anchor, ignore_first_redo=True)) self.rotate_active = False diff --git a/beeref/view.py b/beeref/view.py index cdee5cc..2f6e0b4 100644 --- a/beeref/view.py +++ b/beeref/view.py @@ -352,7 +352,7 @@ class BeeGraphicsView(QtWidgets.QGraphicsView): self.scene.clearSelection() item = BeePixmapItem(img) pos = self.mapToScene(self.mapFromGlobal(self.cursor().pos())) - item.set_pos_center(pos.x(), pos.y()) + item.set_pos_center(pos) self.undo_stack.push(commands.InsertItems(self.scene, [item])) def on_selection_changed(self): diff --git a/tests/test_selection.py b/tests/test_selection.py index 70e2a50..836f51b 100644 --- a/tests/test_selection.py +++ b/tests/test_selection.py @@ -398,37 +398,30 @@ class SelectableMixinScalingTestCase(SelectableMixinBaseTestCase): assert self.item.get_corner_direction( QtCore.QPointF(0, 80)) == QtCore.QPointF(-1, 1) - def test_get_mouse_event_direction_bottomright(self): - event = MagicMock() - event.scenePos = MagicMock(return_value=QtCore.QPointF(100, 90)) - direction = self.item.get_mouse_event_direction(event) + def test_get_direction_from_center_bottomright(self): + direction = self.item.get_direction_from_center( + QtCore.QPointF(100, 90)) assert direction == approx(QtCore.QPointF(1, 1) / math.sqrt(2)) - def test_get_mouse_event_direction_topleft(self): - event = MagicMock() - event.scenePos = MagicMock(return_value=QtCore.QPointF(0, -10)) - direction = self.item.get_mouse_event_direction(event) + def test_get_direction_from_center_topleft(self): + direction = self.item.get_direction_from_center( + QtCore.QPointF(0, -10)) assert direction == approx(QtCore.QPointF(-1, -1) / math.sqrt(2)) - def test_get_mouse_event_direction_bottomright_when_rotated_180(self): + def test_get_direction_from_center_bottomright_when_rotated_180(self): self.item.setRotation(180, QtCore.QPointF(50, 40)) - event = MagicMock() - event.scenePos = MagicMock(return_value=QtCore.QPointF(100, 90)) - direction = self.item.get_mouse_event_direction(event) + direction = self.item.get_direction_from_center( + QtCore.QPointF(100, 90)) assert direction == approx(QtCore.QPointF(1, 1) / math.sqrt(2)) def test_get_rotate_angle(self): self.item.event_anchor = QtCore.QPointF(10, 20) - event = MagicMock() - event.scenePos = MagicMock(return_value=QtCore.QPointF(15, 25)) - assert self.item.get_rotate_angle(event) == -45 + assert self.item.get_rotate_angle(QtCore.QPointF(15, 25)) == -45 def test_get_rotate_delta(self): self.item.event_anchor = QtCore.QPointF(10, 20) self.item.rotate_start_angle = -3 - event = MagicMock() - event.scenePos = MagicMock(return_value=QtCore.QPointF(15, 25)) - assert self.item.get_rotate_delta(event) == -42 + assert self.item.get_rotate_delta(QtCore.QPointF(15, 25)) == -42 class SelectableMixinMouseEventsTestCase(SelectableMixinBaseTestCase): @@ -466,6 +459,30 @@ class SelectableMixinMouseEventsTestCase(SelectableMixinBaseTestCase): self.item.setCursor.assert_called_once_with( Qt.CursorShape.SizeBDiagCursor) + def test_hover_move_event_topright_scale_rotated_90(self): + self.item.setRotation(90) + self.item.setSelected(True) + self.event.pos = MagicMock(return_value=QtCore.QPointF(0, 0)) + self.item.hoverMoveEvent(self.event) + self.item.setCursor.assert_called_once_with( + Qt.CursorShape.SizeBDiagCursor) + + def test_hover_move_event_top_scale_rotated_45(self): + self.item.setRotation(45) + self.item.setSelected(True) + self.event.pos = MagicMock(return_value=QtCore.QPointF(0, 0)) + self.item.hoverMoveEvent(self.event) + self.item.setCursor.assert_called_once_with( + Qt.CursorShape.SizeVerCursor) + + def test_hover_move_event_left_scale_rotated_45(self): + self.item.setRotation(45) + self.item.setSelected(True) + self.event.pos = MagicMock(return_value=QtCore.QPointF(0, 80)) + self.item.hoverMoveEvent(self.event) + self.item.setCursor.assert_called_once_with( + Qt.CursorShape.SizeHorCursor) + def test_hover_move_event_rotate(self): self.item.setSelected(True) self.event.pos = MagicMock(return_value=QtCore.QPointF(110, 90))