# HG changeset patch # User David Demelier # Date 1602866645 -7200 # Node ID e05a792f69107b04db7dedd1c95c7fb04a8ede21 # Parent e8d2740703df06a1239704bdefe2b8ac30042e89 ui: make message less clever and provide spacing field diff -r e8d2740703df -r e05a792f6910 examples/example-message.c --- a/examples/example-message.c Fri Oct 16 17:45:39 2020 +0200 +++ b/examples/example-message.c Fri Oct 16 18:44:05 2020 +0200 @@ -112,13 +112,15 @@ .x = MX, .y = MY, .w = MW, - .h = MH, + .spacing = 12, .text = { "This is a basic message.", - "You need to press to close it." + "Vertical spacing is automatically computed.", + "You need to press to close it.", }, }; + message_query(&msg, NULL, &msg.h); run(&msg); } @@ -129,7 +131,6 @@ .x = MX, .y = MY, .w = MW, - .h = MH, .timeout = MESSAGE_TIMEOUT_DEFAULT, .text = { "This is a an automatic message.", @@ -139,6 +140,7 @@ .flags = MESSAGE_FLAGS_AUTOMATIC }; + message_query(&msg, NULL, &msg.h); run(&msg); } @@ -149,7 +151,6 @@ .x = MX, .y = MY, .w = MW, - .h = MH, .delay = MESSAGE_DELAY_DEFAULT, .text = { "This message will fade in." @@ -157,6 +158,7 @@ .flags = MESSAGE_FLAGS_FADEIN }; + message_query(&msg, NULL, &msg.h); run(&msg); } @@ -167,7 +169,6 @@ .x = MX, .y = MY, .w = MW, - .h = MH, .delay = MESSAGE_DELAY_DEFAULT, .text = { "This message will fade out." @@ -175,6 +176,7 @@ .flags = MESSAGE_FLAGS_FADEOUT }; + message_query(&msg, NULL, &msg.h); run(&msg); } @@ -185,7 +187,6 @@ .x = MX, .y = MY, .w = MW, - .h = MH, .delay = MESSAGE_DELAY_DEFAULT, .text = { "This message will fade in and out." @@ -193,6 +194,7 @@ .flags = MESSAGE_FLAGS_FADEIN | MESSAGE_FLAGS_FADEOUT }; + message_query(&msg, NULL, &msg.h); run(&msg); } @@ -203,7 +205,6 @@ .x = MX, .y = MY, .w = MW, - .h = MH, .text = { "Okay, I've understood.", "Nevermind, I'll do it again." @@ -211,6 +212,7 @@ .flags = MESSAGE_FLAGS_QUESTION }; + message_query(&msg, NULL, &msg.h); run(&msg); } @@ -244,9 +246,10 @@ .x = MX, .y = MY, .w = MW, - .h = 50, + .h = 40, .text = { "This one is too small in height and will emit a warning.", + "Because this line will be incomplete." }, }; @@ -293,23 +296,6 @@ run(&msg); } -static void -large(void) -{ - struct message msg = { - .x = MX, - .y = MY, - .w = 500, - .h = 500, - .text = { - "And this one is terribly large.", - "So lines will have large spacing." - }, - }; - - run(&msg); -} - int main(int argc, char **argv) { @@ -327,6 +313,5 @@ toosmallh(); toosmallw(); custom(); - large(); quit(); } diff -r e8d2740703df -r e05a792f6910 libcore/core/font.c --- a/libcore/core/font.c Fri Oct 16 17:45:39 2020 +0200 +++ b/libcore/core/font.c Fri Oct 16 18:44:05 2020 +0200 @@ -111,7 +111,7 @@ } bool -font_box(const struct font *font, const char *text, unsigned int *w, unsigned int *h) +font_query(const struct font *font, const char *text, unsigned int *w, unsigned int *h) { assert(font_ok(font)); assert(text); diff -r e8d2740703df -r e05a792f6910 libcore/core/font.h --- a/libcore/core/font.h Fri Oct 16 17:45:39 2020 +0200 +++ b/libcore/core/font.h Fri Oct 16 18:44:05 2020 +0200 @@ -140,7 +140,7 @@ * \return false in case of error and pointers to w and h are left unmodified */ bool -font_box(const struct font *font, const char *text, unsigned int *w, unsigned int *h); +font_query(const struct font *font, const char *text, unsigned int *w, unsigned int *h); /** * Close the font. diff -r e8d2740703df -r e05a792f6910 librpg/rpg/message.c --- a/librpg/rpg/message.c Fri Oct 16 17:45:39 2020 +0200 +++ b/librpg/rpg/message.c Fri Oct 16 18:44:05 2020 +0200 @@ -79,63 +79,61 @@ } static inline unsigned int -spacing(const struct message *msg, const struct theme *t, unsigned int fh) +min_width(const struct message *msg) { assert(msg); - assert(t); + + unsigned int maxw = 0, w = 0; - /* No vertical spacing if only one message. */ - if (MESSAGE_LINES_MAX <= 1) - return 0; + for (size_t i = 0; i < MESSAGE_LINES_MAX; ++i) { + if (!msg->text[i]) + continue; + if (!font_query(THEME(msg)->fonts[THEME_FONT_INTERFACE], msg->text[i], &w, NULL)) + panic(); + if (w > maxw) + maxw = w; + } - return ((msg->h - t->padding * 2) - (fh * MESSAGE_LINES_MAX)) / (MESSAGE_LINES_MAX - 1); + return (THEME(msg)->padding * 2) + maxw; } static inline unsigned int -height(const struct theme *t, unsigned int lvreq) +min_height(const struct message *msg) { - assert(t); + assert(msg); - return lvreq * MESSAGE_LINES_MAX; + const struct theme *th = THEME(msg); + const unsigned int lh = font_height(th->fonts[THEME_FONT_INTERFACE]); + + return (th->padding * 2) + (MESSAGE_LINES_MAX * lh) + ((MESSAGE_LINES_MAX - 1) * msg->spacing); } static void draw_lines(const struct message *msg) { struct theme theme; - unsigned int lvreq, vreq, vspace = 0; + struct label label; + unsigned int lw, lh; /* Shallow copy theme to modify colors. */ theme_shallow(&theme, msg->theme); - /* - * Compute the maximum line height for the given font. - * - * Then, compute the minimum height required to draw the lines (without - * their vertical spacing). - */ - lvreq = font_height(theme.fonts[THEME_FONT_INTERFACE]); - vreq = height(&theme, lvreq); - - /* Now if enough space, compute the spacing between lines. */ - if (vreq > msg->h) - trace("message height is too small: %u < %u", msg->h, vreq); - else - vspace = spacing(msg, &theme, lvreq); - for (int i = 0; i < MESSAGE_LINES_MAX; ++i) { if (!msg->text[i]) continue; + if (!font_query(theme.fonts[THEME_FONT_INTERFACE], msg->text[i], &lw, &lh)) + panic(); - struct label label = { - .x = theme.padding, - .y = theme.padding + (i * (lvreq + vspace)), - .w = msg->w, - .h = msg->h, - .theme = &theme, - .text = msg->text[i], - .flags = LABEL_FLAGS_SHADOW - }; + label.theme = &theme; + label.x = theme.padding; + label.y = theme.padding + (i * (lh + msg->spacing)); + label.text = msg->text[i]; + label.flags = LABEL_FLAGS_SHADOW; + + if (label.x + lw > msg->w) + trace("message width too small: %u < %u", msg->w, min_width(msg)); + if (label.y + lh > msg->h) + trace("message height too small: %u < %u", msg->h, min_height(msg)); /* * The function label_draw will use THEME_COLOR_NORMAL to draw @@ -143,14 +141,9 @@ * we need to cheat the normal color. */ if (msg->flags & MESSAGE_FLAGS_QUESTION && msg->index == (unsigned int)i) - theme.colors[THEME_COLOR_NORMAL] = theme.colors[THEME_COLOR_SELECTED]; + theme.colors[THEME_COLOR_NORMAL] = THEME(msg)->colors[THEME_COLOR_SELECTED]; else - theme.colors[THEME_COLOR_NORMAL] = theme.colors[THEME_COLOR_NORMAL]; - - label_query(&label); - - if (label.w > msg->w) - trace("message width is too small: %u < %u", msg->w, label.w); + theme.colors[THEME_COLOR_NORMAL] = THEME(msg)->colors[THEME_COLOR_NORMAL]; label_draw(&label); } @@ -175,6 +168,17 @@ } void +message_query(const struct message *msg, unsigned int *w, unsigned int *h) +{ + assert(msg); + + if (w) + *w = min_width(msg); + if (h) + *h = min_height(msg); +} + +void message_handle(struct message *msg, const union event *ev) { assert(msg); @@ -265,6 +269,11 @@ int x, y; unsigned int w, h; + if (msg->w == 0 || msg->h == 0) { + trace("message has null dimensions"); + return; + } + if (!texture_new(&tex, msg->w, msg->h)) panic(); diff -r e8d2740703df -r e05a792f6910 librpg/rpg/message.h --- a/librpg/rpg/message.h Fri Oct 16 17:45:39 2020 +0200 +++ b/librpg/rpg/message.h Fri Oct 16 18:44:05 2020 +0200 @@ -26,11 +26,8 @@ * \ingroup drawing * * This module's purpose is to show a dialog box into the screen to show text - * and optionally ask the user a question. - * - * By itself, it is very low level and does not prevent other parts of the game - * to use the input so you probably need to inhibit input if your dialog is - * meant to be displayed on a map. + * and optionally ask the user a question. It is similar to what you're used to + * see in many RPGs. * * To use it use the following procedure: * @@ -50,17 +47,31 @@ * .text = { * "Hello, what's up?" * }, - * // This image will be shown on the left as user face. - * .avatar = mysuperavatar, - * // This should point to a image that is used as background. - * .frame = mysuperframe, - * // The first color is normal text, the second is for selected text - * // in case of question. - * .colors = { 0xffffffff, 0x0000ffff }, * // This indicates this message is a question. - * .flags = MESSAGE_QUESTION + * .flags = MESSAGE_FLAGS_QUESTION * }; * \endcode + * + * For performance reasons, flexibility and simplicity, the message box does + * not try to be clever about positions of lines. It will simply create an + * animation for opening the box, drawing the lines to the position according + * to the theme padding and spacing and interact with user. For convenience + * though, the \ref message_query can be used to determine dimensions required + * for a better final result. + * + * ## Example, computing the dimensions: + * + * \code + * // We create a message that we put on the center of the screen. + * struct message msg = { + * .text = { + * "Hi, have you tried turning it off and on again?" + * } + * }; + * + * message_query(&msg, &msg.w, &msg.h); + * align(ALIGN_CENTER, &msg.x, &msg.y, msg.w, msg.h, 0, 0, window.w, window.h); + * \endcode */ #include @@ -110,19 +121,16 @@ /** * \brief Message object. - * - * This structure is used to display a message into the screen. It does not own - * any user properties and therefore must exist while using it. */ struct message { int x; /*!< (+) Position in x. */ int y; /*!< (+) Position in y. */ unsigned int w; /*!< (+) Width. */ unsigned int h; /*!< (+) Height. */ + unsigned int spacing; /*!< (+) Spacing between lines. */ unsigned int delay; /*!< (+) Delay for animations. */ unsigned int timeout; /*!< (+) Timeout in milliseconds. */ const char *text[MESSAGE_LINES_MAX]; /*!< (+) Lines of text to show. */ - struct texture *avatar; /*!< (+&?) Avatar face. */ unsigned int index; /*!< (+) Line selected */ enum message_flags flags; /*!< (+) Message flags */ enum message_state state; /*!< (-) Current state */ @@ -144,6 +152,17 @@ message_start(struct message *msg); /** + * Compute the minimal message dimensions required. + * + * \pre msg != NULL + * \param msg the message to query + * \param w the pointer to width (may be NULL) + * \param h the pointer to height (may be NULL) + */ +void +message_query(const struct message *msg, unsigned int *w, unsigned int *h); + +/** * Handle input events. * * This function will alter state of the message and change its selection in diff -r e8d2740703df -r e05a792f6910 libui/ui/label.c --- a/libui/ui/label.c Fri Oct 16 17:45:39 2020 +0200 +++ b/libui/ui/label.c Fri Oct 16 18:44:05 2020 +0200 @@ -73,7 +73,7 @@ struct theme *t = label->theme ? label->theme : theme_default(); - if (!font_box(t->fonts[THEME_FONT_INTERFACE], label->text, &label->w, &label->h)) + if (!font_query(t->fonts[THEME_FONT_INTERFACE], label->text, &label->w, &label->h)) panic(); }