]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor/label_layer.c
(#892) Add ColorPicker to LabelLayer
[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
18 #define LABEL_LAYER_ID_MAX_SIZE 36
19
20 typedef enum {
21     LABEL_LAYER_IDLE = 0,
22     LABEL_LAYER_MOVE
23 } LabelLayerState;
24
25 // TODO: LabelLayer cannot specify the color of the labels
26 // TODO: LabelLayer cannot add the labels
27 // TODO: LabelLayer cannot modify the labels' text
28 // TODO: 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 };
39
40 LayerPtr label_layer_as_layer(LabelLayer *label_layer)
41 {
42     LayerPtr layer = {
43         .ptr = label_layer,
44         .type = LAYER_LABEL
45     };
46     return layer;
47 }
48
49 LabelLayer *create_label_layer(void)
50 {
51     Lt *lt = create_lt();
52
53     LabelLayer *label_layer = PUSH_LT(
54         lt, nth_calloc(1, sizeof(LabelLayer)), free);
55     if (label_layer == NULL) {
56         RETURN_LT(lt, NULL);
57     }
58     label_layer->lt = lt;
59
60     label_layer->ids = PUSH_LT(
61         lt,
62         create_dynarray(sizeof(char) * LABEL_LAYER_ID_MAX_SIZE),
63         destroy_dynarray);
64     if (label_layer->ids == NULL) {
65         RETURN_LT(lt, NULL);
66     }
67
68     label_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray);
69     if (label_layer->positions == NULL) {
70         RETURN_LT(lt, NULL);
71     }
72
73     label_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray);
74     if (label_layer->colors == NULL) {
75         RETURN_LT(lt, NULL);
76     }
77
78     label_layer->texts = PUSH_LT(lt, create_dynarray(sizeof(char*)), destroy_dynarray);
79     if (label_layer->texts == NULL) {
80         RETURN_LT(lt, NULL);
81     }
82
83     label_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
84     label_layer->selected = -1;
85
86     return label_layer;
87 }
88
89 LabelLayer *create_label_layer_from_line_stream(LineStream *line_stream)
90 {
91     trace_assert(line_stream);
92     LabelLayer *label_layer = create_label_layer();
93
94     if (label_layer == NULL) {
95         RETURN_LT(label_layer->lt, NULL);
96     }
97
98     const char *line = line_stream_next(line_stream);
99     if (line == NULL) {
100         log_fail("Could not read amount of labels\n");
101         RETURN_LT(label_layer->lt, NULL);
102     }
103
104     size_t n = 0;
105     if (sscanf(line, "%zu", &n) == EOF) {
106         log_fail("Could not parse amount of labels\n");
107         RETURN_LT(label_layer->lt, NULL);
108     }
109
110     for (size_t i = 0; i < n; ++i) {
111         char hex[7];
112         char id[LABEL_LAYER_ID_MAX_SIZE];
113         Point position;
114
115         line = line_stream_next(line_stream);
116         if (line == NULL) {
117             log_fail("Could not read label meta info\n");
118             RETURN_LT(label_layer->lt, NULL);
119         }
120
121         if (sscanf(
122                 line,
123                 "%"STRINGIFY(LABEL_LAYER_ID_MAX_SIZE)"s%f%f%6s\n",
124                 id, &position.x, &position.y, hex) == EOF) {
125             log_fail("Could not parse label meta info\n");
126             RETURN_LT(label_layer->lt, NULL);
127         }
128
129         Color color = hexstr(hex);
130
131         dynarray_push(label_layer->ids, id);
132         dynarray_push(label_layer->positions, &position);
133         dynarray_push(label_layer->colors, &color);
134
135         line = line_stream_next(line_stream);
136         if (line == NULL) {
137             log_fail("Could not read label text\n");
138         }
139
140         char *label_text = PUSH_LT(label_layer->lt, string_duplicate(line, NULL), free);
141         trim_endline(label_text);
142         dynarray_push(label_layer->texts, &label_text);
143     }
144
145     return label_layer;
146 }
147
148 void destroy_label_layer(LabelLayer *label_layer)
149 {
150     trace_assert(label_layer);
151     destroy_lt(label_layer->lt);
152 }
153
154 int label_layer_render(const LabelLayer *label_layer,
155                        Camera *camera,
156                        int active)
157 {
158     trace_assert(label_layer);
159     trace_assert(camera);
160
161     if (active && color_picker_render(&label_layer->color_picker, camera) < 0) {
162         return -1;
163     }
164
165     size_t n = dynarray_count(label_layer->ids);
166     Point *positions = dynarray_data(label_layer->positions);
167     Color *colors = dynarray_data(label_layer->colors);
168     char **texts = dynarray_data(label_layer->texts);
169
170     /* TODO(#891): LabelLayer doesn't show the final position of Label after the animation */
171     for (size_t i = 0; i < n; ++i) {
172         if (camera_render_text(
173                 camera,
174                 texts[i],
175                 LABELS_SIZE,
176                 color_scale(
177                     colors[i],
178                     rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)),
179                 positions[i]) < 0) {
180             return -1;
181         }
182     }
183
184     return 0;
185 }
186
187 static
188 int label_layer_element_at(LabelLayer *label_layer,
189                            const Sprite_font *font,
190                            Point position)
191 {
192     trace_assert(label_layer);
193
194     const size_t n = dynarray_count(label_layer->texts);
195     char **texts = dynarray_data(label_layer->texts);
196     Point *positions = dynarray_data(label_layer->positions);
197
198     for (size_t i = 0; i < n; ++i) {
199         Rect boundary = sprite_font_boundary_box(
200             font,
201             positions[i],
202             LABELS_SIZE,
203             texts[i]);
204
205         if (rect_contains_point(boundary, position)) {
206             return (int) i;
207         }
208     }
209
210     return -1;
211 }
212
213 static
214 int label_layer_idle_event(LabelLayer *label_layer,
215                            const SDL_Event *event,
216                            const Camera *camera)
217 {
218     trace_assert(label_layer);
219     trace_assert(event);
220     trace_assert(camera);
221
222     switch (event->type) {
223     case SDL_MOUSEBUTTONDOWN: {
224         switch (event->button.button) {
225         case SDL_BUTTON_LEFT: {
226             const Point position = camera_map_screen(
227                 camera,
228                 event->button.x,
229                 event->button.y);
230
231             const int element = label_layer_element_at(
232                 label_layer,
233                 camera_font(camera),
234                 position);
235
236             if (element >= 0) {
237                 label_layer->selected = element;
238                 label_layer->state = LABEL_LAYER_MOVE;
239             }
240         } break;
241         }
242     } break;
243     }
244
245     return 0;
246 }
247
248 static
249 int label_layer_move_event(LabelLayer *label_layer,
250                            const SDL_Event *event,
251                            const Camera *camera)
252 {
253     trace_assert(label_layer);
254     trace_assert(event);
255     trace_assert(camera);
256     trace_assert(label_layer->selected >= 0);
257
258     switch (event->type) {
259     case SDL_MOUSEMOTION: {
260         Point *positions = dynarray_data(label_layer->positions);
261         positions[label_layer->selected] =
262             camera_map_screen(
263                 camera,
264                 event->motion.x,
265                 event->motion.y);
266     } break;
267
268     case SDL_MOUSEBUTTONUP: {
269         switch (event->button.button) {
270         case SDL_BUTTON_LEFT: {
271             label_layer->state = LABEL_LAYER_IDLE;
272         } break;
273         }
274     } break;
275     }
276
277     return 0;
278 }
279
280 int label_layer_event(LabelLayer *label_layer,
281                       const SDL_Event *event,
282                       const Camera *camera)
283 {
284     trace_assert(label_layer);
285     trace_assert(event);
286     trace_assert(camera);
287
288     switch (label_layer->state) {
289     case LABEL_LAYER_IDLE:
290         return label_layer_idle_event(label_layer, event, camera);
291
292     case LABEL_LAYER_MOVE:
293         return label_layer_move_event(label_layer, event, camera);
294     }
295
296     return 0;
297 }
298
299 size_t label_layer_count(const LabelLayer *label_layer)
300 {
301     return dynarray_count(label_layer->ids);
302 }
303
304 char *label_layer_ids(const LabelLayer *label_layer)
305 {
306     return dynarray_data(label_layer->ids);
307 }
308
309 Point *label_layer_positions(const LabelLayer *label_layer)
310 {
311     return dynarray_data(label_layer->positions);
312 }
313
314 Color *label_layer_colors(const LabelLayer *label_layer)
315 {
316     return dynarray_data(label_layer->colors);
317 }
318
319 char **labels_layer_texts(const LabelLayer *label_layer)
320 {
321     return dynarray_data(label_layer->texts);
322 }
323
324 int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump)
325 {
326     trace_assert(label_layer);
327     trace_assert(filedump);
328
329     size_t n = dynarray_count(label_layer->ids);
330     char *ids = dynarray_data(label_layer->ids);
331     Point *positions = dynarray_data(label_layer->positions);
332     Color *colors = dynarray_data(label_layer->colors);
333     char **texts = dynarray_data(label_layer->texts);
334
335     fprintf(filedump, "%zd\n", n);
336     for (size_t i = 0; i < n; ++i) {
337         fprintf(filedump, "%s %f %f ",
338                 ids + LABEL_LAYER_ID_MAX_SIZE * i,
339                 positions[i].x, positions[i].y);
340         color_hex_to_stream(colors[i], filedump);
341         fprintf(filedump, "\n%s\n", texts[i]);
342     }
343
344     return 0;
345 }