+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)
+
+