Fix scale handle cursor shapes for rotated items

This commit is contained in:
Rebecca Breu 2021-04-06 11:23:42 +02:00
parent a34231cd7d
commit 829d0d19fc
3 changed files with 59 additions and 39 deletions

View file

@ -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

View file

@ -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):

View file

@ -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))