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