4 #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/str.h"
13 #include "undo_history.h"
14 #include "game/level/action.h"
16 #include "math/extrema.h"
18 #define RECT_LAYER_SELECTION_THICCNESS 15.0f
19 #define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
20 #define CREATE_AREA_THRESHOLD 10.0
22 static int rect_clipboard = 0;
23 static Rect rect_clipboard_rect;
24 static Color rect_clipboard_color;
26 static Cursor_Style resize_styles[1 << RECT_SIDE_N] = {
28 CURSOR_STYLE_RESIZE_VERT, // [1]
29 CURSOR_STYLE_RESIZE_HORIS, // [2]
30 CURSOR_STYLE_RESIZE_DIAG1, // [3]
31 CURSOR_STYLE_RESIZE_VERT, // [4]
33 CURSOR_STYLE_RESIZE_DIAG2, // [6]
35 CURSOR_STYLE_RESIZE_HORIS, // [8]
36 CURSOR_STYLE_RESIZE_DIAG2, // [9]
39 CURSOR_STYLE_RESIZE_DIAG1 // [12]
57 char id[ENTITY_MAX_ID_SIZE];
78 UndoElementContext element;
83 RectUndoContext create_rect_undo_add_context(RectLayer *layer, size_t index)
86 trace_assert(index < layer->rects.count);
88 RectUndoContext undo_context;
89 undo_context.add.type = RECT_UNDO_ADD;
90 undo_context.add.layer = layer;
91 undo_context.add.index = index;
96 RectUndoContext create_rect_undo_element_context(RectLayer *layer)
99 size_t index = (size_t) layer->selection;
100 trace_assert(index < layer->rects.count);
102 RectUndoContext undo_context;
103 undo_context.element.layer = layer;
104 undo_context.element.index = index;
105 dynarray_copy_to(&layer->rects, &undo_context.element.rect, index);
106 dynarray_copy_to(&layer->colors, &undo_context.element.color, index);
107 dynarray_copy_to(&layer->ids, undo_context.element.id, index);
108 dynarray_copy_to(&layer->actions, &undo_context.element.action, index);
113 RectUndoContext create_rect_undo_update_context(RectLayer *rect_layer)
115 RectUndoContext undo_context = create_rect_undo_element_context(rect_layer);
116 undo_context.type = RECT_UNDO_UPDATE;
121 RectUndoContext create_rect_undo_delete_context(RectLayer *rect_layer)
123 RectUndoContext undo_context = create_rect_undo_element_context(rect_layer);
124 undo_context.type = RECT_UNDO_DELETE;
129 RectUndoContext create_rect_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2)
131 RectUndoContext undo_context;
132 undo_context.swap.type = RECT_UNDO_SWAP;
133 undo_context.swap.layer = rect_layer;
134 undo_context.swap.index1 = index1;
135 undo_context.swap.index2 = index2;
140 void rect_layer_undo(void *context, size_t context_size)
142 trace_assert(context);
143 trace_assert(sizeof(RectUndoContext) == context_size);
145 RectUndoContext *undo_context = context;
147 switch (undo_context->type) {
148 case RECT_UNDO_ADD: {
149 RectLayer *layer = undo_context->add.layer;
150 dynarray_delete_at(&layer->rects, undo_context->add.index);
151 dynarray_delete_at(&layer->colors, undo_context->add.index);
152 dynarray_delete_at(&layer->ids, undo_context->add.index);
153 dynarray_delete_at(&layer->actions, undo_context->add.index);
154 layer->selection = -1;
157 case RECT_UNDO_DELETE: {
158 RectLayer *layer = undo_context->element.layer;
159 dynarray_insert_before(&layer->rects, undo_context->element.index, &undo_context->element.rect);
160 dynarray_insert_before(&layer->colors, undo_context->element.index, &undo_context->element.color);
161 dynarray_insert_before(&layer->ids, undo_context->element.index, &undo_context->element.id);
162 dynarray_insert_before(&layer->actions, undo_context->element.index, &undo_context->element.action);
163 layer->selection = -1;
166 case RECT_UNDO_UPDATE: {
167 RectLayer *layer = undo_context->element.layer;
168 dynarray_replace_at(&layer->rects, undo_context->element.index, &undo_context->element.rect);
169 dynarray_replace_at(&layer->colors, undo_context->element.index, &undo_context->element.color);
170 dynarray_replace_at(&layer->ids, undo_context->element.index, &undo_context->element.id);
171 dynarray_replace_at(&layer->actions, undo_context->element.index, &undo_context->element.action);
174 case RECT_UNDO_SWAP: {
175 RectLayer *layer = undo_context->element.layer;
176 dynarray_swap(&layer->rects, undo_context->swap.index1, undo_context->swap.index2);
177 dynarray_swap(&layer->colors, undo_context->swap.index1, undo_context->swap.index2);
178 dynarray_swap(&layer->ids, undo_context->swap.index1, undo_context->swap.index2);
179 dynarray_swap(&layer->actions, undo_context->swap.index1, undo_context->swap.index2);
184 #define RECT_UNDO_PUSH(HISTORY, CONTEXT) \
186 RectUndoContext context = (CONTEXT); \
194 static int rect_layer_add_rect(RectLayer *layer,
197 UndoHistory *undo_history)
201 dynarray_push(&layer->rects, &rect);
202 dynarray_push(&layer->colors, &color);
204 char id[ENTITY_MAX_ID_SIZE];
205 snprintf(id, ENTITY_MAX_ID_SIZE, "%s_%d",
206 layer->id_name_prefix,
207 layer->id_name_counter++);
208 dynarray_push(&layer->ids, id);
210 dynarray_push_empty(&layer->actions);
214 create_rect_undo_add_context(
216 layer->rects.count - 1));
221 static int rect_layer_rect_at(RectLayer *layer, Vec2f position)
225 int n = (int) layer->rects.count;
226 Rect *rects = (Rect*)layer->rects.data;
228 for (int i = n - 1; i >= 0; --i) {
229 if (rect_contains_point(rects[i], position)) {
237 static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b,
238 UndoHistory *undo_history)
241 trace_assert(a < layer->rects.count);
242 trace_assert(b < layer->rects.count);
244 dynarray_swap(&layer->rects, a, b);
245 dynarray_swap(&layer->colors, a, b);
246 dynarray_swap(&layer->ids, a, b);
247 dynarray_swap(&layer->actions, a, b);
249 RECT_UNDO_PUSH(undo_history, create_rect_undo_swap_context(layer, a, b));
252 static int rect_layer_delete_rect_at(RectLayer *layer,
254 UndoHistory *undo_history)
258 RECT_UNDO_PUSH(undo_history, create_rect_undo_delete_context(layer));
260 dynarray_delete_at(&layer->rects, i);
261 dynarray_delete_at(&layer->colors, i);
262 dynarray_delete_at(&layer->ids, i);
263 dynarray_delete_at(&layer->actions, i);
268 static int calc_resize_mask(Vec2f point, Rect rect)
271 for (Rect_side side = 0; side < RECT_SIDE_N; ++side) {
272 if (rect_side_distance(rect, point, side) < RECT_LAYER_SELECTION_THICCNESS) {
273 mask = mask | (1 << side);
279 static int rect_layer_event_idle(RectLayer *layer,
280 const SDL_Event *event,
281 const Camera *camera,
282 UndoHistory *undo_history)
286 trace_assert(camera);
288 int color_changed = 0;
289 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
294 if (layer->selection >= 0) {
295 dynarray_copy_to(&layer->colors, &layer->inter_color, (size_t)layer->selection);
296 layer->state = RECT_LAYER_RECOLOR;
301 Rect *rects = (Rect*)layer->rects.data;
303 switch (event->type) {
304 case SDL_MOUSEBUTTONDOWN: {
305 switch (event->button.button) {
306 case SDL_BUTTON_LEFT: {
307 Vec2f position = camera_map_screen(
311 int rect_at_position =
312 rect_layer_rect_at(layer, position);
315 Color *colors = (Color*)layer->colors.data;
317 if (layer->selection >= 0 &&
318 layer->selection == rect_at_position &&
319 (layer->resize_mask = calc_resize_mask(
320 vec((float) event->button.x, (float)event->button.y),
321 camera_rect(camera, rects[layer->selection])))) {
322 layer->state = RECT_LAYER_RESIZE;
323 dynarray_copy_to(&layer->rects, &layer->inter_rect, (size_t) layer->selection);
324 } else if (rect_at_position >= 0) {
325 layer->selection = rect_at_position;
326 layer->state = RECT_LAYER_MOVE;
327 layer->move_anchor = vec_sub(
330 rects[layer->selection].x,
331 rects[layer->selection].y));
332 layer->color_picker =
333 create_color_picker_from_rgba(colors[rect_at_position]);
334 dynarray_copy_to(&layer->rects, &layer->inter_rect, (size_t) rect_at_position);
336 layer->selection = rect_at_position;
338 if (layer->selection < 0) {
339 layer->state = RECT_LAYER_CREATE;
340 layer->create_begin = position;
341 layer->create_end = position;
348 case SDL_MOUSEMOTION: {
350 Vec2f position = camera_map_screen(
354 if (layer->selection >= 0 &&
355 layer->selection == rect_layer_rect_at(layer, position) &&
356 (resize_mask = calc_resize_mask(
357 vec((float) event->button.x, (float)event->button.y),
358 camera_rect(camera, rects[layer->selection])))) {
359 layer->cursor->style = resize_styles[resize_mask];
361 layer->cursor->style = CURSOR_STYLE_POINTER;
366 switch (event->key.keysym.sym) {
368 if ((event->key.keysym.mod & KMOD_SHIFT)
369 && (layer->selection >= 0)
370 && ((size_t)(layer->selection + 1) < layer->rects.count)) {
371 rect_layer_swap_elements(
373 (size_t) layer->selection,
374 (size_t) layer->selection + 1,
381 if ((event->key.keysym.mod & KMOD_SHIFT)
382 && (layer->selection > 0)
383 && ((size_t) layer->selection < layer->rects.count)) {
384 rect_layer_swap_elements(
386 (size_t) layer->selection,
387 (size_t) layer->selection - 1,
394 if (layer->selection >= 0) {
395 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
396 layer->selection = -1;
401 // TODO(#1171): there is no UI indication that we are in the snapping mode
402 layer->snapping_enabled = !layer->snapping_enabled;
406 if (layer->selection >= 0) {
407 const char *ids = (char*)layer->ids.data;
408 Color *colors = (Color*)layer->colors.data;
411 &layer->id_edit_field,
412 RECT_LAYER_ID_LABEL_SIZE,
413 color_invert(colors[layer->selection]));
415 layer->state = RECT_LAYER_ID_RENAME;
417 &layer->id_edit_field,
418 ids + layer->selection * ENTITY_MAX_ID_SIZE);
419 SDL_StartTextInput();
424 if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
426 dynarray_copy_to(&layer->rects, &rect_clipboard_rect, (size_t)layer->selection);
427 dynarray_copy_to(&layer->colors, &rect_clipboard_color, (size_t)layer->selection);
432 if ((event->key.keysym.mod & KMOD_LCTRL) && rect_clipboard) {
434 SDL_GetMouseState(&x, &y);
435 Vec2f position = camera_map_screen(camera, x, y);
439 rect(position.x, position.y,
440 rect_clipboard_rect.w, rect_clipboard_rect.h),
441 rect_clipboard_color,
452 static int rect_layer_event_create(RectLayer *layer,
453 const SDL_Event *event,
454 const Camera *camera,
455 UndoHistory *undo_history)
459 trace_assert(camera);
461 switch (event->type) {
462 case SDL_MOUSEBUTTONUP: {
463 switch (event->button.button) {
464 case SDL_BUTTON_LEFT: {
465 const Rect real_rect =
469 const float area = real_rect.w * real_rect.h;
471 if (area >= CREATE_AREA_THRESHOLD) {
475 color_picker_rgba(&layer->color_picker),
478 log_info("The area is too small %f. Such small box won't be created.\n", area);
480 layer->state = RECT_LAYER_IDLE;
485 case SDL_MOUSEMOTION: {
486 layer->create_end = camera_map_screen(
496 void snap_rect_resize_if_enabled(RectLayer *layer, Rect *a, float snapping_threshold)
499 trace_assert(layer->selection >= 0);
502 if (!layer->snapping_enabled) return;
504 Rect *rects = (Rect*)layer->rects.data;
505 size_t rects_size = layer->rects.count;
507 for (size_t i = 0; i < rects_size; ++i) {
508 if (i == (size_t) layer->selection) continue;
510 const Rect b = rects[i];
512 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
513 snap_var2seg(&a->y, b.y, 0, b.h, snapping_threshold);
516 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
517 snap_var2seg(&a->x, b.x, 0, b.w, snapping_threshold);
522 static int rect_layer_event_resize(RectLayer *layer,
523 const SDL_Event *event,
524 const Camera *camera,
525 UndoHistory *undo_history)
529 trace_assert(camera);
530 trace_assert(layer->selection >= 0);
532 Rect *rects = (Rect*)layer->rects.data;
534 float scaled_snap_threshold = SNAPPING_THRESHOLD / camera->scale;
536 switch (event->type) {
537 case SDL_MOUSEMOTION: {
538 Vec2f position = camera_map_screen(
543 switch (layer->resize_mask) {
545 Rect a = rect(rects[layer->selection].x,
547 rects[layer->selection].w,
548 rects[layer->selection].h);
550 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
552 layer->inter_rect = rect_from_points(
554 rect_position2(rects[layer->selection]));
558 Rect a = rect(position.x,
559 rects[layer->selection].y,
560 rects[layer->selection].w,
561 rects[layer->selection].h);
563 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
565 layer->inter_rect = rect_from_points(
567 rect_position2(rects[layer->selection]));
570 case 3: { // TOP,LEFT
574 rects[layer->selection].w,
575 rects[layer->selection].h);
577 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
579 layer->inter_rect = rect_from_points(
581 rect_position2(rects[layer->selection]));
585 Rect a = rect(rects[layer->selection].x,
587 rects[layer->selection].w,
588 rects[layer->selection].h);
590 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
592 layer->inter_rect = rect_from_points(
593 rect_position(rects[layer->selection]),
594 vec(rects[layer->selection].x + rects[layer->selection].w,
598 case 6: { // BOTTOM,LEFT
602 rects[layer->selection].w,
603 -rects[layer->selection].h);
605 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
607 layer->inter_rect = rect_from_points(
608 vec(a.x, rects[layer->selection].y),
609 vec(rects[layer->selection].x + rects[layer->selection].w,
614 Rect a = rect(position.x,
615 rects[layer->selection].y,
616 rects[layer->selection].w,
617 rects[layer->selection].h);
619 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
621 layer->inter_rect = rect_from_points(
622 rect_position(rects[layer->selection]),
623 vec(a.x, rects[layer->selection].y + rects[layer->selection].h));
626 case 9: { // TOP,RIGHT
630 -rects[layer->selection].w,
631 rects[layer->selection].h);
633 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
635 layer->inter_rect = rect_from_points(
636 vec(rects[layer->selection].x, a.y),
638 rects[layer->selection].y + rects[layer->selection].h));
641 case 12: { // BOTTOM,RIGHT
645 -rects[layer->selection].w,
646 -rects[layer->selection].h);
648 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
650 layer->inter_rect = rect_from_points(
651 rect_position(rects[layer->selection]),
658 case SDL_MOUSEBUTTONUP: {
659 layer->state = RECT_LAYER_IDLE;
660 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
661 dynarray_replace_at(&layer->rects, (size_t) layer->selection, &layer->inter_rect);
669 void snap_rect_move_if_enabled(RectLayer *layer, Rect *a,
670 float snapping_threshold)
674 trace_assert(layer->selection >= 0);
676 if (!layer->snapping_enabled) return;
678 Rect *rects = (Rect*)layer->rects.data;
679 size_t rects_size = layer->rects.count;
681 for (size_t i = 0; i < rects_size; ++i) {
682 if (i == (size_t) layer->selection) continue;
684 const Rect b = rects[i];
686 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
687 snap_seg2seg(&a->y, b.y, a->h, b.h, snapping_threshold);
690 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
691 snap_seg2seg(&a->x, b.x, a->w, b.w, snapping_threshold);
696 static int rect_layer_event_move(RectLayer *layer,
697 const SDL_Event *event,
698 const Camera *camera,
699 UndoHistory *undo_history)
703 trace_assert(camera);
704 trace_assert(layer->selection >= 0);
706 Rect *rects = (Rect*)layer->rects.data;
708 switch (event->type) {
709 case SDL_MOUSEMOTION: {
710 const Uint8 *state = SDL_GetKeyboardState(NULL);
711 const Vec2f mouse_pos = vec_sub(
718 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
719 layer->inter_rect.x = mouse_pos.x;
720 layer->inter_rect.y = mouse_pos.y;
722 const Vec2f rect_pos = rect_position(rects[layer->selection]);
724 const float dx = fabsf(rect_pos.x - mouse_pos.x);
725 const float dy = fabsf(rect_pos.y - mouse_pos.y);
728 layer->inter_rect.x = mouse_pos.x;
729 layer->inter_rect.y = rect_pos.y;
731 layer->inter_rect.x = rect_pos.x;
732 layer->inter_rect.y = mouse_pos.y;
736 snap_rect_move_if_enabled(layer, &layer->inter_rect,
737 SNAPPING_THRESHOLD / camera->scale);
740 case SDL_MOUSEBUTTONUP: {
741 layer->state = RECT_LAYER_IDLE;
743 float distance = vec_length(
744 vec_sub(rect_position(layer->inter_rect),
745 rect_position(rects[layer->selection])));
747 if (distance > 1e-6) {
748 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
749 dynarray_replace_at(&layer->rects, (size_t) layer->selection, &layer->inter_rect);
756 static int rect_layer_event_id_rename(RectLayer *layer,
757 const SDL_Event *event,
758 const Camera *camera,
759 UndoHistory *undo_history)
763 trace_assert(camera);
764 trace_assert(layer->selection >= 0);
766 switch (event->type) {
768 switch (event->key.keysym.sym) {
770 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
772 char *id = dynarray_pointer_at(&layer->ids, (size_t)layer->selection);
773 memset(id, 0, ENTITY_MAX_ID_SIZE);
774 memcpy(id, edit_field_as_text(&layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
775 layer->state = RECT_LAYER_IDLE;
780 layer->state = RECT_LAYER_IDLE;
787 return edit_field_event(&layer->id_edit_field, event);
790 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
799 RectLayer create_rect_layer(const char *id_name_prefix, Cursor *cursor)
801 trace_assert(cursor);
803 RectLayer result = {0};
805 result.ids = create_dynarray(sizeof(char) * ENTITY_MAX_ID_SIZE);
806 result.rects = create_dynarray(sizeof(Rect));
807 result.colors = create_dynarray(sizeof(Color));
808 result.actions = create_dynarray(sizeof(Action));
809 result.id_edit_field.font_size = RECT_LAYER_ID_LABEL_SIZE;
810 result.id_edit_field.font_color = COLOR_BLACK;
811 result.color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
812 result.selection = -1;
813 result.id_name_prefix = id_name_prefix;
814 result.cursor = cursor;
819 void rect_layer_reload(RectLayer *layer, Memory *memory, String *input)
822 trace_assert(memory);
825 rect_layer_clean(layer);
827 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
828 char id[ENTITY_MAX_ID_SIZE];
829 for (int i = 0; i < n; ++i) {
831 String line = trim(chop_by_delim(input, '\n'));
832 String string_id = trim(chop_word(&line));
833 rect.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
834 rect.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
835 rect.w = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
836 rect.h = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
837 Color color = hexs(trim(chop_word(&line)));
839 memset(id, 0, ENTITY_MAX_ID_SIZE);
843 min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
845 dynarray_push(&layer->rects, &rect);
846 dynarray_push(&layer->colors, &color);
847 dynarray_push(&layer->ids, id);
854 String action_string = trim(chop_word(&line));
855 if (action_string.count > 0) {
856 action.type = (ActionType)atol(string_to_cstr(memory, action_string));
857 switch (action.type) {
858 case ACTION_NONE: break;
859 case ACTION_TOGGLE_GOAL:
860 case ACTION_HIDE_LABEL: {
861 String label_id = trim(chop_word(&line));
862 trace_assert(label_id.count > 0);
863 memset(action.entity_id, 0, ENTITY_MAX_ID_SIZE);
864 memcpy(action.entity_id,
867 ENTITY_MAX_ID_SIZE - 1,
871 case ACTION_N: break;
875 dynarray_push(&layer->actions, &action);
879 void rect_layer_clean(RectLayer *rect_layer)
881 trace_assert(rect_layer);
882 dynarray_clear(&rect_layer->ids);
883 dynarray_clear(&rect_layer->rects);
884 dynarray_clear(&rect_layer->colors);
885 dynarray_clear(&rect_layer->actions);
888 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
891 trace_assert(camera);
893 const size_t n = layer->rects.count;
894 Rect *rects = (Rect *)layer->rects.data;
895 Color *colors = (Color *)layer->colors.data;
896 const char *ids = (const char *)layer->ids.data;
899 for (size_t i = 0; i < n; ++i) {
900 Rect rect = rects[i];
901 Color color = colors[i];
903 if (layer->selection == (int) i) {
904 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
905 rect = layer->inter_rect;
908 if (layer->state == RECT_LAYER_RECOLOR) {
909 color = layer->inter_color;
914 if (camera_fill_rect(
919 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
925 if (active && layer->selection >= 0) {
926 Rect rect = rects[layer->selection];
927 Color color = colors[layer->selection];
929 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
930 rect = layer->inter_rect;
933 if (layer->state == RECT_LAYER_RECOLOR) {
934 color = layer->inter_color;
937 const Rect overlay_rect =
939 camera_rect(camera, rect),
940 -RECT_LAYER_SELECTION_THICCNESS * 0.5f);
941 const Color overlay_color = color_invert(color);
944 if (camera_draw_thicc_rect_screen(
948 RECT_LAYER_SELECTION_THICCNESS) < 0) {
952 const Vec2f rect_id_pos = vec_sub(
955 RECT_LAYER_ID_LABEL_SIZE,
956 vec(0.0f, FONT_CHAR_HEIGHT)));
959 if (layer->state == RECT_LAYER_ID_RENAME) {
960 // ID renaming Edit Field
961 if (edit_field_render_world(
962 &layer->id_edit_field,
969 if (camera_render_text(
971 ids + layer->selection * ENTITY_MAX_ID_SIZE,
972 RECT_LAYER_ID_LABEL_SIZE,
981 const Color color = color_picker_rgba(&layer->color_picker);
982 if (layer->state == RECT_LAYER_CREATE) {
983 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
988 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
996 int rect_layer_event_recolor(RectLayer *layer,
997 const SDL_Event *event,
998 const Camera *camera,
999 UndoHistory *undo_history)
1001 trace_assert(layer);
1002 trace_assert(event);
1003 trace_assert(camera);
1004 trace_assert(undo_history);
1005 trace_assert(layer->selection >= 0);
1007 int color_changed = 0;
1008 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
1012 if (color_changed) {
1013 layer->inter_color = color_picker_rgba(&layer->color_picker);
1015 if (!color_picker_drag(&layer->color_picker)) {
1016 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
1017 dynarray_replace_at(&layer->colors, (size_t) layer->selection, &layer->inter_color);
1018 layer->state = RECT_LAYER_IDLE;
1025 int rect_layer_event(RectLayer *layer,
1026 const SDL_Event *event,
1027 const Camera *camera,
1028 UndoHistory *undo_history)
1030 trace_assert(layer);
1031 trace_assert(event);
1032 trace_assert(undo_history);
1034 switch (layer->state) {
1035 case RECT_LAYER_IDLE:
1036 return rect_layer_event_idle(layer, event, camera, undo_history);
1038 case RECT_LAYER_CREATE:
1039 return rect_layer_event_create(layer, event, camera, undo_history);
1041 case RECT_LAYER_RESIZE:
1042 return rect_layer_event_resize(layer, event, camera, undo_history);
1044 case RECT_LAYER_MOVE:
1045 return rect_layer_event_move(layer, event, camera, undo_history);
1047 case RECT_LAYER_ID_RENAME:
1048 return rect_layer_event_id_rename(layer, event, camera, undo_history);
1050 case RECT_LAYER_RECOLOR:
1051 return rect_layer_event_recolor(layer, event, camera, undo_history);
1058 size_t rect_layer_count(const RectLayer *layer)
1060 return layer->rects.count;
1063 const Rect *rect_layer_rects(const RectLayer *layer)
1065 return (const Rect *)layer->rects.data;
1068 const Color *rect_layer_colors(const RectLayer *layer)
1070 return (const Color *)layer->colors.data;
1073 const char *rect_layer_ids(const RectLayer *layer)
1075 return (const char *)layer->ids.data;
1078 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
1080 trace_assert(layer);
1081 trace_assert(filedump);
1083 size_t n = layer->ids.count;
1084 char *ids = (char *)layer->ids.data;
1085 Rect *rects = (Rect *)layer->rects.data;
1086 Color *colors = (Color *)layer->colors.data;
1087 Action *actions = (Action *)layer->actions.data;
1089 fprintf(filedump, "%zd\n", n);
1090 for (size_t i = 0; i < n; ++i) {
1091 fprintf(filedump, "%s %f %f %f %f ",
1092 ids + ENTITY_MAX_ID_SIZE * i,
1093 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
1094 color_hex_to_stream(colors[i], filedump);
1096 switch (actions[i].type) {
1097 case ACTION_NONE: {} break;
1099 case ACTION_TOGGLE_GOAL:
1100 case ACTION_HIDE_LABEL: {
1101 fprintf(filedump, " %d %.*s",
1102 (int)actions[i].type,
1103 ENTITY_MAX_ID_SIZE, actions[i].entity_id);
1105 case ACTION_N: break;
1108 fprintf(filedump, "\n");
1114 const Action *rect_layer_actions(const RectLayer *layer)
1116 return (const Action *)layer->actions.data;