changeset 635:3cb1860d9f11

rpg: improve message selectable lines
author David Demelier <markand@malikania.fr>
date Tue, 05 Sep 2023 11:51:46 +0200
parents 3930234ab1f5
children b826e80c53cd
files doc/Doxyfile doc/images/example-message.png examples/example-message/example-message.c libmlk-rpg/mlk/rpg/message.c libmlk-rpg/mlk/rpg/message.h
diffstat 5 files changed, 143 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/doc/Doxyfile	Tue Aug 29 13:34:10 2023 +0200
+++ b/doc/Doxyfile	Tue Sep 05 11:51:46 2023 +0200
@@ -5,6 +5,7 @@
 PROJECT_BRIEF          = "@molko_DESCRIPTION@"
 
 OUTPUT_DIRECTORY       = "@doc_BINARY_DIR@"
+IMAGE_PATH             = "@doc_SOURCE_DIR@/images"
 
 TAB_SIZE               = 8
 OPTIMIZE_OUTPUT_FOR_C  = YES
Binary file doc/images/example-message.png has changed
--- a/examples/example-message/example-message.c	Tue Aug 29 13:34:10 2023 +0200
+++ b/examples/example-message/example-message.c	Tue Sep 05 11:51:46 2023 +0200
@@ -207,16 +207,17 @@
 question(void)
 {
 	const char * const text[] = {
-		"Okay, I've understood.",
-		"Nevermind, I'll do it again."
+		"Do you think you're brave enough to fight this Karen?",
+		"Sure I am.",
+		"No."
 	};
 	struct mlk_message msg = {
 		.x = MX,
 		.y = MY,
 		.w = MW,
 		.lines = text,
-		.linesz = 2,
-		.flags = MLK_MESSAGE_FLAGS_QUESTION
+		.linesz = 3,
+		.selectable = 0x6
 	};
 
 	mlk_message_query(&msg, NULL, &msg.h);
--- a/libmlk-rpg/mlk/rpg/message.c	Tue Aug 29 13:34:10 2023 +0200
+++ b/libmlk-rpg/mlk/rpg/message.c	Tue Sep 05 11:51:46 2023 +0200
@@ -37,6 +37,12 @@
 
 #include "message.h"
 
+static inline int
+is_selectable(const struct mlk_message *msg, size_t n)
+{
+	return msg->lines[n] && ((msg->selectable >> n) & 0x1) == 1;
+}
+
 static inline struct mlk_message_style *
 get_style(struct mlk_message *msg)
 {
@@ -116,7 +122,7 @@
 		if (!msg->lines[i])
 			continue;
 
-		if ((msg->flags & MLK_MESSAGE_FLAGS_QUESTION) && msg->index == i)
+		if (msg->selectable && msg->selected == i && is_selectable(msg, i))
 			color = style->color_selected;
 		else
 			color = style->color;
@@ -287,6 +293,18 @@
 
 	if (msg->flags & MLK_MESSAGE_FLAGS_AUTOMATIC && style->timeout == 0)
 		mlk_tracef("message is automatic but has zero timeout");
+
+	/*
+	 * Make sure selected index goes in the range of the lines and that it
+	 * starts on a proper selectable line.
+	 */
+	if (msg->selectable) {
+		if (msg->selected >= msg->linesz || !is_selectable(msg, msg->selected))
+			msg->selected = 0;
+
+		while (!is_selectable(msg, msg->selected))
+			msg->selected++;
+	}
 }
 
 int
@@ -297,6 +315,72 @@
 	return MLK__STYLE_CALL(msg->style, mlk_message_style, query, msg, w, h);
 }
 
+static inline size_t
+first(const struct mlk_message *msg)
+{
+	size_t ret = -1;
+
+	for (size_t i = 0; i < msg->linesz; ++i) {
+		if (is_selectable(msg, i)) {
+			ret = i;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static inline size_t
+last(const struct mlk_message *msg)
+{
+	size_t ret = -1;
+
+	for (size_t i = msg->linesz; i >= 0; --i) {
+		if (is_selectable(msg, i)) {
+			ret = i;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static inline size_t
+previous(const struct mlk_message *msg)
+{
+	size_t ret;
+
+	/* wrap */
+	if (msg->selected == first(msg))
+		ret = last(msg);
+	else {
+		ret = msg->selected - 1;
+
+		while (ret > 0 && !is_selectable(msg, ret))
+			ret--;
+	}
+
+	return ret;
+}
+
+static inline size_t
+next(const struct mlk_message *msg)
+{
+	size_t ret;
+
+	/* wrap */
+	if (msg->selected == last(msg))
+		ret = first(msg);
+	else {
+		ret = msg->selected + 1;
+
+		while (ret < msg->linesz && !is_selectable(msg, ret))
+			ret++;
+	}
+
+	return ret;
+}
+
 void
 mlk_message_handle(struct mlk_message *msg, const union mlk_event *ev)
 {
@@ -313,12 +397,12 @@
 
 	switch (ev->key.key) {
 	case MLK_KEY_UP:
-		if (msg->index > 0)
-			msg->index--;
+		if (msg->selectable && msg->linesz)
+			msg->selected = previous(msg);
 		break;
 	case MLK_KEY_DOWN:
-		if (msg->index + 1 < msg->linesz && msg->lines[msg->index + 1])
-			msg->index++;
+		if (msg->selectable && msg->linesz)
+			msg->selected = next(msg);
 		break;
 	case MLK_KEY_ENTER:
 		msg->state = msg->flags & MLK_MESSAGE_FLAGS_FADEOUT
--- a/libmlk-rpg/mlk/rpg/message.h	Tue Aug 29 13:34:10 2023 +0200
+++ b/libmlk-rpg/mlk/rpg/message.h	Tue Sep 05 11:51:46 2023 +0200
@@ -19,9 +19,35 @@
 #ifndef MLK_RPG_MESSAGE_H
 #define MLK_RPG_MESSAGE_H
 
+/**
+ * \file mlk/rpg/message.h
+ * \brief Message dialog.
+ *
+ * This module offers a message box dialog that shows several lines similar to
+ * old school RPG games.
+ *
+ * The dialog can be animated while appearing and hiding using special
+ * ::MLK_MESSAGE_FLAGS_FADEIN and ::MLK_MESSAGE_FLAGS_FADEOUT which aren't set
+ * by default.
+ *
+ * Lines can be selected by specifying a line mask as a bitmask, see
+ * ::mlk_message::selectable field for more information.
+ *
+ * Example of message dialog:
+ *
+ * \image html example-message.png
+ */
+
 #include <stddef.h>
 
+/**
+ * \brief Default message animation speed.
+ */
 #define MLK_MESSAGE_SPEED_DEFAULT       (150)
+
+/**
+ * \brief Default message automatic close duration.
+ */
 #define MLK_MESSAGE_TIMEOUT_DEFAULT     (5000)
 
 struct mlk_font;
@@ -41,11 +67,6 @@
 	MLK_MESSAGE_FLAGS_AUTOMATIC     = (1 << 0),
 
 	/**
-	 * Lines can be selected by the user.
-	 */
-	MLK_MESSAGE_FLAGS_QUESTION      = (1 << 1),
-
-	/**
 	 * Add fade in animation.
 	 */
 	MLK_MESSAGE_FLAGS_FADEIN        = (1 << 2),
@@ -82,6 +103,10 @@
 	MLK_MESSAGE_STATE_HIDING
 };
 
+/**
+ * \struct mlk_message
+ * \brief Message box.
+ */
 struct mlk_message {
 	/**
 	 * (read-write)
@@ -128,9 +153,26 @@
 	/**
 	 * (read-write)
 	 *
+	 * Tells which lines can be selected as bitmask, right most bit being
+	 * the first line in the array.
+	 *
+	 * Example:
+	 *
+	 * [0] Would you open this chest?
+	 * [1] Yes.
+	 * [2] No I prefer run away.
+	 *
+	 * Selectables optiong being 1 and 2, this field must be set to
+	 * `(1 << 1) | (1 << 2)`.
+	 */
+	size_t selectable;
+
+	/**
+	 * (read-write)
+	 *
 	 * Selected item from the user.
 	 */
-	size_t index;
+	size_t selected;
 
 	/**
 	 * (read-write)