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