mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-03-10 16:00:18 +00:00
feat: GTK3-only theming overhaul—new theme service (discover/import/inherits), layered CSS+settings.ini apply w/ safe rollback + caching; widget/xtext palette mapping + all-colors editor; lots of win32/CI libarchive plumbing + installer assets;
This commit is contained in:
@@ -13,11 +13,16 @@ struct session *lastact_sess;
|
||||
struct zoitechatprefs prefs;
|
||||
|
||||
static gboolean stub_dark_active;
|
||||
static gboolean stub_gtk3_active;
|
||||
static ThemeSemanticToken stub_last_color_token;
|
||||
static int stub_runtime_get_color_calls;
|
||||
static int stub_runtime_widget_calls;
|
||||
static int stub_runtime_xtext_calls;
|
||||
static int stub_runtime_xtext_mapped_calls;
|
||||
static size_t stub_runtime_xtext_last_len;
|
||||
static ThemeGtkPaletteMap stub_last_gtk_map;
|
||||
static gboolean stub_last_gtk_map_valid;
|
||||
static gboolean gtk_available;
|
||||
|
||||
static GdkRGBA stub_light_colors[THEME_TOKEN_COUNT];
|
||||
static GdkRGBA stub_dark_colors[THEME_TOKEN_COUNT];
|
||||
@@ -70,6 +75,13 @@ theme_runtime_dark_set_color (ThemeSemanticToken token, const GdkRGBA *col)
|
||||
(void) col;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_runtime_mode_has_user_colors (gboolean dark_mode)
|
||||
{
|
||||
(void) dark_mode;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_runtime_get_color (ThemeSemanticToken token, GdkRGBA *out_rgba)
|
||||
{
|
||||
@@ -109,6 +121,36 @@ theme_runtime_is_dark_active (void)
|
||||
return stub_dark_active;
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_get_widget_style_values_mapped (const ThemeGtkPaletteMap *gtk_map, ThemeWidgetStyleValues *out_values)
|
||||
{
|
||||
(void) gtk_map;
|
||||
theme_runtime_get_widget_style_values (out_values);
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_get_xtext_colors_mapped (const ThemeGtkPaletteMap *gtk_map, XTextColor *palette, size_t palette_len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
stub_runtime_xtext_mapped_calls++;
|
||||
stub_last_gtk_map = *gtk_map;
|
||||
stub_last_gtk_map_valid = TRUE;
|
||||
stub_runtime_xtext_last_len = palette_len;
|
||||
for (i = 0; i < palette_len; i++)
|
||||
{
|
||||
palette[i].red = (unsigned short) (100 + i);
|
||||
palette[i].green = (unsigned short) (200 + i);
|
||||
palette[i].blue = (unsigned short) (300 + i);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_gtk3_is_active (void)
|
||||
{
|
||||
return stub_gtk3_active;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
rgba_equal (const GdkRGBA *a, const GdkRGBA *b)
|
||||
{
|
||||
@@ -127,7 +169,10 @@ reset_stubs (void)
|
||||
stub_runtime_get_color_calls = 0;
|
||||
stub_runtime_widget_calls = 0;
|
||||
stub_runtime_xtext_calls = 0;
|
||||
stub_runtime_xtext_mapped_calls = 0;
|
||||
stub_runtime_xtext_last_len = 0;
|
||||
stub_last_gtk_map_valid = FALSE;
|
||||
stub_gtk3_active = FALSE;
|
||||
for (i = 0; i < THEME_TOKEN_COUNT; i++)
|
||||
{
|
||||
g_snprintf (light, sizeof (light), "#%02x%02x%02x", (unsigned int) (i + 1), 0x11, 0x22);
|
||||
@@ -137,6 +182,15 @@ reset_stubs (void)
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
rgba_close (const GdkRGBA *a, const GdkRGBA *b)
|
||||
{
|
||||
return fabs (a->red - b->red) < 0.0001 &&
|
||||
fabs (a->green - b->green) < 0.0001 &&
|
||||
fabs (a->blue - b->blue) < 0.0001 &&
|
||||
fabs (a->alpha - b->alpha) < 0.0001;
|
||||
}
|
||||
|
||||
static void
|
||||
test_access_semantic_token_routes_directly (void)
|
||||
{
|
||||
@@ -204,6 +258,62 @@ test_access_widget_style_forwarding (void)
|
||||
g_assert_true (fabs (values.foreground.green - (0xfc / 255.0)) < 0.0001);
|
||||
}
|
||||
|
||||
static void
|
||||
test_access_xtext_palette_widget_mapping_when_gtk3_active (void)
|
||||
{
|
||||
GtkWidget *window;
|
||||
GtkWidget *label;
|
||||
GtkStyleContext *context;
|
||||
GtkCssProvider *provider;
|
||||
XTextColor palette[2] = { 0 };
|
||||
GdkRGBA expected;
|
||||
|
||||
if (!gtk_available)
|
||||
{
|
||||
g_test_skip ("GTK display not available");
|
||||
return;
|
||||
}
|
||||
|
||||
reset_stubs ();
|
||||
stub_gtk3_active = TRUE;
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
label = gtk_label_new ("mapped");
|
||||
gtk_container_add (GTK_CONTAINER (window), label);
|
||||
provider = gtk_css_provider_new ();
|
||||
gtk_css_provider_load_from_data (provider,
|
||||
"label { color: #112233; background-color: #445566; }"
|
||||
"label:selected { color: #778899; background-color: #aabbcc; }"
|
||||
"label:link { color: #123456; }",
|
||||
-1,
|
||||
NULL);
|
||||
context = gtk_widget_get_style_context (label);
|
||||
gtk_style_context_add_provider (context,
|
||||
GTK_STYLE_PROVIDER (provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||
gtk_widget_realize (window);
|
||||
|
||||
theme_get_xtext_colors_for_widget (label, palette, G_N_ELEMENTS (palette));
|
||||
|
||||
g_assert_cmpint (stub_runtime_xtext_mapped_calls, ==, 1);
|
||||
g_assert_cmpint (stub_runtime_xtext_calls, ==, 0);
|
||||
g_assert_true (stub_last_gtk_map_valid);
|
||||
g_assert_true (gdk_rgba_parse (&expected, "#112233"));
|
||||
g_assert_true (rgba_close (&stub_last_gtk_map.text_foreground, &expected));
|
||||
g_assert_true (gdk_rgba_parse (&expected, "#445566"));
|
||||
g_assert_true (rgba_close (&stub_last_gtk_map.text_background, &expected));
|
||||
g_assert_true (gdk_rgba_parse (&expected, "#778899"));
|
||||
g_assert_true (rgba_close (&stub_last_gtk_map.selection_foreground, &expected));
|
||||
g_assert_true (gdk_rgba_parse (&expected, "#aabbcc"));
|
||||
g_assert_true (rgba_close (&stub_last_gtk_map.selection_background, &expected));
|
||||
g_assert_true (gdk_rgba_parse (&expected, "#123456"));
|
||||
g_assert_true (rgba_close (&stub_last_gtk_map.accent, &expected));
|
||||
g_assert_cmpuint (palette[0].red, ==, 100);
|
||||
g_assert_cmpuint (palette[1].green, ==, 201);
|
||||
|
||||
gtk_widget_destroy (window);
|
||||
g_object_unref (provider);
|
||||
}
|
||||
|
||||
static void
|
||||
test_access_dark_light_switch_affects_token_consumers (void)
|
||||
{
|
||||
@@ -229,8 +339,11 @@ main (int argc, char **argv)
|
||||
g_test_add_func ("/theme/access/semantic_token_routes_directly", test_access_semantic_token_routes_directly);
|
||||
g_test_add_func ("/theme/access/token_routes_without_legacy_accessor", test_access_token_routes_without_legacy_accessor);
|
||||
g_test_add_func ("/theme/access/xtext_palette_forwarding", test_access_xtext_palette_forwarding);
|
||||
g_test_add_func ("/theme/access/xtext_palette_widget_mapping_when_gtk3_active",
|
||||
test_access_xtext_palette_widget_mapping_when_gtk3_active);
|
||||
g_test_add_func ("/theme/access/widget_style_forwarding", test_access_widget_style_forwarding);
|
||||
g_test_add_func ("/theme/access/dark_light_switch_affects_token_consumers",
|
||||
test_access_dark_light_switch_affects_token_consumers);
|
||||
gtk_available = gtk_init_check (&argc, &argv);
|
||||
return g_test_run ();
|
||||
}
|
||||
|
||||
325
src/fe-gtk/theme/tests/test-theme-gtk3-settings.c
Normal file
325
src/fe-gtk/theme/tests/test-theme-gtk3-settings.c
Normal file
@@ -0,0 +1,325 @@
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "../theme-gtk3.h"
|
||||
#include "../../../common/gtk3-theme-service.h"
|
||||
#include "../../../common/zoitechat.h"
|
||||
#include "../../../common/zoitechatc.h"
|
||||
|
||||
struct session *current_sess;
|
||||
struct session *current_tab;
|
||||
struct session *lastact_sess;
|
||||
struct zoitechatprefs prefs;
|
||||
|
||||
static gboolean gtk_available;
|
||||
static char *temp_root;
|
||||
static char *theme_parent_root;
|
||||
static char *theme_child_root;
|
||||
static char *theme_switch_root;
|
||||
|
||||
gboolean
|
||||
theme_policy_system_prefers_dark (void)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
remove_tree (const char *path)
|
||||
{
|
||||
GDir *dir;
|
||||
const char *name;
|
||||
|
||||
if (!path || !g_file_test (path, G_FILE_TEST_EXISTS))
|
||||
return;
|
||||
if (!g_file_test (path, G_FILE_TEST_IS_DIR))
|
||||
{
|
||||
g_remove (path);
|
||||
return;
|
||||
}
|
||||
|
||||
dir = g_dir_open (path, 0, NULL);
|
||||
if (dir)
|
||||
{
|
||||
while ((name = g_dir_read_name (dir)) != NULL)
|
||||
{
|
||||
char *child = g_build_filename (path, name, NULL);
|
||||
remove_tree (child);
|
||||
g_free (child);
|
||||
}
|
||||
g_dir_close (dir);
|
||||
}
|
||||
g_rmdir (path);
|
||||
}
|
||||
|
||||
static void
|
||||
write_file (const char *path, const char *contents)
|
||||
{
|
||||
gboolean ok = g_file_set_contents (path, contents, -1, NULL);
|
||||
g_assert_true (ok);
|
||||
}
|
||||
|
||||
static void
|
||||
ensure_css_dir (const char *theme_root, const char *css_dir)
|
||||
{
|
||||
char *dir = g_build_filename (theme_root, css_dir, NULL);
|
||||
char *css = g_build_filename (dir, "gtk.css", NULL);
|
||||
int rc = g_mkdir_with_parents (dir, 0700);
|
||||
g_assert_cmpint (rc, ==, 0);
|
||||
write_file (css, "* { }\n");
|
||||
g_free (css);
|
||||
g_free (dir);
|
||||
}
|
||||
|
||||
static void
|
||||
write_settings (const char *theme_root, const char *css_dir, const char *settings)
|
||||
{
|
||||
char *path = g_build_filename (theme_root, css_dir, "settings.ini", NULL);
|
||||
write_file (path, settings);
|
||||
g_free (path);
|
||||
}
|
||||
|
||||
static ZoitechatGtk3Theme *
|
||||
make_theme (const char *id, const char *path)
|
||||
{
|
||||
ZoitechatGtk3Theme *theme = g_new0 (ZoitechatGtk3Theme, 1);
|
||||
theme->id = g_strdup (id);
|
||||
theme->display_name = g_strdup (id);
|
||||
theme->path = g_strdup (path);
|
||||
theme->source = ZOITECHAT_GTK3_THEME_SOURCE_USER;
|
||||
return theme;
|
||||
}
|
||||
|
||||
void
|
||||
zoitechat_gtk3_theme_free (ZoitechatGtk3Theme *theme)
|
||||
{
|
||||
if (!theme)
|
||||
return;
|
||||
g_free (theme->id);
|
||||
g_free (theme->display_name);
|
||||
g_free (theme->path);
|
||||
g_free (theme->thumbnail_path);
|
||||
g_free (theme);
|
||||
}
|
||||
|
||||
ZoitechatGtk3Theme *
|
||||
zoitechat_gtk3_theme_find_by_id (const char *theme_id)
|
||||
{
|
||||
if (g_strcmp0 (theme_id, "layered") == 0)
|
||||
return make_theme (theme_id, theme_child_root);
|
||||
if (g_strcmp0 (theme_id, "switch") == 0)
|
||||
return make_theme (theme_id, theme_switch_root);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
zoitechat_gtk3_theme_pick_css_dir_for_minor (const char *theme_root, int preferred_minor)
|
||||
{
|
||||
char *path;
|
||||
(void) preferred_minor;
|
||||
path = g_build_filename (theme_root, "gtk-3.24", "gtk.css", NULL);
|
||||
if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
|
||||
{
|
||||
g_free (path);
|
||||
return g_strdup ("gtk-3.24");
|
||||
}
|
||||
g_free (path);
|
||||
path = g_build_filename (theme_root, "gtk-3.0", "gtk.css", NULL);
|
||||
if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
|
||||
{
|
||||
g_free (path);
|
||||
return g_strdup ("gtk-3.0");
|
||||
}
|
||||
g_free (path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
zoitechat_gtk3_theme_pick_css_dir (const char *theme_root)
|
||||
{
|
||||
return zoitechat_gtk3_theme_pick_css_dir_for_minor (theme_root, -1);
|
||||
}
|
||||
|
||||
GPtrArray *
|
||||
zoitechat_gtk3_theme_build_inheritance_chain (const char *theme_root)
|
||||
{
|
||||
GPtrArray *chain = g_ptr_array_new_with_free_func (g_free);
|
||||
if (g_strcmp0 (theme_root, theme_child_root) == 0)
|
||||
{
|
||||
g_ptr_array_add (chain, g_strdup (theme_parent_root));
|
||||
g_ptr_array_add (chain, g_strdup (theme_child_root));
|
||||
return chain;
|
||||
}
|
||||
if (g_strcmp0 (theme_root, theme_switch_root) == 0)
|
||||
{
|
||||
g_ptr_array_add (chain, g_strdup (theme_switch_root));
|
||||
return chain;
|
||||
}
|
||||
g_ptr_array_unref (chain);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
get_bool_setting (const char *name)
|
||||
{
|
||||
GtkSettings *settings = gtk_settings_get_default ();
|
||||
gboolean value = FALSE;
|
||||
g_object_get (settings, name, &value, NULL);
|
||||
return value;
|
||||
}
|
||||
|
||||
static gint
|
||||
get_int_setting (const char *name)
|
||||
{
|
||||
GtkSettings *settings = gtk_settings_get_default ();
|
||||
gint value = 0;
|
||||
g_object_get (settings, name, &value, NULL);
|
||||
return value;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_themes (void)
|
||||
{
|
||||
char *path;
|
||||
|
||||
temp_root = g_dir_make_tmp ("zoitechat-theme-gtk3-settings-XXXXXX", NULL);
|
||||
g_assert_nonnull (temp_root);
|
||||
theme_parent_root = g_build_filename (temp_root, "parent", NULL);
|
||||
theme_child_root = g_build_filename (temp_root, "child", NULL);
|
||||
theme_switch_root = g_build_filename (temp_root, "switch", NULL);
|
||||
g_assert_cmpint (g_mkdir_with_parents (theme_parent_root, 0700), ==, 0);
|
||||
g_assert_cmpint (g_mkdir_with_parents (theme_child_root, 0700), ==, 0);
|
||||
g_assert_cmpint (g_mkdir_with_parents (theme_switch_root, 0700), ==, 0);
|
||||
|
||||
ensure_css_dir (theme_parent_root, "gtk-3.24");
|
||||
write_settings (theme_parent_root, "gtk-3.24",
|
||||
"[Settings]\n"
|
||||
"gtk-enable-animations=true\n"
|
||||
"gtk-cursor-blink-time=111\n");
|
||||
|
||||
ensure_css_dir (theme_child_root, "gtk-3.0");
|
||||
ensure_css_dir (theme_child_root, "gtk-3.24");
|
||||
write_settings (theme_child_root, "gtk-3.0",
|
||||
"[Settings]\n"
|
||||
"gtk-enable-animations=false\n"
|
||||
"gtk-cursor-blink-time=222\n");
|
||||
write_settings (theme_child_root, "gtk-3.24",
|
||||
"[Settings]\n"
|
||||
"gtk-cursor-blink-time=333\n");
|
||||
|
||||
ensure_css_dir (theme_switch_root, "gtk-3.24");
|
||||
write_settings (theme_switch_root, "gtk-3.24",
|
||||
"[Settings]\n"
|
||||
"gtk-enable-animations=false\n"
|
||||
"gtk-cursor-blink-time=444\n");
|
||||
|
||||
path = g_build_filename (theme_parent_root, "index.theme", NULL);
|
||||
write_file (path, "[Desktop Entry]\nName=parent\n");
|
||||
g_free (path);
|
||||
path = g_build_filename (theme_child_root, "index.theme", NULL);
|
||||
write_file (path, "[Desktop Entry]\nName=child\nInherits=parent\n");
|
||||
g_free (path);
|
||||
path = g_build_filename (theme_switch_root, "index.theme", NULL);
|
||||
write_file (path, "[Desktop Entry]\nName=switch\n");
|
||||
g_free (path);
|
||||
}
|
||||
|
||||
static void
|
||||
teardown_themes (void)
|
||||
{
|
||||
g_assert_nonnull (temp_root);
|
||||
remove_tree (temp_root);
|
||||
g_free (theme_parent_root);
|
||||
g_free (theme_child_root);
|
||||
g_free (theme_switch_root);
|
||||
g_free (temp_root);
|
||||
theme_parent_root = NULL;
|
||||
theme_child_root = NULL;
|
||||
theme_switch_root = NULL;
|
||||
temp_root = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
test_settings_layer_precedence (void)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!gtk_available)
|
||||
{
|
||||
g_test_skip ("GTK display not available");
|
||||
return;
|
||||
}
|
||||
|
||||
g_assert_true (theme_gtk3_apply ("layered", THEME_GTK3_VARIANT_PREFER_LIGHT, &error));
|
||||
g_assert_no_error (error);
|
||||
g_assert_false (get_bool_setting ("gtk-enable-animations"));
|
||||
g_assert_cmpint (get_int_setting ("gtk-cursor-blink-time"), ==, 333);
|
||||
g_assert_true (theme_gtk3_is_active ());
|
||||
theme_gtk3_disable ();
|
||||
}
|
||||
|
||||
static void
|
||||
test_settings_restored_on_disable_and_switch (void)
|
||||
{
|
||||
GError *error = NULL;
|
||||
gboolean default_animations;
|
||||
gint default_blink;
|
||||
char *default_theme_name = NULL;
|
||||
char *active_theme_name = NULL;
|
||||
|
||||
if (!gtk_available)
|
||||
{
|
||||
g_test_skip ("GTK display not available");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
default_animations = get_bool_setting ("gtk-enable-animations");
|
||||
default_blink = get_int_setting ("gtk-cursor-blink-time");
|
||||
g_object_get (gtk_settings_get_default (), "gtk-theme-name", &default_theme_name, NULL);
|
||||
|
||||
g_assert_true (theme_gtk3_apply ("layered", THEME_GTK3_VARIANT_PREFER_LIGHT, &error));
|
||||
g_assert_no_error (error);
|
||||
g_assert_cmpint (get_int_setting ("gtk-cursor-blink-time"), ==, 333);
|
||||
g_object_get (gtk_settings_get_default (), "gtk-theme-name", &active_theme_name, NULL);
|
||||
g_assert_cmpstr (active_theme_name, ==, "child");
|
||||
g_free (active_theme_name);
|
||||
active_theme_name = NULL;
|
||||
|
||||
g_assert_true (theme_gtk3_apply ("switch", THEME_GTK3_VARIANT_PREFER_LIGHT, &error));
|
||||
g_assert_no_error (error);
|
||||
g_assert_false (get_bool_setting ("gtk-enable-animations"));
|
||||
g_assert_cmpint (get_int_setting ("gtk-cursor-blink-time"), ==, 444);
|
||||
|
||||
theme_gtk3_disable ();
|
||||
g_assert_cmpint (get_int_setting ("gtk-cursor-blink-time"), ==, default_blink);
|
||||
g_assert_cmpint (get_bool_setting ("gtk-enable-animations"), ==, default_animations);
|
||||
g_object_get (gtk_settings_get_default (), "gtk-theme-name", &active_theme_name, NULL);
|
||||
g_assert_cmpstr (active_theme_name, ==, default_theme_name);
|
||||
g_free (active_theme_name);
|
||||
g_free (default_theme_name);
|
||||
g_assert_false (theme_gtk3_is_active ());
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int rc;
|
||||
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
gtk_available = gtk_init_check (&argc, &argv);
|
||||
setup_themes ();
|
||||
|
||||
g_test_add_func ("/theme/gtk3/settings_layer_precedence", test_settings_layer_precedence);
|
||||
g_test_add_func ("/theme/gtk3/settings_restored_on_disable_and_switch", test_settings_restored_on_disable_and_switch);
|
||||
|
||||
prefs.hex_gui_gtk3_variant = THEME_GTK3_VARIANT_PREFER_LIGHT;
|
||||
|
||||
if (!gtk_available)
|
||||
g_test_message ("Skipping GTK3 settings tests because GTK initialization failed");
|
||||
|
||||
rc = g_test_run ();
|
||||
theme_gtk3_disable ();
|
||||
teardown_themes ();
|
||||
return rc;
|
||||
}
|
||||
71
src/fe-gtk/theme/tests/test-theme-gtk3-stub.c
Normal file
71
src/fe-gtk/theme/tests/test-theme-gtk3-stub.c
Normal file
@@ -0,0 +1,71 @@
|
||||
#include <glib.h>
|
||||
|
||||
#include "../theme-gtk3.h"
|
||||
|
||||
static int apply_current_calls;
|
||||
|
||||
void
|
||||
test_theme_gtk3_stub_reset (void)
|
||||
{
|
||||
apply_current_calls = 0;
|
||||
}
|
||||
|
||||
int
|
||||
test_theme_gtk3_stub_apply_current_calls (void)
|
||||
{
|
||||
return apply_current_calls;
|
||||
}
|
||||
|
||||
void
|
||||
theme_gtk3_init (void)
|
||||
{
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_gtk3_apply_current (GError **error)
|
||||
{
|
||||
(void) error;
|
||||
apply_current_calls++;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_gtk3_apply (const char *theme_id, ThemeGtk3Variant variant, GError **error)
|
||||
{
|
||||
(void) theme_id;
|
||||
(void) variant;
|
||||
(void) error;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_gtk3_refresh (const char *theme_id, ThemeGtk3Variant variant, GError **error)
|
||||
{
|
||||
(void) theme_id;
|
||||
(void) variant;
|
||||
(void) error;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ThemeGtk3Variant
|
||||
theme_gtk3_variant_for_theme (const char *theme_id)
|
||||
{
|
||||
(void) theme_id;
|
||||
return THEME_GTK3_VARIANT_PREFER_LIGHT;
|
||||
}
|
||||
|
||||
void
|
||||
theme_gtk3_invalidate_provider_cache (void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
theme_gtk3_disable (void)
|
||||
{
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_gtk3_is_active (void)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "../theme-manager.h"
|
||||
#include "../theme-gtk3.h"
|
||||
#include "../../fe-gtk.h"
|
||||
#include "../../../common/zoitechat.h"
|
||||
#include "../../../common/zoitechatc.h"
|
||||
@@ -19,6 +20,9 @@ static ThemeChangedEvent last_event;
|
||||
static int idle_add_calls;
|
||||
static guint next_idle_source_id = 33;
|
||||
|
||||
void test_theme_gtk3_stub_reset (void);
|
||||
int test_theme_gtk3_stub_apply_current_calls (void);
|
||||
|
||||
void setup_apply_real (const ThemeChangedEvent *event)
|
||||
{
|
||||
(void) event;
|
||||
@@ -85,6 +89,11 @@ void theme_runtime_user_set_color (ThemeSemanticToken token, const GdkRGBA *colo
|
||||
(void) color;
|
||||
}
|
||||
|
||||
void theme_runtime_reset_mode_colors (gboolean dark_mode)
|
||||
{
|
||||
(void) dark_mode;
|
||||
}
|
||||
|
||||
gboolean theme_runtime_apply_mode (unsigned int mode, gboolean *dark_active)
|
||||
{
|
||||
(void) mode;
|
||||
@@ -135,6 +144,12 @@ void theme_get_widget_style_values (ThemeWidgetStyleValues *out_values)
|
||||
gdk_rgba_parse (&out_values->foreground, "#f0f0f0");
|
||||
}
|
||||
|
||||
void theme_get_widget_style_values_for_widget (GtkWidget *widget, ThemeWidgetStyleValues *out_values)
|
||||
{
|
||||
(void) widget;
|
||||
theme_get_widget_style_values (out_values);
|
||||
}
|
||||
|
||||
void fe_win32_apply_native_titlebar (GtkWidget *window, gboolean dark)
|
||||
{
|
||||
(void) window;
|
||||
@@ -167,6 +182,8 @@ reset_state (void)
|
||||
listener_calls = 0;
|
||||
idle_add_calls = 0;
|
||||
next_idle_source_id = 33;
|
||||
prefs.hex_gui_gtk3_variant = THEME_GTK3_VARIANT_FOLLOW_SYSTEM;
|
||||
test_theme_gtk3_stub_reset ();
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -187,6 +204,7 @@ test_auto_refresh_dispatches_mode_palette_and_style_reasons (void)
|
||||
g_assert_cmpint (auto_state_calls, ==, 2);
|
||||
g_assert_true (last_auto_state);
|
||||
g_assert_cmpint (listener_calls, ==, 1);
|
||||
g_assert_cmpint (test_theme_gtk3_stub_apply_current_calls (), ==, 1);
|
||||
g_assert_true (theme_changed_event_has_reason (&last_event, THEME_CHANGED_REASON_PALETTE));
|
||||
g_assert_true (theme_changed_event_has_reason (&last_event, THEME_CHANGED_REASON_WIDGET_STYLE));
|
||||
g_assert_true (theme_changed_event_has_reason (&last_event, THEME_CHANGED_REASON_USERLIST));
|
||||
@@ -212,6 +230,29 @@ test_auto_refresh_ignores_non_auto_mode (void)
|
||||
g_assert_cmpint (idle_add_calls, ==, 1);
|
||||
g_assert_cmpint (auto_state_calls, ==, 0);
|
||||
g_assert_cmpint (listener_calls, ==, 0);
|
||||
g_assert_cmpint (test_theme_gtk3_stub_apply_current_calls (), ==, 0);
|
||||
|
||||
theme_manager_set_idle_add_func (NULL);
|
||||
theme_listener_unregister (listener_id);
|
||||
}
|
||||
|
||||
static void
|
||||
test_auto_refresh_reapplies_gtk3_for_follow_system_variant (void)
|
||||
{
|
||||
guint listener_id;
|
||||
|
||||
reset_state ();
|
||||
prefs.hex_gui_dark_mode = ZOITECHAT_DARK_MODE_DARK;
|
||||
prefs.hex_gui_gtk3_variant = THEME_GTK3_VARIANT_FOLLOW_SYSTEM;
|
||||
listener_id = theme_listener_register ("auto.gtk3", auto_listener, NULL);
|
||||
theme_manager_set_idle_add_func (immediate_idle_add);
|
||||
|
||||
theme_manager_refresh_auto_mode ();
|
||||
|
||||
g_assert_cmpint (idle_add_calls, ==, 1);
|
||||
g_assert_cmpint (auto_state_calls, ==, 0);
|
||||
g_assert_cmpint (listener_calls, ==, 0);
|
||||
g_assert_cmpint (test_theme_gtk3_stub_apply_current_calls (), ==, 1);
|
||||
|
||||
theme_manager_set_idle_add_func (NULL);
|
||||
theme_listener_unregister (listener_id);
|
||||
@@ -225,5 +266,7 @@ main (int argc, char **argv)
|
||||
test_auto_refresh_dispatches_mode_palette_and_style_reasons);
|
||||
g_test_add_func ("/theme/manager/auto_refresh_ignores_non_auto_mode",
|
||||
test_auto_refresh_ignores_non_auto_mode);
|
||||
g_test_add_func ("/theme/manager/auto_refresh_reapplies_gtk3_for_follow_system_variant",
|
||||
test_auto_refresh_reapplies_gtk3_for_follow_system_variant);
|
||||
return g_test_run ();
|
||||
}
|
||||
|
||||
@@ -81,6 +81,11 @@ void theme_runtime_user_set_color (ThemeSemanticToken token, const GdkRGBA *colo
|
||||
(void) color;
|
||||
}
|
||||
|
||||
void theme_runtime_reset_mode_colors (gboolean dark_mode)
|
||||
{
|
||||
(void) dark_mode;
|
||||
}
|
||||
|
||||
gboolean theme_runtime_apply_mode (unsigned int mode, gboolean *dark_active)
|
||||
{
|
||||
(void) mode;
|
||||
@@ -131,6 +136,12 @@ void theme_get_widget_style_values (ThemeWidgetStyleValues *out_values)
|
||||
gdk_rgba_parse (&out_values->foreground, "#f0f0f0");
|
||||
}
|
||||
|
||||
void theme_get_widget_style_values_for_widget (GtkWidget *widget, ThemeWidgetStyleValues *out_values)
|
||||
{
|
||||
(void) widget;
|
||||
theme_get_widget_style_values (out_values);
|
||||
}
|
||||
|
||||
void fe_win32_apply_native_titlebar (GtkWidget *window, gboolean dark)
|
||||
{
|
||||
(void) window;
|
||||
|
||||
@@ -85,6 +85,11 @@ void theme_runtime_user_set_color (ThemeSemanticToken token, const GdkRGBA *colo
|
||||
stub_last_user_token = token;
|
||||
}
|
||||
|
||||
void theme_runtime_reset_mode_colors (gboolean dark_mode)
|
||||
{
|
||||
(void) dark_mode;
|
||||
}
|
||||
|
||||
gboolean theme_runtime_apply_mode (unsigned int mode, gboolean *dark_active)
|
||||
{
|
||||
(void) mode;
|
||||
@@ -147,6 +152,12 @@ void theme_get_widget_style_values (ThemeWidgetStyleValues *out_values)
|
||||
gdk_rgba_parse (&out_values->foreground, "#f0f0f0");
|
||||
}
|
||||
|
||||
void theme_get_widget_style_values_for_widget (GtkWidget *widget, ThemeWidgetStyleValues *out_values)
|
||||
{
|
||||
(void) widget;
|
||||
theme_get_widget_style_values (out_values);
|
||||
}
|
||||
|
||||
void fe_win32_apply_native_titlebar (GtkWidget *window, gboolean dark)
|
||||
{
|
||||
(void) window;
|
||||
|
||||
280
src/fe-gtk/theme/tests/test-theme-preferences-gtk3-populate.c
Normal file
280
src/fe-gtk/theme/tests/test-theme-preferences-gtk3-populate.c
Normal file
@@ -0,0 +1,280 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "../../../common/zoitechat.h"
|
||||
#include "../../../common/zoitechatc.h"
|
||||
#include "../../../common/gtk3-theme-service.h"
|
||||
#include "../../fe-gtk.h"
|
||||
#include "../theme-gtk3.h"
|
||||
#include "../theme-manager.h"
|
||||
|
||||
struct session *current_sess;
|
||||
struct session *current_tab;
|
||||
struct zoitechatprefs prefs;
|
||||
InputStyle *input_style;
|
||||
|
||||
static gboolean gtk_available;
|
||||
static int apply_current_calls;
|
||||
static char applied_theme_id[256];
|
||||
static ThemeGtk3Variant applied_variant;
|
||||
static gboolean removed_selected;
|
||||
|
||||
GtkWidget *
|
||||
gtkutil_box_new (GtkOrientation orientation, gboolean homogeneous, gint spacing)
|
||||
{
|
||||
(void)homogeneous;
|
||||
return gtk_box_new (orientation, spacing);
|
||||
}
|
||||
|
||||
void
|
||||
gtkutil_apply_palette (GtkWidget *wid, const GdkRGBA *fg, const GdkRGBA *bg, const PangoFontDescription *font)
|
||||
{
|
||||
(void)wid;
|
||||
(void)fg;
|
||||
(void)bg;
|
||||
(void)font;
|
||||
}
|
||||
|
||||
void
|
||||
fe_open_url (const char *url)
|
||||
{
|
||||
(void)url;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_get_color (ThemeSemanticToken token, GdkRGBA *color)
|
||||
{
|
||||
(void)token;
|
||||
if (color)
|
||||
gdk_rgba_parse (color, "#000000");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_set_token_color (unsigned int dark_mode, ThemeSemanticToken token, const GdkRGBA *color, gboolean *changed)
|
||||
{
|
||||
(void)dark_mode;
|
||||
(void)token;
|
||||
(void)color;
|
||||
if (changed)
|
||||
*changed = FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_reset_mode_colors (unsigned int mode, gboolean *palette_changed)
|
||||
{
|
||||
(void)mode;
|
||||
if (palette_changed)
|
||||
*palette_changed = FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_save_preferences (void)
|
||||
{
|
||||
}
|
||||
|
||||
ThemePaletteBehavior
|
||||
theme_manager_get_userlist_palette_behavior (const PangoFontDescription *font_desc)
|
||||
{
|
||||
ThemePaletteBehavior behavior;
|
||||
|
||||
behavior.font_desc = font_desc;
|
||||
behavior.apply_background = FALSE;
|
||||
behavior.apply_foreground = FALSE;
|
||||
return behavior;
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_apply_userlist_style (GtkWidget *widget, ThemePaletteBehavior behavior)
|
||||
{
|
||||
(void)widget;
|
||||
(void)behavior;
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_attach_window (GtkWidget *window)
|
||||
{
|
||||
(void)window;
|
||||
}
|
||||
|
||||
char *
|
||||
zoitechat_gtk3_theme_service_get_user_themes_dir (void)
|
||||
{
|
||||
return g_strdup ("/tmp");
|
||||
}
|
||||
|
||||
static ZoitechatGtk3Theme *
|
||||
new_theme (const char *id, const char *name, ZoitechatGtk3ThemeSource source)
|
||||
{
|
||||
ZoitechatGtk3Theme *theme = g_new0 (ZoitechatGtk3Theme, 1);
|
||||
theme->id = g_strdup (id);
|
||||
theme->display_name = g_strdup (name);
|
||||
theme->source = source;
|
||||
return theme;
|
||||
}
|
||||
|
||||
void
|
||||
zoitechat_gtk3_theme_free (ZoitechatGtk3Theme *theme)
|
||||
{
|
||||
if (!theme)
|
||||
return;
|
||||
g_free (theme->id);
|
||||
g_free (theme->display_name);
|
||||
g_free (theme->path);
|
||||
g_free (theme->thumbnail_path);
|
||||
g_free (theme);
|
||||
}
|
||||
|
||||
GPtrArray *
|
||||
zoitechat_gtk3_theme_service_discover (void)
|
||||
{
|
||||
GPtrArray *themes = g_ptr_array_new_with_free_func ((GDestroyNotify)zoitechat_gtk3_theme_free);
|
||||
|
||||
if (!removed_selected)
|
||||
g_ptr_array_add (themes, new_theme ("removed-theme", "Removed Theme", ZOITECHAT_GTK3_THEME_SOURCE_USER));
|
||||
g_ptr_array_add (themes, new_theme ("fallback-theme", "Fallback Theme", ZOITECHAT_GTK3_THEME_SOURCE_SYSTEM));
|
||||
return themes;
|
||||
}
|
||||
|
||||
ZoitechatGtk3Theme *
|
||||
zoitechat_gtk3_theme_find_by_id (const char *theme_id)
|
||||
{
|
||||
(void)theme_id;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gboolean
|
||||
zoitechat_gtk3_theme_service_import (const char *source_path, char **imported_id, GError **error)
|
||||
{
|
||||
(void)source_path;
|
||||
(void)imported_id;
|
||||
(void)error;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
zoitechat_gtk3_theme_service_remove_user_theme (const char *theme_id, GError **error)
|
||||
{
|
||||
(void)error;
|
||||
if (g_strcmp0 (theme_id, "removed-theme") == 0)
|
||||
{
|
||||
removed_selected = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
char *
|
||||
zoitechat_gtk3_theme_pick_css_dir_for_minor (const char *theme_root, int preferred_minor)
|
||||
{
|
||||
(void)theme_root;
|
||||
(void)preferred_minor;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
zoitechat_gtk3_theme_pick_css_dir (const char *theme_root)
|
||||
{
|
||||
(void)theme_root;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GPtrArray *
|
||||
zoitechat_gtk3_theme_build_inheritance_chain (const char *theme_root)
|
||||
{
|
||||
(void)theme_root;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_gtk3_apply_current (GError **error)
|
||||
{
|
||||
(void)error;
|
||||
apply_current_calls++;
|
||||
g_strlcpy (applied_theme_id, prefs.hex_gui_gtk3_theme, sizeof (applied_theme_id));
|
||||
applied_variant = (ThemeGtk3Variant)prefs.hex_gui_gtk3_variant;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
theme_gtk3_init (void)
|
||||
{
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_gtk3_apply (const char *theme_id, ThemeGtk3Variant variant, GError **error)
|
||||
{
|
||||
(void)theme_id;
|
||||
(void)variant;
|
||||
(void)error;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ThemeGtk3Variant
|
||||
theme_gtk3_variant_for_theme (const char *theme_id)
|
||||
{
|
||||
if (g_str_has_suffix (theme_id, "dark"))
|
||||
return THEME_GTK3_VARIANT_PREFER_DARK;
|
||||
return THEME_GTK3_VARIANT_PREFER_LIGHT;
|
||||
}
|
||||
|
||||
void
|
||||
theme_gtk3_disable (void)
|
||||
{
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_gtk3_is_active (void)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#include "../theme-preferences.c"
|
||||
|
||||
static void
|
||||
test_removed_selected_theme_commits_fallback_and_applies (void)
|
||||
{
|
||||
GtkWidget *page;
|
||||
theme_preferences_ui *ui;
|
||||
struct zoitechatprefs setup_prefs;
|
||||
|
||||
if (!gtk_available)
|
||||
{
|
||||
g_test_skip ("GTK display not available");
|
||||
return;
|
||||
}
|
||||
|
||||
memset (&setup_prefs, 0, sizeof (setup_prefs));
|
||||
memset (&prefs, 0, sizeof (prefs));
|
||||
g_strlcpy (prefs.hex_gui_gtk3_theme, "removed-theme", sizeof (prefs.hex_gui_gtk3_theme));
|
||||
prefs.hex_gui_gtk3_variant = THEME_GTK3_VARIANT_PREFER_DARK;
|
||||
removed_selected = FALSE;
|
||||
apply_current_calls = 0;
|
||||
applied_theme_id[0] = '\0';
|
||||
|
||||
page = theme_preferences_create_page (NULL, &setup_prefs, NULL);
|
||||
ui = g_object_get_data (G_OBJECT (page), "theme-preferences-ui");
|
||||
g_assert_nonnull (ui);
|
||||
|
||||
g_assert_nonnull (ui->gtk3_remove);
|
||||
gtk_button_clicked (GTK_BUTTON (ui->gtk3_remove));
|
||||
|
||||
g_assert_cmpstr (prefs.hex_gui_gtk3_theme, ==, "fallback-theme");
|
||||
g_assert_cmpstr (setup_prefs.hex_gui_gtk3_theme, ==, "fallback-theme");
|
||||
g_assert_cmpint (prefs.hex_gui_gtk3_variant, ==, THEME_GTK3_VARIANT_PREFER_LIGHT);
|
||||
g_assert_cmpint (setup_prefs.hex_gui_gtk3_variant, ==, THEME_GTK3_VARIANT_PREFER_LIGHT);
|
||||
g_assert_cmpint (apply_current_calls, ==, 1);
|
||||
g_assert_cmpstr (applied_theme_id, ==, "fallback-theme");
|
||||
g_assert_cmpint (applied_variant, ==, THEME_GTK3_VARIANT_PREFER_LIGHT);
|
||||
|
||||
gtk_widget_destroy (page);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
gtk_available = gtk_init_check (&argc, &argv);
|
||||
g_test_add_func ("/theme/preferences/gtk3_removed_selection_applies_fallback",
|
||||
test_removed_selected_theme_commits_fallback_and_applies);
|
||||
return g_test_run ();
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -267,6 +268,91 @@ test_ui_edits_persist_without_legacy_array_mutation (void)
|
||||
g_assert_true (colors_equal (&dark_loaded, &dark_expected));
|
||||
}
|
||||
|
||||
static void
|
||||
test_gtk_map_colors_blend_with_palette_without_transparency (void)
|
||||
{
|
||||
ThemeGtkPaletteMap map = { 0 };
|
||||
ThemeWidgetStyleValues base_values;
|
||||
ThemeWidgetStyleValues values;
|
||||
GdkRGBA mapped_bg;
|
||||
double alpha;
|
||||
double expected_red;
|
||||
double expected_green;
|
||||
double expected_blue;
|
||||
|
||||
setup_temp_home ();
|
||||
theme_runtime_load ();
|
||||
theme_runtime_get_widget_style_values (&base_values);
|
||||
|
||||
map.enabled = TRUE;
|
||||
g_assert_true (gdk_rgba_parse (&map.text_foreground, "rgba(10, 20, 30, 0.25)"));
|
||||
g_assert_true (gdk_rgba_parse (&map.text_background, "rgba(40, 50, 60, 0.30)"));
|
||||
g_assert_true (gdk_rgba_parse (&map.selection_foreground, "rgba(70, 80, 90, 0.35)"));
|
||||
g_assert_true (gdk_rgba_parse (&map.selection_background, "rgba(100, 110, 120, 0.40)"));
|
||||
g_assert_true (gdk_rgba_parse (&map.accent, "rgba(130, 140, 150, 0.45)"));
|
||||
|
||||
theme_runtime_get_widget_style_values_mapped (&map, &values);
|
||||
g_assert_cmpfloat (values.foreground.alpha, ==, 1.0);
|
||||
g_assert_cmpfloat (values.background.alpha, ==, 1.0);
|
||||
g_assert_cmpfloat (values.selection_foreground.alpha, ==, 1.0);
|
||||
g_assert_cmpfloat (values.selection_background.alpha, ==, 1.0);
|
||||
|
||||
mapped_bg = map.text_background;
|
||||
alpha = mapped_bg.alpha;
|
||||
expected_red = (mapped_bg.red * alpha) + (base_values.background.red * (1.0 - alpha));
|
||||
expected_green = (mapped_bg.green * alpha) + (base_values.background.green * (1.0 - alpha));
|
||||
expected_blue = (mapped_bg.blue * alpha) + (base_values.background.blue * (1.0 - alpha));
|
||||
g_assert_true (fabs (values.background.red - expected_red) < 0.0001);
|
||||
g_assert_true (fabs (values.background.green - expected_green) < 0.0001);
|
||||
g_assert_true (fabs (values.background.blue - expected_blue) < 0.0001);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_gtk_map_uses_theme_defaults_until_custom_token_is_set (void)
|
||||
{
|
||||
ThemeGtkPaletteMap map = { 0 };
|
||||
ThemeWidgetStyleValues values;
|
||||
GdkRGBA custom;
|
||||
|
||||
setup_temp_home ();
|
||||
theme_runtime_load ();
|
||||
|
||||
map.enabled = TRUE;
|
||||
g_assert_true (gdk_rgba_parse (&map.text_foreground, "#010203"));
|
||||
g_assert_true (gdk_rgba_parse (&map.text_background, "#111213"));
|
||||
g_assert_true (gdk_rgba_parse (&map.selection_foreground, "#212223"));
|
||||
g_assert_true (gdk_rgba_parse (&map.selection_background, "#313233"));
|
||||
g_assert_true (gdk_rgba_parse (&map.accent, "#414243"));
|
||||
|
||||
theme_runtime_get_widget_style_values_mapped (&map, &values);
|
||||
g_assert_true (colors_equal (&values.foreground, &map.text_foreground));
|
||||
|
||||
g_assert_true (gdk_rgba_parse (&custom, "#a1b2c3"));
|
||||
theme_runtime_user_set_color (THEME_TOKEN_TEXT_FOREGROUND, &custom);
|
||||
theme_runtime_apply_mode (ZOITECHAT_DARK_MODE_LIGHT, NULL);
|
||||
theme_runtime_get_widget_style_values_mapped (&map, &values);
|
||||
g_assert_true (colors_equal (&values.foreground, &custom));
|
||||
}
|
||||
|
||||
static void
|
||||
test_save_writes_only_custom_token_keys (void)
|
||||
{
|
||||
GdkRGBA custom;
|
||||
char *cfg;
|
||||
|
||||
setup_temp_home ();
|
||||
theme_runtime_load ();
|
||||
g_assert_true (gdk_rgba_parse (&custom, "#445566"));
|
||||
theme_runtime_user_set_color (THEME_TOKEN_TEXT_FOREGROUND, &custom);
|
||||
theme_runtime_save ();
|
||||
|
||||
cfg = read_colors_conf ();
|
||||
g_assert_nonnull (g_strstr_len (cfg, -1, "theme.mode.light.token.text_foreground"));
|
||||
g_assert_null (g_strstr_len (cfg, -1, "theme.mode.light.token.text_background"));
|
||||
g_free (cfg);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
@@ -277,5 +363,11 @@ main (int argc, char **argv)
|
||||
test_loads_legacy_color_keys_via_migration_loader);
|
||||
g_test_add_func ("/theme/runtime/ui_edits_persist_without_legacy_array_mutation",
|
||||
test_ui_edits_persist_without_legacy_array_mutation);
|
||||
g_test_add_func ("/theme/runtime/gtk_map_colors_blend_with_palette_without_transparency",
|
||||
test_gtk_map_colors_blend_with_palette_without_transparency);
|
||||
g_test_add_func ("/theme/runtime/gtk_map_uses_theme_defaults_until_custom_token_is_set",
|
||||
test_gtk_map_uses_theme_defaults_until_custom_token_is_set);
|
||||
g_test_add_func ("/theme/runtime/save_writes_only_custom_token_keys",
|
||||
test_save_writes_only_custom_token_keys);
|
||||
return g_test_run ();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "theme-access.h"
|
||||
|
||||
#include "theme-runtime.h"
|
||||
#include "theme-gtk3.h"
|
||||
|
||||
|
||||
static gboolean
|
||||
@@ -17,6 +18,32 @@ theme_token_to_rgb16 (ThemeSemanticToken token, guint16 *red, guint16 *green, gu
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
theme_access_get_gtk_palette_map (GtkWidget *widget, ThemeGtkPaletteMap *out_map)
|
||||
{
|
||||
GtkStyleContext *context;
|
||||
GdkRGBA accent;
|
||||
|
||||
g_return_val_if_fail (out_map != NULL, FALSE);
|
||||
if (!theme_gtk3_is_active () || widget == NULL)
|
||||
return FALSE;
|
||||
|
||||
context = gtk_widget_get_style_context (widget);
|
||||
if (context == NULL)
|
||||
return FALSE;
|
||||
|
||||
gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &out_map->text_foreground);
|
||||
gtk_style_context_get_background_color (context, GTK_STATE_FLAG_NORMAL, &out_map->text_background);
|
||||
gtk_style_context_get_color (context, GTK_STATE_FLAG_SELECTED, &out_map->selection_foreground);
|
||||
gtk_style_context_get_background_color (context, GTK_STATE_FLAG_SELECTED, &out_map->selection_background);
|
||||
gtk_style_context_get_color (context, GTK_STATE_FLAG_LINK, &accent);
|
||||
if (accent.alpha <= 0.0)
|
||||
accent = out_map->selection_background;
|
||||
out_map->accent = accent;
|
||||
out_map->enabled = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_get_color (ThemeSemanticToken token, GdkRGBA *out_rgba)
|
||||
{
|
||||
@@ -63,11 +90,37 @@ theme_get_legacy_color (int legacy_idx, GdkRGBA *out_rgba)
|
||||
void
|
||||
theme_get_widget_style_values (ThemeWidgetStyleValues *out_values)
|
||||
{
|
||||
theme_get_widget_style_values_for_widget (NULL, out_values);
|
||||
}
|
||||
|
||||
void
|
||||
theme_get_widget_style_values_for_widget (GtkWidget *widget, ThemeWidgetStyleValues *out_values)
|
||||
{
|
||||
ThemeGtkPaletteMap gtk_map = { 0 };
|
||||
|
||||
if (theme_access_get_gtk_palette_map (widget, >k_map))
|
||||
{
|
||||
theme_runtime_get_widget_style_values_mapped (>k_map, out_values);
|
||||
return;
|
||||
}
|
||||
theme_runtime_get_widget_style_values (out_values);
|
||||
}
|
||||
|
||||
void
|
||||
theme_get_xtext_colors (XTextColor *palette, size_t palette_len)
|
||||
{
|
||||
theme_get_xtext_colors_for_widget (NULL, palette, palette_len);
|
||||
}
|
||||
|
||||
void
|
||||
theme_get_xtext_colors_for_widget (GtkWidget *widget, XTextColor *palette, size_t palette_len)
|
||||
{
|
||||
ThemeGtkPaletteMap gtk_map = { 0 };
|
||||
|
||||
if (theme_access_get_gtk_palette_map (widget, >k_map))
|
||||
{
|
||||
theme_runtime_get_xtext_colors_mapped (>k_map, palette, palette_len);
|
||||
return;
|
||||
}
|
||||
theme_runtime_get_xtext_colors (palette, palette_len);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ gboolean theme_get_mirc_color_rgb16 (unsigned int mirc_index, guint16 *red, guin
|
||||
G_DEPRECATED_FOR (theme_get_color)
|
||||
gboolean theme_get_legacy_color (int legacy_idx, GdkRGBA *out_rgba);
|
||||
void theme_get_widget_style_values (ThemeWidgetStyleValues *out_values);
|
||||
void theme_get_widget_style_values_for_widget (GtkWidget *widget, ThemeWidgetStyleValues *out_values);
|
||||
void theme_get_xtext_colors (XTextColor *palette, size_t palette_len);
|
||||
void theme_get_xtext_colors_for_widget (GtkWidget *widget, XTextColor *palette, size_t palette_len);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "../../common/zoitechatc.h"
|
||||
#include "theme-css.h"
|
||||
#include "theme-runtime.h"
|
||||
#include "theme-gtk3.h"
|
||||
#include "../maingui.h"
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
@@ -13,23 +14,52 @@ static void
|
||||
theme_application_apply_windows_theme (gboolean dark)
|
||||
{
|
||||
GtkSettings *settings = gtk_settings_get_default ();
|
||||
static GtkCssProvider *win_theme_provider = NULL;
|
||||
static gboolean win_theme_provider_installed = FALSE;
|
||||
GdkScreen *screen;
|
||||
gboolean prefer_dark = dark;
|
||||
char *css;
|
||||
|
||||
if (theme_gtk3_is_active ())
|
||||
{
|
||||
if (prefs.hex_gui_gtk3_variant == THEME_GTK3_VARIANT_PREFER_DARK)
|
||||
prefer_dark = TRUE;
|
||||
else if (prefs.hex_gui_gtk3_variant == THEME_GTK3_VARIANT_PREFER_LIGHT)
|
||||
prefer_dark = FALSE;
|
||||
}
|
||||
|
||||
if (settings && g_object_class_find_property (G_OBJECT_GET_CLASS (settings),
|
||||
"gtk-application-prefer-dark-theme"))
|
||||
g_object_set (settings, "gtk-application-prefer-dark-theme", prefer_dark, NULL);
|
||||
|
||||
screen = gdk_screen_get_default ();
|
||||
if (!screen)
|
||||
return;
|
||||
|
||||
if (theme_gtk3_is_active ())
|
||||
{
|
||||
g_object_set (settings, "gtk-application-prefer-dark-theme", dark, NULL);
|
||||
if (win_theme_provider_installed && win_theme_provider)
|
||||
{
|
||||
gtk_style_context_remove_provider_for_screen (screen,
|
||||
GTK_STYLE_PROVIDER (win_theme_provider));
|
||||
win_theme_provider_installed = FALSE;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!win_theme_provider)
|
||||
win_theme_provider = gtk_css_provider_new ();
|
||||
|
||||
css = theme_css_build_toplevel_classes ();
|
||||
gtk_css_provider_load_from_data (win_theme_provider, css, -1, NULL);
|
||||
g_free (css);
|
||||
|
||||
if (!win_theme_provider_installed)
|
||||
{
|
||||
static GtkCssProvider *win_theme_provider = NULL;
|
||||
char *css = theme_css_build_toplevel_classes ();
|
||||
|
||||
if (!win_theme_provider)
|
||||
win_theme_provider = gtk_css_provider_new ();
|
||||
|
||||
gtk_css_provider_load_from_data (win_theme_provider, css, -1, NULL);
|
||||
theme_css_apply_app_provider (GTK_STYLE_PROVIDER (win_theme_provider));
|
||||
g_free (css);
|
||||
gtk_style_context_add_provider_for_screen (screen,
|
||||
GTK_STYLE_PROVIDER (win_theme_provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 1);
|
||||
win_theme_provider_installed = TRUE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -77,7 +107,7 @@ theme_application_update_input_style (InputStyle *style)
|
||||
style->font_desc = pango_font_description_from_string ("sans 11");
|
||||
}
|
||||
|
||||
theme_css_reload_input_style (prefs.hex_gui_input_style, style->font_desc);
|
||||
theme_css_reload_input_style (FALSE, style->font_desc);
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "theme-css.h"
|
||||
|
||||
#include "theme-runtime.h"
|
||||
#include "theme-gtk3.h"
|
||||
#include "../gtkutil.h"
|
||||
#include <string.h>
|
||||
|
||||
@@ -10,7 +11,7 @@ static const char *theme_css_selector_palette_class = "zoitechat-palette";
|
||||
static const char *theme_css_selector_dark_class = "zoitechat-dark";
|
||||
static const char *theme_css_selector_light_class = "zoitechat-light";
|
||||
static const char *theme_css_palette_provider_key = "zoitechat-palette-provider";
|
||||
static const guint theme_css_provider_priority = GTK_STYLE_PROVIDER_PRIORITY_APPLICATION;
|
||||
static const guint theme_css_provider_priority = GTK_STYLE_PROVIDER_PRIORITY_USER;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -222,6 +223,8 @@ theme_css_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkR
|
||||
GString *css;
|
||||
gchar *bg_color = NULL;
|
||||
gchar *fg_color = NULL;
|
||||
gchar *sel_bg_color = NULL;
|
||||
gchar *sel_fg_color = NULL;
|
||||
|
||||
if (!widget)
|
||||
return;
|
||||
@@ -260,12 +263,30 @@ theme_css_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkR
|
||||
fg_color = gdk_rgba_to_string (fg);
|
||||
g_string_append_printf (css, " color: %s;", fg_color);
|
||||
}
|
||||
{
|
||||
GdkRGBA selection_bg;
|
||||
GdkRGBA selection_fg;
|
||||
if (theme_runtime_get_color (THEME_TOKEN_SELECTION_BACKGROUND, &selection_bg))
|
||||
sel_bg_color = gdk_rgba_to_string (&selection_bg);
|
||||
if (theme_runtime_get_color (THEME_TOKEN_SELECTION_FOREGROUND, &selection_fg))
|
||||
sel_fg_color = gdk_rgba_to_string (&selection_fg);
|
||||
}
|
||||
gtkutil_append_font_css (css, font_desc);
|
||||
g_string_append (css, " }");
|
||||
g_string_append_printf (css, ".%s *:selected {", theme_css_selector_palette_class);
|
||||
g_string_append_printf (css, ".%s, .%s *, .%s treeview, .%s treeview.view, .%s treeview.view text, .%s treeview.view cell, .%s treeview.view row, .%s list, .%s list row, .%s text {", theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class);
|
||||
if (bg)
|
||||
g_string_append (css, " background-color: @theme_selected_bg_color;");
|
||||
g_string_append_printf (css, " background-color: %s;", bg_color);
|
||||
if (fg)
|
||||
g_string_append_printf (css, " color: %s;", fg_color);
|
||||
g_string_append (css, " }");
|
||||
g_string_append_printf (css, ".%s *:selected, .%s *:selected:focus, .%s *:selected:hover, .%s treeview.view:selected, .%s treeview.view:selected:focus, .%s treeview.view:selected:hover, .%s row:selected, .%s row:selected:focus, .%s row:selected:hover {", theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class, theme_css_selector_palette_class);
|
||||
if (sel_bg_color)
|
||||
g_string_append_printf (css, " background-color: %s;", sel_bg_color);
|
||||
else if (bg)
|
||||
g_string_append (css, " background-color: @theme_selected_bg_color;");
|
||||
if (sel_fg_color)
|
||||
g_string_append_printf (css, " color: %s;", sel_fg_color);
|
||||
else if (fg)
|
||||
g_string_append (css, " color: @theme_selected_fg_color;");
|
||||
g_string_append (css, " }");
|
||||
|
||||
@@ -277,22 +298,46 @@ theme_css_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkR
|
||||
g_string_free (css, TRUE);
|
||||
g_free (bg_color);
|
||||
g_free (fg_color);
|
||||
g_free (sel_bg_color);
|
||||
g_free (sel_fg_color);
|
||||
}
|
||||
|
||||
char *
|
||||
theme_css_build_toplevel_classes (void)
|
||||
{
|
||||
return g_strdup_printf (
|
||||
"window.%s, .%s {"
|
||||
"window.%s, window.%s:backdrop, .%s {"
|
||||
"background-color: #202020;"
|
||||
"color: #f0f0f0;"
|
||||
"border-color: #202020;"
|
||||
"}"
|
||||
"window.%s, .%s {"
|
||||
"window.%s menubar, window.%s menubar:backdrop, window.%s menuitem, window.%s menuitem:backdrop {"
|
||||
"background-color: #202020;"
|
||||
"color: #f0f0f0;"
|
||||
"border-color: #202020;"
|
||||
"}"
|
||||
"window.%s, window.%s:backdrop, .%s {"
|
||||
"background-color: #f6f6f6;"
|
||||
"color: #101010;"
|
||||
"border-color: #f6f6f6;"
|
||||
"}"
|
||||
"window.%s menubar, window.%s menubar:backdrop, window.%s menuitem, window.%s menuitem:backdrop {"
|
||||
"background-color: #f6f6f6;"
|
||||
"color: #101010;"
|
||||
"border-color: #f6f6f6;"
|
||||
"}",
|
||||
theme_css_selector_dark_class,
|
||||
theme_css_selector_dark_class,
|
||||
theme_css_selector_dark_class,
|
||||
theme_css_selector_dark_class,
|
||||
theme_css_selector_dark_class,
|
||||
theme_css_selector_dark_class,
|
||||
theme_css_selector_dark_class,
|
||||
theme_css_selector_light_class,
|
||||
theme_css_selector_light_class,
|
||||
theme_css_selector_light_class,
|
||||
theme_css_selector_light_class,
|
||||
theme_css_selector_light_class,
|
||||
theme_css_selector_light_class,
|
||||
theme_css_selector_light_class);
|
||||
}
|
||||
|
||||
941
src/fe-gtk/theme/theme-gtk3.c
Normal file
941
src/fe-gtk/theme/theme-gtk3.c
Normal file
@@ -0,0 +1,941 @@
|
||||
#include "theme-gtk3.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <gio/gio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "theme-policy.h"
|
||||
#include "../../common/gtk3-theme-service.h"
|
||||
#include "../../common/zoitechat.h"
|
||||
#include "../../common/zoitechatc.h"
|
||||
|
||||
static GPtrArray *theme_gtk3_providers_base;
|
||||
static GPtrArray *theme_gtk3_providers_variant;
|
||||
static GHashTable *theme_gtk3_provider_cache;
|
||||
static gboolean theme_gtk3_active;
|
||||
static char *theme_gtk3_current_id;
|
||||
static ThemeGtk3Variant theme_gtk3_current_variant;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GHashTable *defaults;
|
||||
char **icon_search_path;
|
||||
gint icon_search_path_count;
|
||||
gboolean icon_search_path_captured;
|
||||
} ThemeGtk3SettingsState;
|
||||
|
||||
static ThemeGtk3SettingsState theme_gtk3_settings_state;
|
||||
|
||||
static gboolean settings_apply_property (GtkSettings *settings, const char *property_name, const char *raw_value);
|
||||
|
||||
static gboolean
|
||||
theme_gtk3_theme_name_is_dark (const char *name)
|
||||
{
|
||||
char *lower;
|
||||
gboolean dark;
|
||||
|
||||
if (!name || !name[0])
|
||||
return FALSE;
|
||||
|
||||
lower = g_ascii_strdown (name, -1);
|
||||
dark = strstr (lower, "dark") != NULL;
|
||||
g_free (lower);
|
||||
return dark;
|
||||
}
|
||||
|
||||
static ThemeGtk3Variant
|
||||
theme_gtk3_infer_variant (const ZoitechatGtk3Theme *theme)
|
||||
{
|
||||
char *css_dir;
|
||||
char *light_css;
|
||||
gboolean has_light_css;
|
||||
ThemeGtk3Variant variant;
|
||||
|
||||
if (!theme)
|
||||
return THEME_GTK3_VARIANT_PREFER_LIGHT;
|
||||
|
||||
css_dir = zoitechat_gtk3_theme_pick_css_dir (theme->path);
|
||||
light_css = css_dir ? g_build_filename (theme->path, css_dir, "gtk.css", NULL) : NULL;
|
||||
has_light_css = light_css && g_file_test (light_css, G_FILE_TEST_IS_REGULAR);
|
||||
g_free (light_css);
|
||||
g_free (css_dir);
|
||||
|
||||
variant = THEME_GTK3_VARIANT_PREFER_LIGHT;
|
||||
if ((theme->has_dark_variant && !has_light_css) ||
|
||||
theme_gtk3_theme_name_is_dark (theme->id) ||
|
||||
theme_gtk3_theme_name_is_dark (theme->display_name))
|
||||
variant = THEME_GTK3_VARIANT_PREFER_DARK;
|
||||
|
||||
return variant;
|
||||
}
|
||||
|
||||
static void
|
||||
settings_value_free (gpointer data)
|
||||
{
|
||||
GValue *value = data;
|
||||
|
||||
if (!value)
|
||||
return;
|
||||
|
||||
g_value_unset (value);
|
||||
g_free (value);
|
||||
}
|
||||
|
||||
static GValue *
|
||||
settings_value_dup (const GValue *source)
|
||||
{
|
||||
GValue *copy;
|
||||
|
||||
copy = g_new0 (GValue, 1);
|
||||
g_value_init (copy, G_VALUE_TYPE (source));
|
||||
g_value_copy (source, copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
static GHashTable *
|
||||
settings_defaults_table (void)
|
||||
{
|
||||
if (!theme_gtk3_settings_state.defaults)
|
||||
{
|
||||
theme_gtk3_settings_state.defaults = g_hash_table_new_full (
|
||||
g_str_hash,
|
||||
g_str_equal,
|
||||
g_free,
|
||||
settings_value_free);
|
||||
}
|
||||
|
||||
return theme_gtk3_settings_state.defaults;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
settings_rescan_icon_theme (void)
|
||||
{
|
||||
GtkIconTheme *icon_theme;
|
||||
|
||||
icon_theme = gtk_icon_theme_get_default ();
|
||||
if (!icon_theme)
|
||||
return;
|
||||
|
||||
gtk_icon_theme_rescan_if_needed (icon_theme);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_gtk3_reset_widgets (void)
|
||||
{
|
||||
GdkScreen *screen = gdk_screen_get_default ();
|
||||
|
||||
if (screen)
|
||||
gtk_style_context_reset_widgets (screen);
|
||||
}
|
||||
|
||||
static void
|
||||
settings_capture_icon_search_path (void)
|
||||
{
|
||||
GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
|
||||
|
||||
if (!icon_theme || theme_gtk3_settings_state.icon_search_path_captured)
|
||||
return;
|
||||
|
||||
gtk_icon_theme_get_search_path (icon_theme, &theme_gtk3_settings_state.icon_search_path, &theme_gtk3_settings_state.icon_search_path_count);
|
||||
theme_gtk3_settings_state.icon_search_path_captured = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
settings_append_icon_search_path (const char *path)
|
||||
{
|
||||
GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
|
||||
|
||||
if (!icon_theme || !path || !g_file_test (path, G_FILE_TEST_IS_DIR))
|
||||
return;
|
||||
|
||||
settings_capture_icon_search_path ();
|
||||
gtk_icon_theme_append_search_path (icon_theme, path);
|
||||
gtk_icon_theme_rescan_if_needed (icon_theme);
|
||||
}
|
||||
|
||||
static void
|
||||
settings_apply_icon_paths (const char *theme_root)
|
||||
{
|
||||
char *icons_dir;
|
||||
char *theme_parent;
|
||||
|
||||
if (!theme_root)
|
||||
return;
|
||||
|
||||
icons_dir = g_build_filename (theme_root, "icons", NULL);
|
||||
theme_parent = g_path_get_dirname (theme_root);
|
||||
settings_append_icon_search_path (icons_dir);
|
||||
settings_append_icon_search_path (theme_root);
|
||||
settings_append_icon_search_path (theme_parent);
|
||||
g_free (theme_parent);
|
||||
g_free (icons_dir);
|
||||
}
|
||||
|
||||
static void
|
||||
settings_restore_icon_search_path (void)
|
||||
{
|
||||
GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
|
||||
|
||||
if (!icon_theme || !theme_gtk3_settings_state.icon_search_path_captured)
|
||||
return;
|
||||
|
||||
gtk_icon_theme_set_search_path (icon_theme, (const char * const *) theme_gtk3_settings_state.icon_search_path, theme_gtk3_settings_state.icon_search_path_count);
|
||||
gtk_icon_theme_rescan_if_needed (icon_theme);
|
||||
g_strfreev (theme_gtk3_settings_state.icon_search_path);
|
||||
theme_gtk3_settings_state.icon_search_path = NULL;
|
||||
theme_gtk3_settings_state.icon_search_path_count = 0;
|
||||
theme_gtk3_settings_state.icon_search_path_captured = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
theme_gtk3_parsing_error_cb (GtkCssProvider *provider, GtkCssSection *section, const GError *error, gpointer user_data)
|
||||
{
|
||||
(void) provider;
|
||||
(void) section;
|
||||
(void) error;
|
||||
(void) user_data;
|
||||
g_signal_stop_emission_by_name (provider, "parsing-error");
|
||||
}
|
||||
|
||||
static GHashTable *
|
||||
theme_gtk3_provider_cache_table (void)
|
||||
{
|
||||
if (!theme_gtk3_provider_cache)
|
||||
{
|
||||
theme_gtk3_provider_cache = g_hash_table_new_full (
|
||||
g_str_hash,
|
||||
g_str_equal,
|
||||
g_free,
|
||||
g_object_unref);
|
||||
}
|
||||
|
||||
return theme_gtk3_provider_cache;
|
||||
}
|
||||
|
||||
static char *
|
||||
theme_gtk3_provider_cache_key (const char *theme_root, const char *css_dir, gboolean prefer_dark)
|
||||
{
|
||||
return g_strdup_printf ("%s\n%s\n%d", theme_root, css_dir, prefer_dark ? 1 : 0);
|
||||
}
|
||||
|
||||
static GtkCssProvider *
|
||||
theme_gtk3_provider_cache_load (const char *path, GError **error)
|
||||
{
|
||||
GtkCssProvider *provider;
|
||||
|
||||
provider = gtk_css_provider_new ();
|
||||
g_signal_connect (provider, "parsing-error", G_CALLBACK (theme_gtk3_parsing_error_cb), NULL);
|
||||
if (!gtk_css_provider_load_from_path (provider, path, error))
|
||||
{
|
||||
g_object_unref (provider);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return provider;
|
||||
}
|
||||
|
||||
static GtkCssProvider *
|
||||
theme_gtk3_provider_cache_get_or_load (const char *theme_root, const char *css_dir, gboolean prefer_dark, GError **error)
|
||||
{
|
||||
GHashTable *cache;
|
||||
char *key;
|
||||
char *css_path;
|
||||
GtkCssProvider *provider;
|
||||
|
||||
cache = theme_gtk3_provider_cache_table ();
|
||||
key = theme_gtk3_provider_cache_key (theme_root, css_dir, prefer_dark);
|
||||
provider = g_hash_table_lookup (cache, key);
|
||||
if (provider)
|
||||
{
|
||||
g_object_ref (provider);
|
||||
g_free (key);
|
||||
return provider;
|
||||
}
|
||||
|
||||
css_path = g_build_filename (theme_root, css_dir, prefer_dark ? "gtk-dark.css" : "gtk.css", NULL);
|
||||
provider = theme_gtk3_provider_cache_load (css_path, error);
|
||||
g_free (css_path);
|
||||
if (!provider)
|
||||
{
|
||||
g_free (key);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_hash_table_insert (cache, key, g_object_ref (provider));
|
||||
return provider;
|
||||
}
|
||||
|
||||
void
|
||||
theme_gtk3_invalidate_provider_cache (void)
|
||||
{
|
||||
if (theme_gtk3_provider_cache)
|
||||
g_hash_table_remove_all (theme_gtk3_provider_cache);
|
||||
}
|
||||
|
||||
static void
|
||||
settings_apply_for_variant (ThemeGtk3Variant variant)
|
||||
{
|
||||
GtkSettings *settings = gtk_settings_get_default ();
|
||||
gboolean dark = FALSE;
|
||||
GValue current = G_VALUE_INIT;
|
||||
GParamSpec *property;
|
||||
|
||||
if (!settings)
|
||||
return;
|
||||
|
||||
if (variant == THEME_GTK3_VARIANT_PREFER_DARK)
|
||||
dark = TRUE;
|
||||
else if (variant == THEME_GTK3_VARIANT_FOLLOW_SYSTEM)
|
||||
dark = theme_policy_system_prefers_dark ();
|
||||
|
||||
property = g_object_class_find_property (G_OBJECT_GET_CLASS (settings), "gtk-application-prefer-dark-theme");
|
||||
if (!property)
|
||||
return;
|
||||
|
||||
g_value_init (¤t, G_PARAM_SPEC_VALUE_TYPE (property));
|
||||
g_object_get_property (G_OBJECT (settings), "gtk-application-prefer-dark-theme", ¤t);
|
||||
if (g_value_get_boolean (¤t) != dark)
|
||||
g_object_set (settings, "gtk-application-prefer-dark-theme", dark, NULL);
|
||||
g_value_unset (¤t);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
settings_theme_root_is_searchable (const char *theme_root)
|
||||
{
|
||||
char *parent;
|
||||
char *user_data_themes;
|
||||
char *home_themes;
|
||||
const gchar *const *system_data_dirs;
|
||||
guint i;
|
||||
gboolean searchable = FALSE;
|
||||
|
||||
if (!theme_root || !theme_root[0])
|
||||
return FALSE;
|
||||
|
||||
parent = g_path_get_dirname (theme_root);
|
||||
user_data_themes = g_build_filename (g_get_user_data_dir (), "themes", NULL);
|
||||
home_themes = g_build_filename (g_get_home_dir (), ".themes", NULL);
|
||||
|
||||
if (g_strcmp0 (parent, user_data_themes) == 0 ||
|
||||
g_strcmp0 (parent, home_themes) == 0 ||
|
||||
g_strcmp0 (parent, "/usr/share/themes") == 0)
|
||||
searchable = TRUE;
|
||||
|
||||
system_data_dirs = g_get_system_data_dirs ();
|
||||
for (i = 0; !searchable && system_data_dirs && system_data_dirs[i]; i++)
|
||||
{
|
||||
char *system_themes = g_build_filename (system_data_dirs[i], "themes", NULL);
|
||||
if (g_strcmp0 (parent, system_themes) == 0)
|
||||
searchable = TRUE;
|
||||
g_free (system_themes);
|
||||
}
|
||||
|
||||
g_free (home_themes);
|
||||
g_free (user_data_themes);
|
||||
g_free (parent);
|
||||
return searchable;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
settings_theme_link_search_path (const char *theme_root, const char *theme_name)
|
||||
{
|
||||
char *themes_root;
|
||||
char *link_path;
|
||||
gboolean ok = TRUE;
|
||||
|
||||
if (!theme_root || !theme_name || !theme_name[0])
|
||||
return FALSE;
|
||||
|
||||
themes_root = g_build_filename (g_get_user_data_dir (), "themes", NULL);
|
||||
if (g_mkdir_with_parents (themes_root, 0700) != 0)
|
||||
{
|
||||
g_free (themes_root);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
link_path = g_build_filename (themes_root, theme_name, NULL);
|
||||
if (!g_file_test (link_path, G_FILE_TEST_EXISTS))
|
||||
{
|
||||
GFile *link_file = g_file_new_for_path (link_path);
|
||||
GError *link_error = NULL;
|
||||
ok = g_file_make_symbolic_link (link_file, theme_root, NULL, &link_error);
|
||||
g_clear_error (&link_error);
|
||||
g_object_unref (link_file);
|
||||
}
|
||||
|
||||
g_free (link_path);
|
||||
g_free (themes_root);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void
|
||||
settings_apply_theme_name (const char *theme_root)
|
||||
{
|
||||
GtkSettings *settings;
|
||||
char *theme_name;
|
||||
|
||||
if (!theme_root)
|
||||
return;
|
||||
|
||||
settings = gtk_settings_get_default ();
|
||||
if (!settings)
|
||||
return;
|
||||
|
||||
theme_name = g_path_get_basename (theme_root);
|
||||
if (theme_name && theme_name[0])
|
||||
{
|
||||
gboolean searchable = settings_theme_root_is_searchable (theme_root);
|
||||
if (!searchable)
|
||||
searchable = settings_theme_link_search_path (theme_root, theme_name);
|
||||
if (searchable)
|
||||
settings_apply_property (settings, "gtk-theme-name", theme_name);
|
||||
}
|
||||
g_free (theme_name);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
settings_value_equal_typed (const GValue *a, const GValue *b, GType property_type)
|
||||
{
|
||||
if (property_type == G_TYPE_BOOLEAN)
|
||||
return g_value_get_boolean (a) == g_value_get_boolean (b);
|
||||
if (property_type == G_TYPE_STRING)
|
||||
return g_strcmp0 (g_value_get_string (a), g_value_get_string (b)) == 0;
|
||||
if (property_type == G_TYPE_INT)
|
||||
return g_value_get_int (a) == g_value_get_int (b);
|
||||
if (property_type == G_TYPE_UINT)
|
||||
return g_value_get_uint (a) == g_value_get_uint (b);
|
||||
if (property_type == G_TYPE_FLOAT)
|
||||
return g_value_get_float (a) == g_value_get_float (b);
|
||||
if (property_type == G_TYPE_DOUBLE)
|
||||
return g_value_get_double (a) == g_value_get_double (b);
|
||||
if (G_TYPE_IS_ENUM (property_type))
|
||||
return g_value_get_enum (a) == g_value_get_enum (b);
|
||||
if (G_TYPE_IS_FLAGS (property_type))
|
||||
return g_value_get_flags (a) == g_value_get_flags (b);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
settings_parse_long (const char *text, glong min_value, glong max_value, glong *value)
|
||||
{
|
||||
char *end = NULL;
|
||||
gint64 parsed;
|
||||
|
||||
if (!text)
|
||||
return FALSE;
|
||||
|
||||
parsed = g_ascii_strtoll (text, &end, 10);
|
||||
if (end == text || *end != '\0')
|
||||
return FALSE;
|
||||
if (parsed < min_value || parsed > max_value)
|
||||
return FALSE;
|
||||
|
||||
*value = (glong) parsed;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
settings_remember_default (GtkSettings *settings, const char *property_name, GParamSpec *property)
|
||||
{
|
||||
GHashTable *defaults;
|
||||
GValue current = G_VALUE_INIT;
|
||||
|
||||
if (!settings || !property_name || !property)
|
||||
return;
|
||||
|
||||
defaults = settings_defaults_table ();
|
||||
if (g_hash_table_contains (defaults, property_name))
|
||||
return;
|
||||
|
||||
g_value_init (¤t, G_PARAM_SPEC_VALUE_TYPE (property));
|
||||
g_object_get_property (G_OBJECT (settings), property_name, ¤t);
|
||||
g_hash_table_insert (defaults, g_strdup (property_name), settings_value_dup (¤t));
|
||||
g_value_unset (¤t);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
settings_apply_property (GtkSettings *settings, const char *property_name, const char *raw_value)
|
||||
{
|
||||
GParamSpec *property;
|
||||
GValue value = G_VALUE_INIT;
|
||||
GValue current = G_VALUE_INIT;
|
||||
GType property_type;
|
||||
gboolean ok = FALSE;
|
||||
gboolean changed = TRUE;
|
||||
|
||||
property = g_object_class_find_property (G_OBJECT_GET_CLASS (settings), property_name);
|
||||
if (!property)
|
||||
return FALSE;
|
||||
|
||||
settings_remember_default (settings, property_name, property);
|
||||
property_type = G_PARAM_SPEC_VALUE_TYPE (property);
|
||||
g_value_init (&value, property_type);
|
||||
|
||||
if (property_type == G_TYPE_BOOLEAN)
|
||||
{
|
||||
if (g_ascii_strcasecmp (raw_value, "true") == 0 ||
|
||||
g_ascii_strcasecmp (raw_value, "yes") == 0 ||
|
||||
g_strcmp0 (raw_value, "1") == 0)
|
||||
{
|
||||
g_value_set_boolean (&value, TRUE);
|
||||
ok = TRUE;
|
||||
}
|
||||
else if (g_ascii_strcasecmp (raw_value, "false") == 0 ||
|
||||
g_ascii_strcasecmp (raw_value, "no") == 0 ||
|
||||
g_strcmp0 (raw_value, "0") == 0)
|
||||
{
|
||||
g_value_set_boolean (&value, FALSE);
|
||||
ok = TRUE;
|
||||
}
|
||||
}
|
||||
else if (property_type == G_TYPE_STRING)
|
||||
{
|
||||
g_value_set_string (&value, raw_value);
|
||||
ok = TRUE;
|
||||
}
|
||||
else if (property_type == G_TYPE_INT)
|
||||
{
|
||||
glong parsed;
|
||||
if (settings_parse_long (raw_value, G_MININT, G_MAXINT, &parsed))
|
||||
{
|
||||
g_value_set_int (&value, (gint) parsed);
|
||||
ok = TRUE;
|
||||
}
|
||||
}
|
||||
else if (property_type == G_TYPE_UINT)
|
||||
{
|
||||
glong parsed;
|
||||
if (settings_parse_long (raw_value, 0, G_MAXUINT, &parsed))
|
||||
{
|
||||
g_value_set_uint (&value, (guint) parsed);
|
||||
ok = TRUE;
|
||||
}
|
||||
}
|
||||
else if (property_type == G_TYPE_DOUBLE)
|
||||
{
|
||||
char *end = NULL;
|
||||
double parsed = g_ascii_strtod (raw_value, &end);
|
||||
if (end != raw_value && *end == '\0')
|
||||
{
|
||||
g_value_set_double (&value, parsed);
|
||||
ok = TRUE;
|
||||
}
|
||||
}
|
||||
else if (property_type == G_TYPE_FLOAT)
|
||||
{
|
||||
char *end = NULL;
|
||||
double parsed = g_ascii_strtod (raw_value, &end);
|
||||
if (end != raw_value && *end == '\0')
|
||||
{
|
||||
g_value_set_float (&value, (gfloat) parsed);
|
||||
ok = TRUE;
|
||||
}
|
||||
}
|
||||
else if (G_TYPE_IS_ENUM (property_type))
|
||||
{
|
||||
GEnumClass *enum_class = g_type_class_ref (property_type);
|
||||
GEnumValue *enum_value = g_enum_get_value_by_nick (enum_class, raw_value);
|
||||
if (!enum_value)
|
||||
enum_value = g_enum_get_value_by_name (enum_class, raw_value);
|
||||
if (!enum_value)
|
||||
{
|
||||
glong parsed;
|
||||
if (settings_parse_long (raw_value, G_MININT, G_MAXINT, &parsed))
|
||||
enum_value = g_enum_get_value (enum_class, (gint) parsed);
|
||||
}
|
||||
if (enum_value)
|
||||
{
|
||||
g_value_set_enum (&value, enum_value->value);
|
||||
ok = TRUE;
|
||||
}
|
||||
g_type_class_unref (enum_class);
|
||||
}
|
||||
else if (G_TYPE_IS_FLAGS (property_type))
|
||||
{
|
||||
GFlagsClass *flags_class = g_type_class_ref (property_type);
|
||||
char **tokens = g_strsplit_set (raw_value, ",|", -1);
|
||||
guint flags_value = 0;
|
||||
guint i = 0;
|
||||
for (; tokens && tokens[i]; i++)
|
||||
{
|
||||
char *token = g_strstrip (tokens[i]);
|
||||
GFlagsValue *flag_value;
|
||||
if (!token[0])
|
||||
continue;
|
||||
flag_value = g_flags_get_value_by_nick (flags_class, token);
|
||||
if (!flag_value)
|
||||
flag_value = g_flags_get_value_by_name (flags_class, token);
|
||||
if (!flag_value)
|
||||
{
|
||||
glong parsed;
|
||||
if (!settings_parse_long (token, 0, G_MAXUINT, &parsed))
|
||||
{
|
||||
ok = FALSE;
|
||||
break;
|
||||
}
|
||||
flags_value |= (guint) parsed;
|
||||
ok = TRUE;
|
||||
continue;
|
||||
}
|
||||
flags_value |= flag_value->value;
|
||||
ok = TRUE;
|
||||
}
|
||||
if (ok)
|
||||
g_value_set_flags (&value, flags_value);
|
||||
g_strfreev (tokens);
|
||||
g_type_class_unref (flags_class);
|
||||
}
|
||||
|
||||
|
||||
if (ok)
|
||||
{
|
||||
g_value_init (¤t, property_type);
|
||||
g_object_get_property (G_OBJECT (settings), property_name, ¤t);
|
||||
changed = !settings_value_equal_typed (¤t, &value, property_type);
|
||||
g_value_unset (¤t);
|
||||
}
|
||||
|
||||
if (ok && changed)
|
||||
g_object_set_property (G_OBJECT (settings), property_name, &value);
|
||||
|
||||
g_value_unset (&value);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void
|
||||
settings_restore_defaults (void)
|
||||
{
|
||||
GtkSettings *settings = gtk_settings_get_default ();
|
||||
GHashTableIter iter;
|
||||
gpointer key;
|
||||
gpointer value;
|
||||
|
||||
if (settings && theme_gtk3_settings_state.defaults)
|
||||
{
|
||||
g_hash_table_iter_init (&iter, theme_gtk3_settings_state.defaults);
|
||||
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||
g_object_set_property (G_OBJECT (settings), (const char *) key, (const GValue *) value);
|
||||
|
||||
g_hash_table_remove_all (theme_gtk3_settings_state.defaults);
|
||||
}
|
||||
|
||||
settings_rescan_icon_theme ();
|
||||
settings_restore_icon_search_path ();
|
||||
}
|
||||
|
||||
static void
|
||||
settings_cleanup (void)
|
||||
{
|
||||
if (theme_gtk3_settings_state.defaults)
|
||||
{
|
||||
g_hash_table_destroy (theme_gtk3_settings_state.defaults);
|
||||
theme_gtk3_settings_state.defaults = NULL;
|
||||
}
|
||||
|
||||
if (theme_gtk3_settings_state.icon_search_path_captured)
|
||||
settings_restore_icon_search_path ();
|
||||
}
|
||||
|
||||
static void
|
||||
settings_apply_from_file (const char *theme_root, const char *css_dir)
|
||||
{
|
||||
GtkSettings *settings;
|
||||
GPtrArray *settings_paths;
|
||||
char *selected_path;
|
||||
char *fallback_path;
|
||||
guint layer;
|
||||
|
||||
settings = gtk_settings_get_default ();
|
||||
if (!settings)
|
||||
return;
|
||||
|
||||
if (!css_dir)
|
||||
return;
|
||||
|
||||
settings_apply_icon_paths (theme_root);
|
||||
settings_paths = g_ptr_array_new_with_free_func (g_free);
|
||||
selected_path = g_build_filename (theme_root, css_dir, "settings.ini", NULL);
|
||||
fallback_path = g_build_filename (theme_root, "gtk-3.0", "settings.ini", NULL);
|
||||
if (g_strcmp0 (css_dir, "gtk-3.0") != 0)
|
||||
g_ptr_array_add (settings_paths, fallback_path);
|
||||
else
|
||||
g_free (fallback_path);
|
||||
g_ptr_array_add (settings_paths, selected_path);
|
||||
|
||||
for (layer = 0; layer < settings_paths->len; layer++)
|
||||
{
|
||||
GKeyFile *keyfile;
|
||||
char **keys;
|
||||
gsize n_keys = 0;
|
||||
gsize i;
|
||||
const char *settings_path = g_ptr_array_index (settings_paths, layer);
|
||||
|
||||
if (!g_file_test (settings_path, G_FILE_TEST_IS_REGULAR))
|
||||
continue;
|
||||
|
||||
keyfile = g_key_file_new ();
|
||||
if (!g_key_file_load_from_file (keyfile, settings_path, G_KEY_FILE_NONE, NULL))
|
||||
{
|
||||
g_key_file_unref (keyfile);
|
||||
continue;
|
||||
}
|
||||
|
||||
keys = g_key_file_get_keys (keyfile, "Settings", &n_keys, NULL);
|
||||
for (i = 0; keys && i < n_keys; i++)
|
||||
{
|
||||
char *raw_value;
|
||||
|
||||
raw_value = g_key_file_get_value (keyfile, "Settings", keys[i], NULL);
|
||||
if (!raw_value)
|
||||
continue;
|
||||
|
||||
settings_apply_property (settings, keys[i], raw_value);
|
||||
g_free (raw_value);
|
||||
}
|
||||
|
||||
g_strfreev (keys);
|
||||
g_key_file_unref (keyfile);
|
||||
}
|
||||
|
||||
settings_rescan_icon_theme ();
|
||||
g_ptr_array_unref (settings_paths);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_gtk3_remove_provider (void)
|
||||
{
|
||||
GdkScreen *screen = gdk_screen_get_default ();
|
||||
guint i;
|
||||
|
||||
if (screen && theme_gtk3_providers_variant)
|
||||
{
|
||||
for (i = 0; i < theme_gtk3_providers_variant->len; i++)
|
||||
{
|
||||
GtkCssProvider *provider = g_ptr_array_index (theme_gtk3_providers_variant, i);
|
||||
gtk_style_context_remove_provider_for_screen (screen, GTK_STYLE_PROVIDER (provider));
|
||||
}
|
||||
}
|
||||
if (screen && theme_gtk3_providers_base)
|
||||
{
|
||||
for (i = 0; i < theme_gtk3_providers_base->len; i++)
|
||||
{
|
||||
GtkCssProvider *provider = g_ptr_array_index (theme_gtk3_providers_base, i);
|
||||
gtk_style_context_remove_provider_for_screen (screen, GTK_STYLE_PROVIDER (provider));
|
||||
}
|
||||
}
|
||||
|
||||
if (theme_gtk3_providers_variant)
|
||||
g_ptr_array_unref (theme_gtk3_providers_variant);
|
||||
if (theme_gtk3_providers_base)
|
||||
g_ptr_array_unref (theme_gtk3_providers_base);
|
||||
theme_gtk3_providers_variant = NULL;
|
||||
theme_gtk3_providers_base = NULL;
|
||||
settings_restore_defaults ();
|
||||
theme_gtk3_reset_widgets ();
|
||||
theme_gtk3_active = FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
load_css_with_variant (ZoitechatGtk3Theme *theme, ThemeGtk3Variant variant, GError **error)
|
||||
{
|
||||
gboolean prefer_dark = FALSE;
|
||||
GdkScreen *screen;
|
||||
GPtrArray *chain;
|
||||
guint i;
|
||||
|
||||
if (variant == THEME_GTK3_VARIANT_PREFER_DARK)
|
||||
prefer_dark = TRUE;
|
||||
else if (variant == THEME_GTK3_VARIANT_FOLLOW_SYSTEM)
|
||||
prefer_dark = theme_policy_system_prefers_dark ();
|
||||
|
||||
settings_apply_theme_name (theme->path);
|
||||
|
||||
chain = zoitechat_gtk3_theme_build_inheritance_chain (theme->path);
|
||||
if (!chain || chain->len == 0)
|
||||
{
|
||||
if (chain)
|
||||
g_ptr_array_unref (chain);
|
||||
return g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_NOENT, "GTK3 theme CSS not found."), FALSE;
|
||||
}
|
||||
|
||||
theme_gtk3_providers_base = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
theme_gtk3_providers_variant = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
|
||||
screen = gdk_screen_get_default ();
|
||||
for (i = 0; i < chain->len; i++)
|
||||
{
|
||||
const char *theme_root = g_ptr_array_index (chain, i);
|
||||
char *css_dir = zoitechat_gtk3_theme_pick_css_dir_for_minor (theme_root, gtk_get_minor_version ());
|
||||
char *variant_css;
|
||||
GtkCssProvider *provider;
|
||||
GtkCssProvider *variant_provider;
|
||||
|
||||
if (!css_dir)
|
||||
continue;
|
||||
|
||||
provider = theme_gtk3_provider_cache_get_or_load (theme_root, css_dir, FALSE, error);
|
||||
if (!provider)
|
||||
{
|
||||
g_free (css_dir);
|
||||
g_ptr_array_unref (chain);
|
||||
return FALSE;
|
||||
}
|
||||
if (screen)
|
||||
gtk_style_context_add_provider_for_screen (screen,
|
||||
GTK_STYLE_PROVIDER (provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_USER + (gint) (i * 2));
|
||||
g_ptr_array_add (theme_gtk3_providers_base, provider);
|
||||
|
||||
variant_css = g_build_filename (theme_root, css_dir, "gtk-dark.css", NULL);
|
||||
if (prefer_dark && g_file_test (variant_css, G_FILE_TEST_IS_REGULAR))
|
||||
{
|
||||
variant_provider = theme_gtk3_provider_cache_get_or_load (theme_root, css_dir, TRUE, error);
|
||||
if (!variant_provider)
|
||||
{
|
||||
g_free (variant_css);
|
||||
g_free (css_dir);
|
||||
g_ptr_array_unref (chain);
|
||||
return FALSE;
|
||||
}
|
||||
if (screen)
|
||||
gtk_style_context_add_provider_for_screen (screen,
|
||||
GTK_STYLE_PROVIDER (variant_provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_USER + (gint) (i * 2) + 1);
|
||||
g_ptr_array_add (theme_gtk3_providers_variant, variant_provider);
|
||||
}
|
||||
g_free (variant_css);
|
||||
|
||||
settings_apply_from_file (theme_root, css_dir);
|
||||
g_free (css_dir);
|
||||
}
|
||||
|
||||
g_ptr_array_unref (chain);
|
||||
settings_apply_for_variant (variant);
|
||||
theme_gtk3_reset_widgets ();
|
||||
theme_gtk3_active = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
theme_gtk3_apply_internal (const char *theme_id, ThemeGtk3Variant variant, gboolean force_reload, GError **error)
|
||||
{
|
||||
ZoitechatGtk3Theme *theme;
|
||||
char *previous_id = g_strdup (theme_gtk3_current_id);
|
||||
ThemeGtk3Variant previous_variant = theme_gtk3_current_variant;
|
||||
gboolean had_previous = theme_gtk3_active && previous_id && previous_id[0];
|
||||
gboolean ok;
|
||||
|
||||
if (!force_reload &&
|
||||
theme_gtk3_active &&
|
||||
g_strcmp0 (theme_gtk3_current_id, theme_id) == 0 &&
|
||||
theme_gtk3_current_variant == variant)
|
||||
return TRUE;
|
||||
|
||||
theme = zoitechat_gtk3_theme_find_by_id (theme_id);
|
||||
if (!theme)
|
||||
{
|
||||
g_free (previous_id);
|
||||
return g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_NOENT, "GTK3 theme not found."), FALSE;
|
||||
}
|
||||
|
||||
theme_gtk3_remove_provider ();
|
||||
if (force_reload)
|
||||
theme_gtk3_invalidate_provider_cache ();
|
||||
ok = load_css_with_variant (theme, variant, error);
|
||||
zoitechat_gtk3_theme_free (theme);
|
||||
|
||||
if (ok)
|
||||
{
|
||||
g_free (theme_gtk3_current_id);
|
||||
theme_gtk3_current_id = g_strdup (theme_id);
|
||||
theme_gtk3_current_variant = variant;
|
||||
g_free (previous_id);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (had_previous)
|
||||
{
|
||||
GError *restore_error = NULL;
|
||||
theme = zoitechat_gtk3_theme_find_by_id (previous_id);
|
||||
if (theme)
|
||||
{
|
||||
if (load_css_with_variant (theme, previous_variant, &restore_error))
|
||||
{
|
||||
g_free (theme_gtk3_current_id);
|
||||
theme_gtk3_current_id = g_strdup (previous_id);
|
||||
theme_gtk3_current_variant = previous_variant;
|
||||
}
|
||||
zoitechat_gtk3_theme_free (theme);
|
||||
}
|
||||
g_clear_error (&restore_error);
|
||||
}
|
||||
|
||||
g_free (previous_id);
|
||||
return ok;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_gtk3_apply (const char *theme_id, ThemeGtk3Variant variant, GError **error)
|
||||
{
|
||||
return theme_gtk3_apply_internal (theme_id, variant, FALSE, error);
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_gtk3_refresh (const char *theme_id, ThemeGtk3Variant variant, GError **error)
|
||||
{
|
||||
return theme_gtk3_apply_internal (theme_id, variant, TRUE, error);
|
||||
}
|
||||
|
||||
ThemeGtk3Variant
|
||||
theme_gtk3_variant_for_theme (const char *theme_id)
|
||||
{
|
||||
ZoitechatGtk3Theme *theme;
|
||||
ThemeGtk3Variant variant;
|
||||
|
||||
theme = zoitechat_gtk3_theme_find_by_id (theme_id);
|
||||
if (!theme)
|
||||
return THEME_GTK3_VARIANT_PREFER_LIGHT;
|
||||
|
||||
variant = theme_gtk3_infer_variant (theme);
|
||||
zoitechat_gtk3_theme_free (theme);
|
||||
return variant;
|
||||
}
|
||||
|
||||
void
|
||||
theme_gtk3_disable (void)
|
||||
{
|
||||
theme_gtk3_remove_provider ();
|
||||
g_clear_pointer (&theme_gtk3_current_id, g_free);
|
||||
theme_gtk3_invalidate_provider_cache ();
|
||||
g_clear_pointer (&theme_gtk3_provider_cache, g_hash_table_destroy);
|
||||
settings_cleanup ();
|
||||
}
|
||||
|
||||
void
|
||||
theme_gtk3_init (void)
|
||||
{
|
||||
theme_gtk3_apply_current (NULL);
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_gtk3_apply_current (GError **error)
|
||||
{
|
||||
if (!prefs.hex_gui_gtk3_theme[0])
|
||||
{
|
||||
theme_gtk3_disable ();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return theme_gtk3_apply (prefs.hex_gui_gtk3_theme, (ThemeGtk3Variant) prefs.hex_gui_gtk3_variant, error);
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_gtk3_is_active (void)
|
||||
{
|
||||
return theme_gtk3_active;
|
||||
}
|
||||
22
src/fe-gtk/theme/theme-gtk3.h
Normal file
22
src/fe-gtk/theme/theme-gtk3.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef ZOITECHAT_THEME_GTK3_H
|
||||
#define ZOITECHAT_THEME_GTK3_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
THEME_GTK3_VARIANT_FOLLOW_SYSTEM = 0,
|
||||
THEME_GTK3_VARIANT_PREFER_LIGHT = 1,
|
||||
THEME_GTK3_VARIANT_PREFER_DARK = 2
|
||||
} ThemeGtk3Variant;
|
||||
|
||||
void theme_gtk3_init (void);
|
||||
gboolean theme_gtk3_apply_current (GError **error);
|
||||
gboolean theme_gtk3_apply (const char *theme_id, ThemeGtk3Variant variant, GError **error);
|
||||
gboolean theme_gtk3_refresh (const char *theme_id, ThemeGtk3Variant variant, GError **error);
|
||||
ThemeGtk3Variant theme_gtk3_variant_for_theme (const char *theme_id);
|
||||
void theme_gtk3_invalidate_provider_cache (void);
|
||||
void theme_gtk3_disable (void);
|
||||
gboolean theme_gtk3_is_active (void);
|
||||
|
||||
#endif
|
||||
@@ -2,18 +2,22 @@
|
||||
#include "theme-manager.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "theme-application.h"
|
||||
#include "theme-policy.h"
|
||||
#include "theme-runtime.h"
|
||||
#include "theme-access.h"
|
||||
#include "theme-css.h"
|
||||
#include "theme-gtk3.h"
|
||||
#include "../gtkutil.h"
|
||||
#include "../maingui.h"
|
||||
#include "../setup.h"
|
||||
#include "../../common/zoitechat.h"
|
||||
#include "../../common/zoitechatc.h"
|
||||
|
||||
void theme_runtime_reset_mode_colors (gboolean dark_mode);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guint id;
|
||||
@@ -26,6 +30,38 @@ static GHashTable *theme_manager_listeners;
|
||||
static guint theme_manager_next_listener_id = 1;
|
||||
static guint theme_manager_setup_listener_id;
|
||||
static const char theme_manager_window_destroy_handler_key[] = "theme-manager-window-destroy-handler";
|
||||
static const char theme_manager_window_csd_headerbar_key[] = "theme-manager-window-csd-headerbar";
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gboolean initialized;
|
||||
gboolean resolved_dark_preference;
|
||||
char gtk3_theme_id[sizeof prefs.hex_gui_gtk3_theme];
|
||||
int gtk3_variant;
|
||||
} ThemeManagerAutoRefreshCache;
|
||||
|
||||
static ThemeManagerAutoRefreshCache theme_manager_auto_refresh_cache;
|
||||
|
||||
static void theme_manager_apply_platform_window_theme (GtkWidget *window);
|
||||
|
||||
static void
|
||||
theme_manager_apply_to_toplevel_windows (void)
|
||||
{
|
||||
GList *toplevels;
|
||||
GList *iter;
|
||||
|
||||
toplevels = gtk_window_list_toplevels ();
|
||||
for (iter = toplevels; iter != NULL; iter = iter->next)
|
||||
{
|
||||
GtkWidget *window = GTK_WIDGET (iter->data);
|
||||
|
||||
if (!GTK_IS_WINDOW (window) || gtk_widget_get_mapped (window) == FALSE)
|
||||
continue;
|
||||
|
||||
theme_manager_apply_platform_window_theme (window);
|
||||
}
|
||||
g_list_free (toplevels);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_listener_free (gpointer data)
|
||||
@@ -76,27 +112,59 @@ theme_manager_synthesize_preference_reasons (const struct zoitechatprefs *old_pr
|
||||
return reasons;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
theme_manager_should_refresh_gtk3 (void)
|
||||
{
|
||||
return prefs.hex_gui_gtk3_variant == THEME_GTK3_VARIANT_FOLLOW_SYSTEM;
|
||||
}
|
||||
|
||||
static void
|
||||
theme_manager_auto_dark_mode_changed (GtkSettings *settings, GParamSpec *pspec, gpointer data)
|
||||
{
|
||||
gboolean color_change = FALSE;
|
||||
gboolean should_refresh_gtk3;
|
||||
gboolean gtk3_refresh;
|
||||
gboolean resolved_dark_preference;
|
||||
static gboolean in_handler = FALSE;
|
||||
|
||||
(void) settings;
|
||||
(void) pspec;
|
||||
(void) data;
|
||||
|
||||
if (prefs.hex_gui_dark_mode != ZOITECHAT_DARK_MODE_AUTO)
|
||||
resolved_dark_preference = theme_policy_system_prefers_dark ();
|
||||
gtk3_refresh = theme_manager_should_refresh_gtk3 ();
|
||||
should_refresh_gtk3 = gtk3_refresh || prefs.hex_gui_dark_mode == ZOITECHAT_DARK_MODE_AUTO;
|
||||
|
||||
if (theme_manager_auto_refresh_cache.initialized &&
|
||||
theme_manager_auto_refresh_cache.resolved_dark_preference == resolved_dark_preference &&
|
||||
theme_manager_auto_refresh_cache.gtk3_variant == prefs.hex_gui_gtk3_variant &&
|
||||
g_strcmp0 (theme_manager_auto_refresh_cache.gtk3_theme_id, prefs.hex_gui_gtk3_theme) == 0)
|
||||
return;
|
||||
|
||||
theme_manager_auto_refresh_cache.initialized = TRUE;
|
||||
theme_manager_auto_refresh_cache.resolved_dark_preference = resolved_dark_preference;
|
||||
theme_manager_auto_refresh_cache.gtk3_variant = prefs.hex_gui_gtk3_variant;
|
||||
g_strlcpy (theme_manager_auto_refresh_cache.gtk3_theme_id,
|
||||
prefs.hex_gui_gtk3_theme,
|
||||
sizeof (theme_manager_auto_refresh_cache.gtk3_theme_id));
|
||||
|
||||
if (prefs.hex_gui_dark_mode != ZOITECHAT_DARK_MODE_AUTO && !gtk3_refresh)
|
||||
return;
|
||||
if (in_handler)
|
||||
return;
|
||||
|
||||
in_handler = TRUE;
|
||||
|
||||
fe_set_auto_dark_mode_state (theme_policy_system_prefers_dark ());
|
||||
theme_manager_commit_preferences (prefs.hex_gui_dark_mode, &color_change);
|
||||
if (color_change)
|
||||
theme_manager_dispatch_changed (THEME_CHANGED_REASON_PALETTE | THEME_CHANGED_REASON_WIDGET_STYLE | THEME_CHANGED_REASON_USERLIST | THEME_CHANGED_REASON_MODE);
|
||||
if (prefs.hex_gui_dark_mode == ZOITECHAT_DARK_MODE_AUTO)
|
||||
{
|
||||
fe_set_auto_dark_mode_state (resolved_dark_preference);
|
||||
theme_manager_commit_preferences (prefs.hex_gui_dark_mode, &color_change);
|
||||
if (color_change)
|
||||
theme_manager_dispatch_changed (THEME_CHANGED_REASON_PALETTE | THEME_CHANGED_REASON_WIDGET_STYLE | THEME_CHANGED_REASON_USERLIST | THEME_CHANGED_REASON_MODE);
|
||||
}
|
||||
|
||||
if (should_refresh_gtk3)
|
||||
theme_gtk3_apply_current (NULL);
|
||||
|
||||
in_handler = FALSE;
|
||||
}
|
||||
@@ -141,6 +209,7 @@ theme_manager_init (void)
|
||||
fe_set_auto_dark_mode_state (theme_policy_system_prefers_dark ());
|
||||
|
||||
theme_application_apply_mode (prefs.hex_gui_dark_mode, NULL);
|
||||
theme_gtk3_init ();
|
||||
zoitechat_set_theme_post_apply_callback (theme_manager_handle_theme_applied);
|
||||
|
||||
if (settings)
|
||||
@@ -167,22 +236,37 @@ theme_manager_set_mode (unsigned int mode, gboolean *palette_changed)
|
||||
void
|
||||
theme_manager_set_token_color (unsigned int mode, ThemeSemanticToken token, const GdkRGBA *color, gboolean *palette_changed)
|
||||
{
|
||||
gboolean dark;
|
||||
gboolean changed = FALSE;
|
||||
|
||||
if (!color)
|
||||
return;
|
||||
|
||||
dark = theme_policy_is_dark_mode_active (mode);
|
||||
if (dark)
|
||||
theme_runtime_dark_set_color (token, color);
|
||||
else
|
||||
theme_runtime_user_set_color (token, color);
|
||||
(void) mode;
|
||||
theme_runtime_user_set_color (token, color);
|
||||
|
||||
changed = theme_runtime_apply_mode (mode, NULL);
|
||||
theme_runtime_apply_mode (ZOITECHAT_DARK_MODE_LIGHT, &changed);
|
||||
if (palette_changed)
|
||||
*palette_changed = changed;
|
||||
|
||||
if (changed)
|
||||
theme_manager_dispatch_changed (THEME_CHANGED_REASON_PALETTE | THEME_CHANGED_REASON_WIDGET_STYLE | THEME_CHANGED_REASON_USERLIST);
|
||||
|
||||
theme_application_reload_input_style ();
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_reset_mode_colors (unsigned int mode, gboolean *palette_changed)
|
||||
{
|
||||
gboolean changed;
|
||||
|
||||
(void) mode;
|
||||
theme_runtime_reset_mode_colors (FALSE);
|
||||
theme_runtime_apply_mode (ZOITECHAT_DARK_MODE_LIGHT, &changed);
|
||||
changed = TRUE;
|
||||
if (palette_changed)
|
||||
*palette_changed = changed;
|
||||
theme_manager_dispatch_changed (THEME_CHANGED_REASON_PALETTE | THEME_CHANGED_REASON_WIDGET_STYLE | THEME_CHANGED_REASON_USERLIST);
|
||||
|
||||
theme_application_reload_input_style ();
|
||||
}
|
||||
|
||||
@@ -231,6 +315,13 @@ theme_manager_dispatch_changed (ThemeChangedReason reasons)
|
||||
|
||||
event.reasons = reasons;
|
||||
|
||||
if ((reasons & (THEME_CHANGED_REASON_MODE |
|
||||
THEME_CHANGED_REASON_THEME_PACK |
|
||||
THEME_CHANGED_REASON_WIDGET_STYLE)) != 0)
|
||||
{
|
||||
theme_manager_apply_to_toplevel_windows ();
|
||||
}
|
||||
|
||||
if (!theme_manager_listeners)
|
||||
return;
|
||||
|
||||
@@ -284,11 +375,99 @@ theme_listener_unregister (guint listener_id)
|
||||
void
|
||||
theme_manager_handle_theme_applied (void)
|
||||
{
|
||||
theme_runtime_load ();
|
||||
theme_runtime_apply_mode (prefs.hex_gui_dark_mode, NULL);
|
||||
theme_gtk3_invalidate_provider_cache ();
|
||||
if (prefs.hex_gui_gtk3_theme[0])
|
||||
theme_gtk3_refresh (prefs.hex_gui_gtk3_theme, (ThemeGtk3Variant) prefs.hex_gui_gtk3_variant, NULL);
|
||||
theme_application_apply_mode (prefs.hex_gui_dark_mode, NULL);
|
||||
theme_manager_dispatch_changed (THEME_CHANGED_REASON_THEME_PACK | THEME_CHANGED_REASON_PALETTE | THEME_CHANGED_REASON_WIDGET_STYLE | THEME_CHANGED_REASON_USERLIST | THEME_CHANGED_REASON_MODE);
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
theme_manager_is_kde_wayland (void)
|
||||
{
|
||||
const char *wayland_display;
|
||||
const char *desktop;
|
||||
char *desktop_lower;
|
||||
gboolean is_kde;
|
||||
|
||||
wayland_display = g_getenv ("WAYLAND_DISPLAY");
|
||||
if (!wayland_display || !wayland_display[0])
|
||||
return FALSE;
|
||||
|
||||
desktop = g_getenv ("XDG_CURRENT_DESKTOP");
|
||||
if (!desktop || !desktop[0])
|
||||
desktop = g_getenv ("XDG_SESSION_DESKTOP");
|
||||
if (!desktop || !desktop[0])
|
||||
return FALSE;
|
||||
|
||||
desktop_lower = g_ascii_strdown (desktop, -1);
|
||||
is_kde = strstr (desktop_lower, "kde") != NULL || strstr (desktop_lower, "plasma") != NULL;
|
||||
g_free (desktop_lower);
|
||||
return is_kde;
|
||||
}
|
||||
|
||||
static void
|
||||
theme_manager_apply_wayland_kde_csd (GtkWidget *window)
|
||||
{
|
||||
GtkWindow *gtk_window;
|
||||
GtkWidget *headerbar;
|
||||
gboolean enable_csd;
|
||||
|
||||
if (!window || !GTK_IS_WINDOW (window))
|
||||
return;
|
||||
|
||||
gtk_window = GTK_WINDOW (window);
|
||||
enable_csd = theme_gtk3_is_active () && theme_manager_is_kde_wayland ();
|
||||
headerbar = g_object_get_data (G_OBJECT (window), theme_manager_window_csd_headerbar_key);
|
||||
|
||||
if (enable_csd)
|
||||
{
|
||||
if (!headerbar)
|
||||
{
|
||||
GtkWidget *icon_image;
|
||||
GdkPixbuf *icon_pixbuf;
|
||||
|
||||
if (gtk_widget_get_realized (window))
|
||||
return;
|
||||
|
||||
headerbar = gtk_header_bar_new ();
|
||||
gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (headerbar), TRUE);
|
||||
gtk_header_bar_set_decoration_layout (GTK_HEADER_BAR (headerbar), "menu:minimize,maximize,close");
|
||||
icon_pixbuf = gdk_pixbuf_new_from_resource_at_scale ("/icons/zoitechat.png", 32, 32, TRUE, NULL);
|
||||
icon_image = icon_pixbuf ? gtk_image_new_from_pixbuf (icon_pixbuf) : gtk_image_new_from_resource ("/icons/zoitechat.png");
|
||||
if (icon_pixbuf)
|
||||
g_object_unref (icon_pixbuf);
|
||||
gtk_header_bar_pack_start (GTK_HEADER_BAR (headerbar), icon_image);
|
||||
gtk_widget_show (icon_image);
|
||||
gtk_window_set_titlebar (gtk_window, headerbar);
|
||||
g_object_set_data (G_OBJECT (window), theme_manager_window_csd_headerbar_key, headerbar);
|
||||
}
|
||||
gtk_header_bar_set_title (GTK_HEADER_BAR (headerbar), gtk_window_get_title (gtk_window));
|
||||
gtk_widget_show (headerbar);
|
||||
{
|
||||
GdkScreen *screen = gdk_screen_get_default ();
|
||||
if (screen)
|
||||
gtk_style_context_reset_widgets (screen);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (headerbar)
|
||||
{
|
||||
if (gtk_widget_get_realized (window))
|
||||
return;
|
||||
gtk_window_set_titlebar (gtk_window, NULL);
|
||||
g_object_set_data (G_OBJECT (window), theme_manager_window_csd_headerbar_key, NULL);
|
||||
}
|
||||
|
||||
{
|
||||
GdkScreen *screen = gdk_screen_get_default ();
|
||||
if (screen)
|
||||
gtk_style_context_reset_widgets (screen);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
theme_manager_apply_platform_window_theme (GtkWidget *window)
|
||||
{
|
||||
@@ -300,7 +479,14 @@ theme_manager_apply_platform_window_theme (GtkWidget *window)
|
||||
return;
|
||||
|
||||
context = gtk_widget_get_style_context (window);
|
||||
dark = theme_runtime_is_dark_active ();
|
||||
if (theme_gtk3_is_active ())
|
||||
{
|
||||
dark = prefs.hex_gui_gtk3_variant == THEME_GTK3_VARIANT_PREFER_DARK;
|
||||
if (prefs.hex_gui_gtk3_variant == THEME_GTK3_VARIANT_FOLLOW_SYSTEM)
|
||||
dark = theme_policy_system_prefers_dark ();
|
||||
}
|
||||
else
|
||||
dark = theme_runtime_is_dark_active ();
|
||||
if (context)
|
||||
{
|
||||
gtk_style_context_remove_class (context, "zoitechat-dark");
|
||||
@@ -309,7 +495,7 @@ theme_manager_apply_platform_window_theme (GtkWidget *window)
|
||||
}
|
||||
fe_win32_apply_native_titlebar (window, dark);
|
||||
#else
|
||||
(void) window;
|
||||
theme_manager_apply_wayland_kde_csd (window);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -379,7 +565,7 @@ theme_manager_apply_entry_palette (GtkWidget *widget, const PangoFontDescription
|
||||
if (!widget || !font_desc)
|
||||
return;
|
||||
|
||||
theme_get_widget_style_values (&style_values);
|
||||
theme_get_widget_style_values_for_widget (widget, &style_values);
|
||||
gtkutil_apply_palette (widget, &style_values.background, &style_values.foreground, font_desc);
|
||||
}
|
||||
|
||||
@@ -387,11 +573,10 @@ ThemePaletteBehavior
|
||||
theme_manager_get_userlist_palette_behavior (const PangoFontDescription *font_desc)
|
||||
{
|
||||
ThemePaletteBehavior behavior;
|
||||
gboolean dark_mode_active = theme_policy_is_dark_mode_active (prefs.hex_gui_dark_mode);
|
||||
|
||||
behavior.font_desc = font_desc;
|
||||
behavior.apply_background = prefs.hex_gui_ulist_style || dark_mode_active;
|
||||
behavior.apply_foreground = dark_mode_active;
|
||||
behavior.apply_background = TRUE;
|
||||
behavior.apply_foreground = TRUE;
|
||||
|
||||
return behavior;
|
||||
}
|
||||
@@ -400,11 +585,10 @@ ThemePaletteBehavior
|
||||
theme_manager_get_channel_tree_palette_behavior (const PangoFontDescription *font_desc)
|
||||
{
|
||||
ThemePaletteBehavior behavior;
|
||||
gboolean dark_mode_active = theme_policy_is_dark_mode_active (prefs.hex_gui_dark_mode);
|
||||
|
||||
behavior.font_desc = font_desc;
|
||||
behavior.apply_background = dark_mode_active || prefs.hex_gui_dark_mode == ZOITECHAT_DARK_MODE_LIGHT;
|
||||
behavior.apply_foreground = dark_mode_active || prefs.hex_gui_dark_mode == ZOITECHAT_DARK_MODE_LIGHT;
|
||||
behavior.apply_background = TRUE;
|
||||
behavior.apply_foreground = TRUE;
|
||||
|
||||
return behavior;
|
||||
}
|
||||
@@ -431,7 +615,7 @@ theme_manager_apply_userlist_style (GtkWidget *widget, ThemePaletteBehavior beha
|
||||
if (!widget)
|
||||
return;
|
||||
|
||||
theme_get_widget_style_values (&style_values);
|
||||
theme_get_widget_style_values_for_widget (widget, &style_values);
|
||||
if (behavior.apply_background)
|
||||
background = &style_values.background;
|
||||
if (behavior.apply_foreground)
|
||||
|
||||
@@ -41,6 +41,7 @@ void theme_manager_init (void);
|
||||
gboolean theme_manager_apply_mode (unsigned int mode, gboolean *palette_changed);
|
||||
void theme_manager_set_mode (unsigned int mode, gboolean *palette_changed);
|
||||
void theme_manager_set_token_color (unsigned int mode, ThemeSemanticToken token, const GdkRGBA *color, gboolean *palette_changed);
|
||||
void theme_manager_reset_mode_colors (unsigned int mode, gboolean *palette_changed);
|
||||
void theme_manager_commit_preferences (unsigned int old_mode, gboolean *color_change);
|
||||
void theme_manager_save_preferences (void);
|
||||
gboolean theme_changed_event_has_reason (const ThemeChangedEvent *event, ThemeChangedReason reason);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,9 @@
|
||||
#include "../fe-gtk.h"
|
||||
#include "../../common/zoitechat.h"
|
||||
|
||||
GtkWidget *theme_preferences_create_page (GtkWindow *parent, gboolean *color_change_flag);
|
||||
GtkWidget *theme_preferences_create_page (GtkWindow *parent,
|
||||
struct zoitechatprefs *setup_prefs,
|
||||
gboolean *color_change_flag);
|
||||
GtkWidget *theme_preferences_create_color_page (GtkWindow *parent,
|
||||
struct zoitechatprefs *setup_prefs,
|
||||
gboolean *color_change_flag);
|
||||
|
||||
@@ -97,6 +97,8 @@ static ThemePalette active_palette;
|
||||
static gboolean user_colors_valid = FALSE;
|
||||
static gboolean dark_user_colors_valid = FALSE;
|
||||
static gboolean dark_mode_active = FALSE;
|
||||
static gboolean light_custom_tokens[THEME_TOKEN_COUNT];
|
||||
static gboolean dark_custom_tokens[THEME_TOKEN_COUNT];
|
||||
|
||||
#define THEME_PALETTE_MIGRATION_MARKER_KEY "theme.palette.semantic_migrated"
|
||||
#define THEME_PALETTE_MIGRATION_MARKER_VALUE 1
|
||||
@@ -109,6 +111,75 @@ typedef struct
|
||||
gboolean *mode_valid;
|
||||
} ThemePalettePersistenceMode;
|
||||
|
||||
static void
|
||||
theme_runtime_resolve_color (const GdkRGBA *mapped, const GdkRGBA *fallback, GdkRGBA *resolved)
|
||||
{
|
||||
gdouble alpha;
|
||||
|
||||
g_return_if_fail (mapped != NULL);
|
||||
g_return_if_fail (fallback != NULL);
|
||||
g_return_if_fail (resolved != NULL);
|
||||
|
||||
alpha = CLAMP (mapped->alpha, 0.0, 1.0);
|
||||
resolved->red = (mapped->red * alpha) + (fallback->red * (1.0 - alpha));
|
||||
resolved->green = (mapped->green * alpha) + (fallback->green * (1.0 - alpha));
|
||||
resolved->blue = (mapped->blue * alpha) + (fallback->blue * (1.0 - alpha));
|
||||
resolved->alpha = 1.0;
|
||||
}
|
||||
|
||||
static void
|
||||
theme_runtime_apply_gtk_map (ThemePalette *palette, const ThemeGtkPaletteMap *gtk_map, const gboolean *custom_tokens)
|
||||
{
|
||||
GdkRGBA text_foreground;
|
||||
GdkRGBA text_background;
|
||||
GdkRGBA selection_foreground;
|
||||
GdkRGBA selection_background;
|
||||
GdkRGBA accent;
|
||||
GdkRGBA fallback;
|
||||
|
||||
g_return_if_fail (palette != NULL);
|
||||
if (gtk_map == NULL || !gtk_map->enabled || custom_tokens == NULL)
|
||||
return;
|
||||
|
||||
g_assert (theme_palette_get_color (palette, THEME_TOKEN_TEXT_FOREGROUND, &fallback));
|
||||
theme_runtime_resolve_color (>k_map->text_foreground, &fallback, &text_foreground);
|
||||
g_assert (theme_palette_get_color (palette, THEME_TOKEN_TEXT_BACKGROUND, &fallback));
|
||||
theme_runtime_resolve_color (>k_map->text_background, &fallback, &text_background);
|
||||
g_assert (theme_palette_get_color (palette, THEME_TOKEN_SELECTION_FOREGROUND, &fallback));
|
||||
theme_runtime_resolve_color (>k_map->selection_foreground, &fallback, &selection_foreground);
|
||||
g_assert (theme_palette_get_color (palette, THEME_TOKEN_SELECTION_BACKGROUND, &fallback));
|
||||
theme_runtime_resolve_color (>k_map->selection_background, &fallback, &selection_background);
|
||||
g_assert (theme_palette_get_color (palette, THEME_TOKEN_MARKER, &fallback));
|
||||
theme_runtime_resolve_color (>k_map->accent, &fallback, &accent);
|
||||
|
||||
if (!custom_tokens[THEME_TOKEN_TEXT_FOREGROUND])
|
||||
g_assert (theme_palette_set_color (palette, THEME_TOKEN_TEXT_FOREGROUND, &text_foreground));
|
||||
if (!custom_tokens[THEME_TOKEN_TEXT_BACKGROUND])
|
||||
g_assert (theme_palette_set_color (palette, THEME_TOKEN_TEXT_BACKGROUND, &text_background));
|
||||
if (!custom_tokens[THEME_TOKEN_SELECTION_FOREGROUND])
|
||||
g_assert (theme_palette_set_color (palette, THEME_TOKEN_SELECTION_FOREGROUND, &selection_foreground));
|
||||
if (!custom_tokens[THEME_TOKEN_SELECTION_BACKGROUND])
|
||||
g_assert (theme_palette_set_color (palette, THEME_TOKEN_SELECTION_BACKGROUND, &selection_background));
|
||||
if (!custom_tokens[THEME_TOKEN_MARKER])
|
||||
g_assert (theme_palette_set_color (palette, THEME_TOKEN_MARKER, &accent));
|
||||
if (!custom_tokens[THEME_TOKEN_TAB_NEW_DATA])
|
||||
g_assert (theme_palette_set_color (palette, THEME_TOKEN_TAB_NEW_DATA, &accent));
|
||||
if (!custom_tokens[THEME_TOKEN_TAB_HIGHLIGHT])
|
||||
g_assert (theme_palette_set_color (palette, THEME_TOKEN_TAB_HIGHLIGHT, &accent));
|
||||
if (!custom_tokens[THEME_TOKEN_TAB_NEW_MESSAGE])
|
||||
g_assert (theme_palette_set_color (palette, THEME_TOKEN_TAB_NEW_MESSAGE, &accent));
|
||||
if (!custom_tokens[THEME_TOKEN_TAB_AWAY])
|
||||
g_assert (theme_palette_set_color (palette, THEME_TOKEN_TAB_AWAY, &accent));
|
||||
if (!custom_tokens[THEME_TOKEN_SPELL])
|
||||
g_assert (theme_palette_set_color (palette, THEME_TOKEN_SPELL, &accent));
|
||||
}
|
||||
|
||||
static const gboolean *
|
||||
theme_runtime_active_custom_tokens (void)
|
||||
{
|
||||
return light_custom_tokens;
|
||||
}
|
||||
|
||||
static void
|
||||
palette_color_set_rgb16 (GdkRGBA *color, guint16 red, guint16 green, guint16 blue)
|
||||
{
|
||||
@@ -231,6 +302,13 @@ theme_runtime_get_color (ThemeSemanticToken token, GdkRGBA *out_rgba)
|
||||
return theme_palette_get_color (&active_palette, token, out_rgba);
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_runtime_mode_has_user_colors (gboolean dark_mode)
|
||||
{
|
||||
(void) dark_mode;
|
||||
return user_colors_valid;
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_get_widget_style_values (ThemeWidgetStyleValues *out_values)
|
||||
{
|
||||
@@ -238,6 +316,17 @@ theme_runtime_get_widget_style_values (ThemeWidgetStyleValues *out_values)
|
||||
theme_palette_to_widget_style_values (&active_palette, out_values);
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_get_widget_style_values_mapped (const ThemeGtkPaletteMap *gtk_map, ThemeWidgetStyleValues *out_values)
|
||||
{
|
||||
ThemePalette mapped_palette;
|
||||
|
||||
g_return_if_fail (out_values != NULL);
|
||||
mapped_palette = active_palette;
|
||||
theme_runtime_apply_gtk_map (&mapped_palette, gtk_map, theme_runtime_active_custom_tokens ());
|
||||
theme_palette_to_widget_style_values (&mapped_palette, out_values);
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_get_xtext_colors (XTextColor *palette, size_t palette_len)
|
||||
{
|
||||
@@ -245,6 +334,17 @@ theme_runtime_get_xtext_colors (XTextColor *palette, size_t palette_len)
|
||||
theme_palette_to_xtext_colors (&active_palette, palette, palette_len);
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_get_xtext_colors_mapped (const ThemeGtkPaletteMap *gtk_map, XTextColor *palette, size_t palette_len)
|
||||
{
|
||||
ThemePalette mapped_palette;
|
||||
|
||||
g_return_if_fail (palette != NULL);
|
||||
mapped_palette = active_palette;
|
||||
theme_runtime_apply_gtk_map (&mapped_palette, gtk_map, theme_runtime_active_custom_tokens ());
|
||||
theme_palette_to_xtext_colors (&mapped_palette, palette, palette_len);
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_user_set_color (ThemeSemanticToken token, const GdkRGBA *col)
|
||||
{
|
||||
@@ -256,6 +356,7 @@ theme_runtime_user_set_color (ThemeSemanticToken token, const GdkRGBA *col)
|
||||
light_palette = active_palette;
|
||||
|
||||
g_assert (theme_palette_set_color (&light_palette, token, col));
|
||||
light_custom_tokens[token] = TRUE;
|
||||
user_colors_valid = TRUE;
|
||||
}
|
||||
|
||||
@@ -270,9 +371,23 @@ theme_runtime_dark_set_color (ThemeSemanticToken token, const GdkRGBA *col)
|
||||
dark_palette = active_palette;
|
||||
|
||||
g_assert (theme_palette_set_color (&dark_palette, token, col));
|
||||
dark_custom_tokens[token] = TRUE;
|
||||
dark_user_colors_valid = TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_reset_mode_colors (gboolean dark_mode)
|
||||
{
|
||||
(void) dark_mode;
|
||||
theme_palette_from_legacy_colors (&light_palette, legacy_light_defaults, G_N_ELEMENTS (legacy_light_defaults));
|
||||
active_palette = light_palette;
|
||||
memset (light_custom_tokens, 0, sizeof light_custom_tokens);
|
||||
memset (dark_custom_tokens, 0, sizeof dark_custom_tokens);
|
||||
user_colors_valid = TRUE;
|
||||
dark_user_colors_valid = FALSE;
|
||||
dark_mode_active = FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_load (void)
|
||||
{
|
||||
@@ -287,6 +402,8 @@ theme_runtime_load (void)
|
||||
const size_t mode_count = G_N_ELEMENTS (modes);
|
||||
|
||||
palette_init_defaults ();
|
||||
memset (light_custom_tokens, 0, sizeof light_custom_tokens);
|
||||
memset (dark_custom_tokens, 0, sizeof dark_custom_tokens);
|
||||
|
||||
fh = zoitechat_open_file ("colors.conf", O_RDONLY, 0, 0);
|
||||
if (fh != -1)
|
||||
@@ -312,7 +429,10 @@ theme_runtime_load (void)
|
||||
found = theme_runtime_load_migrated_legacy_color (cfg, &modes[i], def, &color);
|
||||
if (found)
|
||||
{
|
||||
gboolean *custom_tokens;
|
||||
g_assert (theme_palette_set_color (modes[i].palette, def->token, &color));
|
||||
custom_tokens = (modes[i].palette == &dark_palette) ? dark_custom_tokens : light_custom_tokens;
|
||||
custom_tokens[def->token] = TRUE;
|
||||
mode_found = TRUE;
|
||||
}
|
||||
}
|
||||
@@ -379,9 +499,13 @@ theme_runtime_save (void)
|
||||
for (j = 0; j < theme_palette_token_def_count (); j++)
|
||||
{
|
||||
const ThemePaletteTokenDef *def = theme_palette_token_def_at (j);
|
||||
const gboolean *custom_tokens;
|
||||
GdkRGBA color;
|
||||
|
||||
g_assert (def != NULL);
|
||||
custom_tokens = (modes[i].palette == &dark_palette) ? dark_custom_tokens : light_custom_tokens;
|
||||
if (!custom_tokens[def->token])
|
||||
continue;
|
||||
g_assert (theme_palette_get_color (modes[i].palette, def->token, &color));
|
||||
palette_write_token_color (fh, modes[i].mode_name, def, &color);
|
||||
}
|
||||
@@ -450,13 +574,15 @@ theme_runtime_apply_dark_mode (gboolean enable)
|
||||
gboolean
|
||||
theme_runtime_apply_mode (unsigned int mode, gboolean *palette_changed)
|
||||
{
|
||||
gboolean dark = theme_policy_is_dark_mode_active (mode);
|
||||
gboolean changed = theme_runtime_apply_dark_mode (dark);
|
||||
gboolean changed;
|
||||
|
||||
(void) mode;
|
||||
changed = theme_runtime_apply_dark_mode (FALSE);
|
||||
|
||||
if (palette_changed)
|
||||
*palette_changed = changed;
|
||||
|
||||
return dark;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
|
||||
@@ -7,15 +7,29 @@
|
||||
|
||||
#include "theme-palette.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gboolean enabled;
|
||||
GdkRGBA text_foreground;
|
||||
GdkRGBA text_background;
|
||||
GdkRGBA selection_foreground;
|
||||
GdkRGBA selection_background;
|
||||
GdkRGBA accent;
|
||||
} ThemeGtkPaletteMap;
|
||||
|
||||
void theme_runtime_load (void);
|
||||
void theme_runtime_save (void);
|
||||
gboolean theme_runtime_apply_mode (unsigned int mode, gboolean *palette_changed);
|
||||
gboolean theme_runtime_apply_dark_mode (gboolean enable);
|
||||
void theme_runtime_user_set_color (ThemeSemanticToken token, const GdkRGBA *col);
|
||||
void theme_runtime_dark_set_color (ThemeSemanticToken token, const GdkRGBA *col);
|
||||
void theme_runtime_reset_mode_colors (gboolean dark_mode);
|
||||
gboolean theme_runtime_get_color (ThemeSemanticToken token, GdkRGBA *out_rgba);
|
||||
gboolean theme_runtime_mode_has_user_colors (gboolean dark_mode);
|
||||
void theme_runtime_get_widget_style_values (ThemeWidgetStyleValues *out_values);
|
||||
void theme_runtime_get_widget_style_values_mapped (const ThemeGtkPaletteMap *gtk_map, ThemeWidgetStyleValues *out_values);
|
||||
void theme_runtime_get_xtext_colors (XTextColor *palette, size_t palette_len);
|
||||
void theme_runtime_get_xtext_colors_mapped (const ThemeGtkPaletteMap *gtk_map, XTextColor *palette, size_t palette_len);
|
||||
gboolean theme_runtime_is_dark_active (void);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user