201 lines
9.1 KiB
C
201 lines
9.1 KiB
C
#pragma once
|
|
|
|
#include "../../plugin.h"
|
|
#include "../../stream.h"
|
|
|
|
static CLAP_CONSTEXPR const char CLAP_EXT_UNDO[] = "clap.undo/4";
|
|
static CLAP_CONSTEXPR const char CLAP_EXT_UNDO_CONTEXT[] = "clap.undo_context/4";
|
|
static CLAP_CONSTEXPR const char CLAP_EXT_UNDO_DELTA[] = "clap.undo_delta/4";
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/// @page Undo
|
|
///
|
|
/// This extension enables the plugin to merge its undo history with the host.
|
|
/// This leads to a single undo history shared by the host and many plugins.
|
|
///
|
|
/// Calling host->undo() or host->redo() is equivalent to clicking undo/redo within the host's GUI.
|
|
///
|
|
/// If the plugin uses this interface then its undo and redo should be entirely delegated to
|
|
/// the host; clicking in the plugin's UI undo or redo is equivalent to clicking undo or redo in the
|
|
/// host's UI.
|
|
///
|
|
/// Some changes are long running changes, for example a mouse interaction will begin editing some
|
|
/// complex data and it may take multiple events and a long duration to complete the change.
|
|
/// In such case the plugin will call host->begin_change() to indicate the beginning of a long
|
|
/// running change and complete the change by calling host->change_made().
|
|
///
|
|
/// The host may group changes together:
|
|
/// [---------------------------------]
|
|
/// ^-T0 ^-T1 ^-T2 ^-T3
|
|
/// Here a long running change C0 begin at T0.
|
|
/// A instantaneous change C1 at T1, and another one C2 at T2.
|
|
/// Then at T3 the long running change is completed.
|
|
/// The host will then create a single undo step that will merge all the changes into C0.
|
|
///
|
|
/// This leads to another important consideration: starting a long running change without
|
|
/// terminating is **VERY BAD**, because while a change is running it is impossible to call undo or
|
|
/// redo.
|
|
///
|
|
/// Rationale: multiple designs were considered and this one has the benefit of having a single undo
|
|
/// history. This simplifies the host implementation, leading to less bugs, a more robust design
|
|
/// and maybe an easier experience for the user because there's a single undo context versus one
|
|
/// for the host and one for each plugin instance.
|
|
///
|
|
/// This extension tries to make it as easy as possible for the plugin to hook into the host undo
|
|
/// and make it efficient when possible by using deltas. The plugin interfaces are all optional, and
|
|
/// the plugin can for a minimal implementation, just use the host interface and call
|
|
/// host->change_made() without providing a delta. This is enough for the host to know that it can
|
|
/// capture a plugin state for the undo step.
|
|
|
|
typedef struct clap_undo_delta_properties {
|
|
// If true, then the plugin will provide deltas in host->change_made().
|
|
// If false, then all clap_undo_delta_properties's attributes become irrelevant.
|
|
bool has_delta;
|
|
|
|
// If true, then the deltas can be stored on disk and re-used in the future as long as the plugin
|
|
// is compatible with the given format_version.
|
|
//
|
|
// If false, then format_version must be set to CLAP_INVALID_ID.
|
|
bool are_deltas_persistent;
|
|
|
|
// This represents the delta format version that the plugin is currently using.
|
|
// Use CLAP_INVALID_ID for invalid value.
|
|
clap_id format_version;
|
|
} clap_undo_delta_properties_t;
|
|
|
|
// Use CLAP_EXT_UNDO_DELTA.
|
|
// This is an optional interface, using deltas is an optimization versus making a state snapshot.
|
|
typedef struct clap_plugin_undo_delta {
|
|
// Asks the plugin the delta properties.
|
|
// [main-thread]
|
|
void(CLAP_ABI *get_delta_properties)(const clap_plugin_t *plugin,
|
|
clap_undo_delta_properties_t *properties);
|
|
|
|
// Asks the plugin if it can apply a delta using the given format version.
|
|
// Returns true if it is possible.
|
|
// [main-thread]
|
|
bool(CLAP_ABI *can_use_delta_format_version)(const clap_plugin_t *plugin,
|
|
clap_id format_version);
|
|
|
|
// Undo using the delta.
|
|
// Returns true on success.
|
|
//
|
|
// [main-thread]
|
|
bool(CLAP_ABI *undo)(const clap_plugin_t *plugin,
|
|
clap_id format_version,
|
|
const void *delta,
|
|
size_t delta_size);
|
|
|
|
// Redo using the delta.
|
|
// Returns true on success.
|
|
//
|
|
// [main-thread]
|
|
bool(CLAP_ABI *redo)(const clap_plugin_t *plugin,
|
|
clap_id format_version,
|
|
const void *delta,
|
|
size_t delta_size);
|
|
} clap_plugin_undo_delta_t;
|
|
|
|
// Use CLAP_EXT_UNDO_CONTEXT.
|
|
// This is an optional interface, that the plugin can implement in order to know about
|
|
// the current undo context.
|
|
typedef struct clap_plugin_undo_context {
|
|
// Indicate if it is currently possible to perform an undo or redo operation.
|
|
// [main-thread & plugin-subscribed-to-undo-context]
|
|
void(CLAP_ABI *set_can_undo)(const clap_plugin_t *plugin, bool can_undo);
|
|
void(CLAP_ABI *set_can_redo)(const clap_plugin_t *plugin, bool can_redo);
|
|
|
|
// Sets the name of the next undo or redo step.
|
|
// name: null terminated string.
|
|
// [main-thread & plugin-subscribed-to-undo-context]
|
|
void(CLAP_ABI *set_undo_name)(const clap_plugin_t *plugin, const char *name);
|
|
void(CLAP_ABI *set_redo_name)(const clap_plugin_t *plugin, const char *name);
|
|
} clap_plugin_undo_context_t;
|
|
|
|
// Use CLAP_EXT_UNDO.
|
|
typedef struct clap_host_undo {
|
|
// Begins a long running change.
|
|
// The plugin must not call this twice: there must be either a call to cancel_change() or
|
|
// change_made() before calling begin_change() again.
|
|
// [main-thread]
|
|
void(CLAP_ABI *begin_change)(const clap_host_t *host);
|
|
|
|
// Cancels a long running change.
|
|
// cancel_change() must not be called without a preceding begin_change().
|
|
// [main-thread]
|
|
void(CLAP_ABI *cancel_change)(const clap_host_t *host);
|
|
|
|
// Completes an undoable change.
|
|
// At the moment of this function call, plugin_state->save() would include the current change.
|
|
//
|
|
// name: mandatory null terminated string describing the change, this is displayed to the user
|
|
//
|
|
// delta: optional, it is a binary blobs used to perform the undo and redo. When not available
|
|
// the host will save the plugin state and use state->load() to perform undo and redo.
|
|
// The plugin must be able to perform a redo operation using the delta, though the undo operation
|
|
// is only possible if delta_can_undo is true.
|
|
//
|
|
// Note: the provided delta may be used for incremental state saving and crash recovery. The
|
|
// plugin can indicate a format version id and the validity lifetime for the binary blobs.
|
|
// The host can use these to verify the compatibility before applying the delta.
|
|
// If the plugin is unable to use a delta, a notification should be provided to the user and
|
|
// the crash recovery should perform a best effort job, at least restoring the latest saved
|
|
// state.
|
|
//
|
|
// Special case: for objects with shared and synchronized state, changes shouldn't be reported
|
|
// as the host already knows about it.
|
|
// For example, plugin parameter changes shouldn't produce a call to change_made().
|
|
//
|
|
// Note: if the plugin asked for this interface, then host_state->mark_dirty() will not create an
|
|
// implicit undo step.
|
|
//
|
|
// Note: if the plugin did load a preset or did something that leads to a large delta,
|
|
// it may consider not producing a delta (pass null) and let the host make a state snapshot
|
|
// instead.
|
|
//
|
|
// Note: if a plugin is producing a lot of changes within a small amount of time, the host
|
|
// may merge them into a single undo step.
|
|
//
|
|
// [main-thread]
|
|
void(CLAP_ABI *change_made)(const clap_host_t *host,
|
|
const char *name,
|
|
const void *delta,
|
|
size_t delta_size,
|
|
bool delta_can_undo);
|
|
|
|
// Asks the host to perform the next undo or redo step.
|
|
//
|
|
// Note: this maybe a complex and asynchronous operation, which may complete after
|
|
// this function returns.
|
|
//
|
|
// Note: the host may ignore this request if there is no undo/redo step to perform,
|
|
// or if the host is unable to perform undo/redo at the time (eg: a long running
|
|
// change is going on).
|
|
//
|
|
// [main-thread]
|
|
void(CLAP_ABI *request_undo)(const clap_host_t *host);
|
|
void(CLAP_ABI *request_redo)(const clap_host_t *host);
|
|
|
|
// Subscribes to or unsubscribes from undo context info.
|
|
//
|
|
// This method helps reducing the number of calls the host has to perform when updating
|
|
// the undo context info. Consider a large project with 1000+ plugins, we don't want to
|
|
// call 1000+ times update, while the plugin may only need the context info if its GUI
|
|
// is shown and it wants to display undo/redo info.
|
|
//
|
|
// Initial state is unsubscribed.
|
|
//
|
|
// is_subscribed: set to true to receive context info
|
|
//
|
|
// It is mandatory for the plugin to implement CLAP_EXT_UNDO_CONTEXT when using this method.
|
|
//
|
|
// [main-thread]
|
|
void(CLAP_ABI *set_wants_context_updates)(const clap_host_t *host, bool is_subscribed);
|
|
} clap_host_undo_t;
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|