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;
58 char id[RECT_LAYER_ID_MAX_SIZE];
63 UndoContext rect_layer_create_undo_context(RectLayer *rect_layer, size_t index, UndoType type)
65 trace_assert(rect_layer);
66 trace_assert(index < dynarray_count(rect_layer->rects));
68 UndoContext undo_context = {
70 .rect = rect_layer->prev_rect,
71 .color = rect_layer->prev_color,
75 dynarray_copy_to(rect_layer->ids, undo_context.id, index);
81 void rect_layer_undo(void *layer, Context context)
84 RectLayer *rect_layer = layer;
86 trace_assert(sizeof(UndoContext) < CONTEXT_SIZE);
87 UndoContext *undo_context = (UndoContext *)context.data;
89 switch (undo_context->type) {
91 dynarray_delete_at(rect_layer->rects, undo_context->index);
92 dynarray_delete_at(rect_layer->colors, undo_context->index);
93 dynarray_delete_at(rect_layer->ids, undo_context->index);
94 rect_layer->selection = -1;
98 dynarray_insert_before(rect_layer->rects, undo_context->index, &undo_context->rect);
99 dynarray_insert_before(rect_layer->colors, undo_context->index, &undo_context->color);
100 dynarray_insert_before(rect_layer->ids, undo_context->index, &undo_context->id);
101 rect_layer->selection = -1;
105 dynarray_replace_at(rect_layer->rects, undo_context->index, &undo_context->rect);
106 dynarray_replace_at(rect_layer->colors, undo_context->index, &undo_context->color);
107 dynarray_replace_at(rect_layer->ids, undo_context->index, &undo_context->id);
112 static int rect_layer_add_rect(RectLayer *layer,
115 UndoHistory *undo_history)
119 size_t index = dynarray_count(layer->rects);
121 if (dynarray_push(layer->rects, &rect) < 0) {
125 if (dynarray_push(layer->colors, &color) < 0) {
129 char id[RECT_LAYER_ID_MAX_SIZE];
130 for (size_t i = 0; i < RECT_LAYER_ID_MAX_SIZE - 1; ++i) {
131 id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
133 id[RECT_LAYER_ID_MAX_SIZE - 1] = '\0';
135 if (dynarray_push(layer->ids, id)) {
139 UndoContext context =
140 rect_layer_create_undo_context(layer, index, UNDO_ADD);
147 &context, sizeof(context)));
152 // TODO(#956): rect_layer_rect_at doesn't return rectangles according to some z-order
153 static int rect_layer_rect_at(RectLayer *layer, Vec position)
157 const size_t n = dynarray_count(layer->rects);
158 Rect *rects = dynarray_data(layer->rects);
160 for (size_t i = 0; i < n; ++i) {
161 if (rect_contains_point(rects[i], position)) {
169 static Rect rect_layer_resize_anchor(const RectLayer *layer, const Camera *camera, size_t i)
171 Rect *rects = dynarray_data(layer->rects);
172 const Rect overlay_rect =
174 camera_rect(camera, rects[i]),
175 RECT_LAYER_SELECTION_THICCNESS * 0.5f);
178 overlay_rect.x + overlay_rect.w,
179 overlay_rect.y + overlay_rect.h,
180 RECT_LAYER_SELECTION_THICCNESS * 2.0f,
181 RECT_LAYER_SELECTION_THICCNESS * 2.0f);
184 static int rect_layer_delete_rect_at(RectLayer *layer,
186 UndoHistory *undo_history)
190 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);
368 &context, sizeof(context)));
370 layer->prev_rect = rects[layer->selection];
377 static int rect_layer_event_move(RectLayer *layer,
378 const SDL_Event *event,
379 const Camera *camera,
380 UndoHistory *undo_history)
384 trace_assert(camera);
386 Rect *rects = dynarray_data(layer->rects);
388 switch (event->type) {
389 case SDL_MOUSEMOTION: {
390 Point position = vec_sub(
397 trace_assert(layer->selection >= 0);
399 rects[layer->selection].x = position.x;
400 rects[layer->selection].y = position.y;
403 case SDL_MOUSEBUTTONUP: {
404 layer->state = RECT_LAYER_IDLE;
406 UndoContext context =
407 rect_layer_create_undo_context(layer, (size_t)layer->selection, UNDO_UPDATE);
414 &context, sizeof(context)));
416 layer->prev_rect = rects[layer->selection];
422 static int rect_layer_event_id_rename(RectLayer *layer,
423 const SDL_Event *event,
424 const Camera *camera,
425 UndoHistory *undo_history)
429 trace_assert(camera);
431 switch (event->type) {
433 switch (event->key.keysym.sym) {
435 char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
437 UndoContext undo_context =
438 rect_layer_create_undo_context(layer, (size_t)layer->selection, UNDO_UPDATE);
445 &undo_context, sizeof(undo_context)));
447 memset(id, 0, RECT_LAYER_ID_MAX_SIZE);
448 memcpy(id, edit_field_as_text(layer->id_edit_field), RECT_LAYER_ID_MAX_SIZE - 1);
449 layer->state = RECT_LAYER_IDLE;
454 layer->state = RECT_LAYER_IDLE;
461 return edit_field_event(layer->id_edit_field, event);
464 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
473 RectLayer *create_rect_layer(void)
475 Lt *lt = create_lt();
477 RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
483 layer->ids = PUSH_LT(
485 create_dynarray(sizeof(char) * RECT_LAYER_ID_MAX_SIZE),
487 if (layer->ids == NULL) {
491 layer->rects = PUSH_LT(
493 create_dynarray(sizeof(Rect)),
495 if (layer->rects == NULL) {
499 layer->colors = PUSH_LT(
501 create_dynarray(sizeof(Color)),
503 if (layer->colors == NULL) {
507 layer->id_edit_field = PUSH_LT(
513 if (layer->id_edit_field == NULL) {
517 Color init_color = rgba(1.0f, 0.0f, 0.0f, 1.0f);
518 layer->color_picker = create_color_picker_from_rgba(init_color);
519 layer->prev_color = init_color;
520 layer->selection = -1;
525 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream)
527 trace_assert(line_stream);
529 RectLayer *layer = create_rect_layer();
534 const char *line = line_stream_next(line_stream);
536 RETURN_LT(layer->lt, NULL);
540 if (sscanf(line, "%zu", &count) < 0) {
541 RETURN_LT(layer->lt, NULL);
544 for (size_t i = 0; i < count; ++i) {
545 line = line_stream_next(line_stream);
547 RETURN_LT(layer->lt, NULL);
552 char id[RECT_LAYER_ID_MAX_SIZE];
555 "%"STRINGIFY(RECT_LAYER_ID_MAX_SIZE)"s%f%f%f%f%6s\n",
560 RETURN_LT(layer->lt, NULL);
563 Color color = hexstr(hex);
565 dynarray_push(layer->rects, &rect);
566 dynarray_push(layer->ids, id);
567 dynarray_push(layer->colors, &color);
573 void destroy_rect_layer(RectLayer *layer)
576 RETURN_LT0(layer->lt);
579 int rect_layer_render(const RectLayer *layer, Camera *camera, int active)
582 trace_assert(camera);
584 const size_t n = dynarray_count(layer->rects);
585 Rect *rects = dynarray_data(layer->rects);
586 Color *colors = dynarray_data(layer->colors);
587 const char *ids = dynarray_data(layer->ids);
590 for (size_t i = 0; i < n; ++i) {
591 if (camera_fill_rect(
596 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
602 const Color color = color_picker_rgba(&layer->color_picker);
603 if (layer->state == RECT_LAYER_CREATE) {
604 if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
609 // ID renaming Edit Field
610 if (layer->state == RECT_LAYER_ID_RENAME) {
611 if (edit_field_render_screen(layer->id_edit_field, camera, vec(400.0f, 400.0f)) < 0) {
617 if (active && layer->selection >= 0) {
618 const Rect overlay_rect =
620 camera_rect(camera, rects[layer->selection]),
621 RECT_LAYER_SELECTION_THICCNESS * 0.5f);
622 const Color overlay_color = color_invert(colors[layer->selection]);
625 if (camera_fill_rect(
627 rects[layer->selection],
629 colors[layer->selection],
630 rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
634 if (camera_draw_thicc_rect_screen(
638 RECT_LAYER_SELECTION_THICCNESS) < 0) {
643 if (camera_render_text(
645 ids + layer->selection * RECT_LAYER_ID_MAX_SIZE,
647 color_invert(colors[layer->selection]),
648 rect_position(rects[layer->selection])) < 0) {
653 if (camera_fill_rect_screen(
655 rect_layer_resize_anchor(layer, camera, (size_t) layer->selection),
656 overlay_color) < 0) {
661 if (active && color_picker_render(&layer->color_picker, camera) < 0) {
668 int rect_layer_event(RectLayer *layer,
669 const SDL_Event *event,
670 const Camera *camera,
671 UndoHistory *undo_history)
675 trace_assert(undo_history);
677 int color_changed = 0;
678 if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
683 if (layer->selection >= 0) {
684 Color *colors = dynarray_data(layer->colors);
685 colors[layer->selection] = color_picker_rgba(&layer->color_picker);
687 if (!color_picker_drag(&layer->color_picker)) {
688 UndoContext context =
689 rect_layer_create_undo_context(layer, (size_t)layer->selection, UNDO_UPDATE);
698 layer->prev_color = colors[layer->selection];
705 switch (layer->state) {
706 case RECT_LAYER_IDLE:
707 return rect_layer_event_idle(layer, event, camera, undo_history);
709 case RECT_LAYER_CREATE:
710 return rect_layer_event_create(layer, event, camera, undo_history);
712 case RECT_LAYER_RESIZE:
713 return rect_layer_event_resize(layer, event, camera, undo_history);
715 case RECT_LAYER_MOVE:
716 return rect_layer_event_move(layer, event, camera, undo_history);
718 case RECT_LAYER_ID_RENAME:
719 return rect_layer_event_id_rename(layer, event, camera, undo_history);
725 size_t rect_layer_count(const RectLayer *layer)
727 return dynarray_count(layer->rects);
730 const Rect *rect_layer_rects(const RectLayer *layer)
732 return dynarray_data(layer->rects);
735 const Color *rect_layer_colors(const RectLayer *layer)
737 return dynarray_data(layer->colors);
740 const char *rect_layer_ids(const RectLayer *layer)
742 return dynarray_data(layer->ids);
745 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
748 trace_assert(filedump);
750 size_t n = dynarray_count(layer->ids);
751 char *ids = dynarray_data(layer->ids);
752 Rect *rects = dynarray_data(layer->rects);
753 Color *colors = dynarray_data(layer->colors);
755 fprintf(filedump, "%zd\n", n);
756 for (size_t i = 0; i < n; ++i) {
757 fprintf(filedump, "%s %f %f %f %f ",
758 ids + RECT_LAYER_ID_MAX_SIZE * i,
759 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
760 color_hex_to_stream(colors[i], filedump);
761 fprintf(filedump, "\n");