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

Change UndoRedo to use Callables #66070

Merged
merged 1 commit into from
Sep 19, 2022
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 48 additions & 112 deletions core/object/undo_redo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,27 +126,28 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) {
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) {
ERR_FAIL_COND(p_object == nullptr);
void UndoRedo::add_do_method(const Callable &p_callable) {
ERR_FAIL_COND(p_callable.is_null());
ERR_FAIL_COND(action_level <= 0);
ERR_FAIL_COND((current_action + 1) >= actions.size());

Object *object = p_callable.get_object();
ERR_FAIL_NULL(object);

Operation do_op;
do_op.object = p_object->get_instance_id();
if (Object::cast_to<RefCounted>(p_object)) {
do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object));
do_op.callable = p_callable;
do_op.object = p_callable.get_object_id();
if (Object::cast_to<RefCounted>(object)) {
do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(object));
}

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);
}

void UndoRedo::add_undo_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) {
ERR_FAIL_COND(p_object == nullptr);
void UndoRedo::add_undo_method(const Callable &p_callable) {
ERR_FAIL_COND(p_callable.is_null());
ERR_FAIL_COND(action_level <= 0);
ERR_FAIL_COND((current_action + 1) >= actions.size());

Expand All @@ -155,19 +156,19 @@ void UndoRedo::add_undo_methodp(Object *p_object, const StringName &p_method, co
return;
}

Object *object = p_callable.get_object();
ERR_FAIL_NULL(object);

Operation undo_op;
undo_op.object = p_object->get_instance_id();
if (Object::cast_to<RefCounted>(p_object)) {
undo_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object));
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.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);
}

Expand All @@ -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.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);
}

Expand All @@ -206,7 +207,7 @@ void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property,
undo_op.type = Operation::TYPE_PROPERTY;
undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends;
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);
}

Expand Down Expand Up @@ -312,41 +313,50 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) {

switch (op.type) {
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;
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) {
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
Resource *res = Object::cast_to<Resource>(obj);
if (res) {
res->set_edited(true);
}

#endif

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;
case Operation::TYPE_PROPERTY: {
obj->set(op.name, op.args[0]);
obj->set(op.name, op.value);
#ifdef TOOLS_ENABLED
Resource *res = Object::cast_to<Resource>(obj);
if (res) {
res->set_edited(true);
}
#endif
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;
case Operation::TYPE_REFERENCE: {
Expand Down Expand Up @@ -444,87 +454,13 @@ UndoRedo::~UndoRedo() {
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() {
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("is_committing_action"), &UndoRedo::is_committing_action);

{
MethodInfo mi;
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_method", "callable"), &UndoRedo::add_do_method);
ClassDB::bind_method(D_METHOD("add_undo_method", "callable"), &UndoRedo::add_undo_method);
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_do_reference", "object"), &UndoRedo::add_do_reference);
Expand Down
34 changes: 5 additions & 29 deletions core/object/undo_redo.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ class UndoRedo : public Object {
};

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 (*PropertyNotifyCallback)(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value);
Expand All @@ -58,14 +56,14 @@ class UndoRedo : public Object {
TYPE_METHOD,
TYPE_PROPERTY,
TYPE_REFERENCE
};
} type;

Type type;
bool force_keep_in_merge_ends;
Ref<RefCounted> ref;
ObjectID object;
StringName name;
Vector<Variant> args;
Callable callable;
Variant value;

void delete_reference();
};
Expand Down Expand Up @@ -106,30 +104,8 @@ class UndoRedo : public Object {
public:
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_undo_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount);

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_method(const Callable &p_callable);
void add_undo_method(const Callable &p_callable);
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_do_reference(Object *p_object);
Expand Down
14 changes: 6 additions & 8 deletions doc/classes/UndoRedo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,11 @@
<tutorials>
</tutorials>
<methods>
<method name="add_do_method" qualifiers="vararg">
<method name="add_do_method">
<return type="void" />
<param index="0" name="object" type="Object" />
<param index="1" name="method" type="StringName" />
<param index="0" name="callable" type="Callable" />
<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>
</method>
<method name="add_do_property">
Expand All @@ -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.
</description>
</method>
<method name="add_undo_method" qualifiers="vararg">
<method name="add_undo_method">
<return type="void" />
<param index="0" name="object" type="Object" />
<param index="1" name="method" type="StringName" />
<param index="0" name="callable" type="Callable" />
<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>
</method>
<method name="add_undo_property">
Expand Down
4 changes: 2 additions & 2 deletions editor/editor_undo_redo_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
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) {
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) {
Expand Down