]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/label_layer.c
(#964) Make LabelLayer display the ids
[nothing.git] / src / game / level / level_editor / label_layer.c
1 #include <stdio.h>
2
3 #include <SDL.h>
4
5 #include "system/line_stream.h"
6 #include "system/stacktrace.h"
7 #include "system/nth_alloc.h"
8 #include "system/lt.h"
9 #include "system/str.h"
10 #include "system/log.h"
11 #include "math/point.h"
12 #include "label_layer.h"
13 #include "dynarray.h"
14 #include "color.h"
15 #include "game/camera.h"
16 #include "color_picker.h"
17 #include "ui/edit_field.h"
18
19 #define LABEL_LAYER_SELECTION_THICCNESS 5.0f
20
21 typedef enum {
22     LABEL_LAYER_IDLE = 0,
23     LABEL_LAYER_MOVE,
24     LABEL_LAYER_EDIT_TEXT
25 } LabelLayerState;
26
27 // TODO(#963): LabelLayer cannot add the labels
28 // TODO(#964): LabelLayer cannot modify the labels' id
29 struct LabelLayer {
30     Lt *lt;
31     LabelLayerState state;
32     Dynarray *ids;
33     Dynarray *positions;
34     Dynarray *colors;
35     Dynarray *texts;
36     int selected;
37     ColorPicker color_picker;
38     Point move_anchor;
39     Edit_field *edit_field;
40 };
41
42 LayerPtr label_layer_as_layer(LabelLayer *label_layer)
43 {
44     LayerPtr layer = {
45         .ptr = label_layer,
46         .type = LAYER_LABEL
47     };
48     return layer;
49 }
50
51 LabelLayer *create_label_layer(void)
52 {
53     Lt *lt = create_lt();
54
55     LabelLayer *label_layer = PUSH_LT(
56         lt, nth_calloc(1, sizeof(LabelLayer)), free);
57     if (label_layer == NULL) {
58         RETURN_LT(lt, NULL);
59     }
60     label_layer->lt = lt;
61
62     label_layer->ids = PUSH_LT(
63         lt,
64         create_dynarray(sizeof(char) * LABEL_LAYER_ID_MAX_SIZE),
65         destroy_dynarray);
66     if (label_layer->ids == NULL) {
67         RETURN_LT(lt, NULL);
68     }
69
70     label_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray);
71     if (label_layer->positions == NULL) {
72         RETURN_LT(lt, NULL);
73     }
74
75     label_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
76     if (label_layer->colors == NULL) {
77         RETURN_LT(lt, NULL);
78     }
79
80     label_layer->texts = PUSH_LT(
81         lt,
82         create_dynarray(sizeof(char) * LABEL_LAYER_TEXT_MAX_SIZE),
83         destroy_dynarray);
84     if (label_layer->texts == NULL) {
85         RETURN_LT(lt, NULL);
86     }
87
88     label_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
89     label_layer->selected = -1;
90
91     label_layer->edit_field = PUSH_LT(
92         lt,
93         create_edit_field(LABELS_SIZE, COLOR_RED),
94         destroy_edit_field);
95     if (label_layer->edit_field == NULL) {
96         RETURN_LT(lt, NULL);
97     }
98
99     return label_layer;
100 }
101
102 LabelLayer *create_label_layer_from_line_stream(LineStream *line_stream)
103 {
104     trace_assert(line_stream);
105     LabelLayer *label_layer = create_label_layer();
106
107     if (label_layer == NULL) {
108         RETURN_LT(label_layer->lt, NULL);
109     }
110
111     const char *line = line_stream_next(line_stream);
112     if (line == NULL) {
113         log_fail("Could not read amount of labels\n");
114         RETURN_LT(label_layer->lt, NULL);
115     }
116
117     size_t n = 0;
118     if (sscanf(line, "%zu", &n) == EOF) {
119         log_fail("Could not parse amount of labels\n");
120         RETURN_LT(label_layer->lt, NULL);
121     }
122
123     for (size_t i = 0; i < n; ++i) {
124         char hex[7];
125         char id[LABEL_LAYER_ID_MAX_SIZE];
126         Point position;
127
128         line = line_stream_next(line_stream);
129         if (line == NULL) {
130             log_fail("Could not read label meta info\n");
131             RETURN_LT(label_layer->lt, NULL);
132         }
133
134         if (sscanf(
135                 line,
136                 "%"STRINGIFY(LABEL_LAYER_ID_MAX_SIZE)"s%f%f%6s\n",
137                 id, &position.x, &position.y, hex) == EOF) {
138             log_fail("Could not parse label meta info\n");
139             RETURN_LT(label_layer->lt, NULL);
140         }
141
142         Color color = hexstr(hex);
143
144         dynarray_push(label_layer->ids, id);
145         dynarray_push(label_layer->positions, &position);
146         dynarray_push(label_layer->colors, &color);
147
148         line = line_stream_next(line_stream);
149         if (line == NULL) {
150             log_fail("Could not read label text\n");
151         }
152
153         char label_text[LABEL_LAYER_TEXT_MAX_SIZE] = {0};
154         memcpy(label_text, line, LABEL_LAYER_TEXT_MAX_SIZE - 1);
155         trim_endline(label_text);
156         dynarray_push(label_layer->texts, &label_text);
157     }
158
159     return label_layer;
160 }
161
162 void destroy_label_layer(LabelLayer *label_layer)
163 {
164     trace_assert(label_layer);
165     destroy_lt(label_layer->lt);
166 }
167
168 int label_layer_render(const LabelLayer *label_layer,
169                        Camera *camera,
170                        int active)
171 {
172     trace_assert(label_layer);
173     trace_assert(camera);
174
175     if (active && color_picker_render(&label_layer->color_picker, camera) < 0) {
176         return -1;
177     }
178
179     size_t n = dynarray_count(label_layer->ids);
180     char *ids = dynarray_data(label_layer->ids);
181     Point *positions = dynarray_data(label_layer->positions);
182     Color *colors = dynarray_data(label_layer->colors);
183     char *texts = dynarray_data(label_layer->texts);
184
185     /* TODO(#891): LabelLayer doesn't show the final position of Label after the animation */
186     for (size_t i = 0; i < n; ++i) {
187         if (label_layer->state == LABEL_LAYER_EDIT_TEXT) {
188             if (edit_field_render_world(
189                     label_layer->edit_field,
190                     camera,
191                     positions[i]) < 0) {
192                 return -1;
193             }
194         } else {
195             if (camera_render_text(
196                     camera,
197                     texts + i * LABEL_LAYER_TEXT_MAX_SIZE,
198                     LABELS_SIZE,
199                     color_scale(
200                         colors[i],
201                         rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
202                     positions[i]) < 0) {
203                 return -1;
204             }
205         }
206
207         if (camera_render_text(
208                 camera,
209                 ids + i * LABEL_LAYER_ID_MAX_SIZE,
210                 vec(1.0f, 1.0f),
211                 color_scale(
212                     color_invert(colors[i]),
213                     rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
214                 vec_sub(positions[i], vec(0.0f, FONT_CHAR_HEIGHT))) < 0) {
215             return -1;
216         }
217     }
218
219     if (label_layer->selected >= 0) {
220
221         Rect selection =
222             rect_scale(
223                 camera_rect(
224                     camera,
225                     sprite_font_boundary_box(
226                         camera_font(camera),
227                         positions[label_layer->selected],
228                         LABELS_SIZE,
229                         texts + label_layer->selected * LABEL_LAYER_TEXT_MAX_SIZE)),
230                 LABEL_LAYER_SELECTION_THICCNESS * 0.5f);
231
232
233         if (camera_draw_thicc_rect_screen(
234                 camera,
235                 selection,
236                 colors[label_layer->selected],
237                 LABEL_LAYER_SELECTION_THICCNESS) < 0) {
238             return -1;
239         }
240     }
241
242     return 0;
243 }
244
245 static
246 int label_layer_element_at(LabelLayer *label_layer,
247                            const Sprite_font *font,
248                            Point position)
249 {
250     trace_assert(label_layer);
251
252     const size_t n = dynarray_count(label_layer->texts);
253     char *texts = dynarray_data(label_layer->texts);
254     Point *positions = dynarray_data(label_layer->positions);
255
256     for (size_t i = 0; i < n; ++i) {
257         Rect boundary = sprite_font_boundary_box(
258             font,
259             positions[i],
260             LABELS_SIZE,
261             texts + i * LABEL_LAYER_TEXT_MAX_SIZE);
262
263         if (rect_contains_point(boundary, position)) {
264             return (int) i;
265         }
266     }
267
268     return -1;
269 }
270
271 static
272 int label_layer_idle_event(LabelLayer *label_layer,
273                            const SDL_Event *event,
274                            const Camera *camera)
275 {
276     trace_assert(label_layer);
277     trace_assert(event);
278     trace_assert(camera);
279
280     switch (event->type) {
281     case SDL_MOUSEBUTTONDOWN: {
282         switch (event->button.button) {
283         case SDL_BUTTON_LEFT: {
284             const Point position = camera_map_screen(
285                 camera,
286                 event->button.x,
287                 event->button.y);
288
289             const int element = label_layer_element_at(
290                 label_layer,
291                 camera_font(camera),
292                 position);
293
294             if (element >= 0) {
295                 Point *positions = dynarray_data(label_layer->positions);
296                 Color *colors = dynarray_data(label_layer->colors);
297
298                 label_layer->move_anchor = vec_sub(position, positions[element]);
299                 label_layer->selected = element;
300                 label_layer->state = LABEL_LAYER_MOVE;
301
302                 label_layer->color_picker =
303                     create_color_picker_from_rgba(colors[element]);
304             }
305         } break;
306         }
307     } break;
308
309     case SDL_KEYDOWN: {
310         switch (event->key.keysym.sym) {
311         case SDLK_F2: {
312             if (label_layer->selected >= 0) {
313                 char *texts = dynarray_data(label_layer->texts);
314                 label_layer->state = LABEL_LAYER_EDIT_TEXT;
315                 edit_field_replace(
316                     label_layer->edit_field,
317                     texts + label_layer->selected * LABEL_LAYER_TEXT_MAX_SIZE);
318                 SDL_StartTextInput();
319             }
320         } break;
321         }
322     } break;
323     }
324
325     return 0;
326 }
327
328 static
329 int label_layer_move_event(LabelLayer *label_layer,
330                            const SDL_Event *event,
331                            const Camera *camera)
332 {
333     trace_assert(label_layer);
334     trace_assert(event);
335     trace_assert(camera);
336     trace_assert(label_layer->selected >= 0);
337
338     switch (event->type) {
339     case SDL_MOUSEMOTION: {
340         Point *positions = dynarray_data(label_layer->positions);
341         positions[label_layer->selected] =
342             vec_sub(
343                 camera_map_screen(
344                     camera,
345                     event->motion.x,
346                     event->motion.y),
347                 label_layer->move_anchor);
348     } break;
349
350     case SDL_MOUSEBUTTONUP: {
351         switch (event->button.button) {
352         case SDL_BUTTON_LEFT: {
353             label_layer->state = LABEL_LAYER_IDLE;
354         } break;
355         }
356     } break;
357     }
358
359     return 0;
360 }
361
362 static
363 int label_layer_edit_text_event(LabelLayer *label_layer,
364                                 const SDL_Event *event,
365                                 const Camera *camera)
366 {
367     trace_assert(label_layer);
368     trace_assert(event);
369     trace_assert(camera);
370     trace_assert(label_layer->selected >= 0);
371
372     switch (event->type) {
373     case SDL_KEYDOWN: {
374         if (event->key.keysym.sym == SDLK_RETURN) {
375             char *text =
376                 (char*)dynarray_data(label_layer->texts) + label_layer->selected * LABEL_LAYER_TEXT_MAX_SIZE;
377             memset(text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
378             memcpy(text, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_TEXT_MAX_SIZE - 1);
379             label_layer->state = LABEL_LAYER_IDLE;
380             return 0;
381         }
382     } break;
383     }
384
385     return edit_field_event(label_layer->edit_field, event);
386 }
387
388 int label_layer_event(LabelLayer *label_layer,
389                       const SDL_Event *event,
390                       const Camera *camera)
391 {
392     trace_assert(label_layer);
393     trace_assert(event);
394     trace_assert(camera);
395
396     int changed = 0;
397
398     if (color_picker_event(
399             &label_layer->color_picker,
400             event,
401             &changed) < 0) {
402         return -1;
403     }
404
405     if (changed && label_layer->selected >= 0) {
406         Color *colors = dynarray_data(label_layer->colors);
407         colors[label_layer->selected] =
408             color_picker_rgba(&label_layer->color_picker);
409     }
410
411     switch (label_layer->state) {
412     case LABEL_LAYER_IDLE:
413         return label_layer_idle_event(label_layer, event, camera);
414
415     case LABEL_LAYER_MOVE:
416         return label_layer_move_event(label_layer, event, camera);
417
418     case LABEL_LAYER_EDIT_TEXT:
419         return label_layer_edit_text_event(label_layer, event, camera);
420     }
421
422     return 0;
423 }
424
425 size_t label_layer_count(const LabelLayer *label_layer)
426 {
427     return dynarray_count(label_layer->ids);
428 }
429
430 char *label_layer_ids(const LabelLayer *label_layer)
431 {
432     return dynarray_data(label_layer->ids);
433 }
434
435 Point *label_layer_positions(const LabelLayer *label_layer)
436 {
437     return dynarray_data(label_layer->positions);
438 }
439
440 Color *label_layer_colors(const LabelLayer *label_layer)
441 {
442     return dynarray_data(label_layer->colors);
443 }
444
445 char *labels_layer_texts(const LabelLayer *label_layer)
446 {
447     return dynarray_data(label_layer->texts);
448 }
449
450 int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump)
451 {
452     trace_assert(label_layer);
453     trace_assert(filedump);
454
455     size_t n = dynarray_count(label_layer->ids);
456     char *ids = dynarray_data(label_layer->ids);
457     Point *positions = dynarray_data(label_layer->positions);
458     Color *colors = dynarray_data(label_layer->colors);
459     char *texts = dynarray_data(label_layer->texts);
460
461     fprintf(filedump, "%zd\n", n);
462     for (size_t i = 0; i < n; ++i) {
463         fprintf(filedump, "%s %f %f ",
464                 ids + LABEL_LAYER_ID_MAX_SIZE * i,
465                 positions[i].x, positions[i].y);
466         color_hex_to_stream(colors[i], filedump);
467         fprintf(filedump, "\n%s\n", texts + i * LABEL_LAYER_TEXT_MAX_SIZE);
468     }
469
470     return 0;
471 }