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: 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: id renaming in RectLayer is ugly
38 ColorPicker color_picker;
43 Edit_field *id_edit_field;
48 typedef int (*EventHandler)(RectLayer *layer, const SDL_Event *event, const Camera *camera);
51 void rect_layer_undo_add(void *layer, Context context)
54 RectLayer *rect_layer = layer;
56 trace_assert(sizeof(size_t) < CONTEXT_SIZE);
57 size_t *index = (size_t *)context.data;
59 trace_assert(*index < dynarray_count(rect_layer->rects));
60 dynarray_delete_at(rect_layer->rects, *index);
61 dynarray_delete_at(rect_layer->colors, *index);
62 dynarray_delete_at(rect_layer->ids, *index);
65 static int rect_layer_add_rect(RectLayer *layer,
68 UndoHistory *undo_history)
72 size_t index = dynarray_count(layer->rects);
74 if (dynarray_push(layer->rects, &rect) < 0) {
78 if (dynarray_push(layer->colors, &color) < 0) {
82 char id[RECT_LAYER_ID_MAX_SIZE];
83 for (size_t i = 0; i < RECT_LAYER_ID_MAX_SIZE - 1; ++i) {
84 id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
86 id[RECT_LAYER_ID_MAX_SIZE - 1] = '\0';
88 if (dynarray_push(layer->ids, id)) {
97 &index, sizeof(index)));
102 // TODO(#956): rect_layer_rect_at doesn't return rectangles according to some z-order
103 static int rect_layer_rect_at(RectLayer *layer, Vec position)
107 const size_t n = dynarray_count(layer->rects);
108 Rect *rects = dynarray_data(layer->rects);
110 for (size_t i = 0; i < n; ++i) {
111 if (rect_contains_point(rects[i], position)) {
119 static Rect rect_layer_resize_anchor(const RectLayer *layer, const Camera *camera, size_t i)
121 Rect *rects = dynarray_data(layer->rects);
122 const Rect overlay_rect =
124 camera_rect(camera, rects[i]),
125 RECT_LAYER_SELECTION_THICCNESS * 0.5f);
128 overlay_rect.x + overlay_rect.w,
129 overlay_rect.y + overlay_rect.h,
130 RECT_LAYER_SELECTION_THICCNESS * 2.0f,
131 RECT_LAYER_SELECTION_THICCNESS * 2.0f);
137 char id[RECT_LAYER_ID_MAX_SIZE];
142 DeleteContext rect_layer_create_delete_context(RectLayer *layer, size_t index)
145 trace_assert(index < dynarray_count(layer->rects));
147 DeleteContext context = {
148 .rect = *((Rect *)dynarray_pointer_at(layer->rects, index)),
149 .color = *((Color *)dynarray_pointer_at(layer->colors, index)),
154 dynarray_pointer_at(layer->ids, index),
155 RECT_LAYER_ID_MAX_SIZE);
157 context.index = index;
163 void rect_layer_undo_delete(void *layer, Context context)
166 RectLayer *rect_layer = layer;
168 trace_assert(sizeof(DeleteContext) < CONTEXT_SIZE);
169 DeleteContext *delete_context = (DeleteContext *)context.data;
171 dynarray_insert_before(rect_layer->rects, delete_context->index, &delete_context->rect);
172 dynarray_insert_before(rect_layer->colors, delete_context->index, &delete_context->color);
173 dynarray_insert_before(rect_layer->ids, delete_context->index, &delete_context->id);
176 static int rect_layer_delete_rect_at(RectLayer *layer,
178 UndoHistory *undo_history)
182 DeleteContext context = rect_layer_create_delete_context(layer, i);
188 rect_layer_undo_delete,
189 &context, sizeof(context)));
191 dynarray_delete_at(layer->rects, i);
192 dynarray_delete_at(layer->colors, i);
193 dynarray_delete_at(layer->ids, i);
198 static int rect_layer_event_idle(RectLayer *layer,
199 const SDL_Event *event,
200 const Camera *camera,
201 UndoHistory *undo_history)
205 trace_assert(camera);
207 switch (event->type) {
208 case SDL_MOUSEBUTTONDOWN: {
209 switch (event->button.button) {
210 case SDL_BUTTON_LEFT: {
211 Point position = camera_map_screen(
215 int rect_at_position =
216 rect_layer_rect_at(layer, position);
218 if (rect_at_position >= 0) {
219 Rect *rects = dynarray_data(layer->rects);
220 Color *colors = dynarray_data(layer->colors);
221 layer->selection = rect_at_position;
222 layer->state = RECT_LAYER_MOVE;
227 rects[layer->selection].x,
228 rects[layer->selection].y));
229 layer->color_picker =
230 create_color_picker_from_rgba(colors[rect_at_position]);
231 layer->prev_color = colors[rect_at_position];
232 layer->prev_rect = rects[rect_at_position];
233 } else if (layer->selection >= 0 && rect_contains_point(
234 rect_layer_resize_anchor(
237 (size_t)layer->selection),
239 (float) event->button.x,
240 (float) event->button.y))) {
241 layer->state = RECT_LAYER_RESIZE;
243 layer->selection = rect_at_position;
245 if (layer->selection < 0) {
246 layer->state = RECT_LAYER_CREATE;
247 layer->create_begin = position;
248 layer->create_end = position;
256 switch (event->key.keysym.sym) {
258 if (layer->selection >= 0) {
259 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
260 layer->selection = -1;
265 if (layer->selection >= 0) {
266 const char *ids = dynarray_data(layer->ids);
267 layer->state = RECT_LAYER_ID_RENAME;
269 layer->id_edit_field,
270 ids + layer->selection * RECT_LAYER_ID_MAX_SIZE);
271 SDL_StartTextInput();
281 static int rect_layer_event_create(RectLayer *layer,
282 const SDL_Event *event,
283 const Camera *camera,
284 UndoHistory *undo_history)
288 trace_assert(camera);
290 switch (event->type) {
291 case SDL_MOUSEBUTTONUP: {
292 switch (event->button.button) {
293 case SDL_BUTTON_LEFT: {
294 const Rect real_rect =
298 const float area = real_rect.w * real_rect.h;
300 if (area >= CREATE_AREA_THRESHOLD) {
304 color_picker_rgba(&layer->color_picker),
307 log_info("The area is too small %f. Such small box won't be created.\n", area);
309 layer->state = RECT_LAYER_IDLE;
314 case SDL_MOUSEMOTION: {
315 layer->create_end = camera_map_screen(
330 void rect_layer_undo_rect(void *layer, Context context)
333 RectLayer *rect_layer = layer;
335 trace_assert(sizeof(RectContext) <= CONTEXT_SIZE);
336 RectContext *rect_context = (RectContext *)context.data;
337 trace_assert(rect_context->index < dynarray_count(rect_layer->rects));
342 &rect_context->rect);
345 static int rect_layer_event_resize(RectLayer *layer,
346 const SDL_Event *event,
347 const Camera *camera,
348 UndoHistory *undo_history)
352 trace_assert(camera);
354 Rect *rects = dynarray_data(layer->rects);
356 switch (event->type) {
357 case SDL_MOUSEMOTION: {
358 trace_assert(layer->selection >= 0);
359 rects[layer->selection] = rect_from_points(
360 vec(rects[layer->selection].x, rects[layer->selection].y),
366 vec(RECT_LAYER_SELECTION_THICCNESS * -0.5f,
367 RECT_LAYER_SELECTION_THICCNESS * -0.5f)));
370 case SDL_MOUSEBUTTONUP: {
371 layer->state = RECT_LAYER_IDLE;
373 RectContext context = {
374 .index = (size_t) layer->selection,
375 .rect = layer->prev_rect
382 rect_layer_undo_rect,
383 &context, sizeof(context)));
385 layer->prev_rect = rects[layer->selection];
392 static int rect_layer_event_move(RectLayer *layer,
393 const SDL_Event *event,
394 const Camera *camera,
395 UndoHistory *undo_history)
399 trace_assert(camera);
401 Rect *rects = dynarray_data(layer->rects);
403 switch (event->type) {
404 case SDL_MOUSEMOTION: {
405 Point position = vec_sub(
412 trace_assert(layer->selection >= 0);
414 rects[layer->selection].x = position.x;
415 rects[layer->selection].y = position.y;
418 case SDL_MOUSEBUTTONUP: {
419 layer->state = RECT_LAYER_IDLE;
421 RectContext context = {
422 .index = (size_t)layer->selection,
423 .rect = layer->prev_rect
430 rect_layer_undo_rect,
431 &context, sizeof(context)));
433 layer->prev_rect = rects[layer->selection];
441 char id[RECT_LAYER_ID_MAX_SIZE];
445 void rect_layer_undo_rename_id(void *layer, Context context)
448 RectLayer *rect_layer = layer;
450 RenameContext *rename_context = (RenameContext *)context.data;
454 rename_context->index,
458 static int rect_layer_event_id_rename(RectLayer *layer,
459 const SDL_Event *event,
460 const Camera *camera,
461 UndoHistory *undo_history)
465 trace_assert(camera);
467 switch (event->type) {
469 switch (event->key.keysym.sym) {
471 char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
473 RenameContext context = {
474 .index = (size_t)layer->selection,
476 memcpy(context.id, id, RECT_LAYER_ID_MAX_SIZE);
482 rect_layer_undo_rename_id,
483 &context, sizeof(context)));
485 memset(id, 0, RECT_LAYER_ID_MAX_SIZE);
486 memcpy(id, edit_field_as_text(layer->id_edit_field), RECT_LAYER_ID_MAX_SIZE - 1);
487 layer->state = RECT_LAYER_IDLE;
492 layer->state = RECT_LAYER_IDLE;
499 return edit_field_event(layer->id_edit_field, event);
502 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
511 RectLayer *create_rect_layer(void)
513 Lt *lt = create_lt();
515 RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
521 layer->ids = PUSH_LT(
523 create_dynarray(sizeof(char) * RECT_LAYER_ID_MAX_SIZE),
525 if (layer->ids == NULL) {
529 layer->rects = PUSH_LT(
531 create_dynarray(sizeof(Rect)),
533 if (layer->rects == NULL) {
537 layer->colors = PUSH_LT(
539 create_dynarray(sizeof(Color)),
541 if (layer->colors == NULL) {
545 layer->id_edit_field = PUSH_LT(
551 if (layer->id_edit_field == NULL) {
555 Color init_color = rgba(1.0f, 0.0f, 0.0f, 1.0f);
556 layer->color_picker = create_color_picker_from_rgba(init_color);
557 layer->prev_color = init_color;
558 layer->selection = -1;
563 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream)
565 trace_assert(line_stream);
567 RectLayer *layer = create_rect_layer();
572 const char *line = line_stream_next(line_stream);
574 RETURN_LT(layer->lt, NULL);
578 if (sscanf(line, "%zu", &count) < 0) {
579 RETURN_LT(layer->lt, NULL);
582 for (size_t i = 0; i < count; ++i) {
583 line = line_stream_next(line_stream);
585 RETURN_LT(layer->lt, NULL);
590 char id[RECT_LAYER_ID_MAX_SIZE];
593 "%"STRINGIFY(RECT_LAYER_ID_MAX_SIZE)"s%f%f%f%f%6s\n",
598 RETURN_LT(layer->lt, NULL);
601 Color color = hexstr(hex);
603 dynarray_push(layer->rects, &rect);
604 dynarray_push(layer->ids, id);
605 dynarray_push(layer->colors, &color);
611 void destroy_rect_layer(RectLayer *layer)
614 RETURN_LT0(layer->lt);
617 int rect_layer_render(const RectLayer *layer, Camera *camera, int active)
620 trace_assert(camera);
622 const size_t n = dynarray_count(layer->rects);
623 Rect *rects = dynarray_data(layer->rects);
624 Color *colors = dynarray_data(layer->colors);
625 const char *ids = dynarray_data(layer->ids);
628 for (size_t i = 0; i < n; ++i) {
629 if (camera_fill_rect(
634 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
640 const Color color = color_picker_rgba(&layer->color_picker);
641 if (layer->state == RECT_LAYER_CREATE) {
642 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
647 // ID renaming Edit Field
648 if (layer->state == RECT_LAYER_ID_RENAME) {
649 if (edit_field_render_screen(layer->id_edit_field, camera, vec(400.0f, 400.0f)) < 0) {
655 if (active && layer->selection >= 0) {
656 const Rect overlay_rect =
658 camera_rect(camera, rects[layer->selection]),
659 RECT_LAYER_SELECTION_THICCNESS * 0.5f);
660 const Color overlay_color = color_invert(colors[layer->selection]);
663 if (camera_fill_rect(
665 rects[layer->selection],
667 colors[layer->selection],
668 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
672 if (camera_draw_thicc_rect_screen(
676 RECT_LAYER_SELECTION_THICCNESS) < 0) {
681 if (camera_render_text(
683 ids + layer->selection * RECT_LAYER_ID_MAX_SIZE,
685 color_invert(colors[layer->selection]),
686 rect_position(rects[layer->selection])) < 0) {
691 if (camera_fill_rect_screen(
693 rect_layer_resize_anchor(layer, camera, (size_t) layer->selection),
694 overlay_color) < 0) {
699 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
712 void rect_layer_undo_color(void *layer, Context context)
715 RectLayer *rect_layer = layer;
717 trace_assert(sizeof(ColorContext) < CONTEXT_SIZE);
718 ColorContext *color_context = (ColorContext *)context.data;
719 trace_assert(color_context->index < dynarray_count(rect_layer->rects));
723 color_context->index,
724 &color_context->color);
727 int rect_layer_event(RectLayer *layer,
728 const SDL_Event *event,
729 const Camera *camera,
730 UndoHistory *undo_history)
734 trace_assert(undo_history);
736 int color_changed = 0;
737 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
742 if (layer->selection >= 0) {
743 Color *colors = dynarray_data(layer->colors);
744 colors[layer->selection] = color_picker_rgba(&layer->color_picker);
746 if (!color_picker_drag(&layer->color_picker)) {
747 ColorContext context = {
748 .color = layer->prev_color,
749 .index = (size_t) layer->selection
756 rect_layer_undo_color,
759 layer->prev_color = colors[layer->selection];
766 switch (layer->state) {
767 case RECT_LAYER_IDLE:
768 return rect_layer_event_idle(layer, event, camera, undo_history);
770 case RECT_LAYER_CREATE:
771 return rect_layer_event_create(layer, event, camera, undo_history);
773 case RECT_LAYER_RESIZE:
774 return rect_layer_event_resize(layer, event, camera, undo_history);
776 case RECT_LAYER_MOVE:
777 return rect_layer_event_move(layer, event, camera, undo_history);
779 case RECT_LAYER_ID_RENAME:
780 return rect_layer_event_id_rename(layer, event, camera, undo_history);
786 size_t rect_layer_count(const RectLayer *layer)
788 return dynarray_count(layer->rects);
791 const Rect *rect_layer_rects(const RectLayer *layer)
793 return dynarray_data(layer->rects);
796 const Color *rect_layer_colors(const RectLayer *layer)
798 return dynarray_data(layer->colors);
801 const char *rect_layer_ids(const RectLayer *layer)
803 return dynarray_data(layer->ids);
806 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
809 trace_assert(filedump);
811 size_t n = dynarray_count(layer->ids);
812 char *ids = dynarray_data(layer->ids);
813 Rect *rects = dynarray_data(layer->rects);
814 Color *colors = dynarray_data(layer->colors);
816 fprintf(filedump, "%zd\n", n);
817 for (size_t i = 0; i < n; ++i) {
818 fprintf(filedump, "%s %f %f %f %f ",
819 ids + RECT_LAYER_ID_MAX_SIZE * i,
820 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
821 color_hex_to_stream(colors[i], filedump);
822 fprintf(filedump, "\n");