4 #include "game/camera.h"
6 #include "system/stacktrace.h"
7 #include "system/nth_alloc.h"
8 #include "system/log.h"
11 #include "rect_layer.h"
13 #include "system/line_stream.h"
14 #include "color_picker.h"
15 #include "system/str.h"
16 #include "ui/edit_field.h"
17 #include "undo_history.h"
18 #include "game/level/action.h"
19 #include "action_picker.h"
22 #define RECT_LAYER_SELECTION_THICCNESS 15.0f
23 #define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
24 #define CREATE_AREA_THRESHOLD 10.0
25 #define RECT_LAYER_GRID_ROWS 3
26 #define RECT_LAYER_GRID_COLUMNS 4
28 static int rect_clipboard = 0;
29 static Rect rect_clipboard_rect;
30 static Color rect_clipboard_color;
32 static Cursor_Style resize_styles[1 << RECT_SIDE_N] = {
34 CURSOR_STYLE_RESIZE_VERT, // [1]
35 CURSOR_STYLE_RESIZE_HORIS, // [2]
36 CURSOR_STYLE_RESIZE_DIAG1, // [3]
37 CURSOR_STYLE_RESIZE_VERT, // [4]
39 CURSOR_STYLE_RESIZE_DIAG2, // [6]
41 CURSOR_STYLE_RESIZE_HORIS, // [8]
42 CURSOR_STYLE_RESIZE_DIAG2, // [9]
45 CURSOR_STYLE_RESIZE_DIAG1 // [12]
65 ColorPicker color_picker;
66 ActionPicker action_picker;
70 Vec2f move_anchor; // The mouse offset from the left-top
71 // corner of the rect during moving it
72 Edit_field *id_edit_field;
76 const char *id_name_prefix;
98 char id[ENTITY_MAX_ID_SIZE];
119 UndoElementContext element;
120 UndoSwapContext swap;
124 RectUndoContext create_rect_undo_add_context(RectLayer *layer, size_t index)
127 trace_assert(index < dynarray_count(layer->rects));
129 RectUndoContext undo_context;
130 undo_context.add.type = RECT_UNDO_ADD;
131 undo_context.add.layer = layer;
132 undo_context.add.index = index;
137 RectUndoContext create_rect_undo_element_context(RectLayer *layer)
140 size_t index = (size_t) layer->selection;
141 trace_assert(index < dynarray_count(layer->rects));
143 RectUndoContext undo_context;
144 undo_context.element.layer = layer;
145 undo_context.element.index = index;
146 dynarray_copy_to(layer->rects, &undo_context.element.rect, index);
147 dynarray_copy_to(layer->colors, &undo_context.element.color, index);
148 dynarray_copy_to(layer->ids, undo_context.element.id, index);
149 dynarray_copy_to(layer->actions, &undo_context.element.action, index);
154 RectUndoContext create_rect_undo_update_context(RectLayer *rect_layer)
156 RectUndoContext undo_context = create_rect_undo_element_context(rect_layer);
157 undo_context.type = RECT_UNDO_UPDATE;
162 RectUndoContext create_rect_undo_delete_context(RectLayer *rect_layer)
164 RectUndoContext undo_context = create_rect_undo_element_context(rect_layer);
165 undo_context.type = RECT_UNDO_DELETE;
170 RectUndoContext create_rect_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2)
172 RectUndoContext undo_context;
173 undo_context.swap.type = RECT_UNDO_SWAP;
174 undo_context.swap.layer = rect_layer;
175 undo_context.swap.index1 = index1;
176 undo_context.swap.index2 = index2;
181 void rect_layer_undo(void *context, size_t context_size)
183 trace_assert(context);
184 trace_assert(sizeof(RectUndoContext) == context_size);
186 RectUndoContext *undo_context = context;
188 switch (undo_context->type) {
189 case RECT_UNDO_ADD: {
190 RectLayer *layer = undo_context->add.layer;
191 dynarray_delete_at(layer->rects, undo_context->add.index);
192 dynarray_delete_at(layer->colors, undo_context->add.index);
193 dynarray_delete_at(layer->ids, undo_context->add.index);
194 dynarray_delete_at(layer->actions, undo_context->add.index);
195 layer->selection = -1;
198 case RECT_UNDO_DELETE: {
199 RectLayer *layer = undo_context->element.layer;
200 dynarray_insert_before(layer->rects, undo_context->element.index, &undo_context->element.rect);
201 dynarray_insert_before(layer->colors, undo_context->element.index, &undo_context->element.color);
202 dynarray_insert_before(layer->ids, undo_context->element.index, &undo_context->element.id);
203 dynarray_insert_before(layer->actions, undo_context->element.index, &undo_context->element.action);
204 layer->selection = -1;
207 case RECT_UNDO_UPDATE: {
208 RectLayer *layer = undo_context->element.layer;
209 dynarray_replace_at(layer->rects, undo_context->element.index, &undo_context->element.rect);
210 dynarray_replace_at(layer->colors, undo_context->element.index, &undo_context->element.color);
211 dynarray_replace_at(layer->ids, undo_context->element.index, &undo_context->element.id);
212 dynarray_replace_at(layer->actions, undo_context->element.index, &undo_context->element.action);
215 case RECT_UNDO_SWAP: {
216 RectLayer *layer = undo_context->element.layer;
217 dynarray_swap(layer->rects, undo_context->swap.index1, undo_context->swap.index2);
218 dynarray_swap(layer->colors, undo_context->swap.index1, undo_context->swap.index2);
219 dynarray_swap(layer->ids, undo_context->swap.index1, undo_context->swap.index2);
220 dynarray_swap(layer->actions, undo_context->swap.index1, undo_context->swap.index2);
225 #define RECT_UNDO_PUSH(HISTORY, CONTEXT) \
227 RectUndoContext context = (CONTEXT); \
235 static int rect_layer_add_rect(RectLayer *layer,
238 UndoHistory *undo_history)
242 if (dynarray_push(layer->rects, &rect) < 0) {
246 if (dynarray_push(layer->colors, &color) < 0) {
250 char id[ENTITY_MAX_ID_SIZE];
251 snprintf(id, ENTITY_MAX_ID_SIZE, "%s_%d",
252 layer->id_name_prefix,
253 layer->id_name_counter++);
254 if (dynarray_push(layer->ids, id)) {
258 dynarray_push_empty(layer->actions);
262 create_rect_undo_add_context(
264 dynarray_count(layer->rects) - 1));
269 static int rect_layer_rect_at(RectLayer *layer, Vec2f position)
273 int n = (int) dynarray_count(layer->rects);
274 Rect *rects = dynarray_data(layer->rects);
276 for (int i = n - 1; i >= 0; --i) {
277 if (rect_contains_point(rects[i], position)) {
285 static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b,
286 UndoHistory *undo_history)
289 trace_assert(a < dynarray_count(layer->rects));
290 trace_assert(b < dynarray_count(layer->rects));
292 dynarray_swap(layer->rects, a, b);
293 dynarray_swap(layer->colors, a, b);
294 dynarray_swap(layer->ids, a, b);
295 dynarray_swap(layer->actions, a, b);
297 RECT_UNDO_PUSH(undo_history, create_rect_undo_swap_context(layer, a, b));
300 static int rect_layer_delete_rect_at(RectLayer *layer,
302 UndoHistory *undo_history)
306 RECT_UNDO_PUSH(undo_history, create_rect_undo_delete_context(layer));
308 dynarray_delete_at(layer->rects, i);
309 dynarray_delete_at(layer->colors, i);
310 dynarray_delete_at(layer->ids, i);
311 dynarray_delete_at(layer->actions, i);
316 static int calc_resize_mask(Vec2f point, Rect rect)
319 for (Rect_side side = 0; side < RECT_SIDE_N; ++side) {
320 if (rect_side_distance(rect, point, side) < RECT_LAYER_SELECTION_THICCNESS) {
321 mask = mask | (1 << side);
327 static int rect_layer_event_idle(RectLayer *layer,
328 const SDL_Event *event,
329 const Camera *camera,
330 UndoHistory *undo_history)
334 trace_assert(camera);
336 int color_changed = 0;
337 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
342 if (layer->selection >= 0) {
343 dynarray_copy_to(layer->colors, &layer->inter_color, (size_t)layer->selection);
344 layer->state = RECT_LAYER_RECOLOR;
349 Rect *rects = dynarray_data(layer->rects);
351 switch (event->type) {
352 case SDL_MOUSEBUTTONDOWN: {
353 switch (event->button.button) {
354 case SDL_BUTTON_LEFT: {
355 Vec2f position = camera_map_screen(
359 int rect_at_position =
360 rect_layer_rect_at(layer, position);
363 Color *colors = dynarray_data(layer->colors);
365 if (layer->selection >= 0 &&
366 layer->selection == rect_at_position &&
367 (layer->resize_mask = calc_resize_mask(
368 vec((float) event->button.x, (float)event->button.y),
369 camera_rect(camera, rects[layer->selection])))) {
370 layer->state = RECT_LAYER_RESIZE;
371 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) layer->selection);
372 } else if (rect_at_position >= 0) {
373 layer->selection = rect_at_position;
374 layer->state = RECT_LAYER_MOVE;
375 layer->move_anchor = vec_sub(
378 rects[layer->selection].x,
379 rects[layer->selection].y));
380 layer->color_picker =
381 create_color_picker_from_rgba(colors[rect_at_position]);
382 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) rect_at_position);
384 layer->selection = rect_at_position;
386 if (layer->selection < 0) {
387 layer->state = RECT_LAYER_CREATE;
388 layer->create_begin = position;
389 layer->create_end = position;
396 case SDL_MOUSEMOTION: {
398 Vec2f position = camera_map_screen(
402 if (layer->selection >= 0 &&
403 layer->selection == rect_layer_rect_at(layer, position) &&
404 (resize_mask = calc_resize_mask(
405 vec((float) event->button.x, (float)event->button.y),
406 camera_rect(camera, rects[layer->selection])))) {
407 layer->cursor->style = resize_styles[resize_mask];
409 layer->cursor->style = CURSOR_STYLE_POINTER;
414 switch (event->key.keysym.sym) {
416 if ((event->key.keysym.mod & KMOD_SHIFT)
417 && (layer->selection >= 0)
418 && ((size_t)(layer->selection + 1) < dynarray_count(layer->rects))) {
419 rect_layer_swap_elements(
421 (size_t) layer->selection,
422 (size_t) layer->selection + 1,
429 if ((event->key.keysym.mod & KMOD_SHIFT)
430 && (layer->selection > 0)
431 && ((size_t) layer->selection < dynarray_count(layer->rects))) {
432 rect_layer_swap_elements(
434 (size_t) layer->selection,
435 (size_t) layer->selection - 1,
442 if (layer->selection >= 0) {
443 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
444 layer->selection = -1;
449 // TODO(#1171): there is no UI indication that we are in the snapping mode
450 layer->snapping_enabled = !layer->snapping_enabled;
454 if (layer->selection >= 0) {
455 const char *ids = dynarray_data(layer->ids);
456 Color *colors = dynarray_data(layer->colors);
459 layer->id_edit_field,
460 RECT_LAYER_ID_LABEL_SIZE,
461 color_invert(colors[layer->selection]));
463 layer->state = RECT_LAYER_ID_RENAME;
465 layer->id_edit_field,
466 ids + layer->selection * ENTITY_MAX_ID_SIZE);
467 SDL_StartTextInput();
472 if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
474 dynarray_copy_to(layer->rects, &rect_clipboard_rect, (size_t)layer->selection);
475 dynarray_copy_to(layer->colors, &rect_clipboard_color, (size_t)layer->selection);
480 if ((event->key.keysym.mod & KMOD_LCTRL) && rect_clipboard) {
482 SDL_GetMouseState(&x, &y);
483 Vec2f position = camera_map_screen(camera, x, y);
487 rect(position.x, position.y,
488 rect_clipboard_rect.w, rect_clipboard_rect.h),
489 rect_clipboard_color,
500 static int rect_layer_event_create(RectLayer *layer,
501 const SDL_Event *event,
502 const Camera *camera,
503 UndoHistory *undo_history)
507 trace_assert(camera);
509 switch (event->type) {
510 case SDL_MOUSEBUTTONUP: {
511 switch (event->button.button) {
512 case SDL_BUTTON_LEFT: {
513 const Rect real_rect =
517 const float area = real_rect.w * real_rect.h;
519 if (area >= CREATE_AREA_THRESHOLD) {
523 color_picker_rgba(&layer->color_picker),
526 log_info("The area is too small %f. Such small box won't be created.\n", area);
528 layer->state = RECT_LAYER_IDLE;
533 case SDL_MOUSEMOTION: {
534 layer->create_end = camera_map_screen(
544 void snap_rect_resize_if_enabled(RectLayer *layer, Rect *a, float snapping_threshold)
547 trace_assert(layer->selection >= 0);
550 if (!layer->snapping_enabled) return;
552 Rect *rects = dynarray_data(layer->rects);
553 size_t rects_size = dynarray_count(layer->rects);
555 for (size_t i = 0; i < rects_size; ++i) {
556 if (i == (size_t) layer->selection) continue;
558 const Rect b = rects[i];
560 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
561 snap_var2seg(&a->y, b.y, 0, b.h, snapping_threshold);
564 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
565 snap_var2seg(&a->x, b.x, 0, b.w, snapping_threshold);
570 static int rect_layer_event_resize(RectLayer *layer,
571 const SDL_Event *event,
572 const Camera *camera,
573 UndoHistory *undo_history)
577 trace_assert(camera);
578 trace_assert(layer->selection >= 0);
580 Rect *rects = dynarray_data(layer->rects);
582 float scaled_snap_threshold = SNAPPING_THRESHOLD / camera->scale;
584 switch (event->type) {
585 case SDL_MOUSEMOTION: {
586 Vec2f position = camera_map_screen(
591 switch (layer->resize_mask) {
593 Rect a = rect(rects[layer->selection].x,
595 rects[layer->selection].w,
596 rects[layer->selection].h);
598 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
600 layer->inter_rect = rect_from_points(
602 rect_position2(rects[layer->selection]));
606 Rect a = rect(position.x,
607 rects[layer->selection].y,
608 rects[layer->selection].w,
609 rects[layer->selection].h);
611 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
613 layer->inter_rect = rect_from_points(
615 rect_position2(rects[layer->selection]));
618 case 3: { // TOP,LEFT
622 rects[layer->selection].w,
623 rects[layer->selection].h);
625 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
627 layer->inter_rect = rect_from_points(
629 rect_position2(rects[layer->selection]));
633 Rect a = rect(rects[layer->selection].x,
635 rects[layer->selection].w,
636 rects[layer->selection].h);
638 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
640 layer->inter_rect = rect_from_points(
641 rect_position(rects[layer->selection]),
642 vec(rects[layer->selection].x + rects[layer->selection].w,
646 case 6: { // BOTTOM,LEFT
650 rects[layer->selection].w,
651 -rects[layer->selection].h);
653 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
655 layer->inter_rect = rect_from_points(
656 vec(a.x, rects[layer->selection].y),
657 vec(rects[layer->selection].x + rects[layer->selection].w,
662 Rect a = rect(position.x,
663 rects[layer->selection].y,
664 rects[layer->selection].w,
665 rects[layer->selection].h);
667 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
669 layer->inter_rect = rect_from_points(
670 rect_position(rects[layer->selection]),
671 vec(a.x, rects[layer->selection].y + rects[layer->selection].h));
674 case 9: { // TOP,RIGHT
678 -rects[layer->selection].w,
679 rects[layer->selection].h);
681 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
683 layer->inter_rect = rect_from_points(
684 vec(rects[layer->selection].x, a.y),
686 rects[layer->selection].y + rects[layer->selection].h));
689 case 12: { // BOTTOM,RIGHT
693 -rects[layer->selection].w,
694 -rects[layer->selection].h);
696 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
698 layer->inter_rect = rect_from_points(
699 rect_position(rects[layer->selection]),
706 case SDL_MOUSEBUTTONUP: {
707 layer->state = RECT_LAYER_IDLE;
708 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
709 dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
717 void snap_rect_move_if_enabled(RectLayer *layer, Rect *a,
718 float snapping_threshold)
722 trace_assert(layer->selection >= 0);
724 if (!layer->snapping_enabled) return;
726 Rect *rects = dynarray_data(layer->rects);
727 size_t rects_size = dynarray_count(layer->rects);
729 for (size_t i = 0; i < rects_size; ++i) {
730 if (i == (size_t) layer->selection) continue;
732 const Rect b = rects[i];
734 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
735 snap_seg2seg(&a->y, b.y, a->h, b.h, snapping_threshold);
738 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
739 snap_seg2seg(&a->x, b.x, a->w, b.w, snapping_threshold);
744 static int rect_layer_event_move(RectLayer *layer,
745 const SDL_Event *event,
746 const Camera *camera,
747 UndoHistory *undo_history)
751 trace_assert(camera);
752 trace_assert(layer->selection >= 0);
754 Rect *rects = dynarray_data(layer->rects);
756 switch (event->type) {
757 case SDL_MOUSEMOTION: {
758 const Uint8 *state = SDL_GetKeyboardState(NULL);
759 const Vec2f mouse_pos = vec_sub(
766 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
767 layer->inter_rect.x = mouse_pos.x;
768 layer->inter_rect.y = mouse_pos.y;
770 const Vec2f rect_pos = rect_position(rects[layer->selection]);
772 const float dx = fabsf(rect_pos.x - mouse_pos.x);
773 const float dy = fabsf(rect_pos.y - mouse_pos.y);
776 layer->inter_rect.x = mouse_pos.x;
777 layer->inter_rect.y = rect_pos.y;
779 layer->inter_rect.x = rect_pos.x;
780 layer->inter_rect.y = mouse_pos.y;
784 snap_rect_move_if_enabled(layer, &layer->inter_rect,
785 SNAPPING_THRESHOLD / camera->scale);
788 case SDL_MOUSEBUTTONUP: {
789 layer->state = RECT_LAYER_IDLE;
791 float distance = vec_length(
792 vec_sub(rect_position(layer->inter_rect),
793 rect_position(rects[layer->selection])));
795 if (distance > 1e-6) {
796 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
797 dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
804 static int rect_layer_event_id_rename(RectLayer *layer,
805 const SDL_Event *event,
806 const Camera *camera,
807 UndoHistory *undo_history)
811 trace_assert(camera);
812 trace_assert(layer->selection >= 0);
814 switch (event->type) {
816 switch (event->key.keysym.sym) {
818 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
820 char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
821 memset(id, 0, ENTITY_MAX_ID_SIZE);
822 memcpy(id, edit_field_as_text(layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
823 layer->state = RECT_LAYER_IDLE;
828 layer->state = RECT_LAYER_IDLE;
835 return edit_field_event(layer->id_edit_field, event);
838 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
847 RectLayer *create_rect_layer(const char *id_name_prefix, Cursor *cursor)
849 trace_assert(cursor);
851 Lt *lt = create_lt();
853 RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
859 layer->ids = PUSH_LT(
861 create_dynarray(sizeof(char) * ENTITY_MAX_ID_SIZE),
863 if (layer->ids == NULL) {
867 layer->rects = PUSH_LT(
869 create_dynarray(sizeof(Rect)),
871 if (layer->rects == NULL) {
875 layer->colors = PUSH_LT(
877 create_dynarray(sizeof(Color)),
879 if (layer->colors == NULL) {
883 layer->actions = PUSH_LT(
885 create_dynarray(sizeof(Action)),
887 if (layer->actions == NULL) {
891 layer->id_edit_field = PUSH_LT(
894 RECT_LAYER_ID_LABEL_SIZE,
897 if (layer->id_edit_field == NULL) {
906 sizeof(Grid) + sizeof(Widget*) * RECT_LAYER_GRID_ROWS * RECT_LAYER_GRID_COLUMNS),
908 if (layer->grid == NULL) {
911 layer->grid->rows = RECT_LAYER_GRID_ROWS;
912 layer->grid->columns = RECT_LAYER_GRID_COLUMNS;
913 grid_put_widget(layer->grid, &layer->action_picker.widget, 0, RECT_LAYER_GRID_COLUMNS - 1);
915 layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
916 layer->selection = -1;
917 layer->id_name_prefix = id_name_prefix;
918 layer->cursor = cursor;
923 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream,
924 const char *id_name_prefix,
927 trace_assert(line_stream);
929 RectLayer *layer = create_rect_layer(id_name_prefix, cursor);
934 const char *line = line_stream_next(line_stream);
936 RETURN_LT(layer->lt, NULL);
940 if (sscanf(line, "%zu", &count) < 0) {
941 RETURN_LT(layer->lt, NULL);
944 for (size_t i = 0; i < count; ++i) {
945 line = line_stream_next(line_stream);
947 RETURN_LT(layer->lt, NULL);
952 char id[ENTITY_MAX_ID_SIZE];
956 "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s%f%f%f%f%6s%n",
961 log_fail("%s\n", strerror(errno));
962 RETURN_LT(layer->lt, NULL);
966 Color color = hexstr(hex);
967 dynarray_push(layer->rects, &rect);
968 dynarray_push(layer->ids, id);
969 dynarray_push(layer->colors, &color);
976 if (sscanf(line, "%d%n", (int*)&action.type, &n) > 0) {
978 switch (action.type) {
979 case ACTION_NONE: break;
981 case ACTION_TOGGLE_GOAL:
982 case ACTION_HIDE_LABEL: {
983 if (sscanf(line, "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s", action.entity_id) <= 0) {
984 log_fail("%s\n", strerror(errno));
985 RETURN_LT(layer->lt, NULL);
989 case ACTION_N: break;
993 dynarray_push(layer->actions, &action);
999 void destroy_rect_layer(RectLayer *layer)
1001 trace_assert(layer);
1002 RETURN_LT0(layer->lt);
1005 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
1007 trace_assert(layer);
1008 trace_assert(camera);
1010 const size_t n = dynarray_count(layer->rects);
1011 Rect *rects = dynarray_data(layer->rects);
1012 Color *colors = dynarray_data(layer->colors);
1013 const char *ids = dynarray_data(layer->ids);
1016 for (size_t i = 0; i < n; ++i) {
1017 Rect rect = rects[i];
1018 Color color = colors[i];
1020 if (layer->selection == (int) i) {
1021 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
1022 rect = layer->inter_rect;
1025 if (layer->state == RECT_LAYER_RECOLOR) {
1026 color = layer->inter_color;
1031 if (camera_fill_rect(
1036 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
1041 // Selection Overlay
1042 if (active && layer->selection >= 0) {
1043 Rect rect = rects[layer->selection];
1044 Color color = colors[layer->selection];
1046 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
1047 rect = layer->inter_rect;
1050 if (layer->state == RECT_LAYER_RECOLOR) {
1051 color = layer->inter_color;
1054 const Rect overlay_rect =
1056 camera_rect(camera, rect),
1057 -RECT_LAYER_SELECTION_THICCNESS * 0.5f);
1058 const Color overlay_color = color_invert(color);
1061 if (camera_draw_thicc_rect_screen(
1065 RECT_LAYER_SELECTION_THICCNESS) < 0) {
1069 const Vec2f rect_id_pos = vec_sub(
1070 rect_position(rect),
1072 RECT_LAYER_ID_LABEL_SIZE,
1073 vec(0.0f, FONT_CHAR_HEIGHT)));
1076 if (layer->state == RECT_LAYER_ID_RENAME) {
1077 // ID renaming Edit Field
1078 if (edit_field_render_world(
1079 layer->id_edit_field,
1086 if (camera_render_text(
1088 ids + layer->selection * ENTITY_MAX_ID_SIZE,
1089 RECT_LAYER_ID_LABEL_SIZE,
1090 color_invert(color),
1098 const Color color = color_picker_rgba(&layer->color_picker);
1099 if (layer->state == RECT_LAYER_CREATE) {
1100 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
1105 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
1113 int rect_layer_event_recolor(RectLayer *layer,
1114 const SDL_Event *event,
1115 const Camera *camera,
1116 UndoHistory *undo_history)
1118 trace_assert(layer);
1119 trace_assert(event);
1120 trace_assert(camera);
1121 trace_assert(undo_history);
1122 trace_assert(layer->selection >= 0);
1124 int color_changed = 0;
1125 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
1129 if (color_changed) {
1130 layer->inter_color = color_picker_rgba(&layer->color_picker);
1132 if (!color_picker_drag(&layer->color_picker)) {
1133 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
1134 dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color);
1135 layer->state = RECT_LAYER_IDLE;
1142 int rect_layer_event(RectLayer *layer,
1143 const SDL_Event *event,
1144 const Camera *camera,
1145 UndoHistory *undo_history)
1147 trace_assert(layer);
1148 trace_assert(event);
1149 trace_assert(undo_history);
1151 switch (event->type) {
1152 case SDL_WINDOWEVENT: {
1153 switch (event->window.event) {
1154 case SDL_WINDOWEVENT_SIZE_CHANGED: {
1155 grid_relayout(layer->grid, rect(0.0f, 0.0f,
1156 (float) event->window.data1,
1157 (float) event->window.data2));
1163 switch (layer->state) {
1164 case RECT_LAYER_IDLE:
1165 return rect_layer_event_idle(layer, event, camera, undo_history);
1167 case RECT_LAYER_CREATE:
1168 return rect_layer_event_create(layer, event, camera, undo_history);
1170 case RECT_LAYER_RESIZE:
1171 return rect_layer_event_resize(layer, event, camera, undo_history);
1173 case RECT_LAYER_MOVE:
1174 return rect_layer_event_move(layer, event, camera, undo_history);
1176 case RECT_LAYER_ID_RENAME:
1177 return rect_layer_event_id_rename(layer, event, camera, undo_history);
1179 case RECT_LAYER_RECOLOR:
1180 return rect_layer_event_recolor(layer, event, camera, undo_history);
1187 size_t rect_layer_count(const RectLayer *layer)
1189 return dynarray_count(layer->rects);
1192 const Rect *rect_layer_rects(const RectLayer *layer)
1194 return dynarray_data(layer->rects);
1197 const Color *rect_layer_colors(const RectLayer *layer)
1199 return dynarray_data(layer->colors);
1202 const char *rect_layer_ids(const RectLayer *layer)
1204 return dynarray_data(layer->ids);
1207 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
1209 trace_assert(layer);
1210 trace_assert(filedump);
1212 size_t n = dynarray_count(layer->ids);
1213 char *ids = dynarray_data(layer->ids);
1214 Rect *rects = dynarray_data(layer->rects);
1215 Color *colors = dynarray_data(layer->colors);
1216 Action *actions = dynarray_data(layer->actions);
1218 fprintf(filedump, "%zd\n", n);
1219 for (size_t i = 0; i < n; ++i) {
1220 fprintf(filedump, "%s %f %f %f %f ",
1221 ids + ENTITY_MAX_ID_SIZE * i,
1222 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
1223 color_hex_to_stream(colors[i], filedump);
1225 switch (actions[i].type) {
1226 case ACTION_NONE: {} break;
1228 case ACTION_TOGGLE_GOAL:
1229 case ACTION_HIDE_LABEL: {
1230 fprintf(filedump, " %d %.*s",
1231 (int)actions[i].type,
1232 ENTITY_MAX_ID_SIZE, actions[i].entity_id);
1234 case ACTION_N: break;
1237 fprintf(filedump, "\n");
1243 const Action *rect_layer_actions(const RectLayer *layer)
1245 return dynarray_data(layer->actions);