gui: Change Windows title bar based on theme

On Windows, the title bar can be set to light or dark mode via DwmSetWindowAttribute ().
This adds code to update the main title bar and dialogue title bars based on the current theme.
The main title bar uses "prefer-dark-theme", while the dialogue title bars
uses the color of the widget background to assume the correct color.
This commit is contained in:
Alx Sa 2023-10-16 16:43:17 +00:00
parent bf8ee69570
commit ad8b47bff7
11 changed files with 230 additions and 34 deletions

View File

@ -30,6 +30,8 @@
#include "config/gimpcoreconfig.h"
#include "widgets/gimpwidgets-utils.h"
#include "about.h"
#include "git-version.h"
@ -50,23 +52,25 @@
typedef struct
{
GtkWidget *dialog;
GtkWidget *dialog;
Gimp *gimp;
GtkWidget *update_frame;
GimpCoreConfig *config;
GtkWidget *anim_area;
PangoLayout *layout;
GtkWidget *anim_area;
PangoLayout *layout;
gint n_authors;
gint shuffle[G_N_ELEMENTS (authors) - 1]; /* NULL terminated */
gint n_authors;
gint shuffle[G_N_ELEMENTS (authors) - 1]; /* NULL terminated */
guint timer;
guint timer;
gint index;
gint animstep;
gint state;
gboolean visible;
gint index;
gint animstep;
gint state;
gboolean visible;
} GimpAboutDialog;
@ -99,7 +103,8 @@ static void about_dialog_download_clicked
const gchar *link);
GtkWidget *
about_dialog_create (GimpCoreConfig *config)
about_dialog_create (Gimp *gimp,
GimpCoreConfig *config)
{
static GimpAboutDialog dialog;
@ -112,6 +117,7 @@ about_dialog_create (GimpCoreConfig *config)
gchar *copyright;
gchar *version;
dialog.gimp = gimp;
dialog.n_authors = G_N_ELEMENTS (authors) - 1;
dialog.config = config;
@ -206,6 +212,10 @@ about_dialog_map (GtkWidget *widget,
dialog->timer = g_timeout_add (800, about_dialog_timer, dialog);
}
#ifdef G_OS_WIN32
gimp_window_set_title_bar_theme (dialog->gimp, widget, FALSE);
#endif
}
static void

View File

@ -19,7 +19,8 @@
#define __ABOUT_DIALOG_H__
GtkWidget * about_dialog_create (GimpCoreConfig *config);
GtkWidget * about_dialog_create (Gimp *gimp,
GimpCoreConfig *config);
#endif /* __ABOUT_DIALOG_H__ */

View File

@ -217,7 +217,7 @@ dialogs_about_get (GimpDialogFactory *factory,
GimpUIManager *ui_manager,
gint view_size)
{
return about_dialog_create (context->gimp->edit_config);
return about_dialog_create (context->gimp, context->gimp->edit_config);
}
GtkWidget *

View File

@ -435,14 +435,18 @@ gimp_update_about_dialog (GimpCoreConfig *config,
const GParamSpec *pspec,
gpointer user_data)
{
#ifndef GIMP_CONSOLE_COMPILATION
Gimp *gimp = user_data;
#endif
g_signal_handlers_disconnect_by_func (config,
(GCallback) gimp_update_about_dialog,
NULL);
user_data);
if (config->last_known_release != NULL)
{
#ifndef GIMP_CONSOLE_COMPILATION
gtk_widget_show (about_dialog_create (config));
gtk_widget_show (about_dialog_create (gimp, config));
#else
g_printerr (_("A new version of GIMP (%s) was released.\n"
"It is recommended to update."),
@ -639,7 +643,7 @@ gimp_update_auto_check (GimpCoreConfig *config,
g_signal_connect (config, "notify::last-known-release",
(GCallback) gimp_update_about_dialog,
NULL);
gimp);
gimp_update_check (config);

View File

@ -617,6 +617,10 @@ gui_restore_after_callback (Gimp *gimp,
shell = gimp_display_get_shell (display);
#ifdef G_OS_WIN32
themes_set_title_bar (gimp);
#endif
if (gui_config->restore_session)
session_restore (gimp, initial_monitor);
@ -624,7 +628,7 @@ gui_restore_after_callback (Gimp *gimp,
#ifdef G_OS_WIN32
/* Prevents window from reappearing on start-up if the user
* requested it to be minimized via window hints
* requested it to be minimized via window hints
*/
if (StartupInfo.wShowWindow != SW_SHOWMINIMIZED &&
StartupInfo.wShowWindow != SW_SHOWMINNOACTIVE &&

View File

@ -31,6 +31,10 @@
#include "core/gimp.h"
#include "display/gimpimagewindow.h"
#include "widgets/gimpwidgets-utils.h"
#include "themes.h"
#include "gimp-intl.h"
@ -105,6 +109,10 @@ themes_init (Gimp *gimp)
gimp);
themes_theme_change_notify (config, NULL, gimp);
#ifdef G_OS_WIN32
themes_set_title_bar (gimp);
#endif
}
void
@ -469,6 +477,10 @@ themes_theme_change_notify (GimpGuiConfig *config,
g_object_unref (theme_css);
gtk_style_context_reset_widgets (gdk_screen_get_default ());
#ifdef G_OS_WIN32
themes_set_title_bar (gimp);
#endif
}
static void
@ -549,3 +561,22 @@ themes_theme_paths_notify (GimpExtensionManager *manager,
g_list_free_full (path, (GDestroyNotify) g_object_unref);
}
}
void
themes_set_title_bar (Gimp *gimp)
{
#ifdef G_OS_WIN32
GList *windows = gimp_get_image_windows (gimp);
GList *iter;
for (iter = windows; iter; iter = g_list_next (iter))
{
GtkWidget *window = GTK_WIDGET (windows->data);
gimp_window_set_title_bar_theme (gimp, window, TRUE);
}
if (windows)
g_list_free (windows);
#endif
}

View File

@ -30,5 +30,6 @@ GFile * themes_get_theme_file (Gimp *gimp,
const gchar *first_component,
...) G_GNUC_NULL_TERMINATED;
void themes_set_title_bar (Gimp *gimp);
#endif /* __THEMES_H__ */

View File

@ -86,6 +86,8 @@ static gboolean gimp_file_dialog_delete_event (GtkWidget *w
GdkEventAny *event);
static void gimp_file_dialog_response (GtkDialog *dialog,
gint response_id);
static void gimp_file_dialog_map (GimpFileDialog *dialog,
gpointer data);
static GFile * gimp_file_dialog_real_get_default_folder (GimpFileDialog *dialog);
static void gimp_file_dialog_real_save_state (GimpFileDialog *dialog,
const gchar *state_name);
@ -225,6 +227,11 @@ gimp_file_dialog_class_init (GimpFileDialogClass *klass)
static void
gimp_file_dialog_init (GimpFileDialog *dialog)
{
#ifdef G_OS_WIN32
g_signal_connect (dialog, "map",
G_CALLBACK (gimp_file_dialog_map),
NULL);
#endif
}
static void
@ -420,6 +427,15 @@ gimp_file_dialog_response (GtkDialog *dialog,
}
}
static void
gimp_file_dialog_map (GimpFileDialog *dialog,
gpointer data)
{
#ifdef G_OS_WIN32
gimp_window_set_title_bar_theme (dialog->gimp, GTK_WIDGET (dialog), FALSE);
#endif
}
static GFile *
gimp_file_dialog_real_get_default_folder (GimpFileDialog *dialog)
{

View File

@ -26,7 +26,12 @@
#include <gtk/gtk.h>
#ifdef GDK_WINDOWING_WIN32
#include <dwmapi.h>
#include <gdk/gdkwin32.h>
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
#endif
#endif
#ifdef GDK_WINDOWING_X11
@ -51,6 +56,8 @@
#include "gegl/gimp-babl.h"
#include "config/gimpguiconfig.h"
#include "core/gimp.h"
#include "core/gimpprogress.h"
#include "core/gimptoolinfo.h"
@ -2616,3 +2623,59 @@ gimp_window_set_transient_cb (GtkWidget *window,
}
#endif
}
void
gimp_window_set_title_bar_theme (Gimp *gimp,
GtkWidget *dialog,
gboolean is_main_window)
{
#ifdef G_OS_WIN32
HWND hwnd;
GdkWindow *window = NULL;
gboolean use_dark_mode = FALSE;
window = gtk_widget_get_window (GTK_WIDGET (dialog));
if (window)
{
if (gimp)
{
GimpGuiConfig *config;
config = GIMP_GUI_CONFIG (gimp->config);
use_dark_mode = config->prefer_dark_theme;
}
else
{
GtkStyleContext *style;
GdkRGBA *color = NULL;
/* Workaround if we don't have access to GimpGuiConfig.
* If the background color is below the threshold, then we're
* likely in dark mode.
*/
style = gtk_widget_get_style_context (dialog);
gtk_style_context_get (style, gtk_style_context_get_state (style),
GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &color,
NULL);
if (color)
{
if (color->red < 0.5 && color->green < 0.5 && color->blue < 0.5)
use_dark_mode = TRUE;
gdk_rgba_free (color);
}
}
hwnd = (HWND) gdk_win32_window_get_handle (window);
DwmSetWindowAttribute (hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE,
&use_dark_mode, sizeof (use_dark_mode));
if (! is_main_window)
{
/* Toggle the window's visibility so the title bar change appears */
gdk_window_hide (window);
gdk_window_show (window);
}
}
#endif
}

View File

@ -165,5 +165,9 @@ gboolean gimp_utils_are_menu_path_identical (const gchar *path1
gchar **mnemonic_path1,
gchar **path1_section_name);
void gimp_window_set_title_bar_theme (Gimp *gimp,
GtkWidget *dialog,
gboolean is_main_window);
#endif /* __APP_GIMP_WIDGETS_UTILS_H__ */

View File

@ -34,6 +34,14 @@
#include "libgimp/libgimp-intl.h"
#ifdef G_OS_WIN32
#include <dwmapi.h>
#include <gdk/gdkwin32.h>
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
#endif
#endif
/**
* SECTION: gimpdialog
@ -67,27 +75,28 @@ struct _GimpDialogPrivate
#define GET_PRIVATE(obj) (((GimpDialog *) (obj))->priv)
static void gimp_dialog_constructed (GObject *object);
static void gimp_dialog_dispose (GObject *object);
static void gimp_dialog_finalize (GObject *object);
static void gimp_dialog_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_dialog_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_dialog_constructed (GObject *object);
static void gimp_dialog_dispose (GObject *object);
static void gimp_dialog_finalize (GObject *object);
static void gimp_dialog_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_dialog_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_dialog_hide (GtkWidget *widget);
static gboolean gimp_dialog_delete_event (GtkWidget *widget,
GdkEventAny *event);
static void gimp_dialog_hide (GtkWidget *widget);
static gboolean gimp_dialog_delete_event (GtkWidget *widget,
GdkEventAny *event);
static void gimp_dialog_close (GtkDialog *dialog);
static void gimp_dialog_close (GtkDialog *dialog);
static void gimp_dialog_response (GtkDialog *dialog,
gint response_id);
static void gimp_dialog_response (GtkDialog *dialog,
gint response_id);
static void gimp_dialog_set_title_bar_theme (GtkWidget *dialog);
G_DEFINE_TYPE_WITH_PRIVATE (GimpDialog, gimp_dialog, GTK_TYPE_DIALOG)
@ -161,6 +170,12 @@ gimp_dialog_init (GimpDialog *dialog)
g_signal_connect (dialog, "response",
G_CALLBACK (gimp_dialog_response),
NULL);
#ifdef G_OS_WIN32
g_signal_connect (GTK_WIDGET (dialog), "map",
G_CALLBACK (gimp_dialog_set_title_bar_theme),
NULL);
#endif
}
static void
@ -656,6 +671,10 @@ gimp_dialog_run (GimpDialog *dialog)
gtk_window_present (GTK_WINDOW (dialog));
#ifdef G_OS_WIN32
gimp_dialog_set_title_bar_theme (GTK_WIDGET (dialog));
#endif
response_handler = g_signal_connect (dialog, "response",
G_CALLBACK (run_response_handler),
&ri);
@ -750,3 +769,46 @@ gimp_dialogs_show_help_button (gboolean show)
{
show_help_button = show ? TRUE : FALSE;
}
void
gimp_dialog_set_title_bar_theme (GtkWidget *dialog)
{
#ifdef G_OS_WIN32
HWND hwnd;
gboolean use_dark_mode = FALSE;
GdkWindow *window = NULL;
window = gtk_widget_get_window (GTK_WIDGET (dialog));
if (window)
{
GtkStyleContext *style;
GdkRGBA *color = NULL;
hwnd = (HWND) gdk_win32_window_get_handle (window);
/* Workaround since we don't have access to GimpGuiConfig.
* If the background color is below the threshold, then we're
* likely in dark mode.
*/
style = gtk_widget_get_style_context (GTK_WIDGET (dialog));
gtk_style_context_get (style, gtk_style_context_get_state (style),
GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &color,
NULL);
if (color)
{
if (color->red < 0.5 && color->green < 0.5 && color->blue < 0.5)
use_dark_mode = TRUE;
gdk_rgba_free (color);
}
DwmSetWindowAttribute (hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE,
&use_dark_mode, sizeof (use_dark_mode));
UpdateWindow (hwnd);
ShowWindow (hwnd, 5);
/* Toggle the window's visibility so the title bar change appears */
gdk_window_hide (window);
gdk_window_show (window);
}
#endif
}