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"
11 #include "math/point.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
22 // TODO(#1079): LabelLayer doe snot support z reordering
27 LABEL_LAYER_EDIT_TEXT,
33 static char clipboard_text[LABEL_LAYER_TEXT_MAX_SIZE];
34 static Color clipboard_color;
38 LabelLayerState state;
44 ColorPicker color_picker;
46 Edit_field *edit_field;
50 const char *id_name_prefix;
62 char id[LABEL_LAYER_ID_MAX_SIZE];
65 char text[LABEL_LAYER_TEXT_MAX_SIZE];
70 UndoContext create_undo_context(LabelLayer *label_layer, UndoType type)
72 UndoContext undo_context;
74 size_t index = type == UNDO_ADD
75 ? dynarray_count(label_layer->positions) - 1
76 : (size_t)label_layer->selected;
78 undo_context.type = type;
79 undo_context.layer = label_layer;
80 dynarray_copy_to(label_layer->ids, &undo_context.id, index);
81 dynarray_copy_to(label_layer->positions, &undo_context.position, index);
82 dynarray_copy_to(label_layer->colors, &undo_context.color, index);
83 dynarray_copy_to(label_layer->texts, &undo_context.text, index);
84 undo_context.index = index;
90 void label_layer_undo(void *context, size_t context_size)
92 trace_assert(context);
93 trace_assert(sizeof(UndoContext) == context_size);
95 UndoContext *undo_context = context;
96 LabelLayer *label_layer = undo_context->layer;
98 switch (undo_context->type) {
100 dynarray_delete_at(label_layer->ids, undo_context->index);
101 dynarray_delete_at(label_layer->positions, undo_context->index);
102 dynarray_delete_at(label_layer->colors, undo_context->index);
103 dynarray_delete_at(label_layer->texts, undo_context->index);
107 dynarray_insert_before(label_layer->ids, undo_context->index, &undo_context->id);
108 dynarray_insert_before(label_layer->positions, undo_context->index, &undo_context->position);
109 dynarray_insert_before(label_layer->colors, undo_context->index, &undo_context->color);
110 dynarray_insert_before(label_layer->texts, undo_context->index, &undo_context->text);
114 dynarray_replace_at(label_layer->ids, undo_context->index, &undo_context->id);
115 dynarray_replace_at(label_layer->positions, undo_context->index, &undo_context->position);
116 dynarray_replace_at(label_layer->colors, undo_context->index, &undo_context->color);
117 dynarray_replace_at(label_layer->texts, undo_context->index, &undo_context->text);
122 #define UNDO_PUSH(LAYER, HISTORY, UNDO_TYPE) \
124 UndoContext context = create_undo_context(LAYER, UNDO_TYPE); \
133 LayerPtr label_layer_as_layer(LabelLayer *label_layer)
142 LabelLayer *create_label_layer(const char *id_name_prefix)
144 Lt *lt = create_lt();
146 LabelLayer *label_layer = PUSH_LT(
147 lt, nth_calloc(1, sizeof(LabelLayer)), free);
148 if (label_layer == NULL) {
151 label_layer->lt = lt;
153 label_layer->ids = PUSH_LT(
155 create_dynarray(sizeof(char) * LABEL_LAYER_ID_MAX_SIZE),
157 if (label_layer->ids == NULL) {
161 label_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray);
162 if (label_layer->positions == NULL) {
166 label_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
167 if (label_layer->colors == NULL) {
171 label_layer->texts = PUSH_LT(
173 create_dynarray(sizeof(char) * LABEL_LAYER_TEXT_MAX_SIZE),
175 if (label_layer->texts == NULL) {
179 label_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
180 label_layer->selected = -1;
182 label_layer->edit_field = PUSH_LT(
184 create_edit_field(LABELS_SIZE, COLOR_RED),
186 if (label_layer->edit_field == NULL) {
190 label_layer->id_name_prefix = id_name_prefix;
195 LabelLayer *create_label_layer_from_line_stream(LineStream *line_stream, const char *id_name_prefix)
197 trace_assert(line_stream);
198 LabelLayer *label_layer = create_label_layer(id_name_prefix);
200 if (label_layer == NULL) {
201 RETURN_LT(label_layer->lt, NULL);
204 const char *line = line_stream_next(line_stream);
206 log_fail("Could not read amount of labels\n");
207 RETURN_LT(label_layer->lt, NULL);
211 if (sscanf(line, "%zu", &n) == EOF) {
212 log_fail("Could not parse amount of labels\n");
213 RETURN_LT(label_layer->lt, NULL);
216 for (size_t i = 0; i < n; ++i) {
218 char id[LABEL_LAYER_ID_MAX_SIZE];
221 line = line_stream_next(line_stream);
223 log_fail("Could not read label meta info\n");
224 RETURN_LT(label_layer->lt, NULL);
229 "%"STRINGIFY(LABEL_LAYER_ID_MAX_SIZE)"s%f%f%6s\n",
230 id, &position.x, &position.y, hex) == EOF) {
231 log_fail("Could not parse label meta info\n");
232 RETURN_LT(label_layer->lt, NULL);
235 Color color = hexstr(hex);
237 dynarray_push(label_layer->ids, id);
238 dynarray_push(label_layer->positions, &position);
239 dynarray_push(label_layer->colors, &color);
241 line = line_stream_next(line_stream);
243 log_fail("Could not read label text\n");
246 char label_text[LABEL_LAYER_TEXT_MAX_SIZE] = {0};
247 memcpy(label_text, line, LABEL_LAYER_TEXT_MAX_SIZE - 1);
248 trim_endline(label_text);
249 dynarray_push(label_layer->texts, &label_text);
255 void destroy_label_layer(LabelLayer *label_layer)
257 trace_assert(label_layer);
258 destroy_lt(label_layer->lt);
261 int label_layer_render(const LabelLayer *label_layer,
262 const Camera *camera,
265 trace_assert(label_layer);
266 trace_assert(camera);
268 if (active && color_picker_render(&label_layer->color_picker, camera) < 0) {
272 size_t n = dynarray_count(label_layer->ids);
273 char *ids = dynarray_data(label_layer->ids);
274 Point *positions = dynarray_data(label_layer->positions);
275 Color *colors = dynarray_data(label_layer->colors);
276 char *texts = dynarray_data(label_layer->texts);
278 /* TODO(#891): LabelLayer doesn't show the final position of Label after the animation */
279 for (size_t i = 0; i < n; ++i) {
280 const Color color = label_layer->state == LABEL_LAYER_RECOLOR && label_layer->selected == (int) i
281 ? label_layer->inter_color
284 const Point position =
285 label_layer->state == LABEL_LAYER_MOVE && label_layer->selected == (int) i
286 ? label_layer->inter_position
290 if (label_layer->state == LABEL_LAYER_EDIT_TEXT && label_layer->selected == (int) i) {
291 if (edit_field_render_world(
292 label_layer->edit_field,
298 if (camera_render_text(
300 texts + i * LABEL_LAYER_TEXT_MAX_SIZE,
304 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
311 if (label_layer->state == LABEL_LAYER_EDIT_ID && label_layer->selected == (int)i) {
312 if (edit_field_render_world(
313 label_layer->edit_field,
317 vec(0.0f, FONT_CHAR_HEIGHT))) < 0) {
321 if (camera_render_text(
323 ids + i * LABEL_LAYER_ID_MAX_SIZE,
327 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
328 vec_sub(position, vec(0.0f, FONT_CHAR_HEIGHT))) < 0) {
334 if (active && label_layer->selected == (int) i) {
340 sprite_font_boundary_box(
344 texts + label_layer->selected * LABEL_LAYER_TEXT_MAX_SIZE),
345 sprite_font_boundary_box(
349 vec(0.0f, FONT_CHAR_HEIGHT)),
351 ids + label_layer->selected * LABEL_LAYER_ID_MAX_SIZE))),
352 LABEL_LAYER_SELECTION_THICCNESS * 0.5f);
355 if (camera_draw_thicc_rect_screen(
359 LABEL_LAYER_SELECTION_THICCNESS) < 0) {
370 int label_layer_element_at(LabelLayer *label_layer,
371 const Sprite_font *font,
374 trace_assert(label_layer);
376 const int n = (int) dynarray_count(label_layer->texts);
377 char *ids = dynarray_data(label_layer->ids);
378 char *texts = dynarray_data(label_layer->texts);
379 Point *positions = dynarray_data(label_layer->positions);
381 for (int i = n - 1; i >= 0; --i) {
382 Rect boundary = rect_boundary2(
383 sprite_font_boundary_box(
387 texts + i * LABEL_LAYER_TEXT_MAX_SIZE),
388 sprite_font_boundary_box(
392 vec(0.0f, FONT_CHAR_HEIGHT)),
394 ids + i * LABEL_LAYER_ID_MAX_SIZE));
396 if (rect_contains_point(boundary, position)) {
405 void label_layer_delete_selected_label(LabelLayer *label_layer,
406 UndoHistory *undo_history)
408 trace_assert(label_layer);
409 trace_assert(label_layer->selected >= 0);
411 UNDO_PUSH(label_layer, undo_history, UNDO_DELETE);
413 dynarray_delete_at(label_layer->ids, (size_t)label_layer->selected);
414 dynarray_delete_at(label_layer->positions, (size_t)label_layer->selected);
415 dynarray_delete_at(label_layer->colors, (size_t)label_layer->selected);
416 dynarray_delete_at(label_layer->texts, (size_t)label_layer->selected);
418 label_layer->selected = -1;
422 int label_layer_add_label(LabelLayer *label_layer,
426 UndoHistory *undo_history)
428 trace_assert(label_layer);
430 // TODO(#982): id generation code is duplicated in label_layer, point_layer and rect_layer
431 char id[LABEL_LAYER_ID_MAX_SIZE];
432 snprintf(id, LABEL_LAYER_ID_MAX_SIZE, "%s_%d",
433 label_layer->id_name_prefix,
434 label_layer->id_name_counter++);
436 size_t n = dynarray_count(label_layer->ids);
438 dynarray_push(label_layer->ids, id);
439 dynarray_push(label_layer->positions, &position);
440 dynarray_push(label_layer->colors, &color);
441 dynarray_push_empty(label_layer->texts);
443 dynarray_pointer_at(label_layer->texts, n),
445 min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, strlen(text)));
447 UNDO_PUSH(label_layer, undo_history, UNDO_ADD);
453 int label_layer_idle_event(LabelLayer *label_layer,
454 const SDL_Event *event,
455 const Camera *camera,
456 UndoHistory *undo_history)
458 trace_assert(label_layer);
460 trace_assert(camera);
464 if (color_picker_event(
465 &label_layer->color_picker,
473 if (label_layer->selected >= 0) {
474 label_layer->state = LABEL_LAYER_RECOLOR;
475 label_layer->inter_color = color_picker_rgba(&label_layer->color_picker);
480 Color *colors = dynarray_data(label_layer->colors);
481 Point *positions = dynarray_data(label_layer->positions);
482 char *ids = dynarray_data(label_layer->ids);
483 char *texts = dynarray_data(label_layer->texts);
485 switch (event->type) {
486 case SDL_MOUSEBUTTONDOWN: {
487 switch (event->button.button) {
488 case SDL_BUTTON_LEFT: {
489 const Point position = camera_map_screen(
494 const int element = label_layer_element_at(
500 label_layer->move_anchor = vec_sub(position, positions[element]);
501 label_layer->selected = element;
502 label_layer->state = LABEL_LAYER_MOVE;
503 label_layer->inter_position = positions[element];
505 label_layer->color_picker =
506 create_color_picker_from_rgba(colors[element]);
508 label_layer->selected = label_layer_add_label(
512 &label_layer->color_picker),
515 label_layer->state = LABEL_LAYER_EDIT_TEXT;
517 label_layer->edit_field,
518 texts + label_layer->selected * LABEL_LAYER_TEXT_MAX_SIZE);
520 label_layer->edit_field,
522 colors[label_layer->selected]);
523 SDL_StartTextInput();
530 switch (event->key.keysym.sym) {
532 if (label_layer->selected >= 0) {
533 label_layer->state = LABEL_LAYER_EDIT_TEXT;
535 label_layer->edit_field,
536 texts + label_layer->selected * LABEL_LAYER_TEXT_MAX_SIZE);
538 label_layer->edit_field,
540 colors[label_layer->selected]);
541 SDL_StartTextInput();
546 if (label_layer->selected >= 0) {
547 label_layer->state = LABEL_LAYER_EDIT_ID;
549 label_layer->edit_field,
550 ids + label_layer->selected * LABEL_LAYER_ID_MAX_SIZE);
552 label_layer->edit_field,
554 color_invert(colors[label_layer->selected]));
555 SDL_StartTextInput();
560 if (label_layer->selected >= 0) {
561 label_layer_delete_selected_label(
564 label_layer->selected = -1;
569 if ((event->key.keysym.mod & KMOD_LCTRL) && label_layer->selected >= 0) {
571 dynarray_copy_to(label_layer->texts, clipboard_text, (size_t)label_layer->selected);
572 dynarray_copy_to(label_layer->colors, &clipboard_color, (size_t)label_layer->selected);
577 if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
579 SDL_GetMouseState(&x, &y);
580 Point position = camera_map_screen(camera, x, y);
582 label_layer_add_label(
598 int label_layer_move_event(LabelLayer *label_layer,
599 const SDL_Event *event,
600 const Camera *camera,
601 UndoHistory *undo_history)
603 trace_assert(label_layer);
605 trace_assert(camera);
606 trace_assert(label_layer->selected >= 0);
608 switch (event->type) {
609 case SDL_MOUSEMOTION: {
610 label_layer->inter_position = vec_sub(
615 label_layer->move_anchor);
618 case SDL_MOUSEBUTTONUP: {
619 switch (event->button.button) {
620 case SDL_BUTTON_LEFT: {
621 UNDO_PUSH(label_layer, undo_history, UNDO_UPDATE);
624 label_layer->positions,
625 (size_t)label_layer->selected,
626 &label_layer->inter_position);
627 label_layer->state = LABEL_LAYER_IDLE;
637 int label_layer_edit_text_event(LabelLayer *label_layer,
638 const SDL_Event *event,
639 const Camera *camera,
640 UndoHistory *undo_history)
642 trace_assert(label_layer);
644 trace_assert(camera);
645 trace_assert(label_layer->selected >= 0);
647 switch (event->type) {
649 switch (event->key.keysym.sym) {
651 UNDO_PUSH(label_layer, undo_history, UNDO_UPDATE);
654 (char*)dynarray_data(label_layer->texts) + label_layer->selected * LABEL_LAYER_TEXT_MAX_SIZE;
655 memset(text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
656 memcpy(text, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_TEXT_MAX_SIZE - 1);
657 label_layer->state = LABEL_LAYER_IDLE;
663 label_layer->state = LABEL_LAYER_IDLE;
671 return edit_field_event(label_layer->edit_field, event);
675 int label_layer_edit_id_event(LabelLayer *label_layer,
676 const SDL_Event *event,
677 const Camera *camera,
678 UndoHistory *undo_history)
680 trace_assert(label_layer);
682 trace_assert(camera);
683 trace_assert(undo_history);
684 trace_assert(label_layer->selected >= 0);
686 switch (event->type) {
688 switch (event->key.keysym.sym) {
690 UNDO_PUSH(label_layer, undo_history, UNDO_UPDATE);
693 (char*)dynarray_data(label_layer->ids) + label_layer->selected * LABEL_LAYER_ID_MAX_SIZE;
694 memset(id, 0, LABEL_LAYER_ID_MAX_SIZE);
695 memcpy(id, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_ID_MAX_SIZE - 1);
696 label_layer->state = LABEL_LAYER_IDLE;
702 label_layer->state = LABEL_LAYER_IDLE;
710 return edit_field_event(label_layer->edit_field, event);
714 int label_layer_recolor_event(LabelLayer *label_layer,
715 const SDL_Event *event,
716 const Camera *camera,
717 UndoHistory *undo_history)
719 trace_assert(label_layer);
721 trace_assert(camera);
722 trace_assert(undo_history);
723 trace_assert(label_layer->selected >= 0);
727 if (color_picker_event(
728 &label_layer->color_picker,
736 label_layer->inter_color =
737 color_picker_rgba(&label_layer->color_picker);
739 if (!color_picker_drag(&label_layer->color_picker)) {
740 UNDO_PUSH(label_layer, undo_history, UNDO_UPDATE);
744 (size_t) label_layer->selected,
745 &label_layer->inter_color);
746 label_layer->state = LABEL_LAYER_IDLE;
753 int label_layer_event(LabelLayer *label_layer,
754 const SDL_Event *event,
755 const Camera *camera,
756 UndoHistory *undo_history)
758 trace_assert(label_layer);
760 trace_assert(camera);
761 trace_assert(undo_history);
763 switch (label_layer->state) {
764 case LABEL_LAYER_IDLE:
765 return label_layer_idle_event(label_layer, event, camera, undo_history);
767 case LABEL_LAYER_MOVE:
768 return label_layer_move_event(label_layer, event, camera, undo_history);
770 case LABEL_LAYER_EDIT_TEXT:
771 return label_layer_edit_text_event(label_layer, event, camera, undo_history);
773 case LABEL_LAYER_EDIT_ID:
774 return label_layer_edit_id_event(label_layer, event, camera, undo_history);
776 case LABEL_LAYER_RECOLOR:
777 return label_layer_recolor_event(label_layer, event, camera, undo_history);
783 size_t label_layer_count(const LabelLayer *label_layer)
785 return dynarray_count(label_layer->ids);
788 char *label_layer_ids(const LabelLayer *label_layer)
790 return dynarray_data(label_layer->ids);
793 Point *label_layer_positions(const LabelLayer *label_layer)
795 return dynarray_data(label_layer->positions);
798 Color *label_layer_colors(const LabelLayer *label_layer)
800 return dynarray_data(label_layer->colors);
803 char *labels_layer_texts(const LabelLayer *label_layer)
805 return dynarray_data(label_layer->texts);
808 int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump)
810 trace_assert(label_layer);
811 trace_assert(filedump);
813 size_t n = dynarray_count(label_layer->ids);
814 char *ids = dynarray_data(label_layer->ids);
815 Point *positions = dynarray_data(label_layer->positions);
816 Color *colors = dynarray_data(label_layer->colors);
817 char *texts = dynarray_data(label_layer->texts);
819 fprintf(filedump, "%zd\n", n);
820 for (size_t i = 0; i < n; ++i) {
821 fprintf(filedump, "%s %f %f ",
822 ids + LABEL_LAYER_ID_MAX_SIZE * i,
823 positions[i].x, positions[i].y);
824 color_hex_to_stream(colors[i], filedump);
825 fprintf(filedump, "\n%s\n", texts + i * LABEL_LAYER_TEXT_MAX_SIZE);