From 1185a1bec1af855b86dc949f896c108b3107445a Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Thu, 20 Oct 2022 18:58:41 -0700 Subject: [PATCH] Fix #1161 by saving and loading the state of individual docks --- openlp/core/common/settings.py | 6 +- openlp/core/ui/mainwindow.py | 44 ++++++++----- tests/openlp_core/ui/test_mainwindow.py | 83 ++++++++++++++++++++++--- 3 files changed, 110 insertions(+), 23 deletions(-) diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py index 93b1eb523..f8edf1033 100644 --- a/openlp/core/common/settings.py +++ b/openlp/core/common/settings.py @@ -365,6 +365,7 @@ class Settings(QtCore.QSettings): 'themes/theme level': ThemeLevel.Global, 'themes/item transitions': False, 'themes/hot reload': False, + 'user interface/is preset layout': False, 'user interface/live panel': True, 'user interface/live splitter geometry': QtCore.QByteArray(), 'user interface/lock panel': True, @@ -374,7 +375,10 @@ class Settings(QtCore.QSettings): 'user interface/main window state': QtCore.QByteArray(), 'user interface/preview panel': True, 'user interface/preview splitter geometry': QtCore.QByteArray(), - 'user interface/is preset layout': False, + 'user interface/show library': True, + 'user interface/show projectors': True, + 'user interface/show service': True, + 'user interface/show themes': True, 'projector/show after wizard': False, 'projector/db type': 'sqlite', 'projector/db username': '', diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 932a13b16..61a73f3d3 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -277,7 +277,7 @@ class Ui_MainWindow(object): self.settings_configure_item = create_action(main_window, 'settingsConfigureItem', icon=UiIcons().settings, can_shortcuts=True, category=UiStrings().Settings) - # Give QT Extra Hint that this is the Preferences Menu Item + # Give Qt Extra Hint that this is the Preferences Menu Item self.settings_configure_item.setMenuRole(QtWidgets.QAction.PreferencesRole) self.settings_import_item = create_action(main_window, 'settingsImportItem', category=UiStrings().Import, can_shortcuts=True) @@ -287,7 +287,7 @@ class Ui_MainWindow(object): self.about_item = create_action(main_window, 'aboutItem', icon=UiIcons().info, can_shortcuts=True, category=UiStrings().Help, triggers=self.on_about_item_clicked) - # Give QT Extra Hint that this is an About Menu Item + # Give Qt Extra Hint that this is an About Menu Item self.about_item.setMenuRole(QtWidgets.QAction.AboutRole) if is_win(): self.local_help_file = AppLocation.get_directory(AppLocation.AppDir) / 'OpenLP.chm' @@ -495,10 +495,10 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert self.update_recent_files_menu() self.plugin_form = PluginForm(self) # Set up signals and slots - self.media_manager_dock.visibilityChanged.connect(self.view_media_manager_item.setChecked) - self.service_manager_dock.visibilityChanged.connect(self.view_service_manager_item.setChecked) - self.theme_manager_dock.visibilityChanged.connect(self.view_theme_manager_item.setChecked) - self.projector_manager_dock.visibilityChanged.connect(self.view_projector_manager_item.setChecked) + self.media_manager_dock.visibilityChanged.connect(self.toggle_media_manager) + self.service_manager_dock.visibilityChanged.connect(self.toggle_service_manager) + self.theme_manager_dock.visibilityChanged.connect(self.toggle_theme_manager) + self.projector_manager_dock.visibilityChanged.connect(self.toggle_projector_manager) self.import_theme_item.triggered.connect(self.theme_manager_contents.on_import_theme) self.export_theme_item.triggered.connect(self.theme_manager_contents.on_export_theme) self.web_site_item.triggered.connect(self.on_help_web_site_clicked) @@ -661,10 +661,14 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert self.set_view_mode(False, True, False, False, True, True) self.mode_live_item.setChecked(True) else: - self.set_view_mode(True, True, True, - self.settings.value('user interface/preview panel'), - self.settings.value('user interface/live panel'), - True) + self.set_view_mode( + self.settings.value('user interface/show library'), + self.settings.value('user interface/show service'), + self.settings.value('user interface/show themes'), + self.settings.value('user interface/preview panel'), + self.settings.value('user interface/live panel'), + self.settings.value('user interface/show projectors') + ) def first_time(self): """ @@ -1154,15 +1158,21 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert """ Toggle the visibility of the media manager """ - self.media_manager_dock.setVisible(not self.media_manager_dock.isVisible()) + if self.sender() is self.view_media_manager_item: + self.media_manager_dock.setVisible(not self.media_manager_dock.isVisible()) + self.view_media_manager_item.setChecked(self.media_manager_dock.isVisible()) self.settings.setValue('user interface/is preset layout', False) + self.settings.setValue('user interface/show library', self.media_manager_dock.isVisible()) def toggle_projector_manager(self): """ Toggle visibility of the projector manager """ - self.projector_manager_dock.setVisible(not self.projector_manager_dock.isVisible()) + if self.sender() is self.view_projector_manager_item: + self.projector_manager_dock.setVisible(not self.projector_manager_dock.isVisible()) + self.view_projector_manager_item.setChecked(self.projector_manager_dock.isVisible()) self.settings.setValue('user interface/is preset layout', False) + self.settings.setValue('user interface/show projectors', self.projector_manager_dock.isVisible()) # Check/uncheck checkbox on First time wizard based on visibility of this panel. if not self.settings.value('projector/show after wizard'): self.settings.setValue('projector/show after wizard', True) @@ -1173,15 +1183,21 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert """ Toggle the visibility of the service manager """ - self.service_manager_dock.setVisible(not self.service_manager_dock.isVisible()) + if self.sender() is self.view_service_manager_item: + self.service_manager_dock.setVisible(not self.service_manager_dock.isVisible()) + self.view_service_manager_item.setChecked(self.service_manager_dock.isVisible()) self.settings.setValue('user interface/is preset layout', False) + self.settings.setValue('user interface/show service', self.service_manager_dock.isVisible()) def toggle_theme_manager(self): """ Toggle the visibility of the theme manager """ - self.theme_manager_dock.setVisible(not self.theme_manager_dock.isVisible()) + if self.sender() is self.view_theme_manager_item: + self.theme_manager_dock.setVisible(not self.theme_manager_dock.isVisible()) + self.view_theme_manager_item.setChecked(self.theme_manager_dock.isVisible()) self.settings.setValue('user interface/is preset layout', False) + self.settings.setValue('user interface/show themes', self.theme_manager_dock.isVisible()) def set_preview_panel_visibility(self, visible): """ diff --git a/tests/openlp_core/ui/test_mainwindow.py b/tests/openlp_core/ui/test_mainwindow.py index 1ef4570b0..fa822fd43 100644 --- a/tests/openlp_core/ui/test_mainwindow.py +++ b/tests/openlp_core/ui/test_mainwindow.py @@ -72,6 +72,7 @@ def main_window(state, settings, mocked_qapp): mocked_qapp.primaryScreen.return_value = mocked_screen ScreenList.create(mocked_qapp) mainwindow = MainWindow() + mainwindow.activateWindow = MagicMock() yield mainwindow del mainwindow renderer_patcher.stop() @@ -579,8 +580,63 @@ def test_projector_manager_dock_unlocked(main_window_reduced): projector_dock.setFeatures.assert_called_with(7) +@patch('openlp.core.ui.mainwindow.MainWindow.open_cmd_line_files') @patch('openlp.core.ui.mainwindow.MainWindow.set_view_mode') -def test_load_settings_view_mode_default_mode(mocked_view_mode, main_window, settings): +def test_show_cmd_line_args(mocked_view_mode, mocked_open_cmd_line_files, main_window, settings): + """Test that command line arguments are loaded on show""" + # GIVEN: A newly opened OpenLP instance with some command line arguments + main_window.application.args = ['-disable-web-security', 'new_file_name.osz'] + + # WHEN: MainWindow.show() is called + main_window.show() + + # THEN: The command line arguments are searched for a file name + mocked_open_cmd_line_files.assert_called_once_with(main_window.application.args) + + +@patch('openlp.core.ui.mainwindow.MainWindow.set_view_mode') +def test_show_load_last_file(mocked_view_mode, main_window, settings): + """Test that the last file opened is loaded on show""" + # GIVEN: A newly opened OpenLP instance with some command line arguments + main_window.service_manager_contents.load_last_file = MagicMock() + main_window.settings.setValue('core/auto open', True) + + # WHEN: MainWindow.show() is called + main_window.show() + + # THEN: The command line arguments are searched for a file name + main_window.service_manager_contents.load_last_file.assert_called_once_with() + + +@patch('openlp.core.ui.mainwindow.MainWindow.set_view_mode') +def test_load_settings_custom_layout(mocked_view_mode, main_window, settings): + """Test that the view mode is called with the correct parameters for default mode""" + # 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() + main_window.activateWindow = MagicMock() + main_window.settings.setValue('core/view mode', 'default') + main_window.settings.setValue('user interface/is preset layout', False) + main_window.settings.setValue('user interface/show library', True) + main_window.settings.setValue('user interface/show service', False) + main_window.settings.setValue('user interface/show themes', True) + main_window.settings.setValue('user interface/show projectors', False) + main_window.settings.setValue('user interface/live panel', True) + main_window.settings.setValue('user interface/preview panel', False) + + # WHEN: we call to show method + main_window.show() + + # THEN: + # The default mode should have been called. + mocked_view_mode.assert_called_with(True, True, True, False, True, False) + + +@patch('openlp.core.ui.mainwindow.QtWidgets.QWidget.show') +@patch('openlp.core.ui.mainwindow.MainWindow.set_view_mode') +def test_load_settings_view_mode_default_mode(mocked_view_mode, mocked_show, main_window, settings): """ Test that the view mode is called with the correct parameters for default mode """ @@ -589,10 +645,11 @@ def test_load_settings_view_mode_default_mode(mocked_view_mode, main_window, set main_window.control_splitter = MagicMock() main_window._live_controller = MagicMock() main_window._preview_controller = MagicMock() + main_window.activateWindow = MagicMock() main_window.settings.setValue('core/view mode', 'default') main_window.settings.setValue('user interface/is preset layout', True) - # WHENL we call to show method + # WHEN: we call to show method main_window.show() # THEN: @@ -600,10 +657,14 @@ def test_load_settings_view_mode_default_mode(mocked_view_mode, main_window, set mocked_view_mode.assert_called_with(True, True, True, True, True, True) +@patch('openlp.core.ui.mainwindow.QtWidgets.QWidget.show') @patch('openlp.core.ui.mainwindow.MainWindow.set_view_mode') -def test_load_settings_view_mode_setup_mode(mocked_view_mode, main_window, settings): +def test_load_settings_view_mode_setup_mode(mocked_view_mode, mocked_show, main_window, settings): """ Test that the view mode is called with the correct parameters for setup mode + + Note: for some reason, only when the test is running, the QWidget.show() method resets the layout setting, + so this is mocked out to prevent it from interferring in the loading of the view layout """ # GIVEN a newly opened OpenLP instance, mocked screens and settings for a valid window position # mock out some other calls in load_settings() @@ -613,7 +674,7 @@ def test_load_settings_view_mode_setup_mode(mocked_view_mode, main_window, setti main_window.settings.setValue('core/view mode', 'setup') main_window.settings.setValue('user interface/is preset layout', True) - # WHENL we call to show method + # WHEN: we call to show method main_window.show() # THEN: @@ -621,10 +682,14 @@ def test_load_settings_view_mode_setup_mode(mocked_view_mode, main_window, setti mocked_view_mode.assert_called_with(True, True, False, True, False, True) +@patch('openlp.core.ui.mainwindow.QtWidgets.QWidget.show') @patch('openlp.core.ui.mainwindow.MainWindow.set_view_mode') -def test_load_settings_view_mode_live_mode(mocked_view_mode, main_window, settings): +def test_load_settings_view_mode_live_mode(mocked_view_mode, mocked_show, main_window, settings): """ Test that the view mode is called with the correct parameters for live mode + + Note: for some reason, only when the test is running, the QWidget.show() method resets the layout setting, + so this is mocked out to prevent it from interferring in the loading of the view layout """ # GIVEN a newly opened OpenLP instance, mocked screens and settings for a valid window position # mock out some other calls in load_settings() @@ -634,7 +699,7 @@ def test_load_settings_view_mode_live_mode(mocked_view_mode, main_window, settin main_window.settings.setValue('core/view mode', 'live') main_window.settings.setValue('user interface/is preset layout', True) - # WHENL we call to show method + # WHEN: we call to show method main_window.show() # THEN: @@ -652,11 +717,12 @@ def test_load_settings_view_mode_preview(mocked_view_mode, main_window, settings main_window.control_splitter = MagicMock() main_window._live_controller = MagicMock() main_window._preview_controller = MagicMock() + main_window.activateWindow = MagicMock() main_window.settings.setValue('core/view mode', 'default') main_window.settings.setValue('user interface/is preset layout', False) main_window.settings.setValue('user interface/preview panel', False) - # WHENL we call to show method + # WHEN: we call to show method main_window.show() # THEN: @@ -674,11 +740,12 @@ def test_load_settings_view_mode_live(mocked_view_mode, main_window, settings): main_window.control_splitter = MagicMock() main_window._live_controller = MagicMock() main_window._preview_controller = MagicMock() + main_window.activateWindow = MagicMock() main_window.settings.setValue('core/view mode', 'default') main_window.settings.setValue('user interface/is preset layout', False) main_window.settings.setValue('user interface/live panel', False) - # WHENL we call to show method + # WHEN: we call to show method main_window.show() # THEN: