4 #include "game/camera.h"
6 #include "system/stacktrace.h"
7 #include "system/nth_alloc.h"
8 #include "system/log.h"
11 #include "rect_layer.h"
13 #include "system/line_stream.h"
14 #include "color_picker.h"
15 #include "system/str.h"
16 #include "ui/edit_field.h"
17 #include "undo_history.h"
18 #include "game/level/action.h"
19 #include "action_picker.h"
22 #define RECT_LAYER_SELECTION_THICCNESS 15.0f
23 #define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
24 #define CREATE_AREA_THRESHOLD 10.0
25 #define RECT_LAYER_GRID_ROWS 3
26 #define RECT_LAYER_GRID_COLUMNS 4
27 #define SNAPPING_THRESHOLD 10.0f
29 static int clipboard = 0;
30 static Rect clipboard_rect;
31 static Color clipboard_color;
33 static Cursor_Style resize_styles[1 << RECT_SIDE_N] = {
35 CURSOR_STYLE_RESIZE_VERT, // [1]
36 CURSOR_STYLE_RESIZE_HORIS, // [2]
37 CURSOR_STYLE_RESIZE_DIAG1, // [3]
38 CURSOR_STYLE_RESIZE_VERT, // [4]
40 CURSOR_STYLE_RESIZE_DIAG2, // [6]
42 CURSOR_STYLE_RESIZE_HORIS, // [8]
43 CURSOR_STYLE_RESIZE_DIAG2, // [9]
46 CURSOR_STYLE_RESIZE_DIAG1 // [12]
52 // TODO(#955): Rectangles in Level Editor have only one resize anchor to work with
53 // TODO(#1129): different cursor image in resize mode
68 ColorPicker color_picker;
69 ActionPicker action_picker;
73 Vec2f move_anchor; // The mouse offset from the left-top
74 // corner of the rect during moving it
75 Edit_field *id_edit_field;
79 const char *id_name_prefix;
99 char id[ENTITY_MAX_ID_SIZE];
100 } UndoElementContext;
120 UndoElementContext element;
121 UndoSwapContext swap;
125 UndoContext create_undo_add_context(RectLayer *layer, size_t index)
128 trace_assert(index < dynarray_count(layer->rects));
130 UndoContext undo_context;
131 undo_context.add.type = UNDO_ADD;
132 undo_context.add.layer = layer;
133 undo_context.add.index = index;
138 UndoContext create_undo_element_context(RectLayer *layer)
141 size_t index = (size_t) layer->selection;
142 trace_assert(index < dynarray_count(layer->rects));
144 UndoContext undo_context;
145 undo_context.element.layer = layer;
146 undo_context.element.index = index;
147 dynarray_copy_to(layer->rects, &undo_context.element.rect, index);
148 dynarray_copy_to(layer->colors, &undo_context.element.color, index);
149 dynarray_copy_to(layer->ids, undo_context.element.id, index);
150 dynarray_copy_to(layer->actions, &undo_context.element.action, index);
155 UndoContext create_undo_update_context(RectLayer *rect_layer)
157 UndoContext undo_context = create_undo_element_context(rect_layer);
158 undo_context.type = UNDO_UPDATE;
163 UndoContext create_undo_delete_context(RectLayer *rect_layer)
165 UndoContext undo_context = create_undo_element_context(rect_layer);
166 undo_context.type = UNDO_DELETE;
171 UndoContext create_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2)
173 UndoContext undo_context;
174 undo_context.swap.type = UNDO_SWAP;
175 undo_context.swap.layer = rect_layer;
176 undo_context.swap.index1 = index1;
177 undo_context.swap.index2 = index2;
182 void rect_layer_undo(void *context, size_t context_size)
184 trace_assert(context);
185 trace_assert(sizeof(UndoContext) == context_size);
187 UndoContext *undo_context = context;
189 switch (undo_context->type) {
191 RectLayer *layer = undo_context->add.layer;
192 dynarray_delete_at(layer->rects, undo_context->add.index);
193 dynarray_delete_at(layer->colors, undo_context->add.index);
194 dynarray_delete_at(layer->ids, undo_context->add.index);
195 dynarray_delete_at(layer->actions, undo_context->add.index);
196 layer->selection = -1;
200 RectLayer *layer = undo_context->element.layer;
201 dynarray_insert_before(layer->rects, undo_context->element.index, &undo_context->element.rect);
202 dynarray_insert_before(layer->colors, undo_context->element.index, &undo_context->element.color);
203 dynarray_insert_before(layer->ids, undo_context->element.index, &undo_context->element.id);
204 dynarray_insert_before(layer->actions, undo_context->element.index, &undo_context->element.action);
205 layer->selection = -1;
209 RectLayer *layer = undo_context->element.layer;
210 dynarray_replace_at(layer->rects, undo_context->element.index, &undo_context->element.rect);
211 dynarray_replace_at(layer->colors, undo_context->element.index, &undo_context->element.color);
212 dynarray_replace_at(layer->ids, undo_context->element.index, &undo_context->element.id);
213 dynarray_replace_at(layer->actions, undo_context->element.index, &undo_context->element.action);
217 RectLayer *layer = undo_context->element.layer;
218 dynarray_swap(layer->rects, undo_context->swap.index1, undo_context->swap.index2);
219 dynarray_swap(layer->colors, undo_context->swap.index1, undo_context->swap.index2);
220 dynarray_swap(layer->ids, undo_context->swap.index1, undo_context->swap.index2);
221 dynarray_swap(layer->actions, undo_context->swap.index1, undo_context->swap.index2);
226 #define UNDO_PUSH(HISTORY, CONTEXT) \
228 UndoContext context = (CONTEXT); \
236 static int rect_layer_add_rect(RectLayer *layer,
239 UndoHistory *undo_history)
243 if (dynarray_push(layer->rects, &rect) < 0) {
247 if (dynarray_push(layer->colors, &color) < 0) {
251 char id[ENTITY_MAX_ID_SIZE];
252 snprintf(id, ENTITY_MAX_ID_SIZE, "%s_%d",
253 layer->id_name_prefix,
254 layer->id_name_counter++);
255 if (dynarray_push(layer->ids, id)) {
259 dynarray_push_empty(layer->actions);
263 create_undo_add_context(
265 dynarray_count(layer->rects) - 1));
270 static int rect_layer_rect_at(RectLayer *layer, Vec2f position)
274 int n = (int) dynarray_count(layer->rects);
275 Rect *rects = dynarray_data(layer->rects);
277 for (int i = n - 1; i >= 0; --i) {
278 if (rect_contains_point(rects[i], position)) {
286 static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b,
287 UndoHistory *undo_history)
290 trace_assert(a < dynarray_count(layer->rects));
291 trace_assert(b < dynarray_count(layer->rects));
293 dynarray_swap(layer->rects, a, b);
294 dynarray_swap(layer->colors, a, b);
295 dynarray_swap(layer->ids, a, b);
296 dynarray_swap(layer->actions, a, b);
298 UNDO_PUSH(undo_history, create_undo_swap_context(layer, a, b));
301 static int rect_layer_delete_rect_at(RectLayer *layer,
303 UndoHistory *undo_history)
307 UNDO_PUSH(undo_history, create_undo_delete_context(layer));
309 dynarray_delete_at(layer->rects, i);
310 dynarray_delete_at(layer->colors, i);
311 dynarray_delete_at(layer->ids, i);
312 dynarray_delete_at(layer->actions, i);
317 static int calc_resize_mask(Vec2f point, Rect rect)
320 for (Rect_side side = 0; side < RECT_SIDE_N; ++side) {
321 if (rect_side_distance(rect, point, side) < RECT_LAYER_SELECTION_THICCNESS) {
322 mask = mask | (1 << side);
328 static int rect_layer_event_idle(RectLayer *layer,
329 const SDL_Event *event,
330 const Camera *camera,
331 UndoHistory *undo_history)
335 trace_assert(camera);
337 int color_changed = 0;
338 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
343 if (layer->selection >= 0) {
344 dynarray_copy_to(layer->colors, &layer->inter_color, (size_t)layer->selection);
345 layer->state = RECT_LAYER_RECOLOR;
350 Rect *rects = dynarray_data(layer->rects);
352 switch (event->type) {
353 case SDL_MOUSEBUTTONDOWN: {
354 switch (event->button.button) {
355 case SDL_BUTTON_LEFT: {
356 Vec2f position = camera_map_screen(
360 int rect_at_position =
361 rect_layer_rect_at(layer, position);
363 Color *colors = dynarray_data(layer->colors);
365 if (layer->selection >= 0 &&
366 layer->selection == rect_at_position &&
367 (layer->resize_mask = calc_resize_mask(
368 vec((float) event->button.x, (float)event->button.y),
369 camera_rect(camera, rects[layer->selection])))) {
370 layer->state = RECT_LAYER_RESIZE;
371 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) layer->selection);
372 } else if (rect_at_position >= 0) {
373 layer->selection = rect_at_position;
374 layer->state = RECT_LAYER_MOVE;
379 rects[layer->selection].x,
380 rects[layer->selection].y));
381 layer->color_picker =
382 create_color_picker_from_rgba(colors[rect_at_position]);
384 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) rect_at_position);
386 layer->selection = rect_at_position;
388 if (layer->selection < 0) {
389 layer->state = RECT_LAYER_CREATE;
390 layer->create_begin = position;
391 layer->create_end = position;
398 case SDL_MOUSEMOTION: {
400 Vec2f position = camera_map_screen(
404 if (layer->selection >= 0 &&
405 layer->selection == rect_layer_rect_at(layer, position) &&
406 (resize_mask = calc_resize_mask(
407 vec((float) event->button.x, (float)event->button.y),
408 camera_rect(camera, rects[layer->selection])))) {
409 layer->cursor->style = resize_styles[resize_mask];
411 layer->cursor->style = CURSOR_STYLE_POINTER;
416 switch (event->key.keysym.sym) {
418 if ((event->key.keysym.mod & KMOD_SHIFT)
419 && (layer->selection >= 0)
420 && ((size_t)(layer->selection + 1) < dynarray_count(layer->rects))) {
421 rect_layer_swap_elements(
423 (size_t) layer->selection,
424 (size_t) layer->selection + 1,
431 if ((event->key.keysym.mod & KMOD_SHIFT)
432 && (layer->selection > 0)
433 && ((size_t) layer->selection < dynarray_count(layer->rects))) {
434 rect_layer_swap_elements(
436 (size_t) layer->selection,
437 (size_t) layer->selection - 1,
444 if (layer->selection >= 0) {
445 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
446 layer->selection = -1;
451 if (layer->selection >= 0) {
452 const char *ids = dynarray_data(layer->ids);
453 Color *colors = dynarray_data(layer->colors);
456 layer->id_edit_field,
457 RECT_LAYER_ID_LABEL_SIZE,
458 color_invert(colors[layer->selection]));
460 layer->state = RECT_LAYER_ID_RENAME;
462 layer->id_edit_field,
463 ids + layer->selection * ENTITY_MAX_ID_SIZE);
464 SDL_StartTextInput();
469 if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
471 dynarray_copy_to(layer->rects, &clipboard_rect, (size_t)layer->selection);
472 dynarray_copy_to(layer->colors, &clipboard_color, (size_t)layer->selection);
477 if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
479 SDL_GetMouseState(&x, &y);
480 Vec2f position = camera_map_screen(camera, x, y);
484 rect(position.x, position.y,
485 clipboard_rect.w, clipboard_rect.h),
497 static int rect_layer_event_create(RectLayer *layer,
498 const SDL_Event *event,
499 const Camera *camera,
500 UndoHistory *undo_history)
504 trace_assert(camera);
506 switch (event->type) {
507 case SDL_MOUSEBUTTONUP: {
508 switch (event->button.button) {
509 case SDL_BUTTON_LEFT: {
510 const Rect real_rect =
514 const float area = real_rect.w * real_rect.h;
516 if (area >= CREATE_AREA_THRESHOLD) {
520 color_picker_rgba(&layer->color_picker),
523 log_info("The area is too small %f. Such small box won't be created.\n", area);
525 layer->state = RECT_LAYER_IDLE;
530 case SDL_MOUSEMOTION: {
531 layer->create_end = camera_map_screen(
540 static int rect_layer_event_resize(RectLayer *layer,
541 const SDL_Event *event,
542 const Camera *camera,
543 UndoHistory *undo_history)
547 trace_assert(camera);
548 trace_assert(layer->selection >= 0);
550 Rect *rects = dynarray_data(layer->rects);
552 switch (event->type) {
553 case SDL_MOUSEMOTION: {
554 Vec2f position = camera_map_screen(
559 switch (layer->resize_mask) {
561 layer->inter_rect = rect_from_points(
562 vec(rects[layer->selection].x, position.y),
563 rect_position2(rects[layer->selection]));
567 layer->inter_rect = rect_from_points(
568 vec(position.x, rects[layer->selection].y),
569 rect_position2(rects[layer->selection]));
572 case 3: { // TOP,LEFT
573 layer->inter_rect = rect_from_points(
575 rect_position2(rects[layer->selection]));
579 layer->inter_rect = rect_from_points(
580 rect_position(rects[layer->selection]),
581 vec(rects[layer->selection].x + rects[layer->selection].w,
585 case 6: { // BOTTOM,LEFT
586 layer->inter_rect = rect_from_points(
587 vec(position.x, rects[layer->selection].y),
588 vec(rects[layer->selection].x + rects[layer->selection].w,
593 layer->inter_rect = rect_from_points(
594 rect_position(rects[layer->selection]),
596 rects[layer->selection].y + rects[layer->selection].h));
599 case 9: { // TOP,RIGHT
600 layer->inter_rect = rect_from_points(
601 vec(rects[layer->selection].x, position.y),
603 rects[layer->selection].y + rects[layer->selection].h));
606 case 12: { // BOTTOM,RIGHT
607 layer->inter_rect = rect_from_points(
608 rect_position(rects[layer->selection]),
614 case SDL_MOUSEBUTTONUP: {
615 layer->state = RECT_LAYER_IDLE;
616 UNDO_PUSH(undo_history, create_undo_update_context(layer));
617 dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
625 int segment_overlap(Vec2f a, Vec2f b)
627 trace_assert(a.x <= a.y);
628 trace_assert(b.x <= b.y);
629 return a.y >= b.x && b.y >= a.x;
633 void snap_rects(size_t ignore_index, Rect *a,
634 Rect *rects, size_t rects_size)
639 for (size_t i = 0; i < rects_size; ++i) {
640 if (i == ignore_index) continue;
642 const Rect b = rects[i];
644 if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
645 if (fabsf(a->y - b.y) < SNAPPING_THRESHOLD) {
649 if (fabsf((a->y + a->h) - b.y) < SNAPPING_THRESHOLD) {
653 if (fabsf(a->y - (b.y + b.h)) < SNAPPING_THRESHOLD) {
657 if (fabsf((a->y + a->h) - (b.y + b.h)) < SNAPPING_THRESHOLD) {
658 a->y = b.y + b.h - a->h;
662 if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
663 if (fabs(a->x - b.x) < SNAPPING_THRESHOLD) {
667 if (fabs((a->x + a->w) - b.x) < SNAPPING_THRESHOLD) {
671 if (fabsf(a->x - (b.x + b.w)) < SNAPPING_THRESHOLD) {
675 if (fabsf((a->x + a->w) - (b.x + b.w)) < SNAPPING_THRESHOLD) {
676 a->x = b.x + b.w - a->w;
682 static int rect_layer_event_move(RectLayer *layer,
683 const SDL_Event *event,
684 const Camera *camera,
685 UndoHistory *undo_history)
689 trace_assert(camera);
690 trace_assert(layer->selection >= 0);
692 Rect *rects = dynarray_data(layer->rects);
694 switch (event->type) {
695 case SDL_MOUSEMOTION: {
696 const Uint8 *state = SDL_GetKeyboardState(NULL);
697 const Vec2f mouse_pos = vec_sub(
704 if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
705 layer->inter_rect.x = mouse_pos.x;
706 layer->inter_rect.y = mouse_pos.y;
708 const Vec2f rect_pos = rect_position(rects[layer->selection]);
710 const float dx = fabsf(rect_pos.x - mouse_pos.x);
711 const float dy = fabsf(rect_pos.y - mouse_pos.y);
714 layer->inter_rect.x = mouse_pos.x;
715 layer->inter_rect.y = rect_pos.y;
717 layer->inter_rect.x = rect_pos.x;
718 layer->inter_rect.y = mouse_pos.y;
722 // TODO(#1141): Rect Snapping in Level Editor should be optional
723 // TODO(#1142): Resize mode of Rect Layer does not support Snapping
724 snap_rects((size_t) layer->selection, &layer->inter_rect,
725 rects, dynarray_count(layer->rects));
728 case SDL_MOUSEBUTTONUP: {
729 layer->state = RECT_LAYER_IDLE;
731 float distance = vec_length(
732 vec_sub(rect_position(layer->inter_rect),
733 rect_position(rects[layer->selection])));
735 if (distance > 1e-6) {
736 UNDO_PUSH(undo_history, create_undo_update_context(layer));
737 dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
744 static int rect_layer_event_id_rename(RectLayer *layer,
745 const SDL_Event *event,
746 const Camera *camera,
747 UndoHistory *undo_history)
751 trace_assert(camera);
752 trace_assert(layer->selection >= 0);
754 switch (event->type) {
756 switch (event->key.keysym.sym) {
758 UNDO_PUSH(undo_history, create_undo_update_context(layer));
760 char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
761 memset(id, 0, ENTITY_MAX_ID_SIZE);
762 memcpy(id, edit_field_as_text(layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
763 layer->state = RECT_LAYER_IDLE;
768 layer->state = RECT_LAYER_IDLE;
775 return edit_field_event(layer->id_edit_field, event);
778 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
787 RectLayer *create_rect_layer(const char *id_name_prefix, Cursor *cursor)
789 trace_assert(cursor);
791 Lt *lt = create_lt();
793 RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
799 layer->ids = PUSH_LT(
801 create_dynarray(sizeof(char) * ENTITY_MAX_ID_SIZE),
803 if (layer->ids == NULL) {
807 layer->rects = PUSH_LT(
809 create_dynarray(sizeof(Rect)),
811 if (layer->rects == NULL) {
815 layer->colors = PUSH_LT(
817 create_dynarray(sizeof(Color)),
819 if (layer->colors == NULL) {
823 layer->actions = PUSH_LT(
825 create_dynarray(sizeof(Action)),
827 if (layer->actions == NULL) {
831 layer->id_edit_field = PUSH_LT(
834 RECT_LAYER_ID_LABEL_SIZE,
837 if (layer->id_edit_field == NULL) {
846 sizeof(Grid) + sizeof(Widget*) * RECT_LAYER_GRID_ROWS * RECT_LAYER_GRID_COLUMNS),
848 if (layer->grid == NULL) {
851 layer->grid->rows = RECT_LAYER_GRID_ROWS;
852 layer->grid->columns = RECT_LAYER_GRID_COLUMNS;
853 grid_put_widget(layer->grid, &layer->action_picker.widget, 0, RECT_LAYER_GRID_COLUMNS - 1);
855 layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
856 layer->selection = -1;
857 layer->id_name_prefix = id_name_prefix;
858 layer->cursor = cursor;
863 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream,
864 const char *id_name_prefix,
867 trace_assert(line_stream);
869 RectLayer *layer = create_rect_layer(id_name_prefix, cursor);
874 const char *line = line_stream_next(line_stream);
876 RETURN_LT(layer->lt, NULL);
880 if (sscanf(line, "%zu", &count) < 0) {
881 RETURN_LT(layer->lt, NULL);
884 for (size_t i = 0; i < count; ++i) {
885 line = line_stream_next(line_stream);
887 RETURN_LT(layer->lt, NULL);
892 char id[ENTITY_MAX_ID_SIZE];
896 "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s%f%f%f%f%6s%n",
901 log_fail("%s\n", strerror(errno));
902 RETURN_LT(layer->lt, NULL);
906 Color color = hexstr(hex);
907 dynarray_push(layer->rects, &rect);
908 dynarray_push(layer->ids, id);
909 dynarray_push(layer->colors, &color);
916 if (sscanf(line, "%d%n", (int*)&action.type, &n) > 0) {
918 switch (action.type) {
919 case ACTION_NONE: break;
921 case ACTION_TOGGLE_GOAL:
922 case ACTION_HIDE_LABEL: {
923 if (sscanf(line, "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s", action.entity_id) <= 0) {
924 log_fail("%s\n", strerror(errno));
925 RETURN_LT(layer->lt, NULL);
929 case ACTION_N: break;
933 dynarray_push(layer->actions, &action);
939 void destroy_rect_layer(RectLayer *layer)
942 RETURN_LT0(layer->lt);
945 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
948 trace_assert(camera);
950 const size_t n = dynarray_count(layer->rects);
951 Rect *rects = dynarray_data(layer->rects);
952 Color *colors = dynarray_data(layer->colors);
953 const char *ids = dynarray_data(layer->ids);
956 for (size_t i = 0; i < n; ++i) {
957 Rect rect = rects[i];
958 Color color = colors[i];
960 if (layer->selection == (int) i) {
961 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
962 rect = layer->inter_rect;
965 if (layer->state == RECT_LAYER_RECOLOR) {
966 color = layer->inter_color;
971 if (camera_fill_rect(
976 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
982 if (active && layer->selection >= 0) {
983 Rect rect = rects[layer->selection];
984 Color color = colors[layer->selection];
986 if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
987 rect = layer->inter_rect;
990 if (layer->state == RECT_LAYER_RECOLOR) {
991 color = layer->inter_color;
994 const Rect overlay_rect =
996 camera_rect(camera, rect),
997 -RECT_LAYER_SELECTION_THICCNESS * 0.5f);
998 const Color overlay_color = color_invert(color);
1001 if (camera_draw_thicc_rect_screen(
1005 RECT_LAYER_SELECTION_THICCNESS) < 0) {
1009 const Vec2f rect_id_pos = vec_sub(
1010 rect_position(rect),
1012 RECT_LAYER_ID_LABEL_SIZE,
1013 vec(0.0f, FONT_CHAR_HEIGHT)));
1016 if (layer->state == RECT_LAYER_ID_RENAME) {
1017 // ID renaming Edit Field
1018 if (edit_field_render_world(
1019 layer->id_edit_field,
1026 if (camera_render_text(
1028 ids + layer->selection * ENTITY_MAX_ID_SIZE,
1029 RECT_LAYER_ID_LABEL_SIZE,
1030 color_invert(color),
1038 const Color color = color_picker_rgba(&layer->color_picker);
1039 if (layer->state == RECT_LAYER_CREATE) {
1040 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
1045 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
1053 int rect_layer_event_recolor(RectLayer *layer,
1054 const SDL_Event *event,
1055 const Camera *camera,
1056 UndoHistory *undo_history)
1058 trace_assert(layer);
1059 trace_assert(event);
1060 trace_assert(camera);
1061 trace_assert(undo_history);
1062 trace_assert(layer->selection >= 0);
1064 int color_changed = 0;
1065 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
1069 if (color_changed) {
1070 layer->inter_color = color_picker_rgba(&layer->color_picker);
1072 if (!color_picker_drag(&layer->color_picker)) {
1073 UNDO_PUSH(undo_history, create_undo_update_context(layer));
1074 dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color);
1075 layer->state = RECT_LAYER_IDLE;
1082 int rect_layer_event(RectLayer *layer,
1083 const SDL_Event *event,
1084 const Camera *camera,
1085 UndoHistory *undo_history)
1087 trace_assert(layer);
1088 trace_assert(event);
1089 trace_assert(undo_history);
1091 switch (event->type) {
1092 case SDL_WINDOWEVENT: {
1093 switch (event->window.event) {
1094 case SDL_WINDOWEVENT_RESIZED: {
1095 grid_relayout(layer->grid, rect(0.0f, 0.0f,
1096 (float) event->window.data1,
1097 (float) event->window.data2));
1103 switch (layer->state) {
1104 case RECT_LAYER_IDLE:
1105 return rect_layer_event_idle(layer, event, camera, undo_history);
1107 case RECT_LAYER_CREATE:
1108 return rect_layer_event_create(layer, event, camera, undo_history);
1110 case RECT_LAYER_RESIZE:
1111 return rect_layer_event_resize(layer, event, camera, undo_history);
1113 case RECT_LAYER_MOVE:
1114 return rect_layer_event_move(layer, event, camera, undo_history);
1116 case RECT_LAYER_ID_RENAME:
1117 return rect_layer_event_id_rename(layer, event, camera, undo_history);
1119 case RECT_LAYER_RECOLOR:
1120 return rect_layer_event_recolor(layer, event, camera, undo_history);
1127 size_t rect_layer_count(const RectLayer *layer)
1129 return dynarray_count(layer->rects);
1132 const Rect *rect_layer_rects(const RectLayer *layer)
1134 return dynarray_data(layer->rects);
1137 const Color *rect_layer_colors(const RectLayer *layer)
1139 return dynarray_data(layer->colors);
1142 const char *rect_layer_ids(const RectLayer *layer)
1144 return dynarray_data(layer->ids);
1147 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
1149 trace_assert(layer);
1150 trace_assert(filedump);
1152 size_t n = dynarray_count(layer->ids);
1153 char *ids = dynarray_data(layer->ids);
1154 Rect *rects = dynarray_data(layer->rects);
1155 Color *colors = dynarray_data(layer->colors);
1156 Action *actions = dynarray_data(layer->actions);
1158 fprintf(filedump, "%zd\n", n);
1159 for (size_t i = 0; i < n; ++i) {
1160 fprintf(filedump, "%s %f %f %f %f ",
1161 ids + ENTITY_MAX_ID_SIZE * i,
1162 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
1163 color_hex_to_stream(colors[i], filedump);
1165 switch (actions[i].type) {
1166 case ACTION_NONE: {} break;
1168 case ACTION_TOGGLE_GOAL:
1169 case ACTION_HIDE_LABEL: {
1170 fprintf(filedump, " %d %.*s",
1171 (int)actions[i].type,
1172 ENTITY_MAX_ID_SIZE, actions[i].entity_id);
1174 case ACTION_N: break;
1177 fprintf(filedump, "\n");
1183 const Action *rect_layer_actions(const RectLayer *layer)
1185 return dynarray_data(layer->actions);