Mercurial > molko
comparison librpg/rpg/message.c @ 148:c577c15df07f
misc: split libraries, closes #2496
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 15 Oct 2020 10:32:18 +0200 |
parents | libcore/core/message.c@7d7ea7a9cf50 |
children | 9733d379be89 |
comparison
equal
deleted
inserted
replaced
147:b386d25832c8 | 148:c577c15df07f |
---|---|
1 /* | |
2 * message.c -- message dialog | |
3 * | |
4 * Copyright (c) 2020 David Demelier <markand@malikania.fr> | |
5 * | |
6 * Permission to use, copy, modify, and/or distribute this software for any | |
7 * purpose with or without fee is hereby granted, provided that the above | |
8 * copyright notice and this permission notice appear in all copies. | |
9 * | |
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 */ | |
18 | |
19 #include <assert.h> | |
20 #include <stdlib.h> | |
21 #include <string.h> | |
22 | |
23 #include <core/action.h> | |
24 #include <core/event.h> | |
25 #include <core/font.h> | |
26 #include <core/maths.h> | |
27 #include <core/painter.h> | |
28 #include <core/panic.h> | |
29 #include <core/sprite.h> | |
30 #include <core/trace.h> | |
31 #include <core/util.h> | |
32 | |
33 #include <ui/frame.h> | |
34 #include <ui/label.h> | |
35 #include <ui/theme.h> | |
36 | |
37 #include "message.h" | |
38 | |
39 #define THEME(msg) (msg->theme ? msg->theme : theme_default()) | |
40 | |
41 static void | |
42 handle(struct action *action, const union event *ev) | |
43 { | |
44 assert(action); | |
45 assert(ev); | |
46 | |
47 message_handle(action->data, ev); | |
48 } | |
49 | |
50 static bool | |
51 update(struct action *action, unsigned int ticks) | |
52 { | |
53 assert(action); | |
54 | |
55 return message_update(action->data, ticks); | |
56 } | |
57 | |
58 static void | |
59 draw(struct action *action) | |
60 { | |
61 assert(action); | |
62 | |
63 message_draw(action->data); | |
64 } | |
65 | |
66 static void | |
67 draw_frame(const struct message *msg) | |
68 { | |
69 assert(msg); | |
70 | |
71 struct frame frame = { | |
72 .w = msg->w, | |
73 .h = msg->h, | |
74 .theme = msg->theme | |
75 }; | |
76 | |
77 frame_draw(&frame); | |
78 } | |
79 | |
80 static void | |
81 draw_lines(const struct message *msg) | |
82 { | |
83 struct theme theme; | |
84 unsigned int lineh; | |
85 | |
86 /* Shallow copy theme to modify colors. */ | |
87 theme_shallow(&theme, msg->theme); | |
88 | |
89 /* Compute text size for list alignment. */ | |
90 lineh = font_height(theme.fonts[THEME_FONT_INTERFACE]); | |
91 | |
92 for (int i = 0; i < 6; ++i) { | |
93 if (!msg->text[i]) | |
94 continue; | |
95 | |
96 struct label label = { | |
97 .y = i * lineh, | |
98 .w = msg->w, | |
99 .h = msg->h, | |
100 .theme = &theme, | |
101 .text = msg->text[i], | |
102 .align = LABEL_ALIGN_TOP_LEFT, | |
103 .flags = LABEL_FLAGS_SHADOW | |
104 }; | |
105 | |
106 /* | |
107 * The function label_draw will use THEME_COLOR_NORMAL to draw | |
108 * text and THEME_COLOR_SHADOW so if we have selected a line | |
109 * we need to cheat the normal color. | |
110 */ | |
111 if (msg->flags & MESSAGE_FLAGS_QUESTION && msg->index == (unsigned int)i) | |
112 theme.colors[THEME_COLOR_NORMAL] = THEME(msg)->colors[THEME_COLOR_SELECTED]; | |
113 else | |
114 theme.colors[THEME_COLOR_NORMAL] = THEME(msg)->colors[THEME_COLOR_NORMAL]; | |
115 | |
116 label_draw(&label); | |
117 } | |
118 } | |
119 | |
120 void | |
121 message_start(struct message *msg) | |
122 { | |
123 assert(msg); | |
124 | |
125 if (msg->flags & (MESSAGE_FLAGS_FADEIN|MESSAGE_FLAGS_FADEOUT)) | |
126 assert(msg->delay > 0); | |
127 | |
128 msg->elapsed = 0; | |
129 msg->scale = msg->flags & MESSAGE_FLAGS_FADEIN ? 0.0 : 1.0; | |
130 msg->state = msg->flags & MESSAGE_FLAGS_FADEIN | |
131 ? MESSAGE_STATE_OPENING | |
132 : MESSAGE_STATE_SHOWING; | |
133 | |
134 if (msg->flags & MESSAGE_FLAGS_AUTOMATIC && msg->timeout == 0) | |
135 trace("message is automatic but has zero timeout"); | |
136 } | |
137 | |
138 void | |
139 message_handle(struct message *msg, const union event *ev) | |
140 { | |
141 assert(msg); | |
142 assert(ev); | |
143 | |
144 /* Skip if the message animation hasn't complete. */ | |
145 if (msg->state != MESSAGE_STATE_SHOWING) | |
146 return; | |
147 | |
148 /* Only keyboard event are valid. */ | |
149 if (ev->type != EVENT_KEYDOWN || msg->state == MESSAGE_STATE_NONE) | |
150 return; | |
151 | |
152 switch (ev->key.key) { | |
153 case KEY_UP: | |
154 if (msg->index > 0) | |
155 msg->index--; | |
156 break; | |
157 case KEY_DOWN: | |
158 if (msg->index < 5 && msg->text[msg->index + 1]) | |
159 msg->index++; | |
160 break; | |
161 case KEY_ENTER: | |
162 msg->state = msg->flags & MESSAGE_FLAGS_FADEOUT | |
163 ? MESSAGE_STATE_HIDING | |
164 : MESSAGE_STATE_NONE; | |
165 msg->elapsed = 0; | |
166 break; | |
167 default: | |
168 break; | |
169 } | |
170 } | |
171 | |
172 bool | |
173 message_update(struct message *msg, unsigned int ticks) | |
174 { | |
175 assert(msg); | |
176 | |
177 msg->elapsed += ticks; | |
178 | |
179 switch (msg->state) { | |
180 case MESSAGE_STATE_OPENING: | |
181 msg->scale = (double)msg->elapsed / (double)msg->delay; | |
182 | |
183 if (msg->scale > 1) | |
184 msg->scale = 1; | |
185 | |
186 if (msg->elapsed >= msg->delay) { | |
187 msg->state = MESSAGE_STATE_SHOWING; | |
188 msg->elapsed = 0; | |
189 } | |
190 | |
191 break; | |
192 case MESSAGE_STATE_SHOWING: | |
193 /* Do automatically switch state if requested by the user. */ | |
194 if (msg->flags & MESSAGE_FLAGS_AUTOMATIC && msg->elapsed >= msg->timeout) { | |
195 msg->state = msg->flags & MESSAGE_FLAGS_FADEOUT | |
196 ? MESSAGE_STATE_HIDING | |
197 : MESSAGE_STATE_NONE; | |
198 msg->elapsed = 0; | |
199 } | |
200 | |
201 break; | |
202 case MESSAGE_STATE_HIDING: | |
203 msg->scale = 1 - (double)msg->elapsed / (double)msg->delay; | |
204 | |
205 if (msg->scale < 0) | |
206 msg->scale = 0; | |
207 if (msg->elapsed >= msg->delay) { | |
208 msg->state = MESSAGE_STATE_NONE; | |
209 msg->elapsed = 0; | |
210 } | |
211 | |
212 break; | |
213 default: | |
214 break; | |
215 } | |
216 | |
217 return msg->state == MESSAGE_STATE_NONE; | |
218 } | |
219 | |
220 void | |
221 message_draw(struct message *msg) | |
222 { | |
223 assert(msg); | |
224 | |
225 struct texture tex; | |
226 int x, y; | |
227 unsigned int w, h; | |
228 | |
229 if (!texture_new(&tex, msg->w, msg->h)) | |
230 panic(); | |
231 | |
232 PAINTER_BEGIN(&tex); | |
233 draw_frame(msg); | |
234 draw_lines(msg); | |
235 PAINTER_END(); | |
236 | |
237 /* Compute scaling. */ | |
238 w = msg->w * msg->scale; | |
239 h = msg->h * msg->scale; | |
240 | |
241 /* Centerize within its drawing area. */ | |
242 maths_centerize(&x, &y, w, h, msg->x, msg->y, msg->w, msg->h); | |
243 | |
244 /* Draw and clear. */ | |
245 texture_scale(&tex, 0, 0, msg->w, msg->h, x, y, w, h, 0); | |
246 texture_finish(&tex); | |
247 } | |
248 | |
249 void | |
250 message_hide(struct message *msg) | |
251 { | |
252 assert(msg); | |
253 | |
254 msg->state = MESSAGE_STATE_HIDING; | |
255 msg->elapsed = 0; | |
256 } | |
257 | |
258 void | |
259 message_action(struct message *msg, struct action *action) | |
260 { | |
261 assert(msg); | |
262 assert(action); | |
263 | |
264 memset(action, 0, sizeof (struct action)); | |
265 action->data = msg; | |
266 action->handle = handle; | |
267 action->update = update; | |
268 action->draw = draw; | |
269 | |
270 message_start(msg); | |
271 } |