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