]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/rect_layer.c
7b5a8eec9d2a8cf0077d77e398525fd54869004a
[nothing.git] / src / game / level / level_editor / rect_layer.c
1 #include <errno.h>
2
3 #include "game/camera.h"
4 #include "system/lt.h"
5 #include "system/stacktrace.h"
6 #include "system/nth_alloc.h"
7 #include "system/log.h"
8 #include "math/rect.h"
9 #include "color.h"
10 #include "rect_layer.h"
11 #include "dynarray.h"
12 #include "system/line_stream.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"
19
20 #define RECT_LAYER_SELECTION_THICCNESS 15.0f
21 #define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
22 #define CREATE_AREA_THRESHOLD 10.0
23 #define RECT_LAYER_GRID_ROWS 3
24 #define RECT_LAYER_GRID_COLUMNS 4
25
26 static int clipboard = 0;
27 static Rect clipboard_rect;
28 static Color clipboard_color;
29
30 typedef enum {
31     RECT_LAYER_IDLE = 0,
32     RECT_LAYER_CREATE,
33     // TODO(#955): Rectangles in Level Editor have only one resize anchor to work with
34     // TODO: different cursor image in resize mode
35     RECT_LAYER_RESIZE,
36     RECT_LAYER_MOVE,
37     RECT_LAYER_ID_RENAME,
38     RECT_LAYER_RECOLOR
39 } RectLayerState;
40
41 struct RectLayer {
42     Lt *lt;
43     RectLayerState state;
44     int resize_mask;
45     Dynarray *ids;
46     Dynarray *rects;
47     Dynarray *colors;
48     Dynarray *actions;
49     ColorPicker color_picker;
50     ActionPicker action_picker;
51     Vec2f create_begin;
52     Vec2f create_end;
53     int selection;
54     Vec2f move_anchor;          // The mouse offset from the left-top
55                                 // corner of the rect during moving it
56     Edit_field *id_edit_field;
57     Color inter_color;
58     Rect inter_rect;
59     int id_name_counter;
60     const char *id_name_prefix;
61     Grid *grid;
62 };
63
64 typedef enum {
65     UNDO_ADD,
66     UNDO_DELETE,
67     UNDO_UPDATE,
68     UNDO_SWAP
69 } UndoType;
70
71 // Delete, Update
72 typedef struct {
73     UndoType type;
74     RectLayer *layer;
75     size_t index;
76     Rect rect;
77     Color color;
78     Action action;
79     char id[ENTITY_MAX_ID_SIZE];
80 } UndoElementContext;
81
82 // Add
83 typedef struct {
84     UndoType type;
85     RectLayer *layer;
86     size_t index;
87 } UndoAddContext;
88
89 // Swap
90 typedef struct {
91     UndoType type;
92     RectLayer *layer;
93     size_t index1;
94     size_t index2;
95 } UndoSwapContext;
96
97 typedef union {
98     UndoType type;
99     UndoAddContext add;
100     UndoElementContext element;
101     UndoSwapContext swap;
102 } UndoContext;
103
104 static
105 UndoContext create_undo_add_context(RectLayer *layer, size_t index)
106 {
107     trace_assert(layer);
108     trace_assert(index < dynarray_count(layer->rects));
109
110     UndoContext undo_context;
111     undo_context.add.type = UNDO_ADD;
112     undo_context.add.layer = layer;
113     undo_context.add.index = index;
114     return undo_context;
115 }
116
117 static
118 UndoContext create_undo_element_context(RectLayer *layer)
119 {
120     trace_assert(layer);
121     size_t index = (size_t) layer->selection;
122     trace_assert(index < dynarray_count(layer->rects));
123
124     UndoContext undo_context;
125     undo_context.element.layer = layer;
126     undo_context.element.index = index;
127     dynarray_copy_to(layer->rects, &undo_context.element.rect, index);
128     dynarray_copy_to(layer->colors, &undo_context.element.color, index);
129     dynarray_copy_to(layer->ids, undo_context.element.id, index);
130     dynarray_copy_to(layer->actions, &undo_context.element.action, index);
131     return undo_context;
132 }
133
134 static
135 UndoContext create_undo_update_context(RectLayer *rect_layer)
136 {
137     UndoContext undo_context = create_undo_element_context(rect_layer);
138     undo_context.type = UNDO_UPDATE;
139     return undo_context;
140 }
141
142 static
143 UndoContext create_undo_delete_context(RectLayer *rect_layer)
144 {
145     UndoContext undo_context = create_undo_element_context(rect_layer);
146     undo_context.type = UNDO_DELETE;
147     return undo_context;
148 }
149
150 static
151 UndoContext create_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2)
152 {
153     UndoContext undo_context;
154     undo_context.swap.type = UNDO_SWAP;
155     undo_context.swap.layer = rect_layer;
156     undo_context.swap.index1 = index1;
157     undo_context.swap.index2 = index2;
158     return undo_context;
159 }
160
161 static
162 void rect_layer_undo(void *context, size_t context_size)
163 {
164     trace_assert(context);
165     trace_assert(sizeof(UndoContext) == context_size);
166
167     UndoContext *undo_context = context;
168
169     switch (undo_context->type) {
170     case UNDO_ADD: {
171         RectLayer *layer = undo_context->add.layer;
172         dynarray_delete_at(layer->rects, undo_context->add.index);
173         dynarray_delete_at(layer->colors, undo_context->add.index);
174         dynarray_delete_at(layer->ids, undo_context->add.index);
175         dynarray_delete_at(layer->actions, undo_context->add.index);
176         layer->selection = -1;
177     } break;
178
179     case UNDO_DELETE: {
180         RectLayer *layer = undo_context->element.layer;
181         dynarray_insert_before(layer->rects, undo_context->element.index, &undo_context->element.rect);
182         dynarray_insert_before(layer->colors, undo_context->element.index, &undo_context->element.color);
183         dynarray_insert_before(layer->ids, undo_context->element.index, &undo_context->element.id);
184         dynarray_insert_before(layer->actions, undo_context->element.index, &undo_context->element.action);
185         layer->selection = -1;
186     } break;
187
188     case UNDO_UPDATE: {
189         RectLayer *layer = undo_context->element.layer;
190         dynarray_replace_at(layer->rects, undo_context->element.index, &undo_context->element.rect);
191         dynarray_replace_at(layer->colors, undo_context->element.index, &undo_context->element.color);
192         dynarray_replace_at(layer->ids, undo_context->element.index, &undo_context->element.id);
193         dynarray_replace_at(layer->actions, undo_context->element.index, &undo_context->element.action);
194     } break;
195
196     case UNDO_SWAP: {
197         RectLayer *layer = undo_context->element.layer;
198         dynarray_swap(layer->rects, undo_context->swap.index1, undo_context->swap.index2);
199         dynarray_swap(layer->colors, undo_context->swap.index1, undo_context->swap.index2);
200         dynarray_swap(layer->ids, undo_context->swap.index1, undo_context->swap.index2);
201         dynarray_swap(layer->actions, undo_context->swap.index1, undo_context->swap.index2);
202     } break;
203     }
204 }
205
206 #define UNDO_PUSH(HISTORY, CONTEXT)                                     \
207     do {                                                                \
208         UndoContext context = (CONTEXT);                                \
209         undo_history_push(                                              \
210             HISTORY,                                                    \
211             rect_layer_undo,                                            \
212             &context,                                                   \
213             sizeof(context));                                           \
214     } while(0)
215
216 static int rect_layer_add_rect(RectLayer *layer,
217                                Rect rect,
218                                Color color,
219                                UndoHistory *undo_history)
220 {
221     trace_assert(layer);
222
223     if (dynarray_push(layer->rects, &rect) < 0) {
224         return -1;
225     }
226
227     if (dynarray_push(layer->colors, &color) < 0) {
228         return -1;
229     }
230
231     char id[ENTITY_MAX_ID_SIZE];
232     snprintf(id, ENTITY_MAX_ID_SIZE, "%s_%d",
233              layer->id_name_prefix,
234              layer->id_name_counter++);
235     if (dynarray_push(layer->ids, id)) {
236         return -1;
237     }
238
239     dynarray_push_empty(layer->actions);
240
241     UNDO_PUSH(
242         undo_history,
243         create_undo_add_context(
244             layer,
245             dynarray_count(layer->rects) - 1));
246
247     return 0;
248 }
249
250 static int rect_layer_rect_at(RectLayer *layer, Vec2f position)
251 {
252     trace_assert(layer);
253
254     int n = (int) dynarray_count(layer->rects);
255     Rect *rects = dynarray_data(layer->rects);
256
257     for (int i = n - 1; i >= 0; --i) {
258         if (rect_contains_point(rects[i], position)) {
259             return (int) i;
260         }
261     }
262
263     return -1;
264 }
265
266 static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b,
267                                      UndoHistory *undo_history)
268 {
269     trace_assert(layer);
270     trace_assert(a < dynarray_count(layer->rects));
271     trace_assert(b < dynarray_count(layer->rects));
272
273     dynarray_swap(layer->rects, a, b);
274     dynarray_swap(layer->colors, a, b);
275     dynarray_swap(layer->ids, a, b);
276     dynarray_swap(layer->actions, a, b);
277
278     UNDO_PUSH(undo_history, create_undo_swap_context(layer, a, b));
279 }
280
281 static int rect_layer_delete_rect_at(RectLayer *layer,
282                                      size_t i,
283                                      UndoHistory *undo_history)
284 {
285     trace_assert(layer);
286
287     UNDO_PUSH(undo_history, create_undo_delete_context(layer));
288
289     dynarray_delete_at(layer->rects, i);
290     dynarray_delete_at(layer->colors, i);
291     dynarray_delete_at(layer->ids, i);
292     dynarray_delete_at(layer->actions, i);
293
294     return 0;
295 }
296
297 static int calc_resize_mask(Vec2f point, Rect rect)
298 {
299     int mask = 0;
300     for (Rect_side side = 0; side < RECT_SIDE_N; ++side) {
301         if (rect_side_distance(rect, point, side) < RECT_LAYER_SELECTION_THICCNESS) {
302             mask = mask | (1 << side);
303         }
304     }
305     return mask;
306 }
307
308 static int rect_layer_event_idle(RectLayer *layer,
309                                  const SDL_Event *event,
310                                  const Camera *camera,
311                                  UndoHistory *undo_history)
312 {
313     trace_assert(layer);
314     trace_assert(event);
315     trace_assert(camera);
316
317     int color_changed = 0;
318     if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
319         return -1;
320     }
321
322     if (color_changed) {
323         if (layer->selection >= 0) {
324             dynarray_copy_to(layer->colors, &layer->inter_color, (size_t)layer->selection);
325             layer->state = RECT_LAYER_RECOLOR;
326         }
327         return 0;
328     }
329
330     switch (event->type) {
331     case SDL_MOUSEBUTTONDOWN: {
332         switch (event->button.button) {
333         case SDL_BUTTON_LEFT: {
334             Vec2f position = camera_map_screen(
335                 camera,
336                 event->button.x,
337                 event->button.y);
338             int rect_at_position =
339                 rect_layer_rect_at(layer, position);
340
341             Rect *rects = dynarray_data(layer->rects);
342             Color *colors = dynarray_data(layer->colors);
343
344             if (layer->selection >= 0 &&
345                 layer->selection == rect_at_position &&
346                 (layer->resize_mask = calc_resize_mask(
347                     vec((float) event->button.x, (float)event->button.y),
348                     camera_rect(camera, rects[layer->selection])))) {
349                 layer->state = RECT_LAYER_RESIZE;
350                 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) layer->selection);
351             } else if (rect_at_position >= 0) {
352                 layer->selection = rect_at_position;
353                 layer->state = RECT_LAYER_MOVE;
354                 layer->move_anchor =
355                     vec_sub(
356                         position,
357                         vec(
358                             rects[layer->selection].x,
359                             rects[layer->selection].y));
360                 layer->color_picker =
361                     create_color_picker_from_rgba(colors[rect_at_position]);
362
363                 dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) rect_at_position);
364             } else {
365                 layer->selection = rect_at_position;
366
367                 if (layer->selection < 0) {
368                     layer->state = RECT_LAYER_CREATE;
369                     layer->create_begin = position;
370                     layer->create_end = position;
371                 }
372             }
373         } break;
374         }
375     } break;
376
377     case SDL_KEYDOWN: {
378         switch (event->key.keysym.sym) {
379         case SDLK_UP: {
380             if ((event->key.keysym.mod & KMOD_SHIFT)
381                 && (layer->selection >= 0)
382                 && ((size_t)(layer->selection + 1) < dynarray_count(layer->rects))) {
383                 rect_layer_swap_elements(
384                     layer,
385                     (size_t) layer->selection,
386                     (size_t) layer->selection + 1,
387                     undo_history);
388                 layer->selection++;
389             }
390         } break;
391
392         case SDLK_DOWN: {
393             if ((event->key.keysym.mod & KMOD_SHIFT)
394                 && (layer->selection > 0)
395                 && ((size_t) layer->selection < dynarray_count(layer->rects))) {
396                 rect_layer_swap_elements(
397                     layer,
398                     (size_t) layer->selection,
399                     (size_t) layer->selection - 1,
400                     undo_history);
401                 layer->selection--;
402             }
403         } break;
404
405         case SDLK_DELETE: {
406             if (layer->selection >= 0) {
407                 rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
408                 layer->selection = -1;
409             }
410         } break;
411
412         case SDLK_F2: {
413             if (layer->selection >= 0) {
414                 const char *ids = dynarray_data(layer->ids);
415                 Color *colors = dynarray_data(layer->colors);
416
417                 edit_field_restyle(
418                     layer->id_edit_field,
419                     RECT_LAYER_ID_LABEL_SIZE,
420                     color_invert(colors[layer->selection]));
421
422                 layer->state = RECT_LAYER_ID_RENAME;
423                 edit_field_replace(
424                     layer->id_edit_field,
425                     ids + layer->selection * ENTITY_MAX_ID_SIZE);
426                 SDL_StartTextInput();
427             }
428         } break;
429
430         case SDLK_c: {
431             if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
432                 clipboard = 1;
433                 dynarray_copy_to(layer->rects, &clipboard_rect, (size_t)layer->selection);
434                 dynarray_copy_to(layer->colors, &clipboard_color, (size_t)layer->selection);
435             }
436         } break;
437
438         case SDLK_v: {
439             if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
440                 int x, y;
441                 SDL_GetMouseState(&x, &y);
442                 Vec2f position = camera_map_screen(camera, x, y);
443
444                 rect_layer_add_rect(
445                     layer,
446                     rect(position.x, position.y,
447                          clipboard_rect.w, clipboard_rect.h),
448                     clipboard_color,
449                     undo_history);
450             }
451         } break;
452         }
453     } break;
454     }
455
456     return 0;
457 }
458
459 static int rect_layer_event_create(RectLayer *layer,
460                                    const SDL_Event *event,
461                                    const Camera *camera,
462                                    UndoHistory *undo_history)
463 {
464     trace_assert(layer);
465     trace_assert(event);
466     trace_assert(camera);
467
468     switch (event->type) {
469     case SDL_MOUSEBUTTONUP: {
470         switch (event->button.button) {
471         case SDL_BUTTON_LEFT: {
472             const Rect real_rect =
473                 rect_from_points(
474                     layer->create_begin,
475                     layer->create_end);
476             const float area = real_rect.w * real_rect.h;
477
478             if (area >= CREATE_AREA_THRESHOLD) {
479                 rect_layer_add_rect(
480                     layer,
481                     real_rect,
482                     color_picker_rgba(&layer->color_picker),
483                     undo_history);
484             } else {
485                 log_info("The area is too small %f. Such small box won't be created.\n", area);
486             }
487             layer->state = RECT_LAYER_IDLE;
488         } break;
489         }
490     } break;
491
492     case SDL_MOUSEMOTION: {
493         layer->create_end = camera_map_screen(
494             camera,
495             event->motion.x,
496             event->motion.y);
497     } break;
498     }
499     return 0;
500 }
501
502 static int rect_layer_event_resize(RectLayer *layer,
503                                    const SDL_Event *event,
504                                    const Camera *camera,
505                                    UndoHistory *undo_history)
506 {
507     trace_assert(layer);
508     trace_assert(event);
509     trace_assert(camera);
510     trace_assert(layer->selection >= 0);
511
512     Rect *rects = dynarray_data(layer->rects);
513
514     switch (event->type) {
515     case SDL_MOUSEMOTION: {
516         Vec2f position = camera_map_screen(
517             camera,
518             event->button.x,
519             event->button.y);
520
521         switch (layer->resize_mask) {
522         case 1: {               // TOP
523             layer->inter_rect = rect_from_points(
524                 vec(rects[layer->selection].x, position.y),
525                 rect_position2(rects[layer->selection]));
526         } break;
527
528         case 2: {               // LEFT
529             layer->inter_rect = rect_from_points(
530                 vec(position.x, rects[layer->selection].y),
531                 rect_position2(rects[layer->selection]));
532         } break;
533
534         case 3: {               // TOP,LEFT
535             layer->inter_rect = rect_from_points(
536                 position,
537                 rect_position2(rects[layer->selection]));
538         } break;
539
540         case 4: {               // BOTTOM
541             layer->inter_rect = rect_from_points(
542                 rect_position(rects[layer->selection]),
543                 vec(rects[layer->selection].x + rects[layer->selection].w,
544                     position.y));
545         } break;
546
547         case 6: {               // BOTTOM,LEFT
548             layer->inter_rect = rect_from_points(
549                 vec(position.x, rects[layer->selection].y),
550                 vec(rects[layer->selection].x + rects[layer->selection].w,
551                     position.y));
552         } break;
553
554         case 8: {               // RIGHT
555             layer->inter_rect = rect_from_points(
556                 rect_position(rects[layer->selection]),
557                 vec(position.x,
558                     rects[layer->selection].y + rects[layer->selection].h));
559         } break;
560
561         case 9: {               // TOP,RIGHT
562             layer->inter_rect = rect_from_points(
563                 vec(rects[layer->selection].x, position.y),
564                 vec(position.x,
565                     rects[layer->selection].y + rects[layer->selection].h));
566         } break;
567
568         case 12: {              // BOTTOM,RIGHT
569             layer->inter_rect = rect_from_points(
570                 rect_position(rects[layer->selection]),
571                 position);
572         } break;
573         }
574     } break;
575
576     case SDL_MOUSEBUTTONUP: {
577         layer->state = RECT_LAYER_IDLE;
578         UNDO_PUSH(undo_history, create_undo_update_context(layer));
579         dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
580     } break;
581     }
582
583     return 0;
584 }
585
586 static int rect_layer_event_move(RectLayer *layer,
587                                  const SDL_Event *event,
588                                  const Camera *camera,
589                                  UndoHistory *undo_history)
590 {
591     trace_assert(layer);
592     trace_assert(event);
593     trace_assert(camera);
594     trace_assert(layer->selection >= 0);
595
596     Rect *rects = dynarray_data(layer->rects);
597
598     switch (event->type) {
599     case SDL_MOUSEMOTION: {
600         const Uint8 *state = SDL_GetKeyboardState(NULL);
601         const Vec2f mouse_pos = vec_sub(
602             camera_map_screen(
603                 camera,
604                 event->button.x,
605                 event->button.y),
606             layer->move_anchor);
607
608         if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) {
609             layer->inter_rect.x = mouse_pos.x;
610             layer->inter_rect.y = mouse_pos.y;
611         } else {
612             const Vec2f rect_pos = rect_position(rects[layer->selection]);
613
614             const float dx = fabsf(rect_pos.x - mouse_pos.x);
615             const float dy = fabsf(rect_pos.y - mouse_pos.y);
616
617             if (dx > dy) {
618                 layer->inter_rect.x = mouse_pos.x;
619                 layer->inter_rect.y = rect_pos.y;
620             } else {
621                 layer->inter_rect.x = rect_pos.x;
622                 layer->inter_rect.y = mouse_pos.y;
623             }
624         }
625     } break;
626
627     case SDL_MOUSEBUTTONUP: {
628         layer->state = RECT_LAYER_IDLE;
629
630         float distance = vec_length(
631             vec_sub(rect_position(layer->inter_rect),
632                     rect_position(rects[layer->selection])));
633
634         if (distance > 1e-6) {
635             UNDO_PUSH(undo_history, create_undo_update_context(layer));
636             dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
637         }
638     } break;
639     }
640     return 0;
641 }
642
643 static int rect_layer_event_id_rename(RectLayer *layer,
644                                       const SDL_Event *event,
645                                       const Camera *camera,
646                                       UndoHistory *undo_history)
647 {
648     trace_assert(layer);
649     trace_assert(event);
650     trace_assert(camera);
651     trace_assert(layer->selection >= 0);
652
653     switch (event->type) {
654     case SDL_KEYDOWN: {
655         switch (event->key.keysym.sym) {
656         case SDLK_RETURN: {
657             UNDO_PUSH(undo_history, create_undo_update_context(layer));
658
659             char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
660             memset(id, 0, ENTITY_MAX_ID_SIZE);
661             memcpy(id, edit_field_as_text(layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
662             layer->state = RECT_LAYER_IDLE;
663             SDL_StopTextInput();
664         } break;
665
666         case SDLK_ESCAPE: {
667             layer->state = RECT_LAYER_IDLE;
668             SDL_StopTextInput();
669         } break;
670         }
671     } break;
672     }
673
674     return edit_field_event(layer->id_edit_field, event);
675 }
676
677 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
678 {
679     LayerPtr layer = {
680         .type = LAYER_RECT,
681         .ptr = rect_layer
682     };
683     return layer;
684 }
685
686 RectLayer *create_rect_layer(const char *id_name_prefix)
687 {
688     Lt *lt = create_lt();
689
690     RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
691     if (layer == NULL) {
692         RETURN_LT(lt, NULL);
693     }
694     layer->lt = lt;
695
696     layer->ids = PUSH_LT(
697         lt,
698         create_dynarray(sizeof(char) * ENTITY_MAX_ID_SIZE),
699         destroy_dynarray);
700     if (layer->ids == NULL) {
701         RETURN_LT(lt, NULL);
702     }
703
704     layer->rects = PUSH_LT(
705         lt,
706         create_dynarray(sizeof(Rect)),
707         destroy_dynarray);
708     if (layer->rects == NULL) {
709         RETURN_LT(lt, NULL);
710     }
711
712     layer->colors = PUSH_LT(
713         lt,
714         create_dynarray(sizeof(Color)),
715         destroy_dynarray);
716     if (layer->colors == NULL) {
717         RETURN_LT(lt, NULL);
718     }
719
720     layer->actions = PUSH_LT(
721         lt,
722         create_dynarray(sizeof(Action)),
723         destroy_dynarray);
724     if (layer->actions == NULL) {
725         RETURN_LT(lt, NULL);
726     }
727
728     layer->id_edit_field = PUSH_LT(
729         lt,
730         create_edit_field(
731             RECT_LAYER_ID_LABEL_SIZE,
732             COLOR_BLACK),
733         destroy_edit_field);
734     if (layer->id_edit_field == NULL) {
735         RETURN_LT(lt, NULL);
736     }
737
738     layer->grid =
739         PUSH_LT(
740             lt,
741             nth_calloc(
742                 1,
743                 sizeof(Grid) + sizeof(Widget*) * RECT_LAYER_GRID_ROWS * RECT_LAYER_GRID_COLUMNS),
744             free);
745     if (layer->grid == NULL) {
746         RETURN_LT(lt, NULL);
747     }
748     layer->grid->rows = RECT_LAYER_GRID_ROWS;
749     layer->grid->columns = RECT_LAYER_GRID_COLUMNS;
750     grid_put_widget(layer->grid, &layer->action_picker.widget, 0, RECT_LAYER_GRID_COLUMNS - 1);
751
752     layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
753     layer->selection = -1;
754     layer->id_name_prefix = id_name_prefix;
755
756     return layer;
757 }
758
759 RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream, const char *id_name_prefix)
760 {
761     trace_assert(line_stream);
762
763     RectLayer *layer = create_rect_layer(id_name_prefix);
764     if (layer == NULL) {
765         return NULL;
766     }
767
768     const char *line = line_stream_next(line_stream);
769     if (line == NULL) {
770         RETURN_LT(layer->lt, NULL);
771     }
772
773     size_t count = 0;
774     if (sscanf(line, "%zu", &count) < 0) {
775         RETURN_LT(layer->lt, NULL);
776     }
777
778     for (size_t i = 0; i < count; ++i) {
779         line = line_stream_next(line_stream);
780         if (line == NULL) {
781             RETURN_LT(layer->lt, NULL);
782         }
783
784         char hex[7];
785         Rect rect;
786         char id[ENTITY_MAX_ID_SIZE];
787
788         int n = 0;
789         if (sscanf(line,
790                    "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s%f%f%f%f%6s%n",
791                    id,
792                    &rect.x, &rect.y,
793                    &rect.w, &rect.h,
794                    hex, &n) <= 0) {
795             log_fail("%s\n", strerror(errno));
796             RETURN_LT(layer->lt, NULL);
797         }
798         line += n;
799
800         Color color = hexstr(hex);
801         dynarray_push(layer->rects, &rect);
802         dynarray_push(layer->ids, id);
803         dynarray_push(layer->colors, &color);
804
805         Action action = {
806             .type = ACTION_NONE,
807             .entity_id = {0}
808         };
809
810         if (sscanf(line, "%d%n", (int*)&action.type, &n) > 0) {
811             line += n;
812             switch (action.type) {
813             case ACTION_NONE: break;
814
815             case ACTION_TOGGLE_GOAL:
816             case ACTION_HIDE_LABEL: {
817                 if (sscanf(line, "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s", action.entity_id) <= 0) {
818                     log_fail("%s\n", strerror(errno));
819                     RETURN_LT(layer->lt, NULL);
820                 }
821             } break;
822
823             case ACTION_N: break;
824             }
825         }
826
827         dynarray_push(layer->actions, &action);
828     }
829
830     return layer;
831 }
832
833 void destroy_rect_layer(RectLayer *layer)
834 {
835     trace_assert(layer);
836     RETURN_LT0(layer->lt);
837 }
838
839 int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
840 {
841     trace_assert(layer);
842     trace_assert(camera);
843
844     const size_t n = dynarray_count(layer->rects);
845     Rect *rects = dynarray_data(layer->rects);
846     Color *colors = dynarray_data(layer->colors);
847     const char *ids = dynarray_data(layer->ids);
848
849     // The Rectangles
850     for (size_t i = 0; i < n; ++i) {
851         Rect rect = rects[i];
852         Color color = colors[i];
853
854         if (layer->selection == (int) i) {
855             if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
856                 rect = layer->inter_rect;
857             }
858
859             if (layer->state == RECT_LAYER_RECOLOR) {
860                 color = layer->inter_color;
861             }
862         }
863
864         // Main Rectangle
865         if (camera_fill_rect(
866                 camera,
867                 rect,
868                 color_scale(
869                     color,
870                     rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
871             return -1;
872         }
873     }
874
875     // Selection Overlay
876     if (active && layer->selection >= 0) {
877         Rect rect = rects[layer->selection];
878         Color color = colors[layer->selection];
879
880         if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
881             rect = layer->inter_rect;
882         }
883
884         if (layer->state == RECT_LAYER_RECOLOR) {
885             color = layer->inter_color;
886         }
887
888         const Rect overlay_rect =
889             rect_pad(
890                 camera_rect(camera, rect),
891                 -RECT_LAYER_SELECTION_THICCNESS * 0.5f);
892         const Color overlay_color = color_invert(color);
893
894         // Selection
895         if (camera_draw_thicc_rect_screen(
896                 camera,
897                 overlay_rect,
898                 overlay_color,
899                 RECT_LAYER_SELECTION_THICCNESS) < 0) {
900             return -1;
901         }
902
903         const Vec2f rect_id_pos = vec_sub(
904             rect_position(rect),
905             vec_mult(
906                 RECT_LAYER_ID_LABEL_SIZE,
907                 vec(0.0f, FONT_CHAR_HEIGHT)));
908
909         // Rectangle Id
910         if (layer->state == RECT_LAYER_ID_RENAME) {
911             // ID renaming Edit Field
912             if (edit_field_render_world(
913                     layer->id_edit_field,
914                     camera,
915                     rect_id_pos) < 0) {
916                 return -1;
917             }
918         } else {
919             // Id text
920             if (camera_render_text(
921                     camera,
922                     ids + layer->selection * ENTITY_MAX_ID_SIZE,
923                     RECT_LAYER_ID_LABEL_SIZE,
924                     color_invert(color),
925                     rect_id_pos) < 0) {
926                 return -1;
927             }
928         }
929     }
930
931     // Proto Rectangle
932     const Color color = color_picker_rgba(&layer->color_picker);
933     if (layer->state == RECT_LAYER_CREATE) {
934         if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
935             return -1;
936         }
937     }
938
939     if (active && color_picker_render(&layer->color_picker, camera) < 0) {
940         return -1;
941     }
942
943     return 0;
944 }
945
946 static
947 int rect_layer_event_recolor(RectLayer *layer,
948                              const SDL_Event *event,
949                              const Camera *camera,
950                              UndoHistory *undo_history)
951 {
952     trace_assert(layer);
953     trace_assert(event);
954     trace_assert(camera);
955     trace_assert(undo_history);
956     trace_assert(layer->selection >= 0);
957
958     int color_changed = 0;
959     if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
960         return -1;
961     }
962
963     if (color_changed) {
964         layer->inter_color = color_picker_rgba(&layer->color_picker);
965
966         if (!color_picker_drag(&layer->color_picker)) {
967             UNDO_PUSH(undo_history, create_undo_update_context(layer));
968             dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color);
969             layer->state = RECT_LAYER_IDLE;
970         }
971     }
972
973     return 0;
974 }
975
976 int rect_layer_event(RectLayer *layer,
977                      const SDL_Event *event,
978                      const Camera *camera,
979                      UndoHistory *undo_history)
980 {
981     trace_assert(layer);
982     trace_assert(event);
983     trace_assert(undo_history);
984
985     switch (event->type) {
986     case SDL_WINDOWEVENT: {
987         switch (event->window.event) {
988         case SDL_WINDOWEVENT_RESIZED: {
989             grid_relayout(layer->grid, rect(0.0f, 0.0f,
990                                             (float) event->window.data1,
991                                             (float) event->window.data2));
992         } break;
993         }
994     } break;
995     }
996
997     switch (layer->state) {
998     case RECT_LAYER_IDLE:
999         return rect_layer_event_idle(layer, event, camera, undo_history);
1000
1001     case RECT_LAYER_CREATE:
1002         return rect_layer_event_create(layer, event, camera, undo_history);
1003
1004     case RECT_LAYER_RESIZE:
1005         return rect_layer_event_resize(layer, event, camera, undo_history);
1006
1007     case RECT_LAYER_MOVE:
1008         return rect_layer_event_move(layer, event, camera, undo_history);
1009
1010     case RECT_LAYER_ID_RENAME:
1011         return rect_layer_event_id_rename(layer, event, camera, undo_history);
1012
1013     case RECT_LAYER_RECOLOR:
1014         return rect_layer_event_recolor(layer, event, camera, undo_history);
1015     }
1016
1017     return 0;
1018 }
1019
1020 size_t rect_layer_count(const RectLayer *layer)
1021 {
1022     return dynarray_count(layer->rects);
1023 }
1024
1025 const Rect *rect_layer_rects(const RectLayer *layer)
1026 {
1027     return dynarray_data(layer->rects);
1028 }
1029
1030 const Color *rect_layer_colors(const RectLayer *layer)
1031 {
1032     return dynarray_data(layer->colors);
1033 }
1034
1035 const char *rect_layer_ids(const RectLayer *layer)
1036 {
1037     return dynarray_data(layer->ids);
1038 }
1039
1040 int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
1041 {
1042     trace_assert(layer);
1043     trace_assert(filedump);
1044
1045     size_t n = dynarray_count(layer->ids);
1046     char *ids = dynarray_data(layer->ids);
1047     Rect *rects = dynarray_data(layer->rects);
1048     Color *colors = dynarray_data(layer->colors);
1049     Action *actions = dynarray_data(layer->actions);
1050
1051     fprintf(filedump, "%zd\n", n);
1052     for (size_t i = 0; i < n; ++i) {
1053         fprintf(filedump, "%s %f %f %f %f ",
1054                 ids + ENTITY_MAX_ID_SIZE * i,
1055                 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
1056         color_hex_to_stream(colors[i], filedump);
1057
1058         switch (actions[i].type) {
1059         case ACTION_NONE: {} break;
1060
1061         case ACTION_TOGGLE_GOAL:
1062         case ACTION_HIDE_LABEL: {
1063             fprintf(filedump, " %d %.*s",
1064                     (int)actions[i].type,
1065                     ENTITY_MAX_ID_SIZE, actions[i].entity_id);
1066         } break;
1067         case ACTION_N: break;
1068         }
1069
1070         fprintf(filedump, "\n");
1071     }
1072
1073     return 0;
1074 }
1075
1076 const Action *rect_layer_actions(const RectLayer *layer)
1077 {
1078     return dynarray_data(layer->actions);
1079 }