changeset 140:453651d76f7c

core: upgrade message in terms of customization
author David Demelier <markand@malikania.fr>
date Tue, 13 Oct 2020 18:39:35 +0200
parents 8b035f7f978a
children 4eeeccf2b732
files examples/example-action.c examples/example-message.c libcore/core/message.c libcore/core/message.h
diffstat 4 files changed, 283 insertions(+), 96 deletions(-) [+]
line wrap: on
line diff
--- a/examples/example-action.c	Tue Oct 13 15:15:40 2020 +0200
+++ b/examples/example-action.c	Tue Oct 13 18:39:35 2020 +0200
@@ -37,8 +37,13 @@
 #include <assets/sprites/chest.h>
 #include <assets/sprites/people.h>
 
-#define W 1280
-#define H 720
+#define W       (1280)
+#define H       (720)
+
+#define MW      (W * 0.75)
+#define MH      (H * 0.125)
+#define MX      ((W / 2) - (MW / 2))
+#define MY      (100)
 
 /* This is a stack of "parallel" events. */
 static struct action_stack events;
@@ -60,6 +65,10 @@
 	struct action event;
 } chest = {
 	.msg = {
+		.x = MX,
+		.y = MY,
+		.w = MW,
+		.h = MH,
 		.text = {
 			"100000 pièces.",
 			"Te voilà riche sale file de crevette."
@@ -106,6 +115,12 @@
 	.msgs = {
 		{
 			.msg = {
+				.x = MX,
+				.y = MY,
+				.w = MW,
+				.h = MH,
+				.delay = MESSAGE_DELAY_DEFAULT,
+				.flags = MESSAGE_FLAGS_FADEIN | MESSAGE_FLAGS_AUTOMATIC,
 				.text = {
 					"Bienvenue dans ce monde Molko."
 				}
@@ -113,6 +128,10 @@
 		},
 		{
 			.msg = {
+				.x = MX,
+				.y = MY,
+				.w = MW,
+				.h = MH,
 				.text = {
 					"Penses tu vraiment pouvoir me battre ?"
 				}
@@ -120,7 +139,11 @@
 		},
 		{
 			.msg = {
-				.flags = MESSAGE_QUESTION,
+				.x = MX,
+				.y = MY,
+				.w = MW,
+				.h = MH,
+				.flags = MESSAGE_FLAGS_QUESTION,
 				.text = {
 					"Non j'ai la trouille.",
 					"Bien sûr, je suis la légende."
@@ -131,6 +154,10 @@
 		/* In case of NO. */
 		{
 			.msg = {
+				.x = MX,
+				.y = MY,
+				.w = MW,
+				.h = MH,
 				.text = {
 					"Poule mouillée."
 				}
@@ -140,6 +167,10 @@
 		/* In case of YES. */
 		{
 			.msg = {
+				.x = MX,
+				.y = MY,
+				.w = MW,
+				.h = MH,
 				.text = {
 					"Prépare toi à souffrir."
 				}
--- a/examples/example-message.c	Tue Oct 13 15:15:40 2020 +0200
+++ b/examples/example-message.c	Tue Oct 13 18:39:35 2020 +0200
@@ -16,8 +16,11 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <string.h>
+
 #include <core/clock.h>
 #include <core/event.h>
+#include <core/frame.h>
 #include <core/message.h>
 #include <core/painter.h>
 #include <core/panic.h>
@@ -26,8 +29,13 @@
 #include <core/theme.h>
 #include <core/window.h>
 
-#define W 1280
-#define H 720
+#define W       (1280)
+#define H       (720)
+
+#define MW      (W * 0.75)
+#define MH      (H * 0.125)
+#define MX      ((W / 2) - (MW / 2))
+#define MY      (100)
 
 static void
 init(void)
@@ -63,7 +71,7 @@
 		while (event_poll(&ev)) {
 			switch (ev.type) {
 			case EVENT_QUIT:
-				msg->state = MESSAGE_NONE;
+				msg->state = MESSAGE_STATE_NONE;
 				break;
 			default:
 				message_handle(msg, &ev);
@@ -83,9 +91,22 @@
 }
 
 static void
+my_draw_frame(struct theme *th, const struct frame *f)
+{
+	(void)th;
+
+	painter_set_color(0xff0000ff);
+	painter_draw_rectangle(f->x, f->y, f->w, f->h);
+}
+
+static void
 basic(void)
 {
 	struct message msg = {
+		.x = MX,
+		.y = MY,
+		.w = MW,
+		.h = MH,
 		.text = {
 			"This is a basic message.",
 			"You need to press <Enter> to close it."
@@ -99,12 +120,71 @@
 automatic(void)
 {
 	struct message msg = {
+		.x = MX,
+		.y = MY,
+		.w = MW,
+		.h = MH,
+		.timeout = MESSAGE_TIMEOUT_DEFAULT,
 		.text = {
 			"This is a an automatic message.",
 			"It will disappear in a few seconds.",
 			"You can still press <Enter> to close it quicker."
 		},
-		.flags = MESSAGE_AUTOMATIC
+		.flags = MESSAGE_FLAGS_AUTOMATIC
+	};
+
+	run(&msg);
+}
+
+static void
+fadein(void)
+{
+	struct message msg = {
+		.x = MX,
+		.y = MY,
+		.w = MW,
+		.h = MH,
+		.delay = MESSAGE_DELAY_DEFAULT,
+		.text = {
+			"This message will fade in."
+		},
+		.flags = MESSAGE_FLAGS_FADEIN
+	};
+
+	run(&msg);
+}
+
+static void
+fadeout(void)
+{
+	struct message msg = {
+		.x = MX,
+		.y = MY,
+		.w = MW,
+		.h = MH,
+		.delay = MESSAGE_DELAY_DEFAULT,
+		.text = {
+			"This message will fade out."
+		},
+		.flags = MESSAGE_FLAGS_FADEOUT
+	};
+
+	run(&msg);
+}
+
+static void
+fade(void)
+{
+	struct message msg = {
+		.x = MX,
+		.y = MY,
+		.w = MW,
+		.h = MH,
+		.delay = MESSAGE_DELAY_DEFAULT,
+		.text = {
+			"This message will fade in and out."
+		},
+		.flags = MESSAGE_FLAGS_FADEIN | MESSAGE_FLAGS_FADEOUT
 	};
 
 	run(&msg);
@@ -114,16 +194,67 @@
 question(void)
 {
 	struct message msg = {
+		.x = MX,
+		.y = MY,
+		.w = MW,
+		.h = MH,
 		.text = {
 			"Okay, I've understood.",
 			"Nevermind, I'll do it again."
 		},
-		.flags = MESSAGE_QUESTION
+		.flags = MESSAGE_FLAGS_QUESTION
+	};
+
+	run(&msg);
+}
+
+static void
+smallbottom(void)
+{
+	const unsigned int w = window.w / 4;
+	const unsigned int h = 50;
+	const int x = (window.w / 2) - (w / 2);
+	const int y = (window.h - h - 10);
+
+	struct message msg = {
+		.x = x,
+		.y = y,
+		.w = w,
+		.h = h,
+		.delay = MESSAGE_DELAY_DEFAULT,
+		.flags = MESSAGE_FLAGS_FADEIN | MESSAGE_FLAGS_FADEOUT,
+		.text = {
+			"This one is small here."
+		}
 	};
 
 	run(&msg);
 }
 
+static void
+custom(void)
+{
+	struct theme theme;
+	struct message msg = {
+		.x = MX,
+		.y = MY,
+		.w = MW,
+		.h = MH,
+		.text = {
+			"This one will destroy your eyes.",
+			"Because it use a terrible custom theme."
+		},
+		.theme = &theme
+	};
+
+	/* Borrow default theme and change its frame drawing. */
+	memcpy(&theme, theme_default(), sizeof (theme));
+	theme.draw_frame = my_draw_frame;
+	theme.colors[THEME_COLOR_NORMAL] = 0x0000ffff;
+
+	run(&msg);
+}
+
 int
 main(int argc, char **argv)
 {
@@ -132,7 +263,12 @@
 
 	init();
 	basic();
+	fadein();
+	fadeout();
+	fade();
 	automatic();
 	question();
+	smallbottom();
+	custom();
 	quit();
 }
--- a/libcore/core/message.c	Tue Oct 13 15:15:40 2020 +0200
+++ b/libcore/core/message.c	Tue Oct 13 18:39:35 2020 +0200
@@ -25,6 +25,7 @@
 #include "font.h"
 #include "frame.h"
 #include "label.h"
+#include "maths.h"
 #include "message.h"
 #include "painter.h"
 #include "panic.h"
@@ -32,15 +33,8 @@
 #include "texture.h"
 #include "theme.h"
 #include "util.h"
-#include "window.h"
 
-#define MESSAGE_SPEED   100     /* Time delay for animations */
-#define MESSAGE_TIMEOUT 5000    /* Time for auto-closing */
-
-#define WIDTH   (window.w * 0.75)
-#define HEIGHT  (window.h * 0.125)
-
-#define THEME(msg) (msg->theme ? msg->theme : theme_default())
+#define THEME(msg)      (msg->theme ? msg->theme : theme_default())
 
 static void
 handle(struct action *action, const union event *ev)
@@ -67,14 +61,65 @@
 	message_draw(action->data);
 }
 
+static void
+draw_frame(const struct message *msg)
+{
+	assert(msg);
+
+	struct frame frame = {
+		.w = msg->w,
+		.h = msg->h,
+		.theme = msg->theme
+	};
+
+	frame_draw(&frame);
+}
+
+static void
+draw_lines(const struct message *msg)
+{
+	struct theme *theme;
+	struct font *font;
+	unsigned int lineh;
+
+	theme = THEME(msg);
+	font = theme->fonts[THEME_FONT_INTERFACE];
+	lineh = font_height(font);
+
+	for (int i = 0; i < 6; ++i) {
+		if (!msg->text[i])
+			continue;
+
+		struct label label = {
+			.x = theme->padding,
+			.y = theme->padding + (i * lineh),
+			.h = lineh,
+			.theme = msg->theme,
+			.text = msg->text[i],
+			.flags = LABEL_NO_HCENTER
+		};
+
+		/*
+		 * The function label_draw will normally use
+		 * THEME_FONT_INTERFACE so update its color if needed.
+		 */
+		if (msg->flags & MESSAGE_FLAGS_QUESTION && msg->index == (unsigned int)i)
+			label.color = theme->colors[THEME_COLOR_SELECTED];
+
+		label_draw(&label);
+	}
+}
+
 void
 message_start(struct message *msg)
 {
 	assert(msg);
 
 	msg->elapsed = 0;
-	msg->scale = 0.0;
-	msg->state = msg->flags & MESSAGE_QUICK ? MESSAGE_SHOWING : MESSAGE_OPENING;
+	msg->scale = msg->flags & MESSAGE_FLAGS_FADEIN ? 0.0 : 1.0;
+	msg->state = msg->flags & MESSAGE_FLAGS_FADEIN
+	    ? MESSAGE_STATE_OPENING
+	    : MESSAGE_STATE_SHOWING;
 }
 
 void
@@ -84,11 +129,11 @@
 	assert(ev);
 
 	/* Skip if the message animation hasn't complete. */
-	if (msg->state != MESSAGE_SHOWING)
+	if (msg->state != MESSAGE_STATE_SHOWING)
 		return;
 
 	/* Only keyboard event are valid. */
-	if (ev->type != EVENT_KEYDOWN || msg->state == MESSAGE_NONE)
+	if (ev->type != EVENT_KEYDOWN || msg->state == MESSAGE_STATE_NONE)
 		return;
 
 	switch (ev->key.key) {
@@ -101,7 +146,9 @@
 			msg->index++;
 		break;
 	case KEY_ENTER:
-		msg->state = msg->flags & MESSAGE_QUICK ? MESSAGE_NONE : MESSAGE_HIDING;
+		msg->state = msg->flags & MESSAGE_FLAGS_FADEOUT
+		    ? MESSAGE_STATE_HIDING
+		    : MESSAGE_STATE_NONE;
 		msg->elapsed = 0;
 		break;
 	default:
@@ -117,33 +164,35 @@
 	msg->elapsed += ticks;
 
 	switch (msg->state) {
-	case MESSAGE_OPENING:
-		msg->scale = (double)msg->elapsed / (double)MESSAGE_SPEED;
+	case MESSAGE_STATE_OPENING:
+		msg->scale = (double)msg->elapsed / (double)msg->delay;
 
 		if (msg->scale > 1)
 			msg->scale = 1;
 
-		if (msg->elapsed >= MESSAGE_SPEED) {
-			msg->state = MESSAGE_SHOWING;
+		if (msg->elapsed >= msg->delay) {
+			msg->state = MESSAGE_STATE_SHOWING;
 			msg->elapsed = 0;
 		}
 
 		break;
-	case MESSAGE_SHOWING:
+	case MESSAGE_STATE_SHOWING:
 		/* Do automatically switch state if requested by the user. */
-		if (msg->flags & MESSAGE_AUTOMATIC && msg->elapsed >= MESSAGE_TIMEOUT) {
-			msg->state = msg->flags & MESSAGE_QUICK ? MESSAGE_NONE : MESSAGE_HIDING;
+		if (msg->flags & MESSAGE_FLAGS_AUTOMATIC && msg->elapsed >= msg->timeout) {
+			msg->state = msg->flags & MESSAGE_FLAGS_FADEOUT
+			    ? MESSAGE_STATE_HIDING
+			    : MESSAGE_STATE_NONE;
 			msg->elapsed = 0;
 		}
 
 		break;
-	case MESSAGE_HIDING:
-		msg->scale = 1 - (double)msg->elapsed / (double)MESSAGE_SPEED;
+	case MESSAGE_STATE_HIDING:
+		msg->scale = 1 - (double)msg->elapsed / (double)msg->delay;
 
 		if (msg->scale < 0)
 			msg->scale = 0;
-		if (msg->elapsed >= MESSAGE_SPEED) {
-			msg->state = MESSAGE_NONE;
+		if (msg->elapsed >= msg->delay) {
+			msg->state = MESSAGE_STATE_NONE;
 			msg->elapsed = 0;
 		}
 
@@ -152,53 +201,7 @@
 		break;
 	}
 
-	return msg->state == MESSAGE_NONE;
-}
-
-static void
-draw_frame(const struct message *msg)
-{
-	assert(msg);
-
-	struct frame frame = {
-		.w = WIDTH,
-		.h = HEIGHT
-	};
-
-	frame_draw(&frame);
-}
-
-static void
-draw_lines(const struct message *msg)
-{
-	struct font *font;
-	unsigned int lineh;
-
-	font = THEME(msg)->fonts[THEME_FONT_INTERFACE];
-	lineh = font_height(font);
-
-	for (int i = 0; i < 6; ++i) {
-		if (!msg->text[i])
-			continue;
-
-		struct label label = {
-			.x = 10,
-			.y = 10 + (i * lineh),
-			.h = lineh,
-			.theme = msg->theme,
-			.text = msg->text[i],
-			.flags = LABEL_NO_HCENTER
-		};
-
-		/*
-		 * The function label_draw will normally use
-		 * THEME_FONT_INTERFACE so update its color if needed.
-		 */
-		if (msg->flags & MESSAGE_QUESTION && msg->index == (unsigned int)i)
-			label.color = THEME(msg)->colors[THEME_COLOR_SELECTED];
-
-		label_draw(&label);
-	}
+	return msg->state == MESSAGE_STATE_NONE;
 }
 
 void
@@ -207,9 +210,10 @@
 	assert(msg);
 
 	struct texture tex;
-	int x, y, w, h;
+	int x, y;
+	unsigned int w, h;
 
-	if (!texture_new(&tex, WIDTH, HEIGHT))
+	if (!texture_new(&tex, msg->w, msg->h))
 		panic();
 
 	PAINTER_BEGIN(&tex);
@@ -218,14 +222,14 @@
 	PAINTER_END();
 
 	/* Compute scaling. */
-	w = WIDTH * msg->scale;
-	h = HEIGHT * msg->scale;
+	w = msg->w * msg->scale;
+	h = msg->h * msg->scale;
 
-	/* Compute position. */
-	x = (window.w / 2) - (w / 2);
-	y = HEIGHT;
+	/* Centerize within its drawing area. */
+	maths_centerize(&x, &y, w, h, msg->x, msg->y, msg->w, msg->h);
 
-	texture_scale(&tex, 0, 0, WIDTH, HEIGHT, x, y, w, h, 0.0);
+	/* Draw and clear. */
+	texture_scale(&tex, 0, 0, msg->w, msg->h, x, y, w, h, 0);
 	texture_finish(&tex);
 }
 
@@ -234,7 +238,7 @@
 {
 	assert(msg);
 
-	msg->state = MESSAGE_HIDING;
+	msg->state = MESSAGE_STATE_HIDING;
 	msg->elapsed = 0;
 }
 
--- a/libcore/core/message.h	Tue Oct 13 15:15:40 2020 +0200
+++ b/libcore/core/message.h	Tue Oct 13 18:39:35 2020 +0200
@@ -74,22 +74,33 @@
 union event;
 
 /**
+ * \brief Default animation speed in milliseconds.
+ */
+#define MESSAGE_DELAY_DEFAULT   (150)
+
+/**
+ * \brief Default timeout in milliseconds for automatic messages.
+ */
+#define MESSAGE_TIMEOUT_DEFAULT (5000)
+
+/**
  * \brief Message flags.
  */
 enum message_flags {
-	MESSAGE_AUTOMATIC       = (1 << 0),     /*!< Will automatically change state by itself. */
-	MESSAGE_QUESTION        = (1 << 1),     /*!< The message is a question. */
-	MESSAGE_QUICK           = (1 << 2),     /*!< Avoid animations. */
+	MESSAGE_FLAGS_AUTOMATIC         = (1 << 0),     /*!< Will automatically change state by itself. */
+	MESSAGE_FLAGS_QUESTION          = (1 << 1),     /*!< The message is a question. */
+	MESSAGE_FLAGS_FADEIN            = (1 << 2),     /*!< Animate opening. */
+	MESSAGE_FLAGS_FADEOUT           = (1 << 3)      /*!< Animate closing. */
 };
 
 /**
  * \brief Message state.
  */
 enum message_state {
-	MESSAGE_NONE,           /*!< Message hasn't start yet or is finished */
-	MESSAGE_OPENING,        /*!< Message animation is opening */
-	MESSAGE_SHOWING,        /*!< Message is displaying */
-	MESSAGE_HIDING          /*!< Message animation for hiding */
+	MESSAGE_STATE_NONE,             /*!< Message hasn't start yet or is finished */
+	MESSAGE_STATE_OPENING,          /*!< Message animation is opening */
+	MESSAGE_STATE_SHOWING,          /*!< Message is displaying */
+	MESSAGE_STATE_HIDING            /*!< Message animation for hiding */
 };
 
 /**
@@ -99,8 +110,13 @@
  * any user properties and therefore must exist while using it.
  */
 struct message {
+	int x;                          /*!< (RW) Position in x. */
+	int y;                          /*!< (RW) Position in y. */
+	unsigned int w;                 /*!< (RW) Width. */
+	unsigned int h;                 /*!< (RW) Height. */
+	unsigned int delay;             /*!< (RW) Delay for animations. */
+	unsigned int timeout;           /*!< (RW) Timeout in milliseconds. */
 	const char *text[6];            /*!< (RW) Lines of text to show. */
-	struct texture *frame;          /*!< (RW, ref) Frame to use. */
 	struct texture *avatar;         /*!< (RW, ref, optional) Avatar face. */
 	unsigned int index;             /*!< (RW) Line selected */
 	enum message_flags flags;       /*!< (RW) Message flags */