]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/label_layer.c
aece34908d0ae30364ac72a52d6fc7869fc899b2
[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 #include "math/extrema.h"
19
20 #define LABEL_LAYER_SELECTION_THICCNESS 5.0f
21
22 // TODO(#1079): LabelLayer doe snot support z reordering
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     Point move_anchor;
46     Edit_field *edit_field;
47     Point 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     Point 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(Point)), 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         Point 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     Point *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 Point 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_scale(
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                            Point 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     Point *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                           Point 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     Point *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 Point 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                 Point 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     switch (event->type) {
681     case SDL_MOUSEMOTION: {
682         label_layer->inter_position = vec_sub(
683             camera_map_screen(
684                 camera,
685                 event->motion.x,
686                 event->motion.y),
687             label_layer->move_anchor);
688     } break;
689
690     case SDL_MOUSEBUTTONUP: {
691         switch (event->button.button) {
692         case SDL_BUTTON_LEFT: {
693             UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
694
695             dynarray_replace_at(
696                 label_layer->positions,
697                 (size_t)label_layer->selection,
698                 &label_layer->inter_position);
699             label_layer->state = LABEL_LAYER_IDLE;
700         } break;
701         }
702     } break;
703     }
704
705     return 0;
706 }
707
708 static
709 int label_layer_edit_text_event(LabelLayer *label_layer,
710                                 const SDL_Event *event,
711                                 const Camera *camera,
712                                 UndoHistory *undo_history)
713 {
714     trace_assert(label_layer);
715     trace_assert(event);
716     trace_assert(camera);
717     trace_assert(label_layer->selection >= 0);
718
719     switch (event->type) {
720     case SDL_KEYDOWN: {
721         switch (event->key.keysym.sym) {
722         case SDLK_RETURN: {
723             UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
724
725             char *text =
726                 (char*)dynarray_data(label_layer->texts) + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE;
727             memset(text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
728             memcpy(text, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_TEXT_MAX_SIZE - 1);
729             label_layer->state = LABEL_LAYER_IDLE;
730             SDL_StopTextInput();
731             return 0;
732         } break;
733
734         case SDLK_ESCAPE: {
735             label_layer->state = LABEL_LAYER_IDLE;
736             SDL_StopTextInput();
737             return 0;
738         } break;
739         }
740     } break;
741     }
742
743     return edit_field_event(label_layer->edit_field, event);
744 }
745
746 static
747 int label_layer_edit_id_event(LabelLayer *label_layer,
748                               const SDL_Event *event,
749                               const Camera *camera,
750                               UndoHistory *undo_history)
751 {
752     trace_assert(label_layer);
753     trace_assert(event);
754     trace_assert(camera);
755     trace_assert(undo_history);
756     trace_assert(label_layer->selection >= 0);
757
758     switch (event->type) {
759     case SDL_KEYDOWN: {
760         switch (event->key.keysym.sym) {
761         case SDLK_RETURN: {
762             UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
763
764             char *id =
765                 (char*)dynarray_data(label_layer->ids) + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE;
766             memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
767             memcpy(id, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_ID_MAX_SIZE - 1);
768             label_layer->state = LABEL_LAYER_IDLE;
769             SDL_StopTextInput();
770             return 0;
771         } break;
772
773         case SDLK_ESCAPE: {
774             label_layer->state = LABEL_LAYER_IDLE;
775             SDL_StopTextInput();
776             return 0;
777         } break;
778         }
779     } break;
780     }
781
782     return edit_field_event(label_layer->edit_field, event);
783 }
784
785 static
786 int label_layer_recolor_event(LabelLayer *label_layer,
787                               const SDL_Event *event,
788                               const Camera *camera,
789                               UndoHistory *undo_history)
790 {
791     trace_assert(label_layer);
792     trace_assert(event);
793     trace_assert(camera);
794     trace_assert(undo_history);
795     trace_assert(label_layer->selection >= 0);
796
797     int changed = 0;
798
799     if (color_picker_event(
800             &label_layer->color_picker,
801             event,
802             camera,
803             &changed) < 0) {
804         return -1;
805     }
806
807     if (changed) {
808         label_layer->inter_color =
809             color_picker_rgba(&label_layer->color_picker);
810
811         if (!color_picker_drag(&label_layer->color_picker)) {
812             UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
813
814             dynarray_replace_at(
815                 label_layer->colors,
816                 (size_t) label_layer->selection,
817                 &label_layer->inter_color);
818             label_layer->state = LABEL_LAYER_IDLE;
819         }
820     }
821
822     return 0;
823 }
824
825 int label_layer_event(LabelLayer *label_layer,
826                       const SDL_Event *event,
827                       const Camera *camera,
828                       UndoHistory *undo_history)
829 {
830     trace_assert(label_layer);
831     trace_assert(event);
832     trace_assert(camera);
833     trace_assert(undo_history);
834
835     switch (label_layer->state) {
836     case LABEL_LAYER_IDLE:
837         return label_layer_idle_event(label_layer, event, camera, undo_history);
838
839     case LABEL_LAYER_MOVE:
840         return label_layer_move_event(label_layer, event, camera, undo_history);
841
842     case LABEL_LAYER_EDIT_TEXT:
843         return label_layer_edit_text_event(label_layer, event, camera, undo_history);
844
845     case LABEL_LAYER_EDIT_ID:
846         return label_layer_edit_id_event(label_layer, event, camera, undo_history);
847
848     case LABEL_LAYER_RECOLOR:
849         return label_layer_recolor_event(label_layer, event, camera, undo_history);
850     }
851
852     return 0;
853 }
854
855 size_t label_layer_count(const LabelLayer *label_layer)
856 {
857     return dynarray_count(label_layer->ids);
858 }
859
860 char *label_layer_ids(const LabelLayer *label_layer)
861 {
862     return dynarray_data(label_layer->ids);
863 }
864
865 Point *label_layer_positions(const LabelLayer *label_layer)
866 {
867     return dynarray_data(label_layer->positions);
868 }
869
870 Color *label_layer_colors(const LabelLayer *label_layer)
871 {
872     return dynarray_data(label_layer->colors);
873 }
874
875 char *labels_layer_texts(const LabelLayer *label_layer)
876 {
877     return dynarray_data(label_layer->texts);
878 }
879
880 int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump)
881 {
882     trace_assert(label_layer);
883     trace_assert(filedump);
884
885     size_t n = dynarray_count(label_layer->ids);
886     char *ids = dynarray_data(label_layer->ids);
887     Point *positions = dynarray_data(label_layer->positions);
888     Color *colors = dynarray_data(label_layer->colors);
889     char *texts = dynarray_data(label_layer->texts);
890
891     fprintf(filedump, "%zd\n", n);
892     for (size_t i = 0; i < n; ++i) {
893         fprintf(filedump, "%s %f %f ",
894                 ids + LABEL_LAYER_ID_MAX_SIZE * i,
895                 positions[i].x, positions[i].y);
896         color_hex_to_stream(colors[i], filedump);
897         fprintf(filedump, "\n%s\n", texts + i * LABEL_LAYER_TEXT_MAX_SIZE);
898     }
899
900     return 0;
901 }