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.font_size = RECT_LAYER_ID_LABEL_SIZE;
858 layer->id_edit_field.font_color = COLOR_BLACK;
865 sizeof(Grid) + sizeof(Widget*) * RECT_LAYER_GRID_ROWS * RECT_LAYER_GRID_COLUMNS),
867 if (layer->grid == NULL) {
870 layer->grid->rows = RECT_LAYER_GRID_ROWS;
871 layer->grid->columns = RECT_LAYER_GRID_COLUMNS;
872 grid_put_widget(layer->grid, &layer->action_picker.widget, 0, RECT_LAYER_GRID_COLUMNS - 1);
874 layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
875 layer->selection = -1;
876 layer->id_name_prefix = id_name_prefix;
877 layer->cursor = cursor;
882 RectLayer *chop_rect_layer(Memory *memory,
884 const char *id_name_prefix,
887 trace_assert(memory);
890 RectLayer *layer = create_rect_layer(id_name_prefix, cursor);
893 int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
894 char id[ENTITY_MAX_ID_SIZE];
895 for (int i = 0; i < n; ++i) {
897 String line = trim(chop_by_delim(input, '\n'));
898 String string_id = trim(chop_word(&line));
899 rect.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
900 rect.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
901 rect.w = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
902 rect.h = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
903 Color color = hexs(trim(chop_word(&line)));
905 memset(id, 0, ENTITY_MAX_ID_SIZE);
909 min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
911 dynarray_push(&layer->rects, &rect);
912 dynarray_push(&layer->colors, &color);
913 dynarray_push(&layer->ids, id);
920 String action_string = trim(chop_word(&line));
921 if (action_string.count > 0) {
922 action.type = (ActionType)atol(string_to_cstr(memory, action_string));
923 switch (action.type) {
924 case ACTION_NONE: break;
925 case ACTION_TOGGLE_GOAL:
926 case ACTION_HIDE_LABEL: {
927 String label_id = trim(chop_word(&line));
928 trace_assert(label_id.count > 0);
929 memset(action.entity_id, 0, ENTITY_MAX_ID_SIZE);
930 memcpy(action.entity_id,
933 ENTITY_MAX_ID_SIZE - 1,
937 case ACTION_N: break;
941 dynarray_push(&layer->actions, &action);
947 void destroy_rect_layer(RectLayer *layer)
951 free(layer->ids.data);
952 free(layer->rects.data);
953 free(layer->colors.data);
954 free(layer->actions.data);
956 RETURN_LT0(layer->lt);
959 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
962 trace_assert(camera);
964 const size_t n = layer->rects.count;
965 Rect *rects = (Rect *)layer->rects.data;
966 Color *colors = (Color *)layer->colors.data;
967 const char *ids = (const char *)layer->ids.data;
970 for (size_t i = 0; i < n; ++i) {
971 Rect rect = rects[i];
972 Color color = colors[i];
974 if (layer->selection == (int) i) {
975 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
976 rect = layer->inter_rect;
979 if (layer->state == RECT_LAYER_RECOLOR) {
980 color = layer->inter_color;
985 if (camera_fill_rect(
990 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
996 if (active && layer->selection >= 0) {
997 Rect rect = rects[layer->selection];
998 Color color = colors[layer->selection];
1000 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
1001 rect = layer->inter_rect;
1004 if (layer->state == RECT_LAYER_RECOLOR) {
1005 color = layer->inter_color;
1008 const Rect overlay_rect =
1010 camera_rect(camera, rect),
1011 -RECT_LAYER_SELECTION_THICCNESS * 0.5f);
1012 const Color overlay_color = color_invert(color);
1015 if (camera_draw_thicc_rect_screen(
1019 RECT_LAYER_SELECTION_THICCNESS) < 0) {
1023 const Vec2f rect_id_pos = vec_sub(
1024 rect_position(rect),
1026 RECT_LAYER_ID_LABEL_SIZE,
1027 vec(0.0f, FONT_CHAR_HEIGHT)));
1030 if (layer->state == RECT_LAYER_ID_RENAME) {
1031 // ID renaming Edit Field
1032 if (edit_field_render_world(
1033 &layer->id_edit_field,
1040 if (camera_render_text(
1042 ids + layer->selection * ENTITY_MAX_ID_SIZE,
1043 RECT_LAYER_ID_LABEL_SIZE,
1044 color_invert(color),
1052 const Color color = color_picker_rgba(&layer->color_picker);
1053 if (layer->state == RECT_LAYER_CREATE) {
1054 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
1059 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
1067 int rect_layer_event_recolor(RectLayer *layer,
1068 const SDL_Event *event,
1069 const Camera *camera,
1070 UndoHistory *undo_history)
1072 trace_assert(layer);
1073 trace_assert(event);
1074 trace_assert(camera);
1075 trace_assert(undo_history);
1076 trace_assert(layer->selection >= 0);
1078 int color_changed = 0;
1079 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
1083 if (color_changed) {
1084 layer->inter_color = color_picker_rgba(&layer->color_picker);
1086 if (!color_picker_drag(&layer->color_picker)) {
1087 RECT_UNDO_PUSH(undo_history, create_rect_undo_update_context(layer));
1088 dynarray_replace_at(&layer->colors, (size_t) layer->selection, &layer->inter_color);
1089 layer->state = RECT_LAYER_IDLE;
1096 int rect_layer_event(RectLayer *layer,
1097 const SDL_Event *event,
1098 const Camera *camera,
1099 UndoHistory *undo_history)
1101 trace_assert(layer);
1102 trace_assert(event);
1103 trace_assert(undo_history);
1105 switch (event->type) {
1106 case SDL_WINDOWEVENT: {
1107 switch (event->window.event) {
1108 case SDL_WINDOWEVENT_SIZE_CHANGED: {
1109 grid_relayout(layer->grid, rect(0.0f, 0.0f,
1110 (float) event->window.data1,
1111 (float) event->window.data2));
1117 switch (layer->state) {
1118 case RECT_LAYER_IDLE:
1119 return rect_layer_event_idle(layer, event, camera, undo_history);
1121 case RECT_LAYER_CREATE:
1122 return rect_layer_event_create(layer, event, camera, undo_history);
1124 case RECT_LAYER_RESIZE:
1125 return rect_layer_event_resize(layer, event, camera, undo_history);
1127 case RECT_LAYER_MOVE:
1128 return rect_layer_event_move(layer, event, camera, undo_history);
1130 case RECT_LAYER_ID_RENAME:
1131 return rect_layer_event_id_rename(layer, event, camera, undo_history);
1133 case RECT_LAYER_RECOLOR:
1134 return rect_layer_event_recolor(layer, event, camera, undo_history);
1141 size_t rect_layer_count(const RectLayer *layer)
1143 return layer->rects.count;
1146 const Rect *rect_layer_rects(const RectLayer *layer)
1148 return (const Rect *)layer->rects.data;
1151 const Color *rect_layer_colors(const RectLayer *layer)
1153 return (const Color *)layer->colors.data;
1156 const char *rect_layer_ids(const RectLayer *layer)
1158 return (const char *)layer->ids.data;
1161 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
1163 trace_assert(layer);
1164 trace_assert(filedump);
1166 size_t n = layer->ids.count;
1167 char *ids = (char *)layer->ids.data;
1168 Rect *rects = (Rect *)layer->rects.data;
1169 Color *colors = (Color *)layer->colors.data;
1170 Action *actions = (Action *)layer->actions.data;
1172 fprintf(filedump, "%zd\n", n);
1173 for (size_t i = 0; i < n; ++i) {
1174 fprintf(filedump, "%s %f %f %f %f ",
1175 ids + ENTITY_MAX_ID_SIZE * i,
1176 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
1177 color_hex_to_stream(colors[i], filedump);
1179 switch (actions[i].type) {
1180 case ACTION_NONE: {} break;
1182 case ACTION_TOGGLE_GOAL:
1183 case ACTION_HIDE_LABEL: {
1184 fprintf(filedump, " %d %.*s",
1185 (int)actions[i].type,
1186 ENTITY_MAX_ID_SIZE, actions[i].entity_id);
1188 case ACTION_N: break;
1191 fprintf(filedump, "\n");
1197 const Action *rect_layer_actions(const RectLayer *layer)
1199 return (const Action *)layer->actions.data;