3 #include "game/camera.h"
5 #include "system/stacktrace.h"
6 #include "system/nth_alloc.h"
7 #include "system/log.h"
10 #include "rect_layer.h"
12 #include "system/line_stream.h"
13 #include "color_picker.h"
14 #include "system/str.h"
15 #include "ui/edit_field.h"
16 #include "undo_history.h"
17 #include "game/level/action.h"
18 #include "action_picker.h"
20 #define RECT_LAYER_SELECTION_THICCNESS 10.0f
21 #define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
22 #define CREATE_AREA_THRESHOLD 10.0
23 #define RECT_LAYER_GRID_ROWS 3
24 #define RECT_LAYER_GRID_COLUMNS 4
26 static int clipboard = 0;
27 static Rect clipboard_rect;
28 static Color clipboard_color;
33 // TODO(#955): Rectangles in Level Editor have only one resize anchor to work with
47 ColorPicker color_picker;
48 ActionPicker action_picker;
52 Vec2f move_anchor; // The mouse offset from the left-top
53 // corner of the rect during moving it
54 Edit_field *id_edit_field;
58 const char *id_name_prefix;
77 char id[ENTITY_MAX_ID_SIZE];
98 UndoElementContext element;
103 UndoContext create_undo_add_context(RectLayer *layer, size_t index)
106 trace_assert(index < dynarray_count(layer->rects));
108 UndoContext undo_context;
109 undo_context.add.type = UNDO_ADD;
110 undo_context.add.layer = layer;
111 undo_context.add.index = index;
116 UndoContext create_undo_element_context(RectLayer *layer)
119 size_t index = (size_t) layer->selection;
120 trace_assert(index < dynarray_count(layer->rects));
122 UndoContext undo_context;
123 undo_context.element.layer = layer;
124 undo_context.element.index = index;
125 dynarray_copy_to(layer->rects, &undo_context.element.rect, index);
126 dynarray_copy_to(layer->colors, &undo_context.element.color, index);
127 dynarray_copy_to(layer->ids, undo_context.element.id, index);
128 dynarray_copy_to(layer->actions, &undo_context.element.action, index);
133 UndoContext create_undo_update_context(RectLayer *rect_layer)
135 UndoContext undo_context = create_undo_element_context(rect_layer);
136 undo_context.type = UNDO_UPDATE;
141 UndoContext create_undo_delete_context(RectLayer *rect_layer)
143 UndoContext undo_context = create_undo_element_context(rect_layer);
144 undo_context.type = UNDO_DELETE;
149 UndoContext create_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2)
151 UndoContext undo_context;
152 undo_context.swap.type = UNDO_SWAP;
153 undo_context.swap.layer = rect_layer;
154 undo_context.swap.index1 = index1;
155 undo_context.swap.index2 = index2;
160 void rect_layer_undo(void *context, size_t context_size)
162 trace_assert(context);
163 trace_assert(sizeof(UndoContext) == context_size);
165 UndoContext *undo_context = context;
167 switch (undo_context->type) {
169 RectLayer *layer = undo_context->add.layer;
170 dynarray_delete_at(layer->rects, undo_context->add.index);
171 dynarray_delete_at(layer->colors, undo_context->add.index);
172 dynarray_delete_at(layer->ids, undo_context->add.index);
173 dynarray_delete_at(layer->actions, undo_context->add.index);
174 layer->selection = -1;
178 RectLayer *layer = undo_context->element.layer;
179 dynarray_insert_before(layer->rects, undo_context->element.index, &undo_context->element.rect);
180 dynarray_insert_before(layer->colors, undo_context->element.index, &undo_context->element.color);
181 dynarray_insert_before(layer->ids, undo_context->element.index, &undo_context->element.id);
182 dynarray_insert_before(layer->actions, undo_context->element.index, &undo_context->element.action);
183 layer->selection = -1;
187 RectLayer *layer = undo_context->element.layer;
188 dynarray_replace_at(layer->rects, undo_context->element.index, &undo_context->element.rect);
189 dynarray_replace_at(layer->colors, undo_context->element.index, &undo_context->element.color);
190 dynarray_replace_at(layer->ids, undo_context->element.index, &undo_context->element.id);
191 dynarray_replace_at(layer->actions, undo_context->element.index, &undo_context->element.action);
195 RectLayer *layer = undo_context->element.layer;
196 dynarray_swap(layer->rects, undo_context->swap.index1, undo_context->swap.index2);
197 dynarray_swap(layer->colors, undo_context->swap.index1, undo_context->swap.index2);
198 dynarray_swap(layer->ids, undo_context->swap.index1, undo_context->swap.index2);
199 dynarray_swap(layer->actions, undo_context->swap.index1, undo_context->swap.index2);
204 #define UNDO_PUSH(HISTORY, CONTEXT) \
206 UndoContext context = (CONTEXT); \
214 static int rect_layer_add_rect(RectLayer *layer,
217 UndoHistory *undo_history)
221 if (dynarray_push(layer->rects, &rect) < 0) {
225 if (dynarray_push(layer->colors, &color) < 0) {
229 char id[ENTITY_MAX_ID_SIZE];
230 snprintf(id, ENTITY_MAX_ID_SIZE, "%s_%d",
231 layer->id_name_prefix,
232 layer->id_name_counter++);
233 if (dynarray_push(layer->ids, id)) {
237 dynarray_push_empty(layer->actions);
241 create_undo_add_context(
243 dynarray_count(layer->rects) - 1));
248 static int rect_layer_rect_at(RectLayer *layer, Vec2f position)
252 int n = (int) dynarray_count(layer->rects);
253 Rect *rects = dynarray_data(layer->rects);
255 for (int i = n - 1; i >= 0; --i) {
256 if (rect_contains_point(rects[i], position)) {
264 static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b,
265 UndoHistory *undo_history)
268 trace_assert(a < dynarray_count(layer->rects));
269 trace_assert(b < dynarray_count(layer->rects));
271 dynarray_swap(layer->rects, a, b);
272 dynarray_swap(layer->colors, a, b);
273 dynarray_swap(layer->ids, a, b);
274 dynarray_swap(layer->actions, a, b);
276 UNDO_PUSH(undo_history, create_undo_swap_context(layer, a, b));
279 static Rect rect_layer_resize_anchor(const Camera *camera, Rect boundary_rect)
281 const Rect overlay_rect = camera_rect(camera, boundary_rect);
283 overlay_rect.x + overlay_rect.w,
284 overlay_rect.y + overlay_rect.h,
285 RECT_LAYER_SELECTION_THICCNESS * 2.0,
286 RECT_LAYER_SELECTION_THICCNESS * 2.0);
289 static int rect_layer_delete_rect_at(RectLayer *layer,
291 UndoHistory *undo_history)
295 UNDO_PUSH(undo_history, create_undo_delete_context(layer));
297 dynarray_delete_at(layer->rects, i);
298 dynarray_delete_at(layer->colors, i);
299 dynarray_delete_at(layer->ids, i);
300 dynarray_delete_at(layer->actions, i);
305 static int rect_layer_event_idle(RectLayer *layer,
306 const SDL_Event *event,
307 const Camera *camera,
308 UndoHistory *undo_history)
312 trace_assert(camera);
314 int color_changed = 0;
315 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
320 if (layer->selection >= 0) {
321 dynarray_copy_to(layer->colors, &layer->inter_color, (size_t)layer->selection);
322 layer->state = RECT_LAYER_RECOLOR;
327 switch (event->type) {
328 case SDL_MOUSEBUTTONDOWN: {
329 switch (event->button.button) {
330 case SDL_BUTTON_LEFT: {
331 Vec2f position = camera_map_screen(
335 int rect_at_position =
336 rect_layer_rect_at(layer, position);
338 Rect *rects = dynarray_data(layer->rects);
339 Color *colors = dynarray_data(layer->colors);
341 if (layer->selection >= 0 && rect_contains_point(
342 rect_layer_resize_anchor(
344 rects[layer->selection]),
346 (float) event->button.x,
347 (float) event->button.y))) {
348 layer->state = RECT_LAYER_RESIZE;
349 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) layer->selection);
350 } else if (rect_at_position >= 0) {
351 layer->selection = rect_at_position;
352 layer->state = RECT_LAYER_MOVE;
357 rects[layer->selection].x,
358 rects[layer->selection].y));
359 layer->color_picker =
360 create_color_picker_from_rgba(colors[rect_at_position]);
362 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) rect_at_position);
364 layer->selection = rect_at_position;
366 if (layer->selection < 0) {
367 layer->state = RECT_LAYER_CREATE;
368 layer->create_begin = position;
369 layer->create_end = position;
377 switch (event->key.keysym.sym) {
379 if ((event->key.keysym.mod & KMOD_SHIFT)
380 && (layer->selection >= 0)
381 && ((size_t)(layer->selection + 1) < dynarray_count(layer->rects))) {
382 rect_layer_swap_elements(
384 (size_t) layer->selection,
385 (size_t) layer->selection + 1,
392 if ((event->key.keysym.mod & KMOD_SHIFT)
393 && (layer->selection > 0)
394 && ((size_t) layer->selection < dynarray_count(layer->rects))) {
395 rect_layer_swap_elements(
397 (size_t) layer->selection,
398 (size_t) layer->selection - 1,
405 if (layer->selection >= 0) {
406 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
407 layer->selection = -1;
412 if (layer->selection >= 0) {
413 const char *ids = dynarray_data(layer->ids);
414 Color *colors = dynarray_data(layer->colors);
417 layer->id_edit_field,
418 RECT_LAYER_ID_LABEL_SIZE,
419 color_invert(colors[layer->selection]));
421 layer->state = RECT_LAYER_ID_RENAME;
423 layer->id_edit_field,
424 ids + layer->selection * ENTITY_MAX_ID_SIZE);
425 SDL_StartTextInput();
430 if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
432 dynarray_copy_to(layer->rects, &clipboard_rect, (size_t)layer->selection);
433 dynarray_copy_to(layer->colors, &clipboard_color, (size_t)layer->selection);
438 if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
440 SDL_GetMouseState(&x, &y);
441 Vec2f position = camera_map_screen(camera, x, y);
445 rect(position.x, position.y,
446 clipboard_rect.w, clipboard_rect.h),
458 static int rect_layer_event_create(RectLayer *layer,
459 const SDL_Event *event,
460 const Camera *camera,
461 UndoHistory *undo_history)
465 trace_assert(camera);
467 switch (event->type) {
468 case SDL_MOUSEBUTTONUP: {
469 switch (event->button.button) {
470 case SDL_BUTTON_LEFT: {
471 const Rect real_rect =
475 const float area = real_rect.w * real_rect.h;
477 if (area >= CREATE_AREA_THRESHOLD) {
481 color_picker_rgba(&layer->color_picker),
484 log_info("The area is too small %f. Such small box won't be created.\n", area);
486 layer->state = RECT_LAYER_IDLE;
491 case SDL_MOUSEMOTION: {
492 layer->create_end = camera_map_screen(
501 static int rect_layer_event_resize(RectLayer *layer,
502 const SDL_Event *event,
503 const Camera *camera,
504 UndoHistory *undo_history)
508 trace_assert(camera);
509 trace_assert(layer->selection >= 0);
511 switch (event->type) {
512 case SDL_MOUSEMOTION: {
513 layer->inter_rect = rect_from_points(
514 vec(layer->inter_rect.x, layer->inter_rect.y),
520 vec(RECT_LAYER_SELECTION_THICCNESS * -0.5f,
521 RECT_LAYER_SELECTION_THICCNESS * -0.5f)));
524 case SDL_MOUSEBUTTONUP: {
525 layer->state = RECT_LAYER_IDLE;
526 UNDO_PUSH(undo_history, create_undo_update_context(layer));
527 dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
534 static int rect_layer_event_move(RectLayer *layer,
535 const SDL_Event *event,
536 const Camera *camera,
537 UndoHistory *undo_history)
541 trace_assert(camera);
542 trace_assert(layer->selection >= 0);
544 Rect *rects = dynarray_data(layer->rects);
546 switch (event->type) {
547 case SDL_MOUSEMOTION: {
548 const Uint8 *state = SDL_GetKeyboardState(NULL);
549 const Vec2f mouse_pos = vec_sub(
556 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
557 layer->inter_rect.x = mouse_pos.x;
558 layer->inter_rect.y = mouse_pos.y;
560 const Vec2f rect_pos = rect_position(rects[layer->selection]);
562 const float dx = fabsf(rect_pos.x - mouse_pos.x);
563 const float dy = fabsf(rect_pos.y - mouse_pos.y);
566 layer->inter_rect.x = mouse_pos.x;
567 layer->inter_rect.y = rect_pos.y;
569 layer->inter_rect.x = rect_pos.x;
570 layer->inter_rect.y = mouse_pos.y;
575 case SDL_MOUSEBUTTONUP: {
576 layer->state = RECT_LAYER_IDLE;
578 float distance = vec_length(
579 vec_sub(rect_position(layer->inter_rect),
580 rect_position(rects[layer->selection])));
582 if (distance > 1e-6) {
583 UNDO_PUSH(undo_history, create_undo_update_context(layer));
584 dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
591 static int rect_layer_event_id_rename(RectLayer *layer,
592 const SDL_Event *event,
593 const Camera *camera,
594 UndoHistory *undo_history)
598 trace_assert(camera);
599 trace_assert(layer->selection >= 0);
601 switch (event->type) {
603 switch (event->key.keysym.sym) {
605 UNDO_PUSH(undo_history, create_undo_update_context(layer));
607 char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
608 memset(id, 0, ENTITY_MAX_ID_SIZE);
609 memcpy(id, edit_field_as_text(layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
610 layer->state = RECT_LAYER_IDLE;
615 layer->state = RECT_LAYER_IDLE;
622 return edit_field_event(layer->id_edit_field, event);
625 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
634 RectLayer *create_rect_layer(const char *id_name_prefix)
636 Lt *lt = create_lt();
638 RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
644 layer->ids = PUSH_LT(
646 create_dynarray(sizeof(char) * ENTITY_MAX_ID_SIZE),
648 if (layer->ids == NULL) {
652 layer->rects = PUSH_LT(
654 create_dynarray(sizeof(Rect)),
656 if (layer->rects == NULL) {
660 layer->colors = PUSH_LT(
662 create_dynarray(sizeof(Color)),
664 if (layer->colors == NULL) {
668 layer->actions = PUSH_LT(
670 create_dynarray(sizeof(Action)),
672 if (layer->actions == NULL) {
676 layer->id_edit_field = PUSH_LT(
679 RECT_LAYER_ID_LABEL_SIZE,
682 if (layer->id_edit_field == NULL) {
691 sizeof(Grid) + sizeof(Widget*) * RECT_LAYER_GRID_ROWS * RECT_LAYER_GRID_COLUMNS),
693 if (layer->grid == NULL) {
696 layer->grid->rows = RECT_LAYER_GRID_ROWS;
697 layer->grid->columns = RECT_LAYER_GRID_COLUMNS;
698 grid_put_widget(layer->grid, &layer->action_picker.widget, 0, RECT_LAYER_GRID_COLUMNS - 1);
700 layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
701 layer->selection = -1;
702 layer->id_name_prefix = id_name_prefix;
707 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream, const char *id_name_prefix)
709 trace_assert(line_stream);
711 RectLayer *layer = create_rect_layer(id_name_prefix);
716 const char *line = line_stream_next(line_stream);
718 RETURN_LT(layer->lt, NULL);
722 if (sscanf(line, "%zu", &count) < 0) {
723 RETURN_LT(layer->lt, NULL);
726 for (size_t i = 0; i < count; ++i) {
727 line = line_stream_next(line_stream);
729 RETURN_LT(layer->lt, NULL);
734 char id[ENTITY_MAX_ID_SIZE];
738 "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s%f%f%f%f%6s%n",
743 log_fail("%s\n", strerror(errno));
744 RETURN_LT(layer->lt, NULL);
748 Color color = hexstr(hex);
749 dynarray_push(layer->rects, &rect);
750 dynarray_push(layer->ids, id);
751 dynarray_push(layer->colors, &color);
758 if (sscanf(line, "%d%n", (int*)&action.type, &n) > 0) {
760 switch (action.type) {
761 case ACTION_NONE: break;
763 case ACTION_TOGGLE_GOAL:
764 case ACTION_HIDE_LABEL: {
765 if (sscanf(line, "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s", action.entity_id) <= 0) {
766 log_fail("%s\n", strerror(errno));
767 RETURN_LT(layer->lt, NULL);
771 case ACTION_N: break;
775 dynarray_push(layer->actions, &action);
781 void destroy_rect_layer(RectLayer *layer)
784 RETURN_LT0(layer->lt);
787 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
790 trace_assert(camera);
792 const size_t n = dynarray_count(layer->rects);
793 Rect *rects = dynarray_data(layer->rects);
794 Color *colors = dynarray_data(layer->colors);
795 const char *ids = dynarray_data(layer->ids);
798 for (size_t i = 0; i < n; ++i) {
799 Rect rect = rects[i];
800 Color color = colors[i];
802 if (layer->selection == (int) i) {
803 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
804 rect = layer->inter_rect;
807 if (layer->state == RECT_LAYER_RECOLOR) {
808 color = layer->inter_color;
813 if (camera_fill_rect(
818 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
824 if (active && layer->selection >= 0) {
825 Rect rect = rects[layer->selection];
826 Color color = colors[layer->selection];
828 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
829 rect = layer->inter_rect;
832 if (layer->state == RECT_LAYER_RECOLOR) {
833 color = layer->inter_color;
836 const Rect overlay_rect =
838 camera_rect(camera, rect),
839 -RECT_LAYER_SELECTION_THICCNESS * 0.5f);
840 const Color overlay_color = color_invert(color);
843 if (camera_draw_thicc_rect_screen(
847 RECT_LAYER_SELECTION_THICCNESS) < 0) {
851 const Vec2f rect_id_pos = vec_sub(
854 RECT_LAYER_ID_LABEL_SIZE,
855 vec(0.0f, FONT_CHAR_HEIGHT)));
858 if (layer->state == RECT_LAYER_ID_RENAME) {
859 // ID renaming Edit Field
860 if (edit_field_render_world(
861 layer->id_edit_field,
868 if (camera_render_text(
870 ids + layer->selection * ENTITY_MAX_ID_SIZE,
871 RECT_LAYER_ID_LABEL_SIZE,
879 if (camera_fill_rect_screen(
881 rect_layer_resize_anchor(camera, rect),
882 overlay_color) < 0) {
888 const Color color = color_picker_rgba(&layer->color_picker);
889 if (layer->state == RECT_LAYER_CREATE) {
890 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
895 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
903 int rect_layer_event_recolor(RectLayer *layer,
904 const SDL_Event *event,
905 const Camera *camera,
906 UndoHistory *undo_history)
910 trace_assert(camera);
911 trace_assert(undo_history);
912 trace_assert(layer->selection >= 0);
914 int color_changed = 0;
915 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
920 layer->inter_color = color_picker_rgba(&layer->color_picker);
922 if (!color_picker_drag(&layer->color_picker)) {
923 UNDO_PUSH(undo_history, create_undo_update_context(layer));
924 dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color);
925 layer->state = RECT_LAYER_IDLE;
932 int rect_layer_event(RectLayer *layer,
933 const SDL_Event *event,
934 const Camera *camera,
935 UndoHistory *undo_history)
939 trace_assert(undo_history);
941 switch (event->type) {
942 case SDL_WINDOWEVENT: {
943 switch (event->window.event) {
944 case SDL_WINDOWEVENT_RESIZED: {
945 grid_relayout(layer->grid, rect(0.0f, 0.0f,
946 (float) event->window.data1,
947 (float) event->window.data2));
953 switch (layer->state) {
954 case RECT_LAYER_IDLE:
955 return rect_layer_event_idle(layer, event, camera, undo_history);
957 case RECT_LAYER_CREATE:
958 return rect_layer_event_create(layer, event, camera, undo_history);
960 case RECT_LAYER_RESIZE:
961 return rect_layer_event_resize(layer, event, camera, undo_history);
963 case RECT_LAYER_MOVE:
964 return rect_layer_event_move(layer, event, camera, undo_history);
966 case RECT_LAYER_ID_RENAME:
967 return rect_layer_event_id_rename(layer, event, camera, undo_history);
969 case RECT_LAYER_RECOLOR:
970 return rect_layer_event_recolor(layer, event, camera, undo_history);
976 size_t rect_layer_count(const RectLayer *layer)
978 return dynarray_count(layer->rects);
981 const Rect *rect_layer_rects(const RectLayer *layer)
983 return dynarray_data(layer->rects);
986 const Color *rect_layer_colors(const RectLayer *layer)
988 return dynarray_data(layer->colors);
991 const char *rect_layer_ids(const RectLayer *layer)
993 return dynarray_data(layer->ids);
996 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
999 trace_assert(filedump);
1001 size_t n = dynarray_count(layer->ids);
1002 char *ids = dynarray_data(layer->ids);
1003 Rect *rects = dynarray_data(layer->rects);
1004 Color *colors = dynarray_data(layer->colors);
1005 Action *actions = dynarray_data(layer->actions);
1007 fprintf(filedump, "%zd\n", n);
1008 for (size_t i = 0; i < n; ++i) {
1009 fprintf(filedump, "%s %f %f %f %f ",
1010 ids + ENTITY_MAX_ID_SIZE * i,
1011 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
1012 color_hex_to_stream(colors[i], filedump);
1014 switch (actions[i].type) {
1015 case ACTION_NONE: {} break;
1017 case ACTION_TOGGLE_GOAL:
1018 case ACTION_HIDE_LABEL: {
1019 fprintf(filedump, " %d %.*s",
1020 (int)actions[i].type,
1021 ENTITY_MAX_ID_SIZE, actions[i].entity_id);
1023 case ACTION_N: break;
1026 fprintf(filedump, "\n");
1032 const Action *rect_layer_actions(const RectLayer *layer)
1034 return dynarray_data(layer->actions);