mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-03-10 16:00:18 +00:00
feat: centralize theming in theme-manager (palette/tokens, CSS, dark-mode, setup UI), add tests + win32/meson wiring
This commit is contained in:
236
src/fe-gtk/theme/tests/test-theme-access-routing.c
Normal file
236
src/fe-gtk/theme/tests/test-theme-access-routing.c
Normal file
@@ -0,0 +1,236 @@
|
||||
#include <math.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "../theme-access.h"
|
||||
#include "../theme-manager.h"
|
||||
#include "../theme-runtime.h"
|
||||
#include "../../xtext-color.h"
|
||||
#include "../../../common/zoitechat.h"
|
||||
|
||||
struct session *current_sess;
|
||||
struct session *current_tab;
|
||||
struct session *lastact_sess;
|
||||
struct zoitechatprefs prefs;
|
||||
|
||||
static gboolean stub_dark_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 size_t stub_runtime_xtext_last_len;
|
||||
|
||||
static GdkRGBA stub_light_colors[THEME_TOKEN_COUNT];
|
||||
static GdkRGBA stub_dark_colors[THEME_TOKEN_COUNT];
|
||||
|
||||
void
|
||||
setup_apply_real (const ThemeChangedEvent *event)
|
||||
{
|
||||
(void) event;
|
||||
}
|
||||
|
||||
gboolean
|
||||
fe_dark_mode_is_enabled_for (unsigned int mode)
|
||||
{
|
||||
return mode == ZOITECHAT_DARK_MODE_DARK;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_runtime_apply_mode (unsigned int mode, gboolean *dark_active)
|
||||
{
|
||||
if (mode == ZOITECHAT_DARK_MODE_DARK)
|
||||
stub_dark_active = TRUE;
|
||||
if (mode == ZOITECHAT_DARK_MODE_LIGHT)
|
||||
stub_dark_active = FALSE;
|
||||
if (dark_active)
|
||||
*dark_active = stub_dark_active;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_load (void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_save (void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_user_set_color (ThemeSemanticToken token, const GdkRGBA *col)
|
||||
{
|
||||
(void) token;
|
||||
(void) col;
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_dark_set_color (ThemeSemanticToken token, const GdkRGBA *col)
|
||||
{
|
||||
(void) token;
|
||||
(void) col;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_runtime_get_color (ThemeSemanticToken token, GdkRGBA *out_rgba)
|
||||
{
|
||||
g_assert_nonnull (out_rgba);
|
||||
stub_runtime_get_color_calls++;
|
||||
stub_last_color_token = token;
|
||||
*out_rgba = stub_dark_active ? stub_dark_colors[token] : stub_light_colors[token];
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_get_widget_style_values (ThemeWidgetStyleValues *out_values)
|
||||
{
|
||||
stub_runtime_widget_calls++;
|
||||
gdk_rgba_parse (&out_values->background, "#010203");
|
||||
gdk_rgba_parse (&out_values->foreground, "#fdfcfa");
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_get_xtext_colors (XTextColor *palette, size_t palette_len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
stub_runtime_xtext_calls++;
|
||||
stub_runtime_xtext_last_len = palette_len;
|
||||
for (i = 0; i < palette_len; i++)
|
||||
{
|
||||
palette[i].red = (unsigned short) (i + 1);
|
||||
palette[i].green = (unsigned short) (i + 2);
|
||||
palette[i].blue = (unsigned short) (i + 3);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_runtime_is_dark_active (void)
|
||||
{
|
||||
return stub_dark_active;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
rgba_equal (const GdkRGBA *a, const GdkRGBA *b)
|
||||
{
|
||||
return a->red == b->red && a->green == b->green && a->blue == b->blue && a->alpha == b->alpha;
|
||||
}
|
||||
|
||||
static void
|
||||
reset_stubs (void)
|
||||
{
|
||||
size_t i;
|
||||
char light[32];
|
||||
char dark[32];
|
||||
|
||||
stub_dark_active = FALSE;
|
||||
stub_last_color_token = THEME_TOKEN_MIRC_0;
|
||||
stub_runtime_get_color_calls = 0;
|
||||
stub_runtime_widget_calls = 0;
|
||||
stub_runtime_xtext_calls = 0;
|
||||
stub_runtime_xtext_last_len = 0;
|
||||
for (i = 0; i < THEME_TOKEN_COUNT; i++)
|
||||
{
|
||||
g_snprintf (light, sizeof (light), "#%02x%02x%02x", (unsigned int) (i + 1), 0x11, 0x22);
|
||||
g_snprintf (dark, sizeof (dark), "#%02x%02x%02x", (unsigned int) (i + 1), 0xaa, 0xbb);
|
||||
g_assert_true (gdk_rgba_parse (&stub_light_colors[i], light));
|
||||
g_assert_true (gdk_rgba_parse (&stub_dark_colors[i], dark));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_access_semantic_token_routes_directly (void)
|
||||
{
|
||||
ThemeSemanticToken token;
|
||||
GdkRGBA color;
|
||||
size_t i;
|
||||
|
||||
reset_stubs ();
|
||||
for (i = 0; i < theme_palette_token_def_count (); i++)
|
||||
{
|
||||
const ThemePaletteTokenDef *def = theme_palette_token_def_at (i);
|
||||
|
||||
g_assert_nonnull (def);
|
||||
token = def->token;
|
||||
g_assert_true (theme_get_color (token, &color));
|
||||
g_assert_cmpint (stub_last_color_token, ==, token);
|
||||
g_assert_true (rgba_equal (&color, &stub_light_colors[token]));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_access_token_routes_without_legacy_accessor (void)
|
||||
{
|
||||
ThemeSemanticToken token = THEME_TOKEN_MIRC_0;
|
||||
GdkRGBA color;
|
||||
size_t i;
|
||||
|
||||
reset_stubs ();
|
||||
for (i = 0; i < theme_palette_token_def_count (); i++)
|
||||
{
|
||||
const ThemePaletteTokenDef *def = theme_palette_token_def_at (i);
|
||||
|
||||
g_assert_nonnull (def);
|
||||
g_assert_true (theme_palette_legacy_index_to_token (def->legacy_index, &token));
|
||||
g_assert_true (theme_get_color (token, &color));
|
||||
g_assert_cmpint (stub_last_color_token, ==, token);
|
||||
g_assert_true (rgba_equal (&color, &stub_light_colors[token]));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_access_xtext_palette_forwarding (void)
|
||||
{
|
||||
XTextColor palette[4] = { 0 };
|
||||
|
||||
reset_stubs ();
|
||||
theme_get_xtext_colors (palette, G_N_ELEMENTS (palette));
|
||||
g_assert_cmpint (stub_runtime_xtext_calls, ==, 1);
|
||||
g_assert_cmpuint (stub_runtime_xtext_last_len, ==, G_N_ELEMENTS (palette));
|
||||
g_assert_cmpuint (palette[0].red, ==, 1);
|
||||
g_assert_cmpuint (palette[1].green, ==, 3);
|
||||
g_assert_cmpuint (palette[3].blue, ==, 6);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_access_widget_style_forwarding (void)
|
||||
{
|
||||
ThemeWidgetStyleValues values;
|
||||
|
||||
reset_stubs ();
|
||||
theme_get_widget_style_values (&values);
|
||||
g_assert_cmpint (stub_runtime_widget_calls, ==, 1);
|
||||
g_assert_true (fabs (values.background.red - (0x01 / 255.0)) < 0.0001);
|
||||
g_assert_true (fabs (values.foreground.green - (0xfc / 255.0)) < 0.0001);
|
||||
}
|
||||
|
||||
static void
|
||||
test_access_dark_light_switch_affects_token_consumers (void)
|
||||
{
|
||||
ThemeSemanticToken token;
|
||||
GdkRGBA light;
|
||||
GdkRGBA dark;
|
||||
reset_stubs ();
|
||||
token = THEME_TOKEN_TEXT_FOREGROUND;
|
||||
g_assert_true (theme_runtime_apply_mode (ZOITECHAT_DARK_MODE_LIGHT, NULL));
|
||||
g_assert_true (theme_get_color (token, &light));
|
||||
g_assert_true (rgba_equal (&light, &stub_light_colors[token]));
|
||||
|
||||
g_assert_true (theme_runtime_apply_mode (ZOITECHAT_DARK_MODE_DARK, NULL));
|
||||
g_assert_true (theme_get_color (token, &dark));
|
||||
g_assert_true (rgba_equal (&dark, &stub_dark_colors[token]));
|
||||
g_assert_false (rgba_equal (&light, &dark));
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
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/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);
|
||||
return g_test_run ();
|
||||
}
|
||||
122
src/fe-gtk/theme/tests/test-theme-application-input-style.c
Normal file
122
src/fe-gtk/theme/tests/test-theme-application-input-style.c
Normal file
@@ -0,0 +1,122 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "../theme-application.h"
|
||||
#include "../../maingui.h"
|
||||
#include "../../../common/zoitechat.h"
|
||||
#include "../../../common/zoitechatc.h"
|
||||
|
||||
struct session *current_sess;
|
||||
struct session *current_tab;
|
||||
struct session *lastact_sess;
|
||||
struct zoitechatprefs prefs;
|
||||
InputStyle *input_style;
|
||||
|
||||
static gboolean css_enabled;
|
||||
static PangoFontDescription *css_font_desc;
|
||||
static int css_reload_calls;
|
||||
static int message_calls;
|
||||
|
||||
void
|
||||
fe_message (char *msg, int flags)
|
||||
{
|
||||
(void) msg;
|
||||
(void) flags;
|
||||
message_calls++;
|
||||
}
|
||||
|
||||
void
|
||||
theme_css_reload_input_style (gboolean enabled, const PangoFontDescription *font_desc)
|
||||
{
|
||||
css_enabled = enabled;
|
||||
if (css_font_desc)
|
||||
pango_font_description_free (css_font_desc);
|
||||
css_font_desc = font_desc ? pango_font_description_copy (font_desc) : NULL;
|
||||
css_reload_calls++;
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_load (void)
|
||||
{
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_runtime_apply_mode (unsigned int mode, gboolean *palette_changed)
|
||||
{
|
||||
(void) mode;
|
||||
if (palette_changed)
|
||||
*palette_changed = FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
reset_state (void)
|
||||
{
|
||||
if (css_font_desc)
|
||||
{
|
||||
pango_font_description_free (css_font_desc);
|
||||
css_font_desc = NULL;
|
||||
}
|
||||
if (input_style)
|
||||
{
|
||||
if (input_style->font_desc)
|
||||
pango_font_description_free (input_style->font_desc);
|
||||
g_free (input_style);
|
||||
input_style = NULL;
|
||||
}
|
||||
css_enabled = FALSE;
|
||||
css_reload_calls = 0;
|
||||
message_calls = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
test_invalid_font_falls_back_to_sans_11 (void)
|
||||
{
|
||||
InputStyle *style;
|
||||
|
||||
reset_state ();
|
||||
g_strlcpy (prefs.hex_text_font, "Sans", sizeof (prefs.hex_text_font));
|
||||
prefs.hex_gui_input_style = TRUE;
|
||||
|
||||
style = theme_application_update_input_style (NULL);
|
||||
g_assert_nonnull (style);
|
||||
g_assert_nonnull (style->font_desc);
|
||||
g_assert_cmpstr (pango_font_description_get_family (style->font_desc), ==, "sans");
|
||||
g_assert_cmpint (pango_font_description_get_size (style->font_desc), ==, 11 * PANGO_SCALE);
|
||||
g_assert_cmpint (message_calls, ==, 1);
|
||||
g_assert_cmpint (css_reload_calls, ==, 1);
|
||||
g_assert_true (css_enabled);
|
||||
g_assert_nonnull (css_font_desc);
|
||||
|
||||
if (style->font_desc)
|
||||
pango_font_description_free (style->font_desc);
|
||||
g_free (style);
|
||||
}
|
||||
|
||||
static void
|
||||
test_style_toggle_routes_enabled_flag (void)
|
||||
{
|
||||
reset_state ();
|
||||
g_strlcpy (prefs.hex_text_font, "sans 13", sizeof (prefs.hex_text_font));
|
||||
prefs.hex_gui_input_style = FALSE;
|
||||
input_style = NULL;
|
||||
|
||||
theme_application_reload_input_style ();
|
||||
g_assert_nonnull (input_style);
|
||||
g_assert_cmpint (css_reload_calls, ==, 1);
|
||||
g_assert_false (css_enabled);
|
||||
g_assert_nonnull (css_font_desc);
|
||||
g_assert_cmpstr (pango_font_description_get_family (css_font_desc), ==, "sans");
|
||||
g_assert_cmpint (pango_font_description_get_size (css_font_desc), ==, 13 * PANGO_SCALE);
|
||||
g_assert_cmpint (message_calls, ==, 0);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
g_test_add_func ("/theme/application/input_style/invalid_font_fallback",
|
||||
test_invalid_font_falls_back_to_sans_11);
|
||||
g_test_add_func ("/theme/application/input_style/style_toggle_routes_enabled_flag",
|
||||
test_style_toggle_routes_enabled_flag);
|
||||
return g_test_run ();
|
||||
}
|
||||
229
src/fe-gtk/theme/tests/test-theme-manager-auto-refresh.c
Normal file
229
src/fe-gtk/theme/tests/test-theme-manager-auto-refresh.c
Normal file
@@ -0,0 +1,229 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "../theme-manager.h"
|
||||
#include "../../fe-gtk.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 stub_apply_mode_palette_changed;
|
||||
static gboolean stub_system_prefers_dark;
|
||||
static int auto_state_calls;
|
||||
static gboolean last_auto_state;
|
||||
static int listener_calls;
|
||||
static ThemeChangedEvent last_event;
|
||||
static int idle_add_calls;
|
||||
static guint next_idle_source_id = 33;
|
||||
|
||||
void setup_apply_real (const ThemeChangedEvent *event)
|
||||
{
|
||||
(void) event;
|
||||
}
|
||||
|
||||
gboolean fe_dark_mode_is_enabled_for (unsigned int mode)
|
||||
{
|
||||
return mode == ZOITECHAT_DARK_MODE_DARK;
|
||||
}
|
||||
|
||||
void fe_set_auto_dark_mode_state (gboolean enabled)
|
||||
{
|
||||
auto_state_calls++;
|
||||
last_auto_state = enabled;
|
||||
}
|
||||
|
||||
gboolean fe_win32_high_contrast_is_enabled (void)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean fe_win32_try_get_system_dark (gboolean *enabled)
|
||||
{
|
||||
(void) enabled;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void zoitechat_set_theme_post_apply_callback (zoitechat_theme_post_apply_callback callback)
|
||||
{
|
||||
(void) callback;
|
||||
}
|
||||
|
||||
gboolean theme_policy_is_dark_mode_active (unsigned int mode)
|
||||
{
|
||||
return mode == ZOITECHAT_DARK_MODE_DARK;
|
||||
}
|
||||
|
||||
gboolean theme_policy_system_prefers_dark (void)
|
||||
{
|
||||
return stub_system_prefers_dark;
|
||||
}
|
||||
|
||||
gboolean theme_application_apply_mode (unsigned int mode, gboolean *palette_changed)
|
||||
{
|
||||
(void) mode;
|
||||
if (palette_changed)
|
||||
*palette_changed = stub_apply_mode_palette_changed;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void theme_application_reload_input_style (void)
|
||||
{
|
||||
}
|
||||
|
||||
void theme_runtime_dark_set_color (ThemeSemanticToken token, const GdkRGBA *color)
|
||||
{
|
||||
(void) token;
|
||||
(void) color;
|
||||
}
|
||||
|
||||
void theme_runtime_user_set_color (ThemeSemanticToken token, const GdkRGBA *color)
|
||||
{
|
||||
(void) token;
|
||||
(void) color;
|
||||
}
|
||||
|
||||
gboolean theme_runtime_apply_mode (unsigned int mode, gboolean *dark_active)
|
||||
{
|
||||
(void) mode;
|
||||
(void) dark_active;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void theme_css_reload_input_style (gboolean enabled, const PangoFontDescription *font_desc)
|
||||
{
|
||||
(void) enabled;
|
||||
(void) font_desc;
|
||||
}
|
||||
|
||||
void theme_css_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkRGBA *fg,
|
||||
const PangoFontDescription *font_desc)
|
||||
{
|
||||
(void) widget;
|
||||
(void) bg;
|
||||
(void) fg;
|
||||
(void) font_desc;
|
||||
}
|
||||
|
||||
void theme_runtime_load (void)
|
||||
{
|
||||
}
|
||||
|
||||
void theme_runtime_save (void)
|
||||
{
|
||||
}
|
||||
|
||||
gboolean theme_runtime_is_dark_active (void)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void gtkutil_apply_palette (GtkWidget *widget, const GdkRGBA *background, const GdkRGBA *foreground,
|
||||
const PangoFontDescription *font_desc)
|
||||
{
|
||||
(void) widget;
|
||||
(void) background;
|
||||
(void) foreground;
|
||||
(void) font_desc;
|
||||
}
|
||||
|
||||
void theme_get_widget_style_values (ThemeWidgetStyleValues *out_values)
|
||||
{
|
||||
gdk_rgba_parse (&out_values->background, "#101010");
|
||||
gdk_rgba_parse (&out_values->foreground, "#f0f0f0");
|
||||
}
|
||||
|
||||
void fe_win32_apply_native_titlebar (GtkWidget *window, gboolean dark)
|
||||
{
|
||||
(void) window;
|
||||
(void) dark;
|
||||
}
|
||||
|
||||
static void
|
||||
auto_listener (const ThemeChangedEvent *event, gpointer userdata)
|
||||
{
|
||||
(void) userdata;
|
||||
listener_calls++;
|
||||
last_event = *event;
|
||||
}
|
||||
|
||||
static guint
|
||||
immediate_idle_add (GSourceFunc function, gpointer data)
|
||||
{
|
||||
idle_add_calls++;
|
||||
function (data);
|
||||
return next_idle_source_id++;
|
||||
}
|
||||
|
||||
static void
|
||||
reset_state (void)
|
||||
{
|
||||
stub_apply_mode_palette_changed = FALSE;
|
||||
stub_system_prefers_dark = FALSE;
|
||||
auto_state_calls = 0;
|
||||
last_auto_state = FALSE;
|
||||
listener_calls = 0;
|
||||
idle_add_calls = 0;
|
||||
next_idle_source_id = 33;
|
||||
}
|
||||
|
||||
static void
|
||||
test_auto_refresh_dispatches_mode_palette_and_style_reasons (void)
|
||||
{
|
||||
guint listener_id;
|
||||
|
||||
reset_state ();
|
||||
prefs.hex_gui_dark_mode = ZOITECHAT_DARK_MODE_AUTO;
|
||||
stub_apply_mode_palette_changed = TRUE;
|
||||
stub_system_prefers_dark = TRUE;
|
||||
listener_id = theme_listener_register ("auto.refresh", 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, ==, 2);
|
||||
g_assert_true (last_auto_state);
|
||||
g_assert_cmpint (listener_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));
|
||||
g_assert_true (theme_changed_event_has_reason (&last_event, THEME_CHANGED_REASON_MODE));
|
||||
|
||||
theme_manager_set_idle_add_func (NULL);
|
||||
theme_listener_unregister (listener_id);
|
||||
}
|
||||
|
||||
static void
|
||||
test_auto_refresh_ignores_non_auto_mode (void)
|
||||
{
|
||||
guint listener_id;
|
||||
|
||||
reset_state ();
|
||||
prefs.hex_gui_dark_mode = ZOITECHAT_DARK_MODE_DARK;
|
||||
stub_apply_mode_palette_changed = TRUE;
|
||||
listener_id = theme_listener_register ("auto.nonauto", 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);
|
||||
|
||||
theme_manager_set_idle_add_func (NULL);
|
||||
theme_listener_unregister (listener_id);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
g_test_add_func ("/theme/manager/auto_refresh_dispatches_mode_palette_and_style_reasons",
|
||||
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);
|
||||
return g_test_run ();
|
||||
}
|
||||
273
src/fe-gtk/theme/tests/test-theme-manager-dispatch-routing.c
Normal file
273
src/fe-gtk/theme/tests/test-theme-manager-dispatch-routing.c
Normal file
@@ -0,0 +1,273 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../theme-manager.h"
|
||||
#include "../../fe-gtk.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 int window_refresh_calls;
|
||||
static int widget_style_calls;
|
||||
static int palette_reapply_calls;
|
||||
static int unmatched_listener_calls;
|
||||
|
||||
void setup_apply_real (const ThemeChangedEvent *event)
|
||||
{
|
||||
(void) event;
|
||||
}
|
||||
|
||||
gboolean fe_dark_mode_is_enabled_for (unsigned int mode)
|
||||
{
|
||||
return mode == ZOITECHAT_DARK_MODE_DARK;
|
||||
}
|
||||
|
||||
void fe_set_auto_dark_mode_state (gboolean enabled)
|
||||
{
|
||||
(void) enabled;
|
||||
}
|
||||
|
||||
gboolean fe_win32_high_contrast_is_enabled (void)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean fe_win32_try_get_system_dark (gboolean *enabled)
|
||||
{
|
||||
(void) enabled;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void zoitechat_set_theme_post_apply_callback (zoitechat_theme_post_apply_callback callback)
|
||||
{
|
||||
(void) callback;
|
||||
}
|
||||
|
||||
gboolean theme_policy_is_dark_mode_active (unsigned int mode)
|
||||
{
|
||||
return mode == ZOITECHAT_DARK_MODE_DARK;
|
||||
}
|
||||
|
||||
gboolean theme_policy_system_prefers_dark (void)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean theme_application_apply_mode (unsigned int mode, gboolean *palette_changed)
|
||||
{
|
||||
(void) mode;
|
||||
if (palette_changed)
|
||||
*palette_changed = FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void theme_application_reload_input_style (void)
|
||||
{
|
||||
}
|
||||
|
||||
void theme_runtime_dark_set_color (ThemeSemanticToken token, const GdkRGBA *color)
|
||||
{
|
||||
(void) token;
|
||||
(void) color;
|
||||
}
|
||||
|
||||
void theme_runtime_user_set_color (ThemeSemanticToken token, const GdkRGBA *color)
|
||||
{
|
||||
(void) token;
|
||||
(void) color;
|
||||
}
|
||||
|
||||
gboolean theme_runtime_apply_mode (unsigned int mode, gboolean *dark_active)
|
||||
{
|
||||
(void) mode;
|
||||
(void) dark_active;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void theme_css_reload_input_style (gboolean enabled, const PangoFontDescription *font_desc)
|
||||
{
|
||||
(void) enabled;
|
||||
(void) font_desc;
|
||||
}
|
||||
|
||||
void theme_css_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkRGBA *fg,
|
||||
const PangoFontDescription *font_desc)
|
||||
{
|
||||
(void) widget;
|
||||
(void) bg;
|
||||
(void) fg;
|
||||
(void) font_desc;
|
||||
}
|
||||
|
||||
void theme_runtime_load (void)
|
||||
{
|
||||
}
|
||||
|
||||
void theme_runtime_save (void)
|
||||
{
|
||||
}
|
||||
|
||||
gboolean theme_runtime_is_dark_active (void)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void gtkutil_apply_palette (GtkWidget *widget, const GdkRGBA *background, const GdkRGBA *foreground,
|
||||
const PangoFontDescription *font_desc)
|
||||
{
|
||||
(void) widget;
|
||||
(void) background;
|
||||
(void) foreground;
|
||||
(void) font_desc;
|
||||
}
|
||||
|
||||
void theme_get_widget_style_values (ThemeWidgetStyleValues *out_values)
|
||||
{
|
||||
gdk_rgba_parse (&out_values->background, "#101010");
|
||||
gdk_rgba_parse (&out_values->foreground, "#f0f0f0");
|
||||
}
|
||||
|
||||
void fe_win32_apply_native_titlebar (GtkWidget *window, gboolean dark)
|
||||
{
|
||||
(void) window;
|
||||
(void) dark;
|
||||
}
|
||||
|
||||
static void
|
||||
window_refresh_listener (const ThemeChangedEvent *event, gpointer userdata)
|
||||
{
|
||||
(void) userdata;
|
||||
if (theme_changed_event_has_reason (event, THEME_CHANGED_REASON_PALETTE))
|
||||
palette_reapply_calls++;
|
||||
if (theme_changed_event_has_reason (event, THEME_CHANGED_REASON_PALETTE) ||
|
||||
theme_changed_event_has_reason (event, THEME_CHANGED_REASON_WIDGET_STYLE))
|
||||
window_refresh_calls++;
|
||||
}
|
||||
|
||||
static void
|
||||
widget_style_listener (const ThemeChangedEvent *event, gpointer userdata)
|
||||
{
|
||||
(void) userdata;
|
||||
if (theme_changed_event_has_reason (event, THEME_CHANGED_REASON_WIDGET_STYLE))
|
||||
widget_style_calls++;
|
||||
}
|
||||
|
||||
static void
|
||||
unmatched_reason_listener (const ThemeChangedEvent *event, gpointer userdata)
|
||||
{
|
||||
(void) userdata;
|
||||
if (theme_changed_event_has_reason (event, THEME_CHANGED_REASON_IDENTD))
|
||||
unmatched_listener_calls++;
|
||||
}
|
||||
|
||||
static void
|
||||
reset_counters (void)
|
||||
{
|
||||
window_refresh_calls = 0;
|
||||
widget_style_calls = 0;
|
||||
palette_reapply_calls = 0;
|
||||
unmatched_listener_calls = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dispatch_filters_reasons_across_multiple_subscribers (void)
|
||||
{
|
||||
guint listener_window;
|
||||
guint listener_widget;
|
||||
guint listener_unmatched;
|
||||
|
||||
reset_counters ();
|
||||
listener_window = theme_listener_register ("refresh.window", window_refresh_listener, NULL);
|
||||
listener_widget = theme_listener_register ("refresh.widget", widget_style_listener, NULL);
|
||||
listener_unmatched = theme_listener_register ("refresh.unmatched", unmatched_reason_listener, NULL);
|
||||
|
||||
theme_manager_dispatch_changed (THEME_CHANGED_REASON_PALETTE);
|
||||
g_assert_cmpint (window_refresh_calls, ==, 1);
|
||||
g_assert_cmpint (palette_reapply_calls, ==, 1);
|
||||
g_assert_cmpint (widget_style_calls, ==, 0);
|
||||
g_assert_cmpint (unmatched_listener_calls, ==, 0);
|
||||
|
||||
theme_manager_dispatch_changed (THEME_CHANGED_REASON_WIDGET_STYLE | THEME_CHANGED_REASON_USERLIST);
|
||||
g_assert_cmpint (window_refresh_calls, ==, 2);
|
||||
g_assert_cmpint (palette_reapply_calls, ==, 1);
|
||||
g_assert_cmpint (widget_style_calls, ==, 1);
|
||||
g_assert_cmpint (unmatched_listener_calls, ==, 0);
|
||||
|
||||
theme_manager_dispatch_changed (THEME_CHANGED_REASON_LAYOUT);
|
||||
g_assert_cmpint (window_refresh_calls, ==, 2);
|
||||
g_assert_cmpint (palette_reapply_calls, ==, 1);
|
||||
g_assert_cmpint (widget_style_calls, ==, 1);
|
||||
g_assert_cmpint (unmatched_listener_calls, ==, 0);
|
||||
|
||||
theme_listener_unregister (listener_unmatched);
|
||||
theme_listener_unregister (listener_widget);
|
||||
theme_listener_unregister (listener_window);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_preferences_change_synthesizes_theme_reasons (void)
|
||||
{
|
||||
struct zoitechatprefs old_prefs = { 0 };
|
||||
struct zoitechatprefs new_prefs = { 0 };
|
||||
ThemeChangedEvent event;
|
||||
gboolean color_change = TRUE;
|
||||
|
||||
prefs.hex_gui_dark_mode = ZOITECHAT_DARK_MODE_DARK;
|
||||
old_prefs.hex_gui_dark_mode = prefs.hex_gui_dark_mode;
|
||||
new_prefs.hex_gui_dark_mode = prefs.hex_gui_dark_mode;
|
||||
strcpy (old_prefs.hex_text_background, "old.png");
|
||||
strcpy (new_prefs.hex_text_background, "new.png");
|
||||
old_prefs.hex_gui_tab_dots = 0;
|
||||
new_prefs.hex_gui_tab_dots = 1;
|
||||
old_prefs.hex_identd_port = 113;
|
||||
new_prefs.hex_identd_port = 114;
|
||||
old_prefs.hex_gui_ulist_color = 0;
|
||||
new_prefs.hex_gui_ulist_color = 1;
|
||||
|
||||
event = theme_manager_on_preferences_changed (&old_prefs, &new_prefs, prefs.hex_gui_dark_mode, &color_change);
|
||||
|
||||
g_assert_true (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_PIXMAP));
|
||||
g_assert_true (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_LAYOUT));
|
||||
g_assert_true (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_IDENTD));
|
||||
g_assert_true (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_USERLIST));
|
||||
g_assert_true (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_WIDGET_STYLE));
|
||||
}
|
||||
|
||||
static void
|
||||
test_preferences_change_omits_reasons_without_differences (void)
|
||||
{
|
||||
struct zoitechatprefs old_prefs = { 0 };
|
||||
struct zoitechatprefs new_prefs = { 0 };
|
||||
ThemeChangedEvent event;
|
||||
gboolean color_change = FALSE;
|
||||
|
||||
prefs.hex_gui_dark_mode = ZOITECHAT_DARK_MODE_DARK;
|
||||
old_prefs.hex_gui_dark_mode = prefs.hex_gui_dark_mode;
|
||||
new_prefs.hex_gui_dark_mode = prefs.hex_gui_dark_mode;
|
||||
|
||||
event = theme_manager_on_preferences_changed (&old_prefs, &new_prefs, prefs.hex_gui_dark_mode, &color_change);
|
||||
|
||||
g_assert_false (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_PIXMAP));
|
||||
g_assert_false (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_LAYOUT));
|
||||
g_assert_false (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_IDENTD));
|
||||
g_assert_false (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_USERLIST));
|
||||
g_assert_false (theme_changed_event_has_reason (&event, THEME_CHANGED_REASON_WIDGET_STYLE));
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
g_test_add_func ("/theme/manager/dispatch_filters_reasons_across_multiple_subscribers",
|
||||
test_dispatch_filters_reasons_across_multiple_subscribers);
|
||||
g_test_add_func ("/theme/manager/preferences_change_synthesizes_theme_reasons",
|
||||
test_preferences_change_synthesizes_theme_reasons);
|
||||
g_test_add_func ("/theme/manager/preferences_change_omits_reasons_without_differences",
|
||||
test_preferences_change_omits_reasons_without_differences);
|
||||
return g_test_run ();
|
||||
}
|
||||
368
src/fe-gtk/theme/tests/test-theme-manager-policy.c
Normal file
368
src/fe-gtk/theme/tests/test-theme-manager-policy.c
Normal file
@@ -0,0 +1,368 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "../theme-palette.h"
|
||||
#include "../theme-manager.h"
|
||||
#include "../../fe-gtk.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 stub_policy_dark;
|
||||
static unsigned int stub_policy_mode;
|
||||
static gboolean stub_apply_mode_result;
|
||||
static gboolean stub_apply_mode_palette_changed;
|
||||
static int stub_dark_set_calls;
|
||||
static int stub_user_set_calls;
|
||||
static int stub_apply_mode_calls;
|
||||
static int stub_reload_style_calls;
|
||||
static ThemeSemanticToken stub_last_dark_token;
|
||||
static ThemeSemanticToken stub_last_user_token;
|
||||
|
||||
static int listener_a_calls;
|
||||
static int listener_b_calls;
|
||||
static ThemeChangedEvent listener_last_event;
|
||||
|
||||
void setup_apply_real (const ThemeChangedEvent *event)
|
||||
{
|
||||
(void) event;
|
||||
}
|
||||
|
||||
gboolean fe_dark_mode_is_enabled_for (unsigned int mode)
|
||||
{
|
||||
return mode == ZOITECHAT_DARK_MODE_DARK;
|
||||
}
|
||||
|
||||
void fe_set_auto_dark_mode_state (gboolean enabled)
|
||||
{
|
||||
(void) enabled;
|
||||
}
|
||||
|
||||
gboolean fe_win32_high_contrast_is_enabled (void)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean fe_win32_try_get_system_dark (gboolean *enabled)
|
||||
{
|
||||
(void) enabled;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void zoitechat_set_theme_post_apply_callback (zoitechat_theme_post_apply_callback callback)
|
||||
{
|
||||
(void) callback;
|
||||
}
|
||||
|
||||
gboolean theme_policy_is_dark_mode_active (unsigned int mode)
|
||||
{
|
||||
stub_policy_mode = mode;
|
||||
return stub_policy_dark;
|
||||
}
|
||||
|
||||
gboolean theme_application_apply_mode (unsigned int mode, gboolean *palette_changed)
|
||||
{
|
||||
(void) mode;
|
||||
if (palette_changed)
|
||||
*palette_changed = stub_apply_mode_palette_changed;
|
||||
return stub_apply_mode_result;
|
||||
}
|
||||
|
||||
void theme_runtime_dark_set_color (ThemeSemanticToken token, const GdkRGBA *color)
|
||||
{
|
||||
(void) color;
|
||||
stub_dark_set_calls++;
|
||||
stub_last_dark_token = token;
|
||||
}
|
||||
|
||||
void theme_runtime_user_set_color (ThemeSemanticToken token, const GdkRGBA *color)
|
||||
{
|
||||
(void) color;
|
||||
stub_user_set_calls++;
|
||||
stub_last_user_token = token;
|
||||
}
|
||||
|
||||
gboolean theme_runtime_apply_mode (unsigned int mode, gboolean *dark_active)
|
||||
{
|
||||
(void) mode;
|
||||
(void) dark_active;
|
||||
stub_apply_mode_calls++;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void theme_application_reload_input_style (void)
|
||||
{
|
||||
stub_reload_style_calls++;
|
||||
}
|
||||
|
||||
|
||||
void theme_css_reload_input_style (gboolean enabled, const PangoFontDescription *font_desc)
|
||||
{
|
||||
(void) enabled;
|
||||
(void) font_desc;
|
||||
}
|
||||
|
||||
void theme_css_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkRGBA *fg,
|
||||
const PangoFontDescription *font_desc)
|
||||
{
|
||||
(void) widget;
|
||||
(void) bg;
|
||||
(void) fg;
|
||||
(void) font_desc;
|
||||
}
|
||||
|
||||
void theme_runtime_load (void)
|
||||
{
|
||||
}
|
||||
|
||||
void theme_runtime_save (void)
|
||||
{
|
||||
}
|
||||
|
||||
gboolean theme_runtime_is_dark_active (void)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean theme_policy_system_prefers_dark (void)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void gtkutil_apply_palette (GtkWidget *widget, const GdkRGBA *background, const GdkRGBA *foreground,
|
||||
const PangoFontDescription *font_desc)
|
||||
{
|
||||
(void) widget;
|
||||
(void) background;
|
||||
(void) foreground;
|
||||
(void) font_desc;
|
||||
}
|
||||
|
||||
void theme_get_widget_style_values (ThemeWidgetStyleValues *out_values)
|
||||
{
|
||||
gdk_rgba_parse (&out_values->background, "#101010");
|
||||
gdk_rgba_parse (&out_values->foreground, "#f0f0f0");
|
||||
}
|
||||
|
||||
void fe_win32_apply_native_titlebar (GtkWidget *window, gboolean dark)
|
||||
{
|
||||
(void) window;
|
||||
(void) dark;
|
||||
}
|
||||
|
||||
static void
|
||||
listener_a (const ThemeChangedEvent *event, gpointer userdata)
|
||||
{
|
||||
(void) userdata;
|
||||
listener_a_calls++;
|
||||
listener_last_event = *event;
|
||||
}
|
||||
|
||||
static void
|
||||
listener_b (const ThemeChangedEvent *event, gpointer userdata)
|
||||
{
|
||||
(void) userdata;
|
||||
(void) event;
|
||||
listener_b_calls++;
|
||||
}
|
||||
|
||||
static void
|
||||
reset_manager_stubs (void)
|
||||
{
|
||||
stub_policy_dark = FALSE;
|
||||
stub_policy_mode = 999;
|
||||
stub_apply_mode_result = TRUE;
|
||||
stub_apply_mode_palette_changed = FALSE;
|
||||
stub_dark_set_calls = 0;
|
||||
stub_user_set_calls = 0;
|
||||
stub_apply_mode_calls = 0;
|
||||
stub_reload_style_calls = 0;
|
||||
stub_last_dark_token = -1;
|
||||
stub_last_user_token = -1;
|
||||
listener_a_calls = 0;
|
||||
listener_b_calls = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
test_token_roundtrip (void)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < theme_palette_token_def_count (); i++)
|
||||
{
|
||||
const ThemePaletteTokenDef *def = theme_palette_token_def_at (i);
|
||||
int legacy_idx = -1;
|
||||
ThemeSemanticToken token = THEME_TOKEN_MIRC_0;
|
||||
|
||||
g_assert_nonnull (def);
|
||||
g_assert_true (theme_palette_token_to_legacy_index (def->token, &legacy_idx));
|
||||
g_assert_cmpint (legacy_idx, ==, def->legacy_index);
|
||||
g_assert_true (theme_palette_legacy_index_to_token (legacy_idx, &token));
|
||||
g_assert_cmpint (token, ==, def->token);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_policy_mode_resolution (void)
|
||||
{
|
||||
g_assert_false (theme_policy_is_dark_mode_active (ZOITECHAT_DARK_MODE_LIGHT));
|
||||
g_assert_true (theme_policy_is_dark_mode_active (ZOITECHAT_DARK_MODE_DARK));
|
||||
}
|
||||
|
||||
static void
|
||||
test_manager_set_token_color_routes_by_mode (void)
|
||||
{
|
||||
GdkRGBA color = { 0.1, 0.2, 0.3, 1.0 };
|
||||
gboolean palette_changed = FALSE;
|
||||
|
||||
reset_manager_stubs ();
|
||||
stub_policy_dark = FALSE;
|
||||
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_LIGHT, THEME_TOKEN_MIRC_2, &color, &palette_changed);
|
||||
g_assert_cmpint (stub_policy_mode, ==, ZOITECHAT_DARK_MODE_LIGHT);
|
||||
g_assert_cmpint (stub_user_set_calls, ==, 1);
|
||||
g_assert_cmpint (stub_dark_set_calls, ==, 0);
|
||||
g_assert_cmpint (stub_apply_mode_calls, ==, 1);
|
||||
g_assert_cmpint (stub_reload_style_calls, ==, 1);
|
||||
g_assert_true (palette_changed);
|
||||
|
||||
reset_manager_stubs ();
|
||||
stub_policy_dark = TRUE;
|
||||
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_DARK, THEME_TOKEN_MIRC_2, &color, &palette_changed);
|
||||
g_assert_cmpint (stub_policy_mode, ==, ZOITECHAT_DARK_MODE_DARK);
|
||||
g_assert_cmpint (stub_user_set_calls, ==, 0);
|
||||
g_assert_cmpint (stub_dark_set_calls, ==, 1);
|
||||
|
||||
reset_manager_stubs ();
|
||||
stub_policy_dark = TRUE;
|
||||
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_AUTO, THEME_TOKEN_MIRC_2, &color, &palette_changed);
|
||||
g_assert_cmpint (stub_policy_mode, ==, ZOITECHAT_DARK_MODE_AUTO);
|
||||
g_assert_cmpint (stub_user_set_calls, ==, 0);
|
||||
g_assert_cmpint (stub_dark_set_calls, ==, 1);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_manager_set_token_color_routes_setup_indexes (void)
|
||||
{
|
||||
GdkRGBA color = { 0.7, 0.3, 0.2, 1.0 };
|
||||
gboolean palette_changed = FALSE;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < theme_palette_token_def_count (); i++)
|
||||
{
|
||||
const ThemePaletteTokenDef *def = theme_palette_token_def_at (i);
|
||||
|
||||
g_assert_nonnull (def);
|
||||
reset_manager_stubs ();
|
||||
stub_policy_dark = FALSE;
|
||||
palette_changed = FALSE;
|
||||
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_LIGHT, def->token, &color, &palette_changed);
|
||||
g_assert_cmpint (stub_user_set_calls, ==, 1);
|
||||
g_assert_cmpint (stub_last_user_token, ==, def->token);
|
||||
g_assert_cmpint (stub_dark_set_calls, ==, 0);
|
||||
g_assert_true (palette_changed);
|
||||
|
||||
reset_manager_stubs ();
|
||||
stub_policy_dark = TRUE;
|
||||
palette_changed = FALSE;
|
||||
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_DARK, def->token, &color, &palette_changed);
|
||||
g_assert_cmpint (stub_dark_set_calls, ==, 1);
|
||||
g_assert_cmpint (stub_last_dark_token, ==, def->token);
|
||||
g_assert_cmpint (stub_user_set_calls, ==, 0);
|
||||
g_assert_true (palette_changed);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_manager_listener_registration_dispatch_and_unregister (void)
|
||||
{
|
||||
guint id_a;
|
||||
guint id_b;
|
||||
|
||||
reset_manager_stubs ();
|
||||
id_a = theme_listener_register ("test.a", listener_a, NULL);
|
||||
id_b = theme_listener_register ("test.b", listener_b, NULL);
|
||||
g_assert_cmpuint (id_a, >, 0);
|
||||
g_assert_cmpuint (id_b, >, 0);
|
||||
|
||||
theme_manager_dispatch_changed (THEME_CHANGED_REASON_PIXMAP | THEME_CHANGED_REASON_USERLIST | THEME_CHANGED_REASON_IDENTD | THEME_CHANGED_REASON_WIDGET_STYLE);
|
||||
g_assert_cmpint (listener_a_calls, ==, 1);
|
||||
g_assert_cmpint (listener_b_calls, ==, 1);
|
||||
g_assert_true (theme_changed_event_has_reason (&listener_last_event, THEME_CHANGED_REASON_PIXMAP));
|
||||
g_assert_true (theme_changed_event_has_reason (&listener_last_event, THEME_CHANGED_REASON_USERLIST));
|
||||
g_assert_false (theme_changed_event_has_reason (&listener_last_event, THEME_CHANGED_REASON_LAYOUT));
|
||||
g_assert_true (theme_changed_event_has_reason (&listener_last_event, THEME_CHANGED_REASON_IDENTD));
|
||||
g_assert_true (theme_changed_event_has_reason (&listener_last_event, THEME_CHANGED_REASON_WIDGET_STYLE));
|
||||
|
||||
theme_listener_unregister (id_a);
|
||||
theme_manager_dispatch_changed (THEME_CHANGED_REASON_PIXMAP | THEME_CHANGED_REASON_LAYOUT | THEME_CHANGED_REASON_WIDGET_STYLE);
|
||||
g_assert_cmpint (listener_a_calls, ==, 1);
|
||||
g_assert_cmpint (listener_b_calls, ==, 2);
|
||||
|
||||
theme_listener_unregister (id_b);
|
||||
}
|
||||
|
||||
static void
|
||||
test_manager_window_attach_detach_idempotence (void)
|
||||
{
|
||||
GtkWidget *window;
|
||||
gulong *first_handler_ptr;
|
||||
gulong first_handler_id;
|
||||
gulong *second_handler_ptr;
|
||||
gulong second_handler_id;
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
g_assert_nonnull (window);
|
||||
|
||||
theme_manager_attach_window (window);
|
||||
first_handler_ptr = g_object_get_data (G_OBJECT (window), "theme-manager-window-destroy-handler");
|
||||
g_assert_nonnull (first_handler_ptr);
|
||||
first_handler_id = *first_handler_ptr;
|
||||
g_assert_cmpuint (first_handler_id, >, 0);
|
||||
g_assert_true (g_signal_handler_is_connected (G_OBJECT (window), first_handler_id));
|
||||
|
||||
theme_manager_attach_window (window);
|
||||
second_handler_ptr = g_object_get_data (G_OBJECT (window), "theme-manager-window-destroy-handler");
|
||||
g_assert_nonnull (second_handler_ptr);
|
||||
g_assert_true (first_handler_ptr == second_handler_ptr);
|
||||
g_assert_cmpuint (*second_handler_ptr, ==, first_handler_id);
|
||||
|
||||
theme_manager_detach_window (window);
|
||||
g_assert_null (g_object_get_data (G_OBJECT (window), "theme-manager-window-destroy-handler"));
|
||||
g_assert_false (g_signal_handler_is_connected (G_OBJECT (window), first_handler_id));
|
||||
|
||||
theme_manager_detach_window (window);
|
||||
g_assert_null (g_object_get_data (G_OBJECT (window), "theme-manager-window-destroy-handler"));
|
||||
|
||||
theme_manager_attach_window (window);
|
||||
second_handler_ptr = g_object_get_data (G_OBJECT (window), "theme-manager-window-destroy-handler");
|
||||
g_assert_nonnull (second_handler_ptr);
|
||||
second_handler_id = *second_handler_ptr;
|
||||
g_assert_cmpuint (second_handler_id, >, 0);
|
||||
g_assert_true (g_signal_handler_is_connected (G_OBJECT (window), second_handler_id));
|
||||
|
||||
theme_manager_detach_window (window);
|
||||
g_assert_null (g_object_get_data (G_OBJECT (window), "theme-manager-window-destroy-handler"));
|
||||
g_assert_false (g_signal_handler_is_connected (G_OBJECT (window), second_handler_id));
|
||||
|
||||
gtk_widget_destroy (window);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
g_test_add_func ("/theme/palette/token_roundtrip", test_token_roundtrip);
|
||||
g_test_add_func ("/theme/policy/mode_resolution", test_policy_mode_resolution);
|
||||
g_test_add_func ("/theme/manager/set_token_color_routes_by_mode", test_manager_set_token_color_routes_by_mode);
|
||||
g_test_add_func ("/theme/manager/set_token_color_routes_setup_indexes",
|
||||
test_manager_set_token_color_routes_setup_indexes);
|
||||
g_test_add_func ("/theme/manager/listener_registration_dispatch_and_unregister",
|
||||
test_manager_listener_registration_dispatch_and_unregister);
|
||||
g_test_add_func ("/theme/manager/window_attach_detach_idempotence",
|
||||
test_manager_window_attach_detach_idempotence);
|
||||
return g_test_run ();
|
||||
}
|
||||
281
src/fe-gtk/theme/tests/test-theme-runtime-persistence.c
Normal file
281
src/fe-gtk/theme/tests/test-theme-runtime-persistence.c
Normal file
@@ -0,0 +1,281 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../theme-runtime.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 char *test_home_dir;
|
||||
|
||||
static gboolean
|
||||
read_line_value (const char *cfg, const char *key, char *out, gsize out_len)
|
||||
{
|
||||
char *pattern;
|
||||
char *pos;
|
||||
char *line_end;
|
||||
gsize value_len;
|
||||
|
||||
pattern = g_strdup_printf ("%s = ", key);
|
||||
pos = g_strstr_len (cfg, -1, pattern);
|
||||
g_free (pattern);
|
||||
if (!pos)
|
||||
return FALSE;
|
||||
|
||||
pos = strchr (pos, '=');
|
||||
if (!pos)
|
||||
return FALSE;
|
||||
pos++;
|
||||
while (*pos == ' ')
|
||||
pos++;
|
||||
|
||||
line_end = strchr (pos, '\n');
|
||||
if (!line_end)
|
||||
line_end = pos + strlen (pos);
|
||||
value_len = (gsize) (line_end - pos);
|
||||
if (value_len + 1 > out_len)
|
||||
return FALSE;
|
||||
memcpy (out, pos, value_len);
|
||||
out[value_len] = '\0';
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
cfg_get_color (char *cfg, char *var, guint16 *r, guint16 *g, guint16 *b)
|
||||
{
|
||||
char value[128];
|
||||
|
||||
if (!read_line_value (cfg, var, value, sizeof (value)))
|
||||
return 0;
|
||||
if (sscanf (value, "%04hx %04hx %04hx", r, g, b) != 3)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
cfg_get_int (char *cfg, char *var)
|
||||
{
|
||||
char value[128];
|
||||
|
||||
if (!read_line_value (cfg, var, value, sizeof (value)))
|
||||
return 0;
|
||||
return atoi (value);
|
||||
}
|
||||
|
||||
int
|
||||
cfg_put_color (int fh, guint16 r, guint16 g, guint16 b, char *var)
|
||||
{
|
||||
char line[256];
|
||||
int len;
|
||||
|
||||
len = g_snprintf (line, sizeof line, "%s = %04hx %04hx %04hx\n", var, r, g, b);
|
||||
if (len < 0)
|
||||
return 0;
|
||||
return write (fh, line, (size_t) len) == len;
|
||||
}
|
||||
|
||||
int
|
||||
cfg_put_int (int fh, int value, char *var)
|
||||
{
|
||||
char line[128];
|
||||
int len;
|
||||
|
||||
len = g_snprintf (line, sizeof line, "%s = %d\n", var, value);
|
||||
if (len < 0)
|
||||
return 0;
|
||||
return write (fh, line, (size_t) len) == len;
|
||||
}
|
||||
|
||||
int
|
||||
zoitechat_open_file (const char *file, int flags, int mode, int xof_flags)
|
||||
{
|
||||
char *path;
|
||||
int fd;
|
||||
|
||||
(void) xof_flags;
|
||||
path = g_build_filename (test_home_dir, file, NULL);
|
||||
fd = g_open (path, flags, mode);
|
||||
g_free (path);
|
||||
return fd;
|
||||
}
|
||||
|
||||
gboolean
|
||||
fe_dark_mode_is_enabled_for (unsigned int mode)
|
||||
{
|
||||
return mode == ZOITECHAT_DARK_MODE_DARK;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_policy_system_prefers_dark (void)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_policy_is_dark_mode_active (unsigned int mode)
|
||||
{
|
||||
return mode == ZOITECHAT_DARK_MODE_DARK;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_temp_home (void)
|
||||
{
|
||||
if (test_home_dir)
|
||||
return;
|
||||
test_home_dir = g_dir_make_tmp ("zoitechat-theme-tests-XXXXXX", NULL);
|
||||
g_assert_nonnull (test_home_dir);
|
||||
}
|
||||
|
||||
static char *
|
||||
read_colors_conf (void)
|
||||
{
|
||||
char *path;
|
||||
char *content = NULL;
|
||||
gsize length = 0;
|
||||
gboolean ok;
|
||||
|
||||
path = g_build_filename (test_home_dir, "colors.conf", NULL);
|
||||
ok = g_file_get_contents (path, &content, &length, NULL);
|
||||
g_free (path);
|
||||
g_assert_true (ok);
|
||||
g_assert_cmpuint (length, >, 0);
|
||||
return content;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
colors_equal (const GdkRGBA *a, const GdkRGBA *b)
|
||||
{
|
||||
return a->red == b->red && a->green == b->green && a->blue == b->blue;
|
||||
}
|
||||
|
||||
static void
|
||||
apply_ui_color_edit (unsigned int mode, ThemeSemanticToken token, const char *hex)
|
||||
{
|
||||
GdkRGBA color;
|
||||
|
||||
g_assert_true (gdk_rgba_parse (&color, hex));
|
||||
if (theme_policy_is_dark_mode_active (mode))
|
||||
theme_runtime_dark_set_color (token, &color);
|
||||
else
|
||||
theme_runtime_user_set_color (token, &color);
|
||||
theme_runtime_apply_mode (mode, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
test_persistence_roundtrip_light_and_dark (void)
|
||||
{
|
||||
GdkRGBA light_color;
|
||||
GdkRGBA dark_color;
|
||||
GdkRGBA loaded;
|
||||
char *cfg;
|
||||
|
||||
setup_temp_home ();
|
||||
theme_runtime_load ();
|
||||
|
||||
gdk_rgba_parse (&light_color, "#123456");
|
||||
theme_runtime_user_set_color (THEME_TOKEN_MIRC_0, &light_color);
|
||||
theme_runtime_apply_dark_mode (FALSE);
|
||||
|
||||
theme_runtime_apply_dark_mode (TRUE);
|
||||
gdk_rgba_parse (&dark_color, "#abcdef");
|
||||
theme_runtime_dark_set_color (THEME_TOKEN_MIRC_0, &dark_color);
|
||||
|
||||
theme_runtime_save ();
|
||||
cfg = read_colors_conf ();
|
||||
g_assert_nonnull (g_strstr_len (cfg, -1, "theme.mode.light.token.mirc_0"));
|
||||
g_assert_nonnull (g_strstr_len (cfg, -1, "theme.mode.dark.token.mirc_0"));
|
||||
g_assert_null (g_strstr_len (cfg, -1, "color_0 = "));
|
||||
g_assert_null (g_strstr_len (cfg, -1, "dark_color_0 = "));
|
||||
g_free (cfg);
|
||||
|
||||
theme_runtime_load ();
|
||||
theme_runtime_apply_dark_mode (FALSE);
|
||||
g_assert_true (theme_runtime_get_color (THEME_TOKEN_MIRC_0, &loaded));
|
||||
g_assert_true (colors_equal (&light_color, &loaded));
|
||||
|
||||
theme_runtime_apply_dark_mode (TRUE);
|
||||
g_assert_true (theme_runtime_get_color (THEME_TOKEN_MIRC_0, &loaded));
|
||||
g_assert_true (colors_equal (&dark_color, &loaded));
|
||||
}
|
||||
|
||||
static void
|
||||
test_loads_legacy_color_keys_via_migration_loader (void)
|
||||
{
|
||||
char *path;
|
||||
const char *legacy_cfg =
|
||||
"color_0 = 1111 2222 3333\n"
|
||||
"dark_color_0 = aaaa bbbb cccc\n";
|
||||
GdkRGBA loaded;
|
||||
GdkRGBA light_expected;
|
||||
GdkRGBA dark_expected;
|
||||
gboolean ok;
|
||||
|
||||
setup_temp_home ();
|
||||
path = g_build_filename (test_home_dir, "colors.conf", NULL);
|
||||
ok = g_file_set_contents (path, legacy_cfg, -1, NULL);
|
||||
g_free (path);
|
||||
g_assert_true (ok);
|
||||
|
||||
theme_runtime_load ();
|
||||
|
||||
gdk_rgba_parse (&light_expected, "#111122223333");
|
||||
gdk_rgba_parse (&dark_expected, "#aaaabbbbcccc");
|
||||
|
||||
theme_runtime_apply_dark_mode (FALSE);
|
||||
g_assert_true (theme_runtime_get_color (THEME_TOKEN_MIRC_0, &loaded));
|
||||
g_assert_true (colors_equal (&loaded, &light_expected));
|
||||
|
||||
theme_runtime_apply_dark_mode (TRUE);
|
||||
g_assert_true (theme_runtime_get_color (THEME_TOKEN_MIRC_0, &loaded));
|
||||
g_assert_true (colors_equal (&loaded, &dark_expected));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_ui_edits_persist_without_legacy_array_mutation (void)
|
||||
{
|
||||
GdkRGBA light_loaded;
|
||||
GdkRGBA dark_loaded;
|
||||
GdkRGBA light_expected;
|
||||
GdkRGBA dark_expected;
|
||||
|
||||
setup_temp_home ();
|
||||
theme_runtime_load ();
|
||||
|
||||
apply_ui_color_edit (ZOITECHAT_DARK_MODE_LIGHT, THEME_TOKEN_SELECTION_FOREGROUND, "#224466");
|
||||
apply_ui_color_edit (ZOITECHAT_DARK_MODE_DARK, THEME_TOKEN_SELECTION_FOREGROUND, "#88aacc");
|
||||
theme_runtime_save ();
|
||||
|
||||
theme_runtime_load ();
|
||||
theme_runtime_apply_mode (ZOITECHAT_DARK_MODE_LIGHT, NULL);
|
||||
g_assert_true (theme_runtime_get_color (THEME_TOKEN_SELECTION_FOREGROUND, &light_loaded));
|
||||
g_assert_true (gdk_rgba_parse (&light_expected, "#224466"));
|
||||
g_assert_true (colors_equal (&light_loaded, &light_expected));
|
||||
|
||||
theme_runtime_apply_mode (ZOITECHAT_DARK_MODE_DARK, NULL);
|
||||
g_assert_true (theme_runtime_get_color (THEME_TOKEN_SELECTION_FOREGROUND, &dark_loaded));
|
||||
g_assert_true (gdk_rgba_parse (&dark_expected, "#88aacc"));
|
||||
g_assert_true (colors_equal (&dark_loaded, &dark_expected));
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
g_test_add_func ("/theme/runtime/persistence_roundtrip_light_and_dark",
|
||||
test_persistence_roundtrip_light_and_dark);
|
||||
g_test_add_func ("/theme/runtime/loads_legacy_color_keys_via_migration_loader",
|
||||
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);
|
||||
return g_test_run ();
|
||||
}
|
||||
73
src/fe-gtk/theme/theme-access.c
Normal file
73
src/fe-gtk/theme/theme-access.c
Normal file
@@ -0,0 +1,73 @@
|
||||
#include "theme-access.h"
|
||||
|
||||
#include "theme-runtime.h"
|
||||
|
||||
|
||||
static gboolean
|
||||
theme_token_to_rgb16 (ThemeSemanticToken token, guint16 *red, guint16 *green, guint16 *blue)
|
||||
{
|
||||
GdkRGBA color = { 0 };
|
||||
|
||||
g_return_val_if_fail (red != NULL, FALSE);
|
||||
g_return_val_if_fail (green != NULL, FALSE);
|
||||
g_return_val_if_fail (blue != NULL, FALSE);
|
||||
if (!theme_runtime_get_color (token, &color))
|
||||
return FALSE;
|
||||
theme_palette_color_get_rgb16 (&color, red, green, blue);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_get_color (ThemeSemanticToken token, GdkRGBA *out_rgba)
|
||||
{
|
||||
return theme_runtime_get_color (token, out_rgba);
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_get_mirc_color (unsigned int mirc_index, GdkRGBA *out_rgba)
|
||||
{
|
||||
ThemeSemanticToken token = (ThemeSemanticToken) (THEME_TOKEN_MIRC_0 + (int) mirc_index);
|
||||
|
||||
if (mirc_index >= 32)
|
||||
return FALSE;
|
||||
return theme_runtime_get_color (token, out_rgba);
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_get_color_rgb16 (ThemeSemanticToken token, guint16 *red, guint16 *green, guint16 *blue)
|
||||
{
|
||||
return theme_token_to_rgb16 (token, red, green, blue);
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_get_mirc_color_rgb16 (unsigned int mirc_index, guint16 *red, guint16 *green, guint16 *blue)
|
||||
{
|
||||
ThemeSemanticToken token = (ThemeSemanticToken) (THEME_TOKEN_MIRC_0 + (int) mirc_index);
|
||||
|
||||
if (mirc_index >= 32)
|
||||
return FALSE;
|
||||
return theme_token_to_rgb16 (token, red, green, blue);
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_get_legacy_color (int legacy_idx, GdkRGBA *out_rgba)
|
||||
{
|
||||
ThemeSemanticToken token;
|
||||
|
||||
g_return_val_if_fail (out_rgba != NULL, FALSE);
|
||||
if (!theme_palette_legacy_index_to_token (legacy_idx, &token))
|
||||
return FALSE;
|
||||
return theme_runtime_get_color (token, out_rgba);
|
||||
}
|
||||
|
||||
void
|
||||
theme_get_widget_style_values (ThemeWidgetStyleValues *out_values)
|
||||
{
|
||||
theme_runtime_get_widget_style_values (out_values);
|
||||
}
|
||||
|
||||
void
|
||||
theme_get_xtext_colors (XTextColor *palette, size_t palette_len)
|
||||
{
|
||||
theme_runtime_get_xtext_colors (palette, palette_len);
|
||||
}
|
||||
20
src/fe-gtk/theme/theme-access.h
Normal file
20
src/fe-gtk/theme/theme-access.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef ZOITECHAT_THEME_ACCESS_H
|
||||
#define ZOITECHAT_THEME_ACCESS_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "theme-palette.h"
|
||||
#include "../xtext-color.h"
|
||||
|
||||
gboolean theme_get_color (ThemeSemanticToken token, GdkRGBA *out_rgba);
|
||||
gboolean theme_get_mirc_color (unsigned int mirc_index, GdkRGBA *out_rgba);
|
||||
gboolean theme_get_color_rgb16 (ThemeSemanticToken token, guint16 *red, guint16 *green, guint16 *blue);
|
||||
gboolean theme_get_mirc_color_rgb16 (unsigned int mirc_index, guint16 *red, guint16 *green, guint16 *blue);
|
||||
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_xtext_colors (XTextColor *palette, size_t palette_len);
|
||||
|
||||
#endif
|
||||
83
src/fe-gtk/theme/theme-application.c
Normal file
83
src/fe-gtk/theme/theme-application.c
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "theme-application.h"
|
||||
|
||||
#include "../../common/fe.h"
|
||||
#include "../../common/zoitechatc.h"
|
||||
#include "theme-css.h"
|
||||
#include "theme-runtime.h"
|
||||
#include "../maingui.h"
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static void
|
||||
theme_application_apply_windows_theme (gboolean dark)
|
||||
{
|
||||
GtkSettings *settings = gtk_settings_get_default ();
|
||||
|
||||
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", dark, NULL);
|
||||
}
|
||||
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
gboolean
|
||||
theme_application_apply_mode (unsigned int mode, gboolean *palette_changed)
|
||||
{
|
||||
gboolean dark;
|
||||
|
||||
theme_runtime_load ();
|
||||
dark = theme_runtime_apply_mode (mode, palette_changed);
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
theme_application_apply_windows_theme (dark);
|
||||
#endif
|
||||
|
||||
theme_application_reload_input_style ();
|
||||
|
||||
return dark;
|
||||
}
|
||||
|
||||
void
|
||||
theme_application_reload_input_style (void)
|
||||
{
|
||||
input_style = theme_application_update_input_style (input_style);
|
||||
}
|
||||
|
||||
InputStyle *
|
||||
theme_application_update_input_style (InputStyle *style)
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
if (!style)
|
||||
style = g_new0 (InputStyle, 1);
|
||||
|
||||
if (style->font_desc)
|
||||
pango_font_description_free (style->font_desc);
|
||||
style->font_desc = pango_font_description_from_string (prefs.hex_text_font);
|
||||
|
||||
if (pango_font_description_get_size (style->font_desc) == 0)
|
||||
{
|
||||
g_snprintf (buf, sizeof (buf), _("Failed to open font:\n\n%s"), prefs.hex_text_font);
|
||||
fe_message (buf, FE_MSG_ERROR);
|
||||
pango_font_description_free (style->font_desc);
|
||||
style->font_desc = pango_font_description_from_string ("sans 11");
|
||||
}
|
||||
|
||||
theme_css_reload_input_style (prefs.hex_gui_input_style, style->font_desc);
|
||||
|
||||
return style;
|
||||
}
|
||||
11
src/fe-gtk/theme/theme-application.h
Normal file
11
src/fe-gtk/theme/theme-application.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef ZOITECHAT_THEME_APPLICATION_H
|
||||
#define ZOITECHAT_THEME_APPLICATION_H
|
||||
|
||||
#include <glib.h>
|
||||
#include "../fe-gtk.h"
|
||||
|
||||
gboolean theme_application_apply_mode (unsigned int mode, gboolean *palette_changed);
|
||||
void theme_application_reload_input_style (void);
|
||||
InputStyle *theme_application_update_input_style (InputStyle *style);
|
||||
|
||||
#endif
|
||||
298
src/fe-gtk/theme/theme-css.c
Normal file
298
src/fe-gtk/theme/theme-css.c
Normal file
@@ -0,0 +1,298 @@
|
||||
#include "theme-css.h"
|
||||
|
||||
#include "theme-runtime.h"
|
||||
#include "../gtkutil.h"
|
||||
#include <string.h>
|
||||
|
||||
static const char *theme_css_selector_input = "#zoitechat-inputbox";
|
||||
static const char *theme_css_selector_input_text = "#zoitechat-inputbox text";
|
||||
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;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *theme_name;
|
||||
char *font;
|
||||
gboolean enabled;
|
||||
gboolean dark;
|
||||
gboolean colors_set;
|
||||
guint16 fg_red;
|
||||
guint16 fg_green;
|
||||
guint16 fg_blue;
|
||||
guint16 bg_red;
|
||||
guint16 bg_green;
|
||||
guint16 bg_blue;
|
||||
} ThemeCssInputFingerprint;
|
||||
|
||||
static GtkCssProvider *theme_css_input_provider;
|
||||
static ThemeCssInputFingerprint theme_css_input_fp;
|
||||
|
||||
void
|
||||
theme_css_apply_app_provider (GtkStyleProvider *provider)
|
||||
{
|
||||
GdkScreen *screen;
|
||||
|
||||
if (!provider)
|
||||
return;
|
||||
|
||||
screen = gdk_screen_get_default ();
|
||||
if (!screen)
|
||||
return;
|
||||
|
||||
gtk_style_context_add_provider_for_screen (screen, provider, theme_css_provider_priority);
|
||||
}
|
||||
|
||||
void
|
||||
theme_css_remove_app_provider (GtkStyleProvider *provider)
|
||||
{
|
||||
GdkScreen *screen;
|
||||
|
||||
if (!provider)
|
||||
return;
|
||||
|
||||
screen = gdk_screen_get_default ();
|
||||
if (!screen)
|
||||
return;
|
||||
|
||||
gtk_style_context_remove_provider_for_screen (screen, provider);
|
||||
}
|
||||
|
||||
void
|
||||
theme_css_apply_widget_provider (GtkWidget *widget, GtkStyleProvider *provider)
|
||||
{
|
||||
GtkStyleContext *context;
|
||||
|
||||
if (!widget || !provider)
|
||||
return;
|
||||
|
||||
context = gtk_widget_get_style_context (widget);
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
gtk_style_context_add_provider (context, provider, theme_css_provider_priority);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
theme_css_input_fingerprint_matches (const ThemeCssInputFingerprint *next)
|
||||
{
|
||||
if (theme_css_input_fp.enabled != next->enabled)
|
||||
return FALSE;
|
||||
if (theme_css_input_fp.dark != next->dark)
|
||||
return FALSE;
|
||||
if (theme_css_input_fp.colors_set != next->colors_set)
|
||||
return FALSE;
|
||||
if (theme_css_input_fp.fg_red != next->fg_red || theme_css_input_fp.fg_green != next->fg_green
|
||||
|| theme_css_input_fp.fg_blue != next->fg_blue)
|
||||
return FALSE;
|
||||
if (theme_css_input_fp.bg_red != next->bg_red || theme_css_input_fp.bg_green != next->bg_green
|
||||
|| theme_css_input_fp.bg_blue != next->bg_blue)
|
||||
return FALSE;
|
||||
if (g_strcmp0 (theme_css_input_fp.theme_name, next->theme_name) != 0)
|
||||
return FALSE;
|
||||
if (g_strcmp0 (theme_css_input_fp.font, next->font) != 0)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
theme_css_input_fingerprint_replace (ThemeCssInputFingerprint *next)
|
||||
{
|
||||
g_free (theme_css_input_fp.theme_name);
|
||||
g_free (theme_css_input_fp.font);
|
||||
theme_css_input_fp = *next;
|
||||
next->theme_name = NULL;
|
||||
next->font = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
theme_css_input_fingerprint_clear (void)
|
||||
{
|
||||
g_free (theme_css_input_fp.theme_name);
|
||||
g_free (theme_css_input_fp.font);
|
||||
memset (&theme_css_input_fp, 0, sizeof (theme_css_input_fp));
|
||||
}
|
||||
|
||||
static char *
|
||||
theme_css_build_input (const char *theme_name, guint16 fg_red, guint16 fg_green, guint16 fg_blue,
|
||||
guint16 bg_red, guint16 bg_green, guint16 bg_blue)
|
||||
{
|
||||
GString *css = g_string_new ("");
|
||||
|
||||
if (g_str_has_prefix (theme_name, "Adwaita") || g_str_has_prefix (theme_name, "Yaru"))
|
||||
{
|
||||
g_string_append_printf (css, "%s { background-image: none; }", theme_css_selector_input);
|
||||
}
|
||||
|
||||
g_string_append_printf (css,
|
||||
"%s {"
|
||||
"background-color: #%02x%02x%02x;"
|
||||
"color: #%02x%02x%02x;"
|
||||
"caret-color: #%02x%02x%02x;"
|
||||
"}"
|
||||
"%s {"
|
||||
"color: #%02x%02x%02x;"
|
||||
"caret-color: #%02x%02x%02x;"
|
||||
"}",
|
||||
theme_css_selector_input,
|
||||
(bg_red >> 8), (bg_green >> 8), (bg_blue >> 8),
|
||||
(fg_red >> 8), (fg_green >> 8), (fg_blue >> 8),
|
||||
(fg_red >> 8), (fg_green >> 8), (fg_blue >> 8),
|
||||
theme_css_selector_input_text,
|
||||
(fg_red >> 8), (fg_green >> 8), (fg_blue >> 8),
|
||||
(fg_red >> 8), (fg_green >> 8), (fg_blue >> 8));
|
||||
|
||||
return g_string_free (css, FALSE);
|
||||
}
|
||||
|
||||
void
|
||||
theme_css_reload_input_style (gboolean enabled, const PangoFontDescription *font_desc)
|
||||
{
|
||||
ThemeCssInputFingerprint next = {0};
|
||||
|
||||
next.enabled = enabled;
|
||||
next.dark = theme_runtime_is_dark_active ();
|
||||
next.theme_name = NULL;
|
||||
next.font = font_desc ? pango_font_description_to_string (font_desc) : NULL;
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
GtkSettings *settings = gtk_settings_get_default ();
|
||||
char *theme_name = NULL;
|
||||
char *css;
|
||||
|
||||
if (settings)
|
||||
g_object_get (settings, "gtk-theme-name", &theme_name, NULL);
|
||||
|
||||
next.theme_name = g_strdup (theme_name);
|
||||
{
|
||||
GdkRGBA color;
|
||||
|
||||
if (theme_runtime_get_color (THEME_TOKEN_TEXT_FOREGROUND, &color))
|
||||
{
|
||||
theme_palette_color_get_rgb16 (&color, &next.fg_red, &next.fg_green, &next.fg_blue);
|
||||
next.colors_set = TRUE;
|
||||
}
|
||||
if (theme_runtime_get_color (THEME_TOKEN_TEXT_BACKGROUND, &color))
|
||||
{
|
||||
theme_palette_color_get_rgb16 (&color, &next.bg_red, &next.bg_green, &next.bg_blue);
|
||||
next.colors_set = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (theme_css_input_fingerprint_matches (&next))
|
||||
{
|
||||
g_free (theme_name);
|
||||
g_free (next.theme_name);
|
||||
g_free (next.font);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!theme_css_input_provider)
|
||||
theme_css_input_provider = gtk_css_provider_new ();
|
||||
|
||||
css = theme_css_build_input (theme_name ? theme_name : "",
|
||||
next.fg_red, next.fg_green, next.fg_blue,
|
||||
next.bg_red, next.bg_green, next.bg_blue);
|
||||
gtk_css_provider_load_from_data (theme_css_input_provider, css, -1, NULL);
|
||||
g_free (css);
|
||||
theme_css_apply_app_provider (GTK_STYLE_PROVIDER (theme_css_input_provider));
|
||||
|
||||
g_free (theme_name);
|
||||
theme_css_input_fingerprint_replace (&next);
|
||||
return;
|
||||
}
|
||||
|
||||
if (theme_css_input_provider)
|
||||
theme_css_remove_app_provider (GTK_STYLE_PROVIDER (theme_css_input_provider));
|
||||
g_clear_object (&theme_css_input_provider);
|
||||
theme_css_input_fingerprint_clear ();
|
||||
g_free (next.theme_name);
|
||||
g_free (next.font);
|
||||
}
|
||||
|
||||
void
|
||||
theme_css_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkRGBA *fg,
|
||||
const PangoFontDescription *font_desc)
|
||||
{
|
||||
GtkCssProvider *provider;
|
||||
gboolean new_provider = FALSE;
|
||||
GString *css;
|
||||
gchar *bg_color = NULL;
|
||||
gchar *fg_color = NULL;
|
||||
|
||||
if (!widget)
|
||||
return;
|
||||
|
||||
provider = g_object_get_data (G_OBJECT (widget), theme_css_palette_provider_key);
|
||||
|
||||
if (!bg && !fg && !font_desc)
|
||||
{
|
||||
gtk_style_context_remove_class (gtk_widget_get_style_context (widget), theme_css_selector_palette_class);
|
||||
if (provider)
|
||||
{
|
||||
gtk_style_context_remove_provider (gtk_widget_get_style_context (widget), GTK_STYLE_PROVIDER (provider));
|
||||
g_object_set_data (G_OBJECT (widget), theme_css_palette_provider_key, NULL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!provider)
|
||||
{
|
||||
provider = gtk_css_provider_new ();
|
||||
g_object_set_data_full (G_OBJECT (widget), theme_css_palette_provider_key,
|
||||
provider, g_object_unref);
|
||||
new_provider = TRUE;
|
||||
}
|
||||
|
||||
css = g_string_new (".");
|
||||
g_string_append (css, theme_css_selector_palette_class);
|
||||
g_string_append (css, " {");
|
||||
if (bg)
|
||||
{
|
||||
bg_color = gdk_rgba_to_string (bg);
|
||||
g_string_append_printf (css, " background-color: %s;", bg_color);
|
||||
}
|
||||
if (fg)
|
||||
{
|
||||
fg_color = gdk_rgba_to_string (fg);
|
||||
g_string_append_printf (css, " color: %s;", fg_color);
|
||||
}
|
||||
gtkutil_append_font_css (css, font_desc);
|
||||
g_string_append (css, " }");
|
||||
g_string_append_printf (css, ".%s *:selected {", theme_css_selector_palette_class);
|
||||
if (bg)
|
||||
g_string_append (css, " background-color: @theme_selected_bg_color;");
|
||||
if (fg)
|
||||
g_string_append (css, " color: @theme_selected_fg_color;");
|
||||
g_string_append (css, " }");
|
||||
|
||||
gtk_css_provider_load_from_data (provider, css->str, -1, NULL);
|
||||
if (new_provider)
|
||||
theme_css_apply_widget_provider (widget, GTK_STYLE_PROVIDER (provider));
|
||||
gtk_style_context_add_class (gtk_widget_get_style_context (widget), theme_css_selector_palette_class);
|
||||
|
||||
g_string_free (css, TRUE);
|
||||
g_free (bg_color);
|
||||
g_free (fg_color);
|
||||
}
|
||||
|
||||
char *
|
||||
theme_css_build_toplevel_classes (void)
|
||||
{
|
||||
return g_strdup_printf (
|
||||
"window.%s, .%s {"
|
||||
"background-color: #202020;"
|
||||
"color: #f0f0f0;"
|
||||
"}"
|
||||
"window.%s, .%s {"
|
||||
"background-color: #f6f6f6;"
|
||||
"color: #101010;"
|
||||
"}",
|
||||
theme_css_selector_dark_class,
|
||||
theme_css_selector_dark_class,
|
||||
theme_css_selector_light_class,
|
||||
theme_css_selector_light_class);
|
||||
}
|
||||
21
src/fe-gtk/theme/theme-css.h
Normal file
21
src/fe-gtk/theme/theme-css.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef ZOITECHAT_THEME_CSS_H
|
||||
#define ZOITECHAT_THEME_CSS_H
|
||||
|
||||
#include "../fe-gtk.h"
|
||||
|
||||
/**
|
||||
* theme_css_apply_app_provider/theme_css_remove_app_provider:
|
||||
* Use for CSS providers that should apply to the entire application screen.
|
||||
*
|
||||
* theme_css_apply_widget_provider:
|
||||
* Use for widget-local CSS providers attached to a specific widget context.
|
||||
*/
|
||||
void theme_css_apply_app_provider (GtkStyleProvider *provider);
|
||||
void theme_css_remove_app_provider (GtkStyleProvider *provider);
|
||||
void theme_css_apply_widget_provider (GtkWidget *widget, GtkStyleProvider *provider);
|
||||
void theme_css_reload_input_style (gboolean enabled, const PangoFontDescription *font_desc);
|
||||
void theme_css_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkRGBA *fg,
|
||||
const PangoFontDescription *font_desc);
|
||||
char *theme_css_build_toplevel_classes (void);
|
||||
|
||||
#endif
|
||||
9
src/fe-gtk/theme/theme-gtk.h
Normal file
9
src/fe-gtk/theme/theme-gtk.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef ZOITECHAT_THEME_GTK_H
|
||||
#define ZOITECHAT_THEME_GTK_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define THEME_GTK_COLOR_TYPE GDK_TYPE_RGBA
|
||||
#define THEME_GTK_FOREGROUND_PROPERTY "foreground-rgba"
|
||||
|
||||
#endif
|
||||
497
src/fe-gtk/theme/theme-manager.c
Normal file
497
src/fe-gtk/theme/theme-manager.c
Normal file
@@ -0,0 +1,497 @@
|
||||
#include "../fe-gtk.h"
|
||||
#include "theme-manager.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "theme-application.h"
|
||||
#include "theme-policy.h"
|
||||
#include "theme-runtime.h"
|
||||
#include "theme-access.h"
|
||||
#include "theme-css.h"
|
||||
#include "../gtkutil.h"
|
||||
#include "../maingui.h"
|
||||
#include "../setup.h"
|
||||
#include "../../common/zoitechat.h"
|
||||
#include "../../common/zoitechatc.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guint id;
|
||||
char *component_id;
|
||||
ThemeChangedCallback callback;
|
||||
gpointer userdata;
|
||||
} ThemeListener;
|
||||
|
||||
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 void
|
||||
theme_listener_free (gpointer data)
|
||||
{
|
||||
ThemeListener *listener = data;
|
||||
|
||||
if (!listener)
|
||||
return;
|
||||
|
||||
g_free (listener->component_id);
|
||||
g_free (listener);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_manager_setup_apply_listener (const ThemeChangedEvent *event, gpointer userdata)
|
||||
{
|
||||
(void) userdata;
|
||||
theme_manager_dispatch_setup_apply (event);
|
||||
}
|
||||
|
||||
static ThemeChangedReason
|
||||
theme_manager_synthesize_preference_reasons (const struct zoitechatprefs *old_prefs,
|
||||
const struct zoitechatprefs *new_prefs,
|
||||
gboolean color_change)
|
||||
{
|
||||
ThemeChangedReason reasons = THEME_CHANGED_REASON_NONE;
|
||||
|
||||
if (!old_prefs || !new_prefs)
|
||||
return reasons;
|
||||
|
||||
if (strcmp (old_prefs->hex_text_background, new_prefs->hex_text_background) != 0)
|
||||
reasons |= THEME_CHANGED_REASON_PIXMAP;
|
||||
if (old_prefs->hex_gui_tab_dots != new_prefs->hex_gui_tab_dots ||
|
||||
old_prefs->hex_gui_tab_layout != new_prefs->hex_gui_tab_layout)
|
||||
reasons |= THEME_CHANGED_REASON_LAYOUT;
|
||||
if (old_prefs->hex_identd_server != new_prefs->hex_identd_server ||
|
||||
old_prefs->hex_identd_port != new_prefs->hex_identd_port)
|
||||
reasons |= THEME_CHANGED_REASON_IDENTD;
|
||||
if (color_change ||
|
||||
old_prefs->hex_gui_ulist_color != new_prefs->hex_gui_ulist_color ||
|
||||
old_prefs->hex_away_size_max != new_prefs->hex_away_size_max ||
|
||||
old_prefs->hex_away_track != new_prefs->hex_away_track)
|
||||
reasons |= THEME_CHANGED_REASON_USERLIST;
|
||||
|
||||
if (reasons != THEME_CHANGED_REASON_NONE)
|
||||
reasons |= THEME_CHANGED_REASON_WIDGET_STYLE;
|
||||
|
||||
return reasons;
|
||||
}
|
||||
|
||||
static void
|
||||
theme_manager_auto_dark_mode_changed (GtkSettings *settings, GParamSpec *pspec, gpointer data)
|
||||
{
|
||||
gboolean color_change = FALSE;
|
||||
static gboolean in_handler = FALSE;
|
||||
|
||||
(void) settings;
|
||||
(void) pspec;
|
||||
(void) data;
|
||||
|
||||
if (prefs.hex_gui_dark_mode != ZOITECHAT_DARK_MODE_AUTO)
|
||||
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);
|
||||
|
||||
in_handler = FALSE;
|
||||
}
|
||||
|
||||
static guint theme_manager_auto_refresh_source = 0;
|
||||
static ThemeManagerIdleAddFunc theme_manager_idle_add_func = g_idle_add;
|
||||
|
||||
static gboolean
|
||||
theme_manager_run_auto_refresh (gpointer data)
|
||||
{
|
||||
theme_manager_auto_refresh_source = 0;
|
||||
theme_manager_auto_dark_mode_changed (NULL, NULL, data);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
theme_manager_queue_auto_refresh (GtkSettings *settings, GParamSpec *pspec, gpointer data)
|
||||
{
|
||||
(void) settings;
|
||||
(void) pspec;
|
||||
|
||||
if (theme_manager_auto_refresh_source != 0)
|
||||
return;
|
||||
|
||||
theme_manager_auto_refresh_source = theme_manager_idle_add_func (theme_manager_run_auto_refresh, data);
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_init (void)
|
||||
{
|
||||
GtkSettings *settings;
|
||||
|
||||
if (!theme_manager_listeners)
|
||||
theme_manager_listeners = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
|
||||
theme_listener_free);
|
||||
|
||||
if (!theme_manager_setup_listener_id)
|
||||
theme_manager_setup_listener_id = theme_listener_register ("setup.apply", theme_manager_setup_apply_listener, NULL);
|
||||
|
||||
settings = gtk_settings_get_default ();
|
||||
if (settings)
|
||||
fe_set_auto_dark_mode_state (theme_policy_system_prefers_dark ());
|
||||
|
||||
theme_application_apply_mode (prefs.hex_gui_dark_mode, NULL);
|
||||
zoitechat_set_theme_post_apply_callback (theme_manager_handle_theme_applied);
|
||||
|
||||
if (settings)
|
||||
{
|
||||
g_signal_connect (settings, "notify::gtk-application-prefer-dark-theme",
|
||||
G_CALLBACK (theme_manager_queue_auto_refresh), NULL);
|
||||
g_signal_connect (settings, "notify::gtk-theme-name",
|
||||
G_CALLBACK (theme_manager_queue_auto_refresh), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_manager_apply_mode (unsigned int mode, gboolean *palette_changed)
|
||||
{
|
||||
return theme_application_apply_mode (mode, palette_changed);
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_set_mode (unsigned int mode, gboolean *palette_changed)
|
||||
{
|
||||
theme_application_apply_mode (mode, 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);
|
||||
|
||||
changed = theme_runtime_apply_mode (mode, NULL);
|
||||
if (palette_changed)
|
||||
*palette_changed = changed;
|
||||
|
||||
theme_application_reload_input_style ();
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_commit_preferences (unsigned int old_mode, gboolean *color_change)
|
||||
{
|
||||
gboolean palette_changed = FALSE;
|
||||
|
||||
theme_application_apply_mode (prefs.hex_gui_dark_mode, &palette_changed);
|
||||
if (color_change && (prefs.hex_gui_dark_mode != old_mode || palette_changed))
|
||||
*color_change = TRUE;
|
||||
|
||||
if (prefs.hex_gui_dark_mode == ZOITECHAT_DARK_MODE_AUTO)
|
||||
fe_set_auto_dark_mode_state (theme_policy_is_dark_mode_active (ZOITECHAT_DARK_MODE_AUTO));
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_save_preferences (void)
|
||||
{
|
||||
theme_runtime_save ();
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_changed_event_has_reason (const ThemeChangedEvent *event, ThemeChangedReason reason)
|
||||
{
|
||||
if (!event)
|
||||
return FALSE;
|
||||
|
||||
return (event->reasons & reason) != 0;
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_apply_and_dispatch (unsigned int mode, ThemeChangedReason reasons, gboolean *palette_changed)
|
||||
{
|
||||
theme_application_apply_mode (mode, palette_changed);
|
||||
theme_manager_dispatch_changed (reasons);
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_dispatch_changed (ThemeChangedReason reasons)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
gpointer key;
|
||||
gpointer value;
|
||||
ThemeChangedEvent event;
|
||||
|
||||
event.reasons = reasons;
|
||||
|
||||
if (!theme_manager_listeners)
|
||||
return;
|
||||
|
||||
g_hash_table_iter_init (&iter, theme_manager_listeners);
|
||||
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||
{
|
||||
ThemeListener *listener = value;
|
||||
|
||||
if (listener->callback)
|
||||
listener->callback (&event, listener->userdata);
|
||||
}
|
||||
}
|
||||
|
||||
guint
|
||||
theme_listener_register (const char *component_id, ThemeChangedCallback callback, gpointer userdata)
|
||||
{
|
||||
ThemeListener *listener;
|
||||
guint id;
|
||||
|
||||
if (!callback)
|
||||
return 0;
|
||||
|
||||
if (!theme_manager_listeners)
|
||||
theme_manager_listeners = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
|
||||
theme_listener_free);
|
||||
|
||||
id = theme_manager_next_listener_id++;
|
||||
if (theme_manager_next_listener_id == 0)
|
||||
theme_manager_next_listener_id = 1;
|
||||
|
||||
listener = g_new0 (ThemeListener, 1);
|
||||
listener->id = id;
|
||||
listener->component_id = g_strdup (component_id ? component_id : "theme.listener");
|
||||
listener->callback = callback;
|
||||
listener->userdata = userdata;
|
||||
|
||||
g_hash_table_insert (theme_manager_listeners, GUINT_TO_POINTER (id), listener);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void
|
||||
theme_listener_unregister (guint listener_id)
|
||||
{
|
||||
if (!theme_manager_listeners || listener_id == 0)
|
||||
return;
|
||||
|
||||
g_hash_table_remove (theme_manager_listeners, GUINT_TO_POINTER (listener_id));
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_handle_theme_applied (void)
|
||||
{
|
||||
theme_runtime_load ();
|
||||
theme_runtime_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 void
|
||||
theme_manager_apply_platform_window_theme (GtkWidget *window)
|
||||
{
|
||||
#ifdef G_OS_WIN32
|
||||
GtkStyleContext *context;
|
||||
gboolean dark;
|
||||
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
context = gtk_widget_get_style_context (window);
|
||||
dark = theme_runtime_is_dark_active ();
|
||||
if (context)
|
||||
{
|
||||
gtk_style_context_remove_class (context, "zoitechat-dark");
|
||||
gtk_style_context_remove_class (context, "zoitechat-light");
|
||||
gtk_style_context_add_class (context, dark ? "zoitechat-dark" : "zoitechat-light");
|
||||
}
|
||||
fe_win32_apply_native_titlebar (window, dark);
|
||||
#else
|
||||
(void) window;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
theme_manager_window_destroy_cb (GtkWidget *window, gpointer userdata)
|
||||
{
|
||||
(void) userdata;
|
||||
g_object_set_data (G_OBJECT (window), theme_manager_window_destroy_handler_key, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_apply_to_window (GtkWidget *window)
|
||||
{
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
theme_manager_apply_platform_window_theme (window);
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_attach_window (GtkWidget *window)
|
||||
{
|
||||
gulong *handler_id;
|
||||
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
handler_id = g_object_get_data (G_OBJECT (window), theme_manager_window_destroy_handler_key);
|
||||
if (!handler_id)
|
||||
{
|
||||
handler_id = g_new (gulong, 1);
|
||||
*handler_id = g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (theme_manager_window_destroy_cb), NULL);
|
||||
g_object_set_data_full (G_OBJECT (window), theme_manager_window_destroy_handler_key, handler_id, g_free);
|
||||
}
|
||||
|
||||
theme_manager_apply_to_window (window);
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_detach_window (GtkWidget *window)
|
||||
{
|
||||
gulong *handler_id;
|
||||
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
handler_id = g_object_get_data (G_OBJECT (window), theme_manager_window_destroy_handler_key);
|
||||
if (handler_id)
|
||||
{
|
||||
g_signal_handler_disconnect (G_OBJECT (window), *handler_id);
|
||||
g_object_set_data (G_OBJECT (window), theme_manager_window_destroy_handler_key, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkRGBA *fg,
|
||||
const PangoFontDescription *font_desc)
|
||||
{
|
||||
theme_css_apply_palette_widget (widget, bg, fg, font_desc);
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_apply_entry_palette (GtkWidget *widget, const PangoFontDescription *font_desc)
|
||||
{
|
||||
ThemeWidgetStyleValues style_values;
|
||||
|
||||
if (!widget || !font_desc)
|
||||
return;
|
||||
|
||||
theme_get_widget_style_values (&style_values);
|
||||
gtkutil_apply_palette (widget, &style_values.background, &style_values.foreground, font_desc);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return behavior;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return behavior;
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_apply_userlist_palette (GtkWidget *widget, const PangoFontDescription *font_desc,
|
||||
gboolean prefer_background, gboolean prefer_foreground)
|
||||
{
|
||||
ThemePaletteBehavior behavior;
|
||||
|
||||
behavior.font_desc = font_desc;
|
||||
behavior.apply_background = prefer_background;
|
||||
behavior.apply_foreground = prefer_foreground;
|
||||
theme_manager_apply_userlist_style (widget, behavior);
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_apply_userlist_style (GtkWidget *widget, ThemePaletteBehavior behavior)
|
||||
{
|
||||
ThemeWidgetStyleValues style_values;
|
||||
const GdkRGBA *background = NULL;
|
||||
const GdkRGBA *foreground = NULL;
|
||||
|
||||
if (!widget)
|
||||
return;
|
||||
|
||||
theme_get_widget_style_values (&style_values);
|
||||
if (behavior.apply_background)
|
||||
background = &style_values.background;
|
||||
if (behavior.apply_foreground)
|
||||
foreground = &style_values.foreground;
|
||||
|
||||
gtkutil_apply_palette (widget, background, foreground, behavior.font_desc);
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_apply_channel_tree_style (GtkWidget *widget, ThemePaletteBehavior behavior)
|
||||
{
|
||||
theme_manager_apply_userlist_style (widget, behavior);
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_apply_input_style (gboolean enabled, const PangoFontDescription *font_desc)
|
||||
{
|
||||
theme_css_reload_input_style (enabled, font_desc);
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_reload_input_style (void)
|
||||
{
|
||||
theme_application_reload_input_style ();
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_refresh_auto_mode (void)
|
||||
{
|
||||
theme_manager_queue_auto_refresh (NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
ThemeChangedEvent
|
||||
theme_manager_on_preferences_changed (const struct zoitechatprefs *old_prefs,
|
||||
const struct zoitechatprefs *new_prefs,
|
||||
unsigned int old_mode,
|
||||
gboolean *color_change)
|
||||
{
|
||||
ThemeChangedEvent event;
|
||||
gboolean had_color_change = color_change && *color_change;
|
||||
|
||||
theme_manager_commit_preferences (old_mode, color_change);
|
||||
event.reasons = theme_manager_synthesize_preference_reasons (old_prefs, new_prefs,
|
||||
had_color_change || (color_change && *color_change));
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_dispatch_setup_apply (const ThemeChangedEvent *event)
|
||||
{
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
setup_apply_real (event);
|
||||
}
|
||||
|
||||
void
|
||||
theme_manager_set_idle_add_func (ThemeManagerIdleAddFunc idle_add_func)
|
||||
{
|
||||
theme_manager_idle_add_func = idle_add_func ? idle_add_func : g_idle_add;
|
||||
theme_manager_auto_refresh_source = 0;
|
||||
}
|
||||
74
src/fe-gtk/theme/theme-manager.h
Normal file
74
src/fe-gtk/theme/theme-manager.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#ifndef ZOITECHAT_THEME_MANAGER_H
|
||||
#define ZOITECHAT_THEME_MANAGER_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "theme-palette.h"
|
||||
|
||||
typedef struct _GtkWidget GtkWidget;
|
||||
struct zoitechatprefs;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
THEME_CHANGED_REASON_NONE = 0,
|
||||
THEME_CHANGED_REASON_PALETTE = 1 << 0,
|
||||
THEME_CHANGED_REASON_WIDGET_STYLE = 1 << 1,
|
||||
THEME_CHANGED_REASON_MODE = 1 << 2,
|
||||
THEME_CHANGED_REASON_THEME_PACK = 1 << 3,
|
||||
THEME_CHANGED_REASON_PIXMAP = 1 << 4,
|
||||
THEME_CHANGED_REASON_USERLIST = 1 << 5,
|
||||
THEME_CHANGED_REASON_LAYOUT = 1 << 6,
|
||||
THEME_CHANGED_REASON_IDENTD = 1 << 7
|
||||
} ThemeChangedReason;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ThemeChangedReason reasons;
|
||||
} ThemeChangedEvent;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const PangoFontDescription *font_desc;
|
||||
gboolean apply_background;
|
||||
gboolean apply_foreground;
|
||||
} ThemePaletteBehavior;
|
||||
|
||||
typedef void (*ThemeChangedCallback) (const ThemeChangedEvent *event, gpointer userdata);
|
||||
typedef guint (*ThemeManagerIdleAddFunc) (GSourceFunc function, gpointer data);
|
||||
|
||||
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_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);
|
||||
void theme_manager_apply_and_dispatch (unsigned int mode, ThemeChangedReason reasons, gboolean *palette_changed);
|
||||
void theme_manager_dispatch_changed (ThemeChangedReason reasons);
|
||||
guint theme_listener_register (const char *component_id, ThemeChangedCallback callback, gpointer userdata);
|
||||
void theme_listener_unregister (guint listener_id);
|
||||
void theme_manager_handle_theme_applied (void);
|
||||
void theme_manager_apply_to_window (GtkWidget *window);
|
||||
void theme_manager_attach_window (GtkWidget *window);
|
||||
void theme_manager_detach_window (GtkWidget *window);
|
||||
void theme_manager_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkRGBA *fg,
|
||||
const PangoFontDescription *font_desc);
|
||||
void theme_manager_apply_entry_palette (GtkWidget *widget, const PangoFontDescription *font_desc);
|
||||
ThemePaletteBehavior theme_manager_get_userlist_palette_behavior (const PangoFontDescription *font_desc);
|
||||
ThemePaletteBehavior theme_manager_get_channel_tree_palette_behavior (const PangoFontDescription *font_desc);
|
||||
void theme_manager_apply_userlist_palette (GtkWidget *widget, const PangoFontDescription *font_desc,
|
||||
gboolean prefer_background, gboolean prefer_foreground);
|
||||
void theme_manager_apply_userlist_style (GtkWidget *widget, ThemePaletteBehavior behavior);
|
||||
void theme_manager_apply_channel_tree_style (GtkWidget *widget, ThemePaletteBehavior behavior);
|
||||
void theme_manager_apply_input_style (gboolean enabled, const PangoFontDescription *font_desc);
|
||||
void theme_manager_reload_input_style (void);
|
||||
void theme_manager_refresh_auto_mode (void);
|
||||
ThemeChangedEvent theme_manager_on_preferences_changed (const struct zoitechatprefs *old_prefs,
|
||||
const struct zoitechatprefs *new_prefs,
|
||||
unsigned int old_mode,
|
||||
gboolean *color_change);
|
||||
void theme_manager_dispatch_setup_apply (const ThemeChangedEvent *event);
|
||||
void theme_manager_set_idle_add_func (ThemeManagerIdleAddFunc idle_add_func);
|
||||
|
||||
#endif
|
||||
286
src/fe-gtk/theme/theme-palette.c
Normal file
286
src/fe-gtk/theme/theme-palette.c
Normal file
@@ -0,0 +1,286 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "theme-palette.h"
|
||||
|
||||
static const ThemePaletteTokenDef theme_palette_token_defs[] = {
|
||||
{ THEME_TOKEN_MIRC_0, 0, "mirc_0" },
|
||||
{ THEME_TOKEN_MIRC_1, 1, "mirc_1" },
|
||||
{ THEME_TOKEN_MIRC_2, 2, "mirc_2" },
|
||||
{ THEME_TOKEN_MIRC_3, 3, "mirc_3" },
|
||||
{ THEME_TOKEN_MIRC_4, 4, "mirc_4" },
|
||||
{ THEME_TOKEN_MIRC_5, 5, "mirc_5" },
|
||||
{ THEME_TOKEN_MIRC_6, 6, "mirc_6" },
|
||||
{ THEME_TOKEN_MIRC_7, 7, "mirc_7" },
|
||||
{ THEME_TOKEN_MIRC_8, 8, "mirc_8" },
|
||||
{ THEME_TOKEN_MIRC_9, 9, "mirc_9" },
|
||||
{ THEME_TOKEN_MIRC_10, 10, "mirc_10" },
|
||||
{ THEME_TOKEN_MIRC_11, 11, "mirc_11" },
|
||||
{ THEME_TOKEN_MIRC_12, 12, "mirc_12" },
|
||||
{ THEME_TOKEN_MIRC_13, 13, "mirc_13" },
|
||||
{ THEME_TOKEN_MIRC_14, 14, "mirc_14" },
|
||||
{ THEME_TOKEN_MIRC_15, 15, "mirc_15" },
|
||||
{ THEME_TOKEN_MIRC_16, 16, "mirc_16" },
|
||||
{ THEME_TOKEN_MIRC_17, 17, "mirc_17" },
|
||||
{ THEME_TOKEN_MIRC_18, 18, "mirc_18" },
|
||||
{ THEME_TOKEN_MIRC_19, 19, "mirc_19" },
|
||||
{ THEME_TOKEN_MIRC_20, 20, "mirc_20" },
|
||||
{ THEME_TOKEN_MIRC_21, 21, "mirc_21" },
|
||||
{ THEME_TOKEN_MIRC_22, 22, "mirc_22" },
|
||||
{ THEME_TOKEN_MIRC_23, 23, "mirc_23" },
|
||||
{ THEME_TOKEN_MIRC_24, 24, "mirc_24" },
|
||||
{ THEME_TOKEN_MIRC_25, 25, "mirc_25" },
|
||||
{ THEME_TOKEN_MIRC_26, 26, "mirc_26" },
|
||||
{ THEME_TOKEN_MIRC_27, 27, "mirc_27" },
|
||||
{ THEME_TOKEN_MIRC_28, 28, "mirc_28" },
|
||||
{ THEME_TOKEN_MIRC_29, 29, "mirc_29" },
|
||||
{ THEME_TOKEN_MIRC_30, 30, "mirc_30" },
|
||||
{ THEME_TOKEN_MIRC_31, 31, "mirc_31" },
|
||||
{ THEME_TOKEN_SELECTION_FOREGROUND, 32, "selection_foreground" },
|
||||
{ THEME_TOKEN_SELECTION_BACKGROUND, 33, "selection_background" },
|
||||
{ THEME_TOKEN_TEXT_FOREGROUND, 34, "text_foreground" },
|
||||
{ THEME_TOKEN_TEXT_BACKGROUND, 35, "text_background" },
|
||||
{ THEME_TOKEN_MARKER, 36, "marker" },
|
||||
{ THEME_TOKEN_TAB_NEW_DATA, 37, "tab_new_data" },
|
||||
{ THEME_TOKEN_TAB_HIGHLIGHT, 38, "tab_highlight" },
|
||||
{ THEME_TOKEN_TAB_NEW_MESSAGE, 39, "tab_new_message" },
|
||||
{ THEME_TOKEN_TAB_AWAY, 40, "tab_away" },
|
||||
{ THEME_TOKEN_SPELL, 41, "spell" },
|
||||
};
|
||||
|
||||
static const ThemePaletteTokenDef *
|
||||
theme_palette_lookup_token_def (ThemeSemanticToken token)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (theme_palette_token_defs); i++)
|
||||
{
|
||||
if (theme_palette_token_defs[i].token == token)
|
||||
return &theme_palette_token_defs[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const ThemePaletteTokenDef *
|
||||
theme_palette_lookup_legacy_def (int legacy_idx)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (theme_palette_token_defs); i++)
|
||||
{
|
||||
if (theme_palette_token_defs[i].legacy_index == legacy_idx)
|
||||
return &theme_palette_token_defs[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t
|
||||
theme_palette_token_count (void)
|
||||
{
|
||||
return THEME_TOKEN_COUNT;
|
||||
}
|
||||
|
||||
size_t
|
||||
theme_palette_token_def_count (void)
|
||||
{
|
||||
return G_N_ELEMENTS (theme_palette_token_defs);
|
||||
}
|
||||
|
||||
const ThemePaletteTokenDef *
|
||||
theme_palette_token_def_at (size_t index)
|
||||
{
|
||||
if (index >= G_N_ELEMENTS (theme_palette_token_defs))
|
||||
return NULL;
|
||||
|
||||
return &theme_palette_token_defs[index];
|
||||
}
|
||||
|
||||
const ThemePaletteTokenDef *
|
||||
theme_palette_token_def_for_token (ThemeSemanticToken token)
|
||||
{
|
||||
if (token < 0 || token >= THEME_TOKEN_COUNT)
|
||||
return NULL;
|
||||
|
||||
return theme_palette_lookup_token_def (token);
|
||||
}
|
||||
|
||||
const char *
|
||||
theme_palette_token_name (ThemeSemanticToken token)
|
||||
{
|
||||
const ThemePaletteTokenDef *def = theme_palette_token_def_for_token (token);
|
||||
|
||||
if (def == NULL)
|
||||
return NULL;
|
||||
|
||||
return def->name;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_palette_token_to_legacy_index (ThemeSemanticToken token, int *legacy_idx)
|
||||
{
|
||||
const ThemePaletteTokenDef *def;
|
||||
|
||||
if (legacy_idx == NULL)
|
||||
return FALSE;
|
||||
|
||||
def = theme_palette_token_def_for_token (token);
|
||||
if (def == NULL)
|
||||
return FALSE;
|
||||
|
||||
*legacy_idx = def->legacy_index;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_palette_legacy_index_to_token (int legacy_idx, ThemeSemanticToken *token)
|
||||
{
|
||||
const ThemePaletteTokenDef *def;
|
||||
|
||||
if (token == NULL)
|
||||
return FALSE;
|
||||
|
||||
def = theme_palette_lookup_legacy_def (legacy_idx);
|
||||
if (def == NULL)
|
||||
return FALSE;
|
||||
|
||||
*token = def->token;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_palette_set_color (ThemePalette *palette, ThemeSemanticToken token, const GdkRGBA *color)
|
||||
{
|
||||
if (palette == NULL || color == NULL)
|
||||
return FALSE;
|
||||
if (token < 0 || token >= THEME_TOKEN_COUNT)
|
||||
return FALSE;
|
||||
if (theme_palette_token_def_for_token (token) == NULL)
|
||||
return FALSE;
|
||||
|
||||
palette->colors[token] = *color;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_palette_get_color (const ThemePalette *palette, ThemeSemanticToken token, GdkRGBA *color)
|
||||
{
|
||||
if (palette == NULL || color == NULL)
|
||||
return FALSE;
|
||||
if (token < 0 || token >= THEME_TOKEN_COUNT)
|
||||
return FALSE;
|
||||
if (theme_palette_token_def_for_token (token) == NULL)
|
||||
return FALSE;
|
||||
|
||||
*color = palette->colors[token];
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
theme_palette_from_legacy_colors (ThemePalette *palette, const GdkRGBA *legacy_colors, size_t legacy_len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
g_return_if_fail (palette != NULL);
|
||||
g_return_if_fail (legacy_colors != NULL);
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (theme_palette_token_defs); i++)
|
||||
{
|
||||
int legacy_idx = theme_palette_token_defs[i].legacy_index;
|
||||
ThemeSemanticToken token = theme_palette_token_defs[i].token;
|
||||
|
||||
g_return_if_fail (legacy_idx >= 0);
|
||||
g_return_if_fail ((size_t) legacy_idx < legacy_len);
|
||||
palette->colors[token] = legacy_colors[legacy_idx];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
theme_palette_to_legacy_colors (const ThemePalette *palette, GdkRGBA *legacy_colors, size_t legacy_len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
g_return_if_fail (palette != NULL);
|
||||
g_return_if_fail (legacy_colors != NULL);
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (theme_palette_token_defs); i++)
|
||||
{
|
||||
int legacy_idx = theme_palette_token_defs[i].legacy_index;
|
||||
ThemeSemanticToken token = theme_palette_token_defs[i].token;
|
||||
|
||||
g_return_if_fail (legacy_idx >= 0);
|
||||
g_return_if_fail ((size_t) legacy_idx < legacy_len);
|
||||
legacy_colors[legacy_idx] = palette->colors[token];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
theme_palette_to_xtext_colors (const ThemePalette *palette, XTextColor *xtext_colors, size_t xtext_len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
g_return_if_fail (palette != NULL);
|
||||
g_return_if_fail (xtext_colors != NULL);
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (theme_palette_token_defs); i++)
|
||||
{
|
||||
int legacy_idx = theme_palette_token_defs[i].legacy_index;
|
||||
ThemeSemanticToken token = theme_palette_token_defs[i].token;
|
||||
|
||||
if ((size_t) legacy_idx >= xtext_len)
|
||||
continue;
|
||||
xtext_colors[legacy_idx].red = palette->colors[token].red;
|
||||
xtext_colors[legacy_idx].green = palette->colors[token].green;
|
||||
xtext_colors[legacy_idx].blue = palette->colors[token].blue;
|
||||
xtext_colors[legacy_idx].alpha = palette->colors[token].alpha;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
theme_palette_to_widget_style_values (const ThemePalette *palette, ThemeWidgetStyleValues *style_values)
|
||||
{
|
||||
g_return_if_fail (palette != NULL);
|
||||
g_return_if_fail (style_values != NULL);
|
||||
|
||||
style_values->foreground = palette->colors[THEME_TOKEN_TEXT_FOREGROUND];
|
||||
style_values->background = palette->colors[THEME_TOKEN_TEXT_BACKGROUND];
|
||||
style_values->selection_foreground = palette->colors[THEME_TOKEN_SELECTION_FOREGROUND];
|
||||
style_values->selection_background = palette->colors[THEME_TOKEN_SELECTION_BACKGROUND];
|
||||
g_snprintf (style_values->foreground_css, sizeof (style_values->foreground_css),
|
||||
"rgba(%u,%u,%u,%.3f)",
|
||||
(guint) CLAMP (style_values->foreground.red * 255.0 + 0.5, 0.0, 255.0),
|
||||
(guint) CLAMP (style_values->foreground.green * 255.0 + 0.5, 0.0, 255.0),
|
||||
(guint) CLAMP (style_values->foreground.blue * 255.0 + 0.5, 0.0, 255.0),
|
||||
CLAMP (style_values->foreground.alpha, 0.0, 1.0));
|
||||
g_snprintf (style_values->background_css, sizeof (style_values->background_css),
|
||||
"rgba(%u,%u,%u,%.3f)",
|
||||
(guint) CLAMP (style_values->background.red * 255.0 + 0.5, 0.0, 255.0),
|
||||
(guint) CLAMP (style_values->background.green * 255.0 + 0.5, 0.0, 255.0),
|
||||
(guint) CLAMP (style_values->background.blue * 255.0 + 0.5, 0.0, 255.0),
|
||||
CLAMP (style_values->background.alpha, 0.0, 1.0));
|
||||
g_snprintf (style_values->selection_foreground_css, sizeof (style_values->selection_foreground_css),
|
||||
"rgba(%u,%u,%u,%.3f)",
|
||||
(guint) CLAMP (style_values->selection_foreground.red * 255.0 + 0.5, 0.0, 255.0),
|
||||
(guint) CLAMP (style_values->selection_foreground.green * 255.0 + 0.5, 0.0, 255.0),
|
||||
(guint) CLAMP (style_values->selection_foreground.blue * 255.0 + 0.5, 0.0, 255.0),
|
||||
CLAMP (style_values->selection_foreground.alpha, 0.0, 1.0));
|
||||
g_snprintf (style_values->selection_background_css, sizeof (style_values->selection_background_css),
|
||||
"rgba(%u,%u,%u,%.3f)",
|
||||
(guint) CLAMP (style_values->selection_background.red * 255.0 + 0.5, 0.0, 255.0),
|
||||
(guint) CLAMP (style_values->selection_background.green * 255.0 + 0.5, 0.0, 255.0),
|
||||
(guint) CLAMP (style_values->selection_background.blue * 255.0 + 0.5, 0.0, 255.0),
|
||||
CLAMP (style_values->selection_background.alpha, 0.0, 1.0));
|
||||
}
|
||||
|
||||
void
|
||||
theme_palette_color_get_rgb16 (const GdkRGBA *color, guint16 *red, guint16 *green, guint16 *blue)
|
||||
{
|
||||
g_return_if_fail (color != NULL);
|
||||
g_return_if_fail (red != NULL);
|
||||
g_return_if_fail (green != NULL);
|
||||
g_return_if_fail (blue != NULL);
|
||||
|
||||
*red = (guint16) CLAMP (color->red * 65535.0 + 0.5, 0.0, 65535.0);
|
||||
*green = (guint16) CLAMP (color->green * 65535.0 + 0.5, 0.0, 65535.0);
|
||||
*blue = (guint16) CLAMP (color->blue * 65535.0 + 0.5, 0.0, 65535.0);
|
||||
}
|
||||
143
src/fe-gtk/theme/theme-palette.h
Normal file
143
src/fe-gtk/theme/theme-palette.h
Normal file
@@ -0,0 +1,143 @@
|
||||
#ifndef ZOITECHAT_THEME_PALETTE_H
|
||||
#define ZOITECHAT_THEME_PALETTE_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "../xtext-color.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
THEME_LEGACY_MIRC_0 = 0,
|
||||
THEME_LEGACY_MIRC_1,
|
||||
THEME_LEGACY_MIRC_2,
|
||||
THEME_LEGACY_MIRC_3,
|
||||
THEME_LEGACY_MIRC_4,
|
||||
THEME_LEGACY_MIRC_5,
|
||||
THEME_LEGACY_MIRC_6,
|
||||
THEME_LEGACY_MIRC_7,
|
||||
THEME_LEGACY_MIRC_8,
|
||||
THEME_LEGACY_MIRC_9,
|
||||
THEME_LEGACY_MIRC_10,
|
||||
THEME_LEGACY_MIRC_11,
|
||||
THEME_LEGACY_MIRC_12,
|
||||
THEME_LEGACY_MIRC_13,
|
||||
THEME_LEGACY_MIRC_14,
|
||||
THEME_LEGACY_MIRC_15,
|
||||
THEME_LEGACY_MIRC_16,
|
||||
THEME_LEGACY_MIRC_17,
|
||||
THEME_LEGACY_MIRC_18,
|
||||
THEME_LEGACY_MIRC_19,
|
||||
THEME_LEGACY_MIRC_20,
|
||||
THEME_LEGACY_MIRC_21,
|
||||
THEME_LEGACY_MIRC_22,
|
||||
THEME_LEGACY_MIRC_23,
|
||||
THEME_LEGACY_MIRC_24,
|
||||
THEME_LEGACY_MIRC_25,
|
||||
THEME_LEGACY_MIRC_26,
|
||||
THEME_LEGACY_MIRC_27,
|
||||
THEME_LEGACY_MIRC_28,
|
||||
THEME_LEGACY_MIRC_29,
|
||||
THEME_LEGACY_MIRC_30,
|
||||
THEME_LEGACY_MIRC_31,
|
||||
THEME_LEGACY_SELECTION_FOREGROUND,
|
||||
THEME_LEGACY_SELECTION_BACKGROUND,
|
||||
THEME_LEGACY_TEXT_FOREGROUND,
|
||||
THEME_LEGACY_TEXT_BACKGROUND,
|
||||
THEME_LEGACY_MARKER,
|
||||
THEME_LEGACY_TAB_NEW_DATA,
|
||||
THEME_LEGACY_TAB_HIGHLIGHT,
|
||||
THEME_LEGACY_TAB_NEW_MESSAGE,
|
||||
THEME_LEGACY_TAB_AWAY,
|
||||
THEME_LEGACY_SPELL,
|
||||
THEME_LEGACY_MAX = THEME_LEGACY_SPELL
|
||||
} ThemeLegacyColorIndex;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
THEME_TOKEN_MIRC_0 = 0,
|
||||
THEME_TOKEN_MIRC_1,
|
||||
THEME_TOKEN_MIRC_2,
|
||||
THEME_TOKEN_MIRC_3,
|
||||
THEME_TOKEN_MIRC_4,
|
||||
THEME_TOKEN_MIRC_5,
|
||||
THEME_TOKEN_MIRC_6,
|
||||
THEME_TOKEN_MIRC_7,
|
||||
THEME_TOKEN_MIRC_8,
|
||||
THEME_TOKEN_MIRC_9,
|
||||
THEME_TOKEN_MIRC_10,
|
||||
THEME_TOKEN_MIRC_11,
|
||||
THEME_TOKEN_MIRC_12,
|
||||
THEME_TOKEN_MIRC_13,
|
||||
THEME_TOKEN_MIRC_14,
|
||||
THEME_TOKEN_MIRC_15,
|
||||
THEME_TOKEN_MIRC_16,
|
||||
THEME_TOKEN_MIRC_17,
|
||||
THEME_TOKEN_MIRC_18,
|
||||
THEME_TOKEN_MIRC_19,
|
||||
THEME_TOKEN_MIRC_20,
|
||||
THEME_TOKEN_MIRC_21,
|
||||
THEME_TOKEN_MIRC_22,
|
||||
THEME_TOKEN_MIRC_23,
|
||||
THEME_TOKEN_MIRC_24,
|
||||
THEME_TOKEN_MIRC_25,
|
||||
THEME_TOKEN_MIRC_26,
|
||||
THEME_TOKEN_MIRC_27,
|
||||
THEME_TOKEN_MIRC_28,
|
||||
THEME_TOKEN_MIRC_29,
|
||||
THEME_TOKEN_MIRC_30,
|
||||
THEME_TOKEN_MIRC_31,
|
||||
THEME_TOKEN_SELECTION_FOREGROUND,
|
||||
THEME_TOKEN_SELECTION_BACKGROUND,
|
||||
THEME_TOKEN_TEXT_FOREGROUND,
|
||||
THEME_TOKEN_TEXT_BACKGROUND,
|
||||
THEME_TOKEN_MARKER,
|
||||
THEME_TOKEN_TAB_NEW_DATA,
|
||||
THEME_TOKEN_TAB_HIGHLIGHT,
|
||||
THEME_TOKEN_TAB_NEW_MESSAGE,
|
||||
THEME_TOKEN_TAB_AWAY,
|
||||
THEME_TOKEN_SPELL,
|
||||
THEME_TOKEN_COUNT
|
||||
} ThemeSemanticToken;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ThemeSemanticToken token;
|
||||
int legacy_index;
|
||||
const char *name;
|
||||
} ThemePaletteTokenDef;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GdkRGBA colors[THEME_TOKEN_COUNT];
|
||||
} ThemePalette;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GdkRGBA foreground;
|
||||
GdkRGBA background;
|
||||
GdkRGBA selection_foreground;
|
||||
GdkRGBA selection_background;
|
||||
char foreground_css[32];
|
||||
char background_css[32];
|
||||
char selection_foreground_css[32];
|
||||
char selection_background_css[32];
|
||||
} ThemeWidgetStyleValues;
|
||||
|
||||
size_t theme_palette_token_count (void);
|
||||
size_t theme_palette_token_def_count (void);
|
||||
const ThemePaletteTokenDef *theme_palette_token_def_at (size_t index);
|
||||
const ThemePaletteTokenDef *theme_palette_token_def_for_token (ThemeSemanticToken token);
|
||||
const char *theme_palette_token_name (ThemeSemanticToken token);
|
||||
gboolean theme_palette_token_to_legacy_index (ThemeSemanticToken token, int *legacy_idx);
|
||||
gboolean theme_palette_legacy_index_to_token (int legacy_idx, ThemeSemanticToken *token);
|
||||
gboolean theme_palette_set_color (ThemePalette *palette, ThemeSemanticToken token, const GdkRGBA *color);
|
||||
gboolean theme_palette_get_color (const ThemePalette *palette, ThemeSemanticToken token, GdkRGBA *color);
|
||||
void theme_palette_from_legacy_colors (ThemePalette *palette, const GdkRGBA *legacy_colors, size_t legacy_len);
|
||||
void theme_palette_to_legacy_colors (const ThemePalette *palette, GdkRGBA *legacy_colors, size_t legacy_len);
|
||||
void theme_palette_to_xtext_colors (const ThemePalette *palette, XTextColor *xtext_colors, size_t xtext_len);
|
||||
void theme_palette_to_widget_style_values (const ThemePalette *palette, ThemeWidgetStyleValues *style_values);
|
||||
void theme_palette_color_get_rgb16 (const GdkRGBA *color, guint16 *red, guint16 *green, guint16 *blue);
|
||||
|
||||
#endif
|
||||
59
src/fe-gtk/theme/theme-policy.c
Normal file
59
src/fe-gtk/theme/theme-policy.c
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "theme-policy.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "../fe-gtk.h"
|
||||
#include "../../common/zoitechat.h"
|
||||
#include "../../common/zoitechatc.h"
|
||||
|
||||
gboolean
|
||||
theme_policy_system_prefers_dark (void)
|
||||
{
|
||||
GtkSettings *settings = gtk_settings_get_default ();
|
||||
gboolean prefer_dark = FALSE;
|
||||
char *theme_name = NULL;
|
||||
#ifdef G_OS_WIN32
|
||||
gboolean have_win_pref = FALSE;
|
||||
|
||||
if (fe_win32_high_contrast_is_enabled ())
|
||||
return FALSE;
|
||||
|
||||
have_win_pref = fe_win32_try_get_system_dark (&prefer_dark);
|
||||
if (!have_win_pref)
|
||||
#endif
|
||||
if (settings && g_object_class_find_property (G_OBJECT_GET_CLASS (settings),
|
||||
"gtk-application-prefer-dark-theme"))
|
||||
{
|
||||
g_object_get (settings, "gtk-application-prefer-dark-theme", &prefer_dark, NULL);
|
||||
}
|
||||
|
||||
if (settings && !prefer_dark)
|
||||
{
|
||||
g_object_get (settings, "gtk-theme-name", &theme_name, NULL);
|
||||
if (theme_name)
|
||||
{
|
||||
char *lower = g_ascii_strdown (theme_name, -1);
|
||||
if (g_str_has_suffix (lower, "-dark") || g_strrstr (lower, "dark"))
|
||||
prefer_dark = TRUE;
|
||||
g_free (lower);
|
||||
g_free (theme_name);
|
||||
}
|
||||
}
|
||||
|
||||
return prefer_dark;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_policy_is_dark_mode_active (unsigned int mode)
|
||||
{
|
||||
if (mode == ZOITECHAT_DARK_MODE_AUTO)
|
||||
return theme_policy_system_prefers_dark ();
|
||||
|
||||
return fe_dark_mode_is_enabled_for (mode);
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_policy_is_app_dark_mode_active (void)
|
||||
{
|
||||
return theme_policy_is_dark_mode_active (prefs.hex_gui_dark_mode);
|
||||
}
|
||||
10
src/fe-gtk/theme/theme-policy.h
Normal file
10
src/fe-gtk/theme/theme-policy.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef ZOITECHAT_THEME_POLICY_H
|
||||
#define ZOITECHAT_THEME_POLICY_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
gboolean theme_policy_system_prefers_dark (void);
|
||||
gboolean theme_policy_is_dark_mode_active (unsigned int mode);
|
||||
gboolean theme_policy_is_app_dark_mode_active (void);
|
||||
|
||||
#endif
|
||||
580
src/fe-gtk/theme/theme-preferences.c
Normal file
580
src/fe-gtk/theme/theme-preferences.c
Normal file
@@ -0,0 +1,580 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include "../gtkutil.h"
|
||||
#include "../../common/fe.h"
|
||||
#include "../../common/util.h"
|
||||
#include "../../common/cfgfiles.h"
|
||||
#include "../../common/zoitechat.h"
|
||||
#include "../../common/theme-service.h"
|
||||
#include "../../common/zoitechatc.h"
|
||||
#include "theme-css.h"
|
||||
#include "theme-manager.h"
|
||||
#include "theme-preferences.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkWidget *combo;
|
||||
GtkWidget *apply_button;
|
||||
GtkWidget *status_label;
|
||||
GtkWindow *parent;
|
||||
gboolean *color_change_flag;
|
||||
} theme_preferences_ui;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkWidget *button;
|
||||
ThemeSemanticToken token;
|
||||
gboolean *color_change_flag;
|
||||
} theme_color_dialog_data;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct zoitechatprefs *setup_prefs;
|
||||
} theme_preferences_dark_mode_data;
|
||||
|
||||
#define LABEL_INDENT 12
|
||||
|
||||
static void
|
||||
theme_preferences_show_message (theme_preferences_ui *ui, GtkMessageType message_type, const char *primary)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
|
||||
dialog = gtk_message_dialog_new (ui->parent,
|
||||
GTK_DIALOG_MODAL,
|
||||
message_type,
|
||||
GTK_BUTTONS_OK,
|
||||
"%s",
|
||||
primary);
|
||||
gtk_dialog_run (GTK_DIALOG (dialog));
|
||||
gtk_widget_destroy (dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_color_button_apply (GtkWidget *button, const GdkRGBA *color)
|
||||
{
|
||||
GtkWidget *target = g_object_get_data (G_OBJECT (button), "zoitechat-color-box");
|
||||
GtkWidget *apply_widget = GTK_IS_WIDGET (target) ? target : button;
|
||||
|
||||
gtkutil_apply_palette (apply_widget, color, NULL, NULL);
|
||||
|
||||
if (apply_widget != button)
|
||||
gtkutil_apply_palette (button, color, NULL, NULL);
|
||||
|
||||
gtk_widget_queue_draw (button);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_color_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data)
|
||||
{
|
||||
theme_color_dialog_data *data = user_data;
|
||||
|
||||
if (response_id == GTK_RESPONSE_OK)
|
||||
{
|
||||
GdkRGBA rgba;
|
||||
gboolean changed = FALSE;
|
||||
|
||||
gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (dialog), &rgba);
|
||||
theme_manager_set_token_color (prefs.hex_gui_dark_mode,
|
||||
data->token,
|
||||
&rgba,
|
||||
&changed);
|
||||
if (data->color_change_flag)
|
||||
*data->color_change_flag = *data->color_change_flag || changed;
|
||||
theme_preferences_color_button_apply (data->button, &rgba);
|
||||
}
|
||||
|
||||
gtk_widget_destroy (GTK_WIDGET (dialog));
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_color_cb (GtkWidget *button, gpointer userdata)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
ThemeSemanticToken token;
|
||||
GdkRGBA rgba;
|
||||
theme_color_dialog_data *data;
|
||||
|
||||
token = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "zoitechat-theme-token"));
|
||||
|
||||
if (!theme_get_color (token, &rgba))
|
||||
return;
|
||||
dialog = gtk_color_chooser_dialog_new (_("Select color"), GTK_WINDOW (userdata));
|
||||
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (dialog), &rgba);
|
||||
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
|
||||
|
||||
data = g_new0 (theme_color_dialog_data, 1);
|
||||
data->button = button;
|
||||
data->token = token;
|
||||
data->color_change_flag = g_object_get_data (G_OBJECT (button), "zoitechat-theme-color-change");
|
||||
g_signal_connect (dialog, "response", G_CALLBACK (theme_preferences_color_response_cb), data);
|
||||
gtk_widget_show (dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_create_color_button (GtkWidget *table,
|
||||
ThemeSemanticToken token,
|
||||
int row,
|
||||
int col,
|
||||
GtkWindow *parent,
|
||||
gboolean *color_change_flag)
|
||||
{
|
||||
GtkWidget *but;
|
||||
GtkWidget *label;
|
||||
GtkWidget *box;
|
||||
char buf[64];
|
||||
GdkRGBA color;
|
||||
|
||||
if (token > THEME_TOKEN_MIRC_31)
|
||||
strcpy (buf, "<span size=\"x-small\">  </span>");
|
||||
else if (token < 10)
|
||||
sprintf (buf, "<span size=\"x-small\"> %d</span>", token);
|
||||
else
|
||||
sprintf (buf, "<span size=\"x-small\">%d</span>", token);
|
||||
|
||||
but = gtk_button_new ();
|
||||
label = gtk_label_new (" ");
|
||||
gtk_label_set_markup (GTK_LABEL (label), buf);
|
||||
box = gtk_event_box_new ();
|
||||
gtk_event_box_set_visible_window (GTK_EVENT_BOX (box), TRUE);
|
||||
gtk_container_add (GTK_CONTAINER (box), label);
|
||||
gtk_container_add (GTK_CONTAINER (but), box);
|
||||
gtk_widget_set_halign (box, GTK_ALIGN_CENTER);
|
||||
gtk_widget_set_valign (box, GTK_ALIGN_CENTER);
|
||||
gtk_widget_show (label);
|
||||
gtk_widget_show (box);
|
||||
g_object_set_data (G_OBJECT (but), "zoitechat-color", (gpointer)1);
|
||||
g_object_set_data (G_OBJECT (but), "zoitechat-color-box", box);
|
||||
g_object_set_data (G_OBJECT (but), "zoitechat-theme-token", GINT_TO_POINTER (token));
|
||||
g_object_set_data (G_OBJECT (but), "zoitechat-theme-color-change", color_change_flag);
|
||||
gtk_grid_attach (GTK_GRID (table), but, col, row, 1, 1);
|
||||
g_signal_connect (G_OBJECT (but), "clicked", G_CALLBACK (theme_preferences_color_cb), parent);
|
||||
if (theme_get_color (token, &color))
|
||||
theme_preferences_color_button_apply (but, &color);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_create_header (GtkWidget *table, int row, const char *labeltext)
|
||||
{
|
||||
GtkWidget *label;
|
||||
char buf[128];
|
||||
|
||||
if (row == 0)
|
||||
g_snprintf (buf, sizeof (buf), "<b>%s</b>", _(labeltext));
|
||||
else
|
||||
g_snprintf (buf, sizeof (buf), "\n<b>%s</b>", _(labeltext));
|
||||
|
||||
label = gtk_label_new (NULL);
|
||||
gtk_label_set_markup (GTK_LABEL (label), buf);
|
||||
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
||||
gtk_grid_attach (GTK_GRID (table), label, 0, row, 4, 1);
|
||||
gtk_widget_set_margin_bottom (label, 5);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_create_other_color_l (GtkWidget *tab,
|
||||
const char *text,
|
||||
ThemeSemanticToken token,
|
||||
int row,
|
||||
GtkWindow *parent,
|
||||
gboolean *color_change_flag)
|
||||
{
|
||||
GtkWidget *label;
|
||||
|
||||
label = gtk_label_new (text);
|
||||
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
||||
gtk_widget_set_margin_start (label, LABEL_INDENT);
|
||||
gtk_grid_attach (GTK_GRID (tab), label, 2, row, 1, 1);
|
||||
theme_preferences_create_color_button (tab, token, row, 3, parent, color_change_flag);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_create_other_color_r (GtkWidget *tab,
|
||||
const char *text,
|
||||
ThemeSemanticToken token,
|
||||
int row,
|
||||
GtkWindow *parent,
|
||||
gboolean *color_change_flag)
|
||||
{
|
||||
GtkWidget *label;
|
||||
|
||||
label = gtk_label_new (text);
|
||||
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
||||
gtk_widget_set_margin_start (label, LABEL_INDENT);
|
||||
gtk_grid_attach (GTK_GRID (tab), label, 5, row, 4, 1);
|
||||
theme_preferences_create_color_button (tab, token, row, 9, parent, color_change_flag);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_strip_toggle_cb (GtkToggleButton *toggle, gpointer user_data)
|
||||
{
|
||||
int *field = user_data;
|
||||
|
||||
*field = gtk_toggle_button_get_active (toggle);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_create_strip_toggle (GtkWidget *tab,
|
||||
int row,
|
||||
const char *text,
|
||||
int *field)
|
||||
{
|
||||
GtkWidget *toggle;
|
||||
|
||||
toggle = gtk_check_button_new_with_label (text);
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), *field);
|
||||
g_signal_connect (G_OBJECT (toggle), "toggled",
|
||||
G_CALLBACK (theme_preferences_strip_toggle_cb), field);
|
||||
gtk_grid_attach (GTK_GRID (tab), toggle, 2, row, 1, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_dark_mode_changed_cb (GtkComboBox *combo, gpointer user_data)
|
||||
{
|
||||
theme_preferences_dark_mode_data *data = user_data;
|
||||
|
||||
data->setup_prefs->hex_gui_dark_mode = gtk_combo_box_get_active (combo);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_create_dark_mode_menu (GtkWidget *tab,
|
||||
int row,
|
||||
struct zoitechatprefs *setup_prefs)
|
||||
{
|
||||
static const char *const dark_mode_modes[] =
|
||||
{
|
||||
N_("Auto (system)"),
|
||||
N_("Dark"),
|
||||
N_("Light"),
|
||||
NULL
|
||||
};
|
||||
GtkWidget *label;
|
||||
GtkWidget *combo;
|
||||
GtkWidget *box;
|
||||
theme_preferences_dark_mode_data *data;
|
||||
int i;
|
||||
|
||||
label = gtk_label_new (_("Dark mode:"));
|
||||
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
||||
gtk_widget_set_margin_start (label, LABEL_INDENT);
|
||||
gtk_widget_set_tooltip_text (label,
|
||||
_("Choose how ZoiteChat selects its color palette for the chat buffer, channel list, and user list.\n"
|
||||
"This includes message colors, selection colors, and interface highlights.\n"));
|
||||
gtk_grid_attach (GTK_GRID (tab), label, 2, row, 1, 1);
|
||||
|
||||
combo = gtk_combo_box_text_new ();
|
||||
for (i = 0; dark_mode_modes[i] != NULL; i++)
|
||||
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), _(dark_mode_modes[i]));
|
||||
gtk_combo_box_set_active (GTK_COMBO_BOX (combo), setup_prefs->hex_gui_dark_mode);
|
||||
gtk_widget_set_tooltip_text (combo,
|
||||
_("Choose how ZoiteChat selects its color palette for the chat buffer, channel list, and user list.\n"
|
||||
"This includes message colors, selection colors, and interface highlights.\n"));
|
||||
|
||||
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
gtk_box_pack_start (GTK_BOX (box), combo, FALSE, FALSE, 0);
|
||||
gtk_grid_attach (GTK_GRID (tab), box, 3, row, 1, 1);
|
||||
|
||||
data = g_new0 (theme_preferences_dark_mode_data, 1);
|
||||
data->setup_prefs = setup_prefs;
|
||||
g_signal_connect (G_OBJECT (combo), "changed",
|
||||
G_CALLBACK (theme_preferences_dark_mode_changed_cb), data);
|
||||
g_object_set_data_full (G_OBJECT (combo), "zoitechat-dark-mode-data", data, g_free);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
theme_preferences_create_color_page (GtkWindow *parent,
|
||||
struct zoitechatprefs *setup_prefs,
|
||||
gboolean *color_change_flag)
|
||||
{
|
||||
GtkWidget *tab;
|
||||
GtkWidget *box;
|
||||
GtkWidget *label;
|
||||
int i;
|
||||
|
||||
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (box), 6);
|
||||
|
||||
tab = gtk_grid_new ();
|
||||
gtk_container_set_border_width (GTK_CONTAINER (tab), 6);
|
||||
gtk_grid_set_row_spacing (GTK_GRID (tab), 2);
|
||||
gtk_grid_set_column_spacing (GTK_GRID (tab), 3);
|
||||
gtk_container_add (GTK_CONTAINER (box), tab);
|
||||
|
||||
theme_preferences_create_header (tab, 0, N_("Text Colors"));
|
||||
|
||||
label = gtk_label_new (_("mIRC colors:"));
|
||||
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
||||
gtk_widget_set_margin_start (label, LABEL_INDENT);
|
||||
gtk_grid_attach (GTK_GRID (tab), label, 2, 1, 1, 1);
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
theme_preferences_create_color_button (tab,
|
||||
THEME_TOKEN_MIRC_0 + i,
|
||||
1,
|
||||
i + 3,
|
||||
parent,
|
||||
color_change_flag);
|
||||
|
||||
label = gtk_label_new (_("Local colors:"));
|
||||
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
||||
gtk_widget_set_margin_start (label, LABEL_INDENT);
|
||||
gtk_grid_attach (GTK_GRID (tab), label, 2, 2, 1, 1);
|
||||
|
||||
for (i = 16; i < 32; i++)
|
||||
theme_preferences_create_color_button (tab,
|
||||
THEME_TOKEN_MIRC_0 + i,
|
||||
2,
|
||||
(i + 3) - 16,
|
||||
parent,
|
||||
color_change_flag);
|
||||
|
||||
theme_preferences_create_other_color_l (tab, _("Foreground:"), THEME_TOKEN_TEXT_FOREGROUND, 3,
|
||||
parent, color_change_flag);
|
||||
theme_preferences_create_other_color_r (tab, _("Background:"), THEME_TOKEN_TEXT_BACKGROUND, 3,
|
||||
parent, color_change_flag);
|
||||
|
||||
theme_preferences_create_header (tab, 5, N_("Selected Text"));
|
||||
theme_preferences_create_other_color_l (tab, _("Foreground:"), THEME_TOKEN_SELECTION_FOREGROUND, 6,
|
||||
parent, color_change_flag);
|
||||
theme_preferences_create_other_color_r (tab, _("Background:"), THEME_TOKEN_SELECTION_BACKGROUND, 6,
|
||||
parent, color_change_flag);
|
||||
|
||||
theme_preferences_create_header (tab, 8, N_("Interface Colors"));
|
||||
theme_preferences_create_other_color_l (tab, _("New data:"), THEME_TOKEN_TAB_NEW_DATA, 9,
|
||||
parent, color_change_flag);
|
||||
theme_preferences_create_other_color_r (tab, _("Marker line:"), THEME_TOKEN_MARKER, 9,
|
||||
parent, color_change_flag);
|
||||
theme_preferences_create_other_color_l (tab, _("New message:"), THEME_TOKEN_TAB_NEW_MESSAGE, 10,
|
||||
parent, color_change_flag);
|
||||
theme_preferences_create_other_color_r (tab, _("Away user:"), THEME_TOKEN_TAB_AWAY, 10,
|
||||
parent, color_change_flag);
|
||||
theme_preferences_create_other_color_l (tab, _("Highlight:"), THEME_TOKEN_TAB_HIGHLIGHT, 11,
|
||||
parent, color_change_flag);
|
||||
theme_preferences_create_other_color_r (tab, _("Spell checker:"), THEME_TOKEN_SPELL, 11,
|
||||
parent, color_change_flag);
|
||||
theme_preferences_create_dark_mode_menu (tab, 13, setup_prefs);
|
||||
|
||||
theme_preferences_create_header (tab, 15, N_("Color Stripping"));
|
||||
theme_preferences_create_strip_toggle (tab, 16, _("Messages"), &setup_prefs->hex_text_stripcolor_msg);
|
||||
theme_preferences_create_strip_toggle (tab, 17, _("Scrollback"), &setup_prefs->hex_text_stripcolor_replay);
|
||||
theme_preferences_create_strip_toggle (tab, 18, _("Topic"), &setup_prefs->hex_text_stripcolor_topic);
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_populate (theme_preferences_ui *ui)
|
||||
{
|
||||
GStrv themes;
|
||||
int count = 0;
|
||||
guint i;
|
||||
|
||||
gtk_combo_box_text_remove_all (GTK_COMBO_BOX_TEXT (ui->combo));
|
||||
|
||||
themes = zoitechat_theme_service_discover_themes ();
|
||||
for (i = 0; themes[i] != NULL; i++)
|
||||
{
|
||||
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (ui->combo), themes[i]);
|
||||
count++;
|
||||
}
|
||||
g_strfreev (themes);
|
||||
|
||||
gtk_widget_set_sensitive (ui->apply_button, count > 0);
|
||||
gtk_label_set_text (GTK_LABEL (ui->status_label),
|
||||
count > 0 ? _("Select a theme to apply.") : _("No themes found."));
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_refresh_cb (GtkWidget *button, gpointer user_data)
|
||||
{
|
||||
theme_preferences_ui *ui = user_data;
|
||||
|
||||
(void)button;
|
||||
theme_preferences_populate (ui);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_open_folder_cb (GtkWidget *button, gpointer user_data)
|
||||
{
|
||||
theme_preferences_ui *ui = user_data;
|
||||
GAppInfo *handler;
|
||||
char *themes_dir;
|
||||
|
||||
(void)button;
|
||||
themes_dir = zoitechat_theme_service_get_themes_dir ();
|
||||
g_mkdir_with_parents (themes_dir, 0700);
|
||||
|
||||
handler = g_app_info_get_default_for_uri_scheme ("file");
|
||||
if (!handler)
|
||||
{
|
||||
theme_preferences_show_message (ui,
|
||||
GTK_MESSAGE_ERROR,
|
||||
_("No application is configured to open folders."));
|
||||
g_free (themes_dir);
|
||||
return;
|
||||
}
|
||||
|
||||
g_object_unref (handler);
|
||||
fe_open_url (themes_dir);
|
||||
g_free (themes_dir);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_selection_changed (GtkComboBox *combo, gpointer user_data)
|
||||
{
|
||||
theme_preferences_ui *ui = user_data;
|
||||
gboolean has_selection = gtk_combo_box_get_active (combo) >= 0;
|
||||
|
||||
gtk_widget_set_sensitive (ui->apply_button, has_selection);
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_apply_cb (GtkWidget *button, gpointer user_data)
|
||||
{
|
||||
theme_preferences_ui *ui = user_data;
|
||||
GtkWidget *dialog;
|
||||
gint response;
|
||||
char *theme;
|
||||
GError *error = NULL;
|
||||
|
||||
(void)button;
|
||||
theme = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (ui->combo));
|
||||
if (!theme)
|
||||
return;
|
||||
|
||||
dialog = gtk_message_dialog_new (ui->parent, GTK_DIALOG_MODAL,
|
||||
GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL,
|
||||
"%s", _("Applying a theme will overwrite your current colors and event settings.\nContinue?"));
|
||||
response = gtk_dialog_run (GTK_DIALOG (dialog));
|
||||
gtk_widget_destroy (dialog);
|
||||
|
||||
if (response != GTK_RESPONSE_OK)
|
||||
{
|
||||
g_free (theme);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!zoitechat_apply_theme (theme, &error))
|
||||
{
|
||||
theme_preferences_show_message (ui, GTK_MESSAGE_ERROR,
|
||||
error ? error->message : _("Failed to apply theme."));
|
||||
g_clear_error (&error);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (ui->color_change_flag)
|
||||
*ui->color_change_flag = TRUE;
|
||||
|
||||
theme_preferences_show_message (ui,
|
||||
GTK_MESSAGE_INFO,
|
||||
_("Theme applied. Some changes may require a restart to take full effect."));
|
||||
|
||||
cleanup:
|
||||
g_free (theme);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
theme_preferences_create_page (GtkWindow *parent, gboolean *color_change_flag)
|
||||
{
|
||||
theme_preferences_ui *ui;
|
||||
GtkWidget *box;
|
||||
GtkWidget *label;
|
||||
GtkWidget *hbox;
|
||||
GtkWidget *button_box;
|
||||
char *themes_dir;
|
||||
char *markup;
|
||||
|
||||
ui = g_new0 (theme_preferences_ui, 1);
|
||||
ui->parent = parent;
|
||||
ui->color_change_flag = color_change_flag;
|
||||
|
||||
box = gtkutil_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 6);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (box), 6);
|
||||
|
||||
themes_dir = zoitechat_theme_service_get_themes_dir ();
|
||||
markup = g_markup_printf_escaped (_("Theme files are loaded from <tt>%s</tt>."), themes_dir);
|
||||
label = gtk_label_new (NULL);
|
||||
gtk_label_set_markup (GTK_LABEL (label), markup);
|
||||
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
|
||||
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
||||
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
|
||||
g_free (markup);
|
||||
g_free (themes_dir);
|
||||
|
||||
hbox = gtkutil_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 6);
|
||||
gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, FALSE, 0);
|
||||
|
||||
ui->combo = gtk_combo_box_text_new ();
|
||||
gtk_box_pack_start (GTK_BOX (hbox), ui->combo, TRUE, TRUE, 0);
|
||||
g_signal_connect (G_OBJECT (ui->combo), "changed",
|
||||
G_CALLBACK (theme_preferences_selection_changed), ui);
|
||||
|
||||
button_box = gtkutil_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 6);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), button_box, FALSE, FALSE, 0);
|
||||
|
||||
ui->apply_button = gtk_button_new_with_mnemonic (_("_Apply Theme"));
|
||||
gtk_box_pack_start (GTK_BOX (button_box), ui->apply_button, FALSE, FALSE, 0);
|
||||
g_signal_connect (G_OBJECT (ui->apply_button), "clicked",
|
||||
G_CALLBACK (theme_preferences_apply_cb), ui);
|
||||
|
||||
label = gtk_button_new_with_mnemonic (_("_Refresh"));
|
||||
gtk_box_pack_start (GTK_BOX (button_box), label, FALSE, FALSE, 0);
|
||||
g_signal_connect (G_OBJECT (label), "clicked",
|
||||
G_CALLBACK (theme_preferences_refresh_cb), ui);
|
||||
|
||||
label = gtk_button_new_with_mnemonic (_("_Open Folder"));
|
||||
gtk_box_pack_start (GTK_BOX (button_box), label, FALSE, FALSE, 0);
|
||||
g_signal_connect (G_OBJECT (label), "clicked",
|
||||
G_CALLBACK (theme_preferences_open_folder_cb), ui);
|
||||
|
||||
ui->status_label = gtk_label_new (NULL);
|
||||
gtk_widget_set_halign (ui->status_label, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (ui->status_label, GTK_ALIGN_CENTER);
|
||||
gtk_box_pack_start (GTK_BOX (box), ui->status_label, FALSE, FALSE, 0);
|
||||
|
||||
theme_preferences_populate (ui);
|
||||
|
||||
g_object_set_data_full (G_OBJECT (box), "theme-preferences-ui", ui, g_free);
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
static void
|
||||
theme_preferences_apply_entry_style (GtkWidget *entry, InputStyle *input_style)
|
||||
{
|
||||
ThemeWidgetStyleValues style_values;
|
||||
|
||||
theme_get_widget_style_values (&style_values);
|
||||
gtkutil_apply_palette (entry, &style_values.background, &style_values.foreground,
|
||||
input_style ? input_style->font_desc : NULL);
|
||||
}
|
||||
|
||||
void
|
||||
theme_preferences_apply_to_session (session_gui *gui, InputStyle *input_style)
|
||||
{
|
||||
if (prefs.hex_gui_input_style)
|
||||
{
|
||||
theme_css_reload_input_style (TRUE, input_style ? input_style->font_desc : NULL);
|
||||
theme_preferences_apply_entry_style (gui->input_box, input_style);
|
||||
theme_preferences_apply_entry_style (gui->limit_entry, input_style);
|
||||
theme_preferences_apply_entry_style (gui->key_entry, input_style);
|
||||
theme_preferences_apply_entry_style (gui->topic_entry, input_style);
|
||||
}
|
||||
|
||||
if (gui->user_tree)
|
||||
{
|
||||
theme_manager_apply_userlist_style (gui->user_tree,
|
||||
theme_manager_get_userlist_palette_behavior (input_style ? input_style->font_desc : NULL));
|
||||
}
|
||||
}
|
||||
16
src/fe-gtk/theme/theme-preferences.h
Normal file
16
src/fe-gtk/theme/theme-preferences.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef ZOITECHAT_THEME_PREFERENCES_H
|
||||
#define ZOITECHAT_THEME_PREFERENCES_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "theme-access.h"
|
||||
#include "../fe-gtk.h"
|
||||
#include "../../common/zoitechat.h"
|
||||
|
||||
GtkWidget *theme_preferences_create_page (GtkWindow *parent, gboolean *color_change_flag);
|
||||
GtkWidget *theme_preferences_create_color_page (GtkWindow *parent,
|
||||
struct zoitechatprefs *setup_prefs,
|
||||
gboolean *color_change_flag);
|
||||
void theme_preferences_apply_to_session (session_gui *gui, InputStyle *input_style);
|
||||
|
||||
#endif
|
||||
466
src/fe-gtk/theme/theme-runtime.c
Normal file
466
src/fe-gtk/theme/theme-runtime.c
Normal file
@@ -0,0 +1,466 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "theme-runtime.h"
|
||||
#include "theme-policy.h"
|
||||
|
||||
#include "../../common/zoitechat.h"
|
||||
#include "../../common/zoitechatc.h"
|
||||
#include "../../common/util.h"
|
||||
#include "../../common/cfgfiles.h"
|
||||
#include "../../common/typedef.h"
|
||||
|
||||
#define PALETTE_COLOR_INIT(r, g, b) { (r) / 65535.0, (g) / 65535.0, (b) / 65535.0, 1.0 }
|
||||
|
||||
static const GdkRGBA legacy_light_defaults[THEME_LEGACY_MAX + 1] = {
|
||||
PALETTE_COLOR_INIT (0xd3d3, 0xd7d7, 0xcfcf),
|
||||
PALETTE_COLOR_INIT (0x2e2e, 0x3434, 0x3636),
|
||||
PALETTE_COLOR_INIT (0x3434, 0x6565, 0xa4a4),
|
||||
PALETTE_COLOR_INIT (0x4e4e, 0x9a9a, 0x0606),
|
||||
PALETTE_COLOR_INIT (0xcccc, 0x0000, 0x0000),
|
||||
PALETTE_COLOR_INIT (0x8f8f, 0x3939, 0x0202),
|
||||
PALETTE_COLOR_INIT (0x5c5c, 0x3535, 0x6666),
|
||||
PALETTE_COLOR_INIT (0xcece, 0x5c5c, 0x0000),
|
||||
PALETTE_COLOR_INIT (0xc4c4, 0xa0a0, 0x0000),
|
||||
PALETTE_COLOR_INIT (0x7373, 0xd2d2, 0x1616),
|
||||
PALETTE_COLOR_INIT (0x1111, 0xa8a8, 0x7979),
|
||||
PALETTE_COLOR_INIT (0x5858, 0xa1a1, 0x9d9d),
|
||||
PALETTE_COLOR_INIT (0x5757, 0x7979, 0x9e9e),
|
||||
PALETTE_COLOR_INIT (0xa0d0, 0x42d4, 0x6562),
|
||||
PALETTE_COLOR_INIT (0x5555, 0x5757, 0x5353),
|
||||
PALETTE_COLOR_INIT (0x8888, 0x8a8a, 0x8585),
|
||||
PALETTE_COLOR_INIT (0xd3d3, 0xd7d7, 0xcfcf),
|
||||
PALETTE_COLOR_INIT (0x2e2e, 0x3434, 0x3636),
|
||||
PALETTE_COLOR_INIT (0x3434, 0x6565, 0xa4a4),
|
||||
PALETTE_COLOR_INIT (0x4e4e, 0x9a9a, 0x0606),
|
||||
PALETTE_COLOR_INIT (0xcccc, 0x0000, 0x0000),
|
||||
PALETTE_COLOR_INIT (0x8f8f, 0x3939, 0x0202),
|
||||
PALETTE_COLOR_INIT (0x5c5c, 0x3535, 0x6666),
|
||||
PALETTE_COLOR_INIT (0xcece, 0x5c5c, 0x0000),
|
||||
PALETTE_COLOR_INIT (0xc4c4, 0xa0a0, 0x0000),
|
||||
PALETTE_COLOR_INIT (0x7373, 0xd2d2, 0x1616),
|
||||
PALETTE_COLOR_INIT (0x1111, 0xa8a8, 0x7979),
|
||||
PALETTE_COLOR_INIT (0x5858, 0xa1a1, 0x9d9d),
|
||||
PALETTE_COLOR_INIT (0x5757, 0x7979, 0x9e9e),
|
||||
PALETTE_COLOR_INIT (0xa0d0, 0x42d4, 0x6562),
|
||||
PALETTE_COLOR_INIT (0x5555, 0x5757, 0x5353),
|
||||
PALETTE_COLOR_INIT (0x8888, 0x8a8a, 0x8585),
|
||||
PALETTE_COLOR_INIT (0xd3d3, 0xd7d7, 0xcfcf),
|
||||
PALETTE_COLOR_INIT (0x2020, 0x4a4a, 0x8787),
|
||||
PALETTE_COLOR_INIT (0x2512, 0x29e8, 0x2b85),
|
||||
PALETTE_COLOR_INIT (0xfae0, 0xfae0, 0xf8c4),
|
||||
PALETTE_COLOR_INIT (0x8f8f, 0x3939, 0x0202),
|
||||
PALETTE_COLOR_INIT (0x3434, 0x6565, 0xa4a4),
|
||||
PALETTE_COLOR_INIT (0x4e4e, 0x9a9a, 0x0606),
|
||||
PALETTE_COLOR_INIT (0xcece, 0x5c5c, 0x0000),
|
||||
PALETTE_COLOR_INIT (0x8888, 0x8a8a, 0x8585),
|
||||
PALETTE_COLOR_INIT (0xa4a4, 0x0000, 0x0000),
|
||||
};
|
||||
|
||||
static const GdkRGBA legacy_dark_defaults[THEME_LEGACY_MAX + 1] = {
|
||||
PALETTE_COLOR_INIT (0xe5e5, 0xe5e5, 0xe5e5), PALETTE_COLOR_INIT (0x3c3c, 0x3c3c, 0x3c3c),
|
||||
PALETTE_COLOR_INIT (0x5656, 0x9c9c, 0xd6d6), PALETTE_COLOR_INIT (0x0d0d, 0xbcbc, 0x7979),
|
||||
PALETTE_COLOR_INIT (0xf4f4, 0x4747, 0x4747), PALETTE_COLOR_INIT (0xcece, 0x9191, 0x7878),
|
||||
PALETTE_COLOR_INIT (0xc5c5, 0x8686, 0xc0c0), PALETTE_COLOR_INIT (0xd7d7, 0xbaba, 0x7d7d),
|
||||
PALETTE_COLOR_INIT (0xdcdc, 0xdcdc, 0xaaaa), PALETTE_COLOR_INIT (0xb5b5, 0xcece, 0xa8a8),
|
||||
PALETTE_COLOR_INIT (0x4e4e, 0xc9c9, 0xb0b0), PALETTE_COLOR_INIT (0x9c9c, 0xdcdc, 0xfefe),
|
||||
PALETTE_COLOR_INIT (0x3737, 0x9494, 0xffff), PALETTE_COLOR_INIT (0xd6d6, 0x7070, 0xd6d6),
|
||||
PALETTE_COLOR_INIT (0x8080, 0x8080, 0x8080), PALETTE_COLOR_INIT (0xc0c0, 0xc0c0, 0xc0c0),
|
||||
PALETTE_COLOR_INIT (0xe5e5, 0xe5e5, 0xe5e5), PALETTE_COLOR_INIT (0x3c3c, 0x3c3c, 0x3c3c),
|
||||
PALETTE_COLOR_INIT (0x5656, 0x9c9c, 0xd6d6), PALETTE_COLOR_INIT (0x0d0d, 0xbcbc, 0x7979),
|
||||
PALETTE_COLOR_INIT (0xf4f4, 0x4747, 0x4747), PALETTE_COLOR_INIT (0xcece, 0x9191, 0x7878),
|
||||
PALETTE_COLOR_INIT (0xc5c5, 0x8686, 0xc0c0), PALETTE_COLOR_INIT (0xd7d7, 0xbaba, 0x7d7d),
|
||||
PALETTE_COLOR_INIT (0xdcdc, 0xdcdc, 0xaaaa), PALETTE_COLOR_INIT (0xb5b5, 0xcece, 0xa8a8),
|
||||
PALETTE_COLOR_INIT (0x4e4e, 0xc9c9, 0xb0b0), PALETTE_COLOR_INIT (0x9c9c, 0xdcdc, 0xfefe),
|
||||
PALETTE_COLOR_INIT (0x3737, 0x9494, 0xffff), PALETTE_COLOR_INIT (0xd6d6, 0x7070, 0xd6d6),
|
||||
PALETTE_COLOR_INIT (0x8080, 0x8080, 0x8080), PALETTE_COLOR_INIT (0xc0c0, 0xc0c0, 0xc0c0),
|
||||
PALETTE_COLOR_INIT (0xffff, 0xffff, 0xffff), PALETTE_COLOR_INIT (0x2626, 0x4f4f, 0x7878),
|
||||
PALETTE_COLOR_INIT (0xd4d4, 0xd4d4, 0xd4d4), PALETTE_COLOR_INIT (0x1e1e, 0x1e1e, 0x1e1e),
|
||||
PALETTE_COLOR_INIT (0x4040, 0x4040, 0x4040), PALETTE_COLOR_INIT (0x3737, 0x9494, 0xffff),
|
||||
PALETTE_COLOR_INIT (0xd7d7, 0xbaba, 0x7d7d), PALETTE_COLOR_INIT (0xf4f4, 0x4747, 0x4747),
|
||||
PALETTE_COLOR_INIT (0x8080, 0x8080, 0x8080), PALETTE_COLOR_INIT (0xf4f4, 0x4747, 0x4747),
|
||||
};
|
||||
|
||||
static ThemePalette light_palette;
|
||||
static ThemePalette dark_palette;
|
||||
static ThemePalette active_palette;
|
||||
static gboolean user_colors_valid = FALSE;
|
||||
static gboolean dark_user_colors_valid = FALSE;
|
||||
static gboolean dark_mode_active = FALSE;
|
||||
|
||||
#define THEME_PALETTE_MIGRATION_MARKER_KEY "theme.palette.semantic_migrated"
|
||||
#define THEME_PALETTE_MIGRATION_MARKER_VALUE 1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *mode_name;
|
||||
const char *legacy_prefix;
|
||||
ThemePalette *palette;
|
||||
gboolean *mode_valid;
|
||||
} ThemePalettePersistenceMode;
|
||||
|
||||
static void
|
||||
palette_color_set_rgb16 (GdkRGBA *color, guint16 red, guint16 green, guint16 blue)
|
||||
{
|
||||
char color_string[16];
|
||||
GdkRGBA parsed = { 0 };
|
||||
gboolean parsed_ok;
|
||||
|
||||
g_snprintf (color_string, sizeof (color_string), "#%04x%04x%04x", red, green, blue);
|
||||
parsed_ok = gdk_rgba_parse (&parsed, color_string);
|
||||
if (!parsed_ok)
|
||||
{
|
||||
parsed.red = red / 65535.0;
|
||||
parsed.green = green / 65535.0;
|
||||
parsed.blue = blue / 65535.0;
|
||||
parsed.alpha = 1.0;
|
||||
}
|
||||
*color = parsed;
|
||||
}
|
||||
|
||||
static void
|
||||
palette_init_defaults (void)
|
||||
{
|
||||
theme_palette_from_legacy_colors (&light_palette, legacy_light_defaults, G_N_ELEMENTS (legacy_light_defaults));
|
||||
theme_palette_from_legacy_colors (&dark_palette, legacy_dark_defaults, G_N_ELEMENTS (legacy_dark_defaults));
|
||||
active_palette = light_palette;
|
||||
dark_mode_active = FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
palette_legacy_index_to_cfg_key (int legacy_idx)
|
||||
{
|
||||
g_return_val_if_fail (legacy_idx >= 0 && legacy_idx <= THEME_LEGACY_MAX, -1);
|
||||
if (legacy_idx < 32)
|
||||
return legacy_idx;
|
||||
return (legacy_idx - 32) + 256;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
palette_read_token_color (char *cfg, const char *mode_name, const ThemePaletteTokenDef *def, GdkRGBA *out_color)
|
||||
{
|
||||
char prefname[256];
|
||||
guint16 red;
|
||||
guint16 green;
|
||||
guint16 blue;
|
||||
|
||||
g_return_val_if_fail (cfg != NULL, FALSE);
|
||||
g_return_val_if_fail (mode_name != NULL, FALSE);
|
||||
g_return_val_if_fail (def != NULL, FALSE);
|
||||
g_return_val_if_fail (out_color != NULL, FALSE);
|
||||
|
||||
g_snprintf (prefname, sizeof prefname, "theme.mode.%s.token.%s", mode_name, def->name);
|
||||
if (!cfg_get_color (cfg, prefname, &red, &green, &blue))
|
||||
return FALSE;
|
||||
|
||||
palette_color_set_rgb16 (out_color, red, green, blue);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
palette_read_legacy_color (char *cfg, const char *legacy_prefix, int legacy_index, GdkRGBA *out_color)
|
||||
{
|
||||
char prefname[256];
|
||||
guint16 red;
|
||||
guint16 green;
|
||||
guint16 blue;
|
||||
int legacy_key;
|
||||
|
||||
g_return_val_if_fail (cfg != NULL, FALSE);
|
||||
g_return_val_if_fail (legacy_prefix != NULL, FALSE);
|
||||
g_return_val_if_fail (out_color != NULL, FALSE);
|
||||
|
||||
legacy_key = palette_legacy_index_to_cfg_key (legacy_index);
|
||||
g_return_val_if_fail (legacy_key >= 0, FALSE);
|
||||
|
||||
g_snprintf (prefname, sizeof prefname, "%s_%d", legacy_prefix, legacy_key);
|
||||
if (!cfg_get_color (cfg, prefname, &red, &green, &blue))
|
||||
return FALSE;
|
||||
|
||||
palette_color_set_rgb16 (out_color, red, green, blue);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
theme_runtime_load_migrated_legacy_color (char *cfg,
|
||||
const ThemePalettePersistenceMode *mode,
|
||||
const ThemePaletteTokenDef *def,
|
||||
GdkRGBA *out_color)
|
||||
{
|
||||
g_return_val_if_fail (cfg != NULL, FALSE);
|
||||
g_return_val_if_fail (mode != NULL, FALSE);
|
||||
g_return_val_if_fail (def != NULL, FALSE);
|
||||
g_return_val_if_fail (out_color != NULL, FALSE);
|
||||
|
||||
return palette_read_legacy_color (cfg, mode->legacy_prefix, def->legacy_index, out_color);
|
||||
}
|
||||
|
||||
static void
|
||||
palette_write_token_color (int fh, const char *mode_name, const ThemePaletteTokenDef *def, const GdkRGBA *color)
|
||||
{
|
||||
char prefname[256];
|
||||
guint16 red;
|
||||
guint16 green;
|
||||
guint16 blue;
|
||||
|
||||
g_return_if_fail (mode_name != NULL);
|
||||
g_return_if_fail (def != NULL);
|
||||
g_return_if_fail (color != NULL);
|
||||
|
||||
g_snprintf (prefname, sizeof prefname, "theme.mode.%s.token.%s", mode_name, def->name);
|
||||
theme_palette_color_get_rgb16 (color, &red, &green, &blue);
|
||||
cfg_put_color (fh, red, green, blue, prefname);
|
||||
}
|
||||
|
||||
|
||||
|
||||
gboolean
|
||||
theme_runtime_get_color (ThemeSemanticToken token, GdkRGBA *out_rgba)
|
||||
{
|
||||
g_return_val_if_fail (out_rgba != NULL, FALSE);
|
||||
return theme_palette_get_color (&active_palette, token, out_rgba);
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_get_widget_style_values (ThemeWidgetStyleValues *out_values)
|
||||
{
|
||||
g_return_if_fail (out_values != NULL);
|
||||
theme_palette_to_widget_style_values (&active_palette, out_values);
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_get_xtext_colors (XTextColor *palette, size_t palette_len)
|
||||
{
|
||||
g_return_if_fail (palette != NULL);
|
||||
theme_palette_to_xtext_colors (&active_palette, palette, palette_len);
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_user_set_color (ThemeSemanticToken token, const GdkRGBA *col)
|
||||
{
|
||||
if (!col)
|
||||
return;
|
||||
if (token < 0 || token >= THEME_TOKEN_COUNT)
|
||||
return;
|
||||
if (!user_colors_valid)
|
||||
light_palette = active_palette;
|
||||
|
||||
g_assert (theme_palette_set_color (&light_palette, token, col));
|
||||
user_colors_valid = TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_dark_set_color (ThemeSemanticToken token, const GdkRGBA *col)
|
||||
{
|
||||
if (!col)
|
||||
return;
|
||||
if (token < 0 || token >= THEME_TOKEN_COUNT)
|
||||
return;
|
||||
if (!dark_user_colors_valid)
|
||||
dark_palette = active_palette;
|
||||
|
||||
g_assert (theme_palette_set_color (&dark_palette, token, col));
|
||||
dark_user_colors_valid = TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_load (void)
|
||||
{
|
||||
size_t i;
|
||||
int fh;
|
||||
struct stat st;
|
||||
char *cfg;
|
||||
ThemePalettePersistenceMode modes[] = {
|
||||
{ "light", "color", &light_palette, &user_colors_valid },
|
||||
{ "dark", "dark_color", &dark_palette, &dark_user_colors_valid },
|
||||
};
|
||||
const size_t mode_count = G_N_ELEMENTS (modes);
|
||||
|
||||
palette_init_defaults ();
|
||||
|
||||
fh = zoitechat_open_file ("colors.conf", O_RDONLY, 0, 0);
|
||||
if (fh != -1)
|
||||
{
|
||||
fstat (fh, &st);
|
||||
cfg = g_malloc0 (st.st_size + 1);
|
||||
read (fh, cfg, st.st_size);
|
||||
for (i = 0; i < mode_count; i++)
|
||||
{
|
||||
size_t j;
|
||||
gboolean mode_found = FALSE;
|
||||
|
||||
for (j = 0; j < theme_palette_token_def_count (); j++)
|
||||
{
|
||||
const ThemePaletteTokenDef *def = theme_palette_token_def_at (j);
|
||||
GdkRGBA color;
|
||||
gboolean found;
|
||||
|
||||
g_assert (def != NULL);
|
||||
g_assert (theme_palette_get_color (modes[i].palette, def->token, &color));
|
||||
found = palette_read_token_color (cfg, modes[i].mode_name, def, &color);
|
||||
if (!found)
|
||||
found = theme_runtime_load_migrated_legacy_color (cfg, &modes[i], def, &color);
|
||||
if (found)
|
||||
{
|
||||
g_assert (theme_palette_set_color (modes[i].palette, def->token, &color));
|
||||
mode_found = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
*modes[i].mode_valid = mode_found;
|
||||
}
|
||||
|
||||
g_free (cfg);
|
||||
close (fh);
|
||||
}
|
||||
|
||||
active_palette = light_palette;
|
||||
dark_mode_active = FALSE;
|
||||
user_colors_valid = TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
theme_runtime_save (void)
|
||||
{
|
||||
size_t i;
|
||||
size_t j;
|
||||
int fh;
|
||||
ThemePalettePersistenceMode modes[] = {
|
||||
{ "light", "color", &light_palette, &user_colors_valid },
|
||||
{ "dark", "dark_color", &dark_palette, &dark_user_colors_valid },
|
||||
};
|
||||
const size_t mode_count = G_N_ELEMENTS (modes);
|
||||
|
||||
if (dark_mode_active && !user_colors_valid)
|
||||
light_palette = active_palette;
|
||||
|
||||
if (!dark_mode_active)
|
||||
light_palette = active_palette;
|
||||
|
||||
if (dark_mode_active)
|
||||
{
|
||||
if (!dark_user_colors_valid)
|
||||
dark_palette = active_palette;
|
||||
dark_user_colors_valid = TRUE;
|
||||
}
|
||||
|
||||
user_colors_valid = TRUE;
|
||||
modes[0].palette = &light_palette;
|
||||
modes[0].mode_valid = &user_colors_valid;
|
||||
|
||||
if (dark_user_colors_valid)
|
||||
modes[1].palette = &dark_palette;
|
||||
else if (dark_mode_active)
|
||||
modes[1].palette = &active_palette;
|
||||
else
|
||||
modes[1].palette = &dark_palette;
|
||||
|
||||
fh = zoitechat_open_file ("colors.conf", O_TRUNC | O_WRONLY | O_CREAT, 0600, XOF_DOMODE);
|
||||
if (fh == -1)
|
||||
return;
|
||||
|
||||
cfg_put_int (fh, THEME_PALETTE_MIGRATION_MARKER_VALUE, (char *) THEME_PALETTE_MIGRATION_MARKER_KEY);
|
||||
|
||||
for (i = 0; i < mode_count; i++)
|
||||
{
|
||||
if (!*modes[i].mode_valid)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < theme_palette_token_def_count (); j++)
|
||||
{
|
||||
const ThemePaletteTokenDef *def = theme_palette_token_def_at (j);
|
||||
GdkRGBA color;
|
||||
|
||||
g_assert (def != NULL);
|
||||
g_assert (theme_palette_get_color (modes[i].palette, def->token, &color));
|
||||
palette_write_token_color (fh, modes[i].mode_name, def, &color);
|
||||
}
|
||||
}
|
||||
|
||||
close (fh);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
palette_color_eq (const GdkRGBA *a, const GdkRGBA *b)
|
||||
{
|
||||
guint16 red_a;
|
||||
guint16 green_a;
|
||||
guint16 blue_a;
|
||||
guint16 red_b;
|
||||
guint16 green_b;
|
||||
guint16 blue_b;
|
||||
|
||||
theme_palette_color_get_rgb16 (a, &red_a, &green_a, &blue_a);
|
||||
theme_palette_color_get_rgb16 (b, &red_b, &green_b, &blue_b);
|
||||
|
||||
return red_a == red_b && green_a == green_b && blue_a == blue_b;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_runtime_apply_dark_mode (gboolean enable)
|
||||
{
|
||||
ThemePalette previous_palette;
|
||||
size_t i;
|
||||
gboolean changed = FALSE;
|
||||
|
||||
previous_palette = active_palette;
|
||||
|
||||
if (!user_colors_valid)
|
||||
{
|
||||
light_palette = active_palette;
|
||||
user_colors_valid = TRUE;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
active_palette = dark_palette;
|
||||
else
|
||||
active_palette = light_palette;
|
||||
|
||||
dark_mode_active = enable ? TRUE : FALSE;
|
||||
|
||||
for (i = 0; i < theme_palette_token_def_count (); i++)
|
||||
{
|
||||
const ThemePaletteTokenDef *def = theme_palette_token_def_at (i);
|
||||
GdkRGBA old_color;
|
||||
GdkRGBA new_color;
|
||||
|
||||
g_assert (def != NULL);
|
||||
g_assert (theme_palette_get_color (&previous_palette, def->token, &old_color));
|
||||
g_assert (theme_palette_get_color (&active_palette, def->token, &new_color));
|
||||
if (!palette_color_eq (&old_color, &new_color))
|
||||
{
|
||||
changed = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (palette_changed)
|
||||
*palette_changed = changed;
|
||||
|
||||
return dark;
|
||||
}
|
||||
|
||||
gboolean
|
||||
theme_runtime_is_dark_active (void)
|
||||
{
|
||||
return dark_mode_active;
|
||||
}
|
||||
21
src/fe-gtk/theme/theme-runtime.h
Normal file
21
src/fe-gtk/theme/theme-runtime.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef ZOITECHAT_THEME_RUNTIME_H
|
||||
#define ZOITECHAT_THEME_RUNTIME_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "theme-palette.h"
|
||||
|
||||
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);
|
||||
gboolean theme_runtime_get_color (ThemeSemanticToken token, GdkRGBA *out_rgba);
|
||||
void theme_runtime_get_widget_style_values (ThemeWidgetStyleValues *out_values);
|
||||
void theme_runtime_get_xtext_colors (XTextColor *palette, size_t palette_len);
|
||||
gboolean theme_runtime_is_dark_active (void);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user