Mercurial > molko
view src/core/message.c @ 71:1aec066bcdae
core: allow quick message with MESSAGE_QUICK
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 28 Jan 2020 13:05:22 +0100 |
parents | 7187c0d9b9c0 |
children | ac97053d8e2e |
line wrap: on
line source
/* * message.c -- message dialog * * 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 <assert.h> #include <stdlib.h> #include <string.h> #include "action.h" #include "event.h" #include "font.h" #include "message.h" #include "painter.h" #include "sprite.h" #include "texture.h" #include "util.h" #include "window.h" #define MESSAGE_SPEED 200 /* Time delay for animations */ #define MESSAGE_TIMEOUT 5000 /* Time for auto-closing */ static void action_handle(struct action *action, const union event *ev) { assert(action); assert(ev); message_handle(action->data, ev); } static bool action_update(struct action *action, unsigned int ticks) { assert(action); return message_update(action->data, ticks); } static void action_draw(struct action *action) { assert(action); message_draw(action->data); } static void action_finish(struct action *action) { assert(action); message_finish(action->data); free(action->data); } static unsigned int average(const struct message *msg) { unsigned int n = 0; unsigned int total = 0; for (int i = 0; i < 6; ++i) { if (msg->textures[i]) { n += 1; total += texture_height(msg->textures[i]); } } return n > 0 ? total / n : 0; } static void clear(struct message *msg) { for (unsigned int i = 0; i < 12; ++i) { if (msg->textures[i]) { texture_close(msg->textures[i]); msg->textures[i] = NULL; } } } static void redraw(struct message *msg) { clear(msg); /* Generate textures if not already done. */ for (unsigned int i = 0; i < 6; ++i) { if (!msg->text[i]) continue; /* Normal lines of text. */ unsigned long color = msg->colors[0]; if (msg->flags & MESSAGE_QUESTION && msg->index == i) color = msg->colors[1]; if (!msg->textures[i]) msg->textures[i] = font_render( msg->font, msg->text[i], color ); if (!msg->textures[i + 6]) msg->textures[i + 6] = font_render( msg->font, msg->text[i], 0x000000ff ); } } void message_start(struct message *msg) { assert(msg); msg->elapsed = 0; msg->state = msg->flags & MESSAGE_QUICK ? MESSAGE_SHOWING : MESSAGE_OPENING; msg->height[0] = texture_height(msg->frame); msg->height[1] = 0; redraw(msg); } void message_handle(struct message *msg, const union event *ev) { assert(msg); assert(ev); if (ev->type != EVENT_KEYDOWN || msg->state == MESSAGE_NONE) return; switch (ev->key.key) { case KEY_UP: if (msg->index > 0) msg->index--; break; case KEY_DOWN: if (msg->index < 5 && msg->text[msg->index + 1]) msg->index++; break; case KEY_ENTER: msg->state = msg->flags & MESSAGE_QUICK ? MESSAGE_HIDING : MESSAGE_NONE; msg->elapsed = 0; break; default: break; } redraw(msg); } bool message_update(struct message *msg, unsigned int ticks) { assert(msg); msg->elapsed += ticks; switch (msg->state) { case MESSAGE_OPENING: msg->height[1] += texture_height(msg->frame) * ticks / MESSAGE_SPEED; if (msg->height[1] > msg->height[0]) msg->height[1] = msg->height[0]; if (msg->elapsed >= MESSAGE_SPEED) { msg->state = MESSAGE_SHOWING; msg->elapsed = 0; } break; case MESSAGE_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; msg->elapsed = 0; } break; case MESSAGE_HIDING: msg->height[1] -= texture_height(msg->frame) * ticks / MESSAGE_SPEED; if (msg->elapsed >= MESSAGE_SPEED) { msg->state = MESSAGE_NONE; msg->elapsed = 0; } break; default: break; } return msg->state == MESSAGE_NONE; } void message_draw(struct message *msg) { assert(msg); assert(msg->frame); const unsigned int w = texture_width(msg->frame); const unsigned int h = texture_height(msg->frame); const unsigned int x = (window_width() / 2) - (w / 2); const unsigned int y = 80; const unsigned int avgh = average(msg); const unsigned int gapy = (h - (avgh * 6)) / 7; switch (msg->state) { case MESSAGE_OPENING: case MESSAGE_HIDING: texture_draw_ex(msg->frame, 0, 0, w, msg->height[1], x, y, w, msg->height[1], 0); break; case MESSAGE_SHOWING: texture_draw(msg->frame, x, y); for (int i = 0; i < 6; ++i) { /* TODO: avatar handling */ const int real_x = x + 20; const int real_y = y + ((i + 1) * gapy) + (i * avgh); if (!msg->textures[i]) continue; texture_draw(msg->textures[i + 6], real_x + 2, real_y + 2); texture_draw(msg->textures[i], real_x, real_y); } break; default: break; } } void message_hide(struct message *msg) { assert(msg); msg->state = MESSAGE_HIDING; msg->elapsed = 0; } void message_finish(struct message *msg) { assert(msg); clear(msg); } void message_action(const struct message *msg, struct action *action) { assert(msg); assert(action); memset(action, 0, sizeof (struct action)); action->data = ememdup(msg, sizeof (struct message)); action->handle = action_handle; action->update = action_update; action->draw = action_draw; action->finish = action_finish; }