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