mirror of
https://github.com/rbreu/beeref.git
synced 2026-03-11 08:54:28 +00:00
Merge e610333f60 into caed5c3801
This commit is contained in:
commit
66e1ea8331
5 changed files with 88 additions and 23 deletions
|
|
@ -22,6 +22,11 @@ Added
|
|||
further files will be ignored, as previously. If the first argument
|
||||
isn't a bee file, all files will be treated as images and inserted
|
||||
as if opened with "Insert -> Images".
|
||||
* It is now possible to shift around the crop rectagle by clicking and
|
||||
dragging. To confirm the crop, you can now either click outside the
|
||||
crop rectagle or double click inside the crop rectangle. Canceling
|
||||
the crop operation is now only possible by pressing the escape key.
|
||||
(by DarkDefender)
|
||||
|
||||
Fixed
|
||||
-----
|
||||
|
|
|
|||
|
|
@ -520,6 +520,9 @@ class BeePixmapItem(BeeItemMixin, QtWidgets.QGraphicsPixmapItem):
|
|||
if edge().contains(event.pos()):
|
||||
self.set_cursor(self.get_crop_edge_cursor(edge))
|
||||
return
|
||||
if self.crop_temp.contains(event.pos()):
|
||||
self.set_cursor(Qt.CursorShape.SizeAllCursor)
|
||||
return
|
||||
self.unset_cursor()
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
|
|
@ -539,9 +542,42 @@ class BeePixmapItem(BeeItemMixin, QtWidgets.QGraphicsPixmapItem):
|
|||
self.crop_mode_event_start = event.pos()
|
||||
self.crop_mode_move = edge
|
||||
return
|
||||
if self.crop_temp.contains(event.pos()):
|
||||
self.crop_mode_event_start = event.pos()
|
||||
self.crop_mode_move = self.crop_temp
|
||||
return
|
||||
# Click not in handle, end cropping mode:
|
||||
self.exit_crop_mode(
|
||||
confirm=self.crop_temp.contains(event.pos()))
|
||||
self.exit_crop_mode(confirm=True)
|
||||
|
||||
def mouseDoubleClickEvent(self, event):
|
||||
if not self.crop_mode:
|
||||
return super().mouseDoubleClickEvent(event)
|
||||
|
||||
event.accept()
|
||||
if self.crop_temp.contains(event.pos()):
|
||||
self.exit_crop_mode(confirm=True)
|
||||
|
||||
def ensure_crop_box_is_inside(self, point):
|
||||
"""Returns the modified point that ensures that the crop rectangle is
|
||||
still within the pixmap.
|
||||
|
||||
The point passed is assumed to be the top
|
||||
left crop rectangle position.
|
||||
"""
|
||||
|
||||
max_x_pos = self.pixmap().size().width() - self.crop_temp.width()
|
||||
max_y_pos = self.pixmap().size().height() - self.crop_temp.height()
|
||||
|
||||
if point.x() < 0:
|
||||
point.setX(0)
|
||||
elif point.x() > max_x_pos:
|
||||
point.setX(max_x_pos)
|
||||
|
||||
if point.y() < 0:
|
||||
point.setY(0)
|
||||
elif point.y() > max_y_pos:
|
||||
point.setY(max_y_pos)
|
||||
return point
|
||||
|
||||
def ensure_point_within_crop_bounds(self, point, handle):
|
||||
"""Returns the point, or the nearest point within the pixmap."""
|
||||
|
|
@ -549,31 +585,31 @@ class BeePixmapItem(BeeItemMixin, QtWidgets.QGraphicsPixmapItem):
|
|||
if handle == self.crop_handle_topleft:
|
||||
topleft = QtCore.QPointF(0, 0)
|
||||
bottomright = self.crop_temp.bottomRight()
|
||||
if handle == self.crop_handle_bottomleft:
|
||||
elif handle == self.crop_handle_bottomleft:
|
||||
topleft = QtCore.QPointF(0, self.crop_temp.top())
|
||||
bottomright = QtCore.QPointF(
|
||||
self.crop_temp.right(), self.pixmap().size().height())
|
||||
if handle == self.crop_handle_bottomright:
|
||||
elif handle == self.crop_handle_bottomright:
|
||||
topleft = self.crop_temp.topLeft()
|
||||
bottomright = QtCore.QPointF(
|
||||
self.pixmap().size().width(), self.pixmap().size().height())
|
||||
if handle == self.crop_handle_topright:
|
||||
elif handle == self.crop_handle_topright:
|
||||
topleft = QtCore.QPointF(self.crop_temp.left(), 0)
|
||||
bottomright = QtCore.QPointF(
|
||||
self.pixmap().size().width(), self.crop_temp.bottom())
|
||||
if handle == self.crop_edge_top:
|
||||
elif handle == self.crop_edge_top:
|
||||
topleft = QtCore.QPointF(0, 0)
|
||||
bottomright = QtCore.QPointF(
|
||||
self.pixmap().size().width(), self.crop_temp.bottom())
|
||||
if handle == self.crop_edge_bottom:
|
||||
elif handle == self.crop_edge_bottom:
|
||||
topleft = QtCore.QPointF(0, self.crop_temp.top())
|
||||
bottomright = QtCore.QPointF(
|
||||
self.pixmap().size().width(), self.pixmap().size().height())
|
||||
if handle == self.crop_edge_left:
|
||||
elif handle == self.crop_edge_left:
|
||||
topleft = QtCore.QPointF(0, 0)
|
||||
bottomright = QtCore.QPointF(
|
||||
self.crop_temp.right(), self.pixmap().size().height())
|
||||
if handle == self.crop_edge_right:
|
||||
elif handle == self.crop_edge_right:
|
||||
topleft = QtCore.QPointF(self.crop_temp.left(), 0)
|
||||
bottomright = QtCore.QPointF(
|
||||
self.pixmap().size().width(), self.pixmap().size().height())
|
||||
|
|
@ -586,35 +622,39 @@ class BeePixmapItem(BeeItemMixin, QtWidgets.QGraphicsPixmapItem):
|
|||
def mouseMoveEvent(self, event):
|
||||
if self.crop_mode and self.crop_mode_event_start:
|
||||
diff = event.pos() - self.crop_mode_event_start
|
||||
if self.crop_mode_move == self.crop_temp:
|
||||
new = self.ensure_crop_box_is_inside(
|
||||
self.crop_temp.topLeft() + diff)
|
||||
self.crop_temp.moveTo(new)
|
||||
if self.crop_mode_move == self.crop_handle_topleft:
|
||||
new = self.ensure_point_within_crop_bounds(
|
||||
self.crop_temp.topLeft() + diff, self.crop_mode_move)
|
||||
self.crop_temp.setTopLeft(new)
|
||||
if self.crop_mode_move == self.crop_handle_bottomleft:
|
||||
elif self.crop_mode_move == self.crop_handle_bottomleft:
|
||||
new = self.ensure_point_within_crop_bounds(
|
||||
self.crop_temp.bottomLeft() + diff, self.crop_mode_move)
|
||||
self.crop_temp.setBottomLeft(new)
|
||||
if self.crop_mode_move == self.crop_handle_bottomright:
|
||||
elif self.crop_mode_move == self.crop_handle_bottomright:
|
||||
new = self.ensure_point_within_crop_bounds(
|
||||
self.crop_temp.bottomRight() + diff, self.crop_mode_move)
|
||||
self.crop_temp.setBottomRight(new)
|
||||
if self.crop_mode_move == self.crop_handle_topright:
|
||||
elif self.crop_mode_move == self.crop_handle_topright:
|
||||
new = self.ensure_point_within_crop_bounds(
|
||||
self.crop_temp.topRight() + diff, self.crop_mode_move)
|
||||
self.crop_temp.setTopRight(new)
|
||||
if self.crop_mode_move == self.crop_edge_top:
|
||||
elif self.crop_mode_move == self.crop_edge_top:
|
||||
new = self.ensure_point_within_crop_bounds(
|
||||
self.crop_temp.topLeft() + diff, self.crop_mode_move)
|
||||
self.crop_temp.setTop(new.y())
|
||||
if self.crop_mode_move == self.crop_edge_left:
|
||||
elif self.crop_mode_move == self.crop_edge_left:
|
||||
new = self.ensure_point_within_crop_bounds(
|
||||
self.crop_temp.topLeft() + diff, self.crop_mode_move)
|
||||
self.crop_temp.setLeft(new.x())
|
||||
if self.crop_mode_move == self.crop_edge_bottom:
|
||||
elif self.crop_mode_move == self.crop_edge_bottom:
|
||||
new = self.ensure_point_within_crop_bounds(
|
||||
self.crop_temp.bottomLeft() + diff, self.crop_mode_move)
|
||||
self.crop_temp.setBottom(new.y())
|
||||
if self.crop_mode_move == self.crop_edge_right:
|
||||
elif self.crop_mode_move == self.crop_edge_right:
|
||||
new = self.ensure_point_within_crop_bounds(
|
||||
self.crop_temp.topRight() + diff, self.crop_mode_move)
|
||||
self.crop_temp.setRight(new.x())
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
|
|||
def cancel_crop_mode(self):
|
||||
"""Cancels an ongoing crop mode, if there is any."""
|
||||
if self.crop_item:
|
||||
self.crop_item.exit_crop_mode(confirm=False)
|
||||
self.crop_item.exit_crop_mode(confirm=True)
|
||||
|
||||
def copy_selection_to_internal_clipboard(self):
|
||||
self.internal_clipboard = []
|
||||
|
|
@ -394,6 +394,10 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene):
|
|||
super().mousePressEvent(event)
|
||||
|
||||
def mouseDoubleClickEvent(self, event):
|
||||
if self.crop_item:
|
||||
super().mouseDoubleClickEvent(event)
|
||||
return
|
||||
|
||||
self.cancel_active_modes()
|
||||
item = self.itemAt(event.scenePos(), self.views()[0].transform())
|
||||
if item:
|
||||
|
|
|
|||
|
|
@ -734,8 +734,8 @@ def test_mouse_press_event_crop_mode_inside_edge(mouse_mock, qapp, item):
|
|||
event.accept.assert_called_once_with()
|
||||
|
||||
|
||||
@patch('beeref.selection.SelectableMixin.mousePressEvent')
|
||||
def test_mouse_press_event_crop_mode_outside_handle_inside_crop(
|
||||
@patch('PyQt6.QtWidgets.QGraphicsScene.mouseDoubleClickEvent')
|
||||
def test_mouse_doubleclick_event_crop_mode_outside_handle_inside_crop(
|
||||
mouse_mock, qapp, item):
|
||||
item.crop_mode = True
|
||||
item.crop_temp = QtCore.QRectF(0, 0, 100, 80)
|
||||
|
|
@ -744,7 +744,7 @@ def test_mouse_press_event_crop_mode_outside_handle_inside_crop(
|
|||
event = MagicMock()
|
||||
event.pos.return_value = QtCore.QPointF(50, 50)
|
||||
|
||||
item.mousePressEvent(event)
|
||||
item.mouseDoubleClickEvent(event)
|
||||
assert item.crop_mode_move is None
|
||||
item.exit_crop_mode.assert_called_once_with(confirm=True)
|
||||
mouse_mock.assert_not_called()
|
||||
|
|
@ -763,7 +763,7 @@ def test_mouse_press_event_crop_mode_outside_handle_outside_crop(
|
|||
|
||||
item.mousePressEvent(event)
|
||||
assert item.crop_mode_move is None
|
||||
item.exit_crop_mode.assert_called_once_with(confirm=False)
|
||||
item.exit_crop_mode.assert_called_once_with(confirm=True)
|
||||
mouse_mock.assert_not_called()
|
||||
event.accept.assert_called_once_with()
|
||||
|
||||
|
|
@ -820,6 +820,21 @@ def test_ensure_point_within_crop_bounds(
|
|||
assert result == QtCore.QPointF(*expected)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('point,expected',
|
||||
[((-10, -10), (0, 0)),
|
||||
((-10, 80), (0, 40)),
|
||||
((100, 80), (70, 40)),
|
||||
((100, -10), (70, 0))])
|
||||
def test_ensure_crop_box_is_inside(
|
||||
point, expected, qapp, item):
|
||||
pixmap = MagicMock()
|
||||
pixmap.size.return_value = QtCore.QRectF(0, 0, 100, 80)
|
||||
item.pixmap = MagicMock(return_value=pixmap)
|
||||
item.crop_temp = QtCore.QRectF(10, 20, 30, 40)
|
||||
result = item.ensure_crop_box_is_inside(QtCore.QPointF(*point))
|
||||
assert result == QtCore.QPointF(*expected)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'start,pos,handle,expected',
|
||||
[[(10, 10), (5, 5), 'crop_handle_topleft', (5, 15, 35, 45)],
|
||||
|
|
@ -829,7 +844,8 @@ def test_ensure_point_within_crop_bounds(
|
|||
[(25, 10), (20, 5), 'crop_edge_top', (10, 15, 30, 45)],
|
||||
[(10, 40), (5, 35), 'crop_edge_left', (5, 20, 35, 40)],
|
||||
[(35, 25), (30, 20), 'crop_edge_bottom', (10, 20, 30, 35)],
|
||||
[(40, 40), (35, 35), 'crop_edge_right', (10, 20, 25, 40)]])
|
||||
[(40, 40), (35, 35), 'crop_edge_right', (10, 20, 25, 40)],
|
||||
[(15, 30), (5, 10), 'crop_temp', (0, 0, 30, 40)]])
|
||||
@patch('beeref.selection.SelectableMixin.mouseMoveEvent')
|
||||
def test_mouse_move_when_crop_mode_inside_handle(
|
||||
mouse_mock, start, pos, handle, expected, qapp, item):
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ def test_cancel_crop_mode_when_crop(view, item):
|
|||
view.scene.crop_item = item
|
||||
item.exit_crop_mode = MagicMock()
|
||||
view.scene.cancel_crop_mode()
|
||||
item.exit_crop_mode.assert_called_once_with(confirm=False)
|
||||
item.exit_crop_mode.assert_called_once_with(confirm=True)
|
||||
|
||||
|
||||
def test_cancel_crop_mode_when_no_crop(view, item):
|
||||
|
|
|
|||
Loading…
Reference in a new issue