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