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);
755 if (sscanf(line, "%d%n", (int*)&action.type, &n) > 0) {
757 switch (action.type) {
758 case ACTION_NONE: break;
760 case ACTION_TOGGLE_GOAL:
761 case ACTION_HIDE_LABEL: {
762 if (sscanf(line, "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s", action.entity_id) <= 0) {
763 log_fail("%s\n", strerror(errno));
764 RETURN_LT(layer->lt, NULL);
768 case ACTION_N: break;
772 dynarray_push(layer->actions, &action);
778 void destroy_rect_layer(RectLayer *layer)
781 RETURN_LT0(layer->lt);
784 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
787 trace_assert(camera);
789 const size_t n = dynarray_count(layer->rects);
790 Rect *rects = dynarray_data(layer->rects);
791 Color *colors = dynarray_data(layer->colors);
792 const char *ids = dynarray_data(layer->ids);
795 for (size_t i = 0; i < n; ++i) {
796 Rect rect = rects[i];
797 Color color = colors[i];
799 if (layer->selection == (int) i) {
800 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
801 rect = layer->inter_rect;
804 if (layer->state == RECT_LAYER_RECOLOR) {
805 color = layer->inter_color;
810 if (camera_fill_rect(
815 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
821 if (active && layer->selection >= 0) {
822 Rect rect = rects[layer->selection];
823 Color color = colors[layer->selection];
825 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
826 rect = layer->inter_rect;
829 if (layer->state == RECT_LAYER_RECOLOR) {
830 color = layer->inter_color;
833 const Rect overlay_rect =
835 camera_rect(camera, rect),
836 -RECT_LAYER_SELECTION_THICCNESS * 0.5f);
837 const Color overlay_color = color_invert(color);
840 if (camera_draw_thicc_rect_screen(
844 RECT_LAYER_SELECTION_THICCNESS) < 0) {
848 const Vec2f rect_id_pos = vec_sub(
851 RECT_LAYER_ID_LABEL_SIZE,
852 vec(0.0f, FONT_CHAR_HEIGHT)));
855 if (layer->state == RECT_LAYER_ID_RENAME) {
856 // ID renaming Edit Field
857 if (edit_field_render_world(
858 layer->id_edit_field,
865 if (camera_render_text(
867 ids + layer->selection * ENTITY_MAX_ID_SIZE,
868 RECT_LAYER_ID_LABEL_SIZE,
876 if (camera_fill_rect_screen(
878 rect_layer_resize_anchor(camera, rect),
879 overlay_color) < 0) {
885 const Color color = color_picker_rgba(&layer->color_picker);
886 if (layer->state == RECT_LAYER_CREATE) {
887 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
892 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
900 int rect_layer_event_recolor(RectLayer *layer,
901 const SDL_Event *event,
902 const Camera *camera,
903 UndoHistory *undo_history)
907 trace_assert(camera);
908 trace_assert(undo_history);
909 trace_assert(layer->selection >= 0);
911 int color_changed = 0;
912 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
917 layer->inter_color = color_picker_rgba(&layer->color_picker);
919 if (!color_picker_drag(&layer->color_picker)) {
920 UNDO_PUSH(undo_history, create_undo_update_context(layer));
921 dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color);
922 layer->state = RECT_LAYER_IDLE;
929 int rect_layer_event(RectLayer *layer,
930 const SDL_Event *event,
931 const Camera *camera,
932 UndoHistory *undo_history)
936 trace_assert(undo_history);
938 switch (event->type) {
939 case SDL_WINDOWEVENT: {
940 switch (event->window.event) {
941 case SDL_WINDOWEVENT_RESIZED: {
942 grid_relayout(layer->grid, rect(0.0f, 0.0f,
943 (float) event->window.data1,
944 (float) event->window.data2));
950 switch (layer->state) {
951 case RECT_LAYER_IDLE:
952 return rect_layer_event_idle(layer, event, camera, undo_history);
954 case RECT_LAYER_CREATE:
955 return rect_layer_event_create(layer, event, camera, undo_history);
957 case RECT_LAYER_RESIZE:
958 return rect_layer_event_resize(layer, event, camera, undo_history);
960 case RECT_LAYER_MOVE:
961 return rect_layer_event_move(layer, event, camera, undo_history);
963 case RECT_LAYER_ID_RENAME:
964 return rect_layer_event_id_rename(layer, event, camera, undo_history);
966 case RECT_LAYER_RECOLOR:
967 return rect_layer_event_recolor(layer, event, camera, undo_history);
973 size_t rect_layer_count(const RectLayer *layer)
975 return dynarray_count(layer->rects);
978 const Rect *rect_layer_rects(const RectLayer *layer)
980 return dynarray_data(layer->rects);
983 const Color *rect_layer_colors(const RectLayer *layer)
985 return dynarray_data(layer->colors);
988 const char *rect_layer_ids(const RectLayer *layer)
990 return dynarray_data(layer->ids);
993 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
996 trace_assert(filedump);
998 size_t n = dynarray_count(layer->ids);
999 char *ids = dynarray_data(layer->ids);
1000 Rect *rects = dynarray_data(layer->rects);
1001 Color *colors = dynarray_data(layer->colors);
1002 Action *actions = dynarray_data(layer->actions);
1004 fprintf(filedump, "%zd\n", n);
1005 for (size_t i = 0; i < n; ++i) {
1006 fprintf(filedump, "%s %f %f %f %f ",
1007 ids + ENTITY_MAX_ID_SIZE * i,
1008 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
1009 color_hex_to_stream(colors[i], filedump);
1011 switch (actions[i].type) {
1012 case ACTION_NONE: {} break;
1014 case ACTION_TOGGLE_GOAL:
1015 case ACTION_HIDE_LABEL: {
1016 fprintf(filedump, " %d %.*s",
1017 (int)actions[i].type,
1018 ENTITY_MAX_ID_SIZE, actions[i].entity_id);
1020 case ACTION_N: break;
1023 fprintf(filedump, "\n");
1029 const Action *rect_layer_actions(const RectLayer *layer)
1031 return dynarray_data(layer->actions);