#include "ui/edit_field.h"
#include "undo_history.h"
-#define RECT_LAYER_ID_MAX_SIZE 36
#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(#1034): Can we use a single Context for everything in RectLayer
-
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,
- // TODO(#1035): id renaming in RectLayer is ugly
RECT_LAYER_ID_RENAME,
+ RECT_LAYER_RECOLOR
} RectLayerState;
struct RectLayer {
int selection;
Vec move_anchor;
Edit_field *id_edit_field;
- Color prev_color;
- Rect prev_rect;
+ // 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];
} UndoContext;
static
-UndoContext rect_layer_create_undo_context(RectLayer *rect_layer, size_t index, UndoType type)
+UndoContext create_undo_context(RectLayer *rect_layer, UndoType type)
{
trace_assert(rect_layer);
- trace_assert(index < dynarray_count(rect_layer->rects));
- UndoContext undo_context = {
- .type = type,
- .rect = rect_layer->prev_rect,
- .color = rect_layer->prev_color,
- .index = index
- };
+ 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 *layer, void *context, size_t context_size)
+void rect_layer_undo(void *context, size_t context_size)
{
- trace_assert(layer);
trace_assert(context);
trace_assert(sizeof(UndoContext) == context_size);
- RectLayer *rect_layer = layer;
UndoContext *undo_context = context;
+ RectLayer *rect_layer = undo_context->layer;
switch (undo_context->type) {
case UNDO_ADD: {
}
}
+#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,
{
trace_assert(layer);
- size_t index = dynarray_count(layer->rects);
-
if (dynarray_push(layer->rects, &rect) < 0) {
return -1;
}
return -1;
}
- UndoContext context =
- rect_layer_create_undo_context(layer, index, UNDO_ADD);
-
- undo_history_push(
- undo_history,
- layer,
- rect_layer_undo,
- &context, sizeof(context));
+ UNDO_PUSH(layer, undo_history, UNDO_ADD);
return 0;
}
return -1;
}
-static Rect rect_layer_resize_anchor(const RectLayer *layer, const Camera *camera, size_t i)
+static Rect rect_layer_resize_anchor(const Camera *camera, Rect boundary_rect)
{
- Rect *rects = dynarray_data(layer->rects);
const Rect overlay_rect =
rect_scale(
- camera_rect(camera, rects[i]),
+ camera_rect(camera, boundary_rect),
RECT_LAYER_SELECTION_THICCNESS * 0.5f);
return rect(
{
trace_assert(layer);
- UndoContext context = rect_layer_create_undo_context(layer, i, UNDO_DELETE);
-
- undo_history_push(
- undo_history,
- layer,
- rect_layer_undo,
- &context, sizeof(context));
+ UNDO_PUSH(layer, undo_history, UNDO_DELETE);
dynarray_delete_at(layer->rects, i);
dynarray_delete_at(layer->colors, i);
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) {
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) {
- Rect *rects = dynarray_data(layer->rects);
- Color *colors = dynarray_data(layer->colors);
layer->selection = rect_at_position;
layer->state = RECT_LAYER_MOVE;
layer->move_anchor =
rects[layer->selection].y));
layer->color_picker =
create_color_picker_from_rgba(colors[rect_at_position]);
- layer->prev_color = colors[rect_at_position];
- layer->prev_rect = rects[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(
- layer,
camera,
- (size_t)layer->selection),
+ 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;
trace_assert(layer);
trace_assert(event);
trace_assert(camera);
-
- Rect *rects = dynarray_data(layer->rects);
+ trace_assert(layer->selection >= 0);
switch (event->type) {
case SDL_MOUSEMOTION: {
- 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,
case SDL_MOUSEBUTTONUP: {
layer->state = RECT_LAYER_IDLE;
-
- UndoContext context =
- rect_layer_create_undo_context(layer, (size_t)layer->selection, UNDO_UPDATE);
-
- undo_history_push(
- undo_history,
- layer,
- rect_layer_undo,
- &context, sizeof(context));
-
- layer->prev_rect = rects[layer->selection];
+ UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
+ dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
} break;
}
trace_assert(layer);
trace_assert(event);
trace_assert(camera);
-
- Rect *rects = dynarray_data(layer->rects);
+ trace_assert(layer->selection >= 0);
switch (event->type) {
case SDL_MOUSEMOTION: {
trace_assert(layer->selection >= 0);
- rects[layer->selection].x = position.x;
- rects[layer->selection].y = position.y;
+ layer->inter_rect.x = position.x;
+ layer->inter_rect.y = position.y;
} break;
case SDL_MOUSEBUTTONUP: {
layer->state = RECT_LAYER_IDLE;
-
- UndoContext context =
- rect_layer_create_undo_context(layer, (size_t)layer->selection, UNDO_UPDATE);
-
- undo_history_push(
- undo_history,
- layer,
- rect_layer_undo,
- &context, sizeof(context));
-
- layer->prev_rect = rects[layer->selection];
+ UNDO_PUSH(layer, undo_history, UNDO_UPDATE);
+ dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
} break;
}
return 0;
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: {
- char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
-
- UndoContext undo_context =
- rect_layer_create_undo_context(layer, (size_t)layer->selection, UNDO_UPDATE);
-
- undo_history_push(
- undo_history,
- layer,
- rect_layer_undo,
- &undo_context, sizeof(undo_context));
+ 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;
RETURN_LT(lt, NULL);
}
- Color init_color = rgba(1.0f, 0.0f, 0.0f, 1.0f);
- layer->color_picker = create_color_picker_from_rgba(init_color);
- layer->prev_color = init_color;
+ layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
layer->selection = -1;
return layer;
// The Rectangles
for (size_t i = 0; i < n; ++i) {
- if (camera_fill_rect(
- camera,
- rects[i],
- color_scale(
- colors[i],
- rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
- return -1;
- }
- }
-
- // 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;
- }
- }
+ Rect rect = rects[i];
+ Color color = colors[i];
+ if (layer->selection == (int) i) {
+ if (layer->state == RECT_LAYER_RESIZE || layer->state == RECT_LAYER_MOVE) {
+ rect = layer->inter_rect;
+ }
- // Selection Overlay
- if (active && layer->selection >= 0) {
- const Rect overlay_rect =
- rect_scale(
- camera_rect(camera, rects[layer->selection]),
- RECT_LAYER_SELECTION_THICCNESS * 0.5f);
- const Color overlay_color = color_invert(colors[layer->selection]);
+ if (layer->state == RECT_LAYER_RECOLOR) {
+ color = layer->inter_color;
+ }
+ }
- // Main Rectangle
if (camera_fill_rect(
camera,
- rects[layer->selection],
+ rect,
color_scale(
- colors[layer->selection],
+ 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;
- }
+ // 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);
- // Rectangle Id
- if (layer->state == RECT_LAYER_ID_RENAME) {
- // ID renaming Edit Field
- if (edit_field_render_world(
- layer->id_edit_field,
+ // Main Rectangle
+ if (camera_fill_rect(
camera,
- rect_position(rects[layer->selection])) < 0) {
+ rect,
+ color_scale(
+ color,
+ rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f))) < 0) {
return -1;
}
- } else {
- // Id text
- if (camera_render_text(
+
+ if (camera_draw_thicc_rect_screen(
camera,
- ids + layer->selection * RECT_LAYER_ID_MAX_SIZE,
- RECT_LAYER_ID_LABEL_SIZE,
- color_invert(colors[layer->selection]),
- rect_position(rects[layer->selection])) < 0) {
+ 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;
}
}
+ }
- // Resize Anchor
- if (camera_fill_rect_screen(
- camera,
- rect_layer_resize_anchor(layer, camera, (size_t) layer->selection),
- overlay_color) < 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;
}
}
return 0;
}
-int rect_layer_event(RectLayer *layer,
- const SDL_Event *event,
- const Camera *camera,
- UndoHistory *undo_history)
+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 color_changed = 0;
if (color_picker_event(&layer->color_picker, event, camera, &color_changed) < 0) {
}
if (color_changed) {
- if (layer->selection >= 0) {
- Color *colors = dynarray_data(layer->colors);
- colors[layer->selection] = color_picker_rgba(&layer->color_picker);
+ layer->inter_color = color_picker_rgba(&layer->color_picker);
- if (!color_picker_drag(&layer->color_picker)) {
- UndoContext context =
- rect_layer_create_undo_context(layer, (size_t)layer->selection, UNDO_UPDATE);
-
- undo_history_push(
- undo_history,
- layer,
- rect_layer_undo,
- &context,
- sizeof(context));
- layer->prev_color = colors[layer->selection];
- }
+ 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;
}
-
- return 0;
}
+ 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 (layer->state) {
case RECT_LAYER_IDLE:
return rect_layer_event_idle(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;