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 LabelLayer *create_label_layer_from_memory(Memory *memory,
160 const char *id_name_prefix)
162 trace_assert(memory);
163 trace_assert(id_name_prefix);
165 LabelLayer *result = memory_alloc(memory, sizeof(LabelLayer));
166 memset(result, 0, sizeof(LabelLayer));
167 result->ids = create_dynarray_from_memory(memory, sizeof(char) * LABEL_LAYER_ID_MAX_SIZE);
168 result->positions = create_dynarray_from_memory(memory, sizeof(Vec2f));
169 result->colors = create_dynarray_from_memory(memory, sizeof(Color));
170 result->texts = create_dynarray_from_memory(memory, sizeof(char) * LABEL_LAYER_TEXT_MAX_SIZE);
171 result->color_picker = create_color_picker_from_rgba(COLOR_RED);
172 result->selection = -1;
173 result->edit_field.font_size = LABELS_SIZE;
174 result->edit_field.font_color = COLOR_RED;
175 result->id_name_prefix = id_name_prefix;
179 void label_layer_load(LabelLayer *label_layer,
183 trace_assert(label_layer);
184 trace_assert(memory);
187 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
188 char id[LABEL_LAYER_ID_MAX_SIZE];
189 char label_text[LABEL_LAYER_TEXT_MAX_SIZE];
190 for (int i = 0; i < n; ++i) {
191 String meta = trim(chop_by_delim(input, '\n'));
193 String string_id = trim(chop_word(&meta));
195 position.x = strtof(string_to_cstr(memory, trim(chop_word(&meta))), NULL);
196 position.y = strtof(string_to_cstr(memory, trim(chop_word(&meta))), NULL);
197 Color color = hexs(trim(chop_word(&meta)));
199 memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
203 min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, string_id.count));
205 String label_text_string =
206 trim(chop_by_delim(input, '\n'));
207 memset(label_text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
210 label_text_string.data,
211 min_size_t(LABEL_LAYER_TEXT_MAX_SIZE - 1,
212 label_text_string.count));
214 dynarray_push(&label_layer->ids, id);
215 dynarray_push(&label_layer->positions, &position);
216 dynarray_push(&label_layer->colors, &color);
217 dynarray_push(&label_layer->texts, label_text);
222 Rect boundary_of_element(const LabelLayer *label_layer,
226 trace_assert(i < label_layer->texts.count);
228 char *ids = (char *)label_layer->ids.data;
229 char *texts = (char *)label_layer->texts.data;
231 return rect_boundary2(
232 sprite_font_boundary_box(
235 texts + i * LABEL_LAYER_TEXT_MAX_SIZE),
236 sprite_font_boundary_box(
240 vec(0.0f, FONT_CHAR_HEIGHT),
243 ids + i * LABEL_LAYER_ID_MAX_SIZE));
246 int label_layer_render(const LabelLayer *label_layer,
247 const Camera *camera,
250 trace_assert(label_layer);
251 trace_assert(camera);
253 if (active && color_picker_render(&label_layer->color_picker, camera) < 0) {
257 size_t n = label_layer->ids.count;
258 char *ids = (char *)label_layer->ids.data;
259 Vec2f *positions = (Vec2f *)label_layer->positions.data;
260 Color *colors = (Color *)label_layer->colors.data;
261 char *texts = (char *)label_layer->texts.data;
263 /* TODO(#891): LabelLayer doesn't show the final position of Label after the animation */
264 for (size_t i = 0; i < n; ++i) {
265 const Color color = label_layer->state == LABEL_LAYER_RECOLOR && label_layer->selection == (int) i
266 ? label_layer->inter_color
269 const Vec2f position =
270 label_layer->state == LABEL_LAYER_MOVE && label_layer->selection == (int) i
271 ? label_layer->inter_position
275 if (label_layer->state == LABEL_LAYER_EDIT_TEXT && label_layer->selection == (int) i) {
276 if (edit_field_render_world(
277 &label_layer->edit_field,
283 if (camera_render_text(
285 texts + i * LABEL_LAYER_TEXT_MAX_SIZE,
289 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
296 if (label_layer->state == LABEL_LAYER_EDIT_ID && label_layer->selection == (int)i) {
297 if (edit_field_render_world(
298 &label_layer->edit_field,
303 vec(0.0f, FONT_CHAR_HEIGHT),
304 LABELS_SIZE))) < 0) {
308 if (camera_render_text(
310 ids + i * LABEL_LAYER_ID_MAX_SIZE,
314 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
318 vec(0.0f, FONT_CHAR_HEIGHT),
319 LABELS_SIZE))) < 0) {
325 // TODO(#1160): Label Selection has to be internal (just like in Rect Layer)
326 if (active && label_layer->selection == (int) i) {
335 LABEL_LAYER_SELECTION_THICCNESS * 0.5f);
338 if (camera_draw_thicc_rect_screen(
342 LABEL_LAYER_SELECTION_THICCNESS) < 0) {
353 int label_layer_element_at(LabelLayer *label_layer,
356 trace_assert(label_layer);
358 Vec2f *positions = (Vec2f*)label_layer->positions.data;
360 const int n = (int) label_layer->texts.count;
361 for (int i = n - 1; i >= 0; --i) {
362 if (rect_contains_point(
376 void label_layer_delete_selected_label(LabelLayer *label_layer,
377 UndoHistory *undo_history)
379 trace_assert(label_layer);
380 trace_assert(label_layer->selection >= 0);
382 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_DELETE));
384 dynarray_delete_at(&label_layer->ids, (size_t)label_layer->selection);
385 dynarray_delete_at(&label_layer->positions, (size_t)label_layer->selection);
386 dynarray_delete_at(&label_layer->colors, (size_t)label_layer->selection);
387 dynarray_delete_at(&label_layer->texts, (size_t)label_layer->selection);
389 label_layer->selection = -1;
393 int label_layer_add_label(LabelLayer *label_layer,
397 UndoHistory *undo_history)
399 trace_assert(label_layer);
401 // TODO(#982): id generation code is duplicated in label_layer, point_layer and rect_layer
402 char id[LABEL_LAYER_ID_MAX_SIZE];
403 snprintf(id, LABEL_LAYER_ID_MAX_SIZE, "%s_%d",
404 label_layer->id_name_prefix,
405 label_layer->id_name_counter++);
407 size_t n = label_layer->ids.count;
409 dynarray_push(&label_layer->ids, id);
410 dynarray_push(&label_layer->positions, &position);
411 dynarray_push(&label_layer->colors, &color);
412 dynarray_push_empty(&label_layer->texts);
414 dynarray_pointer_at(&label_layer->texts, n),
416 min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, strlen(text)));
418 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_ADD));
424 void label_layer_swap_elements(LabelLayer *label_layer,
426 UndoHistory *undo_history)
428 trace_assert(label_layer);
429 trace_assert(undo_history);
430 trace_assert(a < label_layer->positions.count);
431 trace_assert(b < label_layer->positions.count);
433 dynarray_swap(&label_layer->ids, a, b);
434 dynarray_swap(&label_layer->positions, a, b);
435 dynarray_swap(&label_layer->colors, a, b);
436 dynarray_swap(&label_layer->texts, a, b);
438 LABEL_UNDO_PUSH(undo_history, create_label_undo_swap_context(label_layer, a, b));
442 int label_layer_idle_event(LabelLayer *label_layer,
443 const SDL_Event *event,
444 const Camera *camera,
445 UndoHistory *undo_history)
447 trace_assert(label_layer);
449 trace_assert(camera);
453 if (color_picker_event(
454 &label_layer->color_picker,
462 if (label_layer->selection >= 0) {
463 label_layer->state = LABEL_LAYER_RECOLOR;
464 label_layer->inter_color = color_picker_rgba(&label_layer->color_picker);
469 Color *colors = (Color*)label_layer->colors.data;
470 Vec2f *positions = (Vec2f*)label_layer->positions.data;
471 char *ids = (char*)label_layer->ids.data;
472 char *texts = (char*)label_layer->texts.data;
474 switch (event->type) {
475 case SDL_MOUSEBUTTONDOWN: {
476 switch (event->button.button) {
477 case SDL_BUTTON_LEFT: {
478 const Vec2f position = camera_map_screen(
483 const int element = label_layer_element_at(
488 label_layer->move_anchor = vec_sub(position, positions[element]);
489 label_layer->selection = element;
490 label_layer->state = LABEL_LAYER_MOVE;
491 label_layer->inter_position = positions[element];
493 label_layer->color_picker =
494 create_color_picker_from_rgba(colors[element]);
496 label_layer->selection = label_layer_add_label(
500 &label_layer->color_picker),
503 label_layer->state = LABEL_LAYER_EDIT_TEXT;
505 &label_layer->edit_field,
506 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
508 &label_layer->edit_field,
510 colors[label_layer->selection]);
511 SDL_StartTextInput();
518 switch (event->key.keysym.sym) {
520 if ((event->key.keysym.mod & KMOD_SHIFT)
521 && (label_layer->selection >= 0)
522 && ((size_t)(label_layer->selection + 1) < label_layer->positions.count)) {
523 label_layer_swap_elements(
525 (size_t) label_layer->selection,
526 (size_t) label_layer->selection + 1,
528 label_layer->selection++;
533 if ((event->key.keysym.mod & KMOD_SHIFT)
534 && (label_layer->selection > 0)
535 && ((size_t) label_layer->selection < label_layer->positions.count)) {
536 label_layer_swap_elements(
538 (size_t) label_layer->selection,
539 (size_t) label_layer->selection - 1,
541 label_layer->selection--;
546 if (label_layer->selection >= 0) {
547 label_layer->state = LABEL_LAYER_EDIT_TEXT;
549 &label_layer->edit_field,
550 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
552 &label_layer->edit_field,
554 colors[label_layer->selection]);
555 SDL_StartTextInput();
560 if (label_layer->selection >= 0) {
561 label_layer->state = LABEL_LAYER_EDIT_ID;
563 &label_layer->edit_field,
564 ids + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE);
566 &label_layer->edit_field,
568 color_invert(colors[label_layer->selection]));
569 SDL_StartTextInput();
574 if (label_layer->selection >= 0) {
575 label_layer_delete_selected_label(
578 label_layer->selection = -1;
583 if ((event->key.keysym.mod & KMOD_LCTRL) && label_layer->selection >= 0) {
585 dynarray_copy_to(&label_layer->texts, label_clipboard_text, (size_t)label_layer->selection);
586 dynarray_copy_to(&label_layer->colors, &label_clipboard_color, (size_t)label_layer->selection);
591 if ((event->key.keysym.mod & KMOD_LCTRL) && label_clipboard) {
593 SDL_GetMouseState(&x, &y);
594 Vec2f position = camera_map_screen(camera, x, y);
596 label_layer_add_label(
599 label_clipboard_color,
600 label_clipboard_text,
612 void snap_inter_position(LabelLayer *label_layer, float snap_threshold)
614 trace_assert(label_layer);
615 trace_assert(label_layer->selection >= 0);
616 trace_assert(label_layer->state == LABEL_LAYER_MOVE);
618 const size_t n = label_layer->positions.count;
619 Vec2f *positions = (Vec2f*)label_layer->positions.data;
621 Rect a = boundary_of_element(
623 (size_t) label_layer->selection,
624 label_layer->inter_position);
626 for (size_t i = 0; i < n; ++i) {
627 if (i == (size_t) label_layer->selection) continue;
629 const Rect b = boundary_of_element(label_layer, i, positions[i]);
631 if (segment_overlap(vec(a.x, a.x + a.w), vec(b.x, b.x + b.w))) {
632 snap_seg2seg(&label_layer->inter_position.y,
633 b.y, a.h, b.h, snap_threshold);
636 if (segment_overlap(vec(a.y, a.y + a.h), vec(b.y, b.y + b.h))) {
637 snap_seg2seg(&label_layer->inter_position.x,
638 b.x, a.w, b.w, snap_threshold);
644 int label_layer_move_event(LabelLayer *label_layer,
645 const SDL_Event *event,
646 const Camera *camera,
647 UndoHistory *undo_history)
649 trace_assert(label_layer);
651 trace_assert(camera);
652 trace_assert(label_layer->selection >= 0);
654 Vec2f *positions = (Vec2f*)label_layer->positions.data;
656 switch (event->type) {
657 case SDL_MOUSEMOTION: {
658 const Uint8 *state = SDL_GetKeyboardState(NULL);
659 const Vec2f mouse_pos = vec_sub(
664 label_layer->move_anchor);
666 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
667 label_layer->inter_position = mouse_pos;
669 const Vec2f label_pos = positions[label_layer->selection];
671 const float dx = fabsf(label_pos.x - mouse_pos.x);
672 const float dy = fabsf(label_pos.y - mouse_pos.y);
675 label_layer->inter_position = vec(mouse_pos.x, label_pos.y);
677 label_layer->inter_position = vec(label_pos.x, mouse_pos.y);
681 snap_inter_position(label_layer, SNAPPING_THRESHOLD);
684 case SDL_MOUSEBUTTONUP: {
685 switch (event->button.button) {
686 case SDL_BUTTON_LEFT: {
687 const float distance = vec_length(
688 vec_sub(label_layer->inter_position,
689 positions[label_layer->selection]));
691 if (distance > 1e-6) {
692 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
695 &label_layer->positions,
696 (size_t)label_layer->selection,
697 &label_layer->inter_position);
700 label_layer->state = LABEL_LAYER_IDLE;
710 int label_layer_edit_text_event(LabelLayer *label_layer,
711 const SDL_Event *event,
712 const Camera *camera,
713 UndoHistory *undo_history)
715 trace_assert(label_layer);
717 trace_assert(camera);
718 trace_assert(label_layer->selection >= 0);
720 switch (event->type) {
722 switch (event->key.keysym.sym) {
724 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
727 (char*)label_layer->texts.data + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE;
728 memset(text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
729 memcpy(text, edit_field_as_text(&label_layer->edit_field), LABEL_LAYER_TEXT_MAX_SIZE - 1);
730 label_layer->state = LABEL_LAYER_IDLE;
736 label_layer->state = LABEL_LAYER_IDLE;
744 return edit_field_event(&label_layer->edit_field, event);
748 int label_layer_edit_id_event(LabelLayer *label_layer,
749 const SDL_Event *event,
750 const Camera *camera,
751 UndoHistory *undo_history)
753 trace_assert(label_layer);
755 trace_assert(camera);
756 trace_assert(undo_history);
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->ids.data + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE;
767 memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
768 memcpy(id, edit_field_as_text(&label_layer->edit_field), LABEL_LAYER_ID_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_recolor_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);
800 if (color_picker_event(
801 &label_layer->color_picker,
809 label_layer->inter_color =
810 color_picker_rgba(&label_layer->color_picker);
812 if (!color_picker_drag(&label_layer->color_picker)) {
813 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
816 &label_layer->colors,
817 (size_t) label_layer->selection,
818 &label_layer->inter_color);
819 label_layer->state = LABEL_LAYER_IDLE;
826 int label_layer_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);
836 switch (label_layer->state) {
837 case LABEL_LAYER_IDLE:
838 return label_layer_idle_event(label_layer, event, camera, undo_history);
840 case LABEL_LAYER_MOVE:
841 return label_layer_move_event(label_layer, event, camera, undo_history);
843 case LABEL_LAYER_EDIT_TEXT:
844 return label_layer_edit_text_event(label_layer, event, camera, undo_history);
846 case LABEL_LAYER_EDIT_ID:
847 return label_layer_edit_id_event(label_layer, event, camera, undo_history);
849 case LABEL_LAYER_RECOLOR:
850 return label_layer_recolor_event(label_layer, event, camera, undo_history);
856 size_t label_layer_count(const LabelLayer *label_layer)
858 return label_layer->ids.count;
861 char *label_layer_ids(const LabelLayer *label_layer)
863 return (char *)label_layer->ids.data;
866 Vec2f *label_layer_positions(const LabelLayer *label_layer)
868 return (Vec2f *)label_layer->positions.data;
871 Color *label_layer_colors(const LabelLayer *label_layer)
873 return (Color *)label_layer->colors.data;
876 char *labels_layer_texts(const LabelLayer *label_layer)
878 return (char *)label_layer->texts.data;
881 int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump)
883 trace_assert(label_layer);
884 trace_assert(filedump);
886 size_t n = label_layer->ids.count;
887 char *ids = (char *)label_layer->ids.data;
888 Vec2f *positions = (Vec2f *)label_layer->positions.data;
889 Color *colors = (Color *)label_layer->colors.data;
890 char *texts = (char *)label_layer->texts.data;
892 fprintf(filedump, "%zd\n", n);
893 for (size_t i = 0; i < n; ++i) {
894 fprintf(filedump, "%s %f %f ",
895 ids + LABEL_LAYER_ID_MAX_SIZE * i,
896 positions[i].x, positions[i].y);
897 color_hex_to_stream(colors[i], filedump);
898 fprintf(filedump, "\n%s\n", texts + i * LABEL_LAYER_TEXT_MAX_SIZE);