X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;ds=sidebyside;f=src%2Fgame%2Flevel%2Flevel_editor%2Frect_layer.c;h=7627425f8e7349e64945998fa7efe172c34ea39a;hb=fe418a0e5c29b7320fbb554a0a809ce86d9eeabb;hp=6442693d8b47b30e3159cb39cecf57b7582121e2;hpb=2f52e1acfe15e9100f6ff358b0d1b9f512eca94d;p=nothing.git diff --git a/src/game/level/level_editor/rect_layer.c b/src/game/level/level_editor/rect_layer.c index 6442693d..7627425f 100644 --- a/src/game/level/level_editor/rect_layer.c +++ b/src/game/level/level_editor/rect_layer.c @@ -1,3 +1,5 @@ +#include + #include "game/camera.h" #include "system/lt.h" #include "system/stacktrace.h" @@ -11,17 +13,28 @@ #include "color_picker.h" #include "system/str.h" #include "ui/edit_field.h" +#include "undo_history.h" +#include "game/level/action.h" +#include "action_picker.h" -#define RECT_LAYER_ID_MAX_SIZE 36 -#define RECT_LAYER_SELECTION_THICCNESS 5.0f +#define RECT_LAYER_SELECTION_THICCNESS 10.0f +#define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f) #define CREATE_AREA_THRESHOLD 10.0 +#define RECT_LAYER_GRID_ROWS 3 +#define RECT_LAYER_GRID_COLUMNS 4 + +static int clipboard = 0; +static Rect clipboard_rect; +static Color clipboard_color; typedef enum { RECT_LAYER_IDLE = 0, 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; struct RectLayer { @@ -30,17 +43,178 @@ struct RectLayer { Dynarray *ids; Dynarray *rects; Dynarray *colors; + Dynarray *actions; ColorPicker color_picker; - Vec create_begin; - Vec create_end; + ActionPicker action_picker; + Vec2f create_begin; + Vec2f create_end; int selection; - Vec move_anchor; + Vec2f move_anchor; // The mouse offset from the left-top + // corner of the rect during moving it Edit_field *id_edit_field; + Color inter_color; + Rect inter_rect; + int id_name_counter; + const char *id_name_prefix; + Grid *grid; }; -typedef int (*EventHandler)(RectLayer *layer, const SDL_Event *event, const Camera *camera); +typedef enum { + UNDO_ADD, + UNDO_DELETE, + UNDO_UPDATE, + UNDO_SWAP +} UndoType; + +// Delete, Update +typedef struct { + UndoType type; + RectLayer *layer; + size_t index; + Rect rect; + Color color; + Action action; + char id[ENTITY_MAX_ID_SIZE]; +} UndoElementContext; + +// Add +typedef struct { + UndoType type; + RectLayer *layer; + size_t index; +} UndoAddContext; + +// Swap +typedef struct { + UndoType type; + RectLayer *layer; + size_t index1; + size_t index2; +} UndoSwapContext; + +typedef union { + UndoType type; + UndoAddContext add; + UndoElementContext element; + UndoSwapContext swap; +} UndoContext; + +static +UndoContext create_undo_add_context(RectLayer *layer, size_t index) +{ + trace_assert(layer); + trace_assert(index < dynarray_count(layer->rects)); + + UndoContext undo_context; + undo_context.add.type = UNDO_ADD; + undo_context.add.layer = layer; + undo_context.add.index = index; + return undo_context; +} + +static +UndoContext create_undo_element_context(RectLayer *layer) +{ + trace_assert(layer); + size_t index = (size_t) layer->selection; + trace_assert(index < dynarray_count(layer->rects)); + + UndoContext undo_context; + undo_context.element.layer = layer; + undo_context.element.index = index; + dynarray_copy_to(layer->rects, &undo_context.element.rect, index); + dynarray_copy_to(layer->colors, &undo_context.element.color, index); + dynarray_copy_to(layer->ids, undo_context.element.id, index); + dynarray_copy_to(layer->actions, &undo_context.element.action, index); + return undo_context; +} + +static +UndoContext create_undo_update_context(RectLayer *rect_layer) +{ + UndoContext undo_context = create_undo_element_context(rect_layer); + undo_context.type = UNDO_UPDATE; + return undo_context; +} + +static +UndoContext create_undo_delete_context(RectLayer *rect_layer) +{ + UndoContext undo_context = create_undo_element_context(rect_layer); + undo_context.type = UNDO_DELETE; + return undo_context; +} + +static +UndoContext create_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2) +{ + UndoContext undo_context; + undo_context.swap.type = UNDO_SWAP; + undo_context.swap.layer = rect_layer; + undo_context.swap.index1 = index1; + undo_context.swap.index2 = index2; + 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; + + switch (undo_context->type) { + case UNDO_ADD: { + RectLayer *layer = undo_context->add.layer; + dynarray_delete_at(layer->rects, undo_context->add.index); + dynarray_delete_at(layer->colors, undo_context->add.index); + dynarray_delete_at(layer->ids, undo_context->add.index); + dynarray_delete_at(layer->actions, undo_context->add.index); + layer->selection = -1; + } break; + + case UNDO_DELETE: { + RectLayer *layer = undo_context->element.layer; + dynarray_insert_before(layer->rects, undo_context->element.index, &undo_context->element.rect); + dynarray_insert_before(layer->colors, undo_context->element.index, &undo_context->element.color); + dynarray_insert_before(layer->ids, undo_context->element.index, &undo_context->element.id); + dynarray_insert_before(layer->actions, undo_context->element.index, &undo_context->element.action); + layer->selection = -1; + } break; + + case UNDO_UPDATE: { + RectLayer *layer = undo_context->element.layer; + dynarray_replace_at(layer->rects, undo_context->element.index, &undo_context->element.rect); + dynarray_replace_at(layer->colors, undo_context->element.index, &undo_context->element.color); + dynarray_replace_at(layer->ids, undo_context->element.index, &undo_context->element.id); + dynarray_replace_at(layer->actions, undo_context->element.index, &undo_context->element.action); + } break; -static int rect_layer_add_rect(RectLayer *layer, Rect rect, Color color) + case UNDO_SWAP: { + RectLayer *layer = undo_context->element.layer; + dynarray_swap(layer->rects, undo_context->swap.index1, undo_context->swap.index2); + dynarray_swap(layer->colors, undo_context->swap.index1, undo_context->swap.index2); + dynarray_swap(layer->ids, undo_context->swap.index1, undo_context->swap.index2); + dynarray_swap(layer->actions, undo_context->swap.index1, undo_context->swap.index2); + } break; + } +} + +#define UNDO_PUSH(HISTORY, CONTEXT) \ + do { \ + UndoContext context = (CONTEXT); \ + 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); @@ -52,27 +226,33 @@ static int rect_layer_add_rect(RectLayer *layer, Rect rect, Color color) 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'; - + char id[ENTITY_MAX_ID_SIZE]; + snprintf(id, ENTITY_MAX_ID_SIZE, "%s_%d", + layer->id_name_prefix, + layer->id_name_counter++); if (dynarray_push(layer->ids, id)) { return -1; } + dynarray_push_empty(layer->actions); + + UNDO_PUSH( + undo_history, + create_undo_add_context( + layer, + dynarray_count(layer->rects) - 1)); + return 0; } -static int rect_layer_rect_at(RectLayer *layer, Vec position) +static int rect_layer_rect_at(RectLayer *layer, Vec2f position) { trace_assert(layer); - const size_t n = dynarray_count(layer->rects); + int n = (int) dynarray_count(layer->rects); Rect *rects = dynarray_data(layer->rects); - for (size_t i = 0; i < n; ++i) { + for (int i = n - 1; i >= 0; --i) { if (rect_contains_point(rects[i], position)) { return (int) i; } @@ -81,45 +261,93 @@ static int rect_layer_rect_at(RectLayer *layer, Vec position) return -1; } -static Rect rect_layer_resize_anchor(const RectLayer *layer, size_t i) +static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b, + UndoHistory *undo_history) { - 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); + trace_assert(layer); + trace_assert(a < dynarray_count(layer->rects)); + trace_assert(b < dynarray_count(layer->rects)); + + dynarray_swap(layer->rects, a, b); + dynarray_swap(layer->colors, a, b); + dynarray_swap(layer->ids, a, b); + dynarray_swap(layer->actions, a, b); + + UNDO_PUSH(undo_history, create_undo_swap_context(layer, a, b)); +} + +static Rect rect_layer_resize_anchor(const Camera *camera, Rect boundary_rect) +{ + const Rect overlay_rect = camera_rect(camera, boundary_rect); + return rect( + overlay_rect.x + overlay_rect.w, + overlay_rect.y + overlay_rect.h, + RECT_LAYER_SELECTION_THICCNESS * 2.0, + RECT_LAYER_SELECTION_THICCNESS * 2.0); } -static int rect_layer_delete_rect_at(RectLayer *layer, size_t i) +static int rect_layer_delete_rect_at(RectLayer *layer, + size_t i, + UndoHistory *undo_history) { trace_assert(layer); + UNDO_PUSH(undo_history, create_undo_delete_context(layer)); + dynarray_delete_at(layer->rects, i); dynarray_delete_at(layer->colors, i); dynarray_delete_at(layer->ids, i); + dynarray_delete_at(layer->actions, i); return 0; } -static int rect_layer_event_idle(RectLayer *layer, const SDL_Event *event, const Camera *camera) +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); + int color_changed = 0; + if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 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; + } + switch (event->type) { case SDL_MOUSEBUTTONDOWN: { switch (event->button.button) { case SDL_BUTTON_LEFT: { - Point position = camera_map_screen( + Vec2f position = camera_map_screen( camera, event->button.x, event->button.y); int rect_at_position = rect_layer_rect_at(layer, position); - if (rect_at_position >= 0) { - Rect *rects = dynarray_data(layer->rects); + Rect *rects = dynarray_data(layer->rects); + Color *colors = dynarray_data(layer->colors); + + 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 if (rect_at_position >= 0) { layer->selection = rect_at_position; layer->state = RECT_LAYER_MOVE; layer->move_anchor = @@ -128,12 +356,10 @@ static int rect_layer_event_idle(RectLayer *layer, const SDL_Event *event, const vec( rects[layer->selection].x, rects[layer->selection].y)); - } else if (layer->selection >= 0 && rect_contains_point( - rect_layer_resize_anchor( - layer, - (size_t)layer->selection), - position)) { - layer->state = RECT_LAYER_RESIZE; + 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 { layer->selection = rect_at_position; @@ -149,9 +375,35 @@ static int rect_layer_event_idle(RectLayer *layer, const SDL_Event *event, const case SDL_KEYDOWN: { switch (event->key.keysym.sym) { + case SDLK_UP: { + if ((event->key.keysym.mod & KMOD_SHIFT) + && (layer->selection >= 0) + && ((size_t)(layer->selection + 1) < dynarray_count(layer->rects))) { + rect_layer_swap_elements( + layer, + (size_t) layer->selection, + (size_t) layer->selection + 1, + undo_history); + layer->selection++; + } + } break; + + case SDLK_DOWN: { + if ((event->key.keysym.mod & KMOD_SHIFT) + && (layer->selection > 0) + && ((size_t) layer->selection < dynarray_count(layer->rects))) { + rect_layer_swap_elements( + layer, + (size_t) layer->selection, + (size_t) layer->selection - 1, + undo_history); + layer->selection--; + } + } break; + case SDLK_DELETE: { if (layer->selection >= 0) { - rect_layer_delete_rect_at(layer, (size_t) layer->selection); + rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history); layer->selection = -1; } } break; @@ -159,13 +411,43 @@ static int rect_layer_event_idle(RectLayer *layer, const SDL_Event *event, const 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); + ids + layer->selection * ENTITY_MAX_ID_SIZE); SDL_StartTextInput(); } } break; + + case SDLK_c: { + if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) { + clipboard = 1; + dynarray_copy_to(layer->rects, &clipboard_rect, (size_t)layer->selection); + dynarray_copy_to(layer->colors, &clipboard_color, (size_t)layer->selection); + } + } break; + + case SDLK_v: { + if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) { + int x, y; + SDL_GetMouseState(&x, &y); + Vec2f position = camera_map_screen(camera, x, y); + + rect_layer_add_rect( + layer, + rect(position.x, position.y, + clipboard_rect.w, clipboard_rect.h), + clipboard_color, + undo_history); + } + } break; } } break; } @@ -173,7 +455,10 @@ static int rect_layer_event_idle(RectLayer *layer, const SDL_Event *event, const return 0; } -static int rect_layer_event_create(RectLayer *layer, const SDL_Event *event, const Camera *camera) +static int rect_layer_event_create(RectLayer *layer, + const SDL_Event *event, + const Camera *camera, + UndoHistory *undo_history) { trace_assert(layer); trace_assert(event); @@ -193,7 +478,8 @@ static int rect_layer_event_create(RectLayer *layer, const SDL_Event *event, con rect_layer_add_rect( layer, real_rect, - color_picker_rgba(&layer->color_picker)); + 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); } @@ -212,18 +498,20 @@ static int rect_layer_event_create(RectLayer *layer, const SDL_Event *event, con return 0; } -static int rect_layer_event_resize(RectLayer *layer, const SDL_Event *event, const Camera *camera) +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: { - 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), + layer->inter_rect = rect_from_points( + vec(layer->inter_rect.x, layer->inter_rect.y), vec_sum( camera_map_screen( camera, @@ -235,78 +523,103 @@ static int rect_layer_event_resize(RectLayer *layer, const SDL_Event *event, con case SDL_MOUSEBUTTONUP: { layer->state = RECT_LAYER_IDLE; + UNDO_PUSH(undo_history, create_undo_update_context(layer)); + 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) +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); + + Rect *rects = dynarray_data(layer->rects); switch (event->type) { case SDL_MOUSEMOTION: { - Point position = vec_sub( + const Uint8 *state = SDL_GetKeyboardState(NULL); + const Vec2f mouse_pos = vec_sub( camera_map_screen( camera, event->button.x, event->button.y), layer->move_anchor); - Rect *rects = dynarray_data(layer->rects); + if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) { + layer->inter_rect.x = mouse_pos.x; + layer->inter_rect.y = mouse_pos.y; + } else { + const Vec2f rect_pos = rect_position(rects[layer->selection]); - trace_assert(layer->selection >= 0); + const float dx = fabsf(rect_pos.x - mouse_pos.x); + const float dy = fabsf(rect_pos.y - mouse_pos.y); - rects[layer->selection].x = position.x; - rects[layer->selection].y = position.y; + if (dx > dy) { + layer->inter_rect.x = mouse_pos.x; + layer->inter_rect.y = rect_pos.y; + } else { + layer->inter_rect.x = rect_pos.x; + layer->inter_rect.y = mouse_pos.y; + } + } } break; case SDL_MOUSEBUTTONUP: { layer->state = RECT_LAYER_IDLE; + + float distance = vec_length( + vec_sub(rect_position(layer->inter_rect), + rect_position(rects[layer->selection]))); + + if (distance > 1e-6) { + UNDO_PUSH(undo_history, create_undo_update_context(layer)); + 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) +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_TEXTINPUT: { - if (edit_field_text_input(layer->id_edit_field, &event->text) < 0) { - return -1; - } - } break; - case SDL_KEYDOWN: { switch (event->key.keysym.sym) { case SDLK_RETURN: { - char *id = - (char *)dynarray_data(layer->ids) + layer->selection * RECT_LAYER_ID_MAX_SIZE; - memset(id, 0, RECT_LAYER_ID_MAX_SIZE); - memcpy(id, edit_field_as_text(layer->id_edit_field), RECT_LAYER_ID_MAX_SIZE - 1); + UNDO_PUSH(undo_history, create_undo_update_context(layer)); + + char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection); + memset(id, 0, ENTITY_MAX_ID_SIZE); + memcpy(id, edit_field_as_text(layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1); layer->state = RECT_LAYER_IDLE; + SDL_StopTextInput(); } break; - } - if (edit_field_keyboard(layer->id_edit_field, &event->key) < 0) { - return -1; - } - } break; - - case SDL_KEYUP: { - if (edit_field_keyboard(layer->id_edit_field, &event->key) < 0) { - return -1; + case SDLK_ESCAPE: { + layer->state = RECT_LAYER_IDLE; + SDL_StopTextInput(); + } break; } } break; } - return 0; + + return edit_field_event(layer->id_edit_field, event); } LayerPtr rect_layer_as_layer(RectLayer *rect_layer) @@ -318,7 +631,7 @@ LayerPtr rect_layer_as_layer(RectLayer *rect_layer) return layer; } -RectLayer *create_rect_layer(void) +RectLayer *create_rect_layer(const char *id_name_prefix) { Lt *lt = create_lt(); @@ -330,7 +643,7 @@ RectLayer *create_rect_layer(void) layer->ids = PUSH_LT( lt, - create_dynarray(sizeof(char) * RECT_LAYER_ID_MAX_SIZE), + create_dynarray(sizeof(char) * ENTITY_MAX_ID_SIZE), destroy_dynarray); if (layer->ids == NULL) { RETURN_LT(lt, NULL); @@ -352,27 +665,50 @@ RectLayer *create_rect_layer(void) RETURN_LT(lt, NULL); } + layer->actions = PUSH_LT( + lt, + create_dynarray(sizeof(Action)), + destroy_dynarray); + if (layer->actions == NULL) { + RETURN_LT(lt, NULL); + } + layer->id_edit_field = PUSH_LT( lt, create_edit_field( - vec(3.0f, 3.0f), + RECT_LAYER_ID_LABEL_SIZE, COLOR_BLACK), destroy_edit_field); if (layer->id_edit_field == NULL) { RETURN_LT(lt, NULL); } + layer->grid = + PUSH_LT( + lt, + nth_calloc( + 1, + sizeof(Grid) + sizeof(Widget*) * RECT_LAYER_GRID_ROWS * RECT_LAYER_GRID_COLUMNS), + free); + if (layer->grid == NULL) { + RETURN_LT(lt, NULL); + } + layer->grid->rows = RECT_LAYER_GRID_ROWS; + layer->grid->columns = RECT_LAYER_GRID_COLUMNS; + grid_put_widget(layer->grid, &layer->action_picker.widget, 0, RECT_LAYER_GRID_COLUMNS - 1); + layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f)); layer->selection = -1; + layer->id_name_prefix = id_name_prefix; return layer; } -RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream) +RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream, const char *id_name_prefix) { trace_assert(line_stream); - RectLayer *layer = create_rect_layer(); + RectLayer *layer = create_rect_layer(id_name_prefix); if (layer == NULL) { return NULL; } @@ -395,22 +731,45 @@ RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream) char hex[7]; Rect rect; - char id[RECT_LAYER_ID_MAX_SIZE]; + char id[ENTITY_MAX_ID_SIZE]; + int n = 0; if (sscanf(line, - "%"STRINGIFY(RECT_LAYER_ID_MAX_SIZE)"s%f%f%f%f%6s\n", + "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s%f%f%f%f%6s%n", id, &rect.x, &rect.y, &rect.w, &rect.h, - hex) < 0) { + hex, &n) <= 0) { + log_fail("%s\n", strerror(errno)); RETURN_LT(layer->lt, NULL); } + line += n; Color color = hexstr(hex); - dynarray_push(layer->rects, &rect); dynarray_push(layer->ids, id); dynarray_push(layer->colors, &color); + + Action action = {0}; + + if (sscanf(line, "%d%n", (int*)&action.type, &n) > 0) { + line += n; + switch (action.type) { + case ACTION_NONE: break; + + case ACTION_TOGGLE_GOAL: + case ACTION_HIDE_LABEL: { + if (sscanf(line, "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s", action.entity_id) <= 0) { + log_fail("%s\n", strerror(errno)); + RETURN_LT(layer->lt, NULL); + } + } break; + + case ACTION_N: break; + } + } + + dynarray_push(layer->actions, &action); } return layer; @@ -422,7 +781,7 @@ void destroy_rect_layer(RectLayer *layer) RETURN_LT0(layer->lt); } -int rect_layer_render(const RectLayer *layer, Camera *camera, int active) +int rect_layer_render(const RectLayer *layer, const Camera *camera, int active) { trace_assert(layer); trace_assert(camera); @@ -432,55 +791,100 @@ int rect_layer_render(const RectLayer *layer, Camera *camera, int active) 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, - 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; } } + // Main Rectangle 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 && layer->selection >= 0) { + Rect rect = rects[layer->selection]; + Color color = colors[layer->selection]; + + if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) { + rect = layer->inter_rect; + } + + if (layer->state == RECT_LAYER_RECOLOR) { + color = layer->inter_color; + } + + const Rect overlay_rect = + rect_pad( + camera_rect(camera, rect), + -RECT_LAYER_SELECTION_THICCNESS * 0.5f); + const Color overlay_color = color_invert(color); - if (camera_render_text( + // Selection + if (camera_draw_thicc_rect_screen( camera, - ids + i * RECT_LAYER_ID_MAX_SIZE, - vec(3.0f, 3.0f), - color_invert(colors[i]), - rect_position(rects[i])) < 0) { + overlay_rect, + overlay_color, + RECT_LAYER_SELECTION_THICCNESS) < 0) { return -1; } - } - const Color color = color_picker_rgba(&layer->color_picker); - if (layer->state == RECT_LAYER_CREATE) { - if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) { + const Vec2f rect_id_pos = vec_sub( + rect_position(rect), + vec_mult( + RECT_LAYER_ID_LABEL_SIZE, + vec(0.0f, FONT_CHAR_HEIGHT))); + + // Rectangle Id + if (layer->state == RECT_LAYER_ID_RENAME) { + // ID renaming Edit Field + if (edit_field_render_world( + layer->id_edit_field, + camera, + rect_id_pos) < 0) { + return -1; + } + } else { + // Id text + if (camera_render_text( + camera, + ids + layer->selection * ENTITY_MAX_ID_SIZE, + RECT_LAYER_ID_LABEL_SIZE, + color_invert(color), + rect_id_pos) < 0) { + return -1; + } + } + + // Resize Anchor + if (camera_fill_rect_screen( + camera, + rect_layer_resize_anchor(camera, rect), + overlay_color) < 0) { return -1; } } - if (layer->state == RECT_LAYER_ID_RENAME) { - if (edit_field_render(layer->id_edit_field, camera, vec(400.0f, 400.0f)) < 0) { + // Proto Rectangle + const Color color = color_picker_rgba(&layer->color_picker); + if (layer->state == RECT_LAYER_CREATE) { + if (camera_fill_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) { return -1; } } @@ -492,35 +896,75 @@ 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(undo_history, create_undo_update_context(layer)); + dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color); + layer->state = RECT_LAYER_IDLE; + } + } + + return 0; +} + +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); + + switch (event->type) { + case SDL_WINDOWEVENT: { + switch (event->window.event) { + case SDL_WINDOWEVENT_RESIZED: { + grid_relayout(layer->grid, rect(0.0f, 0.0f, + (float) event->window.data1, + (float) event->window.data2)); + } break; + } + } break; } switch (layer->state) { case RECT_LAYER_IDLE: - return rect_layer_event_idle(layer, event, camera); + return rect_layer_event_idle(layer, event, camera, undo_history); case RECT_LAYER_CREATE: - return rect_layer_event_create(layer, event, camera); + return rect_layer_event_create(layer, event, camera, undo_history); case RECT_LAYER_RESIZE: - return rect_layer_event_resize(layer, event, camera); + return rect_layer_event_resize(layer, event, camera, undo_history); case RECT_LAYER_MOVE: - return rect_layer_event_move(layer, event, camera); + return rect_layer_event_move(layer, event, camera, undo_history); case RECT_LAYER_ID_RENAME: - return rect_layer_event_id_rename(layer, event, camera); + 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; @@ -555,15 +999,34 @@ int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump) char *ids = dynarray_data(layer->ids); Rect *rects = dynarray_data(layer->rects); Color *colors = dynarray_data(layer->colors); + Action *actions = dynarray_data(layer->actions); fprintf(filedump, "%zd\n", n); for (size_t i = 0; i < n; ++i) { fprintf(filedump, "%s %f %f %f %f ", - ids + RECT_LAYER_ID_MAX_SIZE * i, + ids + ENTITY_MAX_ID_SIZE * i, rects[i].x, rects[i].y, rects[i].w, rects[i].h); color_hex_to_stream(colors[i], filedump); + + switch (actions[i].type) { + case ACTION_NONE: {} break; + + case ACTION_TOGGLE_GOAL: + case ACTION_HIDE_LABEL: { + fprintf(filedump, " %d %.*s", + (int)actions[i].type, + ENTITY_MAX_ID_SIZE, actions[i].entity_id); + } break; + case ACTION_N: break; + } + fprintf(filedump, "\n"); } return 0; } + +const Action *rect_layer_actions(const RectLayer *layer) +{ + return dynarray_data(layer->actions); +}