refactor(undo): switch oakundo to refcounted value handles, migrate consumers

- OakUndoCommand/OakUndoStack become neutral by-value handles
  {ctx, addref, release, abi_version}; a handle is a shared_ptr
  equivalent at the ABI level, internals untouched (box owns/observes
  flag; containers adopt, boxes created by factories own)
- oaknode command factories and undoable variants return/write value
  handles; timeline command classes hold value handles; task import
  take_command returns a value handle
- tests updated everywhere; suites green: oakundo 22, oaktimeline 117,
  oaktask 106, oaknode 96, oakrender 44, oakcodec 18, oakcommon 193,
  oakaudio 36
This commit is contained in:
2026-08-07 15:48:34 +08:00
parent da2ab512c6
commit 295ea1bfe5
41 changed files with 837 additions and 684 deletions
+8 -12
View File
@@ -238,24 +238,20 @@ OakNodeNode *oaknode_folder_as_node(OakNodeFolder *folder)
return reinterpret_cast<OakNodeNode *>(folder);
}
OakUndoCommand *oaknode_command_create_folder_add_child(
OakUndoCommand oaknode_command_create_folder_add_child(
OakNodeFolder *folder, OakNodeNode *child)
{
if (!folder || !child) {
return NULL;
return OakUndoCommand{};
}
try {
OakUndoCommand *handle =
new (std::nothrow) OakUndoCommand{ nullptr, true };
if (!handle) {
return NULL;
}
handle->command = new olive::FolderAddChild(
reinterpret_cast<olive::Folder *>(folder),
reinterpret_cast<olive::Node *>(child));
return handle;
return oakundo_capi::make_command_handle(
new olive::FolderAddChild(
reinterpret_cast<olive::Folder *>(folder),
reinterpret_cast<olive::Node *>(child)),
true);
} catch (...) {
return NULL;
return OakUndoCommand{};
}
}
+6 -6
View File
@@ -111,18 +111,18 @@ int oaknode_group_add_input_passthrough_undoable(OakNodeGroup *group,
OakNodeNode *node,
const char *input_id,
int element,
OakUndoCommand **out_command)
OakUndoCommand *out_command)
{
if (!group || !node || !input_id || !out_command) {
return OAKNODE_E_INVALID;
}
try {
OakUndoCommand *handle = oaknode_c_api::wrap_command(
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeGroupAddInputPassthrough(
to_group(group),
olive::NodeInput(to_node(node), input_id, element)));
if (!handle) {
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
*out_command = handle;
@@ -246,17 +246,17 @@ int oaknode_group_set_output_passthrough(OakNodeGroup *group,
}
int oaknode_group_set_output_passthrough_undoable(
OakNodeGroup *group, OakNodeNode *node, OakUndoCommand **out_command)
OakNodeGroup *group, OakNodeNode *node, OakUndoCommand *out_command)
{
if (!group || !out_command) {
return OAKNODE_E_INVALID;
}
try {
OakUndoCommand *handle = oaknode_c_api::wrap_command(
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeGroupSetOutputPassthrough(to_group(group),
to_node(node)));
if (!handle) {
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
*out_command = handle;
+15 -15
View File
@@ -213,19 +213,19 @@ int oaknode_keyframe_set_time(OakNodeKeyframe *keyframe, int64_t time_num,
int oaknode_keyframe_set_time_undoable(OakNodeKeyframe *keyframe,
int64_t time_num, int64_t time_den,
OakUndoCommand **out_command)
OakUndoCommand *out_command)
{
if (!keyframe || !out_command) {
return OAKNODE_E_INVALID;
}
try {
OakUndoCommand *handle = oaknode_c_api::wrap_command(
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeParamSetKeyframeTimeCommand(
to_key(keyframe),
olive::core::Rational(static_cast<int>(time_num),
static_cast<int>(time_den))));
if (!handle) {
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
*out_command = handle;
@@ -316,7 +316,7 @@ int oaknode_keyframe_set_value(OakNodeKeyframe *keyframe,
int oaknode_keyframe_set_value_undoable(OakNodeKeyframe *keyframe,
const oaknode_value *v,
OakUndoCommand **out_command)
OakUndoCommand *out_command)
{
if (!keyframe || !v || !out_command) {
return OAKNODE_E_INVALID;
@@ -328,10 +328,10 @@ int oaknode_keyframe_set_value_undoable(OakNodeKeyframe *keyframe,
return OAKNODE_E_INVALID;
}
OakUndoCommand *handle = oaknode_c_api::wrap_command(
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeParamSetKeyframeValueCommand(to_key(keyframe),
variant));
if (!handle) {
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
*out_command = handle;
@@ -373,17 +373,17 @@ int oaknode_keyframe_set_value_string(OakNodeKeyframe *keyframe,
int oaknode_keyframe_set_value_string_undoable(OakNodeKeyframe *keyframe,
const char *value,
OakUndoCommand **out_command)
OakUndoCommand *out_command)
{
if (!keyframe || !value || !out_command) {
return OAKNODE_E_INVALID;
}
try {
OakUndoCommand *handle = oaknode_c_api::wrap_command(
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeParamSetKeyframeValueCommand(
to_key(keyframe), olive::Variant(value)));
if (!handle) {
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
*out_command = handle;
@@ -426,7 +426,7 @@ int oaknode_keyframe_set_type(OakNodeKeyframe *keyframe, int type)
}
int oaknode_keyframe_set_type_undoable(OakNodeKeyframe *keyframe, int type,
OakUndoCommand **out_command)
OakUndoCommand *out_command)
{
if (!keyframe || !out_command) {
return OAKNODE_E_INVALID;
@@ -438,9 +438,9 @@ int oaknode_keyframe_set_type_undoable(OakNodeKeyframe *keyframe, int type,
return OAKNODE_E_INVALID;
}
OakUndoCommand *handle = oaknode_c_api::wrap_command(
OakUndoCommand handle = oaknode_c_api::wrap_command(
new KeyframeSetTypeCommand(to_key(keyframe), keyframe_type));
if (!handle) {
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
*out_command = handle;
@@ -498,7 +498,7 @@ int oaknode_keyframe_set_bezier_control(OakNodeKeyframe *keyframe, int handle,
int oaknode_keyframe_set_bezier_control_undoable(OakNodeKeyframe *keyframe,
int handle, double x, double y,
OakUndoCommand **out_command)
OakUndoCommand *out_command)
{
if (!keyframe || !out_command) {
return OAKNODE_E_INVALID;
@@ -514,10 +514,10 @@ int oaknode_keyframe_set_bezier_control_undoable(OakNodeKeyframe *keyframe,
return OAKNODE_E_INVALID;
}
OakUndoCommand *handle_ptr = oaknode_c_api::wrap_command(
OakUndoCommand handle_ptr = oaknode_c_api::wrap_command(
new KeyframeSetBezierControlCommand(to_key(keyframe), bezier_handle,
olive::PointF(x, y)));
if (!handle_ptr) {
if (!handle_ptr.ctx) {
return OAKNODE_E_NOMEM;
}
*out_command = handle_ptr;
+38 -38
View File
@@ -167,16 +167,16 @@ int oaknode_node_set_label(OakNodeNode *node, const char *label)
}
int oaknode_node_set_label_undoable(OakNodeNode *node, const char *label,
OakUndoCommand **out_command)
OakUndoCommand *out_command)
{
if (!node || !label || !out_command) {
return OAKNODE_E_INVALID;
}
try {
OakUndoCommand *handle = oaknode_c_api::wrap_command(
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeRenameCommand(to_node(node), label));
if (!handle) {
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
*out_command = handle;
@@ -215,16 +215,16 @@ int oaknode_node_set_override_color(OakNodeNode *node, int index)
}
int oaknode_node_set_override_color_undoable(OakNodeNode *node, int index,
OakUndoCommand **out_command)
OakUndoCommand *out_command)
{
if (!node || !out_command) {
return OAKNODE_E_INVALID;
}
try {
OakUndoCommand *handle = oaknode_c_api::wrap_command(
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeOverrideColorCommand(to_node(node), index));
if (!handle) {
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
*out_command = handle;
@@ -268,7 +268,7 @@ int oaknode_node_set_enabled(OakNodeNode *node, int enabled)
}
int oaknode_node_set_enabled_undoable(OakNodeNode *node, int enabled,
OakUndoCommand **out_command)
OakUndoCommand *out_command)
{
if (!node || !out_command) {
return OAKNODE_E_INVALID;
@@ -279,9 +279,9 @@ int oaknode_node_set_enabled_undoable(OakNodeNode *node, int enabled,
olive::SplitValue split =
olive::NodeValue::split_normal_value_into_track_values(
olive::NodeValue::k_boolean, olive::Variant(enabled != 0));
OakUndoCommand *handle = oaknode_c_api::wrap_command(
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeParamSetSplitStandardValueCommand(ref, split));
if (!handle) {
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
*out_command = handle;
@@ -464,7 +464,7 @@ int oaknode_node_set_input(OakNodeNode *node, const char *input_id,
int oaknode_node_set_input_undoable(OakNodeNode *node, const char *input_id,
const oaknode_value *v,
OakUndoCommand **out_command)
OakUndoCommand *out_command)
{
if (!node || !out_command) {
return OAKNODE_E_INVALID;
@@ -485,9 +485,9 @@ int oaknode_node_set_input_undoable(OakNodeNode *node, const char *input_id,
olive::SplitValue split =
olive::NodeValue::split_normal_value_into_track_values(type, variant);
OakUndoCommand *handle = oaknode_c_api::wrap_command(
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeParamSetSplitStandardValueCommand(ref, split));
if (!handle) {
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
*out_command = handle;
@@ -545,7 +545,7 @@ int oaknode_node_set_input_string(OakNodeNode *node, const char *input_id,
int oaknode_node_set_input_string_undoable(OakNodeNode *node,
const char *input_id,
const char *value,
OakUndoCommand **out_command)
OakUndoCommand *out_command)
{
if (!node || !value || !out_command) {
return OAKNODE_E_INVALID;
@@ -566,9 +566,9 @@ int oaknode_node_set_input_string_undoable(OakNodeNode *node,
to_node(node)->get_input_data_type(input_id),
olive::Variant(value));
OakUndoCommand *handle = oaknode_c_api::wrap_command(
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeParamSetSplitStandardValueCommand(ref, split));
if (!handle) {
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
*out_command = handle;
@@ -610,7 +610,7 @@ int oaknode_node_connect(OakNodeNode *output_node, OakNodeNode *input_node,
int oaknode_node_connect_undoable(OakNodeNode *output_node,
OakNodeNode *input_node,
const char *input_id,
OakUndoCommand **out_command)
OakUndoCommand *out_command)
{
if (!output_node || !input_node || !out_command) {
return OAKNODE_E_INVALID;
@@ -624,11 +624,11 @@ int oaknode_node_connect_undoable(OakNodeNode *output_node,
return OAKNODE_E_INVALID;
}
OakUndoCommand *handle = oaknode_c_api::wrap_command(
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeEdgeAddCommand(
to_node(output_node),
olive::NodeInput(to_node(input_node), input_id)));
if (!handle) {
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
*out_command = handle;
@@ -663,7 +663,7 @@ int oaknode_node_disconnect(OakNodeNode *input_node, const char *input_id)
int oaknode_node_disconnect_undoable(OakNodeNode *input_node,
const char *input_id,
OakUndoCommand **out_command)
OakUndoCommand *out_command)
{
if (!input_node || !out_command) {
return OAKNODE_E_INVALID;
@@ -678,10 +678,10 @@ int oaknode_node_disconnect_undoable(OakNodeNode *input_node,
return OAKNODE_E_NOT_FOUND;
}
OakUndoCommand *handle = oaknode_c_api::wrap_command(
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeEdgeRemoveCommand(
output, olive::NodeInput(to_node(input_node), input_id)));
if (!handle) {
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
*out_command = handle;
@@ -804,16 +804,16 @@ int oaknode_node_unlink(OakNodeNode *a, OakNodeNode *b, int *out_unlinked)
}
int oaknode_node_link_undoable(OakNodeNode *a, OakNodeNode *b, int link,
OakUndoCommand **out_command)
OakUndoCommand *out_command)
{
if (!a || !b || !out_command) {
return OAKNODE_E_INVALID;
}
try {
OakUndoCommand *handle = oaknode_c_api::wrap_command(
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeLinkCommand(to_node(a), to_node(b), link != 0));
if (!handle) {
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
*out_command = handle;
@@ -964,7 +964,7 @@ int oaknode_node_set_context_position(OakNodeNode *node, OakNodeNode *context,
int oaknode_node_set_context_position_undoable(OakNodeNode *node,
OakNodeNode *context, double x,
double y, int expanded,
OakUndoCommand **out_command)
OakUndoCommand *out_command)
{
if (!node || !context || !out_command) {
return OAKNODE_E_INVALID;
@@ -977,10 +977,10 @@ int oaknode_node_set_context_position_undoable(OakNodeNode *node,
// position map lives on the positioned node keyed by the context,
// so the command's (node, context) arguments are swapped relative
// to this function's signature.
OakUndoCommand *handle = oaknode_c_api::wrap_command(
OakUndoCommand handle = oaknode_c_api::wrap_command(
new olive::NodeSetPositionCommand(to_node(context), to_node(node),
position));
if (!handle) {
if (!handle.ctx) {
return OAKNODE_E_NOMEM;
}
*out_command = handle;
@@ -1039,7 +1039,7 @@ void oaknode_node_free(OakNodeNode *node)
}
OakNodeNode *oaknode_node_copy_in_graph(OakNodeNode *node,
OakUndoCommand **out_command)
OakUndoCommand *out_command)
{
if (!node || !out_command) {
return NULL;
@@ -1054,7 +1054,7 @@ OakNodeNode *oaknode_node_copy_in_graph(OakNodeNode *node,
return NULL;
}
*out_command = oaknode_c_api::wrap_command(command);
if (!*out_command) {
if (!out_command->ctx) {
delete command;
return NULL;
}
@@ -1065,10 +1065,10 @@ OakNodeNode *oaknode_node_copy_in_graph(OakNodeNode *node,
}
}
OakUndoCommand *oaknode_command_create_remove_node(OakNodeNode *node)
OakUndoCommand oaknode_command_create_remove_node(OakNodeNode *node)
{
if (!node) {
return NULL;
return OakUndoCommand{};
}
try {
@@ -1076,7 +1076,7 @@ OakUndoCommand *oaknode_command_create_remove_node(OakNodeNode *node)
new olive::NodeRemoveWithExclusiveDependenciesAndDisconnect(
to_node(node)));
} catch (...) {
return NULL;
return OakUndoCommand{};
}
}
@@ -1171,11 +1171,11 @@ int oaknode_node_disconnect_element(OakNodeNode *input_node,
}
}
OakUndoCommand *oaknode_command_create_add_node(OakNodeProject *graph,
OakUndoCommand oaknode_command_create_add_node(OakNodeProject *graph,
OakNodeNode *node)
{
if (!graph || !node) {
return NULL;
return OakUndoCommand{};
}
try {
@@ -1183,15 +1183,15 @@ OakUndoCommand *oaknode_command_create_add_node(OakNodeProject *graph,
new olive::NodeAddCommand(
reinterpret_cast<olive::Project *>(graph), to_node(node)));
} catch (...) {
return NULL;
return OakUndoCommand{};
}
}
OakUndoCommand *oaknode_command_create_set_position_recursive(
OakUndoCommand oaknode_command_create_set_position_recursive(
OakNodeNode *node, OakNodeNode *context, double x, double y)
{
if (!node || !context) {
return NULL;
return OakUndoCommand{};
}
try {
@@ -1200,7 +1200,7 @@ OakUndoCommand *oaknode_command_create_set_position_recursive(
to_node(node), to_node(context),
olive::Node::Position(olive::PointF(x, y))));
} catch (...) {
return NULL;
return OakUndoCommand{};
}
}
+5 -12
View File
@@ -250,22 +250,15 @@ inline int value_from_variant(olive::NodeValue::Type type, const olive::Variant
/**
* @brief Wrap a freshly created olive::UndoCommand in an owned
* OakUndoCommand handle. Returns NULL on allocation failure.
* OakUndoCommand handle (reference count 1). Returns an empty handle
* (ctx == NULL) on allocation failure.
*/
inline OakUndoCommand *wrap_command(olive::UndoCommand *command)
inline OakUndoCommand wrap_command(olive::UndoCommand *command)
{
if (!command) {
return NULL;
return OakUndoCommand{};
}
OakUndoCommand *handle = new (std::nothrow) OakUndoCommand();
if (!handle) {
delete command;
return NULL;
}
handle->command = command;
handle->owned = true;
return handle;
return oakundo_capi::make_command_handle(command, true);
}
}