Windows: Automatically change colors between light and dark when the system wide preference is changed

This commit is contained in:
Kovid Goyal 2022-01-25 12:44:20 +05:30
parent 1ec0085eff
commit f695e49bd6
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 53 additions and 18 deletions

View File

@ -49,8 +49,7 @@ Environment variables
the system theme -- beware of crashes and hangs.
* ``CALIBRE_SHOW_DEPRECATION_WARNINGS`` - causes calibre to print deprecation warnings to stdout. Useful for calibre developers.
* ``CALIBRE_NO_DEFAULT_PROGRAMS`` - prevent calibre from automatically registering the filetypes it is capable of handling with Windows.
* ``CALIBRE_USE_DARK_PALETTE`` - set it to ``1`` to have calibre use dark colors and ``0`` for normal colors (ignored on macOS).
On Windows 10 in the absence of this variable, the Windows system preference for dark colors is used.
* ``CALIBRE_USE_DARK_PALETTE`` - set it to ``1`` to have calibre use dark colors. Works on **Linux only**.
* ``SYSFS_PATH`` - Use if sysfs is mounted somewhere other than /sys
* ``http_proxy``, ``https_proxy`` - used on Linux to specify an HTTP(S) proxy

View File

@ -30,6 +30,7 @@ from calibre.ebooks.metadata import MetaInformation
from calibre.gui2.linux_file_dialogs import (
check_for_linux_native_dialogs, linux_native_dialog
)
from calibre.gui2.palette import dark_palette, fix_palette_colors
from calibre.gui2.qt_file_dialogs import FileDialog
from calibre.ptempfile import base_dir
from calibre.utils.config_base import tweaks
@ -1076,6 +1077,11 @@ class Application(QApplication):
QApplication.setDesktopFileName(override_program_name)
QApplication.setAttribute(Qt.ApplicationAttribute.AA_ShareOpenGLContexts, True) # needed for webengine
QApplication.__init__(self, qargs)
self.original_palette = self.palette()
self.original_palette_modified = fix_palette_colors(self.original_palette)
if iswindows:
self.win_event_filter = WinEventFilter()
self.installNativeEventFilter(self.win_event_filter)
icon_resource_manager.initialize()
sh = self.styleHints()
if hasattr(sh, 'setShowShortcutsInContextMenus'):
@ -1146,14 +1152,6 @@ class Application(QApplication):
raise SystemExit(1)
if iswindows:
# On windows the highlighted colors for inactive widgets are the
# same as non highlighted colors. This is a regression from Qt 4.
# https://bugreports.qt-project.org/browse/QTBUG-41060
p = self.palette()
for role in (QPalette.ColorRole.Highlight, QPalette.ColorRole.HighlightedText, QPalette.ColorRole.Base, QPalette.ColorRole.AlternateBase):
p.setColor(QPalette.ColorGroup.Inactive, role, p.color(QPalette.ColorGroup.Active, role))
self.setPalette(p)
# Prevent text copied to the clipboard from being lost on quit due to
# Qt 5 bug: https://bugreports.qt-project.org/browse/QTBUG-41125
self.aboutToQuit.connect(self.flush_clipboard)
@ -1221,7 +1219,6 @@ class Application(QApplication):
load_builtin_fonts()
def set_dark_mode_palette(self):
from calibre.gui2.palette import dark_palette
self.set_palette(dark_palette())
def setup_styles(self, force_calibre_style):
@ -1232,15 +1229,16 @@ class Application(QApplication):
if force_calibre_style:
using_calibre_style = True
if using_calibre_style:
use_dark_palette = False
if 'CALIBRE_USE_DARK_PALETTE' in os.environ:
if not ismacos:
use_dark_palette = os.environ['CALIBRE_USE_DARK_PALETTE'] != '0'
if iswindows:
use_dark_palette = windows_is_system_dark_mode_enabled()
elif ismacos:
use_dark_palette = False
else:
if iswindows:
use_dark_palette = windows_is_system_dark_mode_enabled()
use_dark_palette = os.environ.get('CALIBRE_USE_DARK_PALETTE') == '1'
if use_dark_palette:
self.set_dark_mode_palette()
elif self.original_palette_modified:
self.set_palette(self.original_palette)
self.using_calibre_style = using_calibre_style
if DEBUG:
@ -1249,6 +1247,15 @@ class Application(QApplication):
self.load_calibre_style()
self.on_palette_change()
def check_for_windows_palette_change(self):
use_dark_palette = bool(windows_is_system_dark_mode_enabled())
if bool(self.is_dark_theme) != use_dark_palette:
if use_dark_palette:
self.set_dark_mode_palette()
else:
self.set_palette(self.original_palette)
self.on_palette_change()
def set_palette(self, pal):
self.ignore_palette_changes = True
self.setPalette(pal)
@ -1633,6 +1640,24 @@ def add_to_recent_docs(path):
winutil.add_to_recent_docs(str(path), app.windows_app_uid)
if iswindows:
import ctypes
from qt.core import QAbstractNativeEventFilter
class WinEventFilter(QAbstractNativeEventFilter):
def nativeEventFilter(self, eventType, message):
if eventType == b"windows_generic_MSG":
msg = ctypes.wintypes.MSG.from_address(message.__int__())
# https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-settingchange
if msg.message == 0x001A: # WM_SETTINGCHANGE
if ctypes.wstring_at(msg.lParam) == 'ImmersiveColorSet':
QApplication.instance().check_for_windows_palette_change()
# prevent Qt from handling this event
return True, 0
return False, 0
def windows_is_system_dark_mode_enabled():
s = QSettings(r"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", QSettings.Format.NativeFormat)
if s.status() == QSettings.Status.NoError:

View File

@ -2,7 +2,7 @@
# License: GPL v3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>
from qt.core import QColor, QPalette, Qt
from calibre.constants import dark_link_color
from calibre.constants import dark_link_color, iswindows
dark_link_color = QColor(dark_link_color)
@ -10,6 +10,17 @@ dark_color = QColor(45,45,45)
dark_text_color = QColor('#ddd')
def fix_palette_colors(p):
if iswindows:
# On Windows the highlighted colors for inactive widgets are the
# same as non highlighted colors. This is a regression from Qt 4.
# https://bugreports.qt-project.org/browse/QTBUG-41060
for role in (QPalette.ColorRole.Highlight, QPalette.ColorRole.HighlightedText, QPalette.ColorRole.Base, QPalette.ColorRole.AlternateBase):
p.setColor(QPalette.ColorGroup.Inactive, role, p.color(QPalette.ColorGroup.Active, role))
return True
return False
def dark_palette():
p = QPalette()
disabled_color = QColor(127,127,127)