Merge branch 'fix-main-window-positioning' into 'master'

Fix main window positioning

Closes #883

See merge request openlp/openlp!343
This commit is contained in:
Tim Bentley 2021-08-19 16:23:57 +00:00
commit 2312ff644f
2 changed files with 88 additions and 0 deletions

View File

@ -1234,6 +1234,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
self.move(self.settings.value('user interface/main window position'))
self.restoreGeometry(self.settings.value('user interface/main window geometry'))
self.restoreState(self.settings.value('user interface/main window state'))
if not self._window_position_is_valid(self.pos(), self.geometry()):
self.move(0, 0)
self.live_controller.splitter.restoreState(self.settings.value('user interface/live splitter geometry'))
self.preview_controller.splitter.restoreState(self.settings.value('user interface/preview splitter geometry'))
self.control_splitter.restoreState(self.settings.value('user interface/main window splitter geometry'))
@ -1241,6 +1243,29 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
# which was True (by default) < OpenLP 2.1.
self.control_splitter.setChildrenCollapsible(False)
def _window_position_is_valid(self, position, geometry):
"""
Checks if the saved window position is still valid by checking if the bar at the top of the window
(which allows the user to move the window) appears on one of the screens.
This may not be the case if the user has unplugged the monitor where openlp was previously shown,
or if the displays have been reconfigured.
:param position: QtCore.QtPoint for the top left position of the window
:param geometry: QtCore.QRect for the geometry of the window
:return: True or False
"""
screens = ScreenList()
for screen in screens:
# window top bar y value must be between top and bottom of a screen
# plus the left edge must be left of the right of the screen
# and the right edge must be right of the left of the screen (allowing 50 pixels for control buttons)
if ((screen.geometry.y() <= position.y() <= screen.geometry.y() + screen.geometry.height()) and
(position.x() < screen.geometry.x() + screen.geometry.width()) and
(position.x() + geometry.width() > screen.geometry.x() + 50)):
return True
return False
def save_settings(self):
"""
Save the main window settings.

View File

@ -174,6 +174,69 @@ def test_set_service_unmodified(main_window):
'The main window\'s title should be set to "<the contents of UiStrings().OpenLP> - test.osz"'
def test_load_settings_position_valid(main_window, settings):
"""
Test that the position of the main window is restored when it's valid
"""
# GIVEN a newly opened OpenLP instance, mocked screens and settings for a valid window position
# mock out some other calls in load_settings()
main_window.control_splitter = MagicMock()
main_window._live_controller = MagicMock()
main_window._preview_controller = MagicMock()
# set up a window position and geometry to use in the settings
main_window.move(QtCore.QPoint(10, 10))
main_window.resize(1000, 500)
# need to call show() to ensure the geometry works as expected
# unfortunately this seems to work on Windows only, not on linux
main_window.show()
main_window.hide()
# store the values in the settings
settings.setValue('user interface/main window position', main_window.pos())
settings.setValue('user interface/main window geometry', main_window.saveGeometry())
settings.setValue('user interface/main window state', main_window.saveState())
# change the position and size - then we can test if load_settings() sets it back correctly
main_window.move(QtCore.QPoint(20, 20))
main_window.resize(500, 300)
# WHEN the settings are loaded
main_window.load_settings()
# THEN the main window's position and geometry should be set to the saved setting
# on linux the tests works for the x position only
assert main_window.pos().x() == 10
def test_load_settings_position_invalid(main_window, settings):
"""
Test that the position of the main window is not restored when it's invalid, but rather set to (0, 0)
"""
# GIVEN a newly opened OpenLP instance, mocked screens and settings for a valid window position
# mock out some other calls in load_settings()
main_window.control_splitter = MagicMock()
main_window._live_controller = MagicMock()
main_window._preview_controller = MagicMock()
# set up a window position outside the parameters of the main_window fixture
# this can represent a monitor positioned above the primary display, but which has been unplugged
main_window.move(QtCore.QPoint(-100, -800))
main_window.resize(1000, 500)
# need to call show() to ensure the geometry works as expected (works on Windows, but not linux)
main_window.show()
main_window.hide()
# store the values in the settings
settings.setValue('user interface/main window position', main_window.pos())
settings.setValue('user interface/main window geometry', main_window.saveGeometry())
settings.setValue('user interface/main window state', main_window.saveState())
# change the position and size
main_window.move(QtCore.QPoint(20, 20))
main_window.resize(500, 300)
# WHEN the settings are loaded
main_window.load_settings()
# THEN the main window's position should be (0, 0)
assert main_window.pos().x() == 0
def test_mainwindow_configuration(main_window):
"""
Check that the Main Window initialises the Registry Correctly