1 #include "game/camera.h"
3 #include "system/stacktrace.h"
4 #include "system/nth_alloc.h"
5 #include "system/log.h"
8 #include "rect_layer.h"
10 #include "system/line_stream.h"
11 #include "color_picker.h"
12 #include "system/str.h"
13 #include "ui/edit_field.h"
14 #include "undo_history.h"
16 #define RECT_LAYER_ID_MAX_SIZE 36
17 #define RECT_LAYER_SELECTION_THICCNESS 10.0f
18 #define CREATE_AREA_THRESHOLD 10.0
20 // TODO(#1034): Can we use a single Context for everything in RectLayer
25 // TODO(#955): Rectangles in Level Editor have only one resize anchor to work with
28 // TODO(#1035): id renaming in RectLayer is ugly
38 ColorPicker color_picker;
43 Edit_field *id_edit_field;
44 // TODO: RectLayer should use intermediate values instead of previous ones
59 char id[RECT_LAYER_ID_MAX_SIZE];
64 UndoContext rect_layer_create_undo_context(RectLayer *rect_layer, size_t index, UndoType type)
66 trace_assert(rect_layer);
67 trace_assert(index < dynarray_count(rect_layer->rects));
69 UndoContext undo_context = {
71 .rect = rect_layer->prev_rect,
72 .color = rect_layer->prev_color,
76 dynarray_copy_to(rect_layer->ids, undo_context.id, index);
82 void rect_layer_undo(void *layer, void *context, size_t context_size)
85 trace_assert(context);
86 trace_assert(sizeof(UndoContext) == context_size);
88 RectLayer *rect_layer = layer;
89 UndoContext *undo_context = context;
91 switch (undo_context->type) {
93 dynarray_delete_at(rect_layer->rects, undo_context->index);
94 dynarray_delete_at(rect_layer->colors, undo_context->index);
95 dynarray_delete_at(rect_layer->ids, undo_context->index);
96 rect_layer->selection = -1;
100 dynarray_insert_before(rect_layer->rects, undo_context->index, &undo_context->rect);
101 dynarray_insert_before(rect_layer->colors, undo_context->index, &undo_context->color);
102 dynarray_insert_before(rect_layer->ids, undo_context->index, &undo_context->id);
103 rect_layer->selection = -1;
107 dynarray_replace_at(rect_layer->rects, undo_context->index, &undo_context->rect);
108 dynarray_replace_at(rect_layer->colors, undo_context->index, &undo_context->color);
109 dynarray_replace_at(rect_layer->ids, undo_context->index, &undo_context->id);
114 static int rect_layer_add_rect(RectLayer *layer,
117 UndoHistory *undo_history)
121 size_t index = dynarray_count(layer->rects);
123 if (dynarray_push(layer->rects, &rect) < 0) {
127 if (dynarray_push(layer->colors, &color) < 0) {
131 char id[RECT_LAYER_ID_MAX_SIZE];
132 for (size_t i = 0; i < RECT_LAYER_ID_MAX_SIZE - 1; ++i) {
133 id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
135 id[RECT_LAYER_ID_MAX_SIZE - 1] = '\0';
137 if (dynarray_push(layer->ids, id)) {
141 UndoContext context =
142 rect_layer_create_undo_context(layer, index, UNDO_ADD);
148 &context, sizeof(context));
153 // TODO(#956): rect_layer_rect_at doesn't return rectangles according to some z-order
154 static int rect_layer_rect_at(RectLayer *layer, Vec position)
158 const size_t n = dynarray_count(layer->rects);
159 Rect *rects = dynarray_data(layer->rects);
161 for (size_t i = 0; i < n; ++i) {
162 if (rect_contains_point(rects[i], position)) {
170 static Rect rect_layer_resize_anchor(const RectLayer *layer, const Camera *camera, size_t i)
172 Rect *rects = dynarray_data(layer->rects);
173 const Rect overlay_rect =
175 camera_rect(camera, rects[i]),
176 RECT_LAYER_SELECTION_THICCNESS * 0.5f);
179 overlay_rect.x + overlay_rect.w,
180 overlay_rect.y + overlay_rect.h,
181 RECT_LAYER_SELECTION_THICCNESS * 2.0f,
182 RECT_LAYER_SELECTION_THICCNESS * 2.0f);
185 static int rect_layer_delete_rect_at(RectLayer *layer,
187 UndoHistory *undo_history)
191 UndoContext context = rect_layer_create_undo_context(layer, i, UNDO_DELETE);
197 &context, sizeof(context));
199 dynarray_delete_at(layer->rects, i);
200 dynarray_delete_at(layer->colors, i);
201 dynarray_delete_at(layer->ids, i);
206 static int rect_layer_event_idle(RectLayer *layer,
207 const SDL_Event *event,
208 const Camera *camera,
209 UndoHistory *undo_history)
213 trace_assert(camera);
215 switch (event->type) {
216 case SDL_MOUSEBUTTONDOWN: {
217 switch (event->button.button) {
218 case SDL_BUTTON_LEFT: {
219 Point position = camera_map_screen(
223 int rect_at_position =
224 rect_layer_rect_at(layer, position);
226 if (rect_at_position >= 0) {
227 Rect *rects = dynarray_data(layer->rects);
228 Color *colors = dynarray_data(layer->colors);
229 layer->selection = rect_at_position;
230 layer->state = RECT_LAYER_MOVE;
235 rects[layer->selection].x,
236 rects[layer->selection].y));
237 layer->color_picker =
238 create_color_picker_from_rgba(colors[rect_at_position]);
239 layer->prev_color = colors[rect_at_position];
240 layer->prev_rect = rects[rect_at_position];
241 } else if (layer->selection >= 0 && rect_contains_point(
242 rect_layer_resize_anchor(
245 (size_t)layer->selection),
247 (float) event->button.x,
248 (float) event->button.y))) {
249 layer->state = RECT_LAYER_RESIZE;
251 layer->selection = rect_at_position;
253 if (layer->selection < 0) {
254 layer->state = RECT_LAYER_CREATE;
255 layer->create_begin = position;
256 layer->create_end = position;
264 switch (event->key.keysym.sym) {
266 if (layer->selection >= 0) {
267 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
268 layer->selection = -1;
273 if (layer->selection >= 0) {
274 const char *ids = dynarray_data(layer->ids);
275 layer->state = RECT_LAYER_ID_RENAME;
277 layer->id_edit_field,
278 ids + layer->selection * RECT_LAYER_ID_MAX_SIZE);
279 SDL_StartTextInput();
289 static int rect_layer_event_create(RectLayer *layer,
290 const SDL_Event *event,
291 const Camera *camera,
292 UndoHistory *undo_history)
296 trace_assert(camera);
298 switch (event->type) {
299 case SDL_MOUSEBUTTONUP: {
300 switch (event->button.button) {
301 case SDL_BUTTON_LEFT: {
302 const Rect real_rect =
306 const float area = real_rect.w * real_rect.h;
308 if (area >= CREATE_AREA_THRESHOLD) {
312 color_picker_rgba(&layer->color_picker),
315 log_info("The area is too small %f. Such small box won't be created.\n", area);
317 layer->state = RECT_LAYER_IDLE;
322 case SDL_MOUSEMOTION: {
323 layer->create_end = camera_map_screen(
332 static int rect_layer_event_resize(RectLayer *layer,
333 const SDL_Event *event,
334 const Camera *camera,
335 UndoHistory *undo_history)
339 trace_assert(camera);
341 Rect *rects = dynarray_data(layer->rects);
343 switch (event->type) {
344 case SDL_MOUSEMOTION: {
345 trace_assert(layer->selection >= 0);
346 rects[layer->selection] = rect_from_points(
347 vec(rects[layer->selection].x, rects[layer->selection].y),
353 vec(RECT_LAYER_SELECTION_THICCNESS * -0.5f,
354 RECT_LAYER_SELECTION_THICCNESS * -0.5f)));
357 case SDL_MOUSEBUTTONUP: {
358 layer->state = RECT_LAYER_IDLE;
360 UndoContext context =
361 rect_layer_create_undo_context(layer, (size_t)layer->selection, UNDO_UPDATE);
367 &context, sizeof(context));
369 layer->prev_rect = rects[layer->selection];
376 static int rect_layer_event_move(RectLayer *layer,
377 const SDL_Event *event,
378 const Camera *camera,
379 UndoHistory *undo_history)
383 trace_assert(camera);
385 Rect *rects = dynarray_data(layer->rects);
387 switch (event->type) {
388 case SDL_MOUSEMOTION: {
389 Point position = vec_sub(
396 trace_assert(layer->selection >= 0);
398 rects[layer->selection].x = position.x;
399 rects[layer->selection].y = position.y;
402 case SDL_MOUSEBUTTONUP: {
403 layer->state = RECT_LAYER_IDLE;
405 UndoContext context =
406 rect_layer_create_undo_context(layer, (size_t)layer->selection, UNDO_UPDATE);
412 &context, sizeof(context));
414 layer->prev_rect = rects[layer->selection];
420 static int rect_layer_event_id_rename(RectLayer *layer,
421 const SDL_Event *event,
422 const Camera *camera,
423 UndoHistory *undo_history)
427 trace_assert(camera);
429 switch (event->type) {
431 switch (event->key.keysym.sym) {
433 char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
435 UndoContext undo_context =
436 rect_layer_create_undo_context(layer, (size_t)layer->selection, UNDO_UPDATE);
442 &undo_context, sizeof(undo_context));
444 memset(id, 0, RECT_LAYER_ID_MAX_SIZE);
445 memcpy(id, edit_field_as_text(layer->id_edit_field), RECT_LAYER_ID_MAX_SIZE - 1);
446 layer->state = RECT_LAYER_IDLE;
451 layer->state = RECT_LAYER_IDLE;
458 return edit_field_event(layer->id_edit_field, event);
461 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
470 RectLayer *create_rect_layer(void)
472 Lt *lt = create_lt();
474 RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
480 layer->ids = PUSH_LT(
482 create_dynarray(sizeof(char) * RECT_LAYER_ID_MAX_SIZE),
484 if (layer->ids == NULL) {
488 layer->rects = PUSH_LT(
490 create_dynarray(sizeof(Rect)),
492 if (layer->rects == NULL) {
496 layer->colors = PUSH_LT(
498 create_dynarray(sizeof(Color)),
500 if (layer->colors == NULL) {
504 layer->id_edit_field = PUSH_LT(
510 if (layer->id_edit_field == NULL) {
514 Color init_color = rgba(1.0f, 0.0f, 0.0f, 1.0f);
515 layer->color_picker = create_color_picker_from_rgba(init_color);
516 layer->prev_color = init_color;
517 layer->selection = -1;
522 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream)
524 trace_assert(line_stream);
526 RectLayer *layer = create_rect_layer();
531 const char *line = line_stream_next(line_stream);
533 RETURN_LT(layer->lt, NULL);
537 if (sscanf(line, "%zu", &count) < 0) {
538 RETURN_LT(layer->lt, NULL);
541 for (size_t i = 0; i < count; ++i) {
542 line = line_stream_next(line_stream);
544 RETURN_LT(layer->lt, NULL);
549 char id[RECT_LAYER_ID_MAX_SIZE];
552 "%"STRINGIFY(RECT_LAYER_ID_MAX_SIZE)"s%f%f%f%f%6s\n",
557 RETURN_LT(layer->lt, NULL);
560 Color color = hexstr(hex);
562 dynarray_push(layer->rects, &rect);
563 dynarray_push(layer->ids, id);
564 dynarray_push(layer->colors, &color);
570 void destroy_rect_layer(RectLayer *layer)
573 RETURN_LT0(layer->lt);
576 int rect_layer_render(const RectLayer *layer, Camera *camera, int active)
579 trace_assert(camera);
581 const size_t n = dynarray_count(layer->rects);
582 Rect *rects = dynarray_data(layer->rects);
583 Color *colors = dynarray_data(layer->colors);
584 const char *ids = dynarray_data(layer->ids);
587 for (size_t i = 0; i < n; ++i) {
588 if (camera_fill_rect(
593 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
599 const Color color = color_picker_rgba(&layer->color_picker);
600 if (layer->state == RECT_LAYER_CREATE) {
601 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
606 // ID renaming Edit Field
607 if (layer->state == RECT_LAYER_ID_RENAME) {
608 if (edit_field_render_screen(layer->id_edit_field, camera, vec(400.0f, 400.0f)) < 0) {
614 if (active && layer->selection >= 0) {
615 const Rect overlay_rect =
617 camera_rect(camera, rects[layer->selection]),
618 RECT_LAYER_SELECTION_THICCNESS * 0.5f);
619 const Color overlay_color = color_invert(colors[layer->selection]);
622 if (camera_fill_rect(
624 rects[layer->selection],
626 colors[layer->selection],
627 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
631 if (camera_draw_thicc_rect_screen(
635 RECT_LAYER_SELECTION_THICCNESS) < 0) {
640 if (camera_render_text(
642 ids + layer->selection * RECT_LAYER_ID_MAX_SIZE,
644 color_invert(colors[layer->selection]),
645 rect_position(rects[layer->selection])) < 0) {
650 if (camera_fill_rect_screen(
652 rect_layer_resize_anchor(layer, camera, (size_t) layer->selection),
653 overlay_color) < 0) {
658 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
665 int rect_layer_event(RectLayer *layer,
666 const SDL_Event *event,
667 const Camera *camera,
668 UndoHistory *undo_history)
672 trace_assert(undo_history);
674 int color_changed = 0;
675 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
680 if (layer->selection >= 0) {
681 Color *colors = dynarray_data(layer->colors);
682 colors[layer->selection] = color_picker_rgba(&layer->color_picker);
684 if (!color_picker_drag(&layer->color_picker)) {
685 UndoContext context =
686 rect_layer_create_undo_context(layer, (size_t)layer->selection, UNDO_UPDATE);
694 layer->prev_color = colors[layer->selection];
701 switch (layer->state) {
702 case RECT_LAYER_IDLE:
703 return rect_layer_event_idle(layer, event, camera, undo_history);
705 case RECT_LAYER_CREATE:
706 return rect_layer_event_create(layer, event, camera, undo_history);
708 case RECT_LAYER_RESIZE:
709 return rect_layer_event_resize(layer, event, camera, undo_history);
711 case RECT_LAYER_MOVE:
712 return rect_layer_event_move(layer, event, camera, undo_history);
714 case RECT_LAYER_ID_RENAME:
715 return rect_layer_event_id_rename(layer, event, camera, undo_history);
721 size_t rect_layer_count(const RectLayer *layer)
723 return dynarray_count(layer->rects);
726 const Rect *rect_layer_rects(const RectLayer *layer)
728 return dynarray_data(layer->rects);
731 const Color *rect_layer_colors(const RectLayer *layer)
733 return dynarray_data(layer->colors);
736 const char *rect_layer_ids(const RectLayer *layer)
738 return dynarray_data(layer->ids);
741 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
744 trace_assert(filedump);
746 size_t n = dynarray_count(layer->ids);
747 char *ids = dynarray_data(layer->ids);
748 Rect *rects = dynarray_data(layer->rects);
749 Color *colors = dynarray_data(layer->colors);
751 fprintf(filedump, "%zd\n", n);
752 for (size_t i = 0; i < n; ++i) {
753 fprintf(filedump, "%s %f %f %f %f ",
754 ids + RECT_LAYER_ID_MAX_SIZE * i,
755 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
756 color_hex_to_stream(colors[i], filedump);
757 fprintf(filedump, "\n");