diff --git a/beeref/documentation/controls.html b/beeref/documentation/controls.html index 807646b..c8473f9 100644 --- a/beeref/documentation/controls.html +++ b/beeref/documentation/controls.html @@ -38,3 +38,9 @@

Shift or Ctrl while rotating

+ +

Move Window

+ +

+ Ctrl + Alt + Left Cick + Drag +

diff --git a/beeref/view.py b/beeref/view.py index b1bb906..8c1d5fc 100644 --- a/beeref/view.py +++ b/beeref/view.py @@ -57,6 +57,7 @@ class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin): self.previous_transform = None self.pan_active = False self.zoom_active = False + self.movewin_active = False self.scene.changed.connect(self.on_scene_changed) self.scene.selectionChanged.connect(self.on_selection_changed) @@ -562,6 +563,14 @@ class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin): event.accept() return + if (event.button() == Qt.MouseButton.LeftButton + and event.modifiers() == (Qt.KeyboardModifier.ControlModifier + | Qt.KeyboardModifier.AltModifier)): + self.movewin_active = True + self.event_start = self.mapToGlobal(event.position()) + event.accept() + return + super().mousePressEvent(event) def mouseMoveEvent(self, event): @@ -582,6 +591,15 @@ class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin): event.accept() return + if self.movewin_active: + pos = self.mapToGlobal(event.position()) + delta = pos - self.event_start + self.event_start = pos + self.parent.move(self.parent.x() + int(delta.x()), + self.parent.y() + int(delta.y())) + event.accept() + return + super().mouseMoveEvent(event) def mouseReleaseEvent(self, event): @@ -594,6 +612,10 @@ class BeeGraphicsView(QtWidgets.QGraphicsView, ActionsMixin): self.zoom_active = False event.accept() return + if self.movewin_active: + self.movewin_active = False + event.accept() + return super().mouseReleaseEvent(event) diff --git a/tests/test_view.py b/tests/test_view.py index e9d5939..6f5dbd4 100644 --- a/tests/test_view.py +++ b/tests/test_view.py @@ -526,6 +526,7 @@ def test_mouse_press_zoom(mouse_event_mock, view): view.mousePressEvent(event) assert view.zoom_active is True assert view.pan_active is False + assert view.movewin_active is False assert view.event_start == QtCore.QPointF(10, 20) assert view.event_anchor == QtCore.QPointF(10, 20) mouse_event_mock.assert_not_called() @@ -541,6 +542,7 @@ def test_mouse_press_pan_middle_drag(mouse_event_mock, view): view.mousePressEvent(event) assert view.pan_active is True assert view.zoom_active is False + assert view.movewin_active is False assert view.event_start == QtCore.QPointF(10, 20) mouse_event_mock.assert_not_called() view.cursor() == Qt.CursorShape.ClosedHandCursor @@ -556,12 +558,29 @@ def test_mouse_press_pan_alt_left_drag(mouse_event_mock, view): view.mousePressEvent(event) assert view.pan_active is True assert view.zoom_active is False + assert view.movewin_active is False assert view.event_start == QtCore.QPointF(10, 20) mouse_event_mock.assert_not_called() view.cursor() == Qt.CursorShape.ClosedHandCursor event.accept.assert_called_once_with() +@patch('PyQt6.QtWidgets.QGraphicsView.mousePressEvent') +def test_mouse_press_move_window(mouse_event_mock, view): + event = MagicMock() + event.position.return_value = QtCore.QPointF(10, 20) + event.button.return_value = Qt.MouseButton.LeftButton + event.modifiers.return_value = ( + Qt.KeyboardModifier.AltModifier | Qt.KeyboardModifier.ControlModifier) + view.mousePressEvent(event) + assert view.pan_active is False + assert view.zoom_active is False + assert view.movewin_active is True + assert view.event_start == view.mapToGlobal(QtCore.QPointF(10, 20)) + mouse_event_mock.assert_not_called() + event.accept.assert_called_once_with() + + @patch('PyQt6.QtWidgets.QGraphicsView.mousePressEvent') def test_mouse_press_unhandled(mouse_event_mock, view): event = MagicMock() @@ -570,6 +589,7 @@ def test_mouse_press_unhandled(mouse_event_mock, view): view.mousePressEvent(event) assert view.pan_active is False assert view.zoom_active is False + assert view.movewin_active is False mouse_event_mock.assert_called_once_with(event) event.accept.assert_not_called() @@ -601,6 +621,19 @@ def test_mouse_move_zoom(zoom_mock, mouse_event_mock, view): event.accept.assert_called_once_with() +@patch('PyQt6.QtWidgets.QGraphicsView.mouseMoveEvent') +@patch('PyQt6.QtWidgets.QWidget.move') +def test_mouse_move_movewin(move_mock, mouse_event_mock, view): + view.movewin_active = True + view.event_start = QtCore.QPointF(10, 20) + event = MagicMock() + event.position.return_value = QtCore.QPointF(15, 18) + view.mouseMoveEvent(event) + move_mock.assert_called_once_with(5, -2) + mouse_event_mock.assert_not_called() + event.accept.assert_called_once_with() + + @patch('PyQt6.QtWidgets.QGraphicsView.mouseMoveEvent') def test_mouse_move_unhandled(mouse_event_mock, view): event = MagicMock() @@ -632,6 +665,16 @@ def test_mouse_release_zoom(mouse_event_mock, view): event.accept.assert_called_once_with() +@patch('PyQt6.QtWidgets.QGraphicsView.mouseReleaseEvent') +def test_mouse_release_movewin(mouse_event_mock, view): + event = MagicMock() + view.movewin_active = True + view.mouseReleaseEvent(event) + mouse_event_mock.assert_not_called() + assert view.movewin_active is False + event.accept.assert_called_once_with() + + @patch('PyQt6.QtWidgets.QGraphicsView.mouseReleaseEvent') def test_mouse_release_unhandled(mouse_event_mock, view): event = MagicMock()