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"
18 #include "action_picker.h"
20 #include "math/extrema.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 < layer->rects.count);
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 < layer->rects.count);
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 dynarray_push(&layer->rects, &rect);
243 dynarray_push(&layer->colors, &color);
245 char id[ENTITY_MAX_ID_SIZE];
246 snprintf(id, ENTITY_MAX_ID_SIZE, "%s_%d",
247 layer->id_name_prefix,
248 layer->id_name_counter++);
249 dynarray_push(&layer->ids, id);
251 dynarray_push_empty(&layer->actions);
255 create_rect_undo_add_context(
257 layer->rects.count - 1));
262 static int rect_layer_rect_at(RectLayer *layer, Vec2f position)
266 int n = (int) layer->rects.count;
267 Rect *rects = (Rect*)layer->rects.data;
269 for (int i = n - 1; i >= 0; --i) {
270 if (rect_contains_point(rects[i], position)) {
278 static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b,
279 UndoHistory *undo_history)
282 trace_assert(a < layer->rects.count);
283 trace_assert(b < layer->rects.count);
285 dynarray_swap(&layer->rects, a, b);
286 dynarray_swap(&layer->colors, a, b);
287 dynarray_swap(&layer->ids, a, b);
288 dynarray_swap(&layer->actions, a, b);
290 RECT_UNDO_PUSH(undo_history, create_rect_undo_swap_context(layer, a, b));
293 static int rect_layer_delete_rect_at(RectLayer *layer,
295 UndoHistory *undo_history)
299 RECT_UNDO_PUSH(undo_history, create_rect_undo_delete_context(layer));
301 dynarray_delete_at(&layer->rects, i);
302 dynarray_delete_at(&layer->colors, i);
303 dynarray_delete_at(&layer->ids, i);
304 dynarray_delete_at(&layer->actions, i);
309 static int calc_resize_mask(Vec2f point, Rect rect)
312 for (Rect_side side = 0; side < RECT_SIDE_N; ++side) {
313 if (rect_side_distance(rect, point, side) < RECT_LAYER_SELECTION_THICCNESS) {
314 mask = mask | (1 << side);
320 static int rect_layer_event_idle(RectLayer *layer,
321 const SDL_Event *event,
322 const Camera *camera,
323 UndoHistory *undo_history)
327 trace_assert(camera);
329 int color_changed = 0;
330 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
335 if (layer->selection >= 0) {
336 dynarray_copy_to(&layer->colors, &layer->inter_color, (size_t)layer->selection);
337 layer->state = RECT_LAYER_RECOLOR;
342 Rect *rects = (Rect*)layer->rects.data;
344 switch (event->type) {
345 case SDL_MOUSEBUTTONDOWN: {
346 switch (event->button.button) {
347 case SDL_BUTTON_LEFT: {
348 Vec2f position = camera_map_screen(
352 int rect_at_position =
353 rect_layer_rect_at(layer, position);
356 Color *colors = (Color*)layer->colors.data;
358 if (layer->selection >= 0 &&
359 layer->selection == rect_at_position &&
360 (layer->resize_mask = calc_resize_mask(
361 vec((float) event->button.x, (float)event->button.y),
362 camera_rect(camera, rects[layer->selection])))) {
363 layer->state = RECT_LAYER_RESIZE;
364 dynarray_copy_to(&layer->rects, &layer->inter_rect, (size_t) layer->selection);
365 } else if (rect_at_position >= 0) {
366 layer->selection = rect_at_position;
367 layer->state = RECT_LAYER_MOVE;
368 layer->move_anchor = vec_sub(
371 rects[layer->selection].x,
372 rects[layer->selection].y));
373 layer->color_picker =
374 create_color_picker_from_rgba(colors[rect_at_position]);
375 dynarray_copy_to(&layer->rects, &layer->inter_rect, (size_t) rect_at_position);
377 layer->selection = rect_at_position;
379 if (layer->selection < 0) {
380 layer->state = RECT_LAYER_CREATE;
381 layer->create_begin = position;
382 layer->create_end = position;
389 case SDL_MOUSEMOTION: {
391 Vec2f position = camera_map_screen(
395 if (layer->selection >= 0 &&
396 layer->selection == rect_layer_rect_at(layer, position) &&
397 (resize_mask = calc_resize_mask(
398 vec((float) event->button.x, (float)event->button.y),
399 camera_rect(camera, rects[layer->selection])))) {
400 layer->cursor->style = resize_styles[resize_mask];
402 layer->cursor->style = CURSOR_STYLE_POINTER;
407 switch (event->key.keysym.sym) {
409 if ((event->key.keysym.mod & KMOD_SHIFT)
410 && (layer->selection >= 0)
411 && ((size_t)(layer->selection + 1) < layer->rects.count)) {
412 rect_layer_swap_elements(
414 (size_t) layer->selection,
415 (size_t) layer->selection + 1,
422 if ((event->key.keysym.mod & KMOD_SHIFT)
423 && (layer->selection > 0)
424 && ((size_t) layer->selection < layer->rects.count)) {
425 rect_layer_swap_elements(
427 (size_t) layer->selection,
428 (size_t) layer->selection - 1,
435 if (layer->selection >= 0) {
436 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
437 layer->selection = -1;
442 // TODO(#1171): there is no UI indication that we are in the snapping mode
443 layer->snapping_enabled = !layer->snapping_enabled;
447 if (layer->selection >= 0) {
448 const char *ids = (char*)layer->ids.data;
449 Color *colors = (Color*)layer->colors.data;
452 layer->id_edit_field,
453 RECT_LAYER_ID_LABEL_SIZE,
454 color_invert(colors[layer->selection]));
456 layer->state = RECT_LAYER_ID_RENAME;
458 layer->id_edit_field,
459 ids + layer->selection * ENTITY_MAX_ID_SIZE);
460 SDL_StartTextInput();
465 if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
467 dynarray_copy_to(&layer->rects, &rect_clipboard_rect, (size_t)layer->selection);
468 dynarray_copy_to(&layer->colors, &rect_clipboard_color, (size_t)layer->selection);
473 if ((event->key.keysym.mod & KMOD_LCTRL) && rect_clipboard) {
475 SDL_GetMouseState(&x, &y);
476 Vec2f position = camera_map_screen(camera, x, y);
480 rect(position.x, position.y,
481 rect_clipboard_rect.w, rect_clipboard_rect.h),
482 rect_clipboard_color,
493 static int rect_layer_event_create(RectLayer *layer,
494 const SDL_Event *event,
495 const Camera *camera,
496 UndoHistory *undo_history)
500 trace_assert(camera);
502 switch (event->type) {
503 case SDL_MOUSEBUTTONUP: {
504 switch (event->button.button) {
505 case SDL_BUTTON_LEFT: {
506 const Rect real_rect =
510 const float area = real_rect.w * real_rect.h;
512 if (area >= CREATE_AREA_THRESHOLD) {
516 color_picker_rgba(&layer->color_picker),
519 log_info("The area is too small %f. Such small box won't be created.\n", area);
521 layer->state = RECT_LAYER_IDLE;
526 case SDL_MOUSEMOTION: {
527 layer->create_end = camera_map_screen(
537 void snap_rect_resize_if_enabled(RectLayer *layer, Rect *a, float snapping_threshold)
540 trace_assert(layer->selection >= 0);
543 if (!layer->snapping_enabled) return;
545 Rect *rects = (Rect*)layer->rects.data;
546 size_t rects_size = layer->rects.count;
548 for (size_t i = 0; i < rects_size; ++i) {
549 if (i == (size_t) layer->selection) continue;
551 const Rect b = rects[i];
553 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
554 snap_var2seg(&a->y, b.y, 0, b.h, snapping_threshold);
557 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
558 snap_var2seg(&a->x, b.x, 0, b.w, snapping_threshold);
563 static int rect_layer_event_resize(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 Rect *rects = (Rect*)layer->rects.data;
575 float scaled_snap_threshold = SNAPPING_THRESHOLD / camera->scale;
577 switch (event->type) {
578 case SDL_MOUSEMOTION: {
579 Vec2f position = camera_map_screen(
584 switch (layer->resize_mask) {
586 Rect a = rect(rects[layer->selection].x,
588 rects[layer->selection].w,
589 rects[layer->selection].h);
591 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
593 layer->inter_rect = rect_from_points(
595 rect_position2(rects[layer->selection]));
599 Rect a = rect(position.x,
600 rects[layer->selection].y,
601 rects[layer->selection].w,
602 rects[layer->selection].h);
604 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
606 layer->inter_rect = rect_from_points(
608 rect_position2(rects[layer->selection]));
611 case 3: { // TOP,LEFT
615 rects[layer->selection].w,
616 rects[layer->selection].h);
618 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
620 layer->inter_rect = rect_from_points(
622 rect_position2(rects[layer->selection]));
626 Rect a = rect(rects[layer->selection].x,
628 rects[layer->selection].w,
629 rects[layer->selection].h);
631 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
633 layer->inter_rect = rect_from_points(
634 rect_position(rects[layer->selection]),
635 vec(rects[layer->selection].x + rects[layer->selection].w,
639 case 6: { // BOTTOM,LEFT
643 rects[layer->selection].w,
644 -rects[layer->selection].h);
646 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
648 layer->inter_rect = rect_from_points(
649 vec(a.x, rects[layer->selection].y),
650 vec(rects[layer->selection].x + rects[layer->selection].w,
655 Rect a = rect(position.x,
656 rects[layer->selection].y,
657 rects[layer->selection].w,
658 rects[layer->selection].h);
660 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
662 layer->inter_rect = rect_from_points(
663 rect_position(rects[layer->selection]),
664 vec(a.x, rects[layer->selection].y + rects[layer->selection].h));
667 case 9: { // TOP,RIGHT
671 -rects[layer->selection].w,
672 rects[layer->selection].h);
674 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
676 layer->inter_rect = rect_from_points(
677 vec(rects[layer->selection].x, a.y),
679 rects[layer->selection].y + rects[layer->selection].h));
682 case 12: { // BOTTOM,RIGHT
686 -rects[layer->selection].w,
687 -rects[layer->selection].h);
689 snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
691 layer->inter_rect = rect_from_points(
692 rect_position(rects[layer->selection]),
699 case SDL_MOUSEBUTTONUP: {
700 layer->state = RECT_LAYER_IDLE;
701 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
702 dynarray_replace_at(&layer->rects, (size_t) layer->selection, &layer->inter_rect);
710 void snap_rect_move_if_enabled(RectLayer *layer, Rect *a,
711 float snapping_threshold)
715 trace_assert(layer->selection >= 0);
717 if (!layer->snapping_enabled) return;
719 Rect *rects = (Rect*)layer->rects.data;
720 size_t rects_size = layer->rects.count;
722 for (size_t i = 0; i < rects_size; ++i) {
723 if (i == (size_t) layer->selection) continue;
725 const Rect b = rects[i];
727 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
728 snap_seg2seg(&a->y, b.y, a->h, b.h, snapping_threshold);
731 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
732 snap_seg2seg(&a->x, b.x, a->w, b.w, snapping_threshold);
737 static int rect_layer_event_move(RectLayer *layer,
738 const SDL_Event *event,
739 const Camera *camera,
740 UndoHistory *undo_history)
744 trace_assert(camera);
745 trace_assert(layer->selection >= 0);
747 Rect *rects = (Rect*)layer->rects.data;
749 switch (event->type) {
750 case SDL_MOUSEMOTION: {
751 const Uint8 *state = SDL_GetKeyboardState(NULL);
752 const Vec2f mouse_pos = vec_sub(
759 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
760 layer->inter_rect.x = mouse_pos.x;
761 layer->inter_rect.y = mouse_pos.y;
763 const Vec2f rect_pos = rect_position(rects[layer->selection]);
765 const float dx = fabsf(rect_pos.x - mouse_pos.x);
766 const float dy = fabsf(rect_pos.y - mouse_pos.y);
769 layer->inter_rect.x = mouse_pos.x;
770 layer->inter_rect.y = rect_pos.y;
772 layer->inter_rect.x = rect_pos.x;
773 layer->inter_rect.y = mouse_pos.y;
777 snap_rect_move_if_enabled(layer, &layer->inter_rect,
778 SNAPPING_THRESHOLD / camera->scale);
781 case SDL_MOUSEBUTTONUP: {
782 layer->state = RECT_LAYER_IDLE;
784 float distance = vec_length(
785 vec_sub(rect_position(layer->inter_rect),
786 rect_position(rects[layer->selection])));
788 if (distance > 1e-6) {
789 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
790 dynarray_replace_at(&layer->rects, (size_t) layer->selection, &layer->inter_rect);
797 static int rect_layer_event_id_rename(RectLayer *layer,
798 const SDL_Event *event,
799 const Camera *camera,
800 UndoHistory *undo_history)
804 trace_assert(camera);
805 trace_assert(layer->selection >= 0);
807 switch (event->type) {
809 switch (event->key.keysym.sym) {
811 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
813 char *id = dynarray_pointer_at(&layer->ids, (size_t)layer->selection);
814 memset(id, 0, ENTITY_MAX_ID_SIZE);
815 memcpy(id, edit_field_as_text(layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
816 layer->state = RECT_LAYER_IDLE;
821 layer->state = RECT_LAYER_IDLE;
828 return edit_field_event(layer->id_edit_field, event);
831 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
840 RectLayer *create_rect_layer(const char *id_name_prefix, Cursor *cursor)
842 trace_assert(cursor);
844 Lt *lt = create_lt();
846 RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
852 layer->ids = create_dynarray(sizeof(char) * ENTITY_MAX_ID_SIZE);
853 layer->rects = create_dynarray(sizeof(Rect));
854 layer->colors = create_dynarray(sizeof(Color));
855 layer->actions = create_dynarray(sizeof(Action));
857 layer->id_edit_field = PUSH_LT(
860 RECT_LAYER_ID_LABEL_SIZE,
863 if (layer->id_edit_field == NULL) {
872 sizeof(Grid) + sizeof(Widget*) * RECT_LAYER_GRID_ROWS * RECT_LAYER_GRID_COLUMNS),
874 if (layer->grid == NULL) {
877 layer->grid->rows = RECT_LAYER_GRID_ROWS;
878 layer->grid->columns = RECT_LAYER_GRID_COLUMNS;
879 grid_put_widget(layer->grid, &layer->action_picker.widget, 0, RECT_LAYER_GRID_COLUMNS - 1);
881 layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
882 layer->selection = -1;
883 layer->id_name_prefix = id_name_prefix;
884 layer->cursor = cursor;
889 RectLayer *chop_rect_layer(Memory *memory,
891 const char *id_name_prefix,
894 trace_assert(memory);
897 RectLayer *layer = create_rect_layer(id_name_prefix, cursor);
900 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
901 char id[ENTITY_MAX_ID_SIZE];
902 for (int i = 0; i < n; ++i) {
904 String line = trim(chop_by_delim(input, '\n'));
905 String string_id = trim(chop_word(&line));
906 rect.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
907 rect.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
908 rect.w = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
909 rect.h = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
910 Color color = hexs(trim(chop_word(&line)));
912 memset(id, 0, ENTITY_MAX_ID_SIZE);
916 min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
918 dynarray_push(&layer->rects, &rect);
919 dynarray_push(&layer->colors, &color);
920 dynarray_push(&layer->ids, id);
927 String action_string = trim(chop_word(&line));
928 if (action_string.count > 0) {
929 action.type = atoi(string_to_cstr(memory, action_string));
930 switch (action.type) {
931 case ACTION_NONE: break;
932 case ACTION_TOGGLE_GOAL:
933 case ACTION_HIDE_LABEL: {
934 String label_id = trim(chop_word(&line));
935 trace_assert(label_id.count > 0);
936 memset(action.entity_id, 0, ENTITY_MAX_ID_SIZE);
937 memcpy(action.entity_id,
940 ENTITY_MAX_ID_SIZE - 1,
944 case ACTION_N: break;
948 dynarray_push(&layer->actions, &action);
954 void destroy_rect_layer(RectLayer *layer)
958 free(layer->ids.data);
959 free(layer->rects.data);
960 free(layer->colors.data);
961 free(layer->actions.data);
963 RETURN_LT0(layer->lt);
966 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
969 trace_assert(camera);
971 const size_t n = layer->rects.count;
972 Rect *rects = (Rect *)layer->rects.data;
973 Color *colors = (Color *)layer->colors.data;
974 const char *ids = (const char *)layer->ids.data;
977 for (size_t i = 0; i < n; ++i) {
978 Rect rect = rects[i];
979 Color color = colors[i];
981 if (layer->selection == (int) i) {
982 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
983 rect = layer->inter_rect;
986 if (layer->state == RECT_LAYER_RECOLOR) {
987 color = layer->inter_color;
992 if (camera_fill_rect(
997 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
1002 // Selection Overlay
1003 if (active && layer->selection >= 0) {
1004 Rect rect = rects[layer->selection];
1005 Color color = colors[layer->selection];
1007 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
1008 rect = layer->inter_rect;
1011 if (layer->state == RECT_LAYER_RECOLOR) {
1012 color = layer->inter_color;
1015 const Rect overlay_rect =
1017 camera_rect(camera, rect),
1018 -RECT_LAYER_SELECTION_THICCNESS * 0.5f);
1019 const Color overlay_color = color_invert(color);
1022 if (camera_draw_thicc_rect_screen(
1026 RECT_LAYER_SELECTION_THICCNESS) < 0) {
1030 const Vec2f rect_id_pos = vec_sub(
1031 rect_position(rect),
1033 RECT_LAYER_ID_LABEL_SIZE,
1034 vec(0.0f, FONT_CHAR_HEIGHT)));
1037 if (layer->state == RECT_LAYER_ID_RENAME) {
1038 // ID renaming Edit Field
1039 if (edit_field_render_world(
1040 layer->id_edit_field,
1047 if (camera_render_text(
1049 ids + layer->selection * ENTITY_MAX_ID_SIZE,
1050 RECT_LAYER_ID_LABEL_SIZE,
1051 color_invert(color),
1059 const Color color = color_picker_rgba(&layer->color_picker);
1060 if (layer->state == RECT_LAYER_CREATE) {
1061 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
1066 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
1074 int rect_layer_event_recolor(RectLayer *layer,
1075 const SDL_Event *event,
1076 const Camera *camera,
1077 UndoHistory *undo_history)
1079 trace_assert(layer);
1080 trace_assert(event);
1081 trace_assert(camera);
1082 trace_assert(undo_history);
1083 trace_assert(layer->selection >= 0);
1085 int color_changed = 0;
1086 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
1090 if (color_changed) {
1091 layer->inter_color = color_picker_rgba(&layer->color_picker);
1093 if (!color_picker_drag(&layer->color_picker)) {
1094 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
1095 dynarray_replace_at(&layer->colors, (size_t) layer->selection, &layer->inter_color);
1096 layer->state = RECT_LAYER_IDLE;
1103 int rect_layer_event(RectLayer *layer,
1104 const SDL_Event *event,
1105 const Camera *camera,
1106 UndoHistory *undo_history)
1108 trace_assert(layer);
1109 trace_assert(event);
1110 trace_assert(undo_history);
1112 switch (event->type) {
1113 case SDL_WINDOWEVENT: {
1114 switch (event->window.event) {
1115 case SDL_WINDOWEVENT_SIZE_CHANGED: {
1116 grid_relayout(layer->grid, rect(0.0f, 0.0f,
1117 (float) event->window.data1,
1118 (float) event->window.data2));
1124 switch (layer->state) {
1125 case RECT_LAYER_IDLE:
1126 return rect_layer_event_idle(layer, event, camera, undo_history);
1128 case RECT_LAYER_CREATE:
1129 return rect_layer_event_create(layer, event, camera, undo_history);
1131 case RECT_LAYER_RESIZE:
1132 return rect_layer_event_resize(layer, event, camera, undo_history);
1134 case RECT_LAYER_MOVE:
1135 return rect_layer_event_move(layer, event, camera, undo_history);
1137 case RECT_LAYER_ID_RENAME:
1138 return rect_layer_event_id_rename(layer, event, camera, undo_history);
1140 case RECT_LAYER_RECOLOR:
1141 return rect_layer_event_recolor(layer, event, camera, undo_history);
1148 size_t rect_layer_count(const RectLayer *layer)
1150 return layer->rects.count;
1153 const Rect *rect_layer_rects(const RectLayer *layer)
1155 return (const Rect *)layer->rects.data;
1158 const Color *rect_layer_colors(const RectLayer *layer)
1160 return (const Color *)layer->colors.data;
1163 const char *rect_layer_ids(const RectLayer *layer)
1165 return (const char *)layer->ids.data;
1168 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
1170 trace_assert(layer);
1171 trace_assert(filedump);
1173 size_t n = layer->ids.count;
1174 char *ids = (char *)layer->ids.data;
1175 Rect *rects = (Rect *)layer->rects.data;
1176 Color *colors = (Color *)layer->colors.data;
1177 Action *actions = (Action *)layer->actions.data;
1179 fprintf(filedump, "%zd\n", n);
1180 for (size_t i = 0; i < n; ++i) {
1181 fprintf(filedump, "%s %f %f %f %f ",
1182 ids + ENTITY_MAX_ID_SIZE * i,
1183 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
1184 color_hex_to_stream(colors[i], filedump);
1186 switch (actions[i].type) {
1187 case ACTION_NONE: {} break;
1189 case ACTION_TOGGLE_GOAL:
1190 case ACTION_HIDE_LABEL: {
1191 fprintf(filedump, " %d %.*s",
1192 (int)actions[i].type,
1193 ENTITY_MAX_ID_SIZE, actions[i].entity_id);
1195 case ACTION_N: break;
1198 fprintf(filedump, "\n");
1204 const Action *rect_layer_actions(const RectLayer *layer)
1206 return (const Action *)layer->actions.data;