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