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(Memory *memory,
145 const char *id_name_prefix)
147 trace_assert(memory);
148 trace_assert(id_name_prefix);
150 LabelLayer *result = memory_alloc(memory, sizeof(LabelLayer));
151 memset(result, 0, sizeof(LabelLayer));
152 result->ids = create_dynarray(memory, sizeof(char) * LABEL_LAYER_ID_MAX_SIZE);
153 result->positions = create_dynarray(memory, sizeof(Vec2f));
154 result->colors = create_dynarray(memory, sizeof(Color));
155 result->texts = create_dynarray(memory, sizeof(char) * LABEL_LAYER_TEXT_MAX_SIZE);
156 result->color_picker = create_color_picker_from_rgba(COLOR_RED);
157 result->selection = -1;
158 result->edit_field.font_size = LABELS_SIZE;
159 result->edit_field.font_color = COLOR_RED;
160 result->id_name_prefix = id_name_prefix;
164 void label_layer_load(LabelLayer *label_layer,
168 trace_assert(label_layer);
169 trace_assert(memory);
172 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
173 char id[LABEL_LAYER_ID_MAX_SIZE];
174 char label_text[LABEL_LAYER_TEXT_MAX_SIZE];
175 for (int i = 0; i < n; ++i) {
176 String meta = trim(chop_by_delim(input, '\n'));
178 String string_id = trim(chop_word(&meta));
180 position.x = strtof(string_to_cstr(memory, trim(chop_word(&meta))), NULL);
181 position.y = strtof(string_to_cstr(memory, trim(chop_word(&meta))), NULL);
182 Color color = hexs(trim(chop_word(&meta)));
184 memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
188 min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, string_id.count));
190 String label_text_string =
191 trim(chop_by_delim(input, '\n'));
192 memset(label_text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
195 label_text_string.data,
196 min_size_t(LABEL_LAYER_TEXT_MAX_SIZE - 1,
197 label_text_string.count));
199 dynarray_push(&label_layer->ids, id);
200 dynarray_push(&label_layer->positions, &position);
201 dynarray_push(&label_layer->colors, &color);
202 dynarray_push(&label_layer->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 if (active && label_layer->selection == (int) i) {
319 LABEL_LAYER_SELECTION_THICCNESS * 0.5f);
322 if (camera_draw_thicc_rect_screen(
326 LABEL_LAYER_SELECTION_THICCNESS) < 0) {
337 int label_layer_element_at(LabelLayer *label_layer,
340 trace_assert(label_layer);
342 Vec2f *positions = (Vec2f*)label_layer->positions.data;
344 const int n = (int) label_layer->texts.count;
345 for (int i = n - 1; i >= 0; --i) {
346 if (rect_contains_point(
360 void label_layer_delete_selected_label(LabelLayer *label_layer,
361 UndoHistory *undo_history)
363 trace_assert(label_layer);
364 trace_assert(label_layer->selection >= 0);
366 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_DELETE));
368 dynarray_delete_at(&label_layer->ids, (size_t)label_layer->selection);
369 dynarray_delete_at(&label_layer->positions, (size_t)label_layer->selection);
370 dynarray_delete_at(&label_layer->colors, (size_t)label_layer->selection);
371 dynarray_delete_at(&label_layer->texts, (size_t)label_layer->selection);
373 label_layer->selection = -1;
377 int label_layer_add_label(LabelLayer *label_layer,
381 UndoHistory *undo_history)
383 trace_assert(label_layer);
385 // TODO(#982): id generation code is duplicated in label_layer, point_layer and rect_layer
386 char id[LABEL_LAYER_ID_MAX_SIZE];
387 snprintf(id, LABEL_LAYER_ID_MAX_SIZE, "%s_%d",
388 label_layer->id_name_prefix,
389 label_layer->id_name_counter++);
391 size_t n = label_layer->ids.count;
393 dynarray_push(&label_layer->ids, id);
394 dynarray_push(&label_layer->positions, &position);
395 dynarray_push(&label_layer->colors, &color);
396 dynarray_push_empty(&label_layer->texts);
398 dynarray_pointer_at(&label_layer->texts, n),
400 min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, strlen(text)));
402 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_ADD));
408 void label_layer_swap_elements(LabelLayer *label_layer,
410 UndoHistory *undo_history)
412 trace_assert(label_layer);
413 trace_assert(undo_history);
414 trace_assert(a < label_layer->positions.count);
415 trace_assert(b < label_layer->positions.count);
417 dynarray_swap(&label_layer->ids, a, b);
418 dynarray_swap(&label_layer->positions, a, b);
419 dynarray_swap(&label_layer->colors, a, b);
420 dynarray_swap(&label_layer->texts, a, b);
422 LABEL_UNDO_PUSH(undo_history, create_label_undo_swap_context(label_layer, a, b));
426 int label_layer_idle_event(LabelLayer *label_layer,
427 const SDL_Event *event,
428 const Camera *camera,
429 UndoHistory *undo_history)
431 trace_assert(label_layer);
433 trace_assert(camera);
437 if (color_picker_event(
438 &label_layer->color_picker,
446 if (label_layer->selection >= 0) {
447 label_layer->state = LABEL_LAYER_RECOLOR;
448 label_layer->inter_color = color_picker_rgba(&label_layer->color_picker);
453 Color *colors = (Color*)label_layer->colors.data;
454 Vec2f *positions = (Vec2f*)label_layer->positions.data;
455 char *ids = (char*)label_layer->ids.data;
456 char *texts = (char*)label_layer->texts.data;
458 switch (event->type) {
459 case SDL_MOUSEBUTTONDOWN: {
460 switch (event->button.button) {
461 case SDL_BUTTON_LEFT: {
462 const Vec2f position = camera_map_screen(
467 const int element = label_layer_element_at(
472 label_layer->move_anchor = vec_sub(position, positions[element]);
473 label_layer->selection = element;
474 label_layer->state = LABEL_LAYER_MOVE;
475 label_layer->inter_position = positions[element];
477 label_layer->color_picker =
478 create_color_picker_from_rgba(colors[element]);
480 label_layer->selection = label_layer_add_label(
484 &label_layer->color_picker),
487 label_layer->state = LABEL_LAYER_EDIT_TEXT;
489 &label_layer->edit_field,
490 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
492 &label_layer->edit_field,
494 colors[label_layer->selection]);
495 SDL_StartTextInput();
502 switch (event->key.keysym.sym) {
504 if ((event->key.keysym.mod & KMOD_SHIFT)
505 && (label_layer->selection >= 0)
506 && ((size_t)(label_layer->selection + 1) < label_layer->positions.count)) {
507 label_layer_swap_elements(
509 (size_t) label_layer->selection,
510 (size_t) label_layer->selection + 1,
512 label_layer->selection++;
517 if ((event->key.keysym.mod & KMOD_SHIFT)
518 && (label_layer->selection > 0)
519 && ((size_t) label_layer->selection < label_layer->positions.count)) {
520 label_layer_swap_elements(
522 (size_t) label_layer->selection,
523 (size_t) label_layer->selection - 1,
525 label_layer->selection--;
530 if (label_layer->selection >= 0) {
531 label_layer->state = LABEL_LAYER_EDIT_TEXT;
533 &label_layer->edit_field,
534 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
536 &label_layer->edit_field,
538 colors[label_layer->selection]);
539 SDL_StartTextInput();
544 if (label_layer->selection >= 0) {
545 label_layer->state = LABEL_LAYER_EDIT_ID;
547 &label_layer->edit_field,
548 ids + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE);
550 &label_layer->edit_field,
552 color_invert(colors[label_layer->selection]));
553 SDL_StartTextInput();
558 if (label_layer->selection >= 0) {
559 label_layer_delete_selected_label(
562 label_layer->selection = -1;
567 if ((event->key.keysym.mod & KMOD_LCTRL) && label_layer->selection >= 0) {
569 dynarray_copy_to(&label_layer->texts, label_clipboard_text, (size_t)label_layer->selection);
570 dynarray_copy_to(&label_layer->colors, &label_clipboard_color, (size_t)label_layer->selection);
575 if ((event->key.keysym.mod & KMOD_LCTRL) && label_clipboard) {
577 SDL_GetMouseState(&x, &y);
578 Vec2f position = camera_map_screen(camera, x, y);
580 label_layer_add_label(
583 label_clipboard_color,
584 label_clipboard_text,
596 void snap_inter_position(LabelLayer *label_layer, float snap_threshold)
598 trace_assert(label_layer);
599 trace_assert(label_layer->selection >= 0);
600 trace_assert(label_layer->state == LABEL_LAYER_MOVE);
602 const size_t n = label_layer->positions.count;
603 Vec2f *positions = (Vec2f*)label_layer->positions.data;
605 Rect a = boundary_of_element(
607 (size_t) label_layer->selection,
608 label_layer->inter_position);
610 for (size_t i = 0; i < n; ++i) {
611 if (i == (size_t) label_layer->selection) continue;
613 const Rect b = boundary_of_element(label_layer, i, positions[i]);
615 if (segment_overlap(vec(a.x, a.x + a.w), vec(b.x, b.x + b.w))) {
616 snap_seg2seg(&label_layer->inter_position.y,
617 b.y, a.h, b.h, snap_threshold);
620 if (segment_overlap(vec(a.y, a.y + a.h), vec(b.y, b.y + b.h))) {
621 snap_seg2seg(&label_layer->inter_position.x,
622 b.x, a.w, b.w, snap_threshold);
628 int label_layer_move_event(LabelLayer *label_layer,
629 const SDL_Event *event,
630 const Camera *camera,
631 UndoHistory *undo_history)
633 trace_assert(label_layer);
635 trace_assert(camera);
636 trace_assert(label_layer->selection >= 0);
638 Vec2f *positions = (Vec2f*)label_layer->positions.data;
640 switch (event->type) {
641 case SDL_MOUSEMOTION: {
642 const Uint8 *state = SDL_GetKeyboardState(NULL);
643 const Vec2f mouse_pos = vec_sub(
648 label_layer->move_anchor);
650 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
651 label_layer->inter_position = mouse_pos;
653 const Vec2f label_pos = positions[label_layer->selection];
655 const float dx = fabsf(label_pos.x - mouse_pos.x);
656 const float dy = fabsf(label_pos.y - mouse_pos.y);
659 label_layer->inter_position = vec(mouse_pos.x, label_pos.y);
661 label_layer->inter_position = vec(label_pos.x, mouse_pos.y);
665 snap_inter_position(label_layer, SNAPPING_THRESHOLD);
668 case SDL_MOUSEBUTTONUP: {
669 switch (event->button.button) {
670 case SDL_BUTTON_LEFT: {
671 const float distance = vec_length(
672 vec_sub(label_layer->inter_position,
673 positions[label_layer->selection]));
675 if (distance > 1e-6) {
676 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
679 &label_layer->positions,
680 (size_t)label_layer->selection,
681 &label_layer->inter_position);
684 label_layer->state = LABEL_LAYER_IDLE;
694 int label_layer_edit_text_event(LabelLayer *label_layer,
695 const SDL_Event *event,
696 const Camera *camera,
697 UndoHistory *undo_history)
699 trace_assert(label_layer);
701 trace_assert(camera);
702 trace_assert(label_layer->selection >= 0);
704 switch (event->type) {
706 switch (event->key.keysym.sym) {
708 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
711 (char*)label_layer->texts.data + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE;
712 memset(text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
713 memcpy(text, edit_field_as_text(&label_layer->edit_field), LABEL_LAYER_TEXT_MAX_SIZE - 1);
714 label_layer->state = LABEL_LAYER_IDLE;
720 label_layer->state = LABEL_LAYER_IDLE;
728 return edit_field_event(&label_layer->edit_field, event);
732 int label_layer_edit_id_event(LabelLayer *label_layer,
733 const SDL_Event *event,
734 const Camera *camera,
735 UndoHistory *undo_history)
737 trace_assert(label_layer);
739 trace_assert(camera);
740 trace_assert(undo_history);
741 trace_assert(label_layer->selection >= 0);
743 switch (event->type) {
745 switch (event->key.keysym.sym) {
747 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
750 (char*)label_layer->ids.data + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE;
751 memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
752 memcpy(id, edit_field_as_text(&label_layer->edit_field), LABEL_LAYER_ID_MAX_SIZE - 1);
753 label_layer->state = LABEL_LAYER_IDLE;
759 label_layer->state = LABEL_LAYER_IDLE;
767 return edit_field_event(&label_layer->edit_field, event);
771 int label_layer_recolor_event(LabelLayer *label_layer,
772 const SDL_Event *event,
773 const Camera *camera,
774 UndoHistory *undo_history)
776 trace_assert(label_layer);
778 trace_assert(camera);
779 trace_assert(undo_history);
780 trace_assert(label_layer->selection >= 0);
784 if (color_picker_event(
785 &label_layer->color_picker,
793 label_layer->inter_color =
794 color_picker_rgba(&label_layer->color_picker);
796 if (!color_picker_drag(&label_layer->color_picker)) {
797 LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE));
800 &label_layer->colors,
801 (size_t) label_layer->selection,
802 &label_layer->inter_color);
803 label_layer->state = LABEL_LAYER_IDLE;
810 int label_layer_event(LabelLayer *label_layer,
811 const SDL_Event *event,
812 const Camera *camera,
813 UndoHistory *undo_history)
815 trace_assert(label_layer);
817 trace_assert(camera);
818 trace_assert(undo_history);
820 switch (label_layer->state) {
821 case LABEL_LAYER_IDLE:
822 return label_layer_idle_event(label_layer, event, camera, undo_history);
824 case LABEL_LAYER_MOVE:
825 return label_layer_move_event(label_layer, event, camera, undo_history);
827 case LABEL_LAYER_EDIT_TEXT:
828 return label_layer_edit_text_event(label_layer, event, camera, undo_history);
830 case LABEL_LAYER_EDIT_ID:
831 return label_layer_edit_id_event(label_layer, event, camera, undo_history);
833 case LABEL_LAYER_RECOLOR:
834 return label_layer_recolor_event(label_layer, event, camera, undo_history);
840 size_t label_layer_count(const LabelLayer *label_layer)
842 return label_layer->ids.count;
845 char *label_layer_ids(const LabelLayer *label_layer)
847 return (char *)label_layer->ids.data;
850 Vec2f *label_layer_positions(const LabelLayer *label_layer)
852 return (Vec2f *)label_layer->positions.data;
855 Color *label_layer_colors(const LabelLayer *label_layer)
857 return (Color *)label_layer->colors.data;
860 char *labels_layer_texts(const LabelLayer *label_layer)
862 return (char *)label_layer->texts.data;
865 int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump)
867 trace_assert(label_layer);
868 trace_assert(filedump);
870 size_t n = label_layer->ids.count;
871 char *ids = (char *)label_layer->ids.data;
872 Vec2f *positions = (Vec2f *)label_layer->positions.data;
873 Color *colors = (Color *)label_layer->colors.data;
874 char *texts = (char *)label_layer->texts.data;
876 fprintf(filedump, "%zd\n", n);
877 for (size_t i = 0; i < n; ++i) {
878 fprintf(filedump, "%s %f %f ",
879 ids + LABEL_LAYER_ID_MAX_SIZE * i,
880 positions[i].x, positions[i].y);
881 color_hex_to_stream(colors[i], filedump);
882 fprintf(filedump, "\n%s\n", texts + i * LABEL_LAYER_TEXT_MAX_SIZE);