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