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"
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"
21 #define LABEL_LAYER_SELECTION_THICCNESS 5.0f
23 // TODO(#1139): Label Layer does not support snapping
28 LABEL_LAYER_EDIT_TEXT,
33 static int label_clipboard = 0;
34 static char label_clipboard_text[LABEL_LAYER_TEXT_MAX_SIZE];
35 static Color label_clipboard_color;
39 LabelLayerState state;
45 ColorPicker color_picker;
47 Edit_field *edit_field;
51 const char *id_name_prefix;
64 char id[LABEL_LAYER_ID_MAX_SIZE];
67 char text[LABEL_LAYER_TEXT_MAX_SIZE];
73 LabelUndoContext create_label_undo_swap_context(LabelLayer *label_layer,
74 size_t index, size_t index2)
76 trace_assert(label_layer);
77 trace_assert(index < label_layer->positions.count);
78 trace_assert(index2 < label_layer->positions.count);
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;
89 LabelUndoContext create_label_undo_context(LabelLayer *label_layer, LabelUndoType type)
91 trace_assert(label_layer);
92 trace_assert(type != LABEL_UNDO_SWAP);
94 LabelUndoContext undo_context;
96 size_t index = type == LABEL_UNDO_ADD
97 ? label_layer->positions.count - 1
98 : (size_t)label_layer->selection;
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;
112 void label_layer_undo(void *context, size_t context_size)
114 trace_assert(context);
115 trace_assert(sizeof(LabelUndoContext) == context_size);
117 LabelUndoContext *undo_context = context;
118 LabelLayer *label_layer = undo_context->layer;
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);
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);
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);
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);
151 #define LABEL_UNDO_PUSH(HISTORY, CONTEXT) \
153 LabelUndoContext context = (CONTEXT); \
162 LayerPtr label_layer_as_layer(LabelLayer *label_layer)
171 LabelLayer *create_label_layer(const char *id_name_prefix)
173 Lt *lt = create_lt();
175 LabelLayer *label_layer = PUSH_LT(
176 lt, nth_calloc(1, sizeof(LabelLayer)), free);
177 if (label_layer == NULL) {
180 label_layer->lt = lt;
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);
187 label_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
188 label_layer->selection = -1;
190 label_layer->edit_field = PUSH_LT(
192 create_edit_field(LABELS_SIZE, COLOR_RED),
194 if (label_layer->edit_field == NULL) {
198 label_layer->id_name_prefix = id_name_prefix;
203 LabelLayer *create_label_layer_from_line_stream(LineStream *line_stream, const char *id_name_prefix)
205 trace_assert(line_stream);
206 LabelLayer *label_layer = create_label_layer(id_name_prefix);
208 if (label_layer == NULL) {
209 RETURN_LT(label_layer->lt, NULL);
212 const char *line = line_stream_next(line_stream);
214 log_fail("Could not read amount of labels\n");
215 RETURN_LT(label_layer->lt, NULL);
219 if (sscanf(line, "%zu", &n) == EOF) {
220 log_fail("Could not parse amount of labels\n");
221 RETURN_LT(label_layer->lt, NULL);
224 for (size_t i = 0; i < n; ++i) {
226 char id[LABEL_LAYER_ID_MAX_SIZE];
229 line = line_stream_next(line_stream);
231 log_fail("Could not read label meta info\n");
232 RETURN_LT(label_layer->lt, NULL);
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);
243 Color color = hexstr(hex);
245 dynarray_push(&label_layer->ids, id);
246 dynarray_push(&label_layer->positions, &position);
247 dynarray_push(&label_layer->colors, &color);
249 line = line_stream_next(line_stream);
251 log_fail("Could not read label text\n");
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);
263 void destroy_label_layer(LabelLayer *label_layer)
265 trace_assert(label_layer);
267 free(label_layer->ids.data);
268 free(label_layer->positions.data);
269 free(label_layer->colors.data);
270 free(label_layer->texts.data);
272 destroy_lt(label_layer->lt);
276 Rect boundary_of_element(const LabelLayer *label_layer,
280 trace_assert(i < label_layer->texts.count);
282 char *ids = (char *)label_layer->ids.data;
283 char *texts = (char *)label_layer->texts.data;
285 return rect_boundary2(
286 sprite_font_boundary_box(
289 texts + i * LABEL_LAYER_TEXT_MAX_SIZE),
290 sprite_font_boundary_box(
294 vec(0.0f, FONT_CHAR_HEIGHT),
297 ids + i * LABEL_LAYER_ID_MAX_SIZE));
300 int label_layer_render(const LabelLayer *label_layer,
301 const Camera *camera,
304 trace_assert(label_layer);
305 trace_assert(camera);
307 if (active && color_picker_render(&label_layer->color_picker, camera) < 0) {
311 size_t n = label_layer->ids.count;
312 char *ids = (char *)label_layer->ids.data;
313 Vec2f *positions = (Vec2f *)label_layer->positions.data;
314 Color *colors = (Color *)label_layer->colors.data;
315 char *texts = (char *)label_layer->texts.data;
317 /* TODO(#891): LabelLayer doesn't show the final position of Label after the animation */
318 for (size_t i = 0; i < n; ++i) {
319 const Color color = label_layer->state == LABEL_LAYER_RECOLOR && label_layer->selection == (int) i
320 ? label_layer->inter_color
323 const Vec2f position =
324 label_layer->state == LABEL_LAYER_MOVE && label_layer->selection == (int) i
325 ? label_layer->inter_position
329 if (label_layer->state == LABEL_LAYER_EDIT_TEXT && label_layer->selection == (int) i) {
330 if (edit_field_render_world(
331 label_layer->edit_field,
337 if (camera_render_text(
339 texts + i * LABEL_LAYER_TEXT_MAX_SIZE,
343 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
350 if (label_layer->state == LABEL_LAYER_EDIT_ID && label_layer->selection == (int)i) {
351 if (edit_field_render_world(
352 label_layer->edit_field,
357 vec(0.0f, FONT_CHAR_HEIGHT),
358 LABELS_SIZE))) < 0) {
362 if (camera_render_text(
364 ids + i * LABEL_LAYER_ID_MAX_SIZE,
368 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
372 vec(0.0f, FONT_CHAR_HEIGHT),
373 LABELS_SIZE))) < 0) {
379 // TODO(#1160): Label Selection has to be internal (just like in Rect Layer)
380 if (active && label_layer->selection == (int) i) {
389 LABEL_LAYER_SELECTION_THICCNESS * 0.5f);
392 if (camera_draw_thicc_rect_screen(
396 LABEL_LAYER_SELECTION_THICCNESS) < 0) {
407 int label_layer_element_at(LabelLayer *label_layer,
410 trace_assert(label_layer);
412 Vec2f *positions = (Vec2f*)label_layer->positions.data;
414 const int n = (int) label_layer->texts.count;
415 for (int i = n - 1; i >= 0; --i) {
416 if (rect_contains_point(
430 void label_layer_delete_selected_label(LabelLayer *label_layer,
431 UndoHistory *undo_history)
433 trace_assert(label_layer);
434 trace_assert(label_layer->selection >= 0);
436 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_DELETE));
438 dynarray_delete_at(&label_layer->ids, (size_t)label_layer->selection);
439 dynarray_delete_at(&label_layer->positions, (size_t)label_layer->selection);
440 dynarray_delete_at(&label_layer->colors, (size_t)label_layer->selection);
441 dynarray_delete_at(&label_layer->texts, (size_t)label_layer->selection);
443 label_layer->selection = -1;
447 int label_layer_add_label(LabelLayer *label_layer,
451 UndoHistory *undo_history)
453 trace_assert(label_layer);
455 // TODO(#982): id generation code is duplicated in label_layer, point_layer and rect_layer
456 char id[LABEL_LAYER_ID_MAX_SIZE];
457 snprintf(id, LABEL_LAYER_ID_MAX_SIZE, "%s_%d",
458 label_layer->id_name_prefix,
459 label_layer->id_name_counter++);
461 size_t n = label_layer->ids.count;
463 dynarray_push(&label_layer->ids, id);
464 dynarray_push(&label_layer->positions, &position);
465 dynarray_push(&label_layer->colors, &color);
466 dynarray_push_empty(&label_layer->texts);
468 dynarray_pointer_at(&label_layer->texts, n),
470 min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, strlen(text)));
472 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_ADD));
478 void label_layer_swap_elements(LabelLayer *label_layer,
480 UndoHistory *undo_history)
482 trace_assert(label_layer);
483 trace_assert(undo_history);
484 trace_assert(a < label_layer->positions.count);
485 trace_assert(b < label_layer->positions.count);
487 dynarray_swap(&label_layer->ids, a, b);
488 dynarray_swap(&label_layer->positions, a, b);
489 dynarray_swap(&label_layer->colors, a, b);
490 dynarray_swap(&label_layer->texts, a, b);
492 LABEL_UNDO_PUSH(undo_history, create_label_undo_swap_context(label_layer, a, b));
496 int label_layer_idle_event(LabelLayer *label_layer,
497 const SDL_Event *event,
498 const Camera *camera,
499 UndoHistory *undo_history)
501 trace_assert(label_layer);
503 trace_assert(camera);
507 if (color_picker_event(
508 &label_layer->color_picker,
516 if (label_layer->selection >= 0) {
517 label_layer->state = LABEL_LAYER_RECOLOR;
518 label_layer->inter_color = color_picker_rgba(&label_layer->color_picker);
523 Color *colors = (Color*)label_layer->colors.data;
524 Vec2f *positions = (Vec2f*)label_layer->positions.data;
525 char *ids = (char*)label_layer->ids.data;
526 char *texts = (char*)label_layer->texts.data;
528 switch (event->type) {
529 case SDL_MOUSEBUTTONDOWN: {
530 switch (event->button.button) {
531 case SDL_BUTTON_LEFT: {
532 const Vec2f position = camera_map_screen(
537 const int element = label_layer_element_at(
542 label_layer->move_anchor = vec_sub(position, positions[element]);
543 label_layer->selection = element;
544 label_layer->state = LABEL_LAYER_MOVE;
545 label_layer->inter_position = positions[element];
547 label_layer->color_picker =
548 create_color_picker_from_rgba(colors[element]);
550 label_layer->selection = label_layer_add_label(
554 &label_layer->color_picker),
557 label_layer->state = LABEL_LAYER_EDIT_TEXT;
559 label_layer->edit_field,
560 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
562 label_layer->edit_field,
564 colors[label_layer->selection]);
565 SDL_StartTextInput();
572 switch (event->key.keysym.sym) {
574 if ((event->key.keysym.mod & KMOD_SHIFT)
575 && (label_layer->selection >= 0)
576 && ((size_t)(label_layer->selection + 1) < label_layer->positions.count)) {
577 label_layer_swap_elements(
579 (size_t) label_layer->selection,
580 (size_t) label_layer->selection + 1,
582 label_layer->selection++;
587 if ((event->key.keysym.mod & KMOD_SHIFT)
588 && (label_layer->selection > 0)
589 && ((size_t) label_layer->selection < label_layer->positions.count)) {
590 label_layer_swap_elements(
592 (size_t) label_layer->selection,
593 (size_t) label_layer->selection - 1,
595 label_layer->selection--;
600 if (label_layer->selection >= 0) {
601 label_layer->state = LABEL_LAYER_EDIT_TEXT;
603 label_layer->edit_field,
604 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
606 label_layer->edit_field,
608 colors[label_layer->selection]);
609 SDL_StartTextInput();
614 if (label_layer->selection >= 0) {
615 label_layer->state = LABEL_LAYER_EDIT_ID;
617 label_layer->edit_field,
618 ids + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE);
620 label_layer->edit_field,
622 color_invert(colors[label_layer->selection]));
623 SDL_StartTextInput();
628 if (label_layer->selection >= 0) {
629 label_layer_delete_selected_label(
632 label_layer->selection = -1;
637 if ((event->key.keysym.mod & KMOD_LCTRL) && label_layer->selection >= 0) {
639 dynarray_copy_to(&label_layer->texts, label_clipboard_text, (size_t)label_layer->selection);
640 dynarray_copy_to(&label_layer->colors, &label_clipboard_color, (size_t)label_layer->selection);
645 if ((event->key.keysym.mod & KMOD_LCTRL) && label_clipboard) {
647 SDL_GetMouseState(&x, &y);
648 Vec2f position = camera_map_screen(camera, x, y);
650 label_layer_add_label(
653 label_clipboard_color,
654 label_clipboard_text,
666 void snap_inter_position(LabelLayer *label_layer, float snap_threshold)
668 trace_assert(label_layer);
669 trace_assert(label_layer->selection >= 0);
670 trace_assert(label_layer->state == LABEL_LAYER_MOVE);
672 const size_t n = label_layer->positions.count;
673 Vec2f *positions = (Vec2f*)label_layer->positions.data;
675 Rect a = boundary_of_element(
677 (size_t) label_layer->selection,
678 label_layer->inter_position);
680 for (size_t i = 0; i < n; ++i) {
681 if (i == (size_t) label_layer->selection) continue;
683 const Rect b = boundary_of_element(label_layer, i, positions[i]);
685 if (segment_overlap(vec(a.x, a.x + a.w), vec(b.x, b.x + b.w))) {
686 snap_seg2seg(&label_layer->inter_position.y,
687 b.y, a.h, b.h, snap_threshold);
690 if (segment_overlap(vec(a.y, a.y + a.h), vec(b.y, b.y + b.h))) {
691 snap_seg2seg(&label_layer->inter_position.x,
692 b.x, a.w, b.w, snap_threshold);
698 int label_layer_move_event(LabelLayer *label_layer,
699 const SDL_Event *event,
700 const Camera *camera,
701 UndoHistory *undo_history)
703 trace_assert(label_layer);
705 trace_assert(camera);
706 trace_assert(label_layer->selection >= 0);
708 Vec2f *positions = (Vec2f*)label_layer->positions.data;
710 switch (event->type) {
711 case SDL_MOUSEMOTION: {
712 const Uint8 *state = SDL_GetKeyboardState(NULL);
713 const Vec2f mouse_pos = vec_sub(
718 label_layer->move_anchor);
720 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
721 label_layer->inter_position = mouse_pos;
723 const Vec2f label_pos = positions[label_layer->selection];
725 const float dx = fabsf(label_pos.x - mouse_pos.x);
726 const float dy = fabsf(label_pos.y - mouse_pos.y);
729 label_layer->inter_position = vec(mouse_pos.x, label_pos.y);
731 label_layer->inter_position = vec(label_pos.x, mouse_pos.y);
735 snap_inter_position(label_layer, SNAPPING_THRESHOLD);
738 case SDL_MOUSEBUTTONUP: {
739 switch (event->button.button) {
740 case SDL_BUTTON_LEFT: {
741 const float distance = vec_length(
742 vec_sub(label_layer->inter_position,
743 positions[label_layer->selection]));
745 if (distance > 1e-6) {
746 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
749 &label_layer->positions,
750 (size_t)label_layer->selection,
751 &label_layer->inter_position);
754 label_layer->state = LABEL_LAYER_IDLE;
764 int label_layer_edit_text_event(LabelLayer *label_layer,
765 const SDL_Event *event,
766 const Camera *camera,
767 UndoHistory *undo_history)
769 trace_assert(label_layer);
771 trace_assert(camera);
772 trace_assert(label_layer->selection >= 0);
774 switch (event->type) {
776 switch (event->key.keysym.sym) {
778 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
781 (char*)label_layer->texts.data + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE;
782 memset(text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
783 memcpy(text, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_TEXT_MAX_SIZE - 1);
784 label_layer->state = LABEL_LAYER_IDLE;
790 label_layer->state = LABEL_LAYER_IDLE;
798 return edit_field_event(label_layer->edit_field, event);
802 int label_layer_edit_id_event(LabelLayer *label_layer,
803 const SDL_Event *event,
804 const Camera *camera,
805 UndoHistory *undo_history)
807 trace_assert(label_layer);
809 trace_assert(camera);
810 trace_assert(undo_history);
811 trace_assert(label_layer->selection >= 0);
813 switch (event->type) {
815 switch (event->key.keysym.sym) {
817 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
820 (char*)label_layer->ids.data + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE;
821 memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
822 memcpy(id, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_ID_MAX_SIZE - 1);
823 label_layer->state = LABEL_LAYER_IDLE;
829 label_layer->state = LABEL_LAYER_IDLE;
837 return edit_field_event(label_layer->edit_field, event);
841 int label_layer_recolor_event(LabelLayer *label_layer,
842 const SDL_Event *event,
843 const Camera *camera,
844 UndoHistory *undo_history)
846 trace_assert(label_layer);
848 trace_assert(camera);
849 trace_assert(undo_history);
850 trace_assert(label_layer->selection >= 0);
854 if (color_picker_event(
855 &label_layer->color_picker,
863 label_layer->inter_color =
864 color_picker_rgba(&label_layer->color_picker);
866 if (!color_picker_drag(&label_layer->color_picker)) {
867 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
870 &label_layer->colors,
871 (size_t) label_layer->selection,
872 &label_layer->inter_color);
873 label_layer->state = LABEL_LAYER_IDLE;
880 int label_layer_event(LabelLayer *label_layer,
881 const SDL_Event *event,
882 const Camera *camera,
883 UndoHistory *undo_history)
885 trace_assert(label_layer);
887 trace_assert(camera);
888 trace_assert(undo_history);
890 switch (label_layer->state) {
891 case LABEL_LAYER_IDLE:
892 return label_layer_idle_event(label_layer, event, camera, undo_history);
894 case LABEL_LAYER_MOVE:
895 return label_layer_move_event(label_layer, event, camera, undo_history);
897 case LABEL_LAYER_EDIT_TEXT:
898 return label_layer_edit_text_event(label_layer, event, camera, undo_history);
900 case LABEL_LAYER_EDIT_ID:
901 return label_layer_edit_id_event(label_layer, event, camera, undo_history);
903 case LABEL_LAYER_RECOLOR:
904 return label_layer_recolor_event(label_layer, event, camera, undo_history);
910 size_t label_layer_count(const LabelLayer *label_layer)
912 return label_layer->ids.count;
915 char *label_layer_ids(const LabelLayer *label_layer)
917 return (char *)label_layer->ids.data;
920 Vec2f *label_layer_positions(const LabelLayer *label_layer)
922 return (Vec2f *)label_layer->positions.data;
925 Color *label_layer_colors(const LabelLayer *label_layer)
927 return (Color *)label_layer->colors.data;
930 char *labels_layer_texts(const LabelLayer *label_layer)
932 return (char *)label_layer->texts.data;
935 int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump)
937 trace_assert(label_layer);
938 trace_assert(filedump);
940 size_t n = label_layer->ids.count;
941 char *ids = (char *)label_layer->ids.data;
942 Vec2f *positions = (Vec2f *)label_layer->positions.data;
943 Color *colors = (Color *)label_layer->colors.data;
944 char *texts = (char *)label_layer->texts.data;
946 fprintf(filedump, "%zd\n", n);
947 for (size_t i = 0; i < n; ++i) {
948 fprintf(filedump, "%s %f %f ",
949 ids + LABEL_LAYER_ID_MAX_SIZE * i,
950 positions[i].x, positions[i].y);
951 color_hex_to_stream(colors[i], filedump);
952 fprintf(filedump, "\n%s\n", texts + i * LABEL_LAYER_TEXT_MAX_SIZE);