From ea52d62018a14ce129609e207a0eb5c11dd79e10 Mon Sep 17 00:00:00 2001 From: Michael Webster Date: Sun, 5 Apr 2026 19:38:08 -0400 Subject: [PATCH 1/2] nemo-application.c: Move theme init code to libnemo-private. --- libnemo-private/meson.build | 1 + libnemo-private/nemo-theme-utils.c | 227 +++++++++++++++++++++++++++++ libnemo-private/nemo-theme-utils.h | 8 + src/nemo-application.c | 221 +--------------------------- 4 files changed, 238 insertions(+), 219 deletions(-) create mode 100644 libnemo-private/nemo-theme-utils.c create mode 100644 libnemo-private/nemo-theme-utils.h diff --git a/libnemo-private/meson.build b/libnemo-private/meson.build index 47ac9f868..19b4cc167 100644 --- a/libnemo-private/meson.build +++ b/libnemo-private/meson.build @@ -69,6 +69,7 @@ nemo_private_sources = [ 'nemo-signaller.c', 'nemo-thumbnails.c', 'nemo-trash-monitor.c', + 'nemo-theme-utils.c', 'nemo-tree-view-drag-dest.c', 'nemo-ui-utilities.c', 'nemo-undo-manager.c', diff --git a/libnemo-private/nemo-theme-utils.c b/libnemo-private/nemo-theme-utils.c new file mode 100644 index 000000000..e3ea43599 --- /dev/null +++ b/libnemo-private/nemo-theme-utils.c @@ -0,0 +1,227 @@ +#include "nemo-theme-utils.h" +#include "nemo-global-preferences.h" + +#include + +static GtkCssProvider *mandatory_css_provider = NULL; + +static gboolean +css_provider_load_from_resource (GtkCssProvider *provider, + const char *resource_path, + GError **error) +{ + GBytes *data; + gboolean retval; + + data = g_resources_lookup_data (resource_path, 0, error); + if (!data) + return FALSE; + + retval = gtk_css_provider_load_from_data (provider, + g_bytes_get_data (data, NULL), + g_bytes_get_size (data), + error); + g_bytes_unref (data); + + return retval; +} + +static gchar * +load_file_contents_from_resource (const char *resource_path, + GError **error) +{ + GBytes *data; + gchar *retval; + + data = g_resources_lookup_data (resource_path, 0, error); + + if (!data) { + return NULL; + } + + retval = g_strdup ((gchar *) g_bytes_get_data (data, NULL)); + + g_bytes_unref (data); + + return retval; +} + +static void +add_css_provider_at_priority (const gchar *rpath, guint priority) +{ + GtkCssProvider *provider; + GError *error = NULL; + + provider = gtk_css_provider_new (); + + if (!css_provider_load_from_resource (provider, rpath, &error)) + { + g_warning ("Failed to load css file '%s': %s", rpath, error->message); + g_clear_error (&error); + goto out; + } + + gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (provider), + priority); + +out: + g_object_unref (provider); +} + +static void +add_fallback_mandatory_css_provider (const gchar *theme_name) +{ + GtkCssProvider *current_provider; + g_autofree gchar *css = NULL; + g_autofree gchar *init_fallback_css = NULL; + g_autofree gchar *final_fallback_css = NULL; + GError *error = NULL; + + current_provider = gtk_css_provider_get_named (theme_name, NULL); + + css = gtk_css_provider_to_string (current_provider); + + if (!g_strstr_len (css, -1, "nemo")) { + g_warning ("The theme appears to have no nemo support. Adding some..."); + + init_fallback_css = load_file_contents_from_resource ("/org/nemo/nemo-style-fallback-mandatory.css", + &error); + + if (!init_fallback_css) { + g_warning ("Failed to load fallback mandatory css file: %s", error->message); + g_clear_error (&error); + + goto out; + } + } else { + goto out; + } + + if (g_strstr_len (css, -1, "theme_selected_bg_color")) { + final_fallback_css = g_strdup (init_fallback_css); + + goto apply; + } + + /* Some themes don't prefix colors with theme_ - remove this from our fallback css */ + if (g_strstr_len (css, -1, "@define-color selected_bg_color")) { + g_warning ("Replacing theme_selected_bg_color with selected_bg_color"); + final_fallback_css = eel_str_replace_substring (init_fallback_css, + "@theme_", + "@"); + } else { + /* If we can find neither, just bail out */ + g_warning ("Theme lacks both theme_selected_bg_color and selected_bg_color - " + "cannot apply fallback CSS"); + goto out; + } + +apply: + mandatory_css_provider = gtk_css_provider_new (); + + gtk_css_provider_load_from_data (mandatory_css_provider, + final_fallback_css, + -1, + &error); + + if (error) { + g_warning ("Failed to create a fallback provider: %s", error->message); + g_clear_error (&error); + g_clear_object (&mandatory_css_provider); + goto out; + } + + gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (mandatory_css_provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); +out: + ; +} + +static const char *supported_theme_hints[] = { + "mint", + "arc", + "numix", + "matcha" +}; + +static gboolean +is_known_supported_theme (const gchar *theme_name) +{ + gint i; + gchar *name; + gboolean ret; + + name = g_utf8_casefold (theme_name, -1); + ret = FALSE; + + for (i = 0; i < G_N_ELEMENTS (supported_theme_hints); i++) { + gchar *hint; + + hint = g_utf8_casefold (supported_theme_hints[i], -1); + + if (g_strstr_len (name, -1, hint)) { + ret = TRUE; + } + + g_free (hint); + + if (ret) { + break; + } + } + + g_free (name); + + return ret; +} + +static void +process_theme (GtkSettings *gtk_settings) +{ + gchar *theme_name; + + if (mandatory_css_provider != NULL) { + gtk_style_context_remove_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (mandatory_css_provider)); + g_clear_object (&mandatory_css_provider); + } + + g_object_get (gtk_settings, + "gtk-theme-name", &theme_name, + NULL); + + if (!is_known_supported_theme (theme_name)) { + g_warning ("Current gtk theme is not known to have nemo support (%s) - checking...", theme_name); + add_fallback_mandatory_css_provider (theme_name); + } + + gtk_style_context_reset_widgets (gdk_screen_get_default ()); + g_free (theme_name); +} + +void +nemo_theme_utils_init_styles (void) +{ + static gboolean initialized = FALSE; + GtkSettings *gtk_settings; + + if (initialized) + return; + + initialized = TRUE; + + add_css_provider_at_priority ("/org/nemo/nemo-style-fallback.css", + GTK_STYLE_PROVIDER_PRIORITY_FALLBACK); + + add_css_provider_at_priority ("/org/nemo/nemo-style-application.css", + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + + gtk_settings = gtk_settings_get_default (); + + g_signal_connect_swapped (gtk_settings, "notify::gtk-theme-name", + G_CALLBACK (process_theme), gtk_settings); + + process_theme (gtk_settings); +} diff --git a/libnemo-private/nemo-theme-utils.h b/libnemo-private/nemo-theme-utils.h new file mode 100644 index 000000000..d74958d35 --- /dev/null +++ b/libnemo-private/nemo-theme-utils.h @@ -0,0 +1,8 @@ +#ifndef NEMO_THEME_UTILS_H +#define NEMO_THEME_UTILS_H + +#include + +void nemo_theme_utils_init_styles (void); + +#endif /* NEMO_THEME_UTILS_H */ diff --git a/src/nemo-application.c b/src/nemo-application.c index 062d105f7..4bede796f 100644 --- a/src/nemo-application.c +++ b/src/nemo-application.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -76,7 +77,6 @@ #include #include #include -#include #include #define GNOME_DESKTOP_USE_UNSTABLE_API @@ -100,209 +100,6 @@ static NemoApplication *singleton = NULL; /* The saving of the accelerator map was requested */ static gboolean save_of_accel_map_requested = FALSE; -static GtkCssProvider *mandatory_css_provider = NULL; - -static gboolean -css_provider_load_from_resource (GtkCssProvider *provider, - const char *resource_path, - GError **error) -{ - GBytes *data; - gboolean retval; - - data = g_resources_lookup_data (resource_path, 0, error); - if (!data) - return FALSE; - - retval = gtk_css_provider_load_from_data (provider, - g_bytes_get_data (data, NULL), - g_bytes_get_size (data), - error); - g_bytes_unref (data); - - return retval; -} - -static gchar * -load_file_contents_from_resource (const char *resource_path, - GError **error) -{ - GBytes *data; - gchar *retval; - - data = NULL; - - data = g_resources_lookup_data (resource_path, 0, error); - - if (!data) { - return FALSE; - } - - retval = g_strdup ((gchar *) g_bytes_get_data (data, NULL)); - - g_bytes_unref (data); - - return retval; -} - -static void -add_css_provider_at_priority (const gchar *rpath, guint priority) -{ - GtkCssProvider *provider; - GError *error = NULL; - - provider = gtk_css_provider_new (); - - if (!css_provider_load_from_resource (provider, rpath, &error)) - { - g_warning ("Failed to load fallback css file: %s", error->message); - if (error->message != NULL) - g_error_free (error); - goto out; - } - - gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), - GTK_STYLE_PROVIDER (provider), - priority); - -out: - g_object_unref (provider); -} - -static void -add_fallback_mandatory_css_provider (const gchar *theme_name) -{ - GtkCssProvider *current_provider; - gchar *css, *init_fallback_css, *final_fallback_css; - GError *error = NULL; - - current_provider = gtk_css_provider_get_named (theme_name, NULL); - - css = gtk_css_provider_to_string (current_provider); - - init_fallback_css = NULL; - - if (!g_strstr_len (css, -1, "nemo")) { - g_warning ("The theme appears to have no nemo support. Adding some..."); - - init_fallback_css = load_file_contents_from_resource ("/org/nemo/nemo-style-fallback-mandatory.css", - &error); - - if (!init_fallback_css) { - g_warning ("Failed to load fallback mandatory css file: %s", error->message); - g_clear_error (&error); - - goto out; - } - } else { - goto out; - } - - final_fallback_css = NULL; - - /* Our fallback uses @theme_ prefixed names for colors. If the active theme does also, skip - * to apply */ - if (g_strstr_len (css, -1, "theme_selected_bg_color")) { - final_fallback_css = g_strdup (init_fallback_css); - - goto apply; - } - - /* Some themes don't prefix colors with theme_ - remove this from our fallback css */ - if (g_strstr_len (css, -1, "@define-color selected_bg_color")) { - g_warning ("Replacing theme_selected_bg_color with selected_bg_color"); - final_fallback_css = eel_str_replace_substring (init_fallback_css, - "@theme_", - "@"); - } else { - /* If we can find neither, just bail out */ - goto out; - } - -apply: - g_free (init_fallback_css); - - mandatory_css_provider = gtk_css_provider_new (); - - gtk_css_provider_load_from_data (mandatory_css_provider, - final_fallback_css, - -1, - &error); - - if (error) { - g_warning ("Failed to create a fallback provider: %s", error->message); - g_clear_error (&error); - goto out; - } - - gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), - GTK_STYLE_PROVIDER (mandatory_css_provider), - GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); -out: - g_free (css); -} - -static const char *supported_theme_hints[] = { - "mint", - "arc", - "numix", - "matcha" -}; - -static gboolean -is_known_supported_theme (const gchar *theme_name) -{ - gint i; - gchar *name; - gboolean ret; - - name = g_utf8_casefold (theme_name, -1); - ret = FALSE; - - for (i = 0; i < G_N_ELEMENTS (supported_theme_hints); i++) { - gchar *hint; - - hint = g_utf8_casefold (supported_theme_hints[i], -1); - - if (g_strstr_len (name, -1, hint)) { - ret = TRUE; - } - - g_free (hint); - - if (ret) { - break; - } - } - - g_free (name); - - return ret; -} - -static void -process_system_theme (GtkSettings *gtk_settings) -{ - gchar *theme_name; - - if (mandatory_css_provider != NULL) { - gtk_style_context_remove_provider_for_screen (gdk_screen_get_default (), GTK_STYLE_PROVIDER (mandatory_css_provider)); - g_clear_object (&mandatory_css_provider); - } - - g_object_get (gtk_settings, - "gtk-theme-name", &theme_name, - NULL); - - if (!is_known_supported_theme (theme_name)) { - g_warning ("Current gtk theme is not known to have nemo support (%s) - checking...", theme_name); - add_fallback_mandatory_css_provider (theme_name); - } - - gtk_style_context_reset_widgets (gdk_screen_get_default ()); - g_free (theme_name); -} - static void init_icons_and_styles (void) { @@ -314,20 +111,7 @@ init_icons_and_styles (void) NEMO_STATUSBAR_ICON_SIZE, NEMO_STATUSBAR_ICON_SIZE); - add_css_provider_at_priority ("/org/nemo/nemo-style-fallback.css", - GTK_STYLE_PROVIDER_PRIORITY_FALLBACK); - - add_css_provider_at_priority ("/org/nemo/nemo-style-application.css", - GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - - GtkSettings *gtk_settings = gtk_settings_get_default (); - /* We create our own 'runtime theme' when we encounter one that doesn't - * include nemo support. We must listen for the theme changing so this - * customization can be removed (then re-applied only if necessary for the - * new theme). */ - g_signal_connect_swapped (gtk_settings, "notify::gtk-theme-name", G_CALLBACK (process_system_theme), gtk_settings); - - process_system_theme (gtk_settings); + nemo_theme_utils_init_styles (); } static gboolean @@ -631,7 +415,6 @@ nemo_application_quit_mainloop (GApplication *app) nemo_icon_info_clear_caches (); save_accel_map (NULL); g_object_unref (NEMO_APPLICATION (app)->undo_manager); - g_clear_object (&mandatory_css_provider); nemo_application_notify_unmount_done (NEMO_APPLICATION (app), NULL); From d7be9b5b26e82130eb457462e21a075d5555df1f Mon Sep 17 00:00:00 2001 From: Michael Webster Date: Sun, 5 Apr 2026 19:49:42 -0400 Subject: [PATCH 2/2] nemo-desktop: Allow adjustment to label shadow. Add two settings: - desktop-text-shadow: Three levels of shadow (normal, darker, darkest) - normal being the traditional amount. - desktop-text-shadow-use-theme: If true, and if the theme supports it, allow the GTK theme to control desktop style, including text shadow. Nemo has always checked for theme support at startup and during theme changes, to ensure custom widgets/features worked correctly for users (such as inactive-pane shading). We add another check for 'nemo-desktop' there. If the theme lacks any support, nemo provides it like before, using the shadow-level setting. --- gresources/nemo-desktop-preferences.glade | 437 +++++++++++++++---- gresources/nemo-style-application.css | 29 +- gresources/nemo-style-desktop.css | 45 ++ gresources/nemo.gresource.xml | 1 + libnemo-extension/nemo-desktop-preferences.c | 180 +++++++- libnemo-private/nemo-global-preferences.h | 2 + libnemo-private/nemo-icon-container.c | 42 ++ libnemo-private/nemo-theme-utils.c | 67 +++ libnemo-private/org.nemo.gschema.xml | 15 + 9 files changed, 700 insertions(+), 118 deletions(-) create mode 100644 gresources/nemo-style-desktop.css diff --git a/gresources/nemo-desktop-preferences.glade b/gresources/nemo-desktop-preferences.glade index f0e6acdcf..0d3442d56 100644 --- a/gresources/nemo-desktop-preferences.glade +++ b/gresources/nemo-desktop-preferences.glade @@ -1,37 +1,37 @@ - + 50 200 100 - 10 - 10 + 10 + 10 75 200 100 - 10 - 10 + 10 + 10 True - False - 6 + False + 6 vertical 5 True - False - 10 + False + 10 10 True - False + False start Desktop Layout @@ -47,9 +47,9 @@ True - False + False end - False + False No desktop icons Show desktop icons on primary monitor only @@ -73,7 +73,7 @@ True - False + False Desktop Icons 0 @@ -89,47 +89,47 @@ True - False - 10 - 0 - 0 - in + False + 10 + 0 + 0 + in True - False + False True - False + False vertical True - False + False vertical True - False + False True - True + True False False True - False - 20 - 20 - 5 + False + 20 + 20 + 5 20 True - False + False Computer @@ -142,13 +142,13 @@ True - True + True center False True - end + end 1 @@ -173,12 +173,12 @@ True - False + False vertical True - False + False False @@ -189,25 +189,25 @@ True - False + False True - True + True False False True - False - 20 - 20 - 5 + False + 20 + 20 + 5 20 True - False + False Home @@ -220,13 +220,13 @@ True - True + True center False True - end + end 1 @@ -251,12 +251,12 @@ True - False + False vertical True - False + False False @@ -267,25 +267,25 @@ True - False + False True - True + True False False True - False - 20 - 20 - 5 + False + 20 + 20 + 5 20 True - False + False Trash @@ -298,13 +298,13 @@ True - True + True center False True - end + end 1 @@ -329,12 +329,12 @@ True - False + False vertical True - False + False False @@ -345,25 +345,25 @@ True - False + False True - True + True False False True - False - 20 - 20 - 5 + False + 20 + 20 + 5 20 True - False + False Mounted Drives @@ -376,13 +376,13 @@ True - True + True center False True - end + end 1 @@ -407,12 +407,12 @@ True - False + False vertical True - False + False False @@ -423,25 +423,25 @@ True - False + False True - True + True False False True - False - 20 - 20 - 5 + False + 20 + 20 + 5 20 True - False + False Network @@ -454,13 +454,13 @@ True - True + True center False True - end + end 1 @@ -499,7 +499,7 @@ True - False + False Options 0 @@ -515,47 +515,47 @@ True - False - 2 - 0 - 0 - in + False + 10 + 0 + 0 + in True - False + False True - False + False vertical True - False + False vertical True - False + False True - True + True False False True - False - 20 - 20 - 5 + False + 20 + 20 + 5 20 True - False + False Show icons from missing monitors @@ -568,13 +568,13 @@ True - True + True center False True - end + end 1 @@ -610,5 +610,258 @@ 4 + + + True + False + Text Shadow + 0 + + + + + + False + True + 5 + + + + + True + False + 10 + 0 + 0 + in + + + True + False + vertical + + + True + False + 8 + 8 + True + spread + + + True + True + False + False + + + 160 + 60 + True + False + 0 + + + True + False + center + 8 + 8 + Desktop label preview + center + True + 15 + + + + + + + + + True + True + 0 + + + + + True + True + False + False + shadow_radio_normal + + + 160 + 60 + True + False + 0 + + + True + False + center + 8 + 8 + Desktop label preview + center + True + 15 + + + + + + + + + True + True + 1 + + + + + True + True + False + False + shadow_radio_normal + + + 160 + 60 + True + False + 0 + + + True + False + center + 8 + 8 + Desktop label preview + center + True + 15 + + + + + + + + + True + True + 2 + + + + + False + True + 0 + + + + + True + False + + + False + True + 1 + + + + + True + False + + + True + True + False + False + + + True + False + 20 + 20 + 5 + 20 + + + True + False + Use theme if supported + + + False + True + 6 + 0 + + + + + True + True + center + + + False + True + end + 1 + + + + + + + + + False + True + 2 + + + + + + + + False + True + 6 + + diff --git a/gresources/nemo-style-application.css b/gresources/nemo-style-application.css index ea7b95283..7f1a34bde 100644 --- a/gresources/nemo-style-application.css +++ b/gresources/nemo-style-application.css @@ -1,4 +1,5 @@ -/* Desktop text stuff */ +/* Desktop window structure — always applied regardless of theme. + * The desktop window must be transparent so the wallpaper shows through. */ .nemo-window.nemo-desktop-window notebook, .nemo-window.nemo-desktop-window paned { @@ -11,37 +12,17 @@ box-shadow: none; } -NemoDesktopWindow GtkPaned { - background-color: transparent; -} - .nemo-canvas-item { border-radius: 3px; } +/* Custom widget property — controls nemo behavior, not appearance. */ .nemo-desktop { -NemoIconContainer-activate-prelight-icon-label: true; } -.nemo-desktop.nemo-canvas-item { - color: #eeeeee; - text-shadow: 1px 1px alpha(black, 0.8); -} - -.nemo-desktop.nemo-canvas-item:hover { - background-color: alpha(black, 0.5); - background-image: none; - text-shadow: none; -} - -.nemo-desktop.nemo-canvas-item:selected { - background-color: alpha(@theme_selected_bg_color, 0.8); - background-image: none; - text-shadow: none; - color: #f5f5f5; -} - -/* EelEditableLabel (icon labels) */ +/* EelEditableLabel (inline rename entry on desktop). + * Always applied so the rename box stays legible over any wallpaper. */ .nemo-desktop.view .entry, .nemo-desktop.view .entry:active, .nemo-desktop.view .entry:focus, diff --git a/gresources/nemo-style-desktop.css b/gresources/nemo-style-desktop.css new file mode 100644 index 000000000..12658bedf --- /dev/null +++ b/gresources/nemo-style-desktop.css @@ -0,0 +1,45 @@ +/* Desktop icon label text shadows. + * + * Nemo applies one of these classes (shadow-normal / shadow-darker / + * shadow-darkest) to the icon container based on the user's preference. + * If "Use GTK theme" is enabled and the current theme provides its own + * .nemo-desktop styling, this entire stylesheet is not loaded — the + * theme controls text appearance directly. + */ + +/* Shadow variant: Normal (traditional nemo default) */ +.nemo-desktop.nemo-canvas-item.shadow-normal { + color: #eeeeee; + text-shadow: 1px 1px alpha(black, 0.8); +} + +/* Shadow variant: Darker */ +.nemo-desktop.nemo-canvas-item.shadow-darker { + color: #eeeeee; + text-shadow: 1px 1px black; +} + +/* Shadow variant: Darkest */ +.nemo-desktop.nemo-canvas-item.shadow-darkest { + color: #eeeeee; + text-shadow: 1px 1px black, 1px 0px alpha(black, 0.6), 0px 1px alpha(black, 0.6); +} + +/* Hover for shadow modes */ +.nemo-desktop.nemo-canvas-item.shadow-normal:hover, +.nemo-desktop.nemo-canvas-item.shadow-darker:hover, +.nemo-desktop.nemo-canvas-item.shadow-darkest:hover { + background-color: alpha(black, 0.5); + background-image: none; + text-shadow: none; +} + +/* Selected for shadow modes */ +.nemo-desktop.nemo-canvas-item.shadow-normal:selected, +.nemo-desktop.nemo-canvas-item.shadow-darker:selected, +.nemo-desktop.nemo-canvas-item.shadow-darkest:selected { + background-color: alpha(@theme_selected_bg_color, 0.8); + background-image: none; + text-shadow: none; + color: #f5f5f5; +} diff --git a/gresources/nemo.gresource.xml b/gresources/nemo.gresource.xml index 7d0870e7f..2b4ee3d61 100644 --- a/gresources/nemo.gresource.xml +++ b/gresources/nemo.gresource.xml @@ -26,5 +26,6 @@ nemo-style-fallback.css nemo-style-fallback-mandatory.css nemo-style-application.css + nemo-style-desktop.css diff --git a/libnemo-extension/nemo-desktop-preferences.c b/libnemo-extension/nemo-desktop-preferences.c index 6b7352224..f3daf9d0f 100644 --- a/libnemo-extension/nemo-desktop-preferences.c +++ b/libnemo-extension/nemo-desktop-preferences.c @@ -3,12 +3,32 @@ #include #include #include +#include #include "nemo-desktop-preferences.h" +#define NUM_SHADOW_OPTIONS 3 + +static const gchar *shadow_options[NUM_SHADOW_OPTIONS] = { + "normal", + "darker", + "darkest", +}; + +static const gchar *preview_bg_css = + ".shadow-preview-bg {" + " background-image: linear-gradient(to bottom, #7ba4d4, #d4e6f7);" + "}"; + typedef struct { GtkBuilder *builder; GSettings *desktop_settings; + GtkCssProvider *desktop_css_provider; + GtkCssProvider *preview_bg_provider; + GtkWidget *shadow_radios[NUM_SHADOW_OPTIONS]; + GtkWidget *shadow_labels[NUM_SHADOW_OPTIONS]; + gulong shadow_setting_handler; + gulong font_changed_handler; } NemoDesktopPreferencesPrivate; struct _NemoDesktopPreferences @@ -43,6 +63,141 @@ bind_builder_string_combo (GtkBuilder *builder, "active-id", G_SETTINGS_BIND_DEFAULT); } +static void +update_preview_fonts (NemoDesktopPreferencesPrivate *priv) +{ + gchar *font_str; + PangoFontDescription *font_desc; + gint i; + + font_str = g_settings_get_string (priv->desktop_settings, "font"); + font_desc = pango_font_description_from_string (font_str); + g_free (font_str); + + for (i = 0; i < NUM_SHADOW_OPTIONS; i++) { + PangoAttrList *attrs = pango_attr_list_new (); + pango_attr_list_insert (attrs, pango_attr_font_desc_new (font_desc)); + gtk_label_set_attributes (GTK_LABEL (priv->shadow_labels[i]), attrs); + pango_attr_list_unref (attrs); + } + + pango_font_description_free (font_desc); +} + +static void +on_shadow_button_toggled (GtkToggleButton *button, gpointer user_data) +{ + NemoDesktopPreferencesPrivate *priv = user_data; + const gchar *id; + + if (!gtk_toggle_button_get_active (button)) + return; + + id = g_object_get_data (G_OBJECT (button), "shadow-id"); + g_settings_set_string (priv->desktop_settings, "desktop-text-shadow", id); +} + +static void +on_shadow_setting_changed (GSettings *settings, + const gchar *key, + gpointer user_data) +{ + NemoDesktopPreferences *preferences = NEMO_DESKTOP_PREFERENCES (user_data); + NemoDesktopPreferencesPrivate *priv = preferences->priv; + gchar *current; + gint i; + + current = g_settings_get_string (priv->desktop_settings, "desktop-text-shadow"); + + for (i = 0; i < NUM_SHADOW_OPTIONS; i++) { + if (g_strcmp0 (current, shadow_options[i]) == 0) { + g_signal_handlers_block_by_func (priv->shadow_radios[i], + on_shadow_button_toggled, priv); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->shadow_radios[i]), TRUE); + g_signal_handlers_unblock_by_func (priv->shadow_radios[i], + on_shadow_button_toggled, priv); + break; + } + } + + g_free (current); +} + +static void +on_desktop_font_changed (GSettings *settings, + const gchar *key, + gpointer user_data) +{ + NemoDesktopPreferences *preferences = NEMO_DESKTOP_PREFERENCES (user_data); + update_preview_fonts (preferences->priv); +} + +static void +add_provider_to_widget (GtkWidget *widget, GtkCssProvider *provider) +{ + gtk_style_context_add_provider (gtk_widget_get_style_context (widget), + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); +} + +static void +setup_shadow_preview_buttons (NemoDesktopPreferences *preferences) +{ + NemoDesktopPreferencesPrivate *priv = preferences->priv; + gchar *current; + gint i; + + priv->desktop_css_provider = gtk_css_provider_new (); + gtk_css_provider_load_from_resource (priv->desktop_css_provider, + "/org/nemo/nemo-style-desktop.css"); + + priv->preview_bg_provider = gtk_css_provider_new (); + gtk_css_provider_load_from_data (priv->preview_bg_provider, preview_bg_css, -1, NULL); + + current = g_settings_get_string (priv->desktop_settings, "desktop-text-shadow"); + + for (i = 0; i < NUM_SHADOW_OPTIONS; i++) { + gchar *name; + + name = g_strdup_printf ("shadow_radio_%s", shadow_options[i]); + priv->shadow_radios[i] = GTK_WIDGET (gtk_builder_get_object (priv->builder, name)); + g_free (name); + + name = g_strdup_printf ("shadow_frame_%s", shadow_options[i]); + add_provider_to_widget (GTK_WIDGET (gtk_builder_get_object (priv->builder, name)), + priv->preview_bg_provider); + g_free (name); + + name = g_strdup_printf ("shadow_label_%s", shadow_options[i]); + priv->shadow_labels[i] = GTK_WIDGET (gtk_builder_get_object (priv->builder, name)); + g_free (name); + + add_provider_to_widget (priv->shadow_labels[i], priv->desktop_css_provider); + + g_object_set_data (G_OBJECT (priv->shadow_radios[i]), "shadow-id", + (gpointer) shadow_options[i]); + + g_signal_connect (priv->shadow_radios[i], "toggled", + G_CALLBACK (on_shadow_button_toggled), priv); + + if (g_strcmp0 (current, shadow_options[i]) == 0) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->shadow_radios[i]), TRUE); + } + } + + g_free (current); + + update_preview_fonts (priv); + + priv->shadow_setting_handler = + g_signal_connect (priv->desktop_settings, "changed::desktop-text-shadow", + G_CALLBACK (on_shadow_setting_changed), preferences); + + priv->font_changed_handler = + g_signal_connect (priv->desktop_settings, "changed::font", + G_CALLBACK (on_desktop_font_changed), preferences); +} + static void nemo_desktop_preferences_init (NemoDesktopPreferences *preferences) { @@ -96,6 +251,13 @@ nemo_desktop_preferences_init (NemoDesktopPreferences *preferences) "orphan_switch", "show-orphaned-desktop-icons"); + bind_builder_bool (priv->builder, + priv->desktop_settings, + "use_theme_switch", + "desktop-text-shadow-use-theme"); + + setup_shadow_preview_buttons (preferences); + gtk_widget_show_all (GTK_WIDGET (preferences)); } @@ -103,9 +265,23 @@ static void nemo_desktop_preferences_dispose (GObject *object) { NemoDesktopPreferences *preferences = NEMO_DESKTOP_PREFERENCES (object); + NemoDesktopPreferencesPrivate *priv = preferences->priv; + + if (priv->desktop_settings != NULL) { + if (priv->shadow_setting_handler > 0) { + g_signal_handler_disconnect (priv->desktop_settings, priv->shadow_setting_handler); + priv->shadow_setting_handler = 0; + } + if (priv->font_changed_handler > 0) { + g_signal_handler_disconnect (priv->desktop_settings, priv->font_changed_handler); + priv->font_changed_handler = 0; + } + } - g_clear_object (&preferences->priv->builder); - g_clear_object (&preferences->priv->desktop_settings); + g_clear_object (&priv->desktop_css_provider); + g_clear_object (&priv->preview_bg_provider); + g_clear_object (&priv->builder); + g_clear_object (&priv->desktop_settings); G_OBJECT_CLASS (nemo_desktop_preferences_parent_class)->dispose (object); } diff --git a/libnemo-private/nemo-global-preferences.h b/libnemo-private/nemo-global-preferences.h index 91f404854..8b593cf0f 100644 --- a/libnemo-private/nemo-global-preferences.h +++ b/libnemo-private/nemo-global-preferences.h @@ -226,6 +226,8 @@ typedef enum #define NEMO_PREFERENCES_DESKTOP_VOLUMES_VISIBLE "volumes-visible" #define NEMO_PREFERENCES_DESKTOP_NETWORK_VISIBLE "network-icon-visible" #define NEMO_PREFERENCES_DESKTOP_BACKGROUND_FADE "background-fade" +#define NEMO_PREFERENCES_DESKTOP_TEXT_SHADOW "desktop-text-shadow" +#define NEMO_PREFERENCES_DESKTOP_TEXT_SHADOW_USE_THEME "desktop-text-shadow-use-theme" #define NEMO_PREFERENCES_DESKTOP_IGNORED_DESKTOP_HANDLERS "ignored-desktop-handlers" /* bulk rename utility */ diff --git a/libnemo-private/nemo-icon-container.c b/libnemo-private/nemo-icon-container.c index e35283290..1903bf5cb 100644 --- a/libnemo-private/nemo-icon-container.c +++ b/libnemo-private/nemo-icon-container.c @@ -7152,6 +7152,38 @@ nemo_icon_container_get_is_desktop (NemoIconContainer *container) return container->details->is_desktop; } +static const gchar *shadow_class_names[] = { + "shadow-normal", + "shadow-darker", + "shadow-darkest" +}; + +static void +update_desktop_shadow_class (NemoIconContainer *container) +{ + GtkStyleContext *context; + gchar *setting, *class_name; + gint i; + + context = gtk_widget_get_style_context (GTK_WIDGET (container)); + + for (i = 0; i < G_N_ELEMENTS (shadow_class_names); i++) { + gtk_style_context_remove_class (context, shadow_class_names[i]); + } + + /* The class is always applied. When the user has opted into theme-driven + * styling and the theme supports it, nemo-theme-utils skips loading the + * shadow stylesheet, so the class simply matches no rules. */ + setting = g_settings_get_string (nemo_desktop_preferences, + NEMO_PREFERENCES_DESKTOP_TEXT_SHADOW); + class_name = g_strdup_printf ("shadow-%s", setting); + gtk_style_context_add_class (context, class_name); + g_free (class_name); + g_free (setting); + + gtk_widget_queue_draw (GTK_WIDGET (container)); +} + void nemo_icon_container_set_is_desktop (NemoIconContainer *container, gboolean is_desktop) @@ -7166,6 +7198,9 @@ nemo_icon_container_set_is_desktop (NemoIconContainer *container, g_signal_handlers_disconnect_by_func (nemo_desktop_preferences, text_ellipsis_limit_changed_container_callback, container); + g_signal_handlers_disconnect_by_func (nemo_desktop_preferences, + update_desktop_shadow_class, + container); if (is_desktop) { GtkStyleContext *context; @@ -7173,10 +7208,17 @@ nemo_icon_container_set_is_desktop (NemoIconContainer *container, context = gtk_widget_get_style_context (GTK_WIDGET (container)); gtk_style_context_add_class (context, "nemo-desktop"); + update_desktop_shadow_class (container); + g_signal_connect_swapped (nemo_desktop_preferences, "changed::" NEMO_PREFERENCES_DESKTOP_TEXT_ELLIPSIS_LIMIT, G_CALLBACK (text_ellipsis_limit_changed_container_callback), container); + + g_signal_connect_swapped (nemo_desktop_preferences, + "changed::" NEMO_PREFERENCES_DESKTOP_TEXT_SHADOW, + G_CALLBACK (update_desktop_shadow_class), + container); } else { g_signal_connect_swapped (nemo_icon_view_preferences, "changed::" NEMO_PREFERENCES_ICON_VIEW_TEXT_ELLIPSIS_LIMIT, diff --git a/libnemo-private/nemo-theme-utils.c b/libnemo-private/nemo-theme-utils.c index e3ea43599..916f91c85 100644 --- a/libnemo-private/nemo-theme-utils.c +++ b/libnemo-private/nemo-theme-utils.c @@ -4,6 +4,8 @@ #include static GtkCssProvider *mandatory_css_provider = NULL; +static GtkCssProvider *desktop_css_provider = NULL; +static gboolean theme_has_desktop_support = FALSE; static gboolean css_provider_load_from_resource (GtkCssProvider *provider, @@ -177,6 +179,57 @@ is_known_supported_theme (const gchar *theme_name) return ret; } +static void +check_desktop_support (const gchar *theme_name) +{ + GtkCssProvider *provider; + gchar *css; + + provider = gtk_css_provider_get_named (theme_name, NULL); + css = gtk_css_provider_to_string (provider); + + theme_has_desktop_support = (g_strstr_len (css, -1, "nemo-desktop") != NULL); + + g_free (css); +} + +static void +update_desktop_provider (void) +{ + gboolean use_theme; + gboolean should_load; + + use_theme = g_settings_get_boolean (nemo_desktop_preferences, + NEMO_PREFERENCES_DESKTOP_TEXT_SHADOW_USE_THEME); + + /* Load our desktop CSS unless the user opted into the theme's own + * desktop styling AND the theme actually provides it. */ + should_load = !(use_theme && theme_has_desktop_support); + + if (should_load && desktop_css_provider == NULL) { + GtkCssProvider *provider; + GError *error = NULL; + + provider = gtk_css_provider_new (); + + if (!css_provider_load_from_resource (provider, "/org/nemo/nemo-style-desktop.css", &error)) { + g_warning ("Failed to load desktop css: %s", error->message); + g_clear_error (&error); + g_object_unref (provider); + return; + } + + gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + desktop_css_provider = provider; + } else if (!should_load && desktop_css_provider != NULL) { + gtk_style_context_remove_provider_for_screen (gdk_screen_get_default (), + GTK_STYLE_PROVIDER (desktop_css_provider)); + g_clear_object (&desktop_css_provider); + } +} + static void process_theme (GtkSettings *gtk_settings) { @@ -197,10 +250,20 @@ process_theme (GtkSettings *gtk_settings) add_fallback_mandatory_css_provider (theme_name); } + check_desktop_support (theme_name); + update_desktop_provider (); + gtk_style_context_reset_widgets (gdk_screen_get_default ()); g_free (theme_name); } +static void +on_use_theme_setting_changed (GSettings *settings, const gchar *key, gpointer user_data) +{ + update_desktop_provider (); + gtk_style_context_reset_widgets (gdk_screen_get_default ()); +} + void nemo_theme_utils_init_styles (void) { @@ -223,5 +286,9 @@ nemo_theme_utils_init_styles (void) g_signal_connect_swapped (gtk_settings, "notify::gtk-theme-name", G_CALLBACK (process_theme), gtk_settings); + g_signal_connect (nemo_desktop_preferences, + "changed::" NEMO_PREFERENCES_DESKTOP_TEXT_SHADOW_USE_THEME, + G_CALLBACK (on_use_theme_setting_changed), NULL); + process_theme (gtk_settings); } diff --git a/libnemo-private/org.nemo.gschema.xml b/libnemo-private/org.nemo.gschema.xml index abc2814d4..49d01653b 100644 --- a/libnemo-private/org.nemo.gschema.xml +++ b/libnemo-private/org.nemo.gschema.xml @@ -626,6 +626,21 @@ Fade the background on change If set to true, then Nemo will use a fade effect to change the desktop background. + + + + + + + 'normal' + Desktop icon text shadow level + Controls the text shadow for desktop icon labels. Values: "normal", "darker", "darkest". + + + false + Use GTK theme for desktop icon text styling + If true and the current GTK theme provides desktop icon styling, use the theme instead of the built-in shadow setting. +