From 92244d6309c26db8ecdfc89347b3d1e88665a991 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Sun, 17 Dec 2023 22:05:11 +0200 Subject: [PATCH] Add color scheme (dark and light mode) support Add 'ui.gtk.color_scheme' config variable with 'system', 'light' and 'dark' string values. Config observer in main.py changes the color to match the config. System color scheme is read from freedesktop.org Settings portal via Dbus. The setting can be changed with a combo box in the preferences window via new function connect_gtk_combo_box_text(). If Settings portal cannot be found, only 'light' and 'dark' options are available in preferences. --- share/gpodder/ui/gtk/gpodderpreferences.ui | 47 +++++++++++++++- src/gpodder/config.py | 1 + src/gpodder/gtkui/app.py | 62 +++++++++++++++++++++- src/gpodder/gtkui/config.py | 7 +++ src/gpodder/gtkui/desktop/preferences.py | 15 ++++++ src/gpodder/gtkui/main.py | 5 ++ 6 files changed, 134 insertions(+), 3 deletions(-) diff --git a/share/gpodder/ui/gtk/gpodderpreferences.ui b/share/gpodder/ui/gtk/gpodderpreferences.ui index 7a7e1840..1992cd31 100644 --- a/share/gpodder/ui/gtk/gpodderpreferences.ui +++ b/share/gpodder/ui/gtk/gpodderpreferences.ui @@ -1,5 +1,5 @@ - + @@ -273,6 +273,50 @@ 2 + + + True + False + + + True + False + 8 + Color scheme: + + + False + True + 0 + + + + + True + False + True + 0 + 1 + + System + Light + Dark + + + + False + True + end + 1 + + + + + False + True + 3 + + general @@ -1157,7 +1201,6 @@ False 0 vertical - 0 True diff --git a/src/gpodder/config.py b/src/gpodder/config.py index 0af09f22..7a1f3ead 100644 --- a/src/gpodder/config.py +++ b/src/gpodder/config.py @@ -182,6 +182,7 @@ defaults = { }, 'html_shownotes': True, # enable webkit renderer + 'color_scheme': None, # system, light or dark. Initialized in app.py }, }, diff --git a/src/gpodder/gtkui/app.py b/src/gpodder/gtkui/app.py index 778e0cce..8f7acaa9 100644 --- a/src/gpodder/gtkui/app.py +++ b/src/gpodder/gtkui/app.py @@ -200,8 +200,67 @@ class gPodderApplication(Gtk.Application): # Handle "subscribe to podcast" events from firefox macosx.register_handlers(self.window) + # Set dark mode from color_scheme config key, or from Settings portal + # if it exists and color_scheme is 'system'. + if getattr(gpodder.dbus_session_bus, 'fake', False): + self.have_settings_portal = False + self._set_default_color_scheme('light') + self.set_dark_mode(self.window.config.ui.gtk.color_scheme == 'dark') + else: + self.read_portal_color_scheme() + gpodder.dbus_session_bus.add_signal_receiver( + self.on_portal_setting_changed, "SettingChanged", None, + "org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop") + self.window.gPodder.present() + def _set_default_color_scheme(self, default): + """Set the default value for color_scheme based on GTK settings. + + If gtk_application_prefer_dark_theme is set to 1 (a non-default value), + the user has set it in GTK settings.ini and we set color_scheme to match + this preference. Otherwise we set the key to the given default, which + should be 'system' in case Settings portal is found, or 'light' if it's not. + """ + if self.window.config.ui.gtk.color_scheme is None: + settings = Gtk.Settings.get_default() + self.window.config.ui.gtk.color_scheme = ( + 'dark' if settings.props.gtk_application_prefer_dark_theme == 1 + else default) + + def set_dark_mode(self, dark): + settings = Gtk.Settings.get_default() + settings.props.gtk_application_prefer_dark_theme = 1 if dark else 0 + + def read_portal_color_scheme(self): + gpodder.dbus_session_bus.call_async( + "org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.Settings", "ReadOne", "ss", + ("org.freedesktop.appearance", "color-scheme"), + self.on_portal_settings_read, self.on_portal_settings_read_error) + + def on_portal_settings_read(self, value): + self.have_settings_portal = True + self._set_default_color_scheme('system') + if self.window.config.ui.gtk.color_scheme == 'system': + self.set_dark_mode(value == 1) + else: + self.set_dark_mode(self.window.config.ui.gtk.color_scheme == 'dark') + + def on_portal_settings_read_error(self, value): + self.have_settings_portal = False + self._set_default_color_scheme('light') + self.set_dark_mode(self.window.config.ui.gtk.color_scheme == 'dark') + + def on_portal_setting_changed(self, namespace, key, value): + if (namespace == 'org.freedesktop.appearance' + and key == 'color-scheme'): + dark = (value == 1) + if self.window.config.ui.gtk.color_scheme == 'system': + logger.debug( + f"'color-scheme' changed to {value}, setting dark mode to {dark}") + self.set_dark_mode(dark) + def on_menu(self, action, param): self.menu_popover.popup() @@ -265,7 +324,8 @@ class gPodderApplication(Gtk.Application): on_send_full_subscriptions=self.window.on_send_full_subscriptions, on_itemExportChannels_activate=self.window.on_itemExportChannels_activate, on_extension_enabled=self.on_extension_enabled, - on_extension_disabled=self.on_extension_disabled) + on_extension_disabled=self.on_extension_disabled, + have_settings_portal=self.have_settings_portal) def on_goto_mygpo(self, action, param): self.window.mygpo_client.open_website() diff --git a/src/gpodder/gtkui/config.py b/src/gpodder/gtkui/config.py index ad93e222..b0f5f246 100644 --- a/src/gpodder/gtkui/config.py +++ b/src/gpodder/gtkui/config.py @@ -154,6 +154,13 @@ class UIConfig(config.Config): setattr(self, name, togglebutton.get_active()) togglebutton.connect('toggled', _togglebutton_toggled) + def connect_gtk_combo_box_text(self, name, combo_text): + combo_text.set_active_id(getattr(self, name)) + + def _combo_box_text_changed(combo): + setattr(self, name, combo.get_active_id()) + combo_text.connect('changed', _combo_box_text_changed) + def connect_gtk_window(self, window, config_prefix, show_window=False): cfg = getattr(self.ui.gtk.state, config_prefix) diff --git a/src/gpodder/gtkui/desktop/preferences.py b/src/gpodder/gtkui/desktop/preferences.py index 4c640ad3..f13472f3 100644 --- a/src/gpodder/gtkui/desktop/preferences.py +++ b/src/gpodder/gtkui/desktop/preferences.py @@ -205,6 +205,21 @@ class gPodderPreferences(BuilderWidget): index = self.video_player_model.get_index(self._config.player.video) self.combo_video_player_app.set_active(index) + self.combo_color_scheme.remove_all() + self.combo_color_scheme.prepend('dark', 'Dark') + self.combo_color_scheme.prepend('light', 'Light') + cs = self._config.ui.gtk.color_scheme + if self.have_settings_portal: + self.combo_color_scheme.prepend('system', 'System') + self.combo_color_scheme.set_active_id(cs) + else: + if cs == 'system': + self.combo_color_scheme.set_active_id('light') + self._config.ui.gtk.color_scheme = 'light' + else: + self.combo_color_scheme.set_active_id(cs) + self._config.connect_gtk_combo_box_text('ui.gtk.color_scheme', self.combo_color_scheme) + self.preferred_youtube_format_model = YouTubeVideoFormatListModel(self._config) self.combobox_preferred_youtube_format.set_model(self.preferred_youtube_format_model) cellrenderer = Gtk.CellRendererText() diff --git a/src/gpodder/gtkui/main.py b/src/gpodder/gtkui/main.py index 5c069016..14a0b7f5 100644 --- a/src/gpodder/gtkui/main.py +++ b/src/gpodder/gtkui/main.py @@ -1486,6 +1486,11 @@ class gPodder(BuilderWidget, dbus.service.Object): self.update_podcast_list_model() elif name == 'ui.gtk.episode_list.columns': self.update_episode_list_columns_visibility() + elif name == 'ui.gtk.color_scheme': + if new_value == 'system': + self.application.read_portal_color_scheme() + else: + self.application.set_dark_mode(new_value == 'dark') elif name == 'limit.downloads.concurrent_max': # Do not allow value to be set below 1 if new_value < 1: