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
24 static int label_clipboard = 0;
25 static char label_clipboard_text[LABEL_LAYER_TEXT_MAX_SIZE];
26 static Color label_clipboard_color;
38 char id[LABEL_LAYER_ID_MAX_SIZE];
41 char text[LABEL_LAYER_TEXT_MAX_SIZE];
47 LabelUndoContext create_label_undo_swap_context(LabelLayer *label_layer,
48 size_t index, size_t index2)
50 trace_assert(label_layer);
51 trace_assert(index < label_layer->positions.count);
52 trace_assert(index2 < label_layer->positions.count);
54 LabelUndoContext undo_context;
55 undo_context.type = LABEL_UNDO_SWAP;
56 undo_context.layer = label_layer;
57 undo_context.index = index;
58 undo_context.index2 = index2;
63 LabelUndoContext create_label_undo_context(LabelLayer *label_layer, LabelUndoType type)
65 trace_assert(label_layer);
66 trace_assert(type != LABEL_UNDO_SWAP);
68 LabelUndoContext undo_context;
70 size_t index = type == LABEL_UNDO_ADD
71 ? label_layer->positions.count - 1
72 : (size_t)label_layer->selection;
74 undo_context.type = type;
75 undo_context.layer = label_layer;
76 dynarray_copy_to(&label_layer->ids, &undo_context.id, index);
77 dynarray_copy_to(&label_layer->positions, &undo_context.position, index);
78 dynarray_copy_to(&label_layer->colors, &undo_context.color, index);
79 dynarray_copy_to(&label_layer->texts, &undo_context.text, index);
80 undo_context.index = index;
86 void label_layer_undo(void *context, size_t context_size)
88 trace_assert(context);
89 trace_assert(sizeof(LabelUndoContext) == context_size);
91 LabelUndoContext *undo_context = context;
92 LabelLayer *label_layer = undo_context->layer;
94 switch (undo_context->type) {
95 case LABEL_UNDO_ADD: {
96 dynarray_delete_at(&label_layer->ids, undo_context->index);
97 dynarray_delete_at(&label_layer->positions, undo_context->index);
98 dynarray_delete_at(&label_layer->colors, undo_context->index);
99 dynarray_delete_at(&label_layer->texts, undo_context->index);
102 case LABEL_UNDO_DELETE: {
103 dynarray_insert_before(&label_layer->ids, undo_context->index, &undo_context->id);
104 dynarray_insert_before(&label_layer->positions, undo_context->index, &undo_context->position);
105 dynarray_insert_before(&label_layer->colors, undo_context->index, &undo_context->color);
106 dynarray_insert_before(&label_layer->texts, undo_context->index, &undo_context->text);
109 case LABEL_UNDO_UPDATE: {
110 dynarray_replace_at(&label_layer->ids, undo_context->index, &undo_context->id);
111 dynarray_replace_at(&label_layer->positions, undo_context->index, &undo_context->position);
112 dynarray_replace_at(&label_layer->colors, undo_context->index, &undo_context->color);
113 dynarray_replace_at(&label_layer->texts, undo_context->index, &undo_context->text);
116 case LABEL_UNDO_SWAP: {
117 dynarray_swap(&label_layer->ids, undo_context->index, undo_context->index2);
118 dynarray_swap(&label_layer->positions, undo_context->index, undo_context->index2);
119 dynarray_swap(&label_layer->colors, undo_context->index, undo_context->index2);
120 dynarray_swap(&label_layer->texts, undo_context->index, undo_context->index2);
125 #define LABEL_UNDO_PUSH(HISTORY, CONTEXT) \
127 LabelUndoContext context = (CONTEXT); \
136 LayerPtr label_layer_as_layer(LabelLayer *label_layer)
145 LabelLayer create_label_layer(const char *id_name_prefix)
147 LabelLayer result = {0};
148 result.ids = create_dynarray(sizeof(char) * LABEL_LAYER_ID_MAX_SIZE);
149 result.positions = create_dynarray(sizeof(Vec2f));
150 result.colors = create_dynarray(sizeof(Color));
151 result.texts = create_dynarray(sizeof(char) * LABEL_LAYER_TEXT_MAX_SIZE);
152 result.color_picker = create_color_picker_from_rgba(COLOR_RED);
153 result.selection = -1;
154 result.edit_field.font_size = LABELS_SIZE;
155 result.edit_field.font_color = COLOR_RED;
156 result.id_name_prefix = id_name_prefix;
160 LabelLayer chop_label_layer(Memory *memory,
162 const char *id_name_prefix)
164 trace_assert(memory);
166 trace_assert(id_name_prefix);
168 LabelLayer result = create_label_layer(id_name_prefix);
170 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
171 char id[LABEL_LAYER_ID_MAX_SIZE];
172 char label_text[LABEL_LAYER_TEXT_MAX_SIZE];
173 for (int i = 0; i < n; ++i) {
174 String meta = trim(chop_by_delim(input, '\n'));
176 String string_id = trim(chop_word(&meta));
178 position.x = strtof(string_to_cstr(memory, trim(chop_word(&meta))), NULL);
179 position.y = strtof(string_to_cstr(memory, trim(chop_word(&meta))), NULL);
180 Color color = hexs(trim(chop_word(&meta)));
182 memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
186 min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, string_id.count));
188 String label_text_string =
189 trim(chop_by_delim(input, '\n'));
190 memset(label_text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
193 label_text_string.data,
194 min_size_t(LABEL_LAYER_TEXT_MAX_SIZE - 1,
195 label_text_string.count));
197 dynarray_push(&result.ids, id);
198 dynarray_push(&result.positions, &position);
199 dynarray_push(&result.colors, &color);
200 dynarray_push(&result.texts, label_text);
207 Rect boundary_of_element(const LabelLayer *label_layer,
211 trace_assert(i < label_layer->texts.count);
213 char *ids = (char *)label_layer->ids.data;
214 char *texts = (char *)label_layer->texts.data;
216 return rect_boundary2(
217 sprite_font_boundary_box(
220 texts + i * LABEL_LAYER_TEXT_MAX_SIZE),
221 sprite_font_boundary_box(
225 vec(0.0f, FONT_CHAR_HEIGHT),
228 ids + i * LABEL_LAYER_ID_MAX_SIZE));
231 int label_layer_render(const LabelLayer *label_layer,
232 const Camera *camera,
235 trace_assert(label_layer);
236 trace_assert(camera);
238 if (active && color_picker_render(&label_layer->color_picker, camera) < 0) {
242 size_t n = label_layer->ids.count;
243 char *ids = (char *)label_layer->ids.data;
244 Vec2f *positions = (Vec2f *)label_layer->positions.data;
245 Color *colors = (Color *)label_layer->colors.data;
246 char *texts = (char *)label_layer->texts.data;
248 /* TODO(#891): LabelLayer doesn't show the final position of Label after the animation */
249 for (size_t i = 0; i < n; ++i) {
250 const Color color = label_layer->state == LABEL_LAYER_RECOLOR && label_layer->selection == (int) i
251 ? label_layer->inter_color
254 const Vec2f position =
255 label_layer->state == LABEL_LAYER_MOVE && label_layer->selection == (int) i
256 ? label_layer->inter_position
260 if (label_layer->state == LABEL_LAYER_EDIT_TEXT && label_layer->selection == (int) i) {
261 if (edit_field_render_world(
262 &label_layer->edit_field,
268 if (camera_render_text(
270 texts + i * LABEL_LAYER_TEXT_MAX_SIZE,
274 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
281 if (label_layer->state == LABEL_LAYER_EDIT_ID && label_layer->selection == (int)i) {
282 if (edit_field_render_world(
283 &label_layer->edit_field,
288 vec(0.0f, FONT_CHAR_HEIGHT),
289 LABELS_SIZE))) < 0) {
293 if (camera_render_text(
295 ids + i * LABEL_LAYER_ID_MAX_SIZE,
299 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
303 vec(0.0f, FONT_CHAR_HEIGHT),
304 LABELS_SIZE))) < 0) {
310 // TODO(#1160): Label Selection has to be internal (just like in Rect Layer)
311 if (active && label_layer->selection == (int) i) {
320 LABEL_LAYER_SELECTION_THICCNESS * 0.5f);
323 if (camera_draw_thicc_rect_screen(
327 LABEL_LAYER_SELECTION_THICCNESS) < 0) {
338 int label_layer_element_at(LabelLayer *label_layer,
341 trace_assert(label_layer);
343 Vec2f *positions = (Vec2f*)label_layer->positions.data;
345 const int n = (int) label_layer->texts.count;
346 for (int i = n - 1; i >= 0; --i) {
347 if (rect_contains_point(
361 void label_layer_delete_selected_label(LabelLayer *label_layer,
362 UndoHistory *undo_history)
364 trace_assert(label_layer);
365 trace_assert(label_layer->selection >= 0);
367 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_DELETE));
369 dynarray_delete_at(&label_layer->ids, (size_t)label_layer->selection);
370 dynarray_delete_at(&label_layer->positions, (size_t)label_layer->selection);
371 dynarray_delete_at(&label_layer->colors, (size_t)label_layer->selection);
372 dynarray_delete_at(&label_layer->texts, (size_t)label_layer->selection);
374 label_layer->selection = -1;
378 int label_layer_add_label(LabelLayer *label_layer,
382 UndoHistory *undo_history)
384 trace_assert(label_layer);
386 // TODO(#982): id generation code is duplicated in label_layer, point_layer and rect_layer
387 char id[LABEL_LAYER_ID_MAX_SIZE];
388 snprintf(id, LABEL_LAYER_ID_MAX_SIZE, "%s_%d",
389 label_layer->id_name_prefix,
390 label_layer->id_name_counter++);
392 size_t n = label_layer->ids.count;
394 dynarray_push(&label_layer->ids, id);
395 dynarray_push(&label_layer->positions, &position);
396 dynarray_push(&label_layer->colors, &color);
397 dynarray_push_empty(&label_layer->texts);
399 dynarray_pointer_at(&label_layer->texts, n),
401 min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, strlen(text)));
403 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_ADD));
409 void label_layer_swap_elements(LabelLayer *label_layer,
411 UndoHistory *undo_history)
413 trace_assert(label_layer);
414 trace_assert(undo_history);
415 trace_assert(a < label_layer->positions.count);
416 trace_assert(b < label_layer->positions.count);
418 dynarray_swap(&label_layer->ids, a, b);
419 dynarray_swap(&label_layer->positions, a, b);
420 dynarray_swap(&label_layer->colors, a, b);
421 dynarray_swap(&label_layer->texts, a, b);
423 LABEL_UNDO_PUSH(undo_history, create_label_undo_swap_context(label_layer, a, b));
427 int label_layer_idle_event(LabelLayer *label_layer,
428 const SDL_Event *event,
429 const Camera *camera,
430 UndoHistory *undo_history)
432 trace_assert(label_layer);
434 trace_assert(camera);
438 if (color_picker_event(
439 &label_layer->color_picker,
447 if (label_layer->selection >= 0) {
448 label_layer->state = LABEL_LAYER_RECOLOR;
449 label_layer->inter_color = color_picker_rgba(&label_layer->color_picker);
454 Color *colors = (Color*)label_layer->colors.data;
455 Vec2f *positions = (Vec2f*)label_layer->positions.data;
456 char *ids = (char*)label_layer->ids.data;
457 char *texts = (char*)label_layer->texts.data;
459 switch (event->type) {
460 case SDL_MOUSEBUTTONDOWN: {
461 switch (event->button.button) {
462 case SDL_BUTTON_LEFT: {
463 const Vec2f position = camera_map_screen(
468 const int element = label_layer_element_at(
473 label_layer->move_anchor = vec_sub(position, positions[element]);
474 label_layer->selection = element;
475 label_layer->state = LABEL_LAYER_MOVE;
476 label_layer->inter_position = positions[element];
478 label_layer->color_picker =
479 create_color_picker_from_rgba(colors[element]);
481 label_layer->selection = label_layer_add_label(
485 &label_layer->color_picker),
488 label_layer->state = LABEL_LAYER_EDIT_TEXT;
490 &label_layer->edit_field,
491 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
493 &label_layer->edit_field,
495 colors[label_layer->selection]);
496 SDL_StartTextInput();
503 switch (event->key.keysym.sym) {
505 if ((event->key.keysym.mod & KMOD_SHIFT)
506 && (label_layer->selection >= 0)
507 && ((size_t)(label_layer->selection + 1) < label_layer->positions.count)) {
508 label_layer_swap_elements(
510 (size_t) label_layer->selection,
511 (size_t) label_layer->selection + 1,
513 label_layer->selection++;
518 if ((event->key.keysym.mod & KMOD_SHIFT)
519 && (label_layer->selection > 0)
520 && ((size_t) label_layer->selection < label_layer->positions.count)) {
521 label_layer_swap_elements(
523 (size_t) label_layer->selection,
524 (size_t) label_layer->selection - 1,
526 label_layer->selection--;
531 if (label_layer->selection >= 0) {
532 label_layer->state = LABEL_LAYER_EDIT_TEXT;
534 &label_layer->edit_field,
535 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
537 &label_layer->edit_field,
539 colors[label_layer->selection]);
540 SDL_StartTextInput();
545 if (label_layer->selection >= 0) {
546 label_layer->state = LABEL_LAYER_EDIT_ID;
548 &label_layer->edit_field,
549 ids + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE);
551 &label_layer->edit_field,
553 color_invert(colors[label_layer->selection]));
554 SDL_StartTextInput();
559 if (label_layer->selection >= 0) {
560 label_layer_delete_selected_label(
563 label_layer->selection = -1;
568 if ((event->key.keysym.mod & KMOD_LCTRL) && label_layer->selection >= 0) {
570 dynarray_copy_to(&label_layer->texts, label_clipboard_text, (size_t)label_layer->selection);
571 dynarray_copy_to(&label_layer->colors, &label_clipboard_color, (size_t)label_layer->selection);
576 if ((event->key.keysym.mod & KMOD_LCTRL) && label_clipboard) {
578 SDL_GetMouseState(&x, &y);
579 Vec2f position = camera_map_screen(camera, x, y);
581 label_layer_add_label(
584 label_clipboard_color,
585 label_clipboard_text,
597 void snap_inter_position(LabelLayer *label_layer, float snap_threshold)
599 trace_assert(label_layer);
600 trace_assert(label_layer->selection >= 0);
601 trace_assert(label_layer->state == LABEL_LAYER_MOVE);
603 const size_t n = label_layer->positions.count;
604 Vec2f *positions = (Vec2f*)label_layer->positions.data;
606 Rect a = boundary_of_element(
608 (size_t) label_layer->selection,
609 label_layer->inter_position);
611 for (size_t i = 0; i < n; ++i) {
612 if (i == (size_t) label_layer->selection) continue;
614 const Rect b = boundary_of_element(label_layer, i, positions[i]);
616 if (segment_overlap(vec(a.x, a.x + a.w), vec(b.x, b.x + b.w))) {
617 snap_seg2seg(&label_layer->inter_position.y,
618 b.y, a.h, b.h, snap_threshold);
621 if (segment_overlap(vec(a.y, a.y + a.h), vec(b.y, b.y + b.h))) {
622 snap_seg2seg(&label_layer->inter_position.x,
623 b.x, a.w, b.w, snap_threshold);
629 int label_layer_move_event(LabelLayer *label_layer,
630 const SDL_Event *event,
631 const Camera *camera,
632 UndoHistory *undo_history)
634 trace_assert(label_layer);
636 trace_assert(camera);
637 trace_assert(label_layer->selection >= 0);
639 Vec2f *positions = (Vec2f*)label_layer->positions.data;
641 switch (event->type) {
642 case SDL_MOUSEMOTION: {
643 const Uint8 *state = SDL_GetKeyboardState(NULL);
644 const Vec2f mouse_pos = vec_sub(
649 label_layer->move_anchor);
651 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
652 label_layer->inter_position = mouse_pos;
654 const Vec2f label_pos = positions[label_layer->selection];
656 const float dx = fabsf(label_pos.x - mouse_pos.x);
657 const float dy = fabsf(label_pos.y - mouse_pos.y);
660 label_layer->inter_position = vec(mouse_pos.x, label_pos.y);
662 label_layer->inter_position = vec(label_pos.x, mouse_pos.y);
666 snap_inter_position(label_layer, SNAPPING_THRESHOLD);
669 case SDL_MOUSEBUTTONUP: {
670 switch (event->button.button) {
671 case SDL_BUTTON_LEFT: {
672 const float distance = vec_length(
673 vec_sub(label_layer->inter_position,
674 positions[label_layer->selection]));
676 if (distance > 1e-6) {
677 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
680 &label_layer->positions,
681 (size_t)label_layer->selection,
682 &label_layer->inter_position);
685 label_layer->state = LABEL_LAYER_IDLE;
695 int label_layer_edit_text_event(LabelLayer *label_layer,
696 const SDL_Event *event,
697 const Camera *camera,
698 UndoHistory *undo_history)
700 trace_assert(label_layer);
702 trace_assert(camera);
703 trace_assert(label_layer->selection >= 0);
705 switch (event->type) {
707 switch (event->key.keysym.sym) {
709 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
712 (char*)label_layer->texts.data + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE;
713 memset(text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
714 memcpy(text, edit_field_as_text(&label_layer->edit_field), LABEL_LAYER_TEXT_MAX_SIZE - 1);
715 label_layer->state = LABEL_LAYER_IDLE;
721 label_layer->state = LABEL_LAYER_IDLE;
729 return edit_field_event(&label_layer->edit_field, event);
733 int label_layer_edit_id_event(LabelLayer *label_layer,
734 const SDL_Event *event,
735 const Camera *camera,
736 UndoHistory *undo_history)
738 trace_assert(label_layer);
740 trace_assert(camera);
741 trace_assert(undo_history);
742 trace_assert(label_layer->selection >= 0);
744 switch (event->type) {
746 switch (event->key.keysym.sym) {
748 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
751 (char*)label_layer->ids.data + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE;
752 memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
753 memcpy(id, edit_field_as_text(&label_layer->edit_field), LABEL_LAYER_ID_MAX_SIZE - 1);
754 label_layer->state = LABEL_LAYER_IDLE;
760 label_layer->state = LABEL_LAYER_IDLE;
768 return edit_field_event(&label_layer->edit_field, event);
772 int label_layer_recolor_event(LabelLayer *label_layer,
773 const SDL_Event *event,
774 const Camera *camera,
775 UndoHistory *undo_history)
777 trace_assert(label_layer);
779 trace_assert(camera);
780 trace_assert(undo_history);
781 trace_assert(label_layer->selection >= 0);
785 if (color_picker_event(
786 &label_layer->color_picker,
794 label_layer->inter_color =
795 color_picker_rgba(&label_layer->color_picker);
797 if (!color_picker_drag(&label_layer->color_picker)) {
798 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
801 &label_layer->colors,
802 (size_t) label_layer->selection,
803 &label_layer->inter_color);
804 label_layer->state = LABEL_LAYER_IDLE;
811 int label_layer_event(LabelLayer *label_layer,
812 const SDL_Event *event,
813 const Camera *camera,
814 UndoHistory *undo_history)
816 trace_assert(label_layer);
818 trace_assert(camera);
819 trace_assert(undo_history);
821 switch (label_layer->state) {
822 case LABEL_LAYER_IDLE:
823 return label_layer_idle_event(label_layer, event, camera, undo_history);
825 case LABEL_LAYER_MOVE:
826 return label_layer_move_event(label_layer, event, camera, undo_history);
828 case LABEL_LAYER_EDIT_TEXT:
829 return label_layer_edit_text_event(label_layer, event, camera, undo_history);
831 case LABEL_LAYER_EDIT_ID:
832 return label_layer_edit_id_event(label_layer, event, camera, undo_history);
834 case LABEL_LAYER_RECOLOR:
835 return label_layer_recolor_event(label_layer, event, camera, undo_history);
841 size_t label_layer_count(const LabelLayer *label_layer)
843 return label_layer->ids.count;
846 char *label_layer_ids(const LabelLayer *label_layer)
848 return (char *)label_layer->ids.data;
851 Vec2f *label_layer_positions(const LabelLayer *label_layer)
853 return (Vec2f *)label_layer->positions.data;
856 Color *label_layer_colors(const LabelLayer *label_layer)
858 return (Color *)label_layer->colors.data;
861 char *labels_layer_texts(const LabelLayer *label_layer)
863 return (char *)label_layer->texts.data;
866 int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump)
868 trace_assert(label_layer);
869 trace_assert(filedump);
871 size_t n = label_layer->ids.count;
872 char *ids = (char *)label_layer->ids.data;
873 Vec2f *positions = (Vec2f *)label_layer->positions.data;
874 Color *colors = (Color *)label_layer->colors.data;
875 char *texts = (char *)label_layer->texts.data;
877 fprintf(filedump, "%zd\n", n);
878 for (size_t i = 0; i < n; ++i) {
879 fprintf(filedump, "%s %f %f ",
880 ids + LABEL_LAYER_ID_MAX_SIZE * i,
881 positions[i].x, positions[i].y);
882 color_hex_to_stream(colors[i], filedump);
883 fprintf(filedump, "\n%s\n", texts + i * LABEL_LAYER_TEXT_MAX_SIZE);