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