]> git.lizzy.rs Git - nothing.git/blobdiff - src/game/level/level_editor/rect_layer.c
Merge pull request #1046 from tsoding/704
[nothing.git] / src / game / level / level_editor / rect_layer.c
index e7ec41f1544c655a07735018ed00ffa3ccb8d260..d392a286b479bda52a64ae82f6714c8cfa3ec4b9 100644 (file)
 #include "system/line_stream.h"
 #include "color_picker.h"
 #include "system/str.h"
+#include "ui/edit_field.h"
+#include "undo_history.h"
 
-#define RECT_LAYER_ID_MAX_SIZE 36
-#define RECT_LAYER_SELECTION_THICCNESS 5.0f
-#define PROTO_AREA_THRESHOLD 10.0
+#define RECT_LAYER_SELECTION_THICCNESS 10.0f
+#define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
+#define CREATE_AREA_THRESHOLD 10.0
 
-// TODO(#942): RectLayer does not allow to move rectangle arround
 typedef enum {
     RECT_LAYER_IDLE = 0,
-    RECT_LAYER_PROTO,
-    RECT_LAYER_RESIZE
+    RECT_LAYER_CREATE,
+    // TODO(#955): Rectangles in Level Editor have only one resize anchor to work with
+    RECT_LAYER_RESIZE,
+    RECT_LAYER_MOVE,
+    RECT_LAYER_ID_RENAME,
+    RECT_LAYER_RECOLOR
 } RectLayerState;
 
-/* TODO(#886): RectLayer does not allow to modify ids of Rects */
 struct RectLayer {
     Lt *lt;
     RectLayerState state;
@@ -30,11 +34,123 @@ struct RectLayer {
     Dynarray *rects;
     Dynarray *colors;
     ColorPicker color_picker;
-    Vec proto_begin;
-    Vec proto_end;
+    Vec create_begin;
+    Vec create_end;
     int selection;
+    Vec move_anchor;
+    Edit_field *id_edit_field;
+    // TODO(#1043): RectLayer should use intermediate values instead of previous ones
+    Color inter_color;
+    Rect inter_rect;
 };
 
+typedef enum {
+    UNDO_ADD,
+    UNDO_DELETE,
+    UNDO_UPDATE
+} UndoType;
+
+typedef struct {
+    UndoType type;
+    RectLayer *layer;
+    Rect rect;
+    Color color;
+    char id[RECT_LAYER_ID_MAX_SIZE];
+    size_t index;
+} UndoContext;
+
+static
+UndoContext create_undo_context(RectLayer *rect_layer, UndoType type)
+{
+    trace_assert(rect_layer);
+
+    size_t index = type == UNDO_ADD ? dynarray_count(rect_layer->rects) - 1 : (size_t) rect_layer->selection;
+
+    UndoContext undo_context;
+    undo_context.type = type;
+    undo_context.layer = rect_layer;
+    dynarray_copy_to(rect_layer->rects, &undo_context.rect, index);
+    dynarray_copy_to(rect_layer->colors, &undo_context.color, index);
+    dynarray_copy_to(rect_layer->ids, undo_context.id, index);
+    undo_context.index = index;
+
+    return undo_context;
+}
+
+static
+void rect_layer_undo(void *context, size_t context_size)
+{
+    trace_assert(context);
+    trace_assert(sizeof(UndoContext) == context_size);
+
+    UndoContext *undo_context = context;
+    RectLayer *rect_layer = undo_context->layer;
+
+    switch (undo_context->type) {
+    case UNDO_ADD: {
+        dynarray_delete_at(rect_layer->rects, undo_context->index);
+        dynarray_delete_at(rect_layer->colors, undo_context->index);
+        dynarray_delete_at(rect_layer->ids, undo_context->index);
+        rect_layer->selection = -1;
+    } break;
+
+    case UNDO_DELETE: {
+        dynarray_insert_before(rect_layer->rects, undo_context->index, &undo_context->rect);
+        dynarray_insert_before(rect_layer->colors, undo_context->index, &undo_context->color);
+        dynarray_insert_before(rect_layer->ids, undo_context->index, &undo_context->id);
+        rect_layer->selection = -1;
+    } break;
+
+    case UNDO_UPDATE: {
+        dynarray_replace_at(rect_layer->rects, undo_context->index, &undo_context->rect);
+        dynarray_replace_at(rect_layer->colors, undo_context->index, &undo_context->color);
+        dynarray_replace_at(rect_layer->ids, undo_context->index, &undo_context->id);
+    } break;
+    }
+}
+
+#define UNDO_PUSH(LAYER, HISTORY, UNDO_TYPE)                            \
+    do {                                                                \
+        UndoContext context = create_undo_context(LAYER, UNDO_TYPE);    \
+        undo_history_push(                                              \
+            HISTORY,                                                    \
+            rect_layer_undo,                                            \
+            &context,                                                   \
+            sizeof(context));                                           \
+    } while(0)
+
+
+static int rect_layer_add_rect(RectLayer *layer,
+                               Rect rect,
+                               Color color,
+                               UndoHistory *undo_history)
+{
+    trace_assert(layer);
+
+    if (dynarray_push(layer->rects, &rect) < 0) {
+        return -1;
+    }
+
+    if (dynarray_push(layer->colors, &color) < 0) {
+        return -1;
+    }
+
+    char id[RECT_LAYER_ID_MAX_SIZE];
+    for (size_t i = 0; i < RECT_LAYER_ID_MAX_SIZE - 1; ++i) {
+        id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
+    }
+    id[RECT_LAYER_ID_MAX_SIZE - 1] = '\0';
+
+    if (dynarray_push(layer->ids, id)) {
+        return -1;
+    }
+
+    UNDO_PUSH(layer, undo_history, UNDO_ADD);
+
+    return 0;
+}
+
+// TODO(#956): rect_layer_rect_at doesn't return rectangles according to some z-order
 static int rect_layer_rect_at(RectLayer *layer, Vec position)
 {
     trace_assert(layer);
@@ -51,10 +167,28 @@ static int rect_layer_rect_at(RectLayer *layer, Vec position)
     return -1;
 }
 
-static int rect_layer_delete_rect_at(RectLayer *layer, size_t i)
+static Rect rect_layer_resize_anchor(const Camera *camera, Rect boundary_rect)
+{
+    const Rect overlay_rect =
+        rect_scale(
+            camera_rect(camera, boundary_rect),
+            RECT_LAYER_SELECTION_THICCNESS * 0.5f);
+
+    return rect(
+        overlay_rect.x + overlay_rect.w,
+        overlay_rect.y + overlay_rect.h,
+        RECT_LAYER_SELECTION_THICCNESS * 2.0f,
+        RECT_LAYER_SELECTION_THICCNESS * 2.0f);
+}
+
+static int rect_layer_delete_rect_at(RectLayer *layer,
+                                     size_t i,
+                                     UndoHistory *undo_history)
 {
     trace_assert(layer);
 
+    UNDO_PUSH(layer, undo_history, UNDO_DELETE);
+
     dynarray_delete_at(layer->rects, i);
     dynarray_delete_at(layer->colors, i);
     dynarray_delete_at(layer->ids, i);
@@ -62,40 +196,253 @@ static int rect_layer_delete_rect_at(RectLayer *layer, size_t i)
     return 0;
 }
 
-static Rect rect_layer_resize_anchor(const RectLayer *layer, size_t i)
-{
-    Rect *rects = dynarray_data(layer->rects);
-    return rect(rects[i].x + rects[i].w,
-                rects[i].y + rects[i].h,
-                RECT_LAYER_SELECTION_THICCNESS * 2.0f,
-                RECT_LAYER_SELECTION_THICCNESS * 2.0f);
-}
-
-static int rect_layer_add_rect(RectLayer *layer, Rect rect, Color color)
+static int rect_layer_event_idle(RectLayer *layer,
+                                 const SDL_Event *event,
+                                 const Camera *camera,
+                                 UndoHistory *undo_history)
 {
     trace_assert(layer);
+    trace_assert(event);
+    trace_assert(camera);
 
-    if (dynarray_push(layer->rects, &rect) < 0) {
+    int color_changed = 0;
+    if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
         return -1;
     }
 
-    if (dynarray_push(layer->colors, &color) < 0) {
-        return -1;
+    if (color_changed) {
+        if (layer->selection >= 0) {
+            dynarray_copy_to(layer->colors, &layer->inter_color, (size_t)layer->selection);
+            layer->state = RECT_LAYER_RECOLOR;
+        }
+        return 0;
     }
 
-    char id[RECT_LAYER_ID_MAX_SIZE];
-    for (size_t i = 0; i < RECT_LAYER_ID_MAX_SIZE - 1; ++i) {
-        id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
+    switch (event->type) {
+    case SDL_MOUSEBUTTONDOWN: {
+        switch (event->button.button) {
+        case SDL_BUTTON_LEFT: {
+            Point position = camera_map_screen(
+                camera,
+                event->button.x,
+                event->button.y);
+            int rect_at_position =
+                rect_layer_rect_at(layer, position);
+
+            Rect *rects = dynarray_data(layer->rects);
+            Color *colors = dynarray_data(layer->colors);
+
+            if (rect_at_position >= 0) {
+                layer->selection = rect_at_position;
+                layer->state = RECT_LAYER_MOVE;
+                layer->move_anchor =
+                    vec_sub(
+                        position,
+                        vec(
+                            rects[layer->selection].x,
+                            rects[layer->selection].y));
+                layer->color_picker =
+                    create_color_picker_from_rgba(colors[rect_at_position]);
+
+                dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) rect_at_position);
+            } else if (layer->selection >= 0 && rect_contains_point(
+                           rect_layer_resize_anchor(
+                               camera,
+                               rects[layer->selection]),
+                           vec(
+                               (float) event->button.x,
+                               (float) event->button.y))) {
+                layer->state = RECT_LAYER_RESIZE;
+                dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) layer->selection);
+            } else {
+                layer->selection = rect_at_position;
+
+                if (layer->selection < 0) {
+                    layer->state = RECT_LAYER_CREATE;
+                    layer->create_begin = position;
+                    layer->create_end = position;
+                }
+            }
+        } break;
+        }
+    } break;
+
+    case SDL_KEYDOWN: {
+        switch (event->key.keysym.sym) {
+        case SDLK_DELETE: {
+            if (layer->selection >= 0) {
+                rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
+                layer->selection = -1;
+            }
+        } break;
+
+        case SDLK_F2: {
+            if (layer->selection >= 0) {
+                const char *ids = dynarray_data(layer->ids);
+                Color *colors = dynarray_data(layer->colors);
+
+                edit_field_restyle(
+                    layer->id_edit_field,
+                    RECT_LAYER_ID_LABEL_SIZE,
+                    color_invert(colors[layer->selection]));
+
+                layer->state = RECT_LAYER_ID_RENAME;
+                edit_field_replace(
+                    layer->id_edit_field,
+                    ids + layer->selection * RECT_LAYER_ID_MAX_SIZE);
+                SDL_StartTextInput();
+            }
+        } break;
+        }
+    } break;
     }
-    id[RECT_LAYER_ID_MAX_SIZE - 1] = '\0';
 
-    if (dynarray_push(layer->ids, id)) {
-        return -1;
+    return 0;
+}
+
+static int rect_layer_event_create(RectLayer *layer,
+                                   const SDL_Event *event,
+                                   const Camera *camera,
+                                   UndoHistory *undo_history)
+{
+    trace_assert(layer);
+    trace_assert(event);
+    trace_assert(camera);
+
+    switch (event->type) {
+    case SDL_MOUSEBUTTONUP: {
+        switch (event->button.button) {
+        case SDL_BUTTON_LEFT: {
+            const Rect real_rect =
+                rect_from_points(
+                    layer->create_begin,
+                    layer->create_end);
+            const float area = real_rect.w * real_rect.h;
+
+            if (area >= CREATE_AREA_THRESHOLD) {
+                rect_layer_add_rect(
+                    layer,
+                    real_rect,
+                    color_picker_rgba(&layer->color_picker),
+                    undo_history);
+            } else {
+                log_info("The area is too small %f. Such small box won't be created.\n", area);
+            }
+            layer->state = RECT_LAYER_IDLE;
+        } break;
+        }
+    } break;
+
+    case SDL_MOUSEMOTION: {
+        layer->create_end = camera_map_screen(
+            camera,
+            event->motion.x,
+            event->motion.y);
+    } break;
     }
+    return 0;
+}
+
+static int rect_layer_event_resize(RectLayer *layer,
+                                   const SDL_Event *event,
+                                   const Camera *camera,
+                                   UndoHistory *undo_history)
+{
+    trace_assert(layer);
+    trace_assert(event);
+    trace_assert(camera);
+    trace_assert(layer->selection >= 0);
+
+    switch (event->type) {
+    case SDL_MOUSEMOTION: {
+        layer->inter_rect = rect_from_points(
+            vec(layer->inter_rect.x, layer->inter_rect.y),
+            vec_sum(
+                camera_map_screen(
+                    camera,
+                    event->button.x,
+                    event->button.y),
+                vec(RECT_LAYER_SELECTION_THICCNESS * -0.5f,
+                    RECT_LAYER_SELECTION_THICCNESS * -0.5f)));
+    } break;
+
+    case SDL_MOUSEBUTTONUP: {
+        layer->state = RECT_LAYER_IDLE;
+        UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
+        dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
+    } break;
+    }
+
+    return 0;
+}
+
+static int rect_layer_event_move(RectLayer *layer,
+                                 const SDL_Event *event,
+                                 const Camera *camera,
+                                 UndoHistory *undo_history)
+{
+    trace_assert(layer);
+    trace_assert(event);
+    trace_assert(camera);
+    trace_assert(layer->selection >= 0);
+
+    switch (event->type) {
+    case SDL_MOUSEMOTION: {
+        Point position = vec_sub(
+            camera_map_screen(
+                camera,
+                event->button.x,
+                event->button.y),
+            layer->move_anchor);
 
+        trace_assert(layer->selection >= 0);
+
+        layer->inter_rect.x = position.x;
+        layer->inter_rect.y = position.y;
+    } break;
+
+    case SDL_MOUSEBUTTONUP: {
+        layer->state = RECT_LAYER_IDLE;
+        UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
+        dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
+    } break;
+    }
     return 0;
 }
 
+static int rect_layer_event_id_rename(RectLayer *layer,
+                                      const SDL_Event *event,
+                                      const Camera *camera,
+                                      UndoHistory *undo_history)
+{
+    trace_assert(layer);
+    trace_assert(event);
+    trace_assert(camera);
+    trace_assert(layer->selection >= 0);
+
+    switch (event->type) {
+    case SDL_KEYDOWN: {
+        switch (event->key.keysym.sym) {
+        case SDLK_RETURN: {
+            UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
+
+            char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
+            memset(id, 0, RECT_LAYER_ID_MAX_SIZE);
+            memcpy(id, edit_field_as_text(layer->id_edit_field), RECT_LAYER_ID_MAX_SIZE - 1);
+            layer->state = RECT_LAYER_IDLE;
+            SDL_StopTextInput();
+        } break;
+
+        case SDLK_ESCAPE: {
+            layer->state = RECT_LAYER_IDLE;
+            SDL_StopTextInput();
+        } break;
+        }
+    } break;
+    }
+
+    return edit_field_event(layer->id_edit_field, event);
+}
 
 LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
 {
@@ -140,6 +487,16 @@ RectLayer *create_rect_layer(void)
         RETURN_LT(lt, NULL);
     }
 
+    layer->id_edit_field = PUSH_LT(
+        lt,
+        create_edit_field(
+            RECT_LAYER_ID_LABEL_SIZE,
+            COLOR_BLACK),
+        destroy_edit_field);
+    if (layer->id_edit_field == NULL) {
+        RETURN_LT(lt, NULL);
+    }
+
     layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
     layer->selection = -1;
 
@@ -208,43 +565,93 @@ int rect_layer_render(const RectLayer *layer, Camera *camera, int active)
     const size_t n = dynarray_count(layer->rects);
     Rect *rects = dynarray_data(layer->rects);
     Color *colors = dynarray_data(layer->colors);
+    const char *ids = dynarray_data(layer->ids);
 
+    // The Rectangles
     for (size_t i = 0; i < n; ++i) {
-        if (layer->selection == (int) i) {
-            if (active) {
-                const Color color = color_invert(colors[i]);
+        Rect rect = rects[i];
+        Color color = colors[i];
 
-                if (camera_fill_rect(
-                        camera,
-                        // TODO: thiccness of RectLayer selection should be probably based on zoom
-                        rect_scale(rects[i], RECT_LAYER_SELECTION_THICCNESS),
-                        color) < 0) {
-                    return -1;
-                }
+        if (layer->selection == (int) i) {
+            if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
+                rect = layer->inter_rect;
+            }
 
-                if (camera_fill_rect(
-                        camera,
-                        rect_layer_resize_anchor(layer, i),
-                        color) < 0) {
-                    return -1;
-                }
+            if (layer->state == RECT_LAYER_RECOLOR) {
+                color = layer->inter_color;
             }
         }
 
         if (camera_fill_rect(
                 camera,
-                rects[i],
+                rect,
                 color_scale(
-                    colors[i],
+                    color,
                     rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
             return -1;
         }
 
+        // Selection Overlay
+        if (active && (size_t) layer->selection == i) {
+            const Rect overlay_rect =
+                rect_scale(
+                    camera_rect(camera, rect),
+                    RECT_LAYER_SELECTION_THICCNESS * 0.5f);
+            const Color overlay_color = color_invert(color);
+
+            // Main Rectangle
+            if (camera_fill_rect(
+                    camera,
+                    rect,
+                    color_scale(
+                        color,
+                        rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
+                return -1;
+            }
+
+            if (camera_draw_thicc_rect_screen(
+                    camera,
+                    overlay_rect,
+                    overlay_color,
+                    RECT_LAYER_SELECTION_THICCNESS) < 0) {
+                return -1;
+            }
+
+            // Rectangle Id
+            if (layer->state == RECT_LAYER_ID_RENAME) {
+                // ID renaming Edit Field
+                if (edit_field_render_world(
+                        layer->id_edit_field,
+                        camera,
+                        rect_position(rect)) < 0) {
+                    return -1;
+                }
+            } else {
+                // Id text
+                if (camera_render_text(
+                        camera,
+                        ids + layer->selection * RECT_LAYER_ID_MAX_SIZE,
+                        RECT_LAYER_ID_LABEL_SIZE,
+                        color_invert(color),
+                        rect_position(rect)) < 0) {
+                    return -1;
+                }
+            }
+
+            // Resize Anchor
+            if (camera_fill_rect_screen(
+                    camera,
+                    rect_layer_resize_anchor(camera, rect),
+                    overlay_color) < 0) {
+                return -1;
+            }
+        }
     }
 
+    // Proto Rectangle
     const Color color = color_picker_rgba(&layer->color_picker);
-    if (layer->state == RECT_LAYER_PROTO) {
-        if (camera_fill_rect(camera, rect_from_points(layer->proto_begin, layer->proto_end), color) < 0) {
+    if (layer->state == RECT_LAYER_CREATE) {
+        if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
             return -1;
         }
     }
@@ -256,112 +663,64 @@ int rect_layer_render(const RectLayer *layer, Camera *camera, int active)
     return 0;
 }
 
-int rect_layer_event(RectLayer *layer, const SDL_Event *event, const Camera *camera)
+static
+int rect_layer_event_recolor(RectLayer *layer,
+                             const SDL_Event *event,
+                             const Camera *camera,
+                             UndoHistory *undo_history)
 {
     trace_assert(layer);
     trace_assert(event);
+    trace_assert(camera);
+    trace_assert(undo_history);
+    trace_assert(layer->selection >= 0);
 
-    int selected = 0;
-    if (color_picker_event(&layer->color_picker, event, &selected) < 0) {
+    int color_changed = 0;
+    if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
         return -1;
     }
 
-    if (selected) {
-        return 0;
+    if (color_changed) {
+        layer->inter_color = color_picker_rgba(&layer->color_picker);
+
+        if (!color_picker_drag(&layer->color_picker)) {
+            UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
+            dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color);
+            layer->state = RECT_LAYER_IDLE;
+        }
     }
 
-    const Color color = color_picker_rgba(&layer->color_picker);
-    if (layer->state == RECT_LAYER_PROTO) {
-        switch (event->type) {
-        case SDL_MOUSEBUTTONUP: {
-            switch (event->button.button) {
-            case SDL_BUTTON_LEFT: {
-                const Rect real_rect =
-                    rect_from_points(
-                        layer->proto_begin,
-                        layer->proto_end);
-                const float area = real_rect.w * real_rect.h;
-
-                if (area >= PROTO_AREA_THRESHOLD) {
-                    rect_layer_add_rect(layer, real_rect, color);
-                } else {
-                    log_info("The area is too small %f. Such small box won't be created.\n", area);
-                }
-                layer->state = RECT_LAYER_IDLE;
-            } break;
-            }
-        } break;
+    return 0;
+}
 
-        case SDL_MOUSEMOTION: {
-            layer->proto_end = camera_map_screen(
-                camera,
-                event->motion.x,
-                event->motion.y);
-        } break;
-        }
-    } else if (layer->state == RECT_LAYER_RESIZE) {
-        switch (event->type) {
-        case SDL_MOUSEMOTION: {
-            Rect *rects = dynarray_data(layer->rects);
-            trace_assert(layer->selection >= 0);
-            rects[layer->selection] = rect_from_points(
-                vec(rects[layer->selection].x, rects[layer->selection].y),
-                vec_sum(
-                    camera_map_screen(
-                        camera,
-                        event->button.x,
-                        event->button.y),
-                    vec(RECT_LAYER_SELECTION_THICCNESS * -0.5f,
-                        RECT_LAYER_SELECTION_THICCNESS * -0.5f)));
-        } break;
+int rect_layer_event(RectLayer *layer,
+                     const SDL_Event *event,
+                     const Camera *camera,
+                     UndoHistory *undo_history)
+{
+    trace_assert(layer);
+    trace_assert(event);
+    trace_assert(undo_history);
 
-        case SDL_MOUSEBUTTONUP: {
-            layer->state = RECT_LAYER_IDLE;
-        } break;
-        }
-    } else {
-        switch (event->type) {
-        case SDL_MOUSEBUTTONDOWN: {
-            switch (event->button.button) {
-            case SDL_BUTTON_LEFT: {
-                Point position = camera_map_screen(
-                    camera,
-                    event->button.x,
-                    event->button.y);
-
-                if (layer->selection >= 0 &&
-                    rect_contains_point(
-                        rect_layer_resize_anchor(
-                            layer,
-                            (size_t)layer->selection),
-                        position)) {
-                    layer->state = RECT_LAYER_RESIZE;
-                } else {
-                    layer->selection = rect_layer_rect_at(layer, position);
-
-                    if (layer->selection < 0) {
-                        layer->state = RECT_LAYER_PROTO;
-                        layer->proto_begin = position;
-                        layer->proto_end = position;
-                    }
-                }
-            } break;
-            }
-        } break;
+    switch (layer->state) {
+    case RECT_LAYER_IDLE:
+        return rect_layer_event_idle(layer, event, camera, undo_history);
 
-        case SDL_KEYDOWN: {
-            switch (event->key.keysym.sym) {
-            case SDLK_DELETE: {
-                if (layer->selection >= 0) {
-                    rect_layer_delete_rect_at(layer, (size_t) layer->selection);
-                    layer->selection = -1;
-                }
-            } break;
-            }
-        } break;
-        }
-    }
+    case RECT_LAYER_CREATE:
+        return rect_layer_event_create(layer, event, camera, undo_history);
+
+    case RECT_LAYER_RESIZE:
+        return rect_layer_event_resize(layer, event, camera, undo_history);
+
+    case RECT_LAYER_MOVE:
+        return rect_layer_event_move(layer, event, camera, undo_history);
 
+    case RECT_LAYER_ID_RENAME:
+        return rect_layer_event_id_rename(layer, event, camera, undo_history);
+
+    case RECT_LAYER_RECOLOR:
+        return rect_layer_event_recolor(layer, event, camera, undo_history);
+    }
 
     return 0;
 }