1 #include "game/camera.h"
3 #include "system/stacktrace.h"
4 #include "system/nth_alloc.h"
5 #include "system/log.h"
8 #include "rect_layer.h"
10 #include "system/line_stream.h"
11 #include "color_picker.h"
12 #include "system/str.h"
13 #include "ui/edit_field.h"
14 #include "undo_history.h"
16 #define RECT_LAYER_SELECTION_THICCNESS 10.0f
17 #define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
18 #define CREATE_AREA_THRESHOLD 10.0
22 static int clipboard = 0;
23 static Rect clipboard_rect;
24 static Color clipboard_color;
29 // TODO(#955): Rectangles in Level Editor have only one resize anchor to work with
42 ColorPicker color_picker;
47 Edit_field *id_edit_field;
51 const char *id_name_prefix;
68 char id[RECT_LAYER_ID_MAX_SIZE];
89 UndoElementContext element;
94 UndoContext create_undo_add_context(RectLayer *layer, size_t index)
97 trace_assert(index < dynarray_count(layer->rects));
99 UndoContext undo_context;
100 undo_context.add.type = UNDO_ADD;
101 undo_context.add.layer = layer;
102 undo_context.add.index = index;
107 UndoContext create_undo_element_context(RectLayer *layer)
110 size_t index = (size_t) layer->selection;
111 trace_assert(index < dynarray_count(layer->rects));
113 UndoContext undo_context;
114 undo_context.element.layer = layer;
115 undo_context.element.index = index;
116 dynarray_copy_to(layer->rects, &undo_context.element.rect, index);
117 dynarray_copy_to(layer->colors, &undo_context.element.color, index);
118 dynarray_copy_to(layer->ids, undo_context.element.id, index);
123 UndoContext create_undo_update_context(RectLayer *rect_layer)
125 UndoContext undo_context = create_undo_element_context(rect_layer);
126 undo_context.type = UNDO_UPDATE;
131 UndoContext create_undo_delete_context(RectLayer *rect_layer)
133 UndoContext undo_context = create_undo_element_context(rect_layer);
134 undo_context.type = UNDO_DELETE;
139 UndoContext create_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2)
141 UndoContext undo_context;
142 undo_context.swap.type = UNDO_SWAP;
143 undo_context.swap.layer = rect_layer;
144 undo_context.swap.index1 = index1;
145 undo_context.swap.index2 = index2;
150 void rect_layer_undo(void *context, size_t context_size)
152 trace_assert(context);
153 trace_assert(sizeof(UndoContext) == context_size);
155 UndoContext *undo_context = context;
157 switch (undo_context->type) {
159 RectLayer *layer = undo_context->add.layer;
160 dynarray_delete_at(layer->rects, undo_context->add.index);
161 dynarray_delete_at(layer->colors, undo_context->add.index);
162 dynarray_delete_at(layer->ids, undo_context->add.index);
163 layer->selection = -1;
167 RectLayer *layer = undo_context->element.layer;
168 dynarray_insert_before(layer->rects, undo_context->element.index, &undo_context->element.rect);
169 dynarray_insert_before(layer->colors, undo_context->element.index, &undo_context->element.color);
170 dynarray_insert_before(layer->ids, undo_context->element.index, &undo_context->element.id);
171 layer->selection = -1;
175 RectLayer *layer = undo_context->element.layer;
176 dynarray_replace_at(layer->rects, undo_context->element.index, &undo_context->element.rect);
177 dynarray_replace_at(layer->colors, undo_context->element.index, &undo_context->element.color);
178 dynarray_replace_at(layer->ids, undo_context->element.index, &undo_context->element.id);
182 RectLayer *layer = undo_context->element.layer;
183 dynarray_swap(layer->rects, undo_context->swap.index1, undo_context->swap.index2);
184 dynarray_swap(layer->colors, undo_context->swap.index1, undo_context->swap.index2);
185 dynarray_swap(layer->ids, undo_context->swap.index1, undo_context->swap.index2);
190 #define UNDO_PUSH(HISTORY, CONTEXT) \
192 UndoContext context = (CONTEXT); \
200 static int rect_layer_add_rect(RectLayer *layer,
203 UndoHistory *undo_history)
207 if (dynarray_push(layer->rects, &rect) < 0) {
211 if (dynarray_push(layer->colors, &color) < 0) {
215 char id[RECT_LAYER_ID_MAX_SIZE];
216 snprintf(id, RECT_LAYER_ID_MAX_SIZE, "%s_%d",
217 layer->id_name_prefix,
218 layer->id_name_counter++);
219 if (dynarray_push(layer->ids, id)) {
225 create_undo_add_context(
227 dynarray_count(layer->rects) - 1));
232 static int rect_layer_rect_at(RectLayer *layer, Vec2f position)
236 int n = (int) dynarray_count(layer->rects);
237 Rect *rects = dynarray_data(layer->rects);
239 for (int i = n - 1; i >= 0; --i) {
240 if (rect_contains_point(rects[i], position)) {
248 static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b,
249 UndoHistory *undo_history)
252 trace_assert(a < dynarray_count(layer->rects));
253 trace_assert(b < dynarray_count(layer->rects));
255 dynarray_swap(layer->rects, a, b);
256 dynarray_swap(layer->colors, a, b);
257 dynarray_swap(layer->ids, a, b);
259 UNDO_PUSH(undo_history, create_undo_swap_context(layer, a, b));
262 static Rect rect_layer_resize_anchor(const Camera *camera, Rect boundary_rect)
264 const Rect overlay_rect =
266 camera_rect(camera, boundary_rect),
267 RECT_LAYER_SELECTION_THICCNESS * 0.5f);
270 overlay_rect.x + overlay_rect.w,
271 overlay_rect.y + overlay_rect.h,
272 RECT_LAYER_SELECTION_THICCNESS * 2.0f,
273 RECT_LAYER_SELECTION_THICCNESS * 2.0f);
276 static int rect_layer_delete_rect_at(RectLayer *layer,
278 UndoHistory *undo_history)
282 UNDO_PUSH(undo_history, create_undo_delete_context(layer));
284 dynarray_delete_at(layer->rects, i);
285 dynarray_delete_at(layer->colors, i);
286 dynarray_delete_at(layer->ids, i);
291 static int rect_layer_event_idle(RectLayer *layer,
292 const SDL_Event *event,
293 const Camera *camera,
294 UndoHistory *undo_history)
298 trace_assert(camera);
300 int color_changed = 0;
301 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
306 if (layer->selection >= 0) {
307 dynarray_copy_to(layer->colors, &layer->inter_color, (size_t)layer->selection);
308 layer->state = RECT_LAYER_RECOLOR;
313 switch (event->type) {
314 case SDL_MOUSEBUTTONDOWN: {
315 switch (event->button.button) {
316 case SDL_BUTTON_LEFT: {
317 Vec2f position = camera_map_screen(
321 int rect_at_position =
322 rect_layer_rect_at(layer, position);
324 Rect *rects = dynarray_data(layer->rects);
325 Color *colors = dynarray_data(layer->colors);
327 if (layer->selection >= 0 && rect_contains_point(
328 rect_layer_resize_anchor(
330 rects[layer->selection]),
332 (float) event->button.x,
333 (float) event->button.y))) {
334 layer->state = RECT_LAYER_RESIZE;
335 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) layer->selection);
336 } else if (rect_at_position >= 0) {
337 layer->selection = rect_at_position;
338 layer->state = RECT_LAYER_MOVE;
343 rects[layer->selection].x,
344 rects[layer->selection].y));
345 layer->color_picker =
346 create_color_picker_from_rgba(colors[rect_at_position]);
348 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) rect_at_position);
350 layer->selection = rect_at_position;
352 if (layer->selection < 0) {
353 layer->state = RECT_LAYER_CREATE;
354 layer->create_begin = position;
355 layer->create_end = position;
363 switch (event->key.keysym.sym) {
365 if ((event->key.keysym.mod & KMOD_SHIFT)
366 && (layer->selection >= 0)
367 && ((size_t)(layer->selection + 1) < dynarray_count(layer->rects))) {
368 rect_layer_swap_elements(
370 (size_t) layer->selection,
371 (size_t) layer->selection + 1,
378 if ((event->key.keysym.mod & KMOD_SHIFT)
379 && (layer->selection > 0)
380 && ((size_t) layer->selection < dynarray_count(layer->rects))) {
381 rect_layer_swap_elements(
383 (size_t) layer->selection,
384 (size_t) layer->selection - 1,
391 if (layer->selection >= 0) {
392 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
393 layer->selection = -1;
398 if (layer->selection >= 0) {
399 const char *ids = dynarray_data(layer->ids);
400 Color *colors = dynarray_data(layer->colors);
403 layer->id_edit_field,
404 RECT_LAYER_ID_LABEL_SIZE,
405 color_invert(colors[layer->selection]));
407 layer->state = RECT_LAYER_ID_RENAME;
409 layer->id_edit_field,
410 ids + layer->selection * RECT_LAYER_ID_MAX_SIZE);
411 SDL_StartTextInput();
416 if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
418 dynarray_copy_to(layer->rects, &clipboard_rect, (size_t)layer->selection);
419 dynarray_copy_to(layer->colors, &clipboard_color, (size_t)layer->selection);
424 if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
426 SDL_GetMouseState(&x, &y);
427 Vec2f position = camera_map_screen(camera, x, y);
431 rect(position.x, position.y,
432 clipboard_rect.w, clipboard_rect.h),
444 static int rect_layer_event_create(RectLayer *layer,
445 const SDL_Event *event,
446 const Camera *camera,
447 UndoHistory *undo_history)
451 trace_assert(camera);
453 switch (event->type) {
454 case SDL_MOUSEBUTTONUP: {
455 switch (event->button.button) {
456 case SDL_BUTTON_LEFT: {
457 const Rect real_rect =
461 const float area = real_rect.w * real_rect.h;
463 if (area >= CREATE_AREA_THRESHOLD) {
467 color_picker_rgba(&layer->color_picker),
470 log_info("The area is too small %f. Such small box won't be created.\n", area);
472 layer->state = RECT_LAYER_IDLE;
477 case SDL_MOUSEMOTION: {
478 layer->create_end = camera_map_screen(
487 static int rect_layer_event_resize(RectLayer *layer,
488 const SDL_Event *event,
489 const Camera *camera,
490 UndoHistory *undo_history)
494 trace_assert(camera);
495 trace_assert(layer->selection >= 0);
497 switch (event->type) {
498 case SDL_MOUSEMOTION: {
499 layer->inter_rect = rect_from_points(
500 vec(layer->inter_rect.x, layer->inter_rect.y),
506 vec(RECT_LAYER_SELECTION_THICCNESS * -0.5f,
507 RECT_LAYER_SELECTION_THICCNESS * -0.5f)));
510 case SDL_MOUSEBUTTONUP: {
511 layer->state = RECT_LAYER_IDLE;
512 UNDO_PUSH(undo_history, create_undo_update_context(layer));
513 dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
520 static int rect_layer_event_move(RectLayer *layer,
521 const SDL_Event *event,
522 const Camera *camera,
523 UndoHistory *undo_history)
527 trace_assert(camera);
528 trace_assert(layer->selection >= 0);
530 Rect *rects = dynarray_data(layer->rects);
532 switch (event->type) {
533 case SDL_MOUSEMOTION: {
534 Vec2f position = vec_sub(
541 trace_assert(layer->selection >= 0);
543 layer->inter_rect.x = position.x;
544 layer->inter_rect.y = position.y;
547 case SDL_MOUSEBUTTONUP: {
548 layer->state = RECT_LAYER_IDLE;
550 float distance = vec_length(
551 vec_sub(rect_position(layer->inter_rect),
552 rect_position(rects[layer->selection])));
554 if (distance > 1e-6) {
555 UNDO_PUSH(undo_history, create_undo_update_context(layer));
556 dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
563 static int rect_layer_event_id_rename(RectLayer *layer,
564 const SDL_Event *event,
565 const Camera *camera,
566 UndoHistory *undo_history)
570 trace_assert(camera);
571 trace_assert(layer->selection >= 0);
573 switch (event->type) {
575 switch (event->key.keysym.sym) {
577 UNDO_PUSH(undo_history, create_undo_update_context(layer));
579 char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
580 memset(id, 0, RECT_LAYER_ID_MAX_SIZE);
581 memcpy(id, edit_field_as_text(layer->id_edit_field), RECT_LAYER_ID_MAX_SIZE - 1);
582 layer->state = RECT_LAYER_IDLE;
587 layer->state = RECT_LAYER_IDLE;
594 return edit_field_event(layer->id_edit_field, event);
597 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
606 RectLayer *create_rect_layer(const char *id_name_prefix)
608 Lt *lt = create_lt();
610 RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
616 layer->ids = PUSH_LT(
618 create_dynarray(sizeof(char) * RECT_LAYER_ID_MAX_SIZE),
620 if (layer->ids == NULL) {
624 layer->rects = PUSH_LT(
626 create_dynarray(sizeof(Rect)),
628 if (layer->rects == NULL) {
632 layer->colors = PUSH_LT(
634 create_dynarray(sizeof(Color)),
636 if (layer->colors == NULL) {
640 layer->id_edit_field = PUSH_LT(
643 RECT_LAYER_ID_LABEL_SIZE,
646 if (layer->id_edit_field == NULL) {
650 layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
651 layer->selection = -1;
652 layer->id_name_prefix = id_name_prefix;
657 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream, const char *id_name_prefix)
659 trace_assert(line_stream);
661 RectLayer *layer = create_rect_layer(id_name_prefix);
666 const char *line = line_stream_next(line_stream);
668 RETURN_LT(layer->lt, NULL);
672 if (sscanf(line, "%zu", &count) < 0) {
673 RETURN_LT(layer->lt, NULL);
676 for (size_t i = 0; i < count; ++i) {
677 line = line_stream_next(line_stream);
679 RETURN_LT(layer->lt, NULL);
684 char id[RECT_LAYER_ID_MAX_SIZE];
687 "%"STRINGIFY(RECT_LAYER_ID_MAX_SIZE)"s%f%f%f%f%6s\n",
692 RETURN_LT(layer->lt, NULL);
695 Color color = hexstr(hex);
697 dynarray_push(layer->rects, &rect);
698 dynarray_push(layer->ids, id);
699 dynarray_push(layer->colors, &color);
705 void destroy_rect_layer(RectLayer *layer)
708 RETURN_LT0(layer->lt);
711 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
714 trace_assert(camera);
716 const size_t n = dynarray_count(layer->rects);
717 Rect *rects = dynarray_data(layer->rects);
718 Color *colors = dynarray_data(layer->colors);
719 const char *ids = dynarray_data(layer->ids);
722 for (size_t i = 0; i < n; ++i) {
723 Rect rect = rects[i];
724 Color color = colors[i];
726 if (layer->selection == (int) i) {
727 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
728 rect = layer->inter_rect;
731 if (layer->state == RECT_LAYER_RECOLOR) {
732 color = layer->inter_color;
736 if (camera_fill_rect(
741 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
746 if (active && (size_t) layer->selection == i) {
747 const Rect overlay_rect =
749 camera_rect(camera, rect),
750 RECT_LAYER_SELECTION_THICCNESS * 0.5f);
751 const Color overlay_color = color_invert(color);
754 if (camera_fill_rect(
759 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
763 if (camera_draw_thicc_rect_screen(
767 RECT_LAYER_SELECTION_THICCNESS) < 0) {
772 if (layer->state == RECT_LAYER_ID_RENAME) {
773 // ID renaming Edit Field
774 if (edit_field_render_world(
775 layer->id_edit_field,
777 rect_position(rect)) < 0) {
782 if (camera_render_text(
784 ids + layer->selection * RECT_LAYER_ID_MAX_SIZE,
785 RECT_LAYER_ID_LABEL_SIZE,
787 rect_position(rect)) < 0) {
793 if (camera_fill_rect_screen(
795 rect_layer_resize_anchor(camera, rect),
796 overlay_color) < 0) {
803 const Color color = color_picker_rgba(&layer->color_picker);
804 if (layer->state == RECT_LAYER_CREATE) {
805 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
810 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
818 int rect_layer_event_recolor(RectLayer *layer,
819 const SDL_Event *event,
820 const Camera *camera,
821 UndoHistory *undo_history)
825 trace_assert(camera);
826 trace_assert(undo_history);
827 trace_assert(layer->selection >= 0);
829 int color_changed = 0;
830 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
835 layer->inter_color = color_picker_rgba(&layer->color_picker);
837 if (!color_picker_drag(&layer->color_picker)) {
838 UNDO_PUSH(undo_history, create_undo_update_context(layer));
839 dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color);
840 layer->state = RECT_LAYER_IDLE;
847 int rect_layer_event(RectLayer *layer,
848 const SDL_Event *event,
849 const Camera *camera,
850 UndoHistory *undo_history)
854 trace_assert(undo_history);
856 switch (layer->state) {
857 case RECT_LAYER_IDLE:
858 return rect_layer_event_idle(layer, event, camera, undo_history);
860 case RECT_LAYER_CREATE:
861 return rect_layer_event_create(layer, event, camera, undo_history);
863 case RECT_LAYER_RESIZE:
864 return rect_layer_event_resize(layer, event, camera, undo_history);
866 case RECT_LAYER_MOVE:
867 return rect_layer_event_move(layer, event, camera, undo_history);
869 case RECT_LAYER_ID_RENAME:
870 return rect_layer_event_id_rename(layer, event, camera, undo_history);
872 case RECT_LAYER_RECOLOR:
873 return rect_layer_event_recolor(layer, event, camera, undo_history);
879 size_t rect_layer_count(const RectLayer *layer)
881 return dynarray_count(layer->rects);
884 const Rect *rect_layer_rects(const RectLayer *layer)
886 return dynarray_data(layer->rects);
889 const Color *rect_layer_colors(const RectLayer *layer)
891 return dynarray_data(layer->colors);
894 const char *rect_layer_ids(const RectLayer *layer)
896 return dynarray_data(layer->ids);
899 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
902 trace_assert(filedump);
904 size_t n = dynarray_count(layer->ids);
905 char *ids = dynarray_data(layer->ids);
906 Rect *rects = dynarray_data(layer->rects);
907 Color *colors = dynarray_data(layer->colors);
909 fprintf(filedump, "%zd\n", n);
910 for (size_t i = 0; i < n; ++i) {
911 fprintf(filedump, "%s %f %f %f %f ",
912 ids + RECT_LAYER_ID_MAX_SIZE * i,
913 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
914 color_hex_to_stream(colors[i], filedump);
915 fprintf(filedump, "\n");