X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fgame%2Flevel%2Flevel_editor%2Flabel_layer.c;h=739c2dae1995cebdd8f42ed0e975ca5f9a5d0ad7;hb=489d14c7610f8495ac287c87a43969fb1f578aef;hp=6335b8bffdc147ca89783d82f69e4bb036b87031;hpb=63c6fee61b2464310788f6e9027a37b9de49dee7;p=nothing.git diff --git a/src/game/level/level_editor/label_layer.c b/src/game/level/level_editor/label_layer.c index 6335b8bf..739c2dae 100644 --- a/src/game/level/level_editor/label_layer.c +++ b/src/game/level/level_editor/label_layer.c @@ -8,38 +8,157 @@ #include "system/lt.h" #include "system/str.h" #include "system/log.h" -#include "math/point.h" +#include "math/vec.h" #include "label_layer.h" #include "dynarray.h" #include "color.h" #include "game/camera.h" #include "color_picker.h" #include "ui/edit_field.h" +#include "math/extrema.h" +#include "config.h" #define LABEL_LAYER_SELECTION_THICCNESS 5.0f +// TODO(#1139): Label Layer does not support snapping + typedef enum { LABEL_LAYER_IDLE = 0, LABEL_LAYER_MOVE, LABEL_LAYER_EDIT_TEXT, - LABEL_LAYER_EDIT_ID + LABEL_LAYER_EDIT_ID, + LABEL_LAYER_RECOLOR } LabelLayerState; -// TODO(#963): LabelLayer cannot add the labels -// TODO(#964): LabelLayer cannot modify the labels' id +static int label_clipboard = 0; +static char label_clipboard_text[LABEL_LAYER_TEXT_MAX_SIZE]; +static Color label_clipboard_color; + struct LabelLayer { Lt *lt; LabelLayerState state; - Dynarray *ids; - Dynarray *positions; - Dynarray *colors; - Dynarray *texts; - int selected; + Dynarray ids; + Dynarray positions; + Dynarray colors; + Dynarray texts; + int selection; ColorPicker color_picker; - Point move_anchor; + Vec2f move_anchor; Edit_field *edit_field; + Vec2f inter_position; + Color inter_color; + int id_name_counter; + const char *id_name_prefix; }; +typedef enum { + LABEL_UNDO_ADD, + LABEL_UNDO_DELETE, + LABEL_UNDO_UPDATE, + LABEL_UNDO_SWAP +} LabelUndoType; + +typedef struct { + LabelUndoType type; + LabelLayer *layer; + char id[LABEL_LAYER_ID_MAX_SIZE]; + Vec2f position; + Color color; + char text[LABEL_LAYER_TEXT_MAX_SIZE]; + size_t index; + size_t index2; +} LabelUndoContext; + +static +LabelUndoContext create_label_undo_swap_context(LabelLayer *label_layer, + size_t index, size_t index2) +{ + trace_assert(label_layer); + trace_assert(index < label_layer->positions.count); + trace_assert(index2 < label_layer->positions.count); + + LabelUndoContext undo_context; + undo_context.type = LABEL_UNDO_SWAP; + undo_context.layer = label_layer; + undo_context.index = index; + undo_context.index2 = index2; + return undo_context; +} + +static +LabelUndoContext create_label_undo_context(LabelLayer *label_layer, LabelUndoType type) +{ + trace_assert(label_layer); + trace_assert(type != LABEL_UNDO_SWAP); + + LabelUndoContext undo_context; + + size_t index = type == LABEL_UNDO_ADD + ? label_layer->positions.count - 1 + : (size_t)label_layer->selection; + + undo_context.type = type; + undo_context.layer = label_layer; + dynarray_copy_to(&label_layer->ids, &undo_context.id, index); + dynarray_copy_to(&label_layer->positions, &undo_context.position, index); + dynarray_copy_to(&label_layer->colors, &undo_context.color, index); + dynarray_copy_to(&label_layer->texts, &undo_context.text, index); + undo_context.index = index; + + return undo_context; +} + +static +void label_layer_undo(void *context, size_t context_size) +{ + trace_assert(context); + trace_assert(sizeof(LabelUndoContext) == context_size); + + LabelUndoContext *undo_context = context; + LabelLayer *label_layer = undo_context->layer; + + switch (undo_context->type) { + case LABEL_UNDO_ADD: { + dynarray_delete_at(&label_layer->ids, undo_context->index); + dynarray_delete_at(&label_layer->positions, undo_context->index); + dynarray_delete_at(&label_layer->colors, undo_context->index); + dynarray_delete_at(&label_layer->texts, undo_context->index); + } break; + + case LABEL_UNDO_DELETE: { + dynarray_insert_before(&label_layer->ids, undo_context->index, &undo_context->id); + dynarray_insert_before(&label_layer->positions, undo_context->index, &undo_context->position); + dynarray_insert_before(&label_layer->colors, undo_context->index, &undo_context->color); + dynarray_insert_before(&label_layer->texts, undo_context->index, &undo_context->text); + } break; + + case LABEL_UNDO_UPDATE: { + dynarray_replace_at(&label_layer->ids, undo_context->index, &undo_context->id); + dynarray_replace_at(&label_layer->positions, undo_context->index, &undo_context->position); + dynarray_replace_at(&label_layer->colors, undo_context->index, &undo_context->color); + dynarray_replace_at(&label_layer->texts, undo_context->index, &undo_context->text); + } break; + + case LABEL_UNDO_SWAP: { + dynarray_swap(&label_layer->ids, undo_context->index, undo_context->index2); + dynarray_swap(&label_layer->positions, undo_context->index, undo_context->index2); + dynarray_swap(&label_layer->colors, undo_context->index, undo_context->index2); + dynarray_swap(&label_layer->texts, undo_context->index, undo_context->index2); + } break; + } +} + +#define LABEL_UNDO_PUSH(HISTORY, CONTEXT) \ + do { \ + LabelUndoContext context = (CONTEXT); \ + undo_history_push( \ + HISTORY, \ + label_layer_undo, \ + &context, \ + sizeof(context)); \ + } while(0) + + LayerPtr label_layer_as_layer(LabelLayer *label_layer) { LayerPtr layer = { @@ -49,7 +168,7 @@ LayerPtr label_layer_as_layer(LabelLayer *label_layer) return layer; } -LabelLayer *create_label_layer(void) +LabelLayer *create_label_layer(const char *id_name_prefix) { Lt *lt = create_lt(); @@ -60,34 +179,13 @@ LabelLayer *create_label_layer(void) } label_layer->lt = lt; - label_layer->ids = PUSH_LT( - lt, - create_dynarray(sizeof(char) * LABEL_LAYER_ID_MAX_SIZE), - destroy_dynarray); - if (label_layer->ids == NULL) { - RETURN_LT(lt, NULL); - } - - label_layer->positions = PUSH_LT(lt, create_dynarray(sizeof(Point)), destroy_dynarray); - if (label_layer->positions == NULL) { - RETURN_LT(lt, NULL); - } - - label_layer->colors = PUSH_LT(lt, create_dynarray(sizeof(Color)), destroy_dynarray); - if (label_layer->colors == NULL) { - RETURN_LT(lt, NULL); - } - - label_layer->texts = PUSH_LT( - lt, - create_dynarray(sizeof(char) * LABEL_LAYER_TEXT_MAX_SIZE), - destroy_dynarray); - if (label_layer->texts == NULL) { - RETURN_LT(lt, NULL); - } + label_layer->ids = create_dynarray(sizeof(char) * LABEL_LAYER_ID_MAX_SIZE); + label_layer->positions = create_dynarray(sizeof(Vec2f)); + label_layer->colors = create_dynarray(sizeof(Color)); + label_layer->texts = create_dynarray(sizeof(char) * LABEL_LAYER_TEXT_MAX_SIZE); label_layer->color_picker = create_color_picker_from_rgba(COLOR_RED); - label_layer->selected = -1; + label_layer->selection = -1; label_layer->edit_field = PUSH_LT( lt, @@ -97,13 +195,15 @@ LabelLayer *create_label_layer(void) RETURN_LT(lt, NULL); } + label_layer->id_name_prefix = id_name_prefix; + return label_layer; } -LabelLayer *create_label_layer_from_line_stream(LineStream *line_stream) +LabelLayer *create_label_layer_from_line_stream(LineStream *line_stream, const char *id_name_prefix) { trace_assert(line_stream); - LabelLayer *label_layer = create_label_layer(); + LabelLayer *label_layer = create_label_layer(id_name_prefix); if (label_layer == NULL) { RETURN_LT(label_layer->lt, NULL); @@ -124,7 +224,7 @@ LabelLayer *create_label_layer_from_line_stream(LineStream *line_stream) for (size_t i = 0; i < n; ++i) { char hex[7]; char id[LABEL_LAYER_ID_MAX_SIZE]; - Point position; + Vec2f position; line = line_stream_next(line_stream); if (line == NULL) { @@ -142,9 +242,9 @@ LabelLayer *create_label_layer_from_line_stream(LineStream *line_stream) Color color = hexstr(hex); - dynarray_push(label_layer->ids, id); - dynarray_push(label_layer->positions, &position); - dynarray_push(label_layer->colors, &color); + dynarray_push(&label_layer->ids, id); + dynarray_push(&label_layer->positions, &position); + dynarray_push(&label_layer->colors, &color); line = line_stream_next(line_stream); if (line == NULL) { @@ -154,7 +254,7 @@ LabelLayer *create_label_layer_from_line_stream(LineStream *line_stream) char label_text[LABEL_LAYER_TEXT_MAX_SIZE] = {0}; memcpy(label_text, line, LABEL_LAYER_TEXT_MAX_SIZE - 1); trim_endline(label_text); - dynarray_push(label_layer->texts, &label_text); + dynarray_push(&label_layer->texts, &label_text); } return label_layer; @@ -163,11 +263,42 @@ LabelLayer *create_label_layer_from_line_stream(LineStream *line_stream) void destroy_label_layer(LabelLayer *label_layer) { trace_assert(label_layer); + + free(label_layer->ids.data); + free(label_layer->positions.data); + free(label_layer->colors.data); + free(label_layer->texts.data); + destroy_lt(label_layer->lt); } +static inline +Rect boundary_of_element(const LabelLayer *label_layer, + size_t i, + Vec2f position) +{ + trace_assert(i < label_layer->texts.count); + + char *ids = (char *)label_layer->ids.data; + char *texts = (char *)label_layer->texts.data; + + return rect_boundary2( + sprite_font_boundary_box( + position, + LABELS_SIZE, + texts + i * LABEL_LAYER_TEXT_MAX_SIZE), + sprite_font_boundary_box( + vec_sum( + position, + vec_mult( + vec(0.0f, FONT_CHAR_HEIGHT), + LABELS_SIZE)), + vec(1.0f, 1.0f), + ids + i * LABEL_LAYER_ID_MAX_SIZE)); +} + int label_layer_render(const LabelLayer *label_layer, - Camera *camera, + const Camera *camera, int active) { trace_assert(label_layer); @@ -177,19 +308,29 @@ int label_layer_render(const LabelLayer *label_layer, return -1; } - size_t n = dynarray_count(label_layer->ids); - char *ids = dynarray_data(label_layer->ids); - Point *positions = dynarray_data(label_layer->positions); - Color *colors = dynarray_data(label_layer->colors); - char *texts = dynarray_data(label_layer->texts); + size_t n = label_layer->ids.count; + char *ids = (char *)label_layer->ids.data; + Vec2f *positions = (Vec2f *)label_layer->positions.data; + Color *colors = (Color *)label_layer->colors.data; + char *texts = (char *)label_layer->texts.data; /* TODO(#891): LabelLayer doesn't show the final position of Label after the animation */ for (size_t i = 0; i < n; ++i) { - if (label_layer->state == LABEL_LAYER_EDIT_TEXT && label_layer->selected == (int) i) { + const Color color = label_layer->state == LABEL_LAYER_RECOLOR && label_layer->selection == (int) i + ? label_layer->inter_color + : colors[i]; + + const Vec2f position = + label_layer->state == LABEL_LAYER_MOVE && label_layer->selection == (int) i + ? label_layer->inter_position + : positions[i]; + + // Label Text + if (label_layer->state == LABEL_LAYER_EDIT_TEXT && label_layer->selection == (int) i) { if (edit_field_render_world( label_layer->edit_field, camera, - positions[i]) < 0) { + position) < 0) { return -1; } } else { @@ -198,20 +339,23 @@ int label_layer_render(const LabelLayer *label_layer, texts + i * LABEL_LAYER_TEXT_MAX_SIZE, LABELS_SIZE, color_scale( - colors[i], + color, rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)), - positions[i]) < 0) { + position) < 0) { return -1; } } - if (label_layer->state == LABEL_LAYER_EDIT_ID && label_layer->selected == (int)i) { + // Label ID + if (label_layer->state == LABEL_LAYER_EDIT_ID && label_layer->selection == (int)i) { if (edit_field_render_world( label_layer->edit_field, camera, - vec_sub( - positions[i], - vec(0.0f, FONT_CHAR_HEIGHT))) < 0) { + vec_sum( + position, + vec_mult( + vec(0.0f, FONT_CHAR_HEIGHT), + LABELS_SIZE))) < 0) { return -1; } } else { @@ -220,69 +364,62 @@ int label_layer_render(const LabelLayer *label_layer, ids + i * LABEL_LAYER_ID_MAX_SIZE, vec(1.0f, 1.0f), color_scale( - color_invert(colors[i]), + color_invert(color), rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f)), - vec_sub(positions[i], vec(0.0f, FONT_CHAR_HEIGHT))) < 0) { + vec_sum( + position, + vec_mult( + vec(0.0f, FONT_CHAR_HEIGHT), + LABELS_SIZE))) < 0) { return -1; } } - } - - if (label_layer->selected >= 0) { - Rect selection = - rect_scale( - camera_rect( + // Label Selection + // TODO(#1160): Label Selection has to be internal (just like in Rect Layer) + if (active && label_layer->selection == (int) i) { + Rect selection = + rect_pad( + camera_rect( + camera, + boundary_of_element( + label_layer, + i, + position)), + LABEL_LAYER_SELECTION_THICCNESS * 0.5f); + + + if (camera_draw_thicc_rect_screen( camera, - rect_boundary2( - sprite_font_boundary_box( - camera_font(camera), - positions[label_layer->selected], - LABELS_SIZE, - texts + label_layer->selected * LABEL_LAYER_TEXT_MAX_SIZE), - sprite_font_boundary_box( - camera_font(camera), - vec_sub( - positions[label_layer->selected], - vec(0.0f, FONT_CHAR_HEIGHT)), - vec(1.0f, 1.0f), - ids + label_layer->selected * LABEL_LAYER_ID_MAX_SIZE))), - LABEL_LAYER_SELECTION_THICCNESS * 0.5f); - - - if (camera_draw_thicc_rect_screen( - camera, - selection, - colors[label_layer->selected], - LABEL_LAYER_SELECTION_THICCNESS) < 0) { - return -1; + selection, + color, + LABEL_LAYER_SELECTION_THICCNESS) < 0) { + return -1; + } } } + return 0; } static int label_layer_element_at(LabelLayer *label_layer, - const Sprite_font *font, - Point position) + Vec2f position) { trace_assert(label_layer); - const size_t n = dynarray_count(label_layer->texts); - char *texts = dynarray_data(label_layer->texts); - Point *positions = dynarray_data(label_layer->positions); + Vec2f *positions = (Vec2f*)label_layer->positions.data; - for (size_t i = 0; i < n; ++i) { - // TODO: the boundary of label_layer_element_at does not include the id - Rect boundary = sprite_font_boundary_box( - font, - positions[i], - LABELS_SIZE, - texts + i * LABEL_LAYER_TEXT_MAX_SIZE); - - if (rect_contains_point(boundary, position)) { - return (int) i; + const int n = (int) label_layer->texts.count; + for (int i = n - 1; i >= 0; --i) { + if (rect_contains_point( + boundary_of_element( + label_layer, + (size_t) i, + positions[i]), + position)) { + return i; } } @@ -290,89 +427,141 @@ int label_layer_element_at(LabelLayer *label_layer, } static -void label_layer_delete_nth_label(LabelLayer *label_layer, - size_t i) +void label_layer_delete_selected_label(LabelLayer *label_layer, + UndoHistory *undo_history) { trace_assert(label_layer); - dynarray_delete_at(label_layer->ids, i); - dynarray_delete_at(label_layer->positions, i); - dynarray_delete_at(label_layer->colors, i); - dynarray_delete_at(label_layer->texts, i); + trace_assert(label_layer->selection >= 0); + + LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_DELETE)); + + dynarray_delete_at(&label_layer->ids, (size_t)label_layer->selection); + dynarray_delete_at(&label_layer->positions, (size_t)label_layer->selection); + dynarray_delete_at(&label_layer->colors, (size_t)label_layer->selection); + dynarray_delete_at(&label_layer->texts, (size_t)label_layer->selection); + + label_layer->selection = -1; } static int label_layer_add_label(LabelLayer *label_layer, - Point position, - Color color) + Vec2f position, + Color color, + const char *text, + UndoHistory *undo_history) { trace_assert(label_layer); - // TODO: id generation code is duplicated + // TODO(#982): id generation code is duplicated in label_layer, point_layer and rect_layer char id[LABEL_LAYER_ID_MAX_SIZE]; - for (size_t i = 0; i < LABEL_LAYER_ID_MAX_SIZE - 1; ++i) { - id[i] = (char) ('a' + rand() % ('z' - 'a' + 1)); - } - id[LABEL_LAYER_ID_MAX_SIZE - 1] = '\0'; + snprintf(id, LABEL_LAYER_ID_MAX_SIZE, "%s_%d", + label_layer->id_name_prefix, + label_layer->id_name_counter++); - size_t n = dynarray_count(label_layer->ids); + size_t n = label_layer->ids.count; - dynarray_push(label_layer->ids, id); - dynarray_push(label_layer->positions, &position); - dynarray_push(label_layer->colors, &color); - dynarray_push_empty(label_layer->texts); + dynarray_push(&label_layer->ids, id); + dynarray_push(&label_layer->positions, &position); + dynarray_push(&label_layer->colors, &color); + dynarray_push_empty(&label_layer->texts); + memcpy( + dynarray_pointer_at(&label_layer->texts, n), + text, + min_size_t(LABEL_LAYER_ID_MAX_SIZE - 1, strlen(text))); + + LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_ADD)); return (int) n; } +static +void label_layer_swap_elements(LabelLayer *label_layer, + size_t a, size_t b, + UndoHistory *undo_history) +{ + trace_assert(label_layer); + trace_assert(undo_history); + trace_assert(a < label_layer->positions.count); + trace_assert(b < label_layer->positions.count); + + dynarray_swap(&label_layer->ids, a, b); + dynarray_swap(&label_layer->positions, a, b); + dynarray_swap(&label_layer->colors, a, b); + dynarray_swap(&label_layer->texts, a, b); + + LABEL_UNDO_PUSH(undo_history, create_label_undo_swap_context(label_layer, a, b)); +} + static int label_layer_idle_event(LabelLayer *label_layer, const SDL_Event *event, - const Camera *camera) + const Camera *camera, + UndoHistory *undo_history) { trace_assert(label_layer); trace_assert(event); trace_assert(camera); - Color *colors = dynarray_data(label_layer->colors); - Point *positions = dynarray_data(label_layer->positions); - char *ids = dynarray_data(label_layer->ids); - char *texts = dynarray_data(label_layer->texts); + + int changed = 0; + if (color_picker_event( + &label_layer->color_picker, + event, + camera, + &changed) < 0) { + return -1; + } + + if (changed) { + if (label_layer->selection >= 0) { + label_layer->state = LABEL_LAYER_RECOLOR; + label_layer->inter_color = color_picker_rgba(&label_layer->color_picker); + } + return 0; + } + + Color *colors = (Color*)label_layer->colors.data; + Vec2f *positions = (Vec2f*)label_layer->positions.data; + char *ids = (char*)label_layer->ids.data; + char *texts = (char*)label_layer->texts.data; switch (event->type) { case SDL_MOUSEBUTTONDOWN: { switch (event->button.button) { case SDL_BUTTON_LEFT: { - const Point position = camera_map_screen( + const Vec2f position = camera_map_screen( camera, event->button.x, event->button.y); const int element = label_layer_element_at( label_layer, - camera_font(camera), position); if (element >= 0) { label_layer->move_anchor = vec_sub(position, positions[element]); - label_layer->selected = element; + label_layer->selection = element; label_layer->state = LABEL_LAYER_MOVE; + label_layer->inter_position = positions[element]; label_layer->color_picker = create_color_picker_from_rgba(colors[element]); } else { - label_layer->selected = label_layer_add_label( + label_layer->selection = label_layer_add_label( label_layer, position, color_picker_rgba( - &label_layer->color_picker)); + &label_layer->color_picker), + "", + undo_history); label_layer->state = LABEL_LAYER_EDIT_TEXT; edit_field_replace( label_layer->edit_field, - texts + label_layer->selected * LABEL_LAYER_TEXT_MAX_SIZE); + texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE); edit_field_restyle( label_layer->edit_field, LABELS_SIZE, - colors[label_layer->selected]); + colors[label_layer->selection]); SDL_StartTextInput(); } } break; @@ -381,40 +570,89 @@ int label_layer_idle_event(LabelLayer *label_layer, case SDL_KEYDOWN: { switch (event->key.keysym.sym) { + case SDLK_UP: { + if ((event->key.keysym.mod & KMOD_SHIFT) + && (label_layer->selection >= 0) + && ((size_t)(label_layer->selection + 1) < label_layer->positions.count)) { + label_layer_swap_elements( + label_layer, + (size_t) label_layer->selection, + (size_t) label_layer->selection + 1, + undo_history); + label_layer->selection++; + } + } break; + + case SDLK_DOWN: { + if ((event->key.keysym.mod & KMOD_SHIFT) + && (label_layer->selection > 0) + && ((size_t) label_layer->selection < label_layer->positions.count)) { + label_layer_swap_elements( + label_layer, + (size_t) label_layer->selection, + (size_t) label_layer->selection - 1, + undo_history); + label_layer->selection--; + } + } break; + case SDLK_F2: { - if (label_layer->selected >= 0) { + if (label_layer->selection >= 0) { label_layer->state = LABEL_LAYER_EDIT_TEXT; edit_field_replace( label_layer->edit_field, - texts + label_layer->selected * LABEL_LAYER_TEXT_MAX_SIZE); + texts + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE); edit_field_restyle( label_layer->edit_field, LABELS_SIZE, - colors[label_layer->selected]); + colors[label_layer->selection]); SDL_StartTextInput(); } } break; case SDLK_F3: { - if (label_layer->selected >= 0) { + if (label_layer->selection >= 0) { label_layer->state = LABEL_LAYER_EDIT_ID; edit_field_replace( label_layer->edit_field, - ids + label_layer->selected * LABEL_LAYER_ID_MAX_SIZE); + ids + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE); edit_field_restyle( label_layer->edit_field, vec(1.0f, 1.0f), - color_invert(colors[label_layer->selected])); + color_invert(colors[label_layer->selection])); SDL_StartTextInput(); } } break; - // TODO: label is not deselected after deletion case SDLK_DELETE: { - if (label_layer->selected >= 0) { - label_layer_delete_nth_label( + if (label_layer->selection >= 0) { + label_layer_delete_selected_label( label_layer, - (size_t) label_layer->selected); + undo_history); + label_layer->selection = -1; + } + } break; + + case SDLK_c: { + if ((event->key.keysym.mod & KMOD_LCTRL) && label_layer->selection >= 0) { + label_clipboard = 1; + dynarray_copy_to(&label_layer->texts, label_clipboard_text, (size_t)label_layer->selection); + dynarray_copy_to(&label_layer->colors, &label_clipboard_color, (size_t)label_layer->selection); + } + } break; + + case SDLK_v: { + if ((event->key.keysym.mod & KMOD_LCTRL) && label_clipboard) { + int x, y; + SDL_GetMouseState(&x, &y); + Vec2f position = camera_map_screen(camera, x, y); + + label_layer_add_label( + label_layer, + position, + label_clipboard_color, + label_clipboard_text, + undo_history); } } break; } @@ -424,31 +662,95 @@ int label_layer_idle_event(LabelLayer *label_layer, return 0; } +static +void snap_inter_position(LabelLayer *label_layer, float snap_threshold) +{ + trace_assert(label_layer); + trace_assert(label_layer->selection >= 0); + trace_assert(label_layer->state == LABEL_LAYER_MOVE); + + const size_t n = label_layer->positions.count; + Vec2f *positions = (Vec2f*)label_layer->positions.data; + + Rect a = boundary_of_element( + label_layer, + (size_t) label_layer->selection, + label_layer->inter_position); + + for (size_t i = 0; i < n; ++i) { + if (i == (size_t) label_layer->selection) continue; + + const Rect b = boundary_of_element(label_layer, i, positions[i]); + + if (segment_overlap(vec(a.x, a.x + a.w), vec(b.x, b.x + b.w))) { + snap_seg2seg(&label_layer->inter_position.y, + b.y, a.h, b.h, snap_threshold); + } + + if (segment_overlap(vec(a.y, a.y + a.h), vec(b.y, b.y + b.h))) { + snap_seg2seg(&label_layer->inter_position.x, + b.x, a.w, b.w, snap_threshold); + } + } +} + static int label_layer_move_event(LabelLayer *label_layer, const SDL_Event *event, - const Camera *camera) + const Camera *camera, + UndoHistory *undo_history) { trace_assert(label_layer); trace_assert(event); trace_assert(camera); - trace_assert(label_layer->selected >= 0); + trace_assert(label_layer->selection >= 0); + + Vec2f *positions = (Vec2f*)label_layer->positions.data; switch (event->type) { case SDL_MOUSEMOTION: { - Point *positions = dynarray_data(label_layer->positions); - positions[label_layer->selected] = - vec_sub( - camera_map_screen( - camera, - event->motion.x, - event->motion.y), - label_layer->move_anchor); + const Uint8 *state = SDL_GetKeyboardState(NULL); + const Vec2f mouse_pos = vec_sub( + camera_map_screen( + camera, + event->motion.x, + event->motion.y), + label_layer->move_anchor); + + if (!(state[SDL_SCANCODE_LCTRL] || state[SDL_SCANCODE_RCTRL])) { + label_layer->inter_position = mouse_pos; + } else { + const Vec2f label_pos = positions[label_layer->selection]; + + const float dx = fabsf(label_pos.x - mouse_pos.x); + const float dy = fabsf(label_pos.y - mouse_pos.y); + + if (dx > dy) { + label_layer->inter_position = vec(mouse_pos.x, label_pos.y); + } else { + label_layer->inter_position = vec(label_pos.x, mouse_pos.y); + } + } + + snap_inter_position(label_layer, SNAPPING_THRESHOLD); } break; case SDL_MOUSEBUTTONUP: { switch (event->button.button) { case SDL_BUTTON_LEFT: { + const float distance = vec_length( + vec_sub(label_layer->inter_position, + positions[label_layer->selection])); + + if (distance > 1e-6) { + LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE)); + + dynarray_replace_at( + &label_layer->positions, + (size_t)label_layer->selection, + &label_layer->inter_position); + } + label_layer->state = LABEL_LAYER_IDLE; } break; } @@ -461,19 +763,22 @@ int label_layer_move_event(LabelLayer *label_layer, static int label_layer_edit_text_event(LabelLayer *label_layer, const SDL_Event *event, - const Camera *camera) + const Camera *camera, + UndoHistory *undo_history) { trace_assert(label_layer); trace_assert(event); trace_assert(camera); - trace_assert(label_layer->selected >= 0); + trace_assert(label_layer->selection >= 0); switch (event->type) { case SDL_KEYDOWN: { switch (event->key.keysym.sym) { case SDLK_RETURN: { + LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE)); + char *text = - (char*)dynarray_data(label_layer->texts) + label_layer->selected * LABEL_LAYER_TEXT_MAX_SIZE; + (char*)label_layer->texts.data + label_layer->selection * LABEL_LAYER_TEXT_MAX_SIZE; memset(text, 0, LABEL_LAYER_TEXT_MAX_SIZE); memcpy(text, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_TEXT_MAX_SIZE - 1); label_layer->state = LABEL_LAYER_IDLE; @@ -493,72 +798,110 @@ int label_layer_edit_text_event(LabelLayer *label_layer, return edit_field_event(label_layer->edit_field, event); } -// TODO: LabelLayer does not support cancelling the editing of ids - static int label_layer_edit_id_event(LabelLayer *label_layer, const SDL_Event *event, - const Camera *camera) + const Camera *camera, + UndoHistory *undo_history) { trace_assert(label_layer); trace_assert(event); trace_assert(camera); - trace_assert(label_layer->selected >= 0); - + trace_assert(undo_history); + trace_assert(label_layer->selection >= 0); switch (event->type) { case SDL_KEYDOWN: { - if (event->key.keysym.sym == SDLK_RETURN) { + switch (event->key.keysym.sym) { + case SDLK_RETURN: { + LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE)); + char *id = - (char*)dynarray_data(label_layer->ids) + label_layer->selected * LABEL_LAYER_ID_MAX_SIZE; + (char*)label_layer->ids.data + label_layer->selection * LABEL_LAYER_ID_MAX_SIZE; memset(id, 0, LABEL_LAYER_ID_MAX_SIZE); memcpy(id, edit_field_as_text(label_layer->edit_field), LABEL_LAYER_ID_MAX_SIZE - 1); label_layer->state = LABEL_LAYER_IDLE; SDL_StopTextInput(); return 0; + } break; + + case SDLK_ESCAPE: { + label_layer->state = LABEL_LAYER_IDLE; + SDL_StopTextInput(); + return 0; + } break; } } break; } - return edit_field_event(label_layer->edit_field, event); } -int label_layer_event(LabelLayer *label_layer, - const SDL_Event *event, - const Camera *camera) +static +int label_layer_recolor_event(LabelLayer *label_layer, + const SDL_Event *event, + const Camera *camera, + UndoHistory *undo_history) { trace_assert(label_layer); trace_assert(event); trace_assert(camera); + trace_assert(undo_history); + trace_assert(label_layer->selection >= 0); int changed = 0; if (color_picker_event( &label_layer->color_picker, event, + camera, &changed) < 0) { return -1; } - if (changed && label_layer->selected >= 0) { - Color *colors = dynarray_data(label_layer->colors); - colors[label_layer->selected] = + if (changed) { + label_layer->inter_color = color_picker_rgba(&label_layer->color_picker); + + if (!color_picker_drag(&label_layer->color_picker)) { + LABEL_UNDO_PUSH(undo_history, create_label_undo_context(label_layer, LABEL_UNDO_UPDATE)); + + dynarray_replace_at( + &label_layer->colors, + (size_t) label_layer->selection, + &label_layer->inter_color); + label_layer->state = LABEL_LAYER_IDLE; + } } + return 0; +} + +int label_layer_event(LabelLayer *label_layer, + const SDL_Event *event, + const Camera *camera, + UndoHistory *undo_history) +{ + trace_assert(label_layer); + trace_assert(event); + trace_assert(camera); + trace_assert(undo_history); + switch (label_layer->state) { case LABEL_LAYER_IDLE: - return label_layer_idle_event(label_layer, event, camera); + return label_layer_idle_event(label_layer, event, camera, undo_history); case LABEL_LAYER_MOVE: - return label_layer_move_event(label_layer, event, camera); + return label_layer_move_event(label_layer, event, camera, undo_history); case LABEL_LAYER_EDIT_TEXT: - return label_layer_edit_text_event(label_layer, event, camera); + return label_layer_edit_text_event(label_layer, event, camera, undo_history); case LABEL_LAYER_EDIT_ID: - return label_layer_edit_id_event(label_layer, event, camera); + return label_layer_edit_id_event(label_layer, event, camera, undo_history); + + case LABEL_LAYER_RECOLOR: + return label_layer_recolor_event(label_layer, event, camera, undo_history); } return 0; @@ -566,27 +909,27 @@ int label_layer_event(LabelLayer *label_layer, size_t label_layer_count(const LabelLayer *label_layer) { - return dynarray_count(label_layer->ids); + return label_layer->ids.count; } char *label_layer_ids(const LabelLayer *label_layer) { - return dynarray_data(label_layer->ids); + return (char *)label_layer->ids.data; } -Point *label_layer_positions(const LabelLayer *label_layer) +Vec2f *label_layer_positions(const LabelLayer *label_layer) { - return dynarray_data(label_layer->positions); + return (Vec2f *)label_layer->positions.data; } Color *label_layer_colors(const LabelLayer *label_layer) { - return dynarray_data(label_layer->colors); + return (Color *)label_layer->colors.data; } char *labels_layer_texts(const LabelLayer *label_layer) { - return dynarray_data(label_layer->texts); + return (char *)label_layer->texts.data; } int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump) @@ -594,11 +937,11 @@ int label_layer_dump_stream(const LabelLayer *label_layer, FILE *filedump) trace_assert(label_layer); trace_assert(filedump); - size_t n = dynarray_count(label_layer->ids); - char *ids = dynarray_data(label_layer->ids); - Point *positions = dynarray_data(label_layer->positions); - Color *colors = dynarray_data(label_layer->colors); - char *texts = dynarray_data(label_layer->texts); + size_t n = label_layer->ids.count; + char *ids = (char *)label_layer->ids.data; + Vec2f *positions = (Vec2f *)label_layer->positions.data; + Color *colors = (Color *)label_layer->colors.data; + char *texts = (char *)label_layer->texts.data; fprintf(filedump, "%zd\n", n); for (size_t i = 0; i < n; ++i) {