Change UndoRedo to use Callables

This commit is contained in:
kobewi 2022-09-18 23:27:00 +02:00
parent 908795301b
commit b3997191d8
4 changed files with 62 additions and 152 deletions

View File

@ -126,27 +126,28 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) {
force_keep_in_merge_ends = false; force_keep_in_merge_ends = false;
} }
void UndoRedo::add_do_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) { void UndoRedo::add_do_method(const Callable &p_callable) {
ERR_FAIL_COND(p_object == nullptr); ERR_FAIL_COND(p_callable.is_null());
ERR_FAIL_COND(action_level <= 0); ERR_FAIL_COND(action_level <= 0);
ERR_FAIL_COND((current_action + 1) >= actions.size()); ERR_FAIL_COND((current_action + 1) >= actions.size());
Object *object = p_callable.get_object();
ERR_FAIL_NULL(object);
Operation do_op; Operation do_op;
do_op.object = p_object->get_instance_id(); do_op.callable = p_callable;
if (Object::cast_to<RefCounted>(p_object)) { do_op.object = p_callable.get_object_id();
do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object)); if (Object::cast_to<RefCounted>(object)) {
do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(object));
} }
do_op.type = Operation::TYPE_METHOD; do_op.type = Operation::TYPE_METHOD;
do_op.name = p_method; do_op.name = p_callable.get_method();
for (int i = 0; i < p_argcount; i++) {
do_op.args.push_back(*p_args[i]);
}
actions.write[current_action + 1].do_ops.push_back(do_op); actions.write[current_action + 1].do_ops.push_back(do_op);
} }
void UndoRedo::add_undo_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) { void UndoRedo::add_undo_method(const Callable &p_callable) {
ERR_FAIL_COND(p_object == nullptr); ERR_FAIL_COND(p_callable.is_null());
ERR_FAIL_COND(action_level <= 0); ERR_FAIL_COND(action_level <= 0);
ERR_FAIL_COND((current_action + 1) >= actions.size()); ERR_FAIL_COND((current_action + 1) >= actions.size());
@ -155,19 +156,19 @@ void UndoRedo::add_undo_methodp(Object *p_object, const StringName &p_method, co
return; return;
} }
Operation undo_op; Object *object = p_callable.get_object();
undo_op.object = p_object->get_instance_id(); ERR_FAIL_NULL(object);
if (Object::cast_to<RefCounted>(p_object)) {
undo_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object));
}
Operation undo_op;
undo_op.callable = p_callable;
undo_op.object = p_callable.get_object_id();
if (Object::cast_to<RefCounted>(object)) {
undo_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(object));
}
undo_op.type = Operation::TYPE_METHOD; undo_op.type = Operation::TYPE_METHOD;
undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends; undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends;
undo_op.name = p_method; undo_op.name = p_callable.get_method();
for (int i = 0; i < p_argcount; i++) {
undo_op.args.push_back(*p_args[i]);
}
actions.write[current_action + 1].undo_ops.push_back(undo_op); actions.write[current_action + 1].undo_ops.push_back(undo_op);
} }
@ -183,7 +184,7 @@ void UndoRedo::add_do_property(Object *p_object, const StringName &p_property, c
do_op.type = Operation::TYPE_PROPERTY; do_op.type = Operation::TYPE_PROPERTY;
do_op.name = p_property; do_op.name = p_property;
do_op.args.push_back(p_value); do_op.value = p_value;
actions.write[current_action + 1].do_ops.push_back(do_op); actions.write[current_action + 1].do_ops.push_back(do_op);
} }
@ -206,7 +207,7 @@ void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property,
undo_op.type = Operation::TYPE_PROPERTY; undo_op.type = Operation::TYPE_PROPERTY;
undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends; undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends;
undo_op.name = p_property; undo_op.name = p_property;
undo_op.args.push_back(p_value); undo_op.value = p_value;
actions.write[current_action + 1].undo_ops.push_back(undo_op); actions.write[current_action + 1].undo_ops.push_back(undo_op);
} }
@ -312,33 +313,42 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) {
switch (op.type) { switch (op.type) {
case Operation::TYPE_METHOD: { case Operation::TYPE_METHOD: {
int argc = op.args.size();
Vector<const Variant *> argptrs;
argptrs.resize(argc);
for (int i = 0; i < argc; i++) {
argptrs.write[i] = &op.args[i];
}
Callable::CallError ce; Callable::CallError ce;
obj->callp(op.name, (const Variant **)argptrs.ptr(), argc, ce); Variant ret;
op.callable.callp(nullptr, 0, ret, ce);
if (ce.error != Callable::CallError::CALL_OK) { if (ce.error != Callable::CallError::CALL_OK) {
ERR_PRINT("Error calling UndoRedo method operation '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, (const Variant **)argptrs.ptr(), argc, ce)); ERR_PRINT("Error calling UndoRedo method operation '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, nullptr, 0, ce));
} }
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
Resource *res = Object::cast_to<Resource>(obj); Resource *res = Object::cast_to<Resource>(obj);
if (res) { if (res) {
res->set_edited(true); res->set_edited(true);
} }
#endif #endif
if (method_callback) { if (method_callback) {
method_callback(method_callback_ud, obj, op.name, (const Variant **)argptrs.ptr(), argc); Vector<Variant> binds;
if (op.callable.is_custom()) {
CallableCustomBind *ccb = dynamic_cast<CallableCustomBind *>(op.callable.get_custom());
if (ccb) {
binds = ccb->get_binds();
}
}
if (binds.is_empty()) {
method_callback(method_callback_ud, obj, op.name, nullptr, 0);
} else {
const Variant **args = (const Variant **)alloca(sizeof(const Variant **) * binds.size());
for (int i = 0; i < binds.size(); i++) {
args[i] = (const Variant *)&binds[i];
}
method_callback(method_callback_ud, obj, op.name, args, binds.size());
}
} }
} break; } break;
case Operation::TYPE_PROPERTY: { case Operation::TYPE_PROPERTY: {
obj->set(op.name, op.args[0]); obj->set(op.name, op.value);
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
Resource *res = Object::cast_to<Resource>(obj); Resource *res = Object::cast_to<Resource>(obj);
if (res) { if (res) {
@ -346,7 +356,7 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) {
} }
#endif #endif
if (property_callback) { if (property_callback) {
property_callback(prop_callback_ud, obj, op.name, op.args[0]); property_callback(prop_callback_ud, obj, op.name, op.value);
} }
} break; } break;
case Operation::TYPE_REFERENCE: { case Operation::TYPE_REFERENCE: {
@ -444,87 +454,13 @@ UndoRedo::~UndoRedo() {
clear_history(); clear_history();
} }
void UndoRedo::_add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 2) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = 0;
return;
}
if (p_args[0]->get_type() != Variant::OBJECT) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::OBJECT;
return;
}
if (p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 1;
r_error.expected = Variant::STRING_NAME;
return;
}
r_error.error = Callable::CallError::CALL_OK;
Object *object = *p_args[0];
StringName method = *p_args[1];
add_do_methodp(object, method, p_args + 2, p_argcount - 2);
}
void UndoRedo::_add_undo_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 2) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = 0;
return;
}
if (p_args[0]->get_type() != Variant::OBJECT) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::OBJECT;
return;
}
if (p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 1;
r_error.expected = Variant::STRING_NAME;
return;
}
r_error.error = Callable::CallError::CALL_OK;
Object *object = *p_args[0];
StringName method = *p_args[1];
add_undo_methodp(object, method, p_args + 2, p_argcount - 2);
}
void UndoRedo::_bind_methods() { void UndoRedo::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_action", "name", "merge_mode"), &UndoRedo::create_action, DEFVAL(MERGE_DISABLE)); ClassDB::bind_method(D_METHOD("create_action", "name", "merge_mode"), &UndoRedo::create_action, DEFVAL(MERGE_DISABLE));
ClassDB::bind_method(D_METHOD("commit_action", "execute"), &UndoRedo::commit_action, DEFVAL(true)); ClassDB::bind_method(D_METHOD("commit_action", "execute"), &UndoRedo::commit_action, DEFVAL(true));
ClassDB::bind_method(D_METHOD("is_committing_action"), &UndoRedo::is_committing_action); ClassDB::bind_method(D_METHOD("is_committing_action"), &UndoRedo::is_committing_action);
{ ClassDB::bind_method(D_METHOD("add_do_method", "callable"), &UndoRedo::add_do_method);
MethodInfo mi; ClassDB::bind_method(D_METHOD("add_undo_method", "callable"), &UndoRedo::add_undo_method);
mi.name = "add_do_method";
mi.arguments.push_back(PropertyInfo(Variant::OBJECT, "object"));
mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method"));
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "add_do_method", &UndoRedo::_add_do_method, mi, varray(), false);
}
{
MethodInfo mi;
mi.name = "add_undo_method";
mi.arguments.push_back(PropertyInfo(Variant::OBJECT, "object"));
mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method"));
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "add_undo_method", &UndoRedo::_add_undo_method, mi, varray(), false);
}
ClassDB::bind_method(D_METHOD("add_do_property", "object", "property", "value"), &UndoRedo::add_do_property); ClassDB::bind_method(D_METHOD("add_do_property", "object", "property", "value"), &UndoRedo::add_do_property);
ClassDB::bind_method(D_METHOD("add_undo_property", "object", "property", "value"), &UndoRedo::add_undo_property); ClassDB::bind_method(D_METHOD("add_undo_property", "object", "property", "value"), &UndoRedo::add_undo_property);
ClassDB::bind_method(D_METHOD("add_do_reference", "object"), &UndoRedo::add_do_reference); ClassDB::bind_method(D_METHOD("add_do_reference", "object"), &UndoRedo::add_do_reference);

View File

@ -46,8 +46,6 @@ public:
}; };
typedef void (*CommitNotifyCallback)(void *p_ud, const String &p_name); typedef void (*CommitNotifyCallback)(void *p_ud, const String &p_name);
void _add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
void _add_undo_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
typedef void (*MethodNotifyCallback)(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount); typedef void (*MethodNotifyCallback)(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount);
typedef void (*PropertyNotifyCallback)(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value); typedef void (*PropertyNotifyCallback)(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value);
@ -58,14 +56,14 @@ private:
TYPE_METHOD, TYPE_METHOD,
TYPE_PROPERTY, TYPE_PROPERTY,
TYPE_REFERENCE TYPE_REFERENCE
}; } type;
Type type;
bool force_keep_in_merge_ends; bool force_keep_in_merge_ends;
Ref<RefCounted> ref; Ref<RefCounted> ref;
ObjectID object; ObjectID object;
StringName name; StringName name;
Vector<Variant> args; Callable callable;
Variant value;
void delete_reference(); void delete_reference();
}; };
@ -106,30 +104,8 @@ protected:
public: public:
void create_action(const String &p_name = "", MergeMode p_mode = MERGE_DISABLE); void create_action(const String &p_name = "", MergeMode p_mode = MERGE_DISABLE);
void add_do_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount); void add_do_method(const Callable &p_callable);
void add_undo_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount); void add_undo_method(const Callable &p_callable);
template <typename... VarArgs>
void add_do_method(Object *p_object, const StringName &p_method, VarArgs... p_args) {
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
const Variant *argptrs[sizeof...(p_args) + 1];
for (uint32_t i = 0; i < sizeof...(p_args); i++) {
argptrs[i] = &args[i];
}
add_do_methodp(p_object, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
}
template <typename... VarArgs>
void add_undo_method(Object *p_object, const StringName &p_method, VarArgs... p_args) {
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
const Variant *argptrs[sizeof...(p_args) + 1];
for (uint32_t i = 0; i < sizeof...(p_args); i++) {
argptrs[i] = &args[i];
}
add_undo_methodp(p_object, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
}
void add_do_property(Object *p_object, const StringName &p_property, const Variant &p_value); void add_do_property(Object *p_object, const StringName &p_property, const Variant &p_value);
void add_undo_property(Object *p_object, const StringName &p_property, const Variant &p_value); void add_undo_property(Object *p_object, const StringName &p_property, const Variant &p_value);
void add_do_reference(Object *p_object); void add_do_reference(Object *p_object);

View File

@ -62,12 +62,11 @@
<tutorials> <tutorials>
</tutorials> </tutorials>
<methods> <methods>
<method name="add_do_method" qualifiers="vararg"> <method name="add_do_method">
<return type="void" /> <return type="void" />
<param index="0" name="object" type="Object" /> <param index="0" name="callable" type="Callable" />
<param index="1" name="method" type="StringName" />
<description> <description>
Register a [param method] that will be called when the action is committed. Register a [Callable] that will be called when the action is committed.
</description> </description>
</method> </method>
<method name="add_do_property"> <method name="add_do_property">
@ -86,12 +85,11 @@
Register a reference for "do" that will be erased if the "do" history is lost. This is useful mostly for new nodes created for the "do" call. Do not use for resources. Register a reference for "do" that will be erased if the "do" history is lost. This is useful mostly for new nodes created for the "do" call. Do not use for resources.
</description> </description>
</method> </method>
<method name="add_undo_method" qualifiers="vararg"> <method name="add_undo_method">
<return type="void" /> <return type="void" />
<param index="0" name="object" type="Object" /> <param index="0" name="callable" type="Callable" />
<param index="1" name="method" type="StringName" />
<description> <description>
Register a [param method] that will be called when the action is undone. Register a [Callable] that will be called when the action is undone.
</description> </description>
</method> </method>
<method name="add_undo_property"> <method name="add_undo_property">

View File

@ -131,12 +131,12 @@ void EditorUndoRedoManager::create_action(const String &p_name, UndoRedo::MergeM
void EditorUndoRedoManager::add_do_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) { void EditorUndoRedoManager::add_do_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) {
UndoRedo *undo_redo = get_history_for_object(p_object).undo_redo; UndoRedo *undo_redo = get_history_for_object(p_object).undo_redo;
undo_redo->add_do_methodp(p_object, p_method, p_args, p_argcount); undo_redo->add_do_method(Callable(p_object, p_method).bindp(p_args, p_argcount));
} }
void EditorUndoRedoManager::add_undo_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) { void EditorUndoRedoManager::add_undo_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) {
UndoRedo *undo_redo = get_history_for_object(p_object).undo_redo; UndoRedo *undo_redo = get_history_for_object(p_object).undo_redo;
undo_redo->add_undo_methodp(p_object, p_method, p_args, p_argcount); undo_redo->add_undo_method(Callable(p_object, p_method).bindp(p_args, p_argcount));
} }
void EditorUndoRedoManager::_add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { void EditorUndoRedoManager::_add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {