changeset 614:281608524dd1

misc: implement dark/light theming
author David Demelier <markand@malikania.fr>
date Sun, 20 Aug 2023 14:45:10 +0200
parents f76cada0bbb2
children ade3030f29f0
files examples/example-label/example-label.c libmlk-core/mlk/core/event.c libmlk-core/mlk/core/event.h libmlk-core/mlk/core/window.c libmlk-core/mlk/core/window.h libmlk-example/mlk/example/example.c libmlk-ui/mlk/ui/ui.c libmlk-ui/mlk/ui/ui.h
diffstat 8 files changed, 226 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/examples/example-label/example-label.c	Sun Aug 20 11:14:58 2023 +0200
+++ b/examples/example-label/example-label.c	Sun Aug 20 14:45:10 2023 +0200
@@ -17,6 +17,7 @@
  */
 
 #include <stddef.h>
+#include <stdio.h>
 
 #include <mlk/core/core.h>
 #include <mlk/core/err.h>
@@ -44,6 +45,14 @@
 	.delay  = 22
 };
 
+static const char * const theme_names[] = {
+	[MLK_WINDOW_THEME_AUTO] = "auto",
+	[MLK_WINDOW_THEME_DARK] = "dark",
+	[MLK_WINDOW_THEME_LIGHT] = "light"
+};
+
+static char theme_help_text[128];
+
 static struct {
 	enum mlk_align align;
 	struct mlk_label label;
@@ -51,8 +60,14 @@
 	{
 		.align = MLK_ALIGN_CENTER,
 		.label = {
+			.text = theme_help_text
+		}
+	},
+	{
+		.align = MLK_ALIGN_CENTER,
+		.label = {
 			.text = "The world is Malikania.",
-			.style = &style_glow,
+			.style = &style_glow
 		}
 	},
 	{
@@ -105,10 +120,6 @@
 	}
 };
 
-static struct mlk_label mouse_label = {
-	.text = "This one follows your mouse and is not aligned."
-};
-
 static void
 style_glow_update(struct mlk_label_style *self, struct mlk_label *label, unsigned int ticks)
 {
@@ -119,6 +130,20 @@
 }
 
 static void
+set_help_text(void)
+{
+	struct mlk_label *l = &table[0].label;
+	unsigned int w, h;
+
+	snprintf(theme_help_text, sizeof (theme_help_text), "Press space to change theme (%s)",
+	    theme_names[mlk_window.theme_user]);
+
+	mlk_label_query(l, &w, &h);
+	mlk_align(MLK_ALIGN_CENTER, &l->x, &l->y, w, h, 0, 0, mlk_window.w, mlk_window.h);
+	l->y += h * 2;
+}
+
+static void
 init(void)
 {
 	unsigned int w, h;
@@ -127,9 +152,6 @@
 	if (mlk_example_init("example-label") < 0)
 		mlk_panic();
 	
-	/* Change default style for all labels. */
-	mlk_label_style->color = 0x005162ff;
-
 	/* Change the glowing style. */
 	style_glow.update = style_glow_update;
 
@@ -139,6 +161,12 @@
 		mlk_align(table[i].align, &l->x, &l->y, w, h, 0, 0, mlk_window.w, mlk_window.h);
 	}
 
+	/* Move a bit the help text. */
+	table[0].label.y += 20;
+
+	/* Initialize theme help text. */
+	set_help_text();
+
 	mlk_glower_init(&glow);
 }
 
@@ -146,11 +174,19 @@
 handle(struct mlk_state *st, const union mlk_event *ev)
 {
 	(void)st;
+	
+	enum mlk_window_theme theme;
+
+	mlk_ui_handle(ev);
 
 	switch (ev->type) {
-	case MLK_EVENT_MOUSE:
-		mouse_label.x = ev->mouse.x;
-		mouse_label.y = ev->mouse.y;
+	case MLK_EVENT_KEYDOWN:
+		if (ev->key.key == MLK_KEY_SPACE) {
+			theme = (mlk_window.theme_user + 1) % MLK_WINDOW_THEME_LAST;
+			mlk_window_set_theme(theme);
+			mlk_ui_set_theme(mlk_window.theme_effective);
+			set_help_text();
+		}
 		break;
 	case MLK_EVENT_QUIT:
 		mlk_game_quit();
@@ -167,8 +203,6 @@
 
 	for (size_t i = 0; i < MLK_UTIL_SIZE(table); ++i)
 		mlk_label_update(&table[i].label, ticks);
-
-	mlk_label_update(&mouse_label, ticks);
 }
 
 static void
@@ -176,13 +210,16 @@
 {
 	(void)st;
 
-	mlk_painter_set_color(MLK_EXAMPLE_BG);
+	if (mlk_window.theme_effective == MLK_WINDOW_THEME_DARK)
+		mlk_painter_set_color(0x323558ff);
+	else
+		mlk_painter_set_color(0xdcd4ffff);
+	
 	mlk_painter_clear();
 
 	for (size_t i = 0; i < MLK_UTIL_SIZE(table); ++i)
 		mlk_label_draw(&table[i].label);
 
-	mlk_label_draw(&mouse_label);
 	mlk_painter_present();
 }
 
--- a/libmlk-core/mlk/core/event.c	Sun Aug 20 11:14:58 2023 +0200
+++ b/libmlk-core/mlk/core/event.c	Sun Aug 20 14:45:10 2023 +0200
@@ -287,6 +287,23 @@
 	}
 }
 
+static void
+convert_theme(union mlk_event *ev)
+{
+	ev->type = MLK_EVENT_THEME;
+
+	switch (SDL_GetSystemTheme()) {
+	case SDL_SYSTEM_THEME_DARK:
+		ev->theme.theme = MLK_WINDOW_THEME_DARK;
+		break;
+	default:
+		ev->theme.theme = MLK_WINDOW_THEME_LIGHT;
+		break;
+	}
+
+	mlk_window.theme_effective = ev->theme.theme;
+}
+
 int
 mlk_event_poll(union mlk_event *ev)
 {
@@ -321,8 +338,18 @@
 		case SDL_EVENT_GAMEPAD_AXIS_MOTION:
 			convert_axis(&event, ev);
 			return 1;
+		case SDL_EVENT_SYSTEM_THEME_CHANGED:
+			/*
+			 * We only report the event if the user preferrence is
+			 * set to auto because we don't need it otherwise.
+			 */
+			if (mlk_window.theme_user == MLK_WINDOW_THEME_AUTO) {
+				convert_theme(ev);
+				return 1;
+			}
+			break;
 		default:
-			continue;
+			break;
 		}
 	}
 
--- a/libmlk-core/mlk/core/event.h	Sun Aug 20 11:14:58 2023 +0200
+++ b/libmlk-core/mlk/core/event.h	Sun Aug 20 14:45:10 2023 +0200
@@ -37,6 +37,7 @@
 #include "key.h"
 #include "mouse.h"
 #include "gamepad.h"
+#include "window.h"
 
 /**
  * \enum mlk_event_type
@@ -100,6 +101,11 @@
 	MLK_EVENT_AXIS,
 
 	/**
+	 * Operating system theme changed.
+	 */
+	MLK_EVENT_THEME,
+	
+	/**
 	 * Window manager quit event.
 	 *
 	 * No value.
@@ -218,6 +224,23 @@
 };
 
 /**
+ * \struct mlk_event_theme
+ * \brief Operating system theme changed
+ */
+struct mlk_event_theme {
+	/**
+	 * Set to ::MLK_EVENT_THEME.
+	 */
+	enum mlk_event_type type;
+
+	/**
+	 * New effective theme ::MLK_WINDOW_THEME_DARK or
+	 * ::MLK_WINDOW_THEME_LIGHT.
+	 */
+	enum mlk_window_theme theme;
+};
+
+/**
  * \union mlk_event
  * \brief Generic input event
  */
@@ -251,6 +274,11 @@
 	 * For ::MLK_EVENT_AXIS.
 	 */
 	struct mlk_event_axis axis;
+
+	/**
+	 * For ::MLK_EVENT_THEME.
+	 */
+	struct mlk_event_theme theme;
 };
 
 #if defined(__cplusplus)
--- a/libmlk-core/mlk/core/window.c	Sun Aug 20 11:14:58 2023 +0200
+++ b/libmlk-core/mlk/core/window.c	Sun Aug 20 14:45:10 2023 +0200
@@ -34,7 +34,9 @@
 static SDL_Cursor *cursors[MLK_WINDOW_CURSOR_LAST];
 
 struct mlk_window mlk_window = {
-	.handle = &handle
+	.handle = &handle,
+	.theme_user = MLK_WINDOW_THEME_AUTO,
+	.theme_effective = MLK_WINDOW_THEME_LIGHT,
 };
 
 static void
@@ -75,7 +77,7 @@
 static int
 load_renderer(void)
 {
-	return (handle.renderer = SDL_CreateRenderer(handle.win, NULL, 0));
+	return (handle.renderer = SDL_CreateRenderer(handle.win, NULL, 0)) != NULL;
 }
 
 int
@@ -94,6 +96,8 @@
 	load_framerate();
 	load_cursors();
 
+	mlk_window_set_theme(mlk_window.theme_user);
+
 	return 0;
 }
 
@@ -111,6 +115,22 @@
 }
 
 void
+mlk_window_set_theme(enum mlk_window_theme theme)
+{
+	assert(theme >= 0 && theme < MLK_WINDOW_THEME_LAST);
+
+	mlk_window.theme_user = theme;
+
+	if (theme == MLK_WINDOW_THEME_AUTO) {
+		if (SDL_GetSystemTheme() == SDL_SYSTEM_THEME_DARK)
+			mlk_window.theme_effective = MLK_WINDOW_THEME_DARK;
+		else
+			mlk_window.theme_effective = MLK_WINDOW_THEME_LIGHT;
+	} else
+		mlk_window.theme_effective = mlk_window.theme_user;
+}
+
+void
 mlk_window_finish(void)
 {
 	if (handle.renderer)
--- a/libmlk-core/mlk/core/window.h	Sun Aug 20 11:14:58 2023 +0200
+++ b/libmlk-core/mlk/core/window.h	Sun Aug 20 14:45:10 2023 +0200
@@ -30,6 +30,37 @@
  */
 
 /**
+ * \enum mlk_window_theme
+ * \brief Operating system theme.
+ *
+ * This enumeration is used for both user preference and current effective theme
+ * which are two distinct things. If user selects `auto` then the effective
+ * theme will be set either to light or dark depending on the system platform.
+ */
+enum mlk_window_theme {
+	/**
+	 * Prefer the operating system user preference, if theme can't be
+	 * detected it will be set to light instead.
+	 */
+	MLK_WINDOW_THEME_AUTO,
+
+	/**
+	 * Light theme.
+	 */
+	MLK_WINDOW_THEME_LIGHT,
+
+	/**
+	 * Dark theme.
+	 */
+	MLK_WINDOW_THEME_DARK,
+	
+	/**
+	 * Unused sentinel value.
+	 */
+	MLK_WINDOW_THEME_LAST
+};
+
+/**
  * \struct mlk_window
  * \brief Window structure
  */
@@ -55,6 +86,27 @@
 	 */
 	unsigned int framerate;
 
+	/**
+	 * (read-only)
+	 *
+	 * User preferred theme.
+	 *
+	 * \warning Do not set this variable manually, it won't have any effect
+	 *          use ::mlk_window_set_theme instead.
+	 */
+	enum mlk_window_theme theme_user;
+
+	/**
+	 * (read-only)
+	 *
+	 * Current effective theme depending on the user preferred setting, this
+	 * value holds either ::MLK_WINDOW_THEME_LIGHT or
+	 * ::MLK_WINDOW_THEME_DARK only.
+	 *
+	 * \warning Do not set this variable manually, it won't have any effect.
+	 */
+	enum mlk_window_theme theme_effective;
+
 	/** \cond MLK_PRIVATE_DECLS */
 	void *handle;
 	/** \endcond MLK_PRIVATE_DECLS */
@@ -144,6 +196,17 @@
 mlk_window_set_cursor(enum mlk_window_cursor cursor);
 
 /**
+ * Select a preferred user theme for the window (auto, dark or light).
+ *
+ * This function will adjust ::mlk_window::theme_user and
+ * ::mlk_window::theme_effective.
+ *
+ * \param theme the new user theme
+ */
+void
+mlk_window_set_theme(enum mlk_window_theme theme);
+
+/**
  * Destroy the window.
  */
 void
--- a/libmlk-example/mlk/example/example.c	Sun Aug 20 11:14:58 2023 +0200
+++ b/libmlk-example/mlk/example/example.c	Sun Aug 20 14:45:10 2023 +0200
@@ -45,6 +45,7 @@
 	if (mlk_window_open(name, MLK_EXAMPLE_W, MLK_EXAMPLE_H) < 0)
 		return err;
 
+	mlk_ui_set_theme(mlk_window.theme_effective);
 	mlk_registry_init();
 
 	return 0;
--- a/libmlk-ui/mlk/ui/ui.c	Sun Aug 20 11:14:58 2023 +0200
+++ b/libmlk-ui/mlk/ui/ui.c	Sun Aug 20 14:45:10 2023 +0200
@@ -24,7 +24,10 @@
 
 #include <libintl.h>
 
+#include <SDL.h>
+
 #include <mlk/core/err.h>
+#include <mlk/core/event.h>
 #include <mlk/core/font.h>
 #include <mlk/core/texture.h>
 #include <mlk/core/trace.h>
@@ -34,6 +37,7 @@
 #include <assets/fonts/opensans-regular.h>
 
 #include "align.h"
+#include "label.h"
 #include "ui.h"
 #include "ui_p.h"
 
@@ -55,7 +59,8 @@
 	FONT_DEF(assets_fonts_opensans_regular, 14, MLK_UI_FONT_INTERFACE)
 };
 
-struct mlk_font mlk_ui_fonts[MLK_UI_FONT_LAST] = {0};
+/* Global fonts. */
+struct mlk_font mlk_ui_fonts[MLK_UI_FONT_LAST] = {};
 
 int
 mlk_ui_init(void)
@@ -80,6 +85,23 @@
 }
 
 void
+mlk_ui_handle(const union mlk_event *ev)
+{
+	if (ev->type == MLK_EVENT_THEME)
+		mlk_ui_set_theme(ev->theme.theme);
+}
+
+void
+mlk_ui_set_theme(enum mlk_window_theme theme)
+{
+	if (theme == MLK_WINDOW_THEME_DARK) {
+		mlk_label_style = &mlk_label_style_dark;
+	} else {
+		mlk_label_style = &mlk_label_style_light;
+	}
+}
+
+void
 mlk_ui_draw_text(enum mlk_align align,
                  struct mlk_font *font,
                  unsigned long color,
--- a/libmlk-ui/mlk/ui/ui.h	Sun Aug 20 11:14:58 2023 +0200
+++ b/libmlk-ui/mlk/ui/ui.h	Sun Aug 20 14:45:10 2023 +0200
@@ -34,6 +34,9 @@
 #define MLK_UI_BORDER           (2)
 
 enum mlk_align;
+enum mlk_window_theme;
+
+union mlk_event;
 
 enum mlk_ui_font {
 	MLK_UI_FONT_INTERFACE,
@@ -49,6 +52,12 @@
 int
 mlk_ui_init(void);
 
+void
+mlk_ui_handle(const union mlk_event *ev);
+
+void
+mlk_ui_set_theme(enum mlk_window_theme theme);
+
 /* TODO: probably better somewhere else? */
 void
 mlk_ui_draw_text(enum mlk_align align,