From aee5d5b49558418bab32a4df722ba70378bcf13c Mon Sep 17 00:00:00 2001 From: robbie jackson Date: Thu, 19 Aug 2021 16:23:56 +0000 Subject: [PATCH] Fix main window positioning --- openlp/core/ui/mainwindow.py | 25 ++++++++++ tests/openlp_core/ui/test_mainwindow.py | 63 +++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 34cb8fa49..768c74269 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -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. diff --git a/tests/openlp_core/ui/test_mainwindow.py b/tests/openlp_core/ui/test_mainwindow.py index 5d6f834b7..1cfd6bfa3 100644 --- a/tests/openlp_core/ui/test_mainwindow.py +++ b/tests/openlp_core/ui/test_mainwindow.py @@ -174,6 +174,69 @@ def test_set_service_unmodified(main_window): 'The main window\'s title should be set to " - 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