5 #include "system/stacktrace.h"
6 #include "system/nth_alloc.h"
7 #include "system/str.h"
8 #include "system/log.h"
10 #include "label_layer.h"
13 #include "game/camera.h"
14 #include "color_picker.h"
15 #include "ui/edit_field.h"
16 #include "math/extrema.h"
19 #define LABEL_LAYER_SELECTION_THICCNESS 5.0f
21 // TODO(#1139): Label Layer does not support snapping
23 static int label_clipboard = 0;
24 static char label_clipboard_text[LABEL_LAYER_TEXT_MAX_SIZE];
25 static Color label_clipboard_color;
37 char id[LABEL_LAYER_ID_MAX_SIZE];
40 char text[LABEL_LAYER_TEXT_MAX_SIZE];
46 LabelUndoContext create_label_undo_swap_context(LabelLayer *label_layer,
47 size_t index, size_t index2)
49 trace_assert(label_layer);
50 trace_assert(index < label_layer->positions.count);
51 trace_assert(index2 < label_layer->positions.count);
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;
62 LabelUndoContext create_label_undo_context(LabelLayer *label_layer, LabelUndoType type)
64 trace_assert(label_layer);
65 trace_assert(type != LABEL_UNDO_SWAP);
67 LabelUndoContext undo_context;
69 size_t index = type == LABEL_UNDO_ADD
70 ? label_layer->positions.count - 1
71 : (size_t)label_layer->selection;
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;
85 void label_layer_undo(void *context, size_t context_size)
87 trace_assert(context);
88 trace_assert(sizeof(LabelUndoContext) == context_size);
90 LabelUndoContext *undo_context = context;
91 LabelLayer *label_layer = undo_context->layer;
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);
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);
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);
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);
124 #define LABEL_UNDO_PUSH(HISTORY, CONTEXT) \
126 LabelUndoContext context = (CONTEXT); \
135 LayerPtr label_layer_as_layer(LabelLayer *label_layer)
144 LabelLayer create_label_layer(const char *id_name_prefix)
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;
159 void label_layer_reload(LabelLayer *label_layer,
163 trace_assert(label_layer);
164 trace_assert(memory);
167 label_layer_clean(label_layer);
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'));
175 String string_id = trim(chop_word(&meta));
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)));
181 memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
185 min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, string_id.count));
187 String label_text_string =
188 trim(chop_by_delim(input, '\n'));
189 memset(label_text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
192 label_text_string.data,
193 min_size_t(LABEL_LAYER_TEXT_MAX_SIZE - 1,
194 label_text_string.count));
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);
203 void label_layer_clean(LabelLayer *label_layer)
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);
213 Rect boundary_of_element(const LabelLayer *label_layer,
217 trace_assert(i < label_layer->texts.count);
219 char *ids = (char *)label_layer->ids.data;
220 char *texts = (char *)label_layer->texts.data;
222 return rect_boundary2(
223 sprite_font_boundary_box(
226 texts + i * LABEL_LAYER_TEXT_MAX_SIZE),
227 sprite_font_boundary_box(
231 vec(0.0f, FONT_CHAR_HEIGHT),
234 ids + i * LABEL_LAYER_ID_MAX_SIZE));
237 int label_layer_render(const LabelLayer *label_layer,
238 const Camera *camera,
241 trace_assert(label_layer);
242 trace_assert(camera);
244 if (active && color_picker_render(&label_layer->color_picker, camera) < 0) {
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;
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
260 const Vec2f position =
261 label_layer->state == LABEL_LAYER_MOVE && label_layer->selection == (int) i
262 ? label_layer->inter_position
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,
274 if (camera_render_text(
276 texts + i * LABEL_LAYER_TEXT_MAX_SIZE,
280 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
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,
294 vec(0.0f, FONT_CHAR_HEIGHT),
295 LABELS_SIZE))) < 0) {
299 if (camera_render_text(
301 ids + i * LABEL_LAYER_ID_MAX_SIZE,
305 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
309 vec(0.0f, FONT_CHAR_HEIGHT),
310 LABELS_SIZE))) < 0) {
316 // TODO(#1160): Label Selection has to be internal (just like in Rect Layer)
317 if (active && label_layer->selection == (int) i) {
326 LABEL_LAYER_SELECTION_THICCNESS * 0.5f);
329 if (camera_draw_thicc_rect_screen(
333 LABEL_LAYER_SELECTION_THICCNESS) < 0) {
344 int label_layer_element_at(LabelLayer *label_layer,
347 trace_assert(label_layer);
349 Vec2f *positions = (Vec2f*)label_layer->positions.data;
351 const int n = (int) label_layer->texts.count;
352 for (int i = n - 1; i >= 0; --i) {
353 if (rect_contains_point(
367 void label_layer_delete_selected_label(LabelLayer *label_layer,
368 UndoHistory *undo_history)
370 trace_assert(label_layer);
371 trace_assert(label_layer->selection >= 0);
373 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_DELETE));
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);
380 label_layer->selection = -1;
384 int label_layer_add_label(LabelLayer *label_layer,
388 UndoHistory *undo_history)
390 trace_assert(label_layer);
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++);
398 size_t n = label_layer->ids.count;
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);
405 dynarray_pointer_at(&label_layer->texts, n),
407 min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, strlen(text)));
409 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_ADD));
415 void label_layer_swap_elements(LabelLayer *label_layer,
417 UndoHistory *undo_history)
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);
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);
429 LABEL_UNDO_PUSH(undo_history, create_label_undo_swap_context(label_layer, a, b));
433 int label_layer_idle_event(LabelLayer *label_layer,
434 const SDL_Event *event,
435 const Camera *camera,
436 UndoHistory *undo_history)
438 trace_assert(label_layer);
440 trace_assert(camera);
444 if (color_picker_event(
445 &label_layer->color_picker,
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);
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;
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(
474 const int element = label_layer_element_at(
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];
484 label_layer->color_picker =
485 create_color_picker_from_rgba(colors[element]);
487 label_layer->selection = label_layer_add_label(
491 &label_layer->color_picker),
494 label_layer->state = LABEL_LAYER_EDIT_TEXT;
496 &label_layer->edit_field,
497 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
499 &label_layer->edit_field,
501 colors[label_layer->selection]);
502 SDL_StartTextInput();
509 switch (event->key.keysym.sym) {
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(
516 (size_t) label_layer->selection,
517 (size_t) label_layer->selection + 1,
519 label_layer->selection++;
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(
529 (size_t) label_layer->selection,
530 (size_t) label_layer->selection - 1,
532 label_layer->selection--;
537 if (label_layer->selection >= 0) {
538 label_layer->state = LABEL_LAYER_EDIT_TEXT;
540 &label_layer->edit_field,
541 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
543 &label_layer->edit_field,
545 colors[label_layer->selection]);
546 SDL_StartTextInput();
551 if (label_layer->selection >= 0) {
552 label_layer->state = LABEL_LAYER_EDIT_ID;
554 &label_layer->edit_field,
555 ids + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE);
557 &label_layer->edit_field,
559 color_invert(colors[label_layer->selection]));
560 SDL_StartTextInput();
565 if (label_layer->selection >= 0) {
566 label_layer_delete_selected_label(
569 label_layer->selection = -1;
574 if ((event->key.keysym.mod & KMOD_LCTRL) && label_layer->selection >= 0) {
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);
582 if ((event->key.keysym.mod & KMOD_LCTRL) && label_clipboard) {
584 SDL_GetMouseState(&x, &y);
585 Vec2f position = camera_map_screen(camera, x, y);
587 label_layer_add_label(
590 label_clipboard_color,
591 label_clipboard_text,
603 void snap_inter_position(LabelLayer *label_layer, float snap_threshold)
605 trace_assert(label_layer);
606 trace_assert(label_layer->selection >= 0);
607 trace_assert(label_layer->state == LABEL_LAYER_MOVE);
609 const size_t n = label_layer->positions.count;
610 Vec2f *positions = (Vec2f*)label_layer->positions.data;
612 Rect a = boundary_of_element(
614 (size_t) label_layer->selection,
615 label_layer->inter_position);
617 for (size_t i = 0; i < n; ++i) {
618 if (i == (size_t) label_layer->selection) continue;
620 const Rect b = boundary_of_element(label_layer, i, positions[i]);
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);
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);
635 int label_layer_move_event(LabelLayer *label_layer,
636 const SDL_Event *event,
637 const Camera *camera,
638 UndoHistory *undo_history)
640 trace_assert(label_layer);
642 trace_assert(camera);
643 trace_assert(label_layer->selection >= 0);
645 Vec2f *positions = (Vec2f*)label_layer->positions.data;
647 switch (event->type) {
648 case SDL_MOUSEMOTION: {
649 const Uint8 *state = SDL_GetKeyboardState(NULL);
650 const Vec2f mouse_pos = vec_sub(
655 label_layer->move_anchor);
657 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
658 label_layer->inter_position = mouse_pos;
660 const Vec2f label_pos = positions[label_layer->selection];
662 const float dx = fabsf(label_pos.x - mouse_pos.x);
663 const float dy = fabsf(label_pos.y - mouse_pos.y);
666 label_layer->inter_position = vec(mouse_pos.x, label_pos.y);
668 label_layer->inter_position = vec(label_pos.x, mouse_pos.y);
672 snap_inter_position(label_layer, SNAPPING_THRESHOLD);
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]));
682 if (distance > 1e-6) {
683 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
686 &label_layer->positions,
687 (size_t)label_layer->selection,
688 &label_layer->inter_position);
691 label_layer->state = LABEL_LAYER_IDLE;
701 int label_layer_edit_text_event(LabelLayer *label_layer,
702 const SDL_Event *event,
703 const Camera *camera,
704 UndoHistory *undo_history)
706 trace_assert(label_layer);
708 trace_assert(camera);
709 trace_assert(label_layer->selection >= 0);
711 switch (event->type) {
713 switch (event->key.keysym.sym) {
715 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
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;
727 label_layer->state = LABEL_LAYER_IDLE;
735 return edit_field_event(&label_layer->edit_field, event);
739 int label_layer_edit_id_event(LabelLayer *label_layer,
740 const SDL_Event *event,
741 const Camera *camera,
742 UndoHistory *undo_history)
744 trace_assert(label_layer);
746 trace_assert(camera);
747 trace_assert(undo_history);
748 trace_assert(label_layer->selection >= 0);
750 switch (event->type) {
752 switch (event->key.keysym.sym) {
754 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
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;
766 label_layer->state = LABEL_LAYER_IDLE;
774 return edit_field_event(&label_layer->edit_field, event);
778 int label_layer_recolor_event(LabelLayer *label_layer,
779 const SDL_Event *event,
780 const Camera *camera,
781 UndoHistory *undo_history)
783 trace_assert(label_layer);
785 trace_assert(camera);
786 trace_assert(undo_history);
787 trace_assert(label_layer->selection >= 0);
791 if (color_picker_event(
792 &label_layer->color_picker,
800 label_layer->inter_color =
801 color_picker_rgba(&label_layer->color_picker);
803 if (!color_picker_drag(&label_layer->color_picker)) {
804 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
807 &label_layer->colors,
808 (size_t) label_layer->selection,
809 &label_layer->inter_color);
810 label_layer->state = LABEL_LAYER_IDLE;
817 int label_layer_event(LabelLayer *label_layer,
818 const SDL_Event *event,
819 const Camera *camera,
820 UndoHistory *undo_history)
822 trace_assert(label_layer);
824 trace_assert(camera);
825 trace_assert(undo_history);
827 switch (label_layer->state) {
828 case LABEL_LAYER_IDLE:
829 return label_layer_idle_event(label_layer, event, camera, undo_history);
831 case LABEL_LAYER_MOVE:
832 return label_layer_move_event(label_layer, event, camera, undo_history);
834 case LABEL_LAYER_EDIT_TEXT:
835 return label_layer_edit_text_event(label_layer, event, camera, undo_history);
837 case LABEL_LAYER_EDIT_ID:
838 return label_layer_edit_id_event(label_layer, event, camera, undo_history);
840 case LABEL_LAYER_RECOLOR:
841 return label_layer_recolor_event(label_layer, event, camera, undo_history);
847 size_t label_layer_count(const LabelLayer *label_layer)
849 return label_layer->ids.count;
852 char *label_layer_ids(const LabelLayer *label_layer)
854 return (char *)label_layer->ids.data;
857 Vec2f *label_layer_positions(const LabelLayer *label_layer)
859 return (Vec2f *)label_layer->positions.data;
862 Color *label_layer_colors(const LabelLayer *label_layer)
864 return (Color *)label_layer->colors.data;
867 char *labels_layer_texts(const LabelLayer *label_layer)
869 return (char *)label_layer->texts.data;
872 int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump)
874 trace_assert(label_layer);
875 trace_assert(filedump);
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;
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);