5 #include "system/line_stream.h"
6 #include "system/stacktrace.h"
7 #include "system/nth_alloc.h"
9 #include "system/str.h"
10 #include "system/log.h"
12 #include "label_layer.h"
15 #include "game/camera.h"
16 #include "color_picker.h"
17 #include "ui/edit_field.h"
18 #include "math/extrema.h"
20 #define LABEL_LAYER_SELECTION_THICCNESS 5.0f
26 LABEL_LAYER_EDIT_TEXT,
32 static char clipboard_text[LABEL_LAYER_TEXT_MAX_SIZE];
33 static Color clipboard_color;
37 LabelLayerState state;
43 ColorPicker color_picker;
45 Edit_field *edit_field;
49 const char *id_name_prefix;
62 char id[LABEL_LAYER_ID_MAX_SIZE];
65 char text[LABEL_LAYER_TEXT_MAX_SIZE];
71 UndoContext create_undo_swap_context(LabelLayer *label_layer,
72 size_t index, size_t index2)
74 trace_assert(label_layer);
75 trace_assert(index < dynarray_count(label_layer->positions));
76 trace_assert(index2 < dynarray_count(label_layer->positions));
78 UndoContext undo_context;
79 undo_context.type = UNDO_SWAP;
80 undo_context.layer = label_layer;
81 undo_context.index = index;
82 undo_context.index2 = index2;
87 UndoContext create_undo_context(LabelLayer *label_layer, UndoType type)
89 trace_assert(label_layer);
90 trace_assert(type != UNDO_SWAP);
92 UndoContext undo_context;
94 size_t index = type == UNDO_ADD
95 ? dynarray_count(label_layer->positions) - 1
96 : (size_t)label_layer->selection;
98 undo_context.type = type;
99 undo_context.layer = label_layer;
100 dynarray_copy_to(label_layer->ids, &undo_context.id, index);
101 dynarray_copy_to(label_layer->positions, &undo_context.position, index);
102 dynarray_copy_to(label_layer->colors, &undo_context.color, index);
103 dynarray_copy_to(label_layer->texts, &undo_context.text, index);
104 undo_context.index = index;
110 void label_layer_undo(void *context, size_t context_size)
112 trace_assert(context);
113 trace_assert(sizeof(UndoContext) == context_size);
115 UndoContext *undo_context = context;
116 LabelLayer *label_layer = undo_context->layer;
118 switch (undo_context->type) {
120 dynarray_delete_at(label_layer->ids, undo_context->index);
121 dynarray_delete_at(label_layer->positions, undo_context->index);
122 dynarray_delete_at(label_layer->colors, undo_context->index);
123 dynarray_delete_at(label_layer->texts, undo_context->index);
127 dynarray_insert_before(label_layer->ids, undo_context->index, &undo_context->id);
128 dynarray_insert_before(label_layer->positions, undo_context->index, &undo_context->position);
129 dynarray_insert_before(label_layer->colors, undo_context->index, &undo_context->color);
130 dynarray_insert_before(label_layer->texts, undo_context->index, &undo_context->text);
134 dynarray_replace_at(label_layer->ids, undo_context->index, &undo_context->id);
135 dynarray_replace_at(label_layer->positions, undo_context->index, &undo_context->position);
136 dynarray_replace_at(label_layer->colors, undo_context->index, &undo_context->color);
137 dynarray_replace_at(label_layer->texts, undo_context->index, &undo_context->text);
141 dynarray_swap(label_layer->ids, undo_context->index, undo_context->index2);
142 dynarray_swap(label_layer->positions, undo_context->index, undo_context->index2);
143 dynarray_swap(label_layer->colors, undo_context->index, undo_context->index2);
144 dynarray_swap(label_layer->texts, undo_context->index, undo_context->index2);
149 #define UNDO_PUSH(HISTORY, CONTEXT) \
151 UndoContext context = (CONTEXT); \
160 LayerPtr label_layer_as_layer(LabelLayer *label_layer)
169 LabelLayer *create_label_layer(const char *id_name_prefix)
171 Lt *lt = create_lt();
173 LabelLayer *label_layer = PUSH_LT(
174 lt, nth_calloc(1, sizeof(LabelLayer)), free);
175 if (label_layer == NULL) {
178 label_layer->lt = lt;
180 label_layer->ids = PUSH_LT(
182 create_dynarray(sizeof(char) * LABEL_LAYER_ID_MAX_SIZE),
184 if (label_layer->ids == NULL) {
188 label_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Vec2f)), destroy_dynarray);
189 if (label_layer->positions == NULL) {
193 label_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
194 if (label_layer->colors == NULL) {
198 label_layer->texts = PUSH_LT(
200 create_dynarray(sizeof(char) * LABEL_LAYER_TEXT_MAX_SIZE),
202 if (label_layer->texts == NULL) {
206 label_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
207 label_layer->selection = -1;
209 label_layer->edit_field = PUSH_LT(
211 create_edit_field(LABELS_SIZE, COLOR_RED),
213 if (label_layer->edit_field == NULL) {
217 label_layer->id_name_prefix = id_name_prefix;
222 LabelLayer *create_label_layer_from_line_stream(LineStream *line_stream, const char *id_name_prefix)
224 trace_assert(line_stream);
225 LabelLayer *label_layer = create_label_layer(id_name_prefix);
227 if (label_layer == NULL) {
228 RETURN_LT(label_layer->lt, NULL);
231 const char *line = line_stream_next(line_stream);
233 log_fail("Could not read amount of labels\n");
234 RETURN_LT(label_layer->lt, NULL);
238 if (sscanf(line, "%zu", &n) == EOF) {
239 log_fail("Could not parse amount of labels\n");
240 RETURN_LT(label_layer->lt, NULL);
243 for (size_t i = 0; i < n; ++i) {
245 char id[LABEL_LAYER_ID_MAX_SIZE];
248 line = line_stream_next(line_stream);
250 log_fail("Could not read label meta info\n");
251 RETURN_LT(label_layer->lt, NULL);
256 "%"STRINGIFY(LABEL_LAYER_ID_MAX_SIZE)"s%f%f%6s\n",
257 id, &position.x, &position.y, hex) == EOF) {
258 log_fail("Could not parse label meta info\n");
259 RETURN_LT(label_layer->lt, NULL);
262 Color color = hexstr(hex);
264 dynarray_push(label_layer->ids, id);
265 dynarray_push(label_layer->positions, &position);
266 dynarray_push(label_layer->colors, &color);
268 line = line_stream_next(line_stream);
270 log_fail("Could not read label text\n");
273 char label_text[LABEL_LAYER_TEXT_MAX_SIZE] = {0};
274 memcpy(label_text, line, LABEL_LAYER_TEXT_MAX_SIZE - 1);
275 trim_endline(label_text);
276 dynarray_push(label_layer->texts, &label_text);
282 void destroy_label_layer(LabelLayer *label_layer)
284 trace_assert(label_layer);
285 destroy_lt(label_layer->lt);
288 int label_layer_render(const LabelLayer *label_layer,
289 const Camera *camera,
292 trace_assert(label_layer);
293 trace_assert(camera);
295 if (active && color_picker_render(&label_layer->color_picker, camera) < 0) {
299 size_t n = dynarray_count(label_layer->ids);
300 char *ids = dynarray_data(label_layer->ids);
301 Vec2f *positions = dynarray_data(label_layer->positions);
302 Color *colors = dynarray_data(label_layer->colors);
303 char *texts = dynarray_data(label_layer->texts);
305 /* TODO(#891): LabelLayer doesn't show the final position of Label after the animation */
306 for (size_t i = 0; i < n; ++i) {
307 const Color color = label_layer->state == LABEL_LAYER_RECOLOR && label_layer->selection == (int) i
308 ? label_layer->inter_color
311 const Vec2f position =
312 label_layer->state == LABEL_LAYER_MOVE && label_layer->selection == (int) i
313 ? label_layer->inter_position
317 if (label_layer->state == LABEL_LAYER_EDIT_TEXT && label_layer->selection == (int) i) {
318 if (edit_field_render_world(
319 label_layer->edit_field,
325 if (camera_render_text(
327 texts + i * LABEL_LAYER_TEXT_MAX_SIZE,
331 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
338 if (label_layer->state == LABEL_LAYER_EDIT_ID && label_layer->selection == (int)i) {
339 if (edit_field_render_world(
340 label_layer->edit_field,
344 vec(0.0f, FONT_CHAR_HEIGHT))) < 0) {
348 if (camera_render_text(
350 ids + i * LABEL_LAYER_ID_MAX_SIZE,
354 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
355 vec_sub(position, vec(0.0f, FONT_CHAR_HEIGHT))) < 0) {
361 if (active && label_layer->selection == (int) i) {
367 sprite_font_boundary_box(
371 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE),
372 sprite_font_boundary_box(
376 vec(0.0f, FONT_CHAR_HEIGHT)),
378 ids + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE))),
379 LABEL_LAYER_SELECTION_THICCNESS * 0.5f);
382 if (camera_draw_thicc_rect_screen(
386 LABEL_LAYER_SELECTION_THICCNESS) < 0) {
397 int label_layer_element_at(LabelLayer *label_layer,
398 const Sprite_font *font,
401 trace_assert(label_layer);
403 const int n = (int) dynarray_count(label_layer->texts);
404 char *ids = dynarray_data(label_layer->ids);
405 char *texts = dynarray_data(label_layer->texts);
406 Vec2f *positions = dynarray_data(label_layer->positions);
408 for (int i = n - 1; i >= 0; --i) {
409 Rect boundary = rect_boundary2(
410 sprite_font_boundary_box(
414 texts + i * LABEL_LAYER_TEXT_MAX_SIZE),
415 sprite_font_boundary_box(
419 vec(0.0f, FONT_CHAR_HEIGHT)),
421 ids + i * LABEL_LAYER_ID_MAX_SIZE));
423 if (rect_contains_point(boundary, position)) {
432 void label_layer_delete_selected_label(LabelLayer *label_layer,
433 UndoHistory *undo_history)
435 trace_assert(label_layer);
436 trace_assert(label_layer->selection >= 0);
438 UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_DELETE));
440 dynarray_delete_at(label_layer->ids, (size_t)label_layer->selection);
441 dynarray_delete_at(label_layer->positions, (size_t)label_layer->selection);
442 dynarray_delete_at(label_layer->colors, (size_t)label_layer->selection);
443 dynarray_delete_at(label_layer->texts, (size_t)label_layer->selection);
445 label_layer->selection = -1;
449 int label_layer_add_label(LabelLayer *label_layer,
453 UndoHistory *undo_history)
455 trace_assert(label_layer);
457 // TODO(#982): id generation code is duplicated in label_layer, point_layer and rect_layer
458 char id[LABEL_LAYER_ID_MAX_SIZE];
459 snprintf(id, LABEL_LAYER_ID_MAX_SIZE, "%s_%d",
460 label_layer->id_name_prefix,
461 label_layer->id_name_counter++);
463 size_t n = dynarray_count(label_layer->ids);
465 dynarray_push(label_layer->ids, id);
466 dynarray_push(label_layer->positions, &position);
467 dynarray_push(label_layer->colors, &color);
468 dynarray_push_empty(label_layer->texts);
470 dynarray_pointer_at(label_layer->texts, n),
472 min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, strlen(text)));
474 UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_ADD));
480 void label_layer_swap_elements(LabelLayer *label_layer,
482 UndoHistory *undo_history)
484 trace_assert(label_layer);
485 trace_assert(undo_history);
486 trace_assert(a < dynarray_count(label_layer->positions));
487 trace_assert(b < dynarray_count(label_layer->positions));
489 dynarray_swap(label_layer->ids, a, b);
490 dynarray_swap(label_layer->positions, a, b);
491 dynarray_swap(label_layer->colors, a, b);
492 dynarray_swap(label_layer->texts, a, b);
494 UNDO_PUSH(undo_history, create_undo_swap_context(label_layer, a, b));
498 int label_layer_idle_event(LabelLayer *label_layer,
499 const SDL_Event *event,
500 const Camera *camera,
501 UndoHistory *undo_history)
503 trace_assert(label_layer);
505 trace_assert(camera);
509 if (color_picker_event(
510 &label_layer->color_picker,
518 if (label_layer->selection >= 0) {
519 label_layer->state = LABEL_LAYER_RECOLOR;
520 label_layer->inter_color = color_picker_rgba(&label_layer->color_picker);
525 Color *colors = dynarray_data(label_layer->colors);
526 Vec2f *positions = dynarray_data(label_layer->positions);
527 char *ids = dynarray_data(label_layer->ids);
528 char *texts = dynarray_data(label_layer->texts);
530 switch (event->type) {
531 case SDL_MOUSEBUTTONDOWN: {
532 switch (event->button.button) {
533 case SDL_BUTTON_LEFT: {
534 const Vec2f position = camera_map_screen(
539 const int element = label_layer_element_at(
545 label_layer->move_anchor = vec_sub(position, positions[element]);
546 label_layer->selection = element;
547 label_layer->state = LABEL_LAYER_MOVE;
548 label_layer->inter_position = positions[element];
550 label_layer->color_picker =
551 create_color_picker_from_rgba(colors[element]);
553 label_layer->selection = label_layer_add_label(
557 &label_layer->color_picker),
560 label_layer->state = LABEL_LAYER_EDIT_TEXT;
562 label_layer->edit_field,
563 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
565 label_layer->edit_field,
567 colors[label_layer->selection]);
568 SDL_StartTextInput();
575 switch (event->key.keysym.sym) {
577 if ((event->key.keysym.mod & KMOD_SHIFT)
578 && (label_layer->selection >= 0)
579 && ((size_t)(label_layer->selection + 1) < dynarray_count(label_layer->positions))) {
580 label_layer_swap_elements(
582 (size_t) label_layer->selection,
583 (size_t) label_layer->selection + 1,
585 label_layer->selection++;
590 if ((event->key.keysym.mod & KMOD_SHIFT)
591 && (label_layer->selection > 0)
592 && ((size_t) label_layer->selection < dynarray_count(label_layer->positions))) {
593 label_layer_swap_elements(
595 (size_t) label_layer->selection,
596 (size_t) label_layer->selection - 1,
598 label_layer->selection--;
603 if (label_layer->selection >= 0) {
604 label_layer->state = LABEL_LAYER_EDIT_TEXT;
606 label_layer->edit_field,
607 texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE);
609 label_layer->edit_field,
611 colors[label_layer->selection]);
612 SDL_StartTextInput();
617 if (label_layer->selection >= 0) {
618 label_layer->state = LABEL_LAYER_EDIT_ID;
620 label_layer->edit_field,
621 ids + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE);
623 label_layer->edit_field,
625 color_invert(colors[label_layer->selection]));
626 SDL_StartTextInput();
631 if (label_layer->selection >= 0) {
632 label_layer_delete_selected_label(
635 label_layer->selection = -1;
640 if ((event->key.keysym.mod & KMOD_LCTRL) && label_layer->selection >= 0) {
642 dynarray_copy_to(label_layer->texts, clipboard_text, (size_t)label_layer->selection);
643 dynarray_copy_to(label_layer->colors, &clipboard_color, (size_t)label_layer->selection);
648 if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
650 SDL_GetMouseState(&x, &y);
651 Vec2f position = camera_map_screen(camera, x, y);
653 label_layer_add_label(
669 int label_layer_move_event(LabelLayer *label_layer,
670 const SDL_Event *event,
671 const Camera *camera,
672 UndoHistory *undo_history)
674 trace_assert(label_layer);
676 trace_assert(camera);
677 trace_assert(label_layer->selection >= 0);
679 Vec2f *positions = dynarray_data(label_layer->positions);
681 switch (event->type) {
682 case SDL_MOUSEMOTION: {
683 label_layer->inter_position = vec_sub(
688 label_layer->move_anchor);
691 case SDL_MOUSEBUTTONUP: {
692 switch (event->button.button) {
693 case SDL_BUTTON_LEFT: {
694 const float distance = vec_length(
695 vec_sub(label_layer->inter_position,
696 positions[label_layer->selection]));
698 if (distance > 1e-6) {
699 UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
702 label_layer->positions,
703 (size_t)label_layer->selection,
704 &label_layer->inter_position);
707 label_layer->state = LABEL_LAYER_IDLE;
717 int label_layer_edit_text_event(LabelLayer *label_layer,
718 const SDL_Event *event,
719 const Camera *camera,
720 UndoHistory *undo_history)
722 trace_assert(label_layer);
724 trace_assert(camera);
725 trace_assert(label_layer->selection >= 0);
727 switch (event->type) {
729 switch (event->key.keysym.sym) {
731 UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
734 (char*)dynarray_data(label_layer->texts) + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE;
735 memset(text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
736 memcpy(text, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_TEXT_MAX_SIZE - 1);
737 label_layer->state = LABEL_LAYER_IDLE;
743 label_layer->state = LABEL_LAYER_IDLE;
751 return edit_field_event(label_layer->edit_field, event);
755 int label_layer_edit_id_event(LabelLayer *label_layer,
756 const SDL_Event *event,
757 const Camera *camera,
758 UndoHistory *undo_history)
760 trace_assert(label_layer);
762 trace_assert(camera);
763 trace_assert(undo_history);
764 trace_assert(label_layer->selection >= 0);
766 switch (event->type) {
768 switch (event->key.keysym.sym) {
770 UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
773 (char*)dynarray_data(label_layer->ids) + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE;
774 memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
775 memcpy(id, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_ID_MAX_SIZE - 1);
776 label_layer->state = LABEL_LAYER_IDLE;
782 label_layer->state = LABEL_LAYER_IDLE;
790 return edit_field_event(label_layer->edit_field, event);
794 int label_layer_recolor_event(LabelLayer *label_layer,
795 const SDL_Event *event,
796 const Camera *camera,
797 UndoHistory *undo_history)
799 trace_assert(label_layer);
801 trace_assert(camera);
802 trace_assert(undo_history);
803 trace_assert(label_layer->selection >= 0);
807 if (color_picker_event(
808 &label_layer->color_picker,
816 label_layer->inter_color =
817 color_picker_rgba(&label_layer->color_picker);
819 if (!color_picker_drag(&label_layer->color_picker)) {
820 UNDO_PUSH(undo_history, create_undo_context(label_layer, UNDO_UPDATE));
824 (size_t) label_layer->selection,
825 &label_layer->inter_color);
826 label_layer->state = LABEL_LAYER_IDLE;
833 int label_layer_event(LabelLayer *label_layer,
834 const SDL_Event *event,
835 const Camera *camera,
836 UndoHistory *undo_history)
838 trace_assert(label_layer);
840 trace_assert(camera);
841 trace_assert(undo_history);
843 switch (label_layer->state) {
844 case LABEL_LAYER_IDLE:
845 return label_layer_idle_event(label_layer, event, camera, undo_history);
847 case LABEL_LAYER_MOVE:
848 return label_layer_move_event(label_layer, event, camera, undo_history);
850 case LABEL_LAYER_EDIT_TEXT:
851 return label_layer_edit_text_event(label_layer, event, camera, undo_history);
853 case LABEL_LAYER_EDIT_ID:
854 return label_layer_edit_id_event(label_layer, event, camera, undo_history);
856 case LABEL_LAYER_RECOLOR:
857 return label_layer_recolor_event(label_layer, event, camera, undo_history);
863 size_t label_layer_count(const LabelLayer *label_layer)
865 return dynarray_count(label_layer->ids);
868 char *label_layer_ids(const LabelLayer *label_layer)
870 return dynarray_data(label_layer->ids);
873 Vec2f *label_layer_positions(const LabelLayer *label_layer)
875 return dynarray_data(label_layer->positions);
878 Color *label_layer_colors(const LabelLayer *label_layer)
880 return dynarray_data(label_layer->colors);
883 char *labels_layer_texts(const LabelLayer *label_layer)
885 return dynarray_data(label_layer->texts);
888 int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump)
890 trace_assert(label_layer);
891 trace_assert(filedump);
893 size_t n = dynarray_count(label_layer->ids);
894 char *ids = dynarray_data(label_layer->ids);
895 Vec2f *positions = dynarray_data(label_layer->positions);
896 Color *colors = dynarray_data(label_layer->colors);
897 char *texts = dynarray_data(label_layer->texts);
899 fprintf(filedump, "%zd\n", n);
900 for (size_t i = 0; i < n; ++i) {
901 fprintf(filedump, "%s %f %f ",
902 ids + LABEL_LAYER_ID_MAX_SIZE * i,
903 positions[i].x, positions[i].y);
904 color_hex_to_stream(colors[i], filedump);
905 fprintf(filedump, "\n%s\n", texts + i * LABEL_LAYER_TEXT_MAX_SIZE);