diff --git a/openlp/core/app.py b/openlp/core/app.py index addfb74b2..51c24183e 100644 --- a/openlp/core/app.py +++ b/openlp/core/app.py @@ -36,7 +36,7 @@ from pathlib import Path from shutil import copytree from traceback import format_exception -from PyQt5 import QtCore, QtWebEngineWidgets, QtWidgets # noqa +from PyQt5 import QtCore, QtGui, QtWebEngineWidgets, QtWidgets # noqa from openlp.core.api.deploy import check_for_remote_update from openlp.core.common import is_macosx, is_win @@ -56,7 +56,7 @@ from openlp.core.ui.firsttimeform import FirstTimeForm from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm from openlp.core.ui.mainwindow import MainWindow from openlp.core.ui.splashscreen import SplashScreen -from openlp.core.ui.style import get_application_stylesheet +from openlp.core.ui.style import get_application_stylesheet, set_windows_darkmode from openlp.core.version import check_for_update, get_version @@ -118,6 +118,9 @@ class OpenLP(QtCore.QObject, LogMixin): self.backup_on_upgrade(has_run_wizard, can_show_splash) # start the main app window loader() + # Set the darkmode for windows is enabled + if is_win(): + set_windows_darkmode(app) self.main_window = MainWindow() self.main_window.installEventFilter(self.main_window) # Correct stylesheet bugs @@ -330,6 +333,10 @@ def main(): # Bug #1018855: Set the WM_CLASS property in X11 if not is_win() and not is_macosx(): qt_args.append('OpenLP') + elif is_win(): + # support dark mode on windows 10. This makes the titlebar dark, the rest is setup later + # by calling set_windows_darkmode + qt_args.extend(['-platform', 'windows:darkmode=1']) # Initialise the resources qInitResources() # Now create and actually run the application. diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index 3aadb1e0b..108a8376c 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -26,7 +26,7 @@ from datetime import datetime, timedelta from PyQt5 import QtCore, QtGui, QtWidgets -from openlp.core.common import SlideLimits +from openlp.core.common import SlideLimits, is_win from openlp.core.common.applocation import AppLocation from openlp.core.common.i18n import UiStrings, format_time, translate from openlp.core.lib.settingstab import SettingsTab @@ -116,7 +116,7 @@ class AdvancedTab(SettingsTab): self.enable_auto_close_check_box.setObjectName('enable_auto_close_check_box') self.ui_layout.addRow(self.enable_auto_close_check_box) self.left_layout.addWidget(self.ui_group_box) - if HAS_DARK_STYLE: + if not is_win() and HAS_DARK_STYLE: self.use_dark_style_checkbox = QtWidgets.QCheckBox(self.ui_group_box) self.use_dark_style_checkbox.setObjectName('use_dark_style_checkbox') self.ui_layout.addRow(self.use_dark_style_checkbox) @@ -292,7 +292,7 @@ class AdvancedTab(SettingsTab): 'Auto-scroll the next slide to bottom')) self.enable_auto_close_check_box.setText(translate('OpenLP.AdvancedTab', 'Enable application exit confirmation')) - if HAS_DARK_STYLE: + if not is_win() and HAS_DARK_STYLE: self.use_dark_style_checkbox.setText(translate('OpenLP.AdvancedTab', 'Use dark style (needs restart)')) self.service_name_group_box.setTitle(translate('OpenLP.AdvancedTab', 'Default Service Name')) self.service_name_check_box.setText(translate('OpenLP.AdvancedTab', 'Enable default service name')) @@ -363,7 +363,7 @@ class AdvancedTab(SettingsTab): if self.autoscroll_map[i] == autoscroll_value and i < self.autoscroll_combo_box.count(): self.autoscroll_combo_box.setCurrentIndex(i) self.enable_auto_close_check_box.setChecked(self.settings.value('advanced/enable exit confirmation')) - if HAS_DARK_STYLE: + if not is_win() and HAS_DARK_STYLE: self.use_dark_style_checkbox.setChecked(self.settings.value('advanced/use_dark_style')) self.hide_mouse_check_box.setChecked(self.settings.value('advanced/hide mouse')) self.service_name_day.setCurrentIndex(self.settings.value('advanced/default service day')) @@ -437,7 +437,7 @@ class AdvancedTab(SettingsTab): self.settings_form.register_post_process('config_screen_changed') self.settings_form.register_post_process('slidecontroller_update_slide_limits') self.settings.setValue('advanced/search as type', self.is_search_as_you_type_enabled) - if HAS_DARK_STYLE: + if not is_win() and HAS_DARK_STYLE: self.settings.setValue('advanced/use_dark_style', self.use_dark_style_checkbox.isChecked()) self.proxy_widget.save() diff --git a/openlp/core/ui/style.py b/openlp/core/ui/style.py index 240e06f5e..b46e78647 100644 --- a/openlp/core/ui/style.py +++ b/openlp/core/ui/style.py @@ -21,7 +21,7 @@ """ The :mod:`~openlp.core.ui.dark` module looks for and loads a dark theme """ -from PyQt5 import QtGui, QtWidgets +from PyQt5 import QtCore, QtGui, QtWidgets from openlp.core.common import is_win from openlp.core.common.registry import Registry @@ -76,6 +76,41 @@ QProgressBar{ """ +def set_windows_darkmode(app): + """ + Setup darkmode on the application if enabled in the OS (windows) settings + Source: https://github.com/worstje/manuskript/blob/develop/manuskript/main.py (GPL3) + """ + theme_settings = QtCore.QSettings('HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes' + '\\Personalize', + QtCore.QSettings.NativeFormat) + if theme_settings.value('AppsUseLightTheme') == 0: + app.setStyle('Fusion') + dark_palette = QtGui.QPalette() + dark_color = QtGui.QColor(45, 45, 45) + disabled_color = QtGui.QColor(127, 127, 127) + dark_palette.setColor(QtGui.QPalette.Window, dark_color) + dark_palette.setColor(QtGui.QPalette.WindowText, QtCore.Qt.white) + dark_palette.setColor(QtGui.QPalette.Base, QtGui.QColor(18, 18, 18)) + dark_palette.setColor(QtGui.QPalette.AlternateBase, dark_color) + dark_palette.setColor(QtGui.QPalette.ToolTipBase, QtCore.Qt.white) + dark_palette.setColor(QtGui.QPalette.ToolTipText, QtCore.Qt.white) + dark_palette.setColor(QtGui.QPalette.Text, QtCore.Qt.white) + dark_palette.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Text, disabled_color) + dark_palette.setColor(QtGui.QPalette.Button, dark_color) + dark_palette.setColor(QtGui.QPalette.ButtonText, QtCore.Qt.white) + dark_palette.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.ButtonText, disabled_color) + dark_palette.setColor(QtGui.QPalette.BrightText, QtCore.Qt.red) + dark_palette.setColor(QtGui.QPalette.Link, QtGui.QColor(42, 130, 218)) + dark_palette.setColor(QtGui.QPalette.Highlight, QtGui.QColor(42, 130, 218)) + dark_palette.setColor(QtGui.QPalette.HighlightedText, QtCore.Qt.black) + dark_palette.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.HighlightedText, disabled_color) + # Fixes ugly (not to mention hard to read) disabled menu items. + # Source: https://bugreports.qt.io/browse/QTBUG-10322?focusedCommentId=371060#comment-371060 + dark_palette.setColor(QtGui.QPalette.Disabled, QtGui.QPalette.Light, QtCore.Qt.transparent) + app.setPalette(dark_palette) + + def get_application_stylesheet(): """ Return the correct application stylesheet based on the current style and operating system @@ -83,7 +118,7 @@ def get_application_stylesheet(): :return str: The correct stylesheet as a string """ stylesheet = '' - if HAS_DARK_STYLE and Registry().get('settings').value('advanced/use_dark_style'): + if not is_win() and HAS_DARK_STYLE and Registry().get('settings').value('advanced/use_dark_style'): stylesheet = qdarkstyle.load_stylesheet_pyqt5() else: if not Registry().get('settings').value('advanced/alternate rows'): diff --git a/tests/functional/openlp_core/ui/test_style.py b/tests/functional/openlp_core/ui/test_style.py index 3fe7748c6..18457b568 100644 --- a/tests/functional/openlp_core/ui/test_style.py +++ b/tests/functional/openlp_core/ui/test_style.py @@ -31,10 +31,12 @@ from openlp.core.ui.style import MEDIA_MANAGER_STYLE, WIN_REPAIR_STYLESHEET, get @skipIf(not hasattr(openlp.core.ui.style, 'qdarkstyle'), 'qdarkstyle is not installed') @patch('openlp.core.ui.style.HAS_DARK_STYLE', True) +@patch('openlp.core.ui.style.is_win') @patch('openlp.core.ui.style.qdarkstyle') -def test_get_application_stylesheet_dark(mocked_qdarkstyle, mock_settings): +def test_get_application_stylesheet_dark(mocked_qdarkstyle, mocked_is_win, mock_settings): """Test that the dark stylesheet is returned when available and enabled""" - # GIVEN: We're on Windows and no dark style is set + # GIVEN: We're not on Windows and dark style is set + mocked_is_win.return_value = False mocked_settings = MagicMock() mocked_settings.value.return_value = True mock_settings.return_value = mocked_settings @@ -52,7 +54,7 @@ def test_get_application_stylesheet_dark(mocked_qdarkstyle, mock_settings): @patch('openlp.core.app.QtWidgets.QApplication.palette') def test_get_application_stylesheet_not_alternate_rows(mocked_palette, mocked_is_win, mock_settings): """Test that the alternate rows stylesheet is returned when enabled in settings""" - # GIVEN: We're on Windows and no dark style is set + # GIVEN: We're not on Windows and no dark style is set mocked_is_win.return_value = False mock_settings.value.return_value = False mocked_palette.return_value.color.return_value.name.return_value = 'color'