From c32e803679b3de2ba089b3d74016c9c4c3f995b2 Mon Sep 17 00:00:00 2001 From: Jehan Date: Thu, 25 Jan 2024 18:16:15 +0100 Subject: [PATCH] app, themes: merge the Gray and Default themes and add theme color scheme concept. Until now, we were following a similar concept of color schemes as what most OS are doing. For instance, Freedesktop recently introduced a tri-state color scheme of "Prefer Light", "Prefer Dark" and "Default", the latter being either whatever the software prefers (e.g. we prefer either Dark or Gray for graphics software usually) or what the system prefers. See #8675. Until now, with GTK, we only had a boolean "prefer dark" setting through the "gtk-application-prefer-dark-theme" settings. There is not even a "prefer light". Nevertheless for graphics application, there is clearly a third case (fourth if we added a "follow system color preferences" which we don't implement for now): gray mode and in particular middle gray. Having a middle gray UI is often considered a necessity when working on colors in order to protect our perception of color from being influenced by surrounding UI. To fill this need, we were proposing a Default vs. a Gray theme in GIMP, but this was a bit confusing and felt illogical, as discussed on IRC some time ago. Also depending on whether you chose "prefer dark" or not for the gray theme, this one was itself 2 themes, which made things odd and harder to work on. Instead this commit: - adds a color scheme concept in GIMP with 3 variants so far: light, gray and dark. A possible fourth (future) variant might be to follow the system preference (do all OS provide such a queriable option?). - Our Gray theme is merged into Default (as the gray color scheme variant). - Custom themes can add the following CSS files: gimp-light.css, gimp-gray.css, gimp-dark.css which are the base file for their respective scheme. gimp.css is still used as a fallback though it is not necessary (our own Default theme does not provide a gimp.css anymore). Custom themes don't have to provide all 3 variants. A theme can just provide one or 2 variants if it only wants to support 1 or 2 use cases. --- app/config/config-enums.c | 31 +++++ app/config/config-enums.h | 14 +++ app/config/gimpguiconfig.c | 21 ++-- app/config/gimpguiconfig.h | 2 +- app/config/gimprc-blurbs.h | 3 + app/dialogs/preferences-dialog.c | 7 +- app/gui/themes.c | 108 +++++++++++++++--- app/widgets/gimpwidgets-utils.c | 2 +- .../{Gray/gimp.css => Default/gimp-gray.css} | 2 +- themes/Default/{gimp.css => gimp-light.css} | 0 themes/Default/meson.build | 2 +- themes/meson.build | 1 - 12 files changed, 162 insertions(+), 31 deletions(-) rename themes/{Gray/gimp.css => Default/gimp-gray.css} (98%) rename themes/Default/{gimp.css => gimp-light.css} (100%) diff --git a/app/config/config-enums.c b/app/config/config-enums.c index 313b5fcd58..fbd72fb89a 100644 --- a/app/config/config-enums.c +++ b/app/config/config-enums.c @@ -387,6 +387,37 @@ gimp_zoom_quality_get_type (void) return type; } +GType +gimp_theme_scheme_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_THEME_LIGHT, "GIMP_THEME_LIGHT", "light" }, + { GIMP_THEME_GRAY, "GIMP_THEME_GRAY", "gray" }, + { GIMP_THEME_DARK, "GIMP_THEME_DARK", "dark" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_THEME_LIGHT, NC_("theme-scheme", "Light Colors"), NULL }, + { GIMP_THEME_GRAY, NC_("theme-scheme", "Middle Gray"), NULL }, + { GIMP_THEME_DARK, NC_("theme-scheme", "Dark Colors"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpThemeScheme", values); + gimp_type_set_translation_context (type, "theme-scheme"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + /* Generated data ends here */ diff --git a/app/config/config-enums.h b/app/config/config-enums.h index 73bc899c6a..e06d90fbd4 100644 --- a/app/config/config-enums.h +++ b/app/config/config-enums.h @@ -164,5 +164,19 @@ typedef enum GIMP_ZOOM_QUALITY_HIGH /*< desc="High" >*/ } GimpZoomQuality; +#define GIMP_TYPE_THEME_SCHEME (gimp_theme_scheme_get_type ()) + +GType gimp_theme_scheme_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_THEME_LIGHT, /*< desc="Light Colors" >*/ + GIMP_THEME_GRAY, /*< desc="Middle Gray" >*/ + GIMP_THEME_DARK, /*< desc="Dark Colors" >*/ + /* TODO: it might be interesting eventually to add a GIMP_THEME_SYSTEM + * following up the system-wide color scheme preference. See #8675. + */ +} GimpThemeScheme; + #endif /* __CONFIG_ENUMS_H__ */ diff --git a/app/config/gimpguiconfig.c b/app/config/gimpguiconfig.c index a362ec420e..a3eb0d92f3 100644 --- a/app/config/gimpguiconfig.c +++ b/app/config/gimpguiconfig.c @@ -70,7 +70,7 @@ enum PROP_TOOLBOX_GROUPS, PROP_THEME_PATH, PROP_THEME, - PROP_PREFER_DARK_THEME, + PROP_THEME_SCHEME, PROP_OVERRIDE_THEME_ICON_SIZE, PROP_CUSTOM_ICON_SIZE, PROP_ICON_THEME_PATH, @@ -315,11 +315,12 @@ gimp_gui_config_class_init (GimpGuiConfigClass *klass) THEME_BLURB, GIMP_CONFIG_DEFAULT_THEME, GIMP_PARAM_STATIC_STRINGS); - GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_PREFER_DARK_THEME, - "prefer-dark-theme", - "Prefer Dark Theme", - THEME_BLURB, - TRUE, + GIMP_CONFIG_PROP_ENUM (object_class, PROP_THEME_SCHEME, + "theme-color-scheme", + "Theme's Color Scheme", + THEME_SCHEME_BLURB, + GIMP_TYPE_THEME_SCHEME, + GIMP_THEME_DARK, GIMP_PARAM_STATIC_STRINGS); GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_OVERRIDE_THEME_ICON_SIZE, "override-theme-icon-size", @@ -683,8 +684,8 @@ gimp_gui_config_set_property (GObject *object, g_free (gui_config->theme); gui_config->theme = g_value_dup_string (value); break; - case PROP_PREFER_DARK_THEME: - gui_config->prefer_dark_theme = g_value_get_boolean (value); + case PROP_THEME_SCHEME: + gui_config->theme_scheme = g_value_get_enum (value); break; case PROP_OVERRIDE_THEME_ICON_SIZE: gui_config->override_icon_size = g_value_get_boolean (value); @@ -865,8 +866,8 @@ gimp_gui_config_get_property (GObject *object, case PROP_THEME: g_value_set_string (value, gui_config->theme); break; - case PROP_PREFER_DARK_THEME: - g_value_set_boolean (value, gui_config->prefer_dark_theme); + case PROP_THEME_SCHEME: + g_value_set_enum (value, gui_config->theme_scheme); break; case PROP_OVERRIDE_THEME_ICON_SIZE: g_value_set_boolean (value, gui_config->override_icon_size); diff --git a/app/config/gimpguiconfig.h b/app/config/gimpguiconfig.h index 9ad7ec6030..e71d61fad5 100644 --- a/app/config/gimpguiconfig.h +++ b/app/config/gimpguiconfig.h @@ -64,7 +64,7 @@ struct _GimpGuiConfig gboolean toolbox_groups; gchar *theme_path; gchar *theme; - gboolean prefer_dark_theme; + GimpThemeScheme theme_scheme; gchar *icon_theme_path; gchar *icon_theme; gboolean prefer_symbolic_icons; diff --git a/app/config/gimprc-blurbs.h b/app/config/gimprc-blurbs.h index de39f998eb..4361821e26 100644 --- a/app/config/gimprc-blurbs.h +++ b/app/config/gimprc-blurbs.h @@ -546,6 +546,9 @@ _("Sets the folder for temporary storage. Files will appear here " \ #define THEME_BLURB \ _("The name of the theme to use.") +#define THEME_SCHEME_BLURB \ +_("Chooses the color scheme variant of the theme.") + #define THEME_PATH_BLURB \ "Sets the theme search path." diff --git a/app/dialogs/preferences-dialog.c b/app/dialogs/preferences-dialog.c index 75f3769fc3..4f89ad7e57 100644 --- a/app/dialogs/preferences-dialog.c +++ b/app/dialogs/preferences-dialog.c @@ -2115,9 +2115,10 @@ prefs_dialog_new (Gimp *gimp, G_CALLBACK (prefs_theme_select_callback), gimp); - prefs_check_button_add (object, "prefer-dark-theme", - _("Use dark theme variant if available"), - GTK_BOX (vbox2)); + grid = prefs_grid_new (GTK_CONTAINER (vbox2)); + button = prefs_enum_combo_box_add (object, "theme-color-scheme", 0, 0, + _("Color scheme variant (if available)"), + GTK_GRID (grid), 0, NULL); /* Override icon sizes. */ button = prefs_check_button_add (object, "override-theme-icon-size", diff --git a/app/gui/themes.c b/app/gui/themes.c index 950b27a6dd..1132744dc1 100644 --- a/app/gui/themes.c +++ b/app/gui/themes.c @@ -92,7 +92,7 @@ themes_init (Gimp *gimp) g_signal_connect (config, "notify::theme", G_CALLBACK (themes_theme_change_notify), gimp); - g_signal_connect (config, "notify::prefer-dark-theme", + g_signal_connect (config, "notify::theme-color-scheme", G_CALLBACK (themes_theme_change_notify), gimp); g_signal_connect (config, "notify::prefer-symbolic-icons", @@ -237,10 +237,12 @@ themes_apply_theme (Gimp *gimp, GFile *theme_css; GOutputStream *output; GError *error = NULL; + gboolean prefer_dark_theme; g_return_if_fail (GIMP_IS_GIMP (gimp)); g_return_if_fail (GIMP_IS_GUI_CONFIG (config)); + prefer_dark_theme = (config->theme_scheme != GIMP_THEME_LIGHT); theme_css = gimp_directory_file ("theme.css", NULL); if (gimp->be_verbose) @@ -263,11 +265,79 @@ themes_apply_theme (Gimp *gimp, if (theme_dir) { - css_files = g_slist_prepend (css_files, g_file_get_child (theme_dir, - "gimp.css")); - if (config->prefer_dark_theme) - css_files = g_slist_prepend (css_files, g_file_get_child (theme_dir, - "gimp-dark.css")); + GFile *file = NULL; + GFile *fallback; + GFile *light; + GFile *gray; + GFile *dark; + + fallback = g_file_get_child (theme_dir, "gimp.css"); + if (! g_file_query_exists (fallback, NULL)) + g_clear_object (&fallback); + + light = g_file_get_child (theme_dir, "gimp-light.css"); + if (! g_file_query_exists (light, NULL)) + g_clear_object (&light); + + gray = g_file_get_child (theme_dir, "gimp-gray.css"); + if (! g_file_query_exists (gray, NULL)) + g_clear_object (&gray); + + dark = g_file_get_child (theme_dir, "gimp-dark.css"); + if (! g_file_query_exists (dark, NULL)) + g_clear_object (&dark); + + switch (config->theme_scheme) + { + case GIMP_THEME_LIGHT: + if (light != NULL) + file = g_object_ref (light); + else if (fallback != NULL) + file = g_object_ref (fallback); + else if (gray != NULL) + file = g_object_ref (gray); + else if (dark != NULL) + file = g_object_ref (dark); + break; + case GIMP_THEME_GRAY: + if (gray != NULL) + file = g_object_ref (gray); + else if (fallback != NULL) + file = g_object_ref (fallback); + else if (dark != NULL) + file = g_object_ref (dark); + else if (light != NULL) + file = g_object_ref (light); + break; + case GIMP_THEME_DARK: + if (dark != NULL) + file = g_object_ref (dark); + else if (fallback != NULL) + file = g_object_ref (fallback); + else if (gray != NULL) + file = g_object_ref (gray); + else if (light != NULL) + file = g_object_ref (light); + break; + } + + if (file != NULL) + { + prefer_dark_theme = (file == dark || file == gray); + css_files = g_slist_prepend (css_files, file); + } + else + { + gimp_message (gimp, NULL, GIMP_MESSAGE_ERROR, + _("Invalid theme: directory '%s' contains neither " + "gimp-dark.css, gimp-gray.css, gimp-light.css nor gimp.css."), + gimp_file_get_utf8_name (theme_dir)); + } + + g_clear_object (&fallback); + g_clear_object (&light); + g_clear_object (&gray); + g_clear_object (&dark); } else { @@ -276,18 +346,30 @@ themes_apply_theme (Gimp *gimp, tmp = g_build_filename (gimp_data_directory (), "themes", "Default", "gimp.css", NULL); - css_files = g_slist_prepend ( - css_files, g_file_new_for_path (tmp)); + css_files = g_slist_prepend (css_files, g_file_new_for_path (tmp)); g_free (tmp); - if (config->prefer_dark_theme) + switch (config->theme_scheme) { + case GIMP_THEME_LIGHT: + tmp = g_build_filename (gimp_data_directory (), + "themes", "Default", "gimp-light.css", + NULL); + break; + case GIMP_THEME_GRAY: + tmp = g_build_filename (gimp_data_directory (), + "themes", "Default", "gimp-gray.css", + NULL); + break; + case GIMP_THEME_DARK: tmp = g_build_filename (gimp_data_directory (), "themes", "Default", "gimp-dark.css", NULL); - css_files = g_slist_prepend (css_files, g_file_new_for_path (tmp)); - g_free (tmp); + break; } + + css_files = g_slist_prepend (css_files, g_file_new_for_path (tmp)); + g_free (tmp); } css_files = g_slist_prepend ( @@ -338,7 +420,7 @@ themes_apply_theme (Gimp *gimp, "\n" "%s", config->prefer_symbolic_icons ? "symbolic" : "regular", - config->prefer_dark_theme ? "/* prefer-dark-theme */\n" : ""); + prefer_dark_theme ? "/* prefer-dark-theme */\n" : ""); } if (! error && config->override_icon_size) @@ -455,7 +537,7 @@ themes_theme_change_notify (GimpGuiConfig *config, GError *error = NULL; g_object_set (gtk_settings_get_for_screen (gdk_screen_get_default ()), - "gtk-application-prefer-dark-theme", config->prefer_dark_theme, + "gtk-application-prefer-dark-theme", config->theme_scheme != GIMP_THEME_LIGHT, NULL); themes_apply_theme (gimp, config); diff --git a/app/widgets/gimpwidgets-utils.c b/app/widgets/gimpwidgets-utils.c index e7298eaf97..b4ba8fb39a 100644 --- a/app/widgets/gimpwidgets-utils.c +++ b/app/widgets/gimpwidgets-utils.c @@ -2642,7 +2642,7 @@ gimp_window_set_title_bar_theme (Gimp *gimp, GimpGuiConfig *config; config = GIMP_GUI_CONFIG (gimp->config); - use_dark_mode = config->prefer_dark_theme; + use_dark_mode = (config->theme_scheme != GIMP_THEME_LIGHT); } else { diff --git a/themes/Gray/gimp.css b/themes/Default/gimp-gray.css similarity index 98% rename from themes/Gray/gimp.css rename to themes/Default/gimp-gray.css index 6bdd2639af..f70d65b987 100644 --- a/themes/Gray/gimp.css +++ b/themes/Default/gimp-gray.css @@ -66,4 +66,4 @@ @define-color ruler-color rgba(100,100,100,0.3); -@import url("../Default/common-light.css"); +@import url("common-light.css"); diff --git a/themes/Default/gimp.css b/themes/Default/gimp-light.css similarity index 100% rename from themes/Default/gimp.css rename to themes/Default/gimp-light.css diff --git a/themes/Default/meson.build b/themes/Default/meson.build index 8c2a46a794..d612fafc8e 100644 --- a/themes/Default/meson.build +++ b/themes/Default/meson.build @@ -1,7 +1,7 @@ subdir('ui') files = [ - 'gimp.css', 'gimp-dark.css', + 'gimp-light.css', 'gimp-gray.css', 'gimp-dark.css', 'common.css', 'common-dark.css', 'common-light.css' ] diff --git a/themes/meson.build b/themes/meson.build index 94b1963518..3fd57c6a5f 100644 --- a/themes/meson.build +++ b/themes/meson.build @@ -3,7 +3,6 @@ subdir('Darker') subdir('Compact') themes = [ - 'Gray', 'System', ]