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 "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"
19 #include "math/extrema.h"
21 #define RECT_LAYER_SELECTION_THICCNESS 15.0f
22 #define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
23 #define CREATE_AREA_THRESHOLD 10.0
24 #define RECT_LAYER_GRID_ROWS 3
25 #define RECT_LAYER_GRID_COLUMNS 4
27 static int rect_clipboard = 0;
28 static Rect rect_clipboard_rect;
29 static Color rect_clipboard_color;
31 static Cursor_Style resize_styles[1 << RECT_SIDE_N] = {
33 CURSOR_STYLE_RESIZE_VERT, // [1]
34 CURSOR_STYLE_RESIZE_HORIS, // [2]
35 CURSOR_STYLE_RESIZE_DIAG1, // [3]
36 CURSOR_STYLE_RESIZE_VERT, // [4]
38 CURSOR_STYLE_RESIZE_DIAG2, // [6]
40 CURSOR_STYLE_RESIZE_HORIS, // [8]
41 CURSOR_STYLE_RESIZE_DIAG2, // [9]
44 CURSOR_STYLE_RESIZE_DIAG1 // [12]
62 char id[ENTITY_MAX_ID_SIZE];
83 UndoElementContext element;
88 RectUndoContext create_rect_undo_add_context(RectLayer *layer, size_t index)
91 trace_assert(index < layer->rects.count);
93 RectUndoContext undo_context;
94 undo_context.add.type = RECT_UNDO_ADD;
95 undo_context.add.layer = layer;
96 undo_context.add.index = index;
101 RectUndoContext create_rect_undo_element_context(RectLayer *layer)
104 size_t index = (size_t) layer->selection;
105 trace_assert(index < layer->rects.count);
107 RectUndoContext undo_context;
108 undo_context.element.layer = layer;
109 undo_context.element.index = index;
110 dynarray_copy_to(&layer->rects, &undo_context.element.rect, index);
111 dynarray_copy_to(&layer->colors, &undo_context.element.color, index);
112 dynarray_copy_to(&layer->ids, undo_context.element.id, index);
113 dynarray_copy_to(&layer->actions, &undo_context.element.action, index);
118 RectUndoContext create_rect_undo_update_context(RectLayer *rect_layer)
120 RectUndoContext undo_context = create_rect_undo_element_context(rect_layer);
121 undo_context.type = RECT_UNDO_UPDATE;
126 RectUndoContext create_rect_undo_delete_context(RectLayer *rect_layer)
128 RectUndoContext undo_context = create_rect_undo_element_context(rect_layer);
129 undo_context.type = RECT_UNDO_DELETE;
134 RectUndoContext create_rect_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2)
136 RectUndoContext undo_context;
137 undo_context.swap.type = RECT_UNDO_SWAP;
138 undo_context.swap.layer = rect_layer;
139 undo_context.swap.index1 = index1;
140 undo_context.swap.index2 = index2;
145 void rect_layer_undo(void *context, size_t context_size)
147 trace_assert(context);
148 trace_assert(sizeof(RectUndoContext) == context_size);
150 RectUndoContext *undo_context = context;
152 switch (undo_context->type) {
153 case RECT_UNDO_ADD: {
154 RectLayer *layer = undo_context->add.layer;
155 dynarray_delete_at(&layer->rects, undo_context->add.index);
156 dynarray_delete_at(&layer->colors, undo_context->add.index);
157 dynarray_delete_at(&layer->ids, undo_context->add.index);
158 dynarray_delete_at(&layer->actions, undo_context->add.index);
159 layer->selection = -1;
162 case RECT_UNDO_DELETE: {
163 RectLayer *layer = undo_context->element.layer;
164 dynarray_insert_before(&layer->rects, undo_context->element.index, &undo_context->element.rect);
165 dynarray_insert_before(&layer->colors, undo_context->element.index, &undo_context->element.color);
166 dynarray_insert_before(&layer->ids, undo_context->element.index, &undo_context->element.id);
167 dynarray_insert_before(&layer->actions, undo_context->element.index, &undo_context->element.action);
168 layer->selection = -1;
171 case RECT_UNDO_UPDATE: {
172 RectLayer *layer = undo_context->element.layer;
173 dynarray_replace_at(&layer->rects, undo_context->element.index, &undo_context->element.rect);
174 dynarray_replace_at(&layer->colors, undo_context->element.index, &undo_context->element.color);
175 dynarray_replace_at(&layer->ids, undo_context->element.index, &undo_context->element.id);
176 dynarray_replace_at(&layer->actions, undo_context->element.index, &undo_context->element.action);
179 case RECT_UNDO_SWAP: {
180 RectLayer *layer = undo_context->element.layer;
181 dynarray_swap(&layer->rects, undo_context->swap.index1, undo_context->swap.index2);
182 dynarray_swap(&layer->colors, undo_context->swap.index1, undo_context->swap.index2);
183 dynarray_swap(&layer->ids, undo_context->swap.index1, undo_context->swap.index2);
184 dynarray_swap(&layer->actions, undo_context->swap.index1, undo_context->swap.index2);
189 #define RECT_UNDO_PUSH(HISTORY, CONTEXT) \
191 RectUndoContext context = (CONTEXT); \
199 static int rect_layer_add_rect(RectLayer *layer,
202 UndoHistory *undo_history)
206 dynarray_push(&layer->rects, &rect);
207 dynarray_push(&layer->colors, &color);
209 char id[ENTITY_MAX_ID_SIZE];
210 snprintf(id, ENTITY_MAX_ID_SIZE, "%s_%d",
211 layer->id_name_prefix,
212 layer->id_name_counter++);
213 dynarray_push(&layer->ids, id);
215 dynarray_push_empty(&layer->actions);
219 create_rect_undo_add_context(
221 layer->rects.count - 1));
226 static int rect_layer_rect_at(RectLayer *layer, Vec2f position)
230 int n = (int) layer->rects.count;
231 Rect *rects = (Rect*)layer->rects.data;
233 for (int i = n - 1; i >= 0; --i) {
234 if (rect_contains_point(rects[i], position)) {
242 static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b,
243 UndoHistory *undo_history)
246 trace_assert(a < layer->rects.count);
247 trace_assert(b < layer->rects.count);
249 dynarray_swap(&layer->rects, a, b);
250 dynarray_swap(&layer->colors, a, b);
251 dynarray_swap(&layer->ids, a, b);
252 dynarray_swap(&layer->actions, a, b);
254 RECT_UNDO_PUSH(undo_history, create_rect_undo_swap_context(layer, a, b));
257 static int rect_layer_delete_rect_at(RectLayer *layer,
259 UndoHistory *undo_history)
263 RECT_UNDO_PUSH(undo_history, create_rect_undo_delete_context(layer));
265 dynarray_delete_at(&layer->rects, i);
266 dynarray_delete_at(&layer->colors, i);
267 dynarray_delete_at(&layer->ids, i);
268 dynarray_delete_at(&layer->actions, i);
273 static int calc_resize_mask(Vec2f point, Rect rect)
276 for (Rect_side side = 0; side < RECT_SIDE_N; ++side) {
277 if (rect_side_distance(rect, point, side) < RECT_LAYER_SELECTION_THICCNESS) {
278 mask = mask | (1 << side);
284 static int rect_layer_event_idle(RectLayer *layer,
285 const SDL_Event *event,
286 const Camera *camera,
287 UndoHistory *undo_history)
291 trace_assert(camera);
293 int color_changed = 0;
294 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
299 if (layer->selection >= 0) {
300 dynarray_copy_to(&layer->colors, &layer->inter_color, (size_t)layer->selection);
301 layer->state = RECT_LAYER_RECOLOR;
306 Rect *rects = (Rect*)layer->rects.data;
308 switch (event->type) {
309 case SDL_MOUSEBUTTONDOWN: {
310 switch (event->button.button) {
311 case SDL_BUTTON_LEFT: {
312 Vec2f position = camera_map_screen(
316 int rect_at_position =
317 rect_layer_rect_at(layer, position);
320 Color *colors = (Color*)layer->colors.data;
322 if (layer->selection >= 0 &&
323 layer->selection == rect_at_position &&
324 (layer->resize_mask = calc_resize_mask(
325 vec((float) event->button.x, (float)event->button.y),
326 camera_rect(camera, rects[layer->selection])))) {
327 layer->state = RECT_LAYER_RESIZE;
328 dynarray_copy_to(&layer->rects, &layer->inter_rect, (size_t) layer->selection);
329 } else if (rect_at_position >= 0) {
330 layer->selection = rect_at_position;
331 layer->state = RECT_LAYER_MOVE;
332 layer->move_anchor = vec_sub(
335 rects[layer->selection].x,
336 rects[layer->selection].y));
337 layer->color_picker =
338 create_color_picker_from_rgba(colors[rect_at_position]);
339 dynarray_copy_to(&layer->rects, &layer->inter_rect, (size_t) rect_at_position);
341 layer->selection = rect_at_position;
343 if (layer->selection < 0) {
344 layer->state = RECT_LAYER_CREATE;
345 layer->create_begin = position;
346 layer->create_end = position;
353 case SDL_MOUSEMOTION: {
355 Vec2f position = camera_map_screen(
359 if (layer->selection >= 0 &&
360 layer->selection == rect_layer_rect_at(layer, position) &&
361 (resize_mask = calc_resize_mask(
362 vec((float) event->button.x, (float)event->button.y),
363 camera_rect(camera, rects[layer->selection])))) {
364 layer->cursor->style = resize_styles[resize_mask];
366 layer->cursor->style = CURSOR_STYLE_POINTER;
371 switch (event->key.keysym.sym) {
373 if ((event->key.keysym.mod & KMOD_SHIFT)
374 && (layer->selection >= 0)
375 && ((size_t)(layer->selection + 1) < layer->rects.count)) {
376 rect_layer_swap_elements(
378 (size_t) layer->selection,
379 (size_t) layer->selection + 1,
386 if ((event->key.keysym.mod & KMOD_SHIFT)
387 && (layer->selection > 0)
388 && ((size_t) layer->selection < layer->rects.count)) {
389 rect_layer_swap_elements(
391 (size_t) layer->selection,
392 (size_t) layer->selection - 1,
399 if (layer->selection >= 0) {
400 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
401 layer->selection = -1;
406 // TODO(#1171): there is no UI indication that we are in the snapping mode
407 layer->snapping_enabled = !layer->snapping_enabled;
411 if (layer->selection >= 0) {
412 const char *ids = (char*)layer->ids.data;
413 Color *colors = (Color*)layer->colors.data;
416 &layer->id_edit_field,
417 RECT_LAYER_ID_LABEL_SIZE,
418 color_invert(colors[layer->selection]));
420 layer->state = RECT_LAYER_ID_RENAME;
422 &layer->id_edit_field,
423 ids + layer->selection * ENTITY_MAX_ID_SIZE);
424 SDL_StartTextInput();
429 if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
431 dynarray_copy_to(&layer->rects, &rect_clipboard_rect, (size_t)layer->selection);
432 dynarray_copy_to(&layer->colors, &rect_clipboard_color, (size_t)layer->selection);
437 if ((event->key.keysym.mod & KMOD_LCTRL) && rect_clipboard) {
439 SDL_GetMouseState(&x, &y);
440 Vec2f position = camera_map_screen(camera, x, y);
444 rect(position.x, position.y,
445 rect_clipboard_rect.w, rect_clipboard_rect.h),
446 rect_clipboard_color,
457 static int rect_layer_event_create(RectLayer *layer,
458 const SDL_Event *event,
459 const Camera *camera,
460 UndoHistory *undo_history)
464 trace_assert(camera);
466 switch (event->type) {
467 case SDL_MOUSEBUTTONUP: {
468 switch (event->button.button) {
469 case SDL_BUTTON_LEFT: {
470 const Rect real_rect =
474 const float area = real_rect.w * real_rect.h;
476 if (area >= CREATE_AREA_THRESHOLD) {
480 color_picker_rgba(&layer->color_picker),
483 log_info("The area is too small %f. Such small box won't be created.\n", area);
485 layer->state = RECT_LAYER_IDLE;
490 case SDL_MOUSEMOTION: {
491 layer->create_end = camera_map_screen(
501 void snap_rect_resize_if_enabled(RectLayer *layer, Rect *a, float snapping_threshold)
504 trace_assert(layer->selection >= 0);
507 if (!layer->snapping_enabled) return;
509 Rect *rects = (Rect*)layer->rects.data;
510 size_t rects_size = layer->rects.count;
512 for (size_t i = 0; i < rects_size; ++i) {
513 if (i == (size_t) layer->selection) continue;
515 const Rect b = rects[i];
517 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
518 snap_var2seg(&a->y, b.y, 0, b.h, snapping_threshold);
521 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
522 snap_var2seg(&a->x, b.x, 0, b.w, snapping_threshold);
527 static int rect_layer_event_resize(RectLayer *layer,
528 const SDL_Event *event,
529 const Camera *camera,
530 UndoHistory *undo_history)
534 trace_assert(camera);
535 trace_assert(layer->selection >= 0);
537 Rect *rects = (Rect*)layer->rects.data;
539 float scaled_snap_threshold = SNAPPING_THRESHOLD / camera->scale;
541 switch (event->type) {
542 case SDL_MOUSEMOTION: {
543 Vec2f position = camera_map_screen(
548 switch (layer->resize_mask) {
550 Rect a = rect(rects[layer->selection].x,
552 rects[layer->selection].w,
553 rects[layer->selection].h);
555 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
557 layer->inter_rect = rect_from_points(
559 rect_position2(rects[layer->selection]));
563 Rect a = rect(position.x,
564 rects[layer->selection].y,
565 rects[layer->selection].w,
566 rects[layer->selection].h);
568 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
570 layer->inter_rect = rect_from_points(
572 rect_position2(rects[layer->selection]));
575 case 3: { // TOP,LEFT
579 rects[layer->selection].w,
580 rects[layer->selection].h);
582 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
584 layer->inter_rect = rect_from_points(
586 rect_position2(rects[layer->selection]));
590 Rect a = rect(rects[layer->selection].x,
592 rects[layer->selection].w,
593 rects[layer->selection].h);
595 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
597 layer->inter_rect = rect_from_points(
598 rect_position(rects[layer->selection]),
599 vec(rects[layer->selection].x + rects[layer->selection].w,
603 case 6: { // BOTTOM,LEFT
607 rects[layer->selection].w,
608 -rects[layer->selection].h);
610 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
612 layer->inter_rect = rect_from_points(
613 vec(a.x, rects[layer->selection].y),
614 vec(rects[layer->selection].x + rects[layer->selection].w,
619 Rect a = rect(position.x,
620 rects[layer->selection].y,
621 rects[layer->selection].w,
622 rects[layer->selection].h);
624 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
626 layer->inter_rect = rect_from_points(
627 rect_position(rects[layer->selection]),
628 vec(a.x, rects[layer->selection].y + rects[layer->selection].h));
631 case 9: { // TOP,RIGHT
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 vec(rects[layer->selection].x, a.y),
643 rects[layer->selection].y + rects[layer->selection].h));
646 case 12: { // BOTTOM,RIGHT
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 rect_position(rects[layer->selection]),
663 case SDL_MOUSEBUTTONUP: {
664 layer->state = RECT_LAYER_IDLE;
665 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
666 dynarray_replace_at(&layer->rects, (size_t) layer->selection, &layer->inter_rect);
674 void snap_rect_move_if_enabled(RectLayer *layer, Rect *a,
675 float snapping_threshold)
679 trace_assert(layer->selection >= 0);
681 if (!layer->snapping_enabled) return;
683 Rect *rects = (Rect*)layer->rects.data;
684 size_t rects_size = layer->rects.count;
686 for (size_t i = 0; i < rects_size; ++i) {
687 if (i == (size_t) layer->selection) continue;
689 const Rect b = rects[i];
691 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
692 snap_seg2seg(&a->y, b.y, a->h, b.h, snapping_threshold);
695 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
696 snap_seg2seg(&a->x, b.x, a->w, b.w, snapping_threshold);
701 static int rect_layer_event_move(RectLayer *layer,
702 const SDL_Event *event,
703 const Camera *camera,
704 UndoHistory *undo_history)
708 trace_assert(camera);
709 trace_assert(layer->selection >= 0);
711 Rect *rects = (Rect*)layer->rects.data;
713 switch (event->type) {
714 case SDL_MOUSEMOTION: {
715 const Uint8 *state = SDL_GetKeyboardState(NULL);
716 const Vec2f mouse_pos = vec_sub(
723 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
724 layer->inter_rect.x = mouse_pos.x;
725 layer->inter_rect.y = mouse_pos.y;
727 const Vec2f rect_pos = rect_position(rects[layer->selection]);
729 const float dx = fabsf(rect_pos.x - mouse_pos.x);
730 const float dy = fabsf(rect_pos.y - mouse_pos.y);
733 layer->inter_rect.x = mouse_pos.x;
734 layer->inter_rect.y = rect_pos.y;
736 layer->inter_rect.x = rect_pos.x;
737 layer->inter_rect.y = mouse_pos.y;
741 snap_rect_move_if_enabled(layer, &layer->inter_rect,
742 SNAPPING_THRESHOLD / camera->scale);
745 case SDL_MOUSEBUTTONUP: {
746 layer->state = RECT_LAYER_IDLE;
748 float distance = vec_length(
749 vec_sub(rect_position(layer->inter_rect),
750 rect_position(rects[layer->selection])));
752 if (distance > 1e-6) {
753 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
754 dynarray_replace_at(&layer->rects, (size_t) layer->selection, &layer->inter_rect);
761 static int rect_layer_event_id_rename(RectLayer *layer,
762 const SDL_Event *event,
763 const Camera *camera,
764 UndoHistory *undo_history)
768 trace_assert(camera);
769 trace_assert(layer->selection >= 0);
771 switch (event->type) {
773 switch (event->key.keysym.sym) {
775 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
777 char *id = dynarray_pointer_at(&layer->ids, (size_t)layer->selection);
778 memset(id, 0, ENTITY_MAX_ID_SIZE);
779 memcpy(id, edit_field_as_text(&layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
780 layer->state = RECT_LAYER_IDLE;
785 layer->state = RECT_LAYER_IDLE;
792 return edit_field_event(&layer->id_edit_field, event);
795 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
804 RectLayer create_rect_layer(const char *id_name_prefix, Cursor *cursor)
806 trace_assert(cursor);
808 RectLayer result = {0};
810 result.ids = create_dynarray(sizeof(char) * ENTITY_MAX_ID_SIZE);
811 result.rects = create_dynarray(sizeof(Rect));
812 result.colors = create_dynarray(sizeof(Color));
813 result.actions = create_dynarray(sizeof(Action));
814 result.id_edit_field.font_size = RECT_LAYER_ID_LABEL_SIZE;
815 result.id_edit_field.font_color = COLOR_BLACK;
816 result.color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
817 result.selection = -1;
818 result.id_name_prefix = id_name_prefix;
819 result.cursor = cursor;
824 RectLayer chop_rect_layer(Memory *memory,
826 const char *id_name_prefix,
829 trace_assert(memory);
832 RectLayer layer = create_rect_layer(id_name_prefix, cursor);
834 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
835 char id[ENTITY_MAX_ID_SIZE];
836 for (int i = 0; i < n; ++i) {
838 String line = trim(chop_by_delim(input, '\n'));
839 String string_id = trim(chop_word(&line));
840 rect.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
841 rect.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
842 rect.w = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
843 rect.h = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
844 Color color = hexs(trim(chop_word(&line)));
846 memset(id, 0, ENTITY_MAX_ID_SIZE);
850 min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
852 dynarray_push(&layer.rects, &rect);
853 dynarray_push(&layer.colors, &color);
854 dynarray_push(&layer.ids, id);
861 String action_string = trim(chop_word(&line));
862 if (action_string.count > 0) {
863 action.type = (ActionType)atol(string_to_cstr(memory, action_string));
864 switch (action.type) {
865 case ACTION_NONE: break;
866 case ACTION_TOGGLE_GOAL:
867 case ACTION_HIDE_LABEL: {
868 String label_id = trim(chop_word(&line));
869 trace_assert(label_id.count > 0);
870 memset(action.entity_id, 0, ENTITY_MAX_ID_SIZE);
871 memcpy(action.entity_id,
874 ENTITY_MAX_ID_SIZE - 1,
878 case ACTION_N: break;
882 dynarray_push(&layer.actions, &action);
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;