5 #include "system/line_stream.h"
6 #include "system/stacktrace.h"
7 #include "system/nth_alloc.h"
9 #include "system/str.h"
10 #include "system/log.h"
11 #include "math/point.h"
12 #include "label_layer.h"
15 #include "game/camera.h"
16 #include "color_picker.h"
17 #include "ui/edit_field.h"
18 #include "math/extrema.h"
20 #define LABEL_LAYER_SELECTION_THICCNESS 5.0f
22 // TODO(#1079): LabelLayer doe snot support z reordering
27 LABEL_LAYER_EDIT_TEXT,
33 static char clipboard_text[LABEL_LAYER_TEXT_MAX_SIZE];
34 static Color clipboard_color;
38 LabelLayerState state;
44 ColorPicker color_picker;
46 Edit_field *edit_field;
50 const char *id_name_prefix;
63 char id[LABEL_LAYER_ID_MAX_SIZE];
66 char text[LABEL_LAYER_TEXT_MAX_SIZE];
72 UndoContext create_undo_swap_context(LabelLayer *label_layer,
73 size_t index, size_t index2)
75 trace_assert(label_layer);
76 trace_assert(index < dynarray_count(label_layer->positions));
77 trace_assert(index2 < dynarray_count(label_layer->positions));
79 UndoContext undo_context;
80 undo_context.type = UNDO_SWAP;
81 undo_context.layer = label_layer;
82 undo_context.index = index;
83 undo_context.index2 = index2;
88 UndoContext create_undo_context(LabelLayer *label_layer, UndoType type)
90 trace_assert(label_layer);
91 trace_assert(type != UNDO_SWAP);
93 UndoContext undo_context;
95 size_t index = type == UNDO_ADD
96 ? dynarray_count(label_layer->positions) - 1
97 : (size_t)label_layer->selection;
99 undo_context.type = type;
100 undo_context.layer = label_layer;
101 dynarray_copy_to(label_layer->ids, &undo_context.id, index);
102 dynarray_copy_to(label_layer->positions, &undo_context.position, index);
103 dynarray_copy_to(label_layer->colors, &undo_context.color, index);
104 dynarray_copy_to(label_layer->texts, &undo_context.text, index);
105 undo_context.index = index;
111 void label_layer_undo(void *context, size_t context_size)
113 trace_assert(context);
114 trace_assert(sizeof(UndoContext) == context_size);
116 UndoContext *undo_context = context;
117 LabelLayer *label_layer = undo_context->layer;
119 switch (undo_context->type) {
121 dynarray_delete_at(label_layer->ids, undo_context->index);
122 dynarray_delete_at(label_layer->positions, undo_context->index);
123 dynarray_delete_at(label_layer->colors, undo_context->index);
124 dynarray_delete_at(label_layer->texts, undo_context->index);
128 dynarray_insert_before(label_layer->ids, undo_context->index, &undo_context->id);
129 dynarray_insert_before(label_layer->positions, undo_context->index, &undo_context->position);
130 dynarray_insert_before(label_layer->colors, undo_context->index, &undo_context->color);
131 dynarray_insert_before(label_layer->texts, undo_context->index, &undo_context->text);
135 dynarray_replace_at(label_layer->ids, undo_context->index, &undo_context->id);
136 dynarray_replace_at(label_layer->positions, undo_context->index, &undo_context->position);
137 dynarray_replace_at(label_layer->colors, undo_context->index, &undo_context->color);
138 dynarray_replace_at(label_layer->texts, undo_context->index, &undo_context->text);
142 dynarray_swap(label_layer->ids, undo_context->index, undo_context->index2);
143 dynarray_swap(label_layer->positions, undo_context->index, undo_context->index2);
144 dynarray_swap(label_layer->colors, undo_context->index, undo_context->index2);
145 dynarray_swap(label_layer->texts, undo_context->index, undo_context->index2);
150 #define UNDO_PUSH(HISTORY, CONTEXT) \
152 UndoContext context = (CONTEXT); \
161 LayerPtr label_layer_as_layer(LabelLayer *label_layer)
170 LabelLayer *create_label_layer(const char *id_name_prefix)
172 Lt *lt = create_lt();
174 LabelLayer *label_layer = PUSH_LT(
175 lt, nth_calloc(1, sizeof(LabelLayer)), free);
176 if (label_layer == NULL) {
179 label_layer->lt = lt;
181 label_layer->ids = PUSH_LT(
183 create_dynarray(sizeof(char) * LABEL_LAYER_ID_MAX_SIZE),
185 if (label_layer->ids == NULL) {
189 label_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray);
190 if (label_layer->positions == NULL) {
194 label_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
195 if (label_layer->colors == NULL) {
199 label_layer->texts = PUSH_LT(
201 create_dynarray(sizeof(char) * LABEL_LAYER_TEXT_MAX_SIZE),
203 if (label_layer->texts == NULL) {
207 label_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
208 label_layer->selection = -1;
210 label_layer->edit_field = PUSH_LT(
212 create_edit_field(LABELS_SIZE, COLOR_RED),
214 if (label_layer->edit_field == NULL) {
218 label_layer->id_name_prefix = id_name_prefix;
223 LabelLayer *create_label_layer_from_line_stream(LineStream *line_stream, const char *id_name_prefix)
225 trace_assert(line_stream);
226 LabelLayer *label_layer = create_label_layer(id_name_prefix);
228 if (label_layer == NULL) {
229 RETURN_LT(label_layer->lt, NULL);
232 const char *line = line_stream_next(line_stream);
234 log_fail("Could not read amount of labels\n");
235 RETURN_LT(label_layer->lt, NULL);
239 if (sscanf(line, "%zu", &n) == EOF) {
240 log_fail("Could not parse amount of labels\n");
241 RETURN_LT(label_layer->lt, NULL);
244 for (size_t i = 0; i < n; ++i) {
246 char id[LABEL_LAYER_ID_MAX_SIZE];
249 line = line_stream_next(line_stream);
251 log_fail("Could not read label meta info\n");
252 RETURN_LT(label_layer->lt, NULL);
257 "%"STRINGIFY(LABEL_LAYER_ID_MAX_SIZE)"s%f%f%6s\n",
258 id, &position.x, &position.y, hex) == EOF) {
259 log_fail("Could not parse label meta info\n");
260 RETURN_LT(label_layer->lt, NULL);
263 Color color = hexstr(hex);
265 dynarray_push(label_layer->ids, id);
266 dynarray_push(label_layer->positions, &position);
267 dynarray_push(label_layer->colors, &color);
269 line = line_stream_next(line_stream);
271 log_fail("Could not read label text\n");
274 char label_text[LABEL_LAYER_TEXT_MAX_SIZE] = {0};
275 memcpy(label_text, line, LABEL_LAYER_TEXT_MAX_SIZE - 1);
276 trim_endline(label_text);
277 dynarray_push(label_layer->texts, &label_text);
283 void destroy_label_layer(LabelLayer *label_layer)
285 trace_assert(label_layer);
286 destroy_lt(label_layer->lt);
289 int label_layer_render(const LabelLayer *label_layer,
290 const Camera *camera,
293 trace_assert(label_layer);
294 trace_assert(camera);
296 if (active && color_picker_render(&label_layer->color_picker, camera) < 0) {
300 size_t n = dynarray_count(label_layer->ids);
301 char *ids = dynarray_data(label_layer->ids);
302 Point *positions = dynarray_data(label_layer->positions);
303 Color *colors = dynarray_data(label_layer->colors);
304 char *texts = dynarray_data(label_layer->texts);
306 /* TODO(#891): LabelLayer doesn't show the final position of Label after the animation */
307 for (size_t i = 0; i < n; ++i) {
308 const Color color = label_layer->state == LABEL_LAYER_RECOLOR && label_layer->selection == (int) i
309 ? label_layer->inter_color
312 const Point position =
313 label_layer->state == LABEL_LAYER_MOVE && label_layer->selection == (int) i
314 ? label_layer->inter_position
318 if (label_layer->state == LABEL_LAYER_EDIT_TEXT && label_layer->selection == (int) i) {
319 if (edit_field_render_world(
320 label_layer->edit_field,
326 if (camera_render_text(
328 texts + i * LABEL_LAYER_TEXT_MAX_SIZE,
332 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
339 if (label_layer->state == LABEL_LAYER_EDIT_ID && label_layer->selection == (int)i) {
340 if (edit_field_render_world(
341 label_layer->edit_field,
345 vec(0.0f, FONT_CHAR_HEIGHT))) < 0) {
349 if (camera_render_text(
351 ids + i * LABEL_LAYER_ID_MAX_SIZE,
355 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
356 vec_sub(position, vec(0.0f, FONT_CHAR_HEIGHT))) < 0) {
362 if (active && label_layer->selection == (int) i) {
368 sprite_font_boundary_box(
372 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE),
373 sprite_font_boundary_box(
377 vec(0.0f, FONT_CHAR_HEIGHT)),
379 ids + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE))),
380 LABEL_LAYER_SELECTION_THICCNESS * 0.5f);
383 if (camera_draw_thicc_rect_screen(
387 LABEL_LAYER_SELECTION_THICCNESS) < 0) {
398 int label_layer_element_at(LabelLayer *label_layer,
399 const Sprite_font *font,
402 trace_assert(label_layer);
404 const int n = (int) dynarray_count(label_layer->texts);
405 char *ids = dynarray_data(label_layer->ids);
406 char *texts = dynarray_data(label_layer->texts);
407 Point *positions = dynarray_data(label_layer->positions);
409 for (int i = n - 1; i >= 0; --i) {
410 Rect boundary = rect_boundary2(
411 sprite_font_boundary_box(
415 texts + i * LABEL_LAYER_TEXT_MAX_SIZE),
416 sprite_font_boundary_box(
420 vec(0.0f, FONT_CHAR_HEIGHT)),
422 ids + i * LABEL_LAYER_ID_MAX_SIZE));
424 if (rect_contains_point(boundary, position)) {
433 void label_layer_delete_selected_label(LabelLayer *label_layer,
434 UndoHistory *undo_history)
436 trace_assert(label_layer);
437 trace_assert(label_layer->selection >= 0);
439 UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_DELETE));
441 dynarray_delete_at(label_layer->ids, (size_t)label_layer->selection);
442 dynarray_delete_at(label_layer->positions, (size_t)label_layer->selection);
443 dynarray_delete_at(label_layer->colors, (size_t)label_layer->selection);
444 dynarray_delete_at(label_layer->texts, (size_t)label_layer->selection);
446 label_layer->selection = -1;
450 int label_layer_add_label(LabelLayer *label_layer,
454 UndoHistory *undo_history)
456 trace_assert(label_layer);
458 // TODO(#982): id generation code is duplicated in label_layer, point_layer and rect_layer
459 char id[LABEL_LAYER_ID_MAX_SIZE];
460 snprintf(id, LABEL_LAYER_ID_MAX_SIZE, "%s_%d",
461 label_layer->id_name_prefix,
462 label_layer->id_name_counter++);
464 size_t n = dynarray_count(label_layer->ids);
466 dynarray_push(label_layer->ids, id);
467 dynarray_push(label_layer->positions, &position);
468 dynarray_push(label_layer->colors, &color);
469 dynarray_push_empty(label_layer->texts);
471 dynarray_pointer_at(label_layer->texts, n),
473 min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, strlen(text)));
475 UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_ADD));
481 void label_layer_swap_elements(LabelLayer *label_layer,
483 UndoHistory *undo_history)
485 trace_assert(label_layer);
486 trace_assert(undo_history);
487 trace_assert(a < dynarray_count(label_layer->positions));
488 trace_assert(b < dynarray_count(label_layer->positions));
490 dynarray_swap(label_layer->ids, a, b);
491 dynarray_swap(label_layer->positions, a, b);
492 dynarray_swap(label_layer->colors, a, b);
493 dynarray_swap(label_layer->texts, a, b);
495 UNDO_PUSH(undo_history, create_undo_swap_context(label_layer, a, b));
499 int label_layer_idle_event(LabelLayer *label_layer,
500 const SDL_Event *event,
501 const Camera *camera,
502 UndoHistory *undo_history)
504 trace_assert(label_layer);
506 trace_assert(camera);
510 if (color_picker_event(
511 &label_layer->color_picker,
519 if (label_layer->selection >= 0) {
520 label_layer->state = LABEL_LAYER_RECOLOR;
521 label_layer->inter_color = color_picker_rgba(&label_layer->color_picker);
526 Color *colors = dynarray_data(label_layer->colors);
527 Point *positions = dynarray_data(label_layer->positions);
528 char *ids = dynarray_data(label_layer->ids);
529 char *texts = dynarray_data(label_layer->texts);
531 switch (event->type) {
532 case SDL_MOUSEBUTTONDOWN: {
533 switch (event->button.button) {
534 case SDL_BUTTON_LEFT: {
535 const Point position = camera_map_screen(
540 const int element = label_layer_element_at(
546 label_layer->move_anchor = vec_sub(position, positions[element]);
547 label_layer->selection = element;
548 label_layer->state = LABEL_LAYER_MOVE;
549 label_layer->inter_position = positions[element];
551 label_layer->color_picker =
552 create_color_picker_from_rgba(colors[element]);
554 label_layer->selection = label_layer_add_label(
558 &label_layer->color_picker),
561 label_layer->state = LABEL_LAYER_EDIT_TEXT;
563 label_layer->edit_field,
564 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
566 label_layer->edit_field,
568 colors[label_layer->selection]);
569 SDL_StartTextInput();
576 switch (event->key.keysym.sym) {
578 if ((event->key.keysym.mod & KMOD_SHIFT)
579 && (label_layer->selection >= 0)
580 && ((size_t)(label_layer->selection + 1) < dynarray_count(label_layer->positions))) {
581 label_layer_swap_elements(
583 (size_t) label_layer->selection,
584 (size_t) label_layer->selection + 1,
586 label_layer->selection++;
591 if ((event->key.keysym.mod & KMOD_SHIFT)
592 && (label_layer->selection > 0)
593 && ((size_t) label_layer->selection < dynarray_count(label_layer->positions))) {
594 label_layer_swap_elements(
596 (size_t) label_layer->selection,
597 (size_t) label_layer->selection - 1,
599 label_layer->selection--;
604 if (label_layer->selection >= 0) {
605 label_layer->state = LABEL_LAYER_EDIT_TEXT;
607 label_layer->edit_field,
608 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
610 label_layer->edit_field,
612 colors[label_layer->selection]);
613 SDL_StartTextInput();
618 if (label_layer->selection >= 0) {
619 label_layer->state = LABEL_LAYER_EDIT_ID;
621 label_layer->edit_field,
622 ids + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE);
624 label_layer->edit_field,
626 color_invert(colors[label_layer->selection]));
627 SDL_StartTextInput();
632 if (label_layer->selection >= 0) {
633 label_layer_delete_selected_label(
636 label_layer->selection = -1;
641 if ((event->key.keysym.mod & KMOD_LCTRL) && label_layer->selection >= 0) {
643 dynarray_copy_to(label_layer->texts, clipboard_text, (size_t)label_layer->selection);
644 dynarray_copy_to(label_layer->colors, &clipboard_color, (size_t)label_layer->selection);
649 if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
651 SDL_GetMouseState(&x, &y);
652 Point position = camera_map_screen(camera, x, y);
654 label_layer_add_label(
670 int label_layer_move_event(LabelLayer *label_layer,
671 const SDL_Event *event,
672 const Camera *camera,
673 UndoHistory *undo_history)
675 trace_assert(label_layer);
677 trace_assert(camera);
678 trace_assert(label_layer->selection >= 0);
680 switch (event->type) {
681 case SDL_MOUSEMOTION: {
682 label_layer->inter_position = vec_sub(
687 label_layer->move_anchor);
690 case SDL_MOUSEBUTTONUP: {
691 switch (event->button.button) {
692 case SDL_BUTTON_LEFT: {
693 UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
696 label_layer->positions,
697 (size_t)label_layer->selection,
698 &label_layer->inter_position);
699 label_layer->state = LABEL_LAYER_IDLE;
709 int label_layer_edit_text_event(LabelLayer *label_layer,
710 const SDL_Event *event,
711 const Camera *camera,
712 UndoHistory *undo_history)
714 trace_assert(label_layer);
716 trace_assert(camera);
717 trace_assert(label_layer->selection >= 0);
719 switch (event->type) {
721 switch (event->key.keysym.sym) {
723 UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
726 (char*)dynarray_data(label_layer->texts) + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE;
727 memset(text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
728 memcpy(text, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_TEXT_MAX_SIZE - 1);
729 label_layer->state = LABEL_LAYER_IDLE;
735 label_layer->state = LABEL_LAYER_IDLE;
743 return edit_field_event(label_layer->edit_field, event);
747 int label_layer_edit_id_event(LabelLayer *label_layer,
748 const SDL_Event *event,
749 const Camera *camera,
750 UndoHistory *undo_history)
752 trace_assert(label_layer);
754 trace_assert(camera);
755 trace_assert(undo_history);
756 trace_assert(label_layer->selection >= 0);
758 switch (event->type) {
760 switch (event->key.keysym.sym) {
762 UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
765 (char*)dynarray_data(label_layer->ids) + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE;
766 memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
767 memcpy(id, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_ID_MAX_SIZE - 1);
768 label_layer->state = LABEL_LAYER_IDLE;
774 label_layer->state = LABEL_LAYER_IDLE;
782 return edit_field_event(label_layer->edit_field, event);
786 int label_layer_recolor_event(LabelLayer *label_layer,
787 const SDL_Event *event,
788 const Camera *camera,
789 UndoHistory *undo_history)
791 trace_assert(label_layer);
793 trace_assert(camera);
794 trace_assert(undo_history);
795 trace_assert(label_layer->selection >= 0);
799 if (color_picker_event(
800 &label_layer->color_picker,
808 label_layer->inter_color =
809 color_picker_rgba(&label_layer->color_picker);
811 if (!color_picker_drag(&label_layer->color_picker)) {
812 UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
816 (size_t) label_layer->selection,
817 &label_layer->inter_color);
818 label_layer->state = LABEL_LAYER_IDLE;
825 int label_layer_event(LabelLayer *label_layer,
826 const SDL_Event *event,
827 const Camera *camera,
828 UndoHistory *undo_history)
830 trace_assert(label_layer);
832 trace_assert(camera);
833 trace_assert(undo_history);
835 switch (label_layer->state) {
836 case LABEL_LAYER_IDLE:
837 return label_layer_idle_event(label_layer, event, camera, undo_history);
839 case LABEL_LAYER_MOVE:
840 return label_layer_move_event(label_layer, event, camera, undo_history);
842 case LABEL_LAYER_EDIT_TEXT:
843 return label_layer_edit_text_event(label_layer, event, camera, undo_history);
845 case LABEL_LAYER_EDIT_ID:
846 return label_layer_edit_id_event(label_layer, event, camera, undo_history);
848 case LABEL_LAYER_RECOLOR:
849 return label_layer_recolor_event(label_layer, event, camera, undo_history);
855 size_t label_layer_count(const LabelLayer *label_layer)
857 return dynarray_count(label_layer->ids);
860 char *label_layer_ids(const LabelLayer *label_layer)
862 return dynarray_data(label_layer->ids);
865 Point *label_layer_positions(const LabelLayer *label_layer)
867 return dynarray_data(label_layer->positions);
870 Color *label_layer_colors(const LabelLayer *label_layer)
872 return dynarray_data(label_layer->colors);
875 char *labels_layer_texts(const LabelLayer *label_layer)
877 return dynarray_data(label_layer->texts);
880 int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump)
882 trace_assert(label_layer);
883 trace_assert(filedump);
885 size_t n = dynarray_count(label_layer->ids);
886 char *ids = dynarray_data(label_layer->ids);
887 Point *positions = dynarray_data(label_layer->positions);
888 Color *colors = dynarray_data(label_layer->colors);
889 char *texts = dynarray_data(label_layer->texts);
891 fprintf(filedump, "%zd\n", n);
892 for (size_t i = 0; i < n; ++i) {
893 fprintf(filedump, "%s %f %f ",
894 ids + LABEL_LAYER_ID_MAX_SIZE * i,
895 positions[i].x, positions[i].y);
896 color_hex_to_stream(colors[i], filedump);
897 fprintf(filedump, "\n%s\n", texts + i * LABEL_LAYER_TEXT_MAX_SIZE);