#define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
#define CREATE_AREA_THRESHOLD 10.0
-// TODO(#1051): RectLayer does not support copy-pasting
+
static int clipboard = 0;
static Rect clipboard_rect;
Dynarray *rects;
Dynarray *colors;
ColorPicker color_picker;
- Vec create_begin;
- Vec create_end;
+ Vec2f create_begin;
+ Vec2f create_end;
int selection;
- Vec move_anchor;
+ Vec2f move_anchor;
Edit_field *id_edit_field;
Color inter_color;
Rect inter_rect;
+ int id_name_counter;
+ const char *id_name_prefix;
};
typedef enum {
UNDO_ADD,
UNDO_DELETE,
- UNDO_UPDATE
+ UNDO_UPDATE,
+ UNDO_SWAP
} UndoType;
+// Delete, Update
typedef struct {
UndoType type;
RectLayer *layer;
+ size_t index;
Rect rect;
Color color;
char id[RECT_LAYER_ID_MAX_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_context(RectLayer *rect_layer, UndoType type)
+UndoContext create_undo_add_context(RectLayer *layer, size_t index)
{
- trace_assert(rect_layer);
+ trace_assert(layer);
+ trace_assert(index < dynarray_count(layer->rects));
- size_t index = type == UNDO_ADD ? dynarray_count(rect_layer->rects) - 1 : (size_t) rect_layer->selection;
+ 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.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;
+ 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);
+ 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;
}
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;
+ 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);
+ 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;
+ 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);
+ 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);
+ 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);
+ } break;
+
+ 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);
} break;
}
}
-#define UNDO_PUSH(LAYER, HISTORY, UNDO_TYPE) \
+#define UNDO_PUSH(HISTORY, CONTEXT) \
do { \
- UndoContext context = create_undo_context(LAYER, UNDO_TYPE); \
+ UndoContext context = (CONTEXT); \
undo_history_push( \
HISTORY, \
rect_layer_undo, \
sizeof(context)); \
} while(0)
-
static int rect_layer_add_rect(RectLayer *layer,
Rect rect,
Color color,
}
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';
-
+ snprintf(id, RECT_LAYER_ID_MAX_SIZE, "%s_%d",
+ layer->id_name_prefix,
+ layer->id_name_counter++);
if (dynarray_push(layer->ids, id)) {
return -1;
}
- UNDO_PUSH(layer, undo_history, UNDO_ADD);
+ UNDO_PUSH(
+ undo_history,
+ create_undo_add_context(
+ layer,
+ dynarray_count(layer->rects) - 1));
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)
+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;
}
return -1;
}
+static void rect_layer_swap_elements(RectLayer *layer, size_t a, size_t b,
+ UndoHistory *undo_history)
+{
+ 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);
+
+ 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 =
{
trace_assert(layer);
- UNDO_PUSH(layer, undo_history, UNDO_DELETE);
+ UNDO_PUSH(undo_history, create_undo_delete_context(layer));
dynarray_delete_at(layer->rects, i);
dynarray_delete_at(layer->colors, i);
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);
Rect *rects = dynarray_data(layer->rects);
Color *colors = dynarray_data(layer->colors);
- if (rect_at_position >= 0) {
+ 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 =
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;
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, undo_history);
if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
int x, y;
SDL_GetMouseState(&x, &y);
- Point position = camera_map_screen(camera, x, y);
+ Vec2f position = camera_map_screen(camera, x, y);
rect_layer_add_rect(
layer,
case SDL_MOUSEBUTTONUP: {
layer->state = RECT_LAYER_IDLE;
- UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
+ UNDO_PUSH(undo_history, create_undo_update_context(layer));
dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
} break;
}
trace_assert(camera);
trace_assert(layer->selection >= 0);
+ Rect *rects = dynarray_data(layer->rects);
+
switch (event->type) {
case SDL_MOUSEMOTION: {
- Point position = vec_sub(
+ Vec2f position = vec_sub(
camera_map_screen(
camera,
event->button.x,
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);
+
+ 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;
case SDL_KEYDOWN: {
switch (event->key.keysym.sym) {
case SDLK_RETURN: {
- UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
+ UNDO_PUSH(undo_history, create_undo_update_context(layer));
char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
memset(id, 0, RECT_LAYER_ID_MAX_SIZE);
return layer;
}
-RectLayer *create_rect_layer(void)
+RectLayer *create_rect_layer(const char *id_name_prefix)
{
Lt *lt = create_lt();
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;
}
layer->inter_color = color_picker_rgba(&layer->color_picker);
if (!color_picker_drag(&layer->color_picker)) {
- UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
+ 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;
}