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
22 static int label_clipboard = 0;
23 static char label_clipboard_text[LABEL_LAYER_TEXT_MAX_SIZE];
24 static Color label_clipboard_color;
36 char id[LABEL_LAYER_ID_MAX_SIZE];
39 char text[LABEL_LAYER_TEXT_MAX_SIZE];
45 LabelUndoContext create_label_undo_swap_context(LabelLayer *label_layer,
46 size_t index, size_t index2)
48 trace_assert(label_layer);
49 trace_assert(index < label_layer->positions.count);
50 trace_assert(index2 < label_layer->positions.count);
52 LabelUndoContext undo_context;
53 undo_context.type = LABEL_UNDO_SWAP;
54 undo_context.layer = label_layer;
55 undo_context.index = index;
56 undo_context.index2 = index2;
61 LabelUndoContext create_label_undo_context(LabelLayer *label_layer, LabelUndoType type)
63 trace_assert(label_layer);
64 trace_assert(type != LABEL_UNDO_SWAP);
66 LabelUndoContext undo_context;
68 size_t index = type == LABEL_UNDO_ADD
69 ? label_layer->positions.count - 1
70 : (size_t)label_layer->selection;
72 undo_context.type = type;
73 undo_context.layer = label_layer;
74 dynarray_copy_to(&label_layer->ids, &undo_context.id, index);
75 dynarray_copy_to(&label_layer->positions, &undo_context.position, index);
76 dynarray_copy_to(&label_layer->colors, &undo_context.color, index);
77 dynarray_copy_to(&label_layer->texts, &undo_context.text, index);
78 undo_context.index = index;
84 void label_layer_undo(void *context, size_t context_size)
86 trace_assert(context);
87 trace_assert(sizeof(LabelUndoContext) == context_size);
89 LabelUndoContext *undo_context = context;
90 LabelLayer *label_layer = undo_context->layer;
92 switch (undo_context->type) {
93 case LABEL_UNDO_ADD: {
94 dynarray_delete_at(&label_layer->ids, undo_context->index);
95 dynarray_delete_at(&label_layer->positions, undo_context->index);
96 dynarray_delete_at(&label_layer->colors, undo_context->index);
97 dynarray_delete_at(&label_layer->texts, undo_context->index);
100 case LABEL_UNDO_DELETE: {
101 dynarray_insert_before(&label_layer->ids, undo_context->index, &undo_context->id);
102 dynarray_insert_before(&label_layer->positions, undo_context->index, &undo_context->position);
103 dynarray_insert_before(&label_layer->colors, undo_context->index, &undo_context->color);
104 dynarray_insert_before(&label_layer->texts, undo_context->index, &undo_context->text);
107 case LABEL_UNDO_UPDATE: {
108 dynarray_replace_at(&label_layer->ids, undo_context->index, &undo_context->id);
109 dynarray_replace_at(&label_layer->positions, undo_context->index, &undo_context->position);
110 dynarray_replace_at(&label_layer->colors, undo_context->index, &undo_context->color);
111 dynarray_replace_at(&label_layer->texts, undo_context->index, &undo_context->text);
114 case LABEL_UNDO_SWAP: {
115 dynarray_swap(&label_layer->ids, undo_context->index, undo_context->index2);
116 dynarray_swap(&label_layer->positions, undo_context->index, undo_context->index2);
117 dynarray_swap(&label_layer->colors, undo_context->index, undo_context->index2);
118 dynarray_swap(&label_layer->texts, undo_context->index, undo_context->index2);
123 #define LABEL_UNDO_PUSH(HISTORY, CONTEXT) \
125 LabelUndoContext context = (CONTEXT); \
134 LayerPtr label_layer_as_layer(LabelLayer *label_layer)
143 LabelLayer *create_label_layer(Memory *memory,
144 const char *id_name_prefix)
146 trace_assert(memory);
147 trace_assert(id_name_prefix);
149 LabelLayer *result = memory_alloc(memory, sizeof(LabelLayer));
150 memset(result, 0, sizeof(LabelLayer));
151 result->ids = create_dynarray(memory, sizeof(char) * LABEL_LAYER_ID_MAX_SIZE);
152 result->positions = create_dynarray(memory, sizeof(Vec2f));
153 result->colors = create_dynarray(memory, sizeof(Color));
154 result->texts = create_dynarray(memory, sizeof(char) * LABEL_LAYER_TEXT_MAX_SIZE);
155 result->color_picker = create_color_picker_from_rgba(COLOR_RED);
156 result->selection = -1;
157 result->edit_field.font_size = LABELS_SIZE;
158 result->edit_field.font_color = COLOR_RED;
159 result->id_name_prefix = id_name_prefix;
163 void label_layer_load(LabelLayer *label_layer,
167 trace_assert(label_layer);
168 trace_assert(memory);
171 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
172 char id[LABEL_LAYER_ID_MAX_SIZE];
173 char label_text[LABEL_LAYER_TEXT_MAX_SIZE];
174 for (int i = 0; i < n; ++i) {
175 String meta = trim(chop_by_delim(input, '\n'));
177 String string_id = trim(chop_word(&meta));
179 position.x = strtof(string_to_cstr(memory, trim(chop_word(&meta))), NULL);
180 position.y = strtof(string_to_cstr(memory, trim(chop_word(&meta))), NULL);
181 Color color = hexs(trim(chop_word(&meta)));
183 memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
187 min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, string_id.count));
189 String label_text_string =
190 trim(chop_by_delim(input, '\n'));
191 memset(label_text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
194 label_text_string.data,
195 min_size_t(LABEL_LAYER_TEXT_MAX_SIZE - 1,
196 label_text_string.count));
198 dynarray_push(&label_layer->ids, id);
199 dynarray_push(&label_layer->positions, &position);
200 dynarray_push(&label_layer->colors, &color);
201 dynarray_push(&label_layer->texts, label_text);
206 Rect boundary_of_element(const LabelLayer *label_layer,
210 trace_assert(i < label_layer->texts.count);
212 char *ids = (char *)label_layer->ids.data;
213 char *texts = (char *)label_layer->texts.data;
215 return rect_boundary2(
216 sprite_font_boundary_box(
219 texts + i * LABEL_LAYER_TEXT_MAX_SIZE),
220 sprite_font_boundary_box(
224 vec(0.0f, FONT_CHAR_HEIGHT),
227 ids + i * LABEL_LAYER_ID_MAX_SIZE));
230 int label_layer_render(const LabelLayer *label_layer,
231 const Camera *camera,
234 trace_assert(label_layer);
235 trace_assert(camera);
237 if (active && color_picker_render(&label_layer->color_picker, camera) < 0) {
241 size_t n = label_layer->ids.count;
242 char *ids = (char *)label_layer->ids.data;
243 Vec2f *positions = (Vec2f *)label_layer->positions.data;
244 Color *colors = (Color *)label_layer->colors.data;
245 char *texts = (char *)label_layer->texts.data;
247 /* TODO(#891): LabelLayer doesn't show the final position of Label after the animation */
248 for (size_t i = 0; i < n; ++i) {
249 const Color color = label_layer->state == LABEL_LAYER_RECOLOR && label_layer->selection == (int) i
250 ? label_layer->inter_color
253 const Vec2f position =
254 label_layer->state == LABEL_LAYER_MOVE && label_layer->selection == (int) i
255 ? label_layer->inter_position
259 if (label_layer->state == LABEL_LAYER_EDIT_TEXT && label_layer->selection == (int) i) {
260 if (edit_field_render_world(
261 &label_layer->edit_field,
267 if (camera_render_text(
269 texts + i * LABEL_LAYER_TEXT_MAX_SIZE,
273 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
280 if (label_layer->state == LABEL_LAYER_EDIT_ID && label_layer->selection == (int)i) {
281 if (edit_field_render_world(
282 &label_layer->edit_field,
287 vec(0.0f, FONT_CHAR_HEIGHT),
288 LABELS_SIZE))) < 0) {
292 if (camera_render_text(
294 ids + i * LABEL_LAYER_ID_MAX_SIZE,
298 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
302 vec(0.0f, FONT_CHAR_HEIGHT),
303 LABELS_SIZE))) < 0) {
309 if (active && label_layer->selection == (int) i) {
318 LABEL_LAYER_SELECTION_THICCNESS * 0.5f);
321 if (camera_draw_thicc_rect_screen(
325 LABEL_LAYER_SELECTION_THICCNESS) < 0) {
336 int label_layer_element_at(LabelLayer *label_layer,
339 trace_assert(label_layer);
341 Vec2f *positions = (Vec2f*)label_layer->positions.data;
343 const int n = (int) label_layer->texts.count;
344 for (int i = n - 1; i >= 0; --i) {
345 if (rect_contains_point(
359 void label_layer_delete_selected_label(LabelLayer *label_layer,
360 UndoHistory *undo_history)
362 trace_assert(label_layer);
363 trace_assert(label_layer->selection >= 0);
365 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_DELETE));
367 dynarray_delete_at(&label_layer->ids, (size_t)label_layer->selection);
368 dynarray_delete_at(&label_layer->positions, (size_t)label_layer->selection);
369 dynarray_delete_at(&label_layer->colors, (size_t)label_layer->selection);
370 dynarray_delete_at(&label_layer->texts, (size_t)label_layer->selection);
372 label_layer->selection = -1;
376 int label_layer_add_label(LabelLayer *label_layer,
380 UndoHistory *undo_history)
382 trace_assert(label_layer);
384 // TODO(#982): id generation code is duplicated in label_layer, point_layer and rect_layer
385 char id[LABEL_LAYER_ID_MAX_SIZE];
386 snprintf(id, LABEL_LAYER_ID_MAX_SIZE, "%s_%d",
387 label_layer->id_name_prefix,
388 label_layer->id_name_counter++);
390 size_t n = label_layer->ids.count;
392 dynarray_push(&label_layer->ids, id);
393 dynarray_push(&label_layer->positions, &position);
394 dynarray_push(&label_layer->colors, &color);
395 dynarray_push_empty(&label_layer->texts);
397 dynarray_pointer_at(&label_layer->texts, n),
399 min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, strlen(text)));
401 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_ADD));
407 void label_layer_swap_elements(LabelLayer *label_layer,
409 UndoHistory *undo_history)
411 trace_assert(label_layer);
412 trace_assert(undo_history);
413 trace_assert(a < label_layer->positions.count);
414 trace_assert(b < label_layer->positions.count);
416 dynarray_swap(&label_layer->ids, a, b);
417 dynarray_swap(&label_layer->positions, a, b);
418 dynarray_swap(&label_layer->colors, a, b);
419 dynarray_swap(&label_layer->texts, a, b);
421 LABEL_UNDO_PUSH(undo_history, create_label_undo_swap_context(label_layer, a, b));
425 int label_layer_idle_event(LabelLayer *label_layer,
426 const SDL_Event *event,
427 const Camera *camera,
428 UndoHistory *undo_history)
430 trace_assert(label_layer);
432 trace_assert(camera);
436 if (color_picker_event(
437 &label_layer->color_picker,
445 if (label_layer->selection >= 0) {
446 label_layer->state = LABEL_LAYER_RECOLOR;
447 label_layer->inter_color = color_picker_rgba(&label_layer->color_picker);
452 Color *colors = (Color*)label_layer->colors.data;
453 Vec2f *positions = (Vec2f*)label_layer->positions.data;
454 char *ids = (char*)label_layer->ids.data;
455 char *texts = (char*)label_layer->texts.data;
457 switch (event->type) {
458 case SDL_MOUSEBUTTONDOWN: {
459 switch (event->button.button) {
460 case SDL_BUTTON_LEFT: {
461 const Vec2f position = camera_map_screen(
466 const int element = label_layer_element_at(
471 label_layer->move_anchor = vec_sub(position, positions[element]);
472 label_layer->selection = element;
473 label_layer->state = LABEL_LAYER_MOVE;
474 label_layer->inter_position = positions[element];
476 label_layer->color_picker =
477 create_color_picker_from_rgba(colors[element]);
479 label_layer->selection = label_layer_add_label(
483 &label_layer->color_picker),
486 label_layer->state = LABEL_LAYER_EDIT_TEXT;
488 &label_layer->edit_field,
489 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
491 &label_layer->edit_field,
493 colors[label_layer->selection]);
494 SDL_StartTextInput();
501 switch (event->key.keysym.sym) {
503 if ((event->key.keysym.mod & KMOD_SHIFT)
504 && (label_layer->selection >= 0)
505 && ((size_t)(label_layer->selection + 1) < label_layer->positions.count)) {
506 label_layer_swap_elements(
508 (size_t) label_layer->selection,
509 (size_t) label_layer->selection + 1,
511 label_layer->selection++;
516 if ((event->key.keysym.mod & KMOD_SHIFT)
517 && (label_layer->selection > 0)
518 && ((size_t) label_layer->selection < label_layer->positions.count)) {
519 label_layer_swap_elements(
521 (size_t) label_layer->selection,
522 (size_t) label_layer->selection - 1,
524 label_layer->selection--;
529 if (label_layer->selection >= 0) {
530 label_layer->state = LABEL_LAYER_EDIT_TEXT;
532 &label_layer->edit_field,
533 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
535 &label_layer->edit_field,
537 colors[label_layer->selection]);
538 SDL_StartTextInput();
543 if (label_layer->selection >= 0) {
544 label_layer->state = LABEL_LAYER_EDIT_ID;
546 &label_layer->edit_field,
547 ids + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE);
549 &label_layer->edit_field,
551 color_invert(colors[label_layer->selection]));
552 SDL_StartTextInput();
557 if (label_layer->selection >= 0) {
558 label_layer_delete_selected_label(
561 label_layer->selection = -1;
566 if ((event->key.keysym.mod & KMOD_LCTRL) && label_layer->selection >= 0) {
568 dynarray_copy_to(&label_layer->texts, label_clipboard_text, (size_t)label_layer->selection);
569 dynarray_copy_to(&label_layer->colors, &label_clipboard_color, (size_t)label_layer->selection);
574 if ((event->key.keysym.mod & KMOD_LCTRL) && label_clipboard) {
576 SDL_GetMouseState(&x, &y);
577 Vec2f position = camera_map_screen(camera, x, y);
579 label_layer_add_label(
582 label_clipboard_color,
583 label_clipboard_text,
595 void snap_inter_position(LabelLayer *label_layer, float snap_threshold)
597 trace_assert(label_layer);
598 trace_assert(label_layer->selection >= 0);
599 trace_assert(label_layer->state == LABEL_LAYER_MOVE);
601 const size_t n = label_layer->positions.count;
602 Vec2f *positions = (Vec2f*)label_layer->positions.data;
604 Rect a = boundary_of_element(
606 (size_t) label_layer->selection,
607 label_layer->inter_position);
609 for (size_t i = 0; i < n; ++i) {
610 if (i == (size_t) label_layer->selection) continue;
612 const Rect b = boundary_of_element(label_layer, i, positions[i]);
614 if (segment_overlap(vec(a.x, a.x + a.w), vec(b.x, b.x + b.w))) {
615 snap_seg2seg(&label_layer->inter_position.y,
616 b.y, a.h, b.h, snap_threshold);
619 if (segment_overlap(vec(a.y, a.y + a.h), vec(b.y, b.y + b.h))) {
620 snap_seg2seg(&label_layer->inter_position.x,
621 b.x, a.w, b.w, snap_threshold);
627 int label_layer_move_event(LabelLayer *label_layer,
628 const SDL_Event *event,
629 const Camera *camera,
630 UndoHistory *undo_history)
632 trace_assert(label_layer);
634 trace_assert(camera);
635 trace_assert(label_layer->selection >= 0);
637 Vec2f *positions = (Vec2f*)label_layer->positions.data;
639 switch (event->type) {
640 case SDL_MOUSEMOTION: {
641 const Uint8 *state = SDL_GetKeyboardState(NULL);
642 const Vec2f mouse_pos = vec_sub(
647 label_layer->move_anchor);
649 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
650 label_layer->inter_position = mouse_pos;
652 const Vec2f label_pos = positions[label_layer->selection];
654 const float dx = fabsf(label_pos.x - mouse_pos.x);
655 const float dy = fabsf(label_pos.y - mouse_pos.y);
658 label_layer->inter_position = vec(mouse_pos.x, label_pos.y);
660 label_layer->inter_position = vec(label_pos.x, mouse_pos.y);
664 snap_inter_position(label_layer, SNAPPING_THRESHOLD);
667 case SDL_MOUSEBUTTONUP: {
668 switch (event->button.button) {
669 case SDL_BUTTON_LEFT: {
670 const float distance = vec_length(
671 vec_sub(label_layer->inter_position,
672 positions[label_layer->selection]));
674 if (distance > 1e-6) {
675 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
678 &label_layer->positions,
679 (size_t)label_layer->selection,
680 &label_layer->inter_position);
683 label_layer->state = LABEL_LAYER_IDLE;
693 int label_layer_edit_text_event(LabelLayer *label_layer,
694 const SDL_Event *event,
695 const Camera *camera,
696 UndoHistory *undo_history)
698 trace_assert(label_layer);
700 trace_assert(camera);
701 trace_assert(label_layer->selection >= 0);
703 switch (event->type) {
705 switch (event->key.keysym.sym) {
707 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
710 (char*)label_layer->texts.data + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE;
711 memset(text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
712 memcpy(text, edit_field_as_text(&label_layer->edit_field), LABEL_LAYER_TEXT_MAX_SIZE - 1);
713 label_layer->state = LABEL_LAYER_IDLE;
719 label_layer->state = LABEL_LAYER_IDLE;
727 return edit_field_event(&label_layer->edit_field, event);
731 int label_layer_edit_id_event(LabelLayer *label_layer,
732 const SDL_Event *event,
733 const Camera *camera,
734 UndoHistory *undo_history)
736 trace_assert(label_layer);
738 trace_assert(camera);
739 trace_assert(undo_history);
740 trace_assert(label_layer->selection >= 0);
742 switch (event->type) {
744 switch (event->key.keysym.sym) {
746 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
749 (char*)label_layer->ids.data + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE;
750 memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
751 memcpy(id, edit_field_as_text(&label_layer->edit_field), LABEL_LAYER_ID_MAX_SIZE - 1);
752 label_layer->state = LABEL_LAYER_IDLE;
758 label_layer->state = LABEL_LAYER_IDLE;
766 return edit_field_event(&label_layer->edit_field, event);
770 int label_layer_recolor_event(LabelLayer *label_layer,
771 const SDL_Event *event,
772 const Camera *camera,
773 UndoHistory *undo_history)
775 trace_assert(label_layer);
777 trace_assert(camera);
778 trace_assert(undo_history);
779 trace_assert(label_layer->selection >= 0);
783 if (color_picker_event(
784 &label_layer->color_picker,
792 label_layer->inter_color =
793 color_picker_rgba(&label_layer->color_picker);
795 if (!color_picker_drag(&label_layer->color_picker)) {
796 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
799 &label_layer->colors,
800 (size_t) label_layer->selection,
801 &label_layer->inter_color);
802 label_layer->state = LABEL_LAYER_IDLE;
809 int label_layer_event(LabelLayer *label_layer,
810 const SDL_Event *event,
811 const Camera *camera,
812 UndoHistory *undo_history)
814 trace_assert(label_layer);
816 trace_assert(camera);
817 trace_assert(undo_history);
819 switch (label_layer->state) {
820 case LABEL_LAYER_IDLE:
821 return label_layer_idle_event(label_layer, event, camera, undo_history);
823 case LABEL_LAYER_MOVE:
824 return label_layer_move_event(label_layer, event, camera, undo_history);
826 case LABEL_LAYER_EDIT_TEXT:
827 return label_layer_edit_text_event(label_layer, event, camera, undo_history);
829 case LABEL_LAYER_EDIT_ID:
830 return label_layer_edit_id_event(label_layer, event, camera, undo_history);
832 case LABEL_LAYER_RECOLOR:
833 return label_layer_recolor_event(label_layer, event, camera, undo_history);
839 size_t label_layer_count(const LabelLayer *label_layer)
841 return label_layer->ids.count;
844 char *label_layer_ids(const LabelLayer *label_layer)
846 return (char *)label_layer->ids.data;
849 Vec2f *label_layer_positions(const LabelLayer *label_layer)
851 return (Vec2f *)label_layer->positions.data;
854 Color *label_layer_colors(const LabelLayer *label_layer)
856 return (Color *)label_layer->colors.data;
859 char *labels_layer_texts(const LabelLayer *label_layer)
861 return (char *)label_layer->texts.data;
864 int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump)
866 trace_assert(label_layer);
867 trace_assert(filedump);
869 size_t n = label_layer->ids.count;
870 char *ids = (char *)label_layer->ids.data;
871 Vec2f *positions = (Vec2f *)label_layer->positions.data;
872 Color *colors = (Color *)label_layer->colors.data;
873 char *texts = (char *)label_layer->texts.data;
875 fprintf(filedump, "%zd\n", n);
876 for (size_t i = 0; i < n; ++i) {
877 fprintf(filedump, "%s %f %f ",
878 ids + LABEL_LAYER_ID_MAX_SIZE * i,
879 positions[i].x, positions[i].y);
880 color_hex_to_stream(colors[i], filedump);
881 fprintf(filedump, "\n%s\n", texts + i * LABEL_LAYER_TEXT_MAX_SIZE);