From 6e9368da676c9cfd6935a6beca1f2690d3717d2a Mon Sep 17 00:00:00 2001 From: Gabriel Ferreira Date: Sun, 28 Mar 2021 16:42:50 -0300 Subject: [PATCH 1/3] Fix theme issues on MacOS --- picard/ui/theme.py | 54 ++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/picard/ui/theme.py b/picard/ui/theme.py index 825d2aae6..8415ec06a 100644 --- a/picard/ui/theme.py +++ b/picard/ui/theme.py @@ -209,31 +209,43 @@ elif IS_MACOS: except ImportError: AppKit = None + dark_appearance = False + if AppKit: + # Default procedure to identify the current appearance (theme) + appearance = AppKit.NSAppearance.currentAppearance() + try: + basic_appearance = appearance.bestMatchFromAppearancesWithNames_([ + AppKit.NSAppearanceNameAqua, + AppKit.NSAppearanceNameDarkAqua + ]) + dark_appearance = basic_appearance == AppKit.NSAppearanceNameDarkAqua + except AttributeError: + pass + class MacTheme(BaseTheme): + def setup(self, app): + super().setup(app) + + if self._loaded_config_theme != UiTheme.DEFAULT: + dark_theme = self._loaded_config_theme == UiTheme.DARK + else: + dark_theme = dark_appearance + + # MacOS uses a NSAppearance object to change the current application appearance + # We call this even if UiTheme is the default, preventing MacOS from switching on-the-fly + if dark_theme: + appearance = AppKit.NSAppearance._darkAquaAppearance() + else: + appearance = AppKit.NSAppearance._aquaAppearance() + AppKit.NSApplication.sharedApplication().setAppearance_(appearance) + @property def is_dark_theme(self): dark_theme = False - if not AppKit: - return dark_theme - if self._loaded_config_theme is not None and self._loaded_config_theme != UiTheme.DEFAULT: - dark_theme = self._loaded_config_theme == UiTheme.DARK - # MacOS uses a NSAppearance object to change the current application appearance - if dark_theme: - appearance = AppKit.NSAppearance._darkAquaAppearance() - else: - appearance = AppKit.NSAppearance._aquaAppearance() - AppKit.NSApplication.sharedApplication().setAppearance_(appearance) - else: - # Default procedure to identify the current appearance (theme) - appearance = AppKit.NSAppearance.currentAppearance() - try: - basic_appearance = appearance.bestMatchFromAppearancesWithNames_([ - AppKit.NSAppearanceNameAqua, - AppKit.NSAppearanceNameDarkAqua - ]) - dark_theme = basic_appearance == AppKit.NSAppearanceNameDarkAqua - except AttributeError: - pass + if self._loaded_config_theme == UiTheme.DEFAULT: + dark_theme = dark_appearance + elif self._loaded_config_theme == UiTheme.DARK: + dark_theme = True return dark_theme # pylint: disable=no-self-use From 10b47a3a5390f68cfd233dbbee3f31658400d52e Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Mon, 29 Mar 2021 09:15:32 +0200 Subject: [PATCH 2/3] Handle dark theme on macOS without AppKit If AppKit is not available fall back to generic dark background color detection . --- picard/ui/theme.py | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/picard/ui/theme.py b/picard/ui/theme.py index 8415ec06a..53470a4e8 100644 --- a/picard/ui/theme.py +++ b/picard/ui/theme.py @@ -38,7 +38,15 @@ from picard.const.sys import ( OS_SUPPORTS_THEMES = True if IS_MACOS: + try: + import AppKit + except ImportError: + AppKit = None + def is_dark_theme_supported(): + if not AppKit: + return False + import platform try: current_version = tuple(map(int, platform.mac_ver()[0].split(".")))[:2] @@ -204,11 +212,6 @@ if IS_WIN: theme = WindowsTheme() elif IS_MACOS: - try: - import AppKit - except ImportError: - AppKit = None - dark_appearance = False if AppKit: # Default procedure to identify the current appearance (theme) @@ -233,20 +236,22 @@ elif IS_MACOS: # MacOS uses a NSAppearance object to change the current application appearance # We call this even if UiTheme is the default, preventing MacOS from switching on-the-fly - if dark_theme: - appearance = AppKit.NSAppearance._darkAquaAppearance() - else: - appearance = AppKit.NSAppearance._aquaAppearance() - AppKit.NSApplication.sharedApplication().setAppearance_(appearance) + if AppKit: + if dark_theme: + appearance = AppKit.NSAppearance._darkAquaAppearance() + else: + appearance = AppKit.NSAppearance._aquaAppearance() + AppKit.NSApplication.sharedApplication().setAppearance_(appearance) @property def is_dark_theme(self): - dark_theme = False - if self._loaded_config_theme == UiTheme.DEFAULT: - dark_theme = dark_appearance - elif self._loaded_config_theme == UiTheme.DARK: - dark_theme = True - return dark_theme + if not AppKit: + # Fall back to generic dark color palette detection + return super().is_dark_theme + elif self._loaded_config_theme == UiTheme.DEFAULT: + return dark_appearance + else: + return self._loaded_config_theme == UiTheme.DARK # pylint: disable=no-self-use def update_palette(self, palette, dark_theme, accent_color): From efe5fbb616c79559774111688f396688c0bda7b6 Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Mon, 29 Mar 2021 14:41:47 +0200 Subject: [PATCH 3/3] Generic feature detection of dark theme support on macOS Instead of relying on version numbers check the API for dark appearance support. --- picard/ui/theme.py | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/picard/ui/theme.py b/picard/ui/theme.py index 53470a4e8..3fe9bf966 100644 --- a/picard/ui/theme.py +++ b/picard/ui/theme.py @@ -43,20 +43,7 @@ if IS_MACOS: except ImportError: AppKit = None - def is_dark_theme_supported(): - if not AppKit: - return False - - import platform - try: - current_version = tuple(map(int, platform.mac_ver()[0].split(".")))[:2] - except ValueError: - log.warning("Error while converting the MacOS version string into a tuple: %s" % platform.mac_ver()[0]) - return False - - mojave_version = (10, 14) # Dark theme support was introduced in Mojave - return current_version >= mojave_version - OS_SUPPORTS_THEMES = is_dark_theme_supported() + OS_SUPPORTS_THEMES = bool(AppKit) and hasattr(AppKit.NSAppearance, '_darkAquaAppearance') elif IS_HAIKU: OS_SUPPORTS_THEMES = False @@ -213,7 +200,7 @@ if IS_WIN: elif IS_MACOS: dark_appearance = False - if AppKit: + if OS_SUPPORTS_THEMES and AppKit: # Default procedure to identify the current appearance (theme) appearance = AppKit.NSAppearance.currentAppearance() try: @@ -236,16 +223,19 @@ elif IS_MACOS: # MacOS uses a NSAppearance object to change the current application appearance # We call this even if UiTheme is the default, preventing MacOS from switching on-the-fly - if AppKit: - if dark_theme: - appearance = AppKit.NSAppearance._darkAquaAppearance() - else: - appearance = AppKit.NSAppearance._aquaAppearance() - AppKit.NSApplication.sharedApplication().setAppearance_(appearance) + if OS_SUPPORTS_THEMES and AppKit: + try: + if dark_theme: + appearance = AppKit.NSAppearance._darkAquaAppearance() + else: + appearance = AppKit.NSAppearance._aquaAppearance() + AppKit.NSApplication.sharedApplication().setAppearance_(appearance) + except AttributeError: + pass @property def is_dark_theme(self): - if not AppKit: + if not OS_SUPPORTS_THEMES: # Fall back to generic dark color palette detection return super().is_dark_theme elif self._loaded_config_theme == UiTheme.DEFAULT: