]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/label_layer.c
a3624d02daf33f0f5dc518dd06b8009b6a1ad896
[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/vec.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 #include "math/extrema.h"
19
20 #define LABEL_LAYER_SELECTION_THICCNESS 5.0f
21
22 // TODO: Label Layer does not support snapping
23
24 typedef enum {
25     LABEL_LAYER_IDLE = 0,
26     LABEL_LAYER_MOVE,
27     LABEL_LAYER_EDIT_TEXT,
28     LABEL_LAYER_EDIT_ID,
29     LABEL_LAYER_RECOLOR
30 } LabelLayerState;
31
32 static int clipboard;
33 static char clipboard_text[LABEL_LAYER_TEXT_MAX_SIZE];
34 static Color clipboard_color;
35
36 struct LabelLayer {
37     Lt *lt;
38     LabelLayerState state;
39     Dynarray *ids;
40     Dynarray *positions;
41     Dynarray *colors;
42     Dynarray *texts;
43     int selection;
44     ColorPicker color_picker;
45     Vec2f move_anchor;
46     Edit_field *edit_field;
47     Vec2f inter_position;
48     Color inter_color;
49     int id_name_counter;
50     const char *id_name_prefix;
51 };
52
53 typedef enum {
54     UNDO_ADD,
55     UNDO_DELETE,
56     UNDO_UPDATE,
57     UNDO_SWAP
58 } UndoType;
59
60 typedef struct {
61     UndoType type;
62     LabelLayer *layer;
63     char id[LABEL_LAYER_ID_MAX_SIZE];
64     Vec2f position;
65     Color color;
66     char text[LABEL_LAYER_TEXT_MAX_SIZE];
67     size_t index;
68     size_t index2;
69 } UndoContext;
70
71 static
72 UndoContext create_undo_swap_context(LabelLayer *label_layer,
73                                      size_t index, size_t index2)
74 {
75     trace_assert(label_layer);
76     trace_assert(index < dynarray_count(label_layer->positions));
77     trace_assert(index2 < dynarray_count(label_layer->positions));
78
79     UndoContext undo_context;
80     undo_context.type = UNDO_SWAP;
81     undo_context.layer = label_layer;
82     undo_context.index = index;
83     undo_context.index2 = index2;
84     return undo_context;
85 }
86
87 static
88 UndoContext create_undo_context(LabelLayer *label_layer, UndoType type)
89 {
90     trace_assert(label_layer);
91     trace_assert(type != UNDO_SWAP);
92
93     UndoContext undo_context;
94
95     size_t index = type == UNDO_ADD
96         ? dynarray_count(label_layer->positions) - 1
97         : (size_t)label_layer->selection;
98
99     undo_context.type = type;
100     undo_context.layer = label_layer;
101     dynarray_copy_to(label_layer->ids, &undo_context.id, index);
102     dynarray_copy_to(label_layer->positions, &undo_context.position, index);
103     dynarray_copy_to(label_layer->colors, &undo_context.color, index);
104     dynarray_copy_to(label_layer->texts, &undo_context.text, index);
105     undo_context.index = index;
106
107     return undo_context;
108 }
109
110 static
111 void label_layer_undo(void *context, size_t context_size)
112 {
113     trace_assert(context);
114     trace_assert(sizeof(UndoContext) == context_size);
115
116     UndoContext *undo_context = context;
117     LabelLayer *label_layer = undo_context->layer;
118
119     switch (undo_context->type) {
120     case UNDO_ADD: {
121         dynarray_delete_at(label_layer->ids, undo_context->index);
122         dynarray_delete_at(label_layer->positions, undo_context->index);
123         dynarray_delete_at(label_layer->colors, undo_context->index);
124         dynarray_delete_at(label_layer->texts, undo_context->index);
125     } break;
126
127     case UNDO_DELETE: {
128         dynarray_insert_before(label_layer->ids, undo_context->index, &undo_context->id);
129         dynarray_insert_before(label_layer->positions, undo_context->index, &undo_context->position);
130         dynarray_insert_before(label_layer->colors, undo_context->index, &undo_context->color);
131         dynarray_insert_before(label_layer->texts, undo_context->index, &undo_context->text);
132     } break;
133
134     case UNDO_UPDATE: {
135         dynarray_replace_at(label_layer->ids, undo_context->index, &undo_context->id);
136         dynarray_replace_at(label_layer->positions, undo_context->index, &undo_context->position);
137         dynarray_replace_at(label_layer->colors, undo_context->index, &undo_context->color);
138         dynarray_replace_at(label_layer->texts, undo_context->index, &undo_context->text);
139     } break;
140
141     case UNDO_SWAP: {
142         dynarray_swap(label_layer->ids, undo_context->index, undo_context->index2);
143         dynarray_swap(label_layer->positions, undo_context->index, undo_context->index2);
144         dynarray_swap(label_layer->colors, undo_context->index, undo_context->index2);
145         dynarray_swap(label_layer->texts, undo_context->index, undo_context->index2);
146     } break;
147     }
148 }
149
150 #define UNDO_PUSH(HISTORY, CONTEXT)                                     \
151     do {                                                                \
152         UndoContext context = (CONTEXT);                                \
153         undo_history_push(                                              \
154             HISTORY,                                                    \
155             label_layer_undo,                                           \
156             &context,                                                   \
157             sizeof(context));                                           \
158     } while(0)
159
160
161 LayerPtr label_layer_as_layer(LabelLayer *label_layer)
162 {
163     LayerPtr layer = {
164         .ptr = label_layer,
165         .type = LAYER_LABEL
166     };
167     return layer;
168 }
169
170 LabelLayer *create_label_layer(const char *id_name_prefix)
171 {
172     Lt *lt = create_lt();
173
174     LabelLayer *label_layer = PUSH_LT(
175         lt, nth_calloc(1, sizeof(LabelLayer)), free);
176     if (label_layer == NULL) {
177         RETURN_LT(lt, NULL);
178     }
179     label_layer->lt = lt;
180
181     label_layer->ids = PUSH_LT(
182         lt,
183         create_dynarray(sizeof(char) * LABEL_LAYER_ID_MAX_SIZE),
184         destroy_dynarray);
185     if (label_layer->ids == NULL) {
186         RETURN_LT(lt, NULL);
187     }
188
189     label_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Vec2f)), destroy_dynarray);
190     if (label_layer->positions == NULL) {
191         RETURN_LT(lt, NULL);
192     }
193
194     label_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
195     if (label_layer->colors == NULL) {
196         RETURN_LT(lt, NULL);
197     }
198
199     label_layer->texts = PUSH_LT(
200         lt,
201         create_dynarray(sizeof(char) * LABEL_LAYER_TEXT_MAX_SIZE),
202         destroy_dynarray);
203     if (label_layer->texts == NULL) {
204         RETURN_LT(lt, NULL);
205     }
206
207     label_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
208     label_layer->selection = -1;
209
210     label_layer->edit_field = PUSH_LT(
211         lt,
212         create_edit_field(LABELS_SIZE, COLOR_RED),
213         destroy_edit_field);
214     if (label_layer->edit_field == NULL) {
215         RETURN_LT(lt, NULL);
216     }
217
218     label_layer->id_name_prefix = id_name_prefix;
219
220     return label_layer;
221 }
222
223 LabelLayer *create_label_layer_from_line_stream(LineStream *line_stream, const char *id_name_prefix)
224 {
225     trace_assert(line_stream);
226     LabelLayer *label_layer = create_label_layer(id_name_prefix);
227
228     if (label_layer == NULL) {
229         RETURN_LT(label_layer->lt, NULL);
230     }
231
232     const char *line = line_stream_next(line_stream);
233     if (line == NULL) {
234         log_fail("Could not read amount of labels\n");
235         RETURN_LT(label_layer->lt, NULL);
236     }
237
238     size_t n = 0;
239     if (sscanf(line, "%zu", &n) == EOF) {
240         log_fail("Could not parse amount of labels\n");
241         RETURN_LT(label_layer->lt, NULL);
242     }
243
244     for (size_t i = 0; i < n; ++i) {
245         char hex[7];
246         char id[LABEL_LAYER_ID_MAX_SIZE];
247         Vec2f position;
248
249         line = line_stream_next(line_stream);
250         if (line == NULL) {
251             log_fail("Could not read label meta info\n");
252             RETURN_LT(label_layer->lt, NULL);
253         }
254
255         if (sscanf(
256                 line,
257                 "%"STRINGIFY(LABEL_LAYER_ID_MAX_SIZE)"s%f%f%6s\n",
258                 id, &position.x, &position.y, hex) == EOF) {
259             log_fail("Could not parse label meta info\n");
260             RETURN_LT(label_layer->lt, NULL);
261         }
262
263         Color color = hexstr(hex);
264
265         dynarray_push(label_layer->ids, id);
266         dynarray_push(label_layer->positions, &position);
267         dynarray_push(label_layer->colors, &color);
268
269         line = line_stream_next(line_stream);
270         if (line == NULL) {
271             log_fail("Could not read label text\n");
272         }
273
274         char label_text[LABEL_LAYER_TEXT_MAX_SIZE] = {0};
275         memcpy(label_text, line, LABEL_LAYER_TEXT_MAX_SIZE - 1);
276         trim_endline(label_text);
277         dynarray_push(label_layer->texts, &label_text);
278     }
279
280     return label_layer;
281 }
282
283 void destroy_label_layer(LabelLayer *label_layer)
284 {
285     trace_assert(label_layer);
286     destroy_lt(label_layer->lt);
287 }
288
289 int label_layer_render(const LabelLayer *label_layer,
290                        const Camera *camera,
291                        int active)
292 {
293     trace_assert(label_layer);
294     trace_assert(camera);
295
296     if (active && color_picker_render(&label_layer->color_picker, camera) < 0) {
297         return -1;
298     }
299
300     size_t n = dynarray_count(label_layer->ids);
301     char *ids = dynarray_data(label_layer->ids);
302     Vec2f *positions = dynarray_data(label_layer->positions);
303     Color *colors = dynarray_data(label_layer->colors);
304     char *texts = dynarray_data(label_layer->texts);
305
306     /* TODO(#891): LabelLayer doesn't show the final position of Label after the animation */
307     for (size_t i = 0; i < n; ++i) {
308         const Color color = label_layer->state == LABEL_LAYER_RECOLOR && label_layer->selection == (int) i
309             ? label_layer->inter_color
310             : colors[i];
311
312         const Vec2f position =
313             label_layer->state == LABEL_LAYER_MOVE && label_layer->selection == (int) i
314             ? label_layer->inter_position
315             : positions[i];
316
317         // Label Text
318         if (label_layer->state == LABEL_LAYER_EDIT_TEXT && label_layer->selection == (int) i) {
319             if (edit_field_render_world(
320                     label_layer->edit_field,
321                     camera,
322                     position) < 0) {
323                 return -1;
324             }
325         } else {
326             if (camera_render_text(
327                     camera,
328                     texts + i * LABEL_LAYER_TEXT_MAX_SIZE,
329                     LABELS_SIZE,
330                     color_scale(
331                         color,
332                         rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
333                     position) < 0) {
334                 return -1;
335             }
336         }
337
338         // Label ID
339         if (label_layer->state == LABEL_LAYER_EDIT_ID && label_layer->selection == (int)i) {
340             if (edit_field_render_world(
341                     label_layer->edit_field,
342                     camera,
343                     vec_sub(
344                         position,
345                         vec(0.0f, FONT_CHAR_HEIGHT))) < 0) {
346                 return -1;
347             }
348         } else {
349             if (camera_render_text(
350                     camera,
351                     ids + i * LABEL_LAYER_ID_MAX_SIZE,
352                     vec(1.0f, 1.0f),
353                     color_scale(
354                         color_invert(color),
355                         rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
356                     vec_sub(position, vec(0.0f, FONT_CHAR_HEIGHT))) < 0) {
357                 return -1;
358             }
359         }
360
361         // Label Selection
362         if (active && label_layer->selection == (int) i) {
363             Rect selection =
364                 rect_pad(
365                     camera_rect(
366                         camera,
367                         rect_boundary2(
368                             sprite_font_boundary_box(
369                                 camera_font(camera),
370                                 position,
371                                 LABELS_SIZE,
372                                 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE),
373                             sprite_font_boundary_box(
374                                 camera_font(camera),
375                                 vec_sub(
376                                     position,
377                                     vec(0.0f, FONT_CHAR_HEIGHT)),
378                                 vec(1.0f, 1.0f),
379                                 ids + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE))),
380                     LABEL_LAYER_SELECTION_THICCNESS * 0.5f);
381
382
383             if (camera_draw_thicc_rect_screen(
384                     camera,
385                     selection,
386                     color,
387                     LABEL_LAYER_SELECTION_THICCNESS) < 0) {
388                 return -1;
389             }
390         }
391     }
392
393
394     return 0;
395 }
396
397 static
398 int label_layer_element_at(LabelLayer *label_layer,
399                            const Sprite_font *font,
400                            Vec2f position)
401 {
402     trace_assert(label_layer);
403
404     const int n = (int) dynarray_count(label_layer->texts);
405     char *ids = dynarray_data(label_layer->ids);
406     char *texts = dynarray_data(label_layer->texts);
407     Vec2f *positions = dynarray_data(label_layer->positions);
408
409     for (int i = n - 1; i >= 0; --i) {
410         Rect boundary = rect_boundary2(
411             sprite_font_boundary_box(
412                 font,
413                 positions[i],
414                 LABELS_SIZE,
415                 texts + i * LABEL_LAYER_TEXT_MAX_SIZE),
416             sprite_font_boundary_box(
417                 font,
418                 vec_sub(
419                     positions[i],
420                     vec(0.0f, FONT_CHAR_HEIGHT)),
421                 vec(1.0f, 1.0f),
422                 ids + i * LABEL_LAYER_ID_MAX_SIZE));
423
424         if (rect_contains_point(boundary, position)) {
425             return i;
426         }
427     }
428
429     return -1;
430 }
431
432 static
433 void label_layer_delete_selected_label(LabelLayer *label_layer,
434                                        UndoHistory *undo_history)
435 {
436     trace_assert(label_layer);
437     trace_assert(label_layer->selection >= 0);
438
439     UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_DELETE));
440
441     dynarray_delete_at(label_layer->ids, (size_t)label_layer->selection);
442     dynarray_delete_at(label_layer->positions, (size_t)label_layer->selection);
443     dynarray_delete_at(label_layer->colors, (size_t)label_layer->selection);
444     dynarray_delete_at(label_layer->texts, (size_t)label_layer->selection);
445
446     label_layer->selection = -1;
447 }
448
449 static
450 int label_layer_add_label(LabelLayer *label_layer,
451                           Vec2f position,
452                           Color color,
453                           const char *text,
454                           UndoHistory *undo_history)
455 {
456     trace_assert(label_layer);
457
458     // TODO(#982): id generation code is duplicated in label_layer, point_layer and rect_layer
459     char id[LABEL_LAYER_ID_MAX_SIZE];
460     snprintf(id, LABEL_LAYER_ID_MAX_SIZE, "%s_%d",
461              label_layer->id_name_prefix,
462              label_layer->id_name_counter++);
463
464     size_t n = dynarray_count(label_layer->ids);
465
466     dynarray_push(label_layer->ids, id);
467     dynarray_push(label_layer->positions, &position);
468     dynarray_push(label_layer->colors, &color);
469     dynarray_push_empty(label_layer->texts);
470     memcpy(
471         dynarray_pointer_at(label_layer->texts, n),
472         text,
473         min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, strlen(text)));
474
475     UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_ADD));
476
477     return (int) n;
478 }
479
480 static
481 void label_layer_swap_elements(LabelLayer *label_layer,
482                                size_t a, size_t b,
483                                UndoHistory *undo_history)
484 {
485     trace_assert(label_layer);
486     trace_assert(undo_history);
487     trace_assert(a < dynarray_count(label_layer->positions));
488     trace_assert(b < dynarray_count(label_layer->positions));
489
490     dynarray_swap(label_layer->ids, a, b);
491     dynarray_swap(label_layer->positions, a, b);
492     dynarray_swap(label_layer->colors, a, b);
493     dynarray_swap(label_layer->texts, a, b);
494
495     UNDO_PUSH(undo_history, create_undo_swap_context(label_layer, a, b));
496 }
497
498 static
499 int label_layer_idle_event(LabelLayer *label_layer,
500                            const SDL_Event *event,
501                            const Camera *camera,
502                            UndoHistory *undo_history)
503 {
504     trace_assert(label_layer);
505     trace_assert(event);
506     trace_assert(camera);
507
508
509     int changed = 0;
510     if (color_picker_event(
511             &label_layer->color_picker,
512             event,
513             camera,
514             &changed) < 0) {
515         return -1;
516     }
517
518     if (changed) {
519         if (label_layer->selection >= 0) {
520             label_layer->state = LABEL_LAYER_RECOLOR;
521             label_layer->inter_color = color_picker_rgba(&label_layer->color_picker);
522         }
523         return 0;
524     }
525
526     Color *colors = dynarray_data(label_layer->colors);
527     Vec2f *positions = dynarray_data(label_layer->positions);
528     char *ids = dynarray_data(label_layer->ids);
529     char *texts = dynarray_data(label_layer->texts);
530
531     switch (event->type) {
532     case SDL_MOUSEBUTTONDOWN: {
533         switch (event->button.button) {
534         case SDL_BUTTON_LEFT: {
535             const Vec2f position = camera_map_screen(
536                 camera,
537                 event->button.x,
538                 event->button.y);
539
540             const int element = label_layer_element_at(
541                 label_layer,
542                 camera_font(camera),
543                 position);
544
545             if (element >= 0) {
546                 label_layer->move_anchor = vec_sub(position, positions[element]);
547                 label_layer->selection = element;
548                 label_layer->state = LABEL_LAYER_MOVE;
549                 label_layer->inter_position = positions[element];
550
551                 label_layer->color_picker =
552                     create_color_picker_from_rgba(colors[element]);
553             } else {
554                 label_layer->selection = label_layer_add_label(
555                     label_layer,
556                     position,
557                     color_picker_rgba(
558                         &label_layer->color_picker),
559                     "",
560                     undo_history);
561                 label_layer->state = LABEL_LAYER_EDIT_TEXT;
562                 edit_field_replace(
563                     label_layer->edit_field,
564                     texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
565                 edit_field_restyle(
566                     label_layer->edit_field,
567                     LABELS_SIZE,
568                     colors[label_layer->selection]);
569                 SDL_StartTextInput();
570             }
571         } break;
572         }
573     } break;
574
575     case SDL_KEYDOWN: {
576         switch (event->key.keysym.sym) {
577         case SDLK_UP: {
578             if ((event->key.keysym.mod & KMOD_SHIFT)
579                 && (label_layer->selection >= 0)
580                 && ((size_t)(label_layer->selection + 1) < dynarray_count(label_layer->positions))) {
581                 label_layer_swap_elements(
582                     label_layer,
583                     (size_t) label_layer->selection,
584                     (size_t) label_layer->selection + 1,
585                     undo_history);
586                 label_layer->selection++;
587             }
588         } break;
589
590         case SDLK_DOWN: {
591             if ((event->key.keysym.mod & KMOD_SHIFT)
592                 && (label_layer->selection > 0)
593                 && ((size_t) label_layer->selection < dynarray_count(label_layer->positions))) {
594                 label_layer_swap_elements(
595                     label_layer,
596                     (size_t) label_layer->selection,
597                     (size_t) label_layer->selection - 1,
598                     undo_history);
599                 label_layer->selection--;
600             }
601         } break;
602
603         case SDLK_F2: {
604             if (label_layer->selection >= 0) {
605                 label_layer->state = LABEL_LAYER_EDIT_TEXT;
606                 edit_field_replace(
607                     label_layer->edit_field,
608                     texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
609                 edit_field_restyle(
610                     label_layer->edit_field,
611                     LABELS_SIZE,
612                     colors[label_layer->selection]);
613                 SDL_StartTextInput();
614             }
615         } break;
616
617         case SDLK_F3: {
618             if (label_layer->selection >= 0) {
619                 label_layer->state = LABEL_LAYER_EDIT_ID;
620                 edit_field_replace(
621                     label_layer->edit_field,
622                     ids + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE);
623                 edit_field_restyle(
624                     label_layer->edit_field,
625                     vec(1.0f, 1.0f),
626                     color_invert(colors[label_layer->selection]));
627                 SDL_StartTextInput();
628             }
629         } break;
630
631         case SDLK_DELETE: {
632             if (label_layer->selection >= 0) {
633                 label_layer_delete_selected_label(
634                     label_layer,
635                     undo_history);
636                 label_layer->selection = -1;
637             }
638         } break;
639
640         case SDLK_c: {
641             if ((event->key.keysym.mod & KMOD_LCTRL) && label_layer->selection >= 0) {
642                 clipboard = 1;
643                 dynarray_copy_to(label_layer->texts, clipboard_text, (size_t)label_layer->selection);
644                 dynarray_copy_to(label_layer->colors, &clipboard_color, (size_t)label_layer->selection);
645             }
646         } break;
647
648         case SDLK_v: {
649             if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
650                 int x, y;
651                 SDL_GetMouseState(&x, &y);
652                 Vec2f position = camera_map_screen(camera, x, y);
653
654                 label_layer_add_label(
655                     label_layer,
656                     position,
657                     clipboard_color,
658                     clipboard_text,
659                     undo_history);
660             }
661         } break;
662         }
663     } break;
664     }
665
666     return 0;
667 }
668
669 static
670 int label_layer_move_event(LabelLayer *label_layer,
671                            const SDL_Event *event,
672                            const Camera *camera,
673                            UndoHistory *undo_history)
674 {
675     trace_assert(label_layer);
676     trace_assert(event);
677     trace_assert(camera);
678     trace_assert(label_layer->selection >= 0);
679
680     Vec2f *positions = dynarray_data(label_layer->positions);
681
682     switch (event->type) {
683     case SDL_MOUSEMOTION: {
684         const Uint8 *state = SDL_GetKeyboardState(NULL);
685         const Vec2f mouse_pos = vec_sub(
686             camera_map_screen(
687                 camera,
688                 event->motion.x,
689                 event->motion.y),
690             label_layer->move_anchor);
691
692         if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
693             label_layer->inter_position = mouse_pos;
694         } else {
695             const Vec2f label_pos = positions[label_layer->selection];
696
697             const float dx = fabsf(label_pos.x - mouse_pos.x);
698             const float dy = fabsf(label_pos.y - mouse_pos.y);
699
700             if (dx > dy) {
701                 label_layer->inter_position = vec(mouse_pos.x, label_pos.y);
702             } else {
703                 label_layer->inter_position = vec(label_pos.x, mouse_pos.y);
704             }
705         }
706     } break;
707
708     case SDL_MOUSEBUTTONUP: {
709         switch (event->button.button) {
710         case SDL_BUTTON_LEFT: {
711             const float distance = vec_length(
712                 vec_sub(label_layer->inter_position,
713                         positions[label_layer->selection]));
714
715             if (distance > 1e-6) {
716                 UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
717
718                 dynarray_replace_at(
719                     label_layer->positions,
720                     (size_t)label_layer->selection,
721                     &label_layer->inter_position);
722             }
723
724             label_layer->state = LABEL_LAYER_IDLE;
725         } break;
726         }
727     } break;
728     }
729
730     return 0;
731 }
732
733 static
734 int label_layer_edit_text_event(LabelLayer *label_layer,
735                                 const SDL_Event *event,
736                                 const Camera *camera,
737                                 UndoHistory *undo_history)
738 {
739     trace_assert(label_layer);
740     trace_assert(event);
741     trace_assert(camera);
742     trace_assert(label_layer->selection >= 0);
743
744     switch (event->type) {
745     case SDL_KEYDOWN: {
746         switch (event->key.keysym.sym) {
747         case SDLK_RETURN: {
748             UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
749
750             char *text =
751                 (char*)dynarray_data(label_layer->texts) + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE;
752             memset(text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
753             memcpy(text, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_TEXT_MAX_SIZE - 1);
754             label_layer->state = LABEL_LAYER_IDLE;
755             SDL_StopTextInput();
756             return 0;
757         } break;
758
759         case SDLK_ESCAPE: {
760             label_layer->state = LABEL_LAYER_IDLE;
761             SDL_StopTextInput();
762             return 0;
763         } break;
764         }
765     } break;
766     }
767
768     return edit_field_event(label_layer->edit_field, event);
769 }
770
771 static
772 int label_layer_edit_id_event(LabelLayer *label_layer,
773                               const SDL_Event *event,
774                               const Camera *camera,
775                               UndoHistory *undo_history)
776 {
777     trace_assert(label_layer);
778     trace_assert(event);
779     trace_assert(camera);
780     trace_assert(undo_history);
781     trace_assert(label_layer->selection >= 0);
782
783     switch (event->type) {
784     case SDL_KEYDOWN: {
785         switch (event->key.keysym.sym) {
786         case SDLK_RETURN: {
787             UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
788
789             char *id =
790                 (char*)dynarray_data(label_layer->ids) + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE;
791             memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
792             memcpy(id, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_ID_MAX_SIZE - 1);
793             label_layer->state = LABEL_LAYER_IDLE;
794             SDL_StopTextInput();
795             return 0;
796         } break;
797
798         case SDLK_ESCAPE: {
799             label_layer->state = LABEL_LAYER_IDLE;
800             SDL_StopTextInput();
801             return 0;
802         } break;
803         }
804     } break;
805     }
806
807     return edit_field_event(label_layer->edit_field, event);
808 }
809
810 static
811 int label_layer_recolor_event(LabelLayer *label_layer,
812                               const SDL_Event *event,
813                               const Camera *camera,
814                               UndoHistory *undo_history)
815 {
816     trace_assert(label_layer);
817     trace_assert(event);
818     trace_assert(camera);
819     trace_assert(undo_history);
820     trace_assert(label_layer->selection >= 0);
821
822     int changed = 0;
823
824     if (color_picker_event(
825             &label_layer->color_picker,
826             event,
827             camera,
828             &changed) < 0) {
829         return -1;
830     }
831
832     if (changed) {
833         label_layer->inter_color =
834             color_picker_rgba(&label_layer->color_picker);
835
836         if (!color_picker_drag(&label_layer->color_picker)) {
837             UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
838
839             dynarray_replace_at(
840                 label_layer->colors,
841                 (size_t) label_layer->selection,
842                 &label_layer->inter_color);
843             label_layer->state = LABEL_LAYER_IDLE;
844         }
845     }
846
847     return 0;
848 }
849
850 int label_layer_event(LabelLayer *label_layer,
851                       const SDL_Event *event,
852                       const Camera *camera,
853                       UndoHistory *undo_history)
854 {
855     trace_assert(label_layer);
856     trace_assert(event);
857     trace_assert(camera);
858     trace_assert(undo_history);
859
860     switch (label_layer->state) {
861     case LABEL_LAYER_IDLE:
862         return label_layer_idle_event(label_layer, event, camera, undo_history);
863
864     case LABEL_LAYER_MOVE:
865         return label_layer_move_event(label_layer, event, camera, undo_history);
866
867     case LABEL_LAYER_EDIT_TEXT:
868         return label_layer_edit_text_event(label_layer, event, camera, undo_history);
869
870     case LABEL_LAYER_EDIT_ID:
871         return label_layer_edit_id_event(label_layer, event, camera, undo_history);
872
873     case LABEL_LAYER_RECOLOR:
874         return label_layer_recolor_event(label_layer, event, camera, undo_history);
875     }
876
877     return 0;
878 }
879
880 size_t label_layer_count(const LabelLayer *label_layer)
881 {
882     return dynarray_count(label_layer->ids);
883 }
884
885 char *label_layer_ids(const LabelLayer *label_layer)
886 {
887     return dynarray_data(label_layer->ids);
888 }
889
890 Vec2f *label_layer_positions(const LabelLayer *label_layer)
891 {
892     return dynarray_data(label_layer->positions);
893 }
894
895 Color *label_layer_colors(const LabelLayer *label_layer)
896 {
897     return dynarray_data(label_layer->colors);
898 }
899
900 char *labels_layer_texts(const LabelLayer *label_layer)
901 {
902     return dynarray_data(label_layer->texts);
903 }
904
905 int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump)
906 {
907     trace_assert(label_layer);
908     trace_assert(filedump);
909
910     size_t n = dynarray_count(label_layer->ids);
911     char *ids = dynarray_data(label_layer->ids);
912     Vec2f *positions = dynarray_data(label_layer->positions);
913     Color *colors = dynarray_data(label_layer->colors);
914     char *texts = dynarray_data(label_layer->texts);
915
916     fprintf(filedump, "%zd\n", n);
917     for (size_t i = 0; i < n; ++i) {
918         fprintf(filedump, "%s %f %f ",
919                 ids + LABEL_LAYER_ID_MAX_SIZE * i,
920                 positions[i].x, positions[i].y);
921         color_hex_to_stream(colors[i], filedump);
922         fprintf(filedump, "\n%s\n", texts + i * LABEL_LAYER_TEXT_MAX_SIZE);
923     }
924
925     return 0;
926 }