]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/point_layer.c
(#892) Introduce Edit Text state for Label Layer
[nothing.git] / src / game / level / level_editor / point_layer.c
1 #include <stdio.h>
2
3 #include <SDL.h>
4
5 #include "dynarray.h"
6 #include "game/camera.h"
7 #include "system/line_stream.h"
8 #include "system/log.h"
9 #include "system/lt.h"
10 #include "system/nth_alloc.h"
11 #include "system/stacktrace.h"
12 #include "system/str.h"
13 #include "ui/edit_field.h"
14 #include "./point_layer.h"
15 #include "math/extrema.h"
16 #include "./color_picker.h"
17
18 #define POINT_LAYER_ELEMENT_RADIUS 10.0f
19
20 typedef enum {
21     POINT_LAYER_NORMAL_STATE = 0,
22     POINT_LAYER_ID_EDITING_STATE
23 } PointLayerState;
24
25 struct PointLayer
26 {
27     Lt *lt;
28     PointLayerState state;
29     Dynarray/*<Point>*/ *points;
30     Dynarray/*<Color>*/ *colors;
31     Dynarray/*<char[ID_MAX_SIZE]>*/ *ids;
32     Edit_field *edit_field;
33     int selected;
34     ColorPicker color_picker;
35 };
36
37 LayerPtr point_layer_as_layer(PointLayer *point_layer)
38 {
39     LayerPtr layer = {
40         .type = LAYER_POINT,
41         .ptr = point_layer
42     };
43     return layer;
44 }
45
46 PointLayer *create_point_layer(void)
47 {
48     Lt *lt = create_lt();
49
50     PointLayer *point_layer = PUSH_LT(lt, nth_calloc(1, sizeof(PointLayer)), free);
51     if (point_layer == NULL) {
52         RETURN_LT(lt, NULL);
53     }
54     point_layer->lt = lt;
55
56     point_layer->state = POINT_LAYER_NORMAL_STATE;
57
58     point_layer->points = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray);
59     if (point_layer->points == NULL) {
60         RETURN_LT(lt, NULL);
61     }
62
63     point_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
64     if (point_layer->colors == NULL) {
65         RETURN_LT(lt, NULL);
66     }
67
68     point_layer->ids = PUSH_LT(lt, create_dynarray(sizeof(char) * ID_MAX_SIZE), destroy_dynarray);
69     if (point_layer->ids == NULL) {
70         RETURN_LT(lt, NULL);
71     }
72
73     point_layer->edit_field = PUSH_LT(
74         lt,
75         create_edit_field(
76             vec(5.0f, 5.0f),
77             rgba(0.0f, 0.0f, 0.0f, 1.0f)),
78         destroy_edit_field);
79     if (point_layer->edit_field == NULL) {
80         RETURN_LT(lt, NULL);
81     }
82
83     return point_layer;
84 }
85
86 PointLayer *create_point_layer_from_line_stream(LineStream *line_stream)
87 {
88     trace_assert(line_stream);
89
90     PointLayer *point_layer = create_point_layer();
91
92     size_t count = 0;
93     if (sscanf(
94             line_stream_next(line_stream),
95             "%zu",
96             &count) == EOF) {
97         log_fail("Could not read amount of points");
98         RETURN_LT(point_layer->lt, NULL);
99     }
100
101     char color_name[7];
102     char id[ID_MAX_SIZE];
103     float x, y;
104     for (size_t i = 0; i < count; ++i) {
105         if (sscanf(
106                 line_stream_next(line_stream),
107                 "%"STRINGIFY(ID_MAX_SIZE)"s%f%f%6s",
108                 id, &x, &y, color_name) < 0) {
109             log_fail("Could not read %dth goal\n", i);
110             RETURN_LT(point_layer->lt, NULL);
111         }
112         const Color color = hexstr(color_name);
113         const Point point = vec(x, y);
114
115         dynarray_push(point_layer->colors, &color);
116         dynarray_push(point_layer->points, &point);
117         dynarray_push(point_layer->ids, id);
118     }
119
120     point_layer->selected = -1;
121
122     point_layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
123
124     return point_layer;
125 }
126
127 void destroy_point_layer(PointLayer *point_layer)
128 {
129     trace_assert(point_layer);
130     RETURN_LT0(point_layer->lt);
131 }
132
133 int point_layer_render(const PointLayer *point_layer,
134                        Camera *camera,
135                        int active)
136 {
137     trace_assert(point_layer);
138     trace_assert(camera);
139
140     const int n = (int) dynarray_count(point_layer->points);
141     Point *points = dynarray_data(point_layer->points);
142     Color *colors = dynarray_data(point_layer->colors);
143
144     for (int i = 0; i < n; ++i) {
145         const Triangle t = triangle_mat3x3_product(
146             equilateral_triangle(),
147             mat3x3_product(
148                 trans_mat(points[i].x, points[i].y),
149                 scale_mat(POINT_LAYER_ELEMENT_RADIUS)));
150
151         const Color color = color_scale(
152             colors[i],
153             rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
154
155         if (i == point_layer->selected) {
156             const Triangle t0 = triangle_mat3x3_product(
157                 equilateral_triangle(),
158                 mat3x3_product(
159                     trans_mat(points[i].x, points[i].y),
160                     scale_mat(15.0f)));
161
162             if (camera_fill_triangle(camera, t0, color_invert(color)) < 0) {
163                 return -1;
164             }
165         }
166
167         if (camera_fill_triangle(camera, t, color) < 0) {
168             return -1;
169         }
170
171         /* TODO(#854): The ids of PointLayer are not displayed constantly */
172     }
173
174     if (point_layer->state == POINT_LAYER_ID_EDITING_STATE) {
175         /* TODO(#855): PointLayer edit field is not scaled on zoom */
176         if (edit_field_render(
177                 point_layer->edit_field,
178                 camera,
179                 camera_point(camera, points[point_layer->selected])) < 0) {
180             return -1;
181         }
182     }
183
184     if (active && color_picker_render(&point_layer->color_picker, camera) < 0) {
185         return -1;
186     }
187
188
189     return 0;
190 }
191
192
193 static int point_layer_mouse_button(PointLayer *point_layer,
194                                     const SDL_MouseButtonEvent *event,
195                                     const Camera *camera)
196 {
197     trace_assert(point_layer);
198     trace_assert(event);
199
200     if (point_layer->state == POINT_LAYER_NORMAL_STATE &&
201         event->type == SDL_MOUSEBUTTONDOWN &&
202         event->button == SDL_BUTTON_LEFT) {
203         const int n = (int) dynarray_count(point_layer->points);
204         const Point *points = dynarray_data(point_layer->points);
205         const Point point = camera_map_screen(camera, event->x, event->y);
206         const Color color = color_picker_rgba(&point_layer->color_picker);
207
208         for (int i = 0; i < n; ++i) {
209             if (vec_length(vec_sub(points[i], point)) < POINT_LAYER_ELEMENT_RADIUS) {
210                 point_layer->selected = i;
211                 return 0;
212             }
213         }
214
215         char id[ID_MAX_SIZE];
216
217         for (size_t i = 0; i < ID_MAX_SIZE - 1; ++i) {
218             id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
219         }
220         id[ID_MAX_SIZE - 1] = '\0';
221
222         dynarray_push(point_layer->points, &point);
223         dynarray_push(point_layer->colors, &color);
224         dynarray_push(point_layer->ids, id);
225     }
226
227     return 0;
228 }
229
230 static
231 int point_layer_keyboard(PointLayer *point_layer,
232                          const SDL_KeyboardEvent *key)
233 {
234     trace_assert(point_layer);
235     trace_assert(key);
236
237     switch(point_layer->state) {
238     case POINT_LAYER_NORMAL_STATE: {
239         if (key->type == SDL_KEYDOWN) {
240             switch (key->keysym.sym) {
241             case SDLK_DELETE: {
242                 if (0 <= point_layer->selected && point_layer->selected < (int) dynarray_count(point_layer->points)) {
243                     dynarray_delete_at(point_layer->points, (size_t) point_layer->selected);
244                     dynarray_delete_at(point_layer->colors, (size_t) point_layer->selected);
245                     dynarray_delete_at(point_layer->ids, (size_t) point_layer->selected);
246                 }
247                 point_layer->selected = -1;
248             } break;
249
250             case SDLK_F2: {
251                 if (point_layer->selected >= 0) {
252                     char *ids = dynarray_data(point_layer->ids);
253                     point_layer->state = POINT_LAYER_ID_EDITING_STATE;
254                     edit_field_replace(
255                         point_layer->edit_field,
256                         ids + ID_MAX_SIZE * point_layer->selected);
257                     SDL_StartTextInput();
258                 }
259             } break;
260
261             default: {}
262             }
263         }
264     } break;
265
266     case POINT_LAYER_ID_EDITING_STATE: {
267         if (edit_field_keyboard(point_layer->edit_field, key) < 0) {
268             return -1;
269         }
270
271         if (key->type == SDL_KEYDOWN) {
272             switch(key->keysym.sym) {
273             case SDLK_RETURN: {
274                 char *ids = dynarray_data(point_layer->ids);
275                 const char *text = edit_field_as_text(point_layer->edit_field);
276                 size_t n = max_size_t(strlen(text), ID_MAX_SIZE - 1);
277                 memcpy(ids + point_layer->selected * ID_MAX_SIZE, text, n);
278                 *(ids + point_layer->selected * ID_MAX_SIZE + n) = '\0';
279                 point_layer->state = POINT_LAYER_NORMAL_STATE;
280                 SDL_StopTextInput();
281             } break;
282
283             case SDLK_ESCAPE: {
284                 point_layer->state = POINT_LAYER_NORMAL_STATE;
285                 SDL_StopTextInput();
286             } break;
287             }
288         }
289     } break;
290     }
291
292
293     return 0;
294 }
295
296 static
297 int point_layer_text_input(PointLayer *point_layer,
298                            const SDL_TextInputEvent *text_input)
299 {
300     trace_assert(point_layer);
301     trace_assert(text_input);
302
303     if (point_layer->state == POINT_LAYER_ID_EDITING_STATE) {
304         /* TODO(#856): Special development keybindings interfere with id editing field */
305         return edit_field_text_input(point_layer->edit_field, text_input);
306     }
307
308     return 0;
309 }
310
311 int point_layer_event(PointLayer *point_layer,
312                       const SDL_Event *event,
313                       const Camera *camera)
314 {
315     trace_assert(point_layer);
316     trace_assert(event);
317     trace_assert(camera);
318
319     int selected = 0;
320     if (color_picker_event(
321             &point_layer->color_picker,
322             event,
323             &selected) < 0) {
324         return -1;
325     }
326
327     if (selected) {
328         return 0;
329     }
330
331     switch(event->type) {
332     case SDL_MOUSEBUTTONDOWN:
333     case SDL_MOUSEBUTTONUP:
334         return point_layer_mouse_button(
335             point_layer,
336             &event->button,
337             camera);
338
339     case SDL_KEYDOWN:
340     case SDL_KEYUP:
341         return point_layer_keyboard(
342             point_layer,
343             &event->key);
344
345     case SDL_TEXTINPUT:
346         return point_layer_text_input(
347             point_layer,
348             &event->text);
349     }
350
351     return 0;
352 }
353
354 size_t point_layer_count(const PointLayer *point_layer)
355 {
356     trace_assert(point_layer);
357     return dynarray_count(point_layer->points);
358 }
359
360 const Point *point_layer_points(const PointLayer *point_layer)
361 {
362     trace_assert(point_layer);
363     return dynarray_data(point_layer->points);
364 }
365
366 const Color *point_layer_colors(const PointLayer *point_layer)
367 {
368     trace_assert(point_layer);
369     return dynarray_data(point_layer->colors);
370 }
371
372 const char *point_layer_ids(const PointLayer *point_layer)
373 {
374     trace_assert(point_layer);
375     return dynarray_data(point_layer->ids);
376 }
377
378 int point_layer_dump_stream(const PointLayer *point_layer,
379                             FILE *filedump)
380 {
381     trace_assert(point_layer);
382     trace_assert(filedump);
383
384     size_t n = dynarray_count(point_layer->ids);
385     char *ids = dynarray_data(point_layer->ids);
386     Point *points = dynarray_data(point_layer->points);
387     Color *colors = dynarray_data(point_layer->colors);
388
389     fprintf(filedump, "%zd\n", n);
390     for (size_t i = 0; i < n; ++i) {
391         fprintf(filedump, "%s %f %f ",
392                 ids + ID_MAX_SIZE * i,
393                 points[i].x, points[i].y);
394         color_hex_to_stream(colors[i], filedump);
395         fprintf(filedump, "\n");
396     }
397
398     return 0;
399 }