changeset 145:7f1af54bb35a

core: rework label alignment, closes #2494 @1h Now labels can be positioned on a bounding box with much more convenient possibilities.
author David Demelier <markand@malikania.fr>
date Wed, 14 Oct 2020 18:11:38 +0200
parents 28d9bb62fcb1
children 7d7ea7a9cf50
files examples/CMakeLists.txt examples/example-drawable.c examples/example-label.c examples/example-sound.c libadventure/adventure/trace_hud.c libcore/core/inventory_dialog.c libcore/core/label.c libcore/core/label.h libcore/core/message.c libcore/core/theme.c
diffstat 10 files changed, 296 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/examples/CMakeLists.txt	Wed Oct 14 16:40:34 2020 +0200
+++ b/examples/CMakeLists.txt	Wed Oct 14 18:11:38 2020 +0200
@@ -46,6 +46,12 @@
 )
 
 molko_define_executable(
+	TARGET example-label
+	SOURCES example-label.c
+	LIBRARIES libcore
+)
+
+molko_define_executable(
 	TARGET example-message
 	SOURCES example-message.c
 	FOLDER examples
--- a/examples/example-drawable.c	Wed Oct 14 16:40:34 2020 +0200
+++ b/examples/example-drawable.c	Wed Oct 14 18:11:38 2020 +0200
@@ -44,9 +44,9 @@
 	.x = 10,
 	.y = 10,
 	.w = W,
-	.h = 32,
-	.flags = LABEL_NO_HCENTER,
-	.color = 0x4f8fbaff
+	.h = H,
+	.align = LABEL_ALIGN_TOP_LEFT,
+	.flags = LABEL_FLAGS_SHADOW
 };
 
 #if 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/example-label.c	Wed Oct 14 18:11:38 2020 +0200
@@ -0,0 +1,168 @@
+/*
+ * example-label.c -- show how to use label and alignments
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <core/clock.h>
+#include <core/event.h>
+#include <core/label.h>
+#include <core/painter.h>
+#include <core/panic.h>
+#include <core/sys.h>
+#include <core/theme.h>
+#include <core/util.h>
+#include <core/window.h>
+
+#define W       (1280)
+#define H       (720)
+
+static void
+init(void)
+{
+	if (!sys_init() ||
+	    !window_init("Example - Label", W, H) ||
+	    !theme_init())
+		panic();
+}
+
+static void
+quit(void)
+{
+	theme_finish();
+	window_finish();
+	sys_finish();
+}
+
+static void
+run(void)
+{
+	struct clock clock = {0};
+	struct label labels[] = {
+		{
+			.x = 0,
+			.y = 0,
+			.w = W,
+			.h = H,
+			.text = "Top left",
+			.align = LABEL_ALIGN_TOP_LEFT
+		},
+		{
+			.x = 0,
+			.y = 0,
+			.w = W,
+			.h = H,
+			.text = "Top",
+			.align = LABEL_ALIGN_TOP
+		},
+		{
+			.x = 0,
+			.y = 0,
+			.w = W,
+			.h = H,
+			.text = "Top right",
+			.align = LABEL_ALIGN_TOP_RIGHT
+		},
+		{
+			.x = 0,
+			.y = 0,
+			.w = W,
+			.h = H,
+			.text = "Right",
+			.align = LABEL_ALIGN_RIGHT
+		},
+		{
+			.x = 0,
+			.y = 0,
+			.w = W,
+			.h = H,
+			.text = "Bottom right",
+			.align = LABEL_ALIGN_BOTTOM_RIGHT
+		},
+		{
+			.x = 0,
+			.y = 0,
+			.w = W,
+			.h = H,
+			.text = "Bottom",
+			.align = LABEL_ALIGN_BOTTOM
+		},
+		{
+			.x = 0,
+			.y = 0,
+			.w = W,
+			.h = H,
+			.text = "Bottom left",
+			.align = LABEL_ALIGN_BOTTOM_LEFT
+		},
+		{
+			.x = 0,
+			.y = 0,
+			.w = W,
+			.h = H,
+			.text = "Left",
+			.align = LABEL_ALIGN_LEFT
+		},
+		{
+			.x = 0,
+			.y = 0,
+			.w = W,
+			.h = H,
+			.text = "The world is Malikania.",
+			.flags = LABEL_FLAGS_SHADOW
+		},
+	};
+
+	clock_start(&clock);
+
+	for (;;) {
+		union event ev;
+		unsigned int elapsed = clock_elapsed(&clock);
+
+		clock_start(&clock);
+
+		while (event_poll(&ev)) {
+			switch (ev.type) {
+			case EVENT_QUIT:
+				return;
+			default:
+				break;
+			}
+		}
+
+		painter_set_color(0x4f8fbaff);
+		painter_clear();
+
+		for (size_t i = 0; i < NELEM(labels); ++i)
+			label_draw(&labels[i]);
+
+		painter_present();
+
+		if ((elapsed = clock_elapsed(&clock)) < 20)
+			delay(20 - elapsed);
+	}
+}
+
+int
+main(int argc, char **argv)
+{
+	(void)argc;
+	(void)argv;
+
+	init();
+	run();
+	quit();
+}
+
--- a/examples/example-sound.c	Wed Oct 14 16:40:34 2020 +0200
+++ b/examples/example-sound.c	Wed Oct 14 18:11:38 2020 +0200
@@ -39,8 +39,9 @@
 	.x = 10,
 	.y = 10,
 	.w = W,
-	.h = 32,
-	.flags = LABEL_NO_HCENTER
+	.h = H,
+	.align = LABEL_ALIGN_TOP_LEFT,
+	.flags = LABEL_FLAGS_SHADOW
 };
 
 static void
--- a/libadventure/adventure/trace_hud.c	Wed Oct 14 16:40:34 2020 +0200
+++ b/libadventure/adventure/trace_hud.c	Wed Oct 14 18:11:38 2020 +0200
@@ -98,7 +98,7 @@
 			.y = y,
 			.text = data.lines[i],
 			.theme = th,
-			.flags = LABEL_NO_HCENTER
+			.flags = LABEL_ALIGN_LEFT
 		});
 
 		y += font_height(th->fonts[THEME_FONT_INTERFACE]);
--- a/libcore/core/inventory_dialog.c	Wed Oct 14 16:40:34 2020 +0200
+++ b/libcore/core/inventory_dialog.c	Wed Oct 14 18:11:38 2020 +0200
@@ -196,7 +196,7 @@
 	dlg->fname.x = dlg->lname.x = dlg->x;
 	dlg->fname.y = dlg->lname.y = dlg->y + GRID_HEIGHT;
 	dlg->lname.x += ITEM_PADDING;
-	dlg->lname.flags = LABEL_NO_HCENTER;
+	dlg->lname.align = LABEL_ALIGN_LEFT;
 
 	/* Description label. */
 	dlg->fdesc.w = dlg->ldesc.w = LABEL_WIDTH;
@@ -204,7 +204,7 @@
 	dlg->fdesc.x = dlg->ldesc.x = dlg->y;
 	dlg->fdesc.y = dlg->ldesc.y = dlg->y + GRID_HEIGHT + (LABEL_HEIGHT / 2);
 	dlg->ldesc.x += ITEM_PADDING;
-	dlg->ldesc.flags = LABEL_NO_HCENTER;
+	dlg->ldesc.align = LABEL_ALIGN_LEFT;
 
 	/* Button sort. */
 	dlg->bsort.x = dlg->x;
--- a/libcore/core/label.c	Wed Oct 14 16:40:34 2020 +0200
+++ b/libcore/core/label.c	Wed Oct 14 18:11:38 2020 +0200
@@ -20,11 +20,16 @@
 
 #include "label.h"
 #include "theme.h"
+#include "trace.h"
 
 void
 label_draw(const struct label *label)
 {
 	assert(label);
+	assert(label->text);
+
+	if (label->w == 0 || label->h == 0)
+		trace("label %p has null dimensions", label);
 
 	theme_draw_label(label->theme, label);
 }
--- a/libcore/core/label.h	Wed Oct 14 16:40:34 2020 +0200
+++ b/libcore/core/label.h	Wed Oct 14 18:11:38 2020 +0200
@@ -30,10 +30,35 @@
  * \brief Label flags.
  */
 enum label_flags {
-	LABEL_NONE,                             /*!< No flags. */
-	LABEL_NO_SHADOW         = (1 << 0),     /*!< Disable shadow. */
-	LABEL_NO_VCENTER        = (1 << 1),     /*!< Disable vertical centering. */
-	LABEL_NO_HCENTER        = (1 << 2)      /*!< Disable horizontal centering. */
+	LABEL_FLAGS_NONE,                       /*!< No flags. */
+	LABEL_FLAGS_SHADOW      = (1 << 0),     /*!< Enable shadow. */
+};
+
+/**
+ * \brief Label alignment in bounding box.
+ *
+ * The alignment is described as following:
+ *
+ * ```
+ * +---------------------+
+ * | 1        2        3 |
+ * |                     |
+ * | 8        0        4 |
+ * |                     |
+ * | 7        6        5 |
+ * +---------------------+
+ * ```
+ */
+enum label_align {
+	LABEL_ALIGN_CENTER,             /*!< Align to the center (default). */
+	LABEL_ALIGN_TOP_LEFT,           /*!< Top left. */
+	LABEL_ALIGN_TOP,                /*!< Top center (aligned horizontally). */
+	LABEL_ALIGN_TOP_RIGHT,          /*!< Top right. */
+	LABEL_ALIGN_RIGHT,              /*!< Right (aligned vertically). */
+	LABEL_ALIGN_BOTTOM_RIGHT,       /*!< Bottom right. */
+	LABEL_ALIGN_BOTTOM,             /*!< Bottom (aligned horizontally). */
+	LABEL_ALIGN_BOTTOM_LEFT,        /*!< Bottom left. */
+	LABEL_ALIGN_LEFT                /*!< Left (aligned vertically). */
 };
 
 /**
@@ -46,7 +71,7 @@
 	unsigned int h;         /*!< (RW) Height. */
 	const char *text;       /*!< (RW, ref) Text to show. */
 	enum label_flags flags; /*!< (RW) Optional flags. */
-	unsigned long color;    /*!< (RW) Color to use (0 = use theme). */
+	enum label_align align; /*!< (RW) How to positionate label. */
 	struct theme *theme;    /*!< (RW, ref, optional) Theme to use. */
 };
 
--- a/libcore/core/message.c	Wed Oct 14 16:40:34 2020 +0200
+++ b/libcore/core/message.c	Wed Oct 14 18:11:38 2020 +0200
@@ -79,12 +79,14 @@
 static void
 draw_lines(const struct message *msg)
 {
-	struct theme *theme;
+	struct theme theme;
 	struct font *font;
 	unsigned int lineh;
 
-	theme = THEME(msg);
-	font = theme->fonts[THEME_FONT_INTERFACE];
+	/* Shallow copy theme to modify colors. */
+	memcpy(&theme, THEME(msg), sizeof (theme));
+
+	font = theme.fonts[THEME_FONT_INTERFACE];
 	lineh = font_height(font);
 
 	for (int i = 0; i < 6; ++i) {
@@ -92,20 +94,24 @@
 			continue;
 
 		struct label label = {
-			.x = theme->padding,
-			.y = theme->padding + (i * lineh),
-			.h = lineh,
-			.theme = msg->theme,
+			.y = i * lineh,
+			.w = msg->w,
+			.h = msg->h,
+			.theme = &theme,
 			.text = msg->text[i],
-			.flags = LABEL_NO_HCENTER
+			.align = LABEL_ALIGN_TOP_LEFT,
+			.flags = LABEL_FLAGS_SHADOW
 		};
 
 		/*
-		 * The function label_draw will normally use
-		 * THEME_FONT_INTERFACE so update its color if needed.
+		 * The function label_draw will use THEME_COLOR_NORMAL to draw
+		 * text and THEME_COLOR_SHADOW so if we have selected a line
+		 * we need to cheat the normal color.
 		 */
 		if (msg->flags & MESSAGE_FLAGS_QUESTION && msg->index == (unsigned int)i)
-			label.color = theme->colors[THEME_COLOR_SELECTED];
+			theme.colors[THEME_COLOR_NORMAL] = THEME(msg)->colors[THEME_COLOR_SELECTED];
+		else
+			theme.colors[THEME_COLOR_NORMAL] = THEME(msg)->colors[THEME_COLOR_NORMAL];
 
 		label_draw(&label);
 	}
--- a/libcore/core/theme.c	Wed Oct 14 16:40:34 2020 +0200
+++ b/libcore/core/theme.c	Wed Oct 14 18:11:38 2020 +0200
@@ -69,40 +69,81 @@
 static void
 draw_label(struct theme *t, const struct label *label)
 {
+	struct font *font;
 	struct texture tex;
-	int x = label->x, y = label->y;
-	int *px = &x, *py = &y;
+	int x, y, bx, by;
+	unsigned int tw, th, bw, bh;
+
+	/* Compute real box size according to padding. */
+	bx = label->x + t->padding;
+	by = label->y + t->padding;
+	bw = label->w - (t->padding * 2);
+	bh = label->h - (t->padding * 2);
+
+	/* Make a shallow copy of the interface font. */
+	font = t->fonts[THEME_FONT_INTERFACE];
+
+	/* Compute text size. */
+	if (!font_box(font, label->text, &tw, &th))
+		panic();
 
-	if (label->flags & LABEL_NO_HCENTER)
-		px = NULL;
-	if (label->flags & LABEL_NO_VCENTER)
-		py = NULL;
+	/* Compute position according to alignment and box. */
+	switch (label->align) {
+	case LABEL_ALIGN_CENTER:
+		maths_centerize(&x, &y, tw, th, bx, by, bw, bh);
+		break;
+	case LABEL_ALIGN_TOP_LEFT:
+		x = bx;
+		y = by;
+		break;
+	case LABEL_ALIGN_TOP:
+		maths_centerize(&x, NULL, tw, th, bx, by, bw, bh);
+		y = by;
+		break;
+	case LABEL_ALIGN_TOP_RIGHT:
+		x = bx + bw - tw;
+		y = by;
+		break;
+	case LABEL_ALIGN_RIGHT:
+		maths_centerize(NULL, &y, tw, th, bx, by, bw, bh);
+		x = bx + bw - tw;
+		break;
+	case LABEL_ALIGN_BOTTOM_RIGHT:
+		x = bx + bw - tw;
+		y = by + bh - th;
+		break;
+	case LABEL_ALIGN_BOTTOM:
+		maths_centerize(&x, NULL, tw, th, bx, by, bw, bh);
+		y = by + bh - th;
+		break;
+	case LABEL_ALIGN_BOTTOM_LEFT:
+		x = bx;
+		y = by + bh - th;
+		break;
+	case LABEL_ALIGN_LEFT:
+		maths_centerize(NULL, &y, tw, th, bx, by, bw, bh);
+		x = bx;
+	default:
+		break;
+	}
 
 	/* Shadow text, only if enabled. */
-	if (!(label->flags & LABEL_NO_SHADOW)) {
-		t->fonts[THEME_FONT_INTERFACE]->color = t->colors[THEME_COLOR_SHADOW];
+	if (label->flags & LABEL_FLAGS_SHADOW) {
+		font->color = t->colors[THEME_COLOR_SHADOW];
 
-		if (!font_render(t->fonts[THEME_FONT_INTERFACE], &tex, label->text))
+		if (!font_render(font, &tex, label->text))
 			panic();
 
-		maths_centerize(px, py, tex.w, tex.h,
-		    label->x, label->y, label->w, label->h);
-
 		texture_draw(&tex, x + 1, y + 1);
 		texture_finish(&tex);
 	}
 
 	/* Normal text. */
-	t->fonts[THEME_FONT_INTERFACE]->color = label->color
-		? label->color
-		: t->colors[THEME_COLOR_NORMAL];
+	font->color = t->colors[THEME_COLOR_NORMAL];
 
-	if (!font_render(t->fonts[THEME_FONT_INTERFACE], &tex, label->text))
+	if (!font_render(font, &tex, label->text))
 		panic();
 
-	maths_centerize(px, py, tex.w, tex.h,
-	    label->x, label->y, label->w, label->h);
-
 	texture_draw(&tex, x, y);
 	texture_finish(&tex);
 }
@@ -142,7 +183,7 @@
 
 		struct label label = {
 			.text = cb->label,
-			.flags = LABEL_NO_HCENTER,
+			.align = LABEL_ALIGN_LEFT,
 			.x = x,
 			.y = cb->y,
 			.w = w,