]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/label_layer.c
(#824) Introduce UndoHistory
[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     LABEL_LAYER_EDIT_ID
26 } LabelLayerState;
27
28 struct LabelLayer {
29     Lt *lt;
30     LabelLayerState state;
31     Dynarray *ids;
32     Dynarray *positions;
33     Dynarray *colors;
34     Dynarray *texts;
35     int selected;
36     ColorPicker color_picker;
37     Point move_anchor;
38     Edit_field *edit_field;
39 };
40
41 LayerPtr label_layer_as_layer(LabelLayer *label_layer)
42 {
43     LayerPtr layer = {
44         .ptr = label_layer,
45         .type = LAYER_LABEL
46     };
47     return layer;
48 }
49
50 LabelLayer *create_label_layer(void)
51 {
52     Lt *lt = create_lt();
53
54     LabelLayer *label_layer = PUSH_LT(
55         lt, nth_calloc(1, sizeof(LabelLayer)), free);
56     if (label_layer == NULL) {
57         RETURN_LT(lt, NULL);
58     }
59     label_layer->lt = lt;
60
61     label_layer->ids = PUSH_LT(
62         lt,
63         create_dynarray(sizeof(char) * LABEL_LAYER_ID_MAX_SIZE),
64         destroy_dynarray);
65     if (label_layer->ids == NULL) {
66         RETURN_LT(lt, NULL);
67     }
68
69     label_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray);
70     if (label_layer->positions == NULL) {
71         RETURN_LT(lt, NULL);
72     }
73
74     label_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
75     if (label_layer->colors == NULL) {
76         RETURN_LT(lt, NULL);
77     }
78
79     label_layer->texts = PUSH_LT(
80         lt,
81         create_dynarray(sizeof(char) * LABEL_LAYER_TEXT_MAX_SIZE),
82         destroy_dynarray);
83     if (label_layer->texts == NULL) {
84         RETURN_LT(lt, NULL);
85     }
86
87     label_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
88     label_layer->selected = -1;
89
90     label_layer->edit_field = PUSH_LT(
91         lt,
92         create_edit_field(LABELS_SIZE, COLOR_RED),
93         destroy_edit_field);
94     if (label_layer->edit_field == NULL) {
95         RETURN_LT(lt, NULL);
96     }
97
98     return label_layer;
99 }
100
101 LabelLayer *create_label_layer_from_line_stream(LineStream *line_stream)
102 {
103     trace_assert(line_stream);
104     LabelLayer *label_layer = create_label_layer();
105
106     if (label_layer == NULL) {
107         RETURN_LT(label_layer->lt, NULL);
108     }
109
110     const char *line = line_stream_next(line_stream);
111     if (line == NULL) {
112         log_fail("Could not read amount of labels\n");
113         RETURN_LT(label_layer->lt, NULL);
114     }
115
116     size_t n = 0;
117     if (sscanf(line, "%zu", &n) == EOF) {
118         log_fail("Could not parse amount of labels\n");
119         RETURN_LT(label_layer->lt, NULL);
120     }
121
122     for (size_t i = 0; i < n; ++i) {
123         char hex[7];
124         char id[LABEL_LAYER_ID_MAX_SIZE];
125         Point position;
126
127         line = line_stream_next(line_stream);
128         if (line == NULL) {
129             log_fail("Could not read label meta info\n");
130             RETURN_LT(label_layer->lt, NULL);
131         }
132
133         if (sscanf(
134                 line,
135                 "%"STRINGIFY(LABEL_LAYER_ID_MAX_SIZE)"s%f%f%6s\n",
136                 id, &position.x, &position.y, hex) == EOF) {
137             log_fail("Could not parse label meta info\n");
138             RETURN_LT(label_layer->lt, NULL);
139         }
140
141         Color color = hexstr(hex);
142
143         dynarray_push(label_layer->ids, id);
144         dynarray_push(label_layer->positions, &position);
145         dynarray_push(label_layer->colors, &color);
146
147         line = line_stream_next(line_stream);
148         if (line == NULL) {
149             log_fail("Could not read label text\n");
150         }
151
152         char label_text[LABEL_LAYER_TEXT_MAX_SIZE] = {0};
153         memcpy(label_text, line, LABEL_LAYER_TEXT_MAX_SIZE - 1);
154         trim_endline(label_text);
155         dynarray_push(label_layer->texts, &label_text);
156     }
157
158     return label_layer;
159 }
160
161 void destroy_label_layer(LabelLayer *label_layer)
162 {
163     trace_assert(label_layer);
164     destroy_lt(label_layer->lt);
165 }
166
167 int label_layer_render(const LabelLayer *label_layer,
168                        Camera *camera,
169                        int active)
170 {
171     trace_assert(label_layer);
172     trace_assert(camera);
173
174     if (active && color_picker_render(&label_layer->color_picker, camera) < 0) {
175         return -1;
176     }
177
178     size_t n = dynarray_count(label_layer->ids);
179     char *ids = dynarray_data(label_layer->ids);
180     Point *positions = dynarray_data(label_layer->positions);
181     Color *colors = dynarray_data(label_layer->colors);
182     char *texts = dynarray_data(label_layer->texts);
183
184     /* TODO(#891): LabelLayer doesn't show the final position of Label after the animation */
185     for (size_t i = 0; i < n; ++i) {
186         if (label_layer->state == LABEL_LAYER_EDIT_TEXT && label_layer->selected == (int) i) {
187             if (edit_field_render_world(
188                     label_layer->edit_field,
189                     camera,
190                     positions[i]) < 0) {
191                 return -1;
192             }
193         } else {
194             if (camera_render_text(
195                     camera,
196                     texts + i * LABEL_LAYER_TEXT_MAX_SIZE,
197                     LABELS_SIZE,
198                     color_scale(
199                         colors[i],
200                         rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
201                     positions[i]) < 0) {
202                 return -1;
203             }
204         }
205
206         if (label_layer->state == LABEL_LAYER_EDIT_ID && label_layer->selected == (int)i) {
207             if (edit_field_render_world(
208                     label_layer->edit_field,
209                     camera,
210                     vec_sub(
211                         positions[i],
212                         vec(0.0f, FONT_CHAR_HEIGHT))) < 0) {
213                 return -1;
214             }
215         } else {
216             if (camera_render_text(
217                     camera,
218                     ids + i * LABEL_LAYER_ID_MAX_SIZE,
219                     vec(1.0f, 1.0f),
220                     color_scale(
221                         color_invert(colors[i]),
222                         rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
223                     vec_sub(positions[i], vec(0.0f, FONT_CHAR_HEIGHT))) < 0) {
224                 return -1;
225             }
226         }
227     }
228
229     if (label_layer->selected >= 0) {
230
231         Rect selection =
232             rect_scale(
233                 camera_rect(
234                     camera,
235                     rect_boundary2(
236                         sprite_font_boundary_box(
237                             camera_font(camera),
238                             positions[label_layer->selected],
239                             LABELS_SIZE,
240                             texts + label_layer->selected * LABEL_LAYER_TEXT_MAX_SIZE),
241                         sprite_font_boundary_box(
242                             camera_font(camera),
243                             vec_sub(
244                                 positions[label_layer->selected],
245                                 vec(0.0f, FONT_CHAR_HEIGHT)),
246                             vec(1.0f, 1.0f),
247                             ids + label_layer->selected * LABEL_LAYER_ID_MAX_SIZE))),
248                 LABEL_LAYER_SELECTION_THICCNESS * 0.5f);
249
250
251         if (camera_draw_thicc_rect_screen(
252                 camera,
253                 selection,
254                 colors[label_layer->selected],
255                 LABEL_LAYER_SELECTION_THICCNESS) < 0) {
256             return -1;
257         }
258     }
259
260     return 0;
261 }
262
263 static
264 int label_layer_element_at(LabelLayer *label_layer,
265                            const Sprite_font *font,
266                            Point position)
267 {
268     trace_assert(label_layer);
269
270     const size_t n = dynarray_count(label_layer->texts);
271     char *ids = dynarray_data(label_layer->ids);
272     char *texts = dynarray_data(label_layer->texts);
273     Point *positions = dynarray_data(label_layer->positions);
274
275     for (size_t i = 0; i < n; ++i) {
276         Rect boundary = rect_boundary2(
277             sprite_font_boundary_box(
278                 font,
279                 positions[i],
280                 LABELS_SIZE,
281                 texts + i * LABEL_LAYER_TEXT_MAX_SIZE),
282             sprite_font_boundary_box(
283                 font,
284                 vec_sub(
285                     positions[i],
286                     vec(0.0f, FONT_CHAR_HEIGHT)),
287                 vec(1.0f, 1.0f),
288                 ids + i * LABEL_LAYER_ID_MAX_SIZE));
289
290         if (rect_contains_point(boundary, position)) {
291             return (int) i;
292         }
293     }
294
295     return -1;
296 }
297
298 static
299 void label_layer_delete_nth_label(LabelLayer *label_layer,
300                                   size_t i)
301 {
302     trace_assert(label_layer);
303     dynarray_delete_at(label_layer->ids, i);
304     dynarray_delete_at(label_layer->positions, i);
305     dynarray_delete_at(label_layer->colors, i);
306     dynarray_delete_at(label_layer->texts, i);
307 }
308
309 static
310 int label_layer_add_label(LabelLayer *label_layer,
311                            Point position,
312                            Color color)
313 {
314     trace_assert(label_layer);
315
316     // TODO(#982): id generation code is duplicated in label_layer, point_layer and rect_layer
317     char id[LABEL_LAYER_ID_MAX_SIZE];
318     for (size_t i = 0; i < LABEL_LAYER_ID_MAX_SIZE - 1; ++i) {
319         id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
320     }
321     id[LABEL_LAYER_ID_MAX_SIZE - 1] = '\0';
322
323     size_t n = dynarray_count(label_layer->ids);
324
325     dynarray_push(label_layer->ids, id);
326     dynarray_push(label_layer->positions, &position);
327     dynarray_push(label_layer->colors, &color);
328     dynarray_push_empty(label_layer->texts);
329
330     return (int) n;
331 }
332
333 static
334 int label_layer_idle_event(LabelLayer *label_layer,
335                            const SDL_Event *event,
336                            const Camera *camera)
337 {
338     trace_assert(label_layer);
339     trace_assert(event);
340     trace_assert(camera);
341
342     Color *colors = dynarray_data(label_layer->colors);
343     Point *positions = dynarray_data(label_layer->positions);
344     char *ids = dynarray_data(label_layer->ids);
345     char *texts = dynarray_data(label_layer->texts);
346
347     switch (event->type) {
348     case SDL_MOUSEBUTTONDOWN: {
349         switch (event->button.button) {
350         case SDL_BUTTON_LEFT: {
351             const Point position = camera_map_screen(
352                 camera,
353                 event->button.x,
354                 event->button.y);
355
356             const int element = label_layer_element_at(
357                 label_layer,
358                 camera_font(camera),
359                 position);
360
361             if (element >= 0) {
362                 label_layer->move_anchor = vec_sub(position, positions[element]);
363                 label_layer->selected = element;
364                 label_layer->state = LABEL_LAYER_MOVE;
365
366                 label_layer->color_picker =
367                     create_color_picker_from_rgba(colors[element]);
368             } else {
369                 label_layer->selected = label_layer_add_label(
370                     label_layer,
371                     position,
372                     color_picker_rgba(
373                         &label_layer->color_picker));
374                 label_layer->state = LABEL_LAYER_EDIT_TEXT;
375                 edit_field_replace(
376                     label_layer->edit_field,
377                     texts + label_layer->selected * LABEL_LAYER_TEXT_MAX_SIZE);
378                 edit_field_restyle(
379                     label_layer->edit_field,
380                     LABELS_SIZE,
381                     colors[label_layer->selected]);
382                 SDL_StartTextInput();
383             }
384         } break;
385         }
386     } break;
387
388     case SDL_KEYDOWN: {
389         switch (event->key.keysym.sym) {
390         case SDLK_F2: {
391             if (label_layer->selected >= 0) {
392                 label_layer->state = LABEL_LAYER_EDIT_TEXT;
393                 edit_field_replace(
394                     label_layer->edit_field,
395                     texts + label_layer->selected * LABEL_LAYER_TEXT_MAX_SIZE);
396                 edit_field_restyle(
397                     label_layer->edit_field,
398                     LABELS_SIZE,
399                     colors[label_layer->selected]);
400                 SDL_StartTextInput();
401             }
402         } break;
403
404         case SDLK_F3: {
405             if (label_layer->selected >= 0) {
406                 label_layer->state = LABEL_LAYER_EDIT_ID;
407                 edit_field_replace(
408                     label_layer->edit_field,
409                     ids + label_layer->selected * LABEL_LAYER_ID_MAX_SIZE);
410                 edit_field_restyle(
411                     label_layer->edit_field,
412                     vec(1.0f, 1.0f),
413                     color_invert(colors[label_layer->selected]));
414                 SDL_StartTextInput();
415             }
416         } break;
417
418         case SDLK_DELETE: {
419             if (label_layer->selected >= 0) {
420                 label_layer_delete_nth_label(
421                     label_layer,
422                     (size_t) label_layer->selected);
423                 label_layer->selected = -1;
424             }
425         } break;
426         }
427     } break;
428     }
429
430     return 0;
431 }
432
433 static
434 int label_layer_move_event(LabelLayer *label_layer,
435                            const SDL_Event *event,
436                            const Camera *camera)
437 {
438     trace_assert(label_layer);
439     trace_assert(event);
440     trace_assert(camera);
441     trace_assert(label_layer->selected >= 0);
442
443     switch (event->type) {
444     case SDL_MOUSEMOTION: {
445         Point *positions = dynarray_data(label_layer->positions);
446         positions[label_layer->selected] =
447             vec_sub(
448                 camera_map_screen(
449                     camera,
450                     event->motion.x,
451                     event->motion.y),
452                 label_layer->move_anchor);
453     } break;
454
455     case SDL_MOUSEBUTTONUP: {
456         switch (event->button.button) {
457         case SDL_BUTTON_LEFT: {
458             label_layer->state = LABEL_LAYER_IDLE;
459         } break;
460         }
461     } break;
462     }
463
464     return 0;
465 }
466
467 static
468 int label_layer_edit_text_event(LabelLayer *label_layer,
469                                 const SDL_Event *event,
470                                 const Camera *camera)
471 {
472     trace_assert(label_layer);
473     trace_assert(event);
474     trace_assert(camera);
475     trace_assert(label_layer->selected >= 0);
476
477     switch (event->type) {
478     case SDL_KEYDOWN: {
479         switch (event->key.keysym.sym) {
480         case SDLK_RETURN: {
481             char *text =
482                 (char*)dynarray_data(label_layer->texts) + label_layer->selected * LABEL_LAYER_TEXT_MAX_SIZE;
483             memset(text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
484             memcpy(text, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_TEXT_MAX_SIZE - 1);
485             label_layer->state = LABEL_LAYER_IDLE;
486             SDL_StopTextInput();
487             return 0;
488         } break;
489
490         case SDLK_ESCAPE: {
491             label_layer->state = LABEL_LAYER_IDLE;
492             SDL_StopTextInput();
493             return 0;
494         } break;
495         }
496     } break;
497     }
498
499     return edit_field_event(label_layer->edit_field, event);
500 }
501
502 static
503 int label_layer_edit_id_event(LabelLayer *label_layer,
504                               const SDL_Event *event,
505                               const Camera *camera)
506 {
507     trace_assert(label_layer);
508     trace_assert(event);
509     trace_assert(camera);
510     trace_assert(label_layer->selected >= 0);
511
512     switch (event->type) {
513     case SDL_KEYDOWN: {
514         switch (event->key.keysym.sym) {
515         case SDLK_RETURN: {
516             char *id =
517                 (char*)dynarray_data(label_layer->ids) + label_layer->selected * LABEL_LAYER_ID_MAX_SIZE;
518             memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
519             memcpy(id, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_ID_MAX_SIZE - 1);
520             label_layer->state = LABEL_LAYER_IDLE;
521             SDL_StopTextInput();
522             return 0;
523         } break;
524
525         case SDLK_ESCAPE: {
526             label_layer->state = LABEL_LAYER_IDLE;
527             SDL_StopTextInput();
528             return 0;
529         } break;
530         }
531     } break;
532     }
533
534     return edit_field_event(label_layer->edit_field, event);
535 }
536
537 int label_layer_event(LabelLayer *label_layer,
538                       const SDL_Event *event,
539                       const Camera *camera)
540 {
541     trace_assert(label_layer);
542     trace_assert(event);
543     trace_assert(camera);
544
545     int changed = 0;
546
547     if (color_picker_event(
548             &label_layer->color_picker,
549             event,
550             &changed) < 0) {
551         return -1;
552     }
553
554     if (changed) {
555         if (label_layer->selected >= 0) {
556             Color *colors = dynarray_data(label_layer->colors);
557             colors[label_layer->selected] =
558                 color_picker_rgba(&label_layer->color_picker);
559         }
560         return 0;
561     }
562
563     switch (label_layer->state) {
564     case LABEL_LAYER_IDLE:
565         return label_layer_idle_event(label_layer, event, camera);
566
567     case LABEL_LAYER_MOVE:
568         return label_layer_move_event(label_layer, event, camera);
569
570     case LABEL_LAYER_EDIT_TEXT:
571         return label_layer_edit_text_event(label_layer, event, camera);
572
573     case LABEL_LAYER_EDIT_ID:
574         return label_layer_edit_id_event(label_layer, event, camera);
575     }
576
577     return 0;
578 }
579
580 size_t label_layer_count(const LabelLayer *label_layer)
581 {
582     return dynarray_count(label_layer->ids);
583 }
584
585 char *label_layer_ids(const LabelLayer *label_layer)
586 {
587     return dynarray_data(label_layer->ids);
588 }
589
590 Point *label_layer_positions(const LabelLayer *label_layer)
591 {
592     return dynarray_data(label_layer->positions);
593 }
594
595 Color *label_layer_colors(const LabelLayer *label_layer)
596 {
597     return dynarray_data(label_layer->colors);
598 }
599
600 char *labels_layer_texts(const LabelLayer *label_layer)
601 {
602     return dynarray_data(label_layer->texts);
603 }
604
605 int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump)
606 {
607     trace_assert(label_layer);
608     trace_assert(filedump);
609
610     size_t n = dynarray_count(label_layer->ids);
611     char *ids = dynarray_data(label_layer->ids);
612     Point *positions = dynarray_data(label_layer->positions);
613     Color *colors = dynarray_data(label_layer->colors);
614     char *texts = dynarray_data(label_layer->texts);
615
616     fprintf(filedump, "%zd\n", n);
617     for (size_t i = 0; i < n; ++i) {
618         fprintf(filedump, "%s %f %f ",
619                 ids + LABEL_LAYER_ID_MAX_SIZE * i,
620                 positions[i].x, positions[i].y);
621         color_hex_to_stream(colors[i], filedump);
622         fprintf(filedump, "\n%s\n", texts + i * LABEL_LAYER_TEXT_MAX_SIZE);
623     }
624
625     return 0;
626 }