]> git.lizzy.rs Git - nothing.git/blobdiff - src/game/level/level_editor/point_layer.c
(#1048) Implement copy-pasting for PointLayer
[nothing.git] / src / game / level / level_editor / point_layer.c
index 0bcdb3bc93e20ce78f43a64168a8c1622050c36e..e32a44feaf20c7f9a8cf646f53edd205e8e3dbbd 100644 (file)
@@ -13,6 +13,7 @@
 #include "ui/edit_field.h"
 #include "./point_layer.h"
 #include "math/extrema.h"
+#include "math/mat3x3.h"
 #include "./color_picker.h"
 #include "undo_history.h"
 
 #define POINT_LAYER_ID_TEXT_SIZE vec(2.0f, 2.0f)
 #define POINT_LAYER_ID_TEXT_COLOR COLOR_BLACK
 
-
 typedef enum {
     POINT_LAYER_IDLE = 0,
     POINT_LAYER_EDIT_ID,
-    POINT_LAYER_MOVE
+    POINT_LAYER_MOVE,
+    POINT_LAYER_RECOLOR
 } PointLayerState;
 
 struct PointLayer
@@ -34,15 +35,23 @@ struct PointLayer
     Dynarray/*<Point>*/ *positions;
     Dynarray/*<Color>*/ *colors;
     Dynarray/*<char[ID_MAX_SIZE]>*/ *ids;
-    Edit_field *edit_field;
     int selected;
     ColorPicker color_picker;
-    Color prev_color;
-    Point prev_position;
+
+    Point inter_position;
+    Color inter_color;
+    Edit_field *edit_field;
 };
 
+typedef enum {
+    UNDO_ADD,
+    UNDO_DELETE,
+    UNDO_UPDATE
+} UndoType;
+
 typedef struct {
     UndoType type;
+    PointLayer *layer;
     Point position;
     Color color;
     char id[ID_MAX_SIZE];
@@ -51,14 +60,19 @@ typedef struct {
 
 static
 UndoContext point_layer_create_undo_context(PointLayer *point_layer,
-                                            size_t index,
                                             UndoType type)
 {
     UndoContext undo_context;
 
+    size_t index =
+        type == UNDO_ADD
+        ? dynarray_count(point_layer->positions) - 1
+        : (size_t) point_layer->selected;
+
     undo_context.type = type;
-    undo_context.position = point_layer->prev_position;
-    undo_context.color = point_layer->prev_color;
+    undo_context.layer = point_layer;
+    dynarray_copy_to(point_layer->positions, &undo_context.position, index);
+    dynarray_copy_to(point_layer->colors, &undo_context.color, index);
     dynarray_copy_to(point_layer->ids, &undo_context.id, index);
     undo_context.index = index;
 
@@ -66,12 +80,13 @@ UndoContext point_layer_create_undo_context(PointLayer *point_layer,
 }
 
 static
-void point_layer_undo(void *layer, Context context)
+void point_layer_undo(void *context, size_t context_size)
 {
-    trace_assert(layer);
-    PointLayer *point_layer = layer;
+    trace_assert(context);
+    trace_assert(sizeof(UndoContext) == context_size);
 
-    UndoContext *undo_context = (UndoContext *)context.data;
+    UndoContext *undo_context = context;
+    PointLayer *point_layer = undo_context->layer;
 
     switch (undo_context->type) {
     case UNDO_ADD: {
@@ -96,6 +111,16 @@ void point_layer_undo(void *layer, Context context)
     }
 }
 
+#define UNDO_PUSH(LAYER, HISTORY, UNDO_TYPE)                            \
+    do {                                                                \
+        UndoContext context = point_layer_create_undo_context(LAYER, UNDO_TYPE); \
+        undo_history_push(                                              \
+            HISTORY,                                                    \
+            point_layer_undo,                                           \
+            &context,                                                   \
+            sizeof(context));                                           \
+    } while(0)
+
 LayerPtr point_layer_as_layer(PointLayer *point_layer)
 {
     LayerPtr layer = {
@@ -182,7 +207,6 @@ PointLayer *create_point_layer_from_line_stream(LineStream *line_stream)
     point_layer->selected = -1;
 
     point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
-    point_layer->prev_color = COLOR_RED;
 
     return point_layer;
 }
@@ -193,6 +217,16 @@ void destroy_point_layer(PointLayer *point_layer)
     RETURN_LT0(point_layer->lt);
 }
 
+static inline
+Triangle element_shape(Point position, float scale)
+{
+    return triangle_mat3x3_product(
+        equilateral_triangle(),
+        mat3x3_product(
+            trans_mat_vec(position),
+            scale_mat(scale)));
+}
+
 int point_layer_render(const PointLayer *point_layer,
                        Camera *camera,
                        int active)
@@ -206,24 +240,25 @@ int point_layer_render(const PointLayer *point_layer,
     char *ids = dynarray_data(point_layer->ids);
 
     for (int i = 0; i < n; ++i) {
-        const Triangle t = triangle_mat3x3_product(
-            equilateral_triangle(),
-            mat3x3_product(
-                trans_mat(positions[i].x, positions[i].y),
-                scale_mat(POINT_LAYER_ELEMENT_RADIUS)));
-
         const Color color = color_scale(
-            colors[i],
+            point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selected
+            ? point_layer->inter_color
+            : colors[i],
             rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
 
-        if (i == point_layer->selected) {
-            const Triangle t0 = triangle_mat3x3_product(
-                equilateral_triangle(),
-                mat3x3_product(
-                    trans_mat(positions[i].x, positions[i].y),
-                    scale_mat(15.0f)));
+        const Point position =
+            point_layer->state == POINT_LAYER_MOVE && i == point_layer->selected
+            ? point_layer->inter_position
+            : positions[i];
 
-            if (camera_fill_triangle(camera, t0, color_invert(color)) < 0) {
+        // Selection Layer
+        if (active && i == point_layer->selected) {
+            if (camera_fill_triangle(
+                    camera,
+                    element_shape(
+                        position,
+                        POINT_LAYER_ELEMENT_RADIUS + 5.0f),
+                    color_invert(color)) < 0) {
                 return -1;
             }
 
@@ -233,15 +268,19 @@ int point_layer_render(const PointLayer *point_layer,
                     ids + ID_MAX_SIZE * i,
                     POINT_LAYER_ID_TEXT_SIZE,
                     POINT_LAYER_ID_TEXT_COLOR,
-                    positions[i]) < 0) {
+                    position) < 0) {
                 return -1;
             }
         }
 
-        if (camera_fill_triangle(camera, t, color) < 0) {
+        if (camera_fill_triangle(
+                camera,
+                element_shape(
+                    position,
+                    POINT_LAYER_ELEMENT_RADIUS),
+                color) < 0) {
             return -1;
         }
-
     }
 
     if (point_layer->state == POINT_LAYER_EDIT_ID) {
@@ -288,8 +327,6 @@ int point_layer_add_element(PointLayer *point_layer,
     trace_assert(point_layer);
     trace_assert(undo_history);
 
-    size_t index = dynarray_count(point_layer->positions);
-
     char id[ID_MAX_SIZE];
     for (size_t i = 0; i < ID_MAX_SIZE - 1; ++i) {
         id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
@@ -300,15 +337,7 @@ int point_layer_add_element(PointLayer *point_layer,
     dynarray_push(point_layer->colors, &color);
     dynarray_push(point_layer->ids, id);
 
-    UndoContext context =
-        point_layer_create_undo_context(point_layer, index, UNDO_ADD);
-
-    undo_history_push(
-        undo_history,
-        create_action(
-            point_layer,
-            point_layer_undo,
-            &context, sizeof(context)));
+    UNDO_PUSH(point_layer, undo_history, UNDO_ADD);
 
     return 0;
 }
@@ -320,13 +349,7 @@ void point_layer_delete_nth_element(PointLayer *point_layer,
 {
     trace_assert(point_layer);
 
-    UndoContext context = point_layer_create_undo_context(point_layer, i, UNDO_DELETE);
-    undo_history_push(
-        undo_history,
-        create_action(
-            point_layer,
-            point_layer_undo,
-            &context, sizeof(context)));
+    UNDO_PUSH(point_layer, undo_history, UNDO_DELETE);
 
     dynarray_delete_at(point_layer->positions, i);
     dynarray_delete_at(point_layer->colors, i);
@@ -354,27 +377,9 @@ int point_layer_idle_event(PointLayer *point_layer,
 
     if (selected) {
         if (point_layer->selected >= 0) {
-            Color *colors = dynarray_data(point_layer->colors);
-
-            if (!color_picker_drag(&point_layer->color_picker)) {
-                UndoContext context =
-                    point_layer_create_undo_context(point_layer, (size_t)point_layer->selected, UNDO_UPDATE);
-
-                undo_history_push(
-                    undo_history,
-                    create_action(
-                        point_layer,
-                        point_layer_undo,
-                        &context, sizeof(context)));
-
-                point_layer->prev_color =
-                    color_picker_rgba(&point_layer->color_picker);
-            }
-
-            colors[point_layer->selected] =
-                color_picker_rgba(&point_layer->color_picker);
+            point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
+            point_layer->state = POINT_LAYER_RECOLOR;
         }
-
         return 0;
     }
 
@@ -400,9 +405,7 @@ int point_layer_idle_event(PointLayer *point_layer,
                 point_layer->state = POINT_LAYER_MOVE;
                 point_layer->color_picker =
                     create_color_picker_from_rgba(colors[point_layer->selected]);
-
-                point_layer->prev_color = colors[point_layer->selected];
-                point_layer->prev_position = positions[point_layer->selected];
+                point_layer->inter_position = positions[point_layer->selected];
             }
         } break;
         }
@@ -430,6 +433,35 @@ int point_layer_idle_event(PointLayer *point_layer,
                 SDL_StartTextInput();
             }
         } break;
+
+        case SDLK_c: {
+            if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selected >= 0) {
+                Color *colors = dynarray_data(point_layer->colors);
+#define COLOR_BUFFER_SIZE 32
+                char buffer[COLOR_BUFFER_SIZE];
+                buffer[0] = '#';
+                color_hex_to_string(colors[point_layer->selected], buffer + 1, COLOR_BUFFER_SIZE - 1);
+                SDL_SetClipboardText(buffer);
+#undef COLOR_BUFFER_SIZE
+            }
+        } break;
+
+        case SDLK_v: {
+            if ((event->key.keysym.mod & KMOD_LCTRL) && SDL_HasClipboardText()) {
+                const char *hex = SDL_GetClipboardText();
+                if (strlen(hex) == 7 && hex[0] == '#') {
+                    Color color = hexstr(hex + 1);
+                    int x, y;
+                    SDL_GetMouseState(&x, &y);
+                    Point position = camera_map_screen(camera, x, y);
+                    point_layer_add_element(
+                        point_layer,
+                        position,
+                        color,
+                        undo_history);
+                }
+            }
+        } break;
         }
     } break;
     }
@@ -451,16 +483,7 @@ int point_layer_edit_id_event(PointLayer *point_layer,
     case SDL_KEYDOWN: {
         switch(event->key.keysym.sym) {
         case SDLK_RETURN: {
-            UndoContext context =
-                point_layer_create_undo_context(point_layer, (size_t) point_layer->selected, UNDO_UPDATE);
-
-            undo_history_push(
-                undo_history,
-                create_action(
-                    point_layer,
-                    point_layer_undo,
-                    &context,
-                    sizeof(context)));
+            UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
 
             char *id = dynarray_pointer_at(point_layer->ids, (size_t) point_layer->selected);
             const char *text = edit_field_as_text(point_layer->edit_field);
@@ -502,24 +525,19 @@ int point_layer_move_event(PointLayer *point_layer,
         case SDL_BUTTON_LEFT: {
             point_layer->state = POINT_LAYER_IDLE;
 
-            UndoContext context =
-                point_layer_create_undo_context(point_layer, (size_t) point_layer->selected, UNDO_UPDATE);
-
             // TODO(#1014): just click (without moving) on the point creates an undo history entry
-            undo_history_push(
-                undo_history,
-                create_action(
-                    point_layer,
-                    point_layer_undo,
-                    &context,
-                    sizeof(context)));
+            UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
+
+            dynarray_replace_at(
+                point_layer->positions,
+                (size_t) point_layer->selected,
+                &point_layer->inter_position);
         } break;
         }
     } break;
 
     case SDL_MOUSEMOTION: {
-        Point *positions = dynarray_data(point_layer->positions);
-        positions[point_layer->selected] =
+        point_layer->inter_position =
             camera_map_screen(camera, event->motion.x, event->motion.y);
     } break;
     }
@@ -527,6 +545,46 @@ int point_layer_move_event(PointLayer *point_layer,
     return 0;
 }
 
+static
+int point_layer_recolor_event(PointLayer *point_layer,
+                              const SDL_Event *event,
+                              const Camera *camera,
+                              UndoHistory *undo_history)
+{
+    trace_assert(point_layer);
+    trace_assert(event);
+    trace_assert(camera);
+    trace_assert(undo_history);
+    trace_assert(point_layer->selected >= 0);
+
+    int selected = 0;
+    if (color_picker_event(
+            &point_layer->color_picker,
+            event,
+            camera,
+            &selected) < 0) {
+        return -1;
+    }
+
+    if (selected) {
+        point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
+
+        if (!color_picker_drag(&point_layer->color_picker)) {
+            UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
+
+            dynarray_replace_at(
+                point_layer->colors,
+                (size_t) point_layer->selected,
+                &point_layer->inter_color);
+
+            point_layer->state = POINT_LAYER_IDLE;
+        }
+    }
+
+
+    return 0;
+}
+
 int point_layer_event(PointLayer *point_layer,
                       const SDL_Event *event,
                       const Camera *camera,
@@ -546,6 +604,9 @@ int point_layer_event(PointLayer *point_layer,
 
     case POINT_LAYER_MOVE:
         return point_layer_move_event(point_layer, event, camera, undo_history);
+
+    case POINT_LAYER_RECOLOR:
+        return point_layer_recolor_event(point_layer, event, camera, undo_history);
     }
 
     return 0;