#include "ui/edit_field.h"
#include "./point_layer.h"
#include "math/extrema.h"
+#include "math/mat3x3.h"
#include "./color_picker.h"
#include "undo_history.h"
#define POINT_LAYER_ID_TEXT_SIZE vec(2.0f, 2.0f)
#define POINT_LAYER_ID_TEXT_COLOR COLOR_BLACK
-
typedef enum {
POINT_LAYER_IDLE = 0,
POINT_LAYER_EDIT_ID,
- POINT_LAYER_MOVE
+ POINT_LAYER_MOVE,
+ POINT_LAYER_RECOLOR
} PointLayerState;
struct PointLayer
Dynarray/*<Point>*/ *positions;
Dynarray/*<Color>*/ *colors;
Dynarray/*<char[ID_MAX_SIZE]>*/ *ids;
- Edit_field *edit_field;
int selected;
ColorPicker color_picker;
- Color prev_color;
- Point prev_position;
+
+ Point inter_position;
+ Color inter_color;
+ Edit_field *edit_field;
};
+typedef enum {
+ UNDO_ADD,
+ UNDO_DELETE,
+ UNDO_UPDATE
+} UndoType;
+
typedef struct {
UndoType type;
+ PointLayer *layer;
Point position;
Color color;
char id[ID_MAX_SIZE];
static
UndoContext point_layer_create_undo_context(PointLayer *point_layer,
- size_t index,
UndoType type)
{
UndoContext undo_context;
+ size_t index =
+ type == UNDO_ADD
+ ? dynarray_count(point_layer->positions) - 1
+ : (size_t) point_layer->selected;
+
undo_context.type = type;
- undo_context.position = point_layer->prev_position;
- undo_context.color = point_layer->prev_color;
+ undo_context.layer = point_layer;
+ dynarray_copy_to(point_layer->positions, &undo_context.position, index);
+ dynarray_copy_to(point_layer->colors, &undo_context.color, index);
dynarray_copy_to(point_layer->ids, &undo_context.id, index);
undo_context.index = index;
}
static
-void point_layer_undo(void *layer, Context context)
+void point_layer_undo(void *context, size_t context_size)
{
- trace_assert(layer);
- PointLayer *point_layer = layer;
+ trace_assert(context);
+ trace_assert(sizeof(UndoContext) == context_size);
- UndoContext *undo_context = (UndoContext *)context.data;
+ UndoContext *undo_context = context;
+ PointLayer *point_layer = undo_context->layer;
switch (undo_context->type) {
case UNDO_ADD: {
}
}
+#define UNDO_PUSH(LAYER, HISTORY, UNDO_TYPE) \
+ do { \
+ UndoContext context = point_layer_create_undo_context(LAYER, UNDO_TYPE); \
+ undo_history_push( \
+ HISTORY, \
+ point_layer_undo, \
+ &context, \
+ sizeof(context)); \
+ } while(0)
+
LayerPtr point_layer_as_layer(PointLayer *point_layer)
{
LayerPtr layer = {
point_layer->selected = -1;
point_layer->color_picker = create_color_picker_from_rgba(COLOR_RED);
- point_layer->prev_color = COLOR_RED;
return point_layer;
}
RETURN_LT0(point_layer->lt);
}
+static inline
+Triangle element_shape(Point position, float scale)
+{
+ return triangle_mat3x3_product(
+ equilateral_triangle(),
+ mat3x3_product(
+ trans_mat_vec(position),
+ scale_mat(scale)));
+}
+
int point_layer_render(const PointLayer *point_layer,
Camera *camera,
int active)
char *ids = dynarray_data(point_layer->ids);
for (int i = 0; i < n; ++i) {
- const Triangle t = triangle_mat3x3_product(
- equilateral_triangle(),
- mat3x3_product(
- trans_mat(positions[i].x, positions[i].y),
- scale_mat(POINT_LAYER_ELEMENT_RADIUS)));
-
const Color color = color_scale(
- colors[i],
+ point_layer->state == POINT_LAYER_RECOLOR && i == point_layer->selected
+ ? point_layer->inter_color
+ : colors[i],
rgba(1.0f, 1.0f, 1.0f, active ? 1.0f : 0.5f));
- if (i == point_layer->selected) {
- const Triangle t0 = triangle_mat3x3_product(
- equilateral_triangle(),
- mat3x3_product(
- trans_mat(positions[i].x, positions[i].y),
- scale_mat(15.0f)));
+ const Point position =
+ point_layer->state == POINT_LAYER_MOVE && i == point_layer->selected
+ ? point_layer->inter_position
+ : positions[i];
- if (camera_fill_triangle(camera, t0, color_invert(color)) < 0) {
+ // Selection Layer
+ if (active && i == point_layer->selected) {
+ if (camera_fill_triangle(
+ camera,
+ element_shape(
+ position,
+ POINT_LAYER_ELEMENT_RADIUS + 5.0f),
+ color_invert(color)) < 0) {
return -1;
}
ids + ID_MAX_SIZE * i,
POINT_LAYER_ID_TEXT_SIZE,
POINT_LAYER_ID_TEXT_COLOR,
- positions[i]) < 0) {
+ position) < 0) {
return -1;
}
}
- if (camera_fill_triangle(camera, t, color) < 0) {
+ if (camera_fill_triangle(
+ camera,
+ element_shape(
+ position,
+ POINT_LAYER_ELEMENT_RADIUS),
+ color) < 0) {
return -1;
}
-
}
if (point_layer->state == POINT_LAYER_EDIT_ID) {
trace_assert(point_layer);
trace_assert(undo_history);
- size_t index = dynarray_count(point_layer->positions);
-
char id[ID_MAX_SIZE];
for (size_t i = 0; i < ID_MAX_SIZE - 1; ++i) {
id[i] = (char) ('a' + rand() % ('z' - 'a' + 1));
dynarray_push(point_layer->colors, &color);
dynarray_push(point_layer->ids, id);
- UndoContext context =
- point_layer_create_undo_context(point_layer, index, UNDO_ADD);
-
- undo_history_push(
- undo_history,
- create_action(
- point_layer,
- point_layer_undo,
- &context, sizeof(context)));
+ UNDO_PUSH(point_layer, undo_history, UNDO_ADD);
return 0;
}
{
trace_assert(point_layer);
- UndoContext context = point_layer_create_undo_context(point_layer, i, UNDO_DELETE);
- undo_history_push(
- undo_history,
- create_action(
- point_layer,
- point_layer_undo,
- &context, sizeof(context)));
+ UNDO_PUSH(point_layer, undo_history, UNDO_DELETE);
dynarray_delete_at(point_layer->positions, i);
dynarray_delete_at(point_layer->colors, i);
if (selected) {
if (point_layer->selected >= 0) {
- Color *colors = dynarray_data(point_layer->colors);
-
- if (!color_picker_drag(&point_layer->color_picker)) {
- UndoContext context =
- point_layer_create_undo_context(point_layer, (size_t)point_layer->selected, UNDO_UPDATE);
-
- undo_history_push(
- undo_history,
- create_action(
- point_layer,
- point_layer_undo,
- &context, sizeof(context)));
-
- point_layer->prev_color =
- color_picker_rgba(&point_layer->color_picker);
- }
-
- colors[point_layer->selected] =
- color_picker_rgba(&point_layer->color_picker);
+ point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
+ point_layer->state = POINT_LAYER_RECOLOR;
}
-
return 0;
}
point_layer->state = POINT_LAYER_MOVE;
point_layer->color_picker =
create_color_picker_from_rgba(colors[point_layer->selected]);
-
- point_layer->prev_color = colors[point_layer->selected];
- point_layer->prev_position = positions[point_layer->selected];
+ point_layer->inter_position = positions[point_layer->selected];
}
} break;
}
SDL_StartTextInput();
}
} break;
+
+ case SDLK_c: {
+ if ((event->key.keysym.mod & KMOD_LCTRL) && point_layer->selected >= 0) {
+ Color *colors = dynarray_data(point_layer->colors);
+#define COLOR_BUFFER_SIZE 32
+ char buffer[COLOR_BUFFER_SIZE];
+ buffer[0] = '#';
+ color_hex_to_string(colors[point_layer->selected], buffer + 1, COLOR_BUFFER_SIZE - 1);
+ SDL_SetClipboardText(buffer);
+#undef COLOR_BUFFER_SIZE
+ }
+ } break;
+
+ case SDLK_v: {
+ if ((event->key.keysym.mod & KMOD_LCTRL) && SDL_HasClipboardText()) {
+ const char *hex = SDL_GetClipboardText();
+ if (strlen(hex) == 7 && hex[0] == '#') {
+ Color color = hexstr(hex + 1);
+ int x, y;
+ SDL_GetMouseState(&x, &y);
+ Point position = camera_map_screen(camera, x, y);
+ point_layer_add_element(
+ point_layer,
+ position,
+ color,
+ undo_history);
+ }
+ }
+ } break;
}
} break;
}
case SDL_KEYDOWN: {
switch(event->key.keysym.sym) {
case SDLK_RETURN: {
- UndoContext context =
- point_layer_create_undo_context(point_layer, (size_t) point_layer->selected, UNDO_UPDATE);
-
- undo_history_push(
- undo_history,
- create_action(
- point_layer,
- point_layer_undo,
- &context,
- sizeof(context)));
+ UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
char *id = dynarray_pointer_at(point_layer->ids, (size_t) point_layer->selected);
const char *text = edit_field_as_text(point_layer->edit_field);
case SDL_BUTTON_LEFT: {
point_layer->state = POINT_LAYER_IDLE;
- UndoContext context =
- point_layer_create_undo_context(point_layer, (size_t) point_layer->selected, UNDO_UPDATE);
-
// TODO(#1014): just click (without moving) on the point creates an undo history entry
- undo_history_push(
- undo_history,
- create_action(
- point_layer,
- point_layer_undo,
- &context,
- sizeof(context)));
+ UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
+
+ dynarray_replace_at(
+ point_layer->positions,
+ (size_t) point_layer->selected,
+ &point_layer->inter_position);
} break;
}
} break;
case SDL_MOUSEMOTION: {
- Point *positions = dynarray_data(point_layer->positions);
- positions[point_layer->selected] =
+ point_layer->inter_position =
camera_map_screen(camera, event->motion.x, event->motion.y);
} break;
}
return 0;
}
+static
+int point_layer_recolor_event(PointLayer *point_layer,
+ const SDL_Event *event,
+ const Camera *camera,
+ UndoHistory *undo_history)
+{
+ trace_assert(point_layer);
+ trace_assert(event);
+ trace_assert(camera);
+ trace_assert(undo_history);
+ trace_assert(point_layer->selected >= 0);
+
+ int selected = 0;
+ if (color_picker_event(
+ &point_layer->color_picker,
+ event,
+ camera,
+ &selected) < 0) {
+ return -1;
+ }
+
+ if (selected) {
+ point_layer->inter_color = color_picker_rgba(&point_layer->color_picker);
+
+ if (!color_picker_drag(&point_layer->color_picker)) {
+ UNDO_PUSH(point_layer, undo_history, UNDO_UPDATE);
+
+ dynarray_replace_at(
+ point_layer->colors,
+ (size_t) point_layer->selected,
+ &point_layer->inter_color);
+
+ point_layer->state = POINT_LAYER_IDLE;
+ }
+ }
+
+
+ return 0;
+}
+
int point_layer_event(PointLayer *point_layer,
const SDL_Event *event,
const Camera *camera,
case POINT_LAYER_MOVE:
return point_layer_move_event(point_layer, event, camera, undo_history);
+
+ case POINT_LAYER_RECOLOR:
+ return point_layer_recolor_event(point_layer, event, camera, undo_history);
}
return 0;