Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial take on undo support #336

Merged
merged 3 commits into from Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions ChangeLog.md
Expand Up @@ -6,6 +6,10 @@
* [gui.h](include/clap/ext/gui.h): documentation clarifications
* [entry.h](include/clap/entry.h): documentation clarifications

## Draft extensions

* [undo.h](include/clap/ext/draft/undo.h): undo support

# Changes in 1.1.9

* [entry.h](include/clap/entry.h): clarify what the `plugin_path` is on macOS
Expand Down
1 change: 1 addition & 0 deletions include/clap/clap.h
Expand Up @@ -69,3 +69,4 @@
#include "ext/draft/tuning.h"
#include "ext/draft/configurable-audio-ports.h"
#include "ext/draft/extensible-audio-ports.h"
#include "ext/draft/undo.h"
75 changes: 75 additions & 0 deletions include/clap/ext/draft/undo.h
@@ -0,0 +1,75 @@
#pragma once

#include "../../plugin.h"
#include "../../string-sizes.h"

static CLAP_CONSTEXPR const char CLAP_EXT_UNDO[] = "clap.undo.draft/0";

// Describes a change which can be undone or redone
typedef struct clap_undo_change_info {
// This is the unique identifier of this undo step.
// It is valid until loading a state or destroying the plugin.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also still valid when after_destructive_change gets called? Maybe that should be mentioned here as well if relevant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Newly produced id shouldn't clash with those produced beforeafter_destructive_change().
I'd have one clap_id next_id that I'd only increment for the whole lifetime of the plugin instance.

clap_id change_id;

// A short string which describes the corresponding change.
char description[CLAP_NAME_SIZE];
} clap_undo_change_info_t;

typedef struct clap_plugin_undo {
// returns true if an undo/redo step exists and the info were provided
// [main-thread]
bool (*get_current_undo_info)(clap_plugin_t *plugin, clap_undo_change_info_t *info);
bool (*get_current_redo_info)(clap_plugin_t *plugin, clap_undo_change_info_t *info);

// Request the plugin to perform an undo operation (async).
//
// Returns true if the request is being processed, false otherwise.
// When returning true, the plugin **must** call clap_host_undo->after_undo_request() once the
// request is completed or failed.
//
// The plugin should only perform the operation if the current undo/redo operation matches the
// given id; this is because of the asynchronous nature of the task and to avoid race conditions
// if the plugin's undo manager lives in a different thread.
//
// Call sequence:
// 1. plugin->request_undo(change_id=X) -> returns true
// 2. later on host->begin_undo(change_id=X)
// 3. later on host->end_undo(change_id=X), host->after_undo(change_id=X, true, nullptr)
// [main-thread]
bool (*request_undo)(clap_plugin_t *plugin, clap_id change_id);
bool (*request_redo)(clap_plugin_t *plugin, clap_id change_id);
} clap_plugin_undo_t;

typedef struct clap_host_undo {
// Marks the begining and end of a change which will lead to the creation of an undo step.
// [main-thread]
void (*begin_change)(clap_host_t *host, const clap_undo_change_info_t *info);
void (*end_change)(clap_host_t *host, const clap_undo_change_info_t *info);

// Marks the beginning and end of processing an undo change.
// [main-thread]
void (*begin_undo)(clap_host_t *host, const clap_undo_change_info_t *info);
void (*end_undo)(clap_host_t *host, const clap_undo_change_info_t *info);

// Marks the beginning and end of processing a redo change.
// [main-thread]
void (*begin_redo)(clap_host_t *host, const clap_undo_change_info_t *info);
void (*end_redo)(clap_host_t *host, const clap_undo_change_info_t *info);

// A destructive change happened which makes it impossible to perform an undo.
// The entire plugin's undo/redo stack has been cleared.
// [main-thread]
void (*after_destructive_change)(clap_host_t *host);

// Callbacks for clap_plugin_undo->request_*()
// If succeed is true, then error_msg is ignored and may be null.
// [main-thread]
void (*after_undo_request)(clap_host_t *host,
clap_id change_id,
bool succeed,
const char *error_msg);
void (*after_redo_request)(clap_host_t *host,
clap_id change_id,
bool succeed,
const char *error_msg);
} clap_host_undo_t;