]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/label_layer.c
(#965) edit_field_render -> edit_field_render_screen
[nothing.git] / src / game / level / level_editor / label_layer.c
1 #include <stdio.h>
2
3 #include <SDL.h>
4
5 #include "system/line_stream.h"
6 #include "system/stacktrace.h"
7 #include "system/nth_alloc.h"
8 #include "system/lt.h"
9 #include "system/str.h"
10 #include "system/log.h"
11 #include "math/point.h"
12 #include "label_layer.h"
13 #include "dynarray.h"
14 #include "color.h"
15 #include "game/camera.h"
16 #include "color_picker.h"
17 #include "ui/edit_field.h"
18
19 #define LABEL_LAYER_SELECTION_THICCNESS 5.0f
20
21 typedef enum {
22     LABEL_LAYER_IDLE = 0,
23     LABEL_LAYER_MOVE,
24     LABEL_LAYER_EDIT_TEXT
25 } LabelLayerState;
26
27 // TODO(#963): LabelLayer cannot add the labels
28 // TODO(#964): LabelLayer cannot modify the labels' id
29 struct LabelLayer {
30     Lt *lt;
31     LabelLayerState state;
32     Dynarray *ids;
33     Dynarray *positions;
34     Dynarray *colors;
35     Dynarray *texts;
36     int selected;
37     ColorPicker color_picker;
38     Point move_anchor;
39     Edit_field *edit_field;
40 };
41
42 LayerPtr label_layer_as_layer(LabelLayer *label_layer)
43 {
44     LayerPtr layer = {
45         .ptr = label_layer,
46         .type = LAYER_LABEL
47     };
48     return layer;
49 }
50
51 LabelLayer *create_label_layer(void)
52 {
53     Lt *lt = create_lt();
54
55     LabelLayer *label_layer = PUSH_LT(
56         lt, nth_calloc(1, sizeof(LabelLayer)), free);
57     if (label_layer == NULL) {
58         RETURN_LT(lt, NULL);
59     }
60     label_layer->lt = lt;
61
62     label_layer->ids = PUSH_LT(
63         lt,
64         create_dynarray(sizeof(char) * LABEL_LAYER_ID_MAX_SIZE),
65         destroy_dynarray);
66     if (label_layer->ids == NULL) {
67         RETURN_LT(lt, NULL);
68     }
69
70     label_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray);
71     if (label_layer->positions == NULL) {
72         RETURN_LT(lt, NULL);
73     }
74
75     label_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
76     if (label_layer->colors == NULL) {
77         RETURN_LT(lt, NULL);
78     }
79
80     label_layer->texts = PUSH_LT(
81         lt,
82         create_dynarray(sizeof(char) * LABEL_LAYER_TEXT_MAX_SIZE),
83         destroy_dynarray);
84     if (label_layer->texts == NULL) {
85         RETURN_LT(lt, NULL);
86     }
87
88     label_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
89     label_layer->selected = -1;
90
91     label_layer->edit_field = PUSH_LT(
92         lt,
93         create_edit_field(LABELS_SIZE, COLOR_RED),
94         destroy_edit_field);
95     if (label_layer->edit_field == NULL) {
96         RETURN_LT(lt, NULL);
97     }
98
99     return label_layer;
100 }
101
102 LabelLayer *create_label_layer_from_line_stream(LineStream *line_stream)
103 {
104     trace_assert(line_stream);
105     LabelLayer *label_layer = create_label_layer();
106
107     if (label_layer == NULL) {
108         RETURN_LT(label_layer->lt, NULL);
109     }
110
111     const char *line = line_stream_next(line_stream);
112     if (line == NULL) {
113         log_fail("Could not read amount of labels\n");
114         RETURN_LT(label_layer->lt, NULL);
115     }
116
117     size_t n = 0;
118     if (sscanf(line, "%zu", &n) == EOF) {
119         log_fail("Could not parse amount of labels\n");
120         RETURN_LT(label_layer->lt, NULL);
121     }
122
123     for (size_t i = 0; i < n; ++i) {
124         char hex[7];
125         char id[LABEL_LAYER_ID_MAX_SIZE];
126         Point position;
127
128         line = line_stream_next(line_stream);
129         if (line == NULL) {
130             log_fail("Could not read label meta info\n");
131             RETURN_LT(label_layer->lt, NULL);
132         }
133
134         if (sscanf(
135                 line,
136                 "%"STRINGIFY(LABEL_LAYER_ID_MAX_SIZE)"s%f%f%6s\n",
137                 id, &position.x, &position.y, hex) == EOF) {
138             log_fail("Could not parse label meta info\n");
139             RETURN_LT(label_layer->lt, NULL);
140         }
141
142         Color color = hexstr(hex);
143
144         dynarray_push(label_layer->ids, id);
145         dynarray_push(label_layer->positions, &position);
146         dynarray_push(label_layer->colors, &color);
147
148         line = line_stream_next(line_stream);
149         if (line == NULL) {
150             log_fail("Could not read label text\n");
151         }
152
153         char label_text[LABEL_LAYER_TEXT_MAX_SIZE] = {0};
154         memcpy(label_text, line, LABEL_LAYER_TEXT_MAX_SIZE - 1);
155         trim_endline(label_text);
156         dynarray_push(label_layer->texts, &label_text);
157     }
158
159     return label_layer;
160 }
161
162 void destroy_label_layer(LabelLayer *label_layer)
163 {
164     trace_assert(label_layer);
165     destroy_lt(label_layer->lt);
166 }
167
168 int label_layer_render(const LabelLayer *label_layer,
169                        Camera *camera,
170                        int active)
171 {
172     trace_assert(label_layer);
173     trace_assert(camera);
174
175     if (active && color_picker_render(&label_layer->color_picker, camera) < 0) {
176         return -1;
177     }
178
179     size_t n = dynarray_count(label_layer->ids);
180     Point *positions = dynarray_data(label_layer->positions);
181     Color *colors = dynarray_data(label_layer->colors);
182     char *texts = dynarray_data(label_layer->texts);
183
184     /* TODO(#891): LabelLayer doesn't show the final position of Label after the animation */
185     for (size_t i = 0; i < n; ++i) {
186         if (label_layer->state == LABEL_LAYER_EDIT_TEXT) {
187             // TODO(#965): LabelLayer Edit Field should be rendered inside of the world
188             if (edit_field_render_screen(
189                     label_layer->edit_field,
190                     camera,
191                     camera_point(camera, positions[i])) < 0) {
192                 return -1;
193             }
194         } else {
195             if (camera_render_text(
196                     camera,
197                     texts + i * LABEL_LAYER_TEXT_MAX_SIZE,
198                     LABELS_SIZE,
199                     color_scale(
200                         colors[i],
201                         rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
202                     positions[i]) < 0) {
203                 return -1;
204             }
205         }
206     }
207
208     if (label_layer->selected >= 0) {
209
210         Rect selection =
211             rect_scale(
212                 camera_rect(
213                     camera,
214                     sprite_font_boundary_box(
215                         camera_font(camera),
216                         positions[label_layer->selected],
217                         LABELS_SIZE,
218                         texts + label_layer->selected * LABEL_LAYER_TEXT_MAX_SIZE)),
219                 LABEL_LAYER_SELECTION_THICCNESS * 0.5f);
220
221
222         if (camera_draw_thicc_rect_screen(
223                 camera,
224                 selection,
225                 colors[label_layer->selected],
226                 LABEL_LAYER_SELECTION_THICCNESS) < 0) {
227             return -1;
228         }
229     }
230
231     return 0;
232 }
233
234 static
235 int label_layer_element_at(LabelLayer *label_layer,
236                            const Sprite_font *font,
237                            Point position)
238 {
239     trace_assert(label_layer);
240
241     const size_t n = dynarray_count(label_layer->texts);
242     char *texts = dynarray_data(label_layer->texts);
243     Point *positions = dynarray_data(label_layer->positions);
244
245     for (size_t i = 0; i < n; ++i) {
246         Rect boundary = sprite_font_boundary_box(
247             font,
248             positions[i],
249             LABELS_SIZE,
250             texts + i * LABEL_LAYER_TEXT_MAX_SIZE);
251
252         if (rect_contains_point(boundary, position)) {
253             return (int) i;
254         }
255     }
256
257     return -1;
258 }
259
260 static
261 int label_layer_idle_event(LabelLayer *label_layer,
262                            const SDL_Event *event,
263                            const Camera *camera)
264 {
265     trace_assert(label_layer);
266     trace_assert(event);
267     trace_assert(camera);
268
269     switch (event->type) {
270     case SDL_MOUSEBUTTONDOWN: {
271         switch (event->button.button) {
272         case SDL_BUTTON_LEFT: {
273             const Point position = camera_map_screen(
274                 camera,
275                 event->button.x,
276                 event->button.y);
277
278             const int element = label_layer_element_at(
279                 label_layer,
280                 camera_font(camera),
281                 position);
282
283             if (element >= 0) {
284                 Point *positions = dynarray_data(label_layer->positions);
285                 Color *colors = dynarray_data(label_layer->colors);
286
287                 label_layer->move_anchor = vec_sub(position, positions[element]);
288                 label_layer->selected = element;
289                 label_layer->state = LABEL_LAYER_MOVE;
290
291                 label_layer->color_picker =
292                     create_color_picker_from_rgba(colors[element]);
293             }
294         } break;
295         }
296     } break;
297
298     case SDL_KEYDOWN: {
299         switch (event->key.keysym.sym) {
300         case SDLK_F2: {
301             if (label_layer->selected >= 0) {
302                 char *texts = dynarray_data(label_layer->texts);
303                 label_layer->state = LABEL_LAYER_EDIT_TEXT;
304                 edit_field_replace(
305                     label_layer->edit_field,
306                     texts + label_layer->selected * LABEL_LAYER_TEXT_MAX_SIZE);
307                 SDL_StartTextInput();
308             }
309         } break;
310         }
311     } break;
312     }
313
314     return 0;
315 }
316
317 static
318 int label_layer_move_event(LabelLayer *label_layer,
319                            const SDL_Event *event,
320                            const Camera *camera)
321 {
322     trace_assert(label_layer);
323     trace_assert(event);
324     trace_assert(camera);
325     trace_assert(label_layer->selected >= 0);
326
327     switch (event->type) {
328     case SDL_MOUSEMOTION: {
329         Point *positions = dynarray_data(label_layer->positions);
330         positions[label_layer->selected] =
331             vec_sub(
332                 camera_map_screen(
333                     camera,
334                     event->motion.x,
335                     event->motion.y),
336                 label_layer->move_anchor);
337     } break;
338
339     case SDL_MOUSEBUTTONUP: {
340         switch (event->button.button) {
341         case SDL_BUTTON_LEFT: {
342             label_layer->state = LABEL_LAYER_IDLE;
343         } break;
344         }
345     } break;
346     }
347
348     return 0;
349 }
350
351 static
352 int label_layer_edit_text_event(LabelLayer *label_layer,
353                                 const SDL_Event *event,
354                                 const Camera *camera)
355 {
356     trace_assert(label_layer);
357     trace_assert(event);
358     trace_assert(camera);
359     trace_assert(label_layer->selected >= 0);
360
361     switch (event->type) {
362     case SDL_KEYDOWN: {
363         if (event->key.keysym.sym == SDLK_RETURN) {
364             char *text =
365                 (char*)dynarray_data(label_layer->texts) + label_layer->selected * LABEL_LAYER_TEXT_MAX_SIZE;
366             memset(text, 0, LABEL_LAYER_TEXT_MAX_SIZE);
367             memcpy(text, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_TEXT_MAX_SIZE - 1);
368             label_layer->state = LABEL_LAYER_IDLE;
369             return 0;
370         }
371     } break;
372     }
373
374     return edit_field_event(label_layer->edit_field, event);
375 }
376
377 int label_layer_event(LabelLayer *label_layer,
378                       const SDL_Event *event,
379                       const Camera *camera)
380 {
381     trace_assert(label_layer);
382     trace_assert(event);
383     trace_assert(camera);
384
385     int changed = 0;
386
387     if (color_picker_event(
388             &label_layer->color_picker,
389             event,
390             &changed) < 0) {
391         return -1;
392     }
393
394     if (changed && label_layer->selected >= 0) {
395         Color *colors = dynarray_data(label_layer->colors);
396         colors[label_layer->selected] =
397             color_picker_rgba(&label_layer->color_picker);
398     }
399
400     switch (label_layer->state) {
401     case LABEL_LAYER_IDLE:
402         return label_layer_idle_event(label_layer, event, camera);
403
404     case LABEL_LAYER_MOVE:
405         return label_layer_move_event(label_layer, event, camera);
406
407     case LABEL_LAYER_EDIT_TEXT:
408         return label_layer_edit_text_event(label_layer, event, camera);
409     }
410
411     return 0;
412 }
413
414 size_t label_layer_count(const LabelLayer *label_layer)
415 {
416     return dynarray_count(label_layer->ids);
417 }
418
419 char *label_layer_ids(const LabelLayer *label_layer)
420 {
421     return dynarray_data(label_layer->ids);
422 }
423
424 Point *label_layer_positions(const LabelLayer *label_layer)
425 {
426     return dynarray_data(label_layer->positions);
427 }
428
429 Color *label_layer_colors(const LabelLayer *label_layer)
430 {
431     return dynarray_data(label_layer->colors);
432 }
433
434 char *labels_layer_texts(const LabelLayer *label_layer)
435 {
436     return dynarray_data(label_layer->texts);
437 }
438
439 int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump)
440 {
441     trace_assert(label_layer);
442     trace_assert(filedump);
443
444     size_t n = dynarray_count(label_layer->ids);
445     char *ids = dynarray_data(label_layer->ids);
446     Point *positions = dynarray_data(label_layer->positions);
447     Color *colors = dynarray_data(label_layer->colors);
448     char *texts = dynarray_data(label_layer->texts);
449
450     fprintf(filedump, "%zd\n", n);
451     for (size_t i = 0; i < n; ++i) {
452         fprintf(filedump, "%s %f %f ",
453                 ids + LABEL_LAYER_ID_MAX_SIZE * i,
454                 positions[i].x, positions[i].y);
455         color_hex_to_stream(colors[i], filedump);
456         fprintf(filedump, "\n%s\n", texts + i * LABEL_LAYER_TEXT_MAX_SIZE);
457     }
458
459     return 0;
460 }