Merge branch 'win-dark-mode' into 'master'

Implement support for windows 10 dark mode, fixes #370.

Closes #370

See merge request openlp/openlp!254
This commit is contained in:
Raoul Snyman 2020-10-02 02:18:39 +00:00
commit c9c9bc092d
4 changed files with 56 additions and 12 deletions

View File

@ -36,7 +36,7 @@ from pathlib import Path
from shutil import copytree from shutil import copytree
from traceback import format_exception 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.api.deploy import check_for_remote_update
from openlp.core.common import is_macosx, is_win 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.firsttimelanguageform import FirstTimeLanguageForm
from openlp.core.ui.mainwindow import MainWindow from openlp.core.ui.mainwindow import MainWindow
from openlp.core.ui.splashscreen import SplashScreen 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 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) self.backup_on_upgrade(has_run_wizard, can_show_splash)
# start the main app window # start the main app window
loader() loader()
# Set the darkmode for windows is enabled
if is_win():
set_windows_darkmode(app)
self.main_window = MainWindow() self.main_window = MainWindow()
self.main_window.installEventFilter(self.main_window) self.main_window.installEventFilter(self.main_window)
# Correct stylesheet bugs # Correct stylesheet bugs
@ -330,6 +333,10 @@ def main():
# Bug #1018855: Set the WM_CLASS property in X11 # Bug #1018855: Set the WM_CLASS property in X11
if not is_win() and not is_macosx(): if not is_win() and not is_macosx():
qt_args.append('OpenLP') 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 # Initialise the resources
qInitResources() qInitResources()
# Now create and actually run the application. # Now create and actually run the application.

View File

@ -26,7 +26,7 @@ from datetime import datetime, timedelta
from PyQt5 import QtCore, QtGui, QtWidgets 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.applocation import AppLocation
from openlp.core.common.i18n import UiStrings, format_time, translate from openlp.core.common.i18n import UiStrings, format_time, translate
from openlp.core.lib.settingstab import SettingsTab 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.enable_auto_close_check_box.setObjectName('enable_auto_close_check_box')
self.ui_layout.addRow(self.enable_auto_close_check_box) self.ui_layout.addRow(self.enable_auto_close_check_box)
self.left_layout.addWidget(self.ui_group_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 = QtWidgets.QCheckBox(self.ui_group_box)
self.use_dark_style_checkbox.setObjectName('use_dark_style_checkbox') self.use_dark_style_checkbox.setObjectName('use_dark_style_checkbox')
self.ui_layout.addRow(self.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')) 'Auto-scroll the next slide to bottom'))
self.enable_auto_close_check_box.setText(translate('OpenLP.AdvancedTab', self.enable_auto_close_check_box.setText(translate('OpenLP.AdvancedTab',
'Enable application exit confirmation')) '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.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_group_box.setTitle(translate('OpenLP.AdvancedTab', 'Default Service Name'))
self.service_name_check_box.setText(translate('OpenLP.AdvancedTab', 'Enable 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(): if self.autoscroll_map[i] == autoscroll_value and i < self.autoscroll_combo_box.count():
self.autoscroll_combo_box.setCurrentIndex(i) self.autoscroll_combo_box.setCurrentIndex(i)
self.enable_auto_close_check_box.setChecked(self.settings.value('advanced/enable exit confirmation')) 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.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.hide_mouse_check_box.setChecked(self.settings.value('advanced/hide mouse'))
self.service_name_day.setCurrentIndex(self.settings.value('advanced/default service day')) 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('config_screen_changed')
self.settings_form.register_post_process('slidecontroller_update_slide_limits') self.settings_form.register_post_process('slidecontroller_update_slide_limits')
self.settings.setValue('advanced/search as type', self.is_search_as_you_type_enabled) 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.settings.setValue('advanced/use_dark_style', self.use_dark_style_checkbox.isChecked())
self.proxy_widget.save() self.proxy_widget.save()

View File

@ -21,7 +21,7 @@
""" """
The :mod:`~openlp.core.ui.dark` module looks for and loads a dark theme 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 import is_win
from openlp.core.common.registry import Registry 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(): def get_application_stylesheet():
""" """
Return the correct application stylesheet based on the current style and operating system 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 :return str: The correct stylesheet as a string
""" """
stylesheet = '' 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() stylesheet = qdarkstyle.load_stylesheet_pyqt5()
else: else:
if not Registry().get('settings').value('advanced/alternate rows'): if not Registry().get('settings').value('advanced/alternate rows'):

View File

@ -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') @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.HAS_DARK_STYLE', True)
@patch('openlp.core.ui.style.is_win')
@patch('openlp.core.ui.style.qdarkstyle') @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""" """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 = MagicMock()
mocked_settings.value.return_value = True mocked_settings.value.return_value = True
mock_settings.return_value = mocked_settings 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') @patch('openlp.core.app.QtWidgets.QApplication.palette')
def test_get_application_stylesheet_not_alternate_rows(mocked_palette, mocked_is_win, mock_settings): 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""" """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 mocked_is_win.return_value = False
mock_settings.value.return_value = False mock_settings.value.return_value = False
mocked_palette.return_value.color.return_value.name.return_value = 'color' mocked_palette.return_value.color.return_value.name.return_value = 'color'