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