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