diff --git a/beeref/items.py b/beeref/items.py index 9711d5f..ca6f001 100644 --- a/beeref/items.py +++ b/beeref/items.py @@ -369,46 +369,80 @@ class BeePixmapItem(BeeItemMixin, QtWidgets.QGraphicsPixmapItem): self.exit_crop_mode( confirm=self.crop_temp.contains(event.pos())) - def ensure_point_within_pixmap_bounds(self, point): + def ensure_point_within_crop_bounds(self, point, handle): """Returns the point, or the nearest point within the pixmap.""" - point.setX(min(self.pixmap().size().width(), max(0, point.x()))) - point.setY(min(self.pixmap().size().height(), max(0, point.y()))) + + if handle == self.crop_handle_topleft: + topleft = QtCore.QPointF(0, 0) + bottomright = self.crop_temp.bottomRight() + if 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: + topleft = self.crop_temp.topLeft() + bottomright = QtCore.QPointF( + self.pixmap().size().width(), self.pixmap().size().height()) + if 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: + topleft = QtCore.QPointF(0, 0) + bottomright = QtCore.QPointF( + self.pixmap().size().width(), self.crop_temp.bottom()) + if 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: + topleft = QtCore.QPointF(0, 0) + bottomright = QtCore.QPointF( + self.crop_temp.right(), self.pixmap().size().height()) + if handle == self.crop_edge_right: + topleft = QtCore.QPointF(self.crop_temp.left(), 0) + bottomright = QtCore.QPointF( + self.pixmap().size().width(), self.pixmap().size().height()) + + point.setX(min(bottomright.x(), max(topleft.x(), point.x()))) + point.setY(min(bottomright.y(), max(topleft.y(), point.y()))) + return point def mouseMoveEvent(self, event): if self.crop_mode: diff = event.pos() - self.crop_mode_event_start if self.crop_mode_move == self.crop_handle_topleft: - new = self.ensure_point_within_pixmap_bounds( - self.crop_temp.topLeft() + diff) + 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: - new = self.ensure_point_within_pixmap_bounds( - self.crop_temp.bottomLeft() + diff) + 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: - new = self.ensure_point_within_pixmap_bounds( - self.crop_temp.bottomRight() + diff) + 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: - new = self.ensure_point_within_pixmap_bounds( - self.crop_temp.topRight() + diff) + 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: - new = self.ensure_point_within_pixmap_bounds( - self.crop_temp.topLeft() + diff) + 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: - new = self.ensure_point_within_pixmap_bounds( - self.crop_temp.topLeft() + diff) + 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: - new = self.ensure_point_within_pixmap_bounds( - self.crop_temp.bottomLeft() + diff) + 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: - new = self.ensure_point_within_pixmap_bounds( - self.crop_temp.topRight() + diff) + new = self.ensure_point_within_crop_bounds( + self.crop_temp.topRight() + diff, self.crop_mode_move) self.crop_temp.setRight(new.x()) self.update() self.crop_mode_event_start = event.pos() diff --git a/tests/items/test_pixmapitem.py b/tests/items/test_pixmapitem.py index 9237bbc..f5eed4e 100644 --- a/tests/items/test_pixmapitem.py +++ b/tests/items/test_pixmapitem.py @@ -520,17 +520,55 @@ def test_mouse_press_event_crop_mode_outside_handle_outside_crop( event.accept.assert_called_once_with() -@pytest.mark.parametrize('point,expected', - [((45, 56), (45, 56)), - ((0, 0), (0, 0)), - ((-5, -5), (0, 0)), - ((100, 80), (100, 80)), - ((105, 85), (100, 80))]) -def test_ensure_point_within_pixmap_bounds_inside(point, expected, qapp, item): +@pytest.mark.parametrize('point,handle,expected', + [((25, 40), 'crop_handle_topleft', (25, 40)), + ((0, 0), 'crop_handle_topleft', (0, 0)), + ((-5, -5), 'crop_handle_topleft', (0, 0)), + ((40, 60), 'crop_handle_topleft', (40, 60)), + ((100, 80), 'crop_handle_topleft', (40, 60)), + ((25, 40), 'crop_handle_bottomleft', (25, 40)), + ((0, 80), 'crop_handle_bottomleft', (0, 80)), + ((-5, 85), 'crop_handle_bottomleft', (0, 80)), + ((40, 20), 'crop_handle_bottomleft', (40, 20)), + ((45, 15), 'crop_handle_bottomleft', (40, 20)), + ((25, 40), 'crop_handle_bottomright', (25, 40)), + ((10, 20), 'crop_handle_bottomright', (10, 20)), + ((5, 15), 'crop_handle_bottomright', (10, 20)), + ((100, 80), 'crop_handle_bottomright', (100, 80)), + ((105, 85), 'crop_handle_bottomright', (100, 80)), + ((25, 40), 'crop_handle_topright', (25, 40)), + ((10, 0), 'crop_handle_topright', (10, 0)), + ((5, -5), 'crop_handle_topright', (10, 0)), + ((100, 60), 'crop_handle_topright', (100, 60)), + ((105, 65), 'crop_handle_topright', (100, 60)), + ((25, 40), 'crop_edge_top', (25, 40)), + ((0, 0), 'crop_edge_top', (0, 0)), + ((-5, -5), 'crop_edge_top', (0, 0)), + ((100, 60), 'crop_edge_top', (100, 60)), + ((105, 65), 'crop_edge_top', (100, 60)), + ((25, 40), 'crop_edge_bottom', (25, 40)), + ((0, 20), 'crop_edge_bottom', (0, 20)), + ((-5, 15), 'crop_edge_bottom', (0, 20)), + ((100, 80), 'crop_edge_bottom', (100, 80)), + ((105, 85), 'crop_edge_bottom', (100, 80)), + ((25, 40), 'crop_edge_left', (25, 40)), + ((0, 0), 'crop_edge_left', (0, 0)), + ((-5, -5), 'crop_edge_left', (0, 0)), + ((40, 80), 'crop_edge_left', (40, 80)), + ((45, 85), 'crop_edge_left', (40, 80)), + ((25, 40), 'crop_edge_right', (25, 40)), + ((10, 0), 'crop_edge_right', (10, 0)), + ((5, -5), 'crop_edge_right', (10, 0)), + ((100, 80), 'crop_edge_right', (100, 80)), + ((105, 85), 'crop_edge_right', (100, 80))]) +def test_ensure_point_within_crop_bounds( + point, handle, expected, qapp, item): pixmap = MagicMock() pixmap.size.return_value = QtCore.QRectF(0, 0, 100, 80) item.pixmap = MagicMock(return_value=pixmap) - result = item.ensure_point_within_pixmap_bounds(QtCore.QPointF(*point)) + item.crop_temp = QtCore.QRectF(10, 20, 30, 40) + result = item.ensure_point_within_crop_bounds( + QtCore.QPointF(*point), getattr(item, handle)) assert result == QtCore.QPointF(*expected)