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