1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
|
#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
|