Add action "Move window"

This commit is contained in:
Rebecca Breu 2023-11-26 16:43:02 +01:00
parent 55c0e91fb6
commit 723bb5819d
7 changed files with 122 additions and 15 deletions

View file

@ -10,18 +10,20 @@ Added
behaviour can be changed to always use PNG (the former behaviour) or
always JPG. To apply this behaviour to already saved images in
existing bee files, you can save them as new files.
* Enable antialiasing/smoothing. Images that are being displayed at a
* Enabled antialiasing/smoothing. Images that are being displayed at a
large zoom factor are exempt to make sure that icons, pixel sprites
etc can be viewed correctly.
* A scene can now be exported to a single image (File -> Export Scene...)
* Editing of text items will now be undoable after leaving edit mode
* Empty text items will be deleted after leaving edit mode
* Text edit mode can now be aborted with Escape
* Alternative way to move the BeeRef window without the title bar:
View -> Move Window (or press "M")
Changed
-------
* Editing of text items will now be undoable after leaving edit mode
* Empty text items will be deleted after leaving edit mode
* Text edit mode can now be aborted with Escape
* "Save as" will now open pre-select the folder of the currently opened file
* "Save" and "Save as" are now inactive when the scene is empty

View file

@ -274,6 +274,12 @@ actions = [
'checked': True,
'callback': 'on_action_show_titlebar',
},
{
'id': 'move_window',
'text': 'Move &Window',
'shortcuts': ['Ctrl+M'],
'callback': 'on_action_move_window',
},
{
'id': 'fullscreen',
'text': '&Fullscreen',

View file

@ -62,6 +62,8 @@ menu_structure = [
'show_scrollbars',
'show_menubar',
'show_titlebar',
MENU_SEPARATOR,
'move_window',
],
},
{

View file

@ -31,6 +31,7 @@ class MainControlsMixin:
* Right-click menu
* Dropping files
* Moving the window without title bar
"""
def init_main_controls(self, main_window):
@ -42,6 +43,21 @@ class MainControlsMixin:
self.setAcceptDrops(True)
self.movewin_active = False
def enter_movewin_mode(self):
logger.debug('Entering movewin mode')
self.setMouseTracking(True)
self.movewin_active = True
self.event_start = QtCore.QPointF(self.cursor().pos())
if hasattr(self, 'disable_mouse_events'):
self.disable_mouse_events()
def exit_movewin_mode(self):
logger.debug('Exiting movewin mode')
self.setMouseTracking(False)
self.movewin_active = False
if hasattr(self, 'enable_mouse_events'):
self.enable_mouse_events()
def dragEnterEvent(self, event):
mimedata = event.mimeData()
logger.debug(f'Drag enter event: {mimedata.formats()}')
@ -80,11 +96,14 @@ class MainControlsMixin:
logger.info('Drop not an image')
def mousePressEventMainControls(self, event):
if self.movewin_active:
self.exit_movewin_mode()
event.accept()
return True
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())
self.enter_movewin_mode()
event.accept()
return True
@ -100,6 +119,12 @@ class MainControlsMixin:
def mouseReleaseEventMainControls(self, event):
if self.movewin_active:
self.movewin_active = False
self.exit_movewin_mode()
event.accept()
return True
def keyPressEventMainControls(self, event):
if self.movewin_active:
self.exit_movewin_mode()
event.accept()
return True

View file

@ -105,9 +105,13 @@ class BeeGraphicsView(MainControlsMixin,
if not self.scene.items():
logger.debug('No items in scene')
self.setTransform(QtGui.QTransform())
self.welcome_overlay.setFocus()
self.clearFocus()
self.welcome_overlay.show()
self.actiongroup_set_enabled('active_when_items_in_scene', False)
else:
self.setFocus()
self.welcome_overlay.clearFocus()
self.welcome_overlay.hide()
self.actiongroup_set_enabled('active_when_items_in_scene', True)
self.recalc_scene_rect()
@ -216,6 +220,12 @@ class BeeGraphicsView(MainControlsMixin,
self.parent.create()
self.parent.show()
def on_action_move_window(self):
if self.welcome_overlay.isHidden():
self.enter_movewin_mode()
else:
self.welcome_overlay.enter_movewin_mode()
def on_action_undo(self):
logger.debug('Undo: %s' % self.undo_stack.undoText())
self.scene.cancel_crop_mode()
@ -662,6 +672,9 @@ class BeeGraphicsView(MainControlsMixin,
event.accept()
def mousePressEvent(self, event):
if self.mousePressEventMainControls(event):
return
if (event.button() == Qt.MouseButton.MiddleButton
and event.modifiers() == Qt.KeyboardModifier.ControlModifier):
self.zoom_active = True
@ -679,8 +692,6 @@ class BeeGraphicsView(MainControlsMixin,
event.accept()
return
if self.mousePressEventMainControls(event):
return
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
@ -723,3 +734,8 @@ class BeeGraphicsView(MainControlsMixin,
super().resizeEvent(event)
self.recalc_scene_rect()
self.welcome_overlay.resize(self.size())
def keyPressEvent(self, event):
if self.keyPressEventMainControls(event):
return
super().keyPressEvent(event)

View file

@ -107,12 +107,12 @@ class WelcomeOverlay(MainControlsMixin, QtWidgets.QWidget):
self.files_layout.addStretch(50)
# Help text
label = QtWidgets.QLabel(self.txt, self)
label.setAlignment(Qt.AlignmentFlag.AlignVCenter
| Qt.AlignmentFlag.AlignCenter)
self.label = QtWidgets.QLabel(self.txt, self)
self.label.setAlignment(Qt.AlignmentFlag.AlignVCenter
| Qt.AlignmentFlag.AlignCenter)
self.layout = QtWidgets.QHBoxLayout()
self.layout.addStretch(50)
self.layout.addWidget(label)
self.layout.addWidget(self.label)
self.layout.addStretch(50)
self.setLayout(self.layout)
@ -123,6 +123,20 @@ class WelcomeOverlay(MainControlsMixin, QtWidgets.QWidget):
self.layout.insertLayout(0, self.files_layout)
super().show()
def disable_mouse_events(self):
self.files_view.setAttribute(
Qt.WidgetAttribute.WA_TransparentForMouseEvents)
self.label.setAttribute(
Qt.WidgetAttribute.WA_TransparentForMouseEvents)
def enable_mouse_events(self):
self.files_view.setAttribute(
Qt.WidgetAttribute.WA_TransparentForMouseEvents,
on=False)
self.label.setAttribute(
Qt.WidgetAttribute.WA_TransparentForMouseEvents,
on=False)
def mousePressEvent(self, event):
if self.mousePressEventMainControls(event):
return
@ -138,6 +152,11 @@ class WelcomeOverlay(MainControlsMixin, QtWidgets.QWidget):
return
super().mouseReleaseEvent(event)
def keyPressEvent(self, event):
if self.keyPressEventMainControls(event):
return
super().keyPressEvent(event)
class BeeProgressDialog(QtWidgets.QProgressDialog):

View file

@ -598,6 +598,25 @@ def test_on_action_show_titlebar_unchecked(
create_mock.assert_called_once()
@patch('beeref.widgets.WelcomeOverlay.cursor')
def test_on_action_move_window_when_welcome_overlay(cursor_mock, view):
cursor_mock.return_value = MagicMock(
pos=MagicMock(return_value=QtCore.QPointF(10.0, 20.0)))
view.on_action_move_window()
assert view.welcome_overlay.movewin_active is True
assert view.welcome_overlay.event_start == QtCore.QPointF(10.0, 20.0)
@patch('beeref.view.BeeGraphicsView.cursor')
def test_on_action_move_window_when_scene(cursor_mock, view):
cursor_mock.return_value = MagicMock(
pos=MagicMock(return_value=QtCore.QPointF(10.0, 20.0)))
view.welcome_overlay.hide()
view.on_action_move_window()
assert view.movewin_active is True
assert view.event_start == QtCore.QPointF(10.0, 20.0)
def test_on_action_delete_items(view, item):
view.scene.cancel_crop_mode = MagicMock()
view.scene.addItem(item)
@ -781,9 +800,11 @@ def test_mouse_press_pan_alt_left_drag(mouse_event_mock, view):
@patch('PyQt6.QtWidgets.QGraphicsView.mousePressEvent')
def test_mouse_press_move_window(mouse_event_mock, view):
@patch('beeref.view.BeeGraphicsView.cursor')
def test_mouse_press_move_window(cursor_mock, mouse_event_mock, view):
event = MagicMock()
event.position.return_value = QtCore.QPointF(10.0, 20.0)
cursor_mock.return_value = MagicMock(
pos=MagicMock(return_value=QtCore.QPointF(10.0, 20.0)))
event.button.return_value = Qt.MouseButton.LeftButton
event.modifiers.return_value = (
Qt.KeyboardModifier.AltModifier | Qt.KeyboardModifier.ControlModifier)
@ -796,6 +817,22 @@ def test_mouse_press_move_window(mouse_event_mock, view):
event.accept.assert_called_once_with()
@patch('PyQt6.QtWidgets.QGraphicsView.mousePressEvent')
def test_mouse_press_when_move_window_active(mouse_event_mock, view):
view.movewin_active = True
view.mousePressEvent(MagicMock())
assert view.movewin_active is False
mouse_event_mock.assert_not_called()
@patch('PyQt6.QtWidgets.QGraphicsView.keyPressEvent')
def test_key_press_when_move_window_active(key_event_mock, view):
view.movewin_active = True
view.keyPressEvent(MagicMock())
assert view.movewin_active is False
key_event_mock.assert_not_called()
@patch('PyQt6.QtWidgets.QGraphicsView.mousePressEvent')
def test_mouse_press_unhandled(mouse_event_mock, view):
event = MagicMock()