]> git.lizzy.rs Git - nothing.git/blobdiff - src/game/level/level_editor/point_layer.c
Merge pull request #902 from tsoding/893
[nothing.git] / src / game / level / level_editor / point_layer.c
index b3543b6d28d4ad768eadcf7aaa4a8803cd830996..d774f9d85ecf1d44a84c1c9a2b4b18f71c67a0ba 100644 (file)
@@ -1,33 +1,53 @@
-#include <SDL2/SDL.h>
+#include <stdio.h>
 
-#include "system/stacktrace.h"
+#include <SDL.h>
+
+#include "dynarray.h"
+#include "game/camera.h"
 #include "system/line_stream.h"
 #include "system/log.h"
 #include "system/lt.h"
 #include "system/nth_alloc.h"
+#include "system/stacktrace.h"
 #include "system/str.h"
-#include "dynarray.h"
-#include "game/camera.h"
+#include "ui/edit_field.h"
 #include "./point_layer.h"
+#include "math/extrema.h"
+#include "./color_picker.h"
+
+#define POINT_LAYER_ELEMENT_RADIUS 10.0f
+
+typedef enum {
+    POINT_LAYER_NORMAL_STATE = 0,
+    POINT_LAYER_ID_EDITING_STATE
+} PointLayerState;
 
 struct PointLayer
 {
     Lt *lt;
-    Dynarray *points;
-    Dynarray *colors;
-    Dynarray *ids;
+    PointLayerState state;
+    Dynarray/*<Point>*/ *points;
+    Dynarray/*<Color>*/ *colors;
+    Dynarray/*<char[ID_MAX_SIZE]>*/ *ids;
+    Edit_field *edit_field;
+    int selected;
+    ColorPicker color_picker;
 };
 
-// TODO(#837): PointLayer does not allow to edit itself
+LayerPtr point_layer_as_layer(PointLayer *point_layer)
+{
+    LayerPtr layer = {
+        .type = LAYER_POINT,
+        .ptr = point_layer
+    };
+    return layer;
+}
 
 PointLayer *create_point_layer_from_line_stream(LineStream *line_stream)
 {
     trace_assert(line_stream);
 
     Lt *lt = create_lt();
-    if (lt == NULL) {
-        return NULL;
-    }
 
     PointLayer *point_layer = PUSH_LT(lt, nth_calloc(1, sizeof(PointLayer)), free);
     if (point_layer == NULL) {
@@ -35,6 +55,8 @@ PointLayer *create_point_layer_from_line_stream(LineStream *line_stream)
     }
     point_layer->lt = lt;
 
+    point_layer->state = POINT_LAYER_NORMAL_STATE;
+
     point_layer->points = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray);
     if (point_layer->points == NULL) {
         RETURN_LT(lt, NULL);
@@ -78,6 +100,20 @@ PointLayer *create_point_layer_from_line_stream(LineStream *line_stream)
         dynarray_push(point_layer->ids, id);
     }
 
+    point_layer->edit_field = PUSH_LT(
+        point_layer->lt,
+        create_edit_field(
+            vec(5.0f, 5.0f),
+            rgba(0.0f, 0.0f, 0.0f, 1.0f)),
+        destroy_edit_field);
+    if (point_layer->edit_field == NULL) {
+        RETURN_LT(point_layer->lt, NULL);
+    }
+
+    point_layer->selected = -1;
+
+    point_layer->color_picker.color = rgba(1.0f, 0.0f, 0.0f, 1.0f);
+
     return point_layer;
 }
 
@@ -88,29 +124,219 @@ void destroy_point_layer(PointLayer *point_layer)
 }
 
 int point_layer_render(const PointLayer *point_layer,
-                       Camera *camera)
+                       Camera *camera,
+                       float fa)
 {
     trace_assert(point_layer);
     trace_assert(camera);
 
+    const int n = (int) dynarray_count(point_layer->points);
+    Point *points = dynarray_data(point_layer->points);
+    Color *colors = dynarray_data(point_layer->colors);
+
+    for (int i = 0; i < n; ++i) {
+        const Triangle t = triangle_mat3x3_product(
+            equilateral_triangle(),
+            mat3x3_product(
+                trans_mat(points[i].x, points[i].y),
+                scale_mat(POINT_LAYER_ELEMENT_RADIUS)));
+
+        const Color color = color_scale(
+            colors[i],
+            rgba(1.0f, 1.0f, 1.0f, fa));
+
+        if (i == point_layer->selected) {
+            const Triangle t0 = triangle_mat3x3_product(
+                equilateral_triangle(),
+                mat3x3_product(
+                    trans_mat(points[i].x, points[i].y),
+                    scale_mat(15.0f)));
+
+            if (camera_fill_triangle(camera, t0, color_invert(color)) < 0) {
+                return -1;
+            }
+        }
+
+        if (camera_fill_triangle(camera, t, color) < 0) {
+            return -1;
+        }
+
+        /* TODO(#854): The ids of PointLayer are not displayed constantly */
+    }
+
+    if (point_layer->state == POINT_LAYER_ID_EDITING_STATE) {
+        /* TODO(#855): PointLayer edit field is not scaled on zoom */
+        if (edit_field_render(
+                point_layer->edit_field,
+                camera,
+                camera_point(camera, points[point_layer->selected])) < 0) {
+            return -1;
+        }
+    }
+
+    if (color_picker_render(&point_layer->color_picker, camera) < 0) {
+        return -1;
+    }
+
+
     return 0;
 }
 
-int point_layer_mouse_button(PointLayer *point_layer,
-                             const SDL_MouseButtonEvent *event)
+
+static int point_layer_mouse_button(PointLayer *point_layer,
+                                    const SDL_MouseButtonEvent *event,
+                                    const Camera *camera)
 {
     trace_assert(point_layer);
     trace_assert(event);
-    log_info("Clicked %d %d\n", event->x, event->y);
+
+    bool selected = false;
+    if (color_picker_mouse_button(
+            &point_layer->color_picker,
+            event,
+            &selected) < 0) {
+        return -1;
+    }
+
+    if (!selected &&
+        point_layer->state == POINT_LAYER_NORMAL_STATE &&
+        event->type == SDL_MOUSEBUTTONDOWN &&
+        event->button == SDL_BUTTON_LEFT) {
+        const int n = (int) dynarray_count(point_layer->points);
+        const Point *points = dynarray_data(point_layer->points);
+        const Point point = camera_map_screen(camera, event->x, event->y);
+
+        for (int i = 0; i < n; ++i) {
+            if (vec_length(vec_sub(points[i], point)) < POINT_LAYER_ELEMENT_RADIUS) {
+                point_layer->selected = i;
+                return 0;
+            }
+        }
+
+        char id[ID_MAX_SIZE];
+
+        for (size_t i = 0; i < ID_MAX_SIZE - 1; ++i) {
+            id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
+        }
+        id[ID_MAX_SIZE - 1] = '\0';
+
+        dynarray_push(point_layer->points, &point);
+        dynarray_push(point_layer->colors, &point_layer->color_picker.color);
+        dynarray_push(point_layer->ids, id);
+    }
+
     return 0;
 }
 
+static
+int point_layer_keyboard(PointLayer *point_layer,
+                         const SDL_KeyboardEvent *key)
+{
+    trace_assert(point_layer);
+    trace_assert(key);
+
+    switch(point_layer->state) {
+    case POINT_LAYER_NORMAL_STATE: {
+        if (key->type == SDL_KEYDOWN) {
+            switch (key->keysym.sym) {
+            case SDLK_DELETE: {
+                if (0 <= point_layer->selected && point_layer->selected < (int) dynarray_count(point_layer->points)) {
+                    dynarray_delete_at(point_layer->points, (size_t) point_layer->selected);
+                    dynarray_delete_at(point_layer->colors, (size_t) point_layer->selected);
+                    dynarray_delete_at(point_layer->ids, (size_t) point_layer->selected);
+                }
+                point_layer->selected = -1;
+            } break;
+
+            case SDLK_F2: {
+                if (point_layer->selected >= 0) {
+                    char *ids = dynarray_data(point_layer->ids);
+                    point_layer->state = POINT_LAYER_ID_EDITING_STATE;
+                    edit_field_replace(
+                        point_layer->edit_field,
+                        ids + ID_MAX_SIZE * point_layer->selected);
+                    SDL_StartTextInput();
+                }
+            } break;
+
+            default: {}
+            }
+        }
+    } break;
+
+    case POINT_LAYER_ID_EDITING_STATE: {
+        if (edit_field_keyboard(point_layer->edit_field, key) < 0) {
+            return -1;
+        }
+
+        if (key->type == SDL_KEYDOWN) {
+            switch(key->keysym.sym) {
+            case SDLK_RETURN: {
+                char *ids = dynarray_data(point_layer->ids);
+                const char *text = edit_field_as_text(point_layer->edit_field);
+                size_t n = max_size_t(strlen(text), ID_MAX_SIZE - 1);
+                memcpy(ids + point_layer->selected * ID_MAX_SIZE, text, n);
+                *(ids + point_layer->selected * ID_MAX_SIZE + n) = '\0';
+                point_layer->state = POINT_LAYER_NORMAL_STATE;
+                SDL_StopTextInput();
+            } break;
+
+            case SDLK_ESCAPE: {
+                point_layer->state = POINT_LAYER_NORMAL_STATE;
+                SDL_StopTextInput();
+            } break;
+            }
+        }
+    } break;
+    }
+
+
+    return 0;
+}
+
+static
+int point_layer_text_input(PointLayer *point_layer,
+                           const SDL_TextInputEvent *text_input)
+{
+    trace_assert(point_layer);
+    trace_assert(text_input);
+
+    if (point_layer->state == POINT_LAYER_ID_EDITING_STATE) {
+        /* TODO(#856): Special development keybindings interfere with id editing field */
+        return edit_field_text_input(point_layer->edit_field, text_input);
+    }
+
+    return 0;
+}
 
 int point_layer_event(PointLayer *point_layer,
-                      const SDL_Event *event)
+                      const SDL_Event *event,
+                      const Camera *camera)
 {
     trace_assert(point_layer);
     trace_assert(event);
+    trace_assert(camera);
+
+    switch(event->type) {
+    case SDL_MOUSEBUTTONDOWN:
+    case SDL_MOUSEBUTTONUP:
+        return point_layer_mouse_button(
+            point_layer,
+            &event->button,
+            camera);
+
+    case SDL_KEYDOWN:
+    case SDL_KEYUP:
+        return point_layer_keyboard(
+            point_layer,
+            &event->key);
+
+    case SDL_TEXTINPUT:
+        return point_layer_text_input(
+            point_layer,
+            &event->text);
+    }
+
     return 0;
 }
 
@@ -137,3 +363,26 @@ const char *point_layer_ids(const PointLayer *point_layer)
     trace_assert(point_layer);
     return dynarray_data(point_layer->ids);
 }
+
+int point_layer_dump_stream(const PointLayer *point_layer,
+                            FILE *filedump)
+{
+    trace_assert(point_layer);
+    trace_assert(filedump);
+
+    size_t n = dynarray_count(point_layer->ids);
+    char *ids = dynarray_data(point_layer->ids);
+    Point *points = dynarray_data(point_layer->points);
+    Color *colors = dynarray_data(point_layer->colors);
+
+    fprintf(filedump, "%ld\n", n);
+    for (size_t i = 0; i < n; ++i) {
+        fprintf(filedump, "%s %f %f ",
+                ids + ID_MAX_SIZE * i,
+                points[i].x, points[i].y);
+        color_hex_to_stream(colors[i], filedump);
+        fprintf(filedump, "\n");
+    }
+
+    return 0;
+}