5 #include "system/stacktrace.h"
6 #include "system/nth_alloc.h"
8 #include "system/str.h"
9 #include "system/log.h"
11 #include "label_layer.h"
14 #include "game/camera.h"
15 #include "color_picker.h"
16 #include "ui/edit_field.h"
17 #include "math/extrema.h"
20 #define LABEL_LAYER_SELECTION_THICCNESS 5.0f
22 // TODO(#1139): Label Layer does not support snapping
27 LABEL_LAYER_EDIT_TEXT,
32 static int label_clipboard = 0;
33 static char label_clipboard_text[LABEL_LAYER_TEXT_MAX_SIZE];
34 static Color label_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 LabelUndoContext create_label_undo_swap_context(LabelLayer *label_layer,
73 size_t index, size_t index2)
75 trace_assert(label_layer);
76 trace_assert(index < label_layer->positions.count);
77 trace_assert(index2 < label_layer->positions.count);
79 LabelUndoContext undo_context;
80 undo_context.type = LABEL_UNDO_SWAP;
81 undo_context.layer = label_layer;
82 undo_context.index = index;
83 undo_context.index2 = index2;
88 LabelUndoContext create_label_undo_context(LabelLayer *label_layer, LabelUndoType type)
90 trace_assert(label_layer);
91 trace_assert(type != LABEL_UNDO_SWAP);
93 LabelUndoContext undo_context;
95 size_t index = type == LABEL_UNDO_ADD
96 ? label_layer->positions.count - 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(LabelUndoContext) == context_size);
116 LabelUndoContext *undo_context = context;
117 LabelLayer *label_layer = undo_context->layer;
119 switch (undo_context->type) {
120 case LABEL_UNDO_ADD: {
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);
127 case LABEL_UNDO_DELETE: {
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);
134 case LABEL_UNDO_UPDATE: {
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);
141 case LABEL_UNDO_SWAP: {
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 LABEL_UNDO_PUSH(HISTORY, CONTEXT) \
152 LabelUndoContext 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 = create_dynarray(sizeof(char) * LABEL_LAYER_ID_MAX_SIZE);
182 label_layer->positions = create_dynarray(sizeof(Vec2f));
183 label_layer->colors = create_dynarray(sizeof(Color));
184 label_layer->texts = create_dynarray(sizeof(char) * LABEL_LAYER_TEXT_MAX_SIZE);
186 label_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
187 label_layer->selection = -1;
189 label_layer->edit_field = PUSH_LT(
191 create_edit_field(LABELS_SIZE, COLOR_RED),
193 if (label_layer->edit_field == NULL) {
197 label_layer->id_name_prefix = id_name_prefix;
202 LabelLayer *chop_label_layer(Memory *memory,
204 const char *id_name_prefix)
206 trace_assert(memory);
208 trace_assert(id_name_prefix);
210 LabelLayer *label_layer = create_label_layer(id_name_prefix);
212 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
213 char id[LABEL_LAYER_ID_MAX_SIZE];
214 char label_text[LABEL_LAYER_TEXT_MAX_SIZE];
215 for (int i = 0; i < n; ++i) {
216 String meta = trim(chop_by_delim(input, '\n'));
218 String string_id = trim(chop_word(&meta));
220 position.x = strtof(string_to_cstr(memory, trim(chop_word(&meta))), NULL);
221 position.y = strtof(string_to_cstr(memory, trim(chop_word(&meta))), NULL);
222 Color color = hexs(trim(chop_word(&meta)));
224 memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
228 min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, string_id.count));
230 String label_text_string =
231 trim(chop_by_delim(input, '\n'));
232 memset(label_text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
235 label_text_string.data,
236 min_size_t(LABEL_LAYER_TEXT_MAX_SIZE - 1,
237 label_text_string.count));
239 dynarray_push(&label_layer->ids, id);
240 dynarray_push(&label_layer->positions, &position);
241 dynarray_push(&label_layer->colors, &color);
242 dynarray_push(&label_layer->texts, label_text);
248 void destroy_label_layer(LabelLayer *label_layer)
250 trace_assert(label_layer);
252 free(label_layer->ids.data);
253 free(label_layer->positions.data);
254 free(label_layer->colors.data);
255 free(label_layer->texts.data);
257 destroy_lt(label_layer->lt);
261 Rect boundary_of_element(const LabelLayer *label_layer,
265 trace_assert(i < label_layer->texts.count);
267 char *ids = (char *)label_layer->ids.data;
268 char *texts = (char *)label_layer->texts.data;
270 return rect_boundary2(
271 sprite_font_boundary_box(
274 texts + i * LABEL_LAYER_TEXT_MAX_SIZE),
275 sprite_font_boundary_box(
279 vec(0.0f, FONT_CHAR_HEIGHT),
282 ids + i * LABEL_LAYER_ID_MAX_SIZE));
285 int label_layer_render(const LabelLayer *label_layer,
286 const Camera *camera,
289 trace_assert(label_layer);
290 trace_assert(camera);
292 if (active && color_picker_render(&label_layer->color_picker, camera) < 0) {
296 size_t n = label_layer->ids.count;
297 char *ids = (char *)label_layer->ids.data;
298 Vec2f *positions = (Vec2f *)label_layer->positions.data;
299 Color *colors = (Color *)label_layer->colors.data;
300 char *texts = (char *)label_layer->texts.data;
302 /* TODO(#891): LabelLayer doesn't show the final position of Label after the animation */
303 for (size_t i = 0; i < n; ++i) {
304 const Color color = label_layer->state == LABEL_LAYER_RECOLOR && label_layer->selection == (int) i
305 ? label_layer->inter_color
308 const Vec2f position =
309 label_layer->state == LABEL_LAYER_MOVE && label_layer->selection == (int) i
310 ? label_layer->inter_position
314 if (label_layer->state == LABEL_LAYER_EDIT_TEXT && label_layer->selection == (int) i) {
315 if (edit_field_render_world(
316 label_layer->edit_field,
322 if (camera_render_text(
324 texts + i * LABEL_LAYER_TEXT_MAX_SIZE,
328 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
335 if (label_layer->state == LABEL_LAYER_EDIT_ID && label_layer->selection == (int)i) {
336 if (edit_field_render_world(
337 label_layer->edit_field,
342 vec(0.0f, FONT_CHAR_HEIGHT),
343 LABELS_SIZE))) < 0) {
347 if (camera_render_text(
349 ids + i * LABEL_LAYER_ID_MAX_SIZE,
353 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
357 vec(0.0f, FONT_CHAR_HEIGHT),
358 LABELS_SIZE))) < 0) {
364 // TODO(#1160): Label Selection has to be internal (just like in Rect Layer)
365 if (active && label_layer->selection == (int) i) {
374 LABEL_LAYER_SELECTION_THICCNESS * 0.5f);
377 if (camera_draw_thicc_rect_screen(
381 LABEL_LAYER_SELECTION_THICCNESS) < 0) {
392 int label_layer_element_at(LabelLayer *label_layer,
395 trace_assert(label_layer);
397 Vec2f *positions = (Vec2f*)label_layer->positions.data;
399 const int n = (int) label_layer->texts.count;
400 for (int i = n - 1; i >= 0; --i) {
401 if (rect_contains_point(
415 void label_layer_delete_selected_label(LabelLayer *label_layer,
416 UndoHistory *undo_history)
418 trace_assert(label_layer);
419 trace_assert(label_layer->selection >= 0);
421 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_DELETE));
423 dynarray_delete_at(&label_layer->ids, (size_t)label_layer->selection);
424 dynarray_delete_at(&label_layer->positions, (size_t)label_layer->selection);
425 dynarray_delete_at(&label_layer->colors, (size_t)label_layer->selection);
426 dynarray_delete_at(&label_layer->texts, (size_t)label_layer->selection);
428 label_layer->selection = -1;
432 int label_layer_add_label(LabelLayer *label_layer,
436 UndoHistory *undo_history)
438 trace_assert(label_layer);
440 // TODO(#982): id generation code is duplicated in label_layer, point_layer and rect_layer
441 char id[LABEL_LAYER_ID_MAX_SIZE];
442 snprintf(id, LABEL_LAYER_ID_MAX_SIZE, "%s_%d",
443 label_layer->id_name_prefix,
444 label_layer->id_name_counter++);
446 size_t n = label_layer->ids.count;
448 dynarray_push(&label_layer->ids, id);
449 dynarray_push(&label_layer->positions, &position);
450 dynarray_push(&label_layer->colors, &color);
451 dynarray_push_empty(&label_layer->texts);
453 dynarray_pointer_at(&label_layer->texts, n),
455 min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, strlen(text)));
457 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_ADD));
463 void label_layer_swap_elements(LabelLayer *label_layer,
465 UndoHistory *undo_history)
467 trace_assert(label_layer);
468 trace_assert(undo_history);
469 trace_assert(a < label_layer->positions.count);
470 trace_assert(b < label_layer->positions.count);
472 dynarray_swap(&label_layer->ids, a, b);
473 dynarray_swap(&label_layer->positions, a, b);
474 dynarray_swap(&label_layer->colors, a, b);
475 dynarray_swap(&label_layer->texts, a, b);
477 LABEL_UNDO_PUSH(undo_history, create_label_undo_swap_context(label_layer, a, b));
481 int label_layer_idle_event(LabelLayer *label_layer,
482 const SDL_Event *event,
483 const Camera *camera,
484 UndoHistory *undo_history)
486 trace_assert(label_layer);
488 trace_assert(camera);
492 if (color_picker_event(
493 &label_layer->color_picker,
501 if (label_layer->selection >= 0) {
502 label_layer->state = LABEL_LAYER_RECOLOR;
503 label_layer->inter_color = color_picker_rgba(&label_layer->color_picker);
508 Color *colors = (Color*)label_layer->colors.data;
509 Vec2f *positions = (Vec2f*)label_layer->positions.data;
510 char *ids = (char*)label_layer->ids.data;
511 char *texts = (char*)label_layer->texts.data;
513 switch (event->type) {
514 case SDL_MOUSEBUTTONDOWN: {
515 switch (event->button.button) {
516 case SDL_BUTTON_LEFT: {
517 const Vec2f position = camera_map_screen(
522 const int element = label_layer_element_at(
527 label_layer->move_anchor = vec_sub(position, positions[element]);
528 label_layer->selection = element;
529 label_layer->state = LABEL_LAYER_MOVE;
530 label_layer->inter_position = positions[element];
532 label_layer->color_picker =
533 create_color_picker_from_rgba(colors[element]);
535 label_layer->selection = label_layer_add_label(
539 &label_layer->color_picker),
542 label_layer->state = LABEL_LAYER_EDIT_TEXT;
544 label_layer->edit_field,
545 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
547 label_layer->edit_field,
549 colors[label_layer->selection]);
550 SDL_StartTextInput();
557 switch (event->key.keysym.sym) {
559 if ((event->key.keysym.mod & KMOD_SHIFT)
560 && (label_layer->selection >= 0)
561 && ((size_t)(label_layer->selection + 1) < label_layer->positions.count)) {
562 label_layer_swap_elements(
564 (size_t) label_layer->selection,
565 (size_t) label_layer->selection + 1,
567 label_layer->selection++;
572 if ((event->key.keysym.mod & KMOD_SHIFT)
573 && (label_layer->selection > 0)
574 && ((size_t) label_layer->selection < label_layer->positions.count)) {
575 label_layer_swap_elements(
577 (size_t) label_layer->selection,
578 (size_t) label_layer->selection - 1,
580 label_layer->selection--;
585 if (label_layer->selection >= 0) {
586 label_layer->state = LABEL_LAYER_EDIT_TEXT;
588 label_layer->edit_field,
589 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
591 label_layer->edit_field,
593 colors[label_layer->selection]);
594 SDL_StartTextInput();
599 if (label_layer->selection >= 0) {
600 label_layer->state = LABEL_LAYER_EDIT_ID;
602 label_layer->edit_field,
603 ids + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE);
605 label_layer->edit_field,
607 color_invert(colors[label_layer->selection]));
608 SDL_StartTextInput();
613 if (label_layer->selection >= 0) {
614 label_layer_delete_selected_label(
617 label_layer->selection = -1;
622 if ((event->key.keysym.mod & KMOD_LCTRL) && label_layer->selection >= 0) {
624 dynarray_copy_to(&label_layer->texts, label_clipboard_text, (size_t)label_layer->selection);
625 dynarray_copy_to(&label_layer->colors, &label_clipboard_color, (size_t)label_layer->selection);
630 if ((event->key.keysym.mod & KMOD_LCTRL) && label_clipboard) {
632 SDL_GetMouseState(&x, &y);
633 Vec2f position = camera_map_screen(camera, x, y);
635 label_layer_add_label(
638 label_clipboard_color,
639 label_clipboard_text,
651 void snap_inter_position(LabelLayer *label_layer, float snap_threshold)
653 trace_assert(label_layer);
654 trace_assert(label_layer->selection >= 0);
655 trace_assert(label_layer->state == LABEL_LAYER_MOVE);
657 const size_t n = label_layer->positions.count;
658 Vec2f *positions = (Vec2f*)label_layer->positions.data;
660 Rect a = boundary_of_element(
662 (size_t) label_layer->selection,
663 label_layer->inter_position);
665 for (size_t i = 0; i < n; ++i) {
666 if (i == (size_t) label_layer->selection) continue;
668 const Rect b = boundary_of_element(label_layer, i, positions[i]);
670 if (segment_overlap(vec(a.x, a.x + a.w), vec(b.x, b.x + b.w))) {
671 snap_seg2seg(&label_layer->inter_position.y,
672 b.y, a.h, b.h, snap_threshold);
675 if (segment_overlap(vec(a.y, a.y + a.h), vec(b.y, b.y + b.h))) {
676 snap_seg2seg(&label_layer->inter_position.x,
677 b.x, a.w, b.w, snap_threshold);
683 int label_layer_move_event(LabelLayer *label_layer,
684 const SDL_Event *event,
685 const Camera *camera,
686 UndoHistory *undo_history)
688 trace_assert(label_layer);
690 trace_assert(camera);
691 trace_assert(label_layer->selection >= 0);
693 Vec2f *positions = (Vec2f*)label_layer->positions.data;
695 switch (event->type) {
696 case SDL_MOUSEMOTION: {
697 const Uint8 *state = SDL_GetKeyboardState(NULL);
698 const Vec2f mouse_pos = vec_sub(
703 label_layer->move_anchor);
705 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
706 label_layer->inter_position = mouse_pos;
708 const Vec2f label_pos = positions[label_layer->selection];
710 const float dx = fabsf(label_pos.x - mouse_pos.x);
711 const float dy = fabsf(label_pos.y - mouse_pos.y);
714 label_layer->inter_position = vec(mouse_pos.x, label_pos.y);
716 label_layer->inter_position = vec(label_pos.x, mouse_pos.y);
720 snap_inter_position(label_layer, SNAPPING_THRESHOLD);
723 case SDL_MOUSEBUTTONUP: {
724 switch (event->button.button) {
725 case SDL_BUTTON_LEFT: {
726 const float distance = vec_length(
727 vec_sub(label_layer->inter_position,
728 positions[label_layer->selection]));
730 if (distance > 1e-6) {
731 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
734 &label_layer->positions,
735 (size_t)label_layer->selection,
736 &label_layer->inter_position);
739 label_layer->state = LABEL_LAYER_IDLE;
749 int label_layer_edit_text_event(LabelLayer *label_layer,
750 const SDL_Event *event,
751 const Camera *camera,
752 UndoHistory *undo_history)
754 trace_assert(label_layer);
756 trace_assert(camera);
757 trace_assert(label_layer->selection >= 0);
759 switch (event->type) {
761 switch (event->key.keysym.sym) {
763 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
766 (char*)label_layer->texts.data + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE;
767 memset(text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
768 memcpy(text, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_TEXT_MAX_SIZE - 1);
769 label_layer->state = LABEL_LAYER_IDLE;
775 label_layer->state = LABEL_LAYER_IDLE;
783 return edit_field_event(label_layer->edit_field, event);
787 int label_layer_edit_id_event(LabelLayer *label_layer,
788 const SDL_Event *event,
789 const Camera *camera,
790 UndoHistory *undo_history)
792 trace_assert(label_layer);
794 trace_assert(camera);
795 trace_assert(undo_history);
796 trace_assert(label_layer->selection >= 0);
798 switch (event->type) {
800 switch (event->key.keysym.sym) {
802 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
805 (char*)label_layer->ids.data + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE;
806 memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
807 memcpy(id, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_ID_MAX_SIZE - 1);
808 label_layer->state = LABEL_LAYER_IDLE;
814 label_layer->state = LABEL_LAYER_IDLE;
822 return edit_field_event(label_layer->edit_field, event);
826 int label_layer_recolor_event(LabelLayer *label_layer,
827 const SDL_Event *event,
828 const Camera *camera,
829 UndoHistory *undo_history)
831 trace_assert(label_layer);
833 trace_assert(camera);
834 trace_assert(undo_history);
835 trace_assert(label_layer->selection >= 0);
839 if (color_picker_event(
840 &label_layer->color_picker,
848 label_layer->inter_color =
849 color_picker_rgba(&label_layer->color_picker);
851 if (!color_picker_drag(&label_layer->color_picker)) {
852 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
855 &label_layer->colors,
856 (size_t) label_layer->selection,
857 &label_layer->inter_color);
858 label_layer->state = LABEL_LAYER_IDLE;
865 int label_layer_event(LabelLayer *label_layer,
866 const SDL_Event *event,
867 const Camera *camera,
868 UndoHistory *undo_history)
870 trace_assert(label_layer);
872 trace_assert(camera);
873 trace_assert(undo_history);
875 switch (label_layer->state) {
876 case LABEL_LAYER_IDLE:
877 return label_layer_idle_event(label_layer, event, camera, undo_history);
879 case LABEL_LAYER_MOVE:
880 return label_layer_move_event(label_layer, event, camera, undo_history);
882 case LABEL_LAYER_EDIT_TEXT:
883 return label_layer_edit_text_event(label_layer, event, camera, undo_history);
885 case LABEL_LAYER_EDIT_ID:
886 return label_layer_edit_id_event(label_layer, event, camera, undo_history);
888 case LABEL_LAYER_RECOLOR:
889 return label_layer_recolor_event(label_layer, event, camera, undo_history);
895 size_t label_layer_count(const LabelLayer *label_layer)
897 return label_layer->ids.count;
900 char *label_layer_ids(const LabelLayer *label_layer)
902 return (char *)label_layer->ids.data;
905 Vec2f *label_layer_positions(const LabelLayer *label_layer)
907 return (Vec2f *)label_layer->positions.data;
910 Color *label_layer_colors(const LabelLayer *label_layer)
912 return (Color *)label_layer->colors.data;
915 char *labels_layer_texts(const LabelLayer *label_layer)
917 return (char *)label_layer->texts.data;
920 int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump)
922 trace_assert(label_layer);
923 trace_assert(filedump);
925 size_t n = label_layer->ids.count;
926 char *ids = (char *)label_layer->ids.data;
927 Vec2f *positions = (Vec2f *)label_layer->positions.data;
928 Color *colors = (Color *)label_layer->colors.data;
929 char *texts = (char *)label_layer->texts.data;
931 fprintf(filedump, "%zd\n", n);
932 for (size_t i = 0; i < n; ++i) {
933 fprintf(filedump, "%s %f %f ",
934 ids + LABEL_LAYER_ID_MAX_SIZE * i,
935 positions[i].x, positions[i].y);
936 color_hex_to_stream(colors[i], filedump);
937 fprintf(filedump, "\n%s\n", texts + i * LABEL_LAYER_TEXT_MAX_SIZE);