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

Make UndoRedo use Callables #43872

Closed
wants to merge 1 commit into from
Closed
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
188 changes: 65 additions & 123 deletions core/object/undo_redo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,33 +98,65 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) {
action_level++;
}

void UndoRedo::add_do_method(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) {
void UndoRedo::add_do_method_compat(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) {
VARIANT_ARGPTRS

int argc = 0;
for (; argc < VARIANT_ARG_MAX; argc++) {
if (argptr[argc]->get_type() == Variant::NIL) {
break;
}
}

const Variant **argptr_bind = (const Variant **)alloca(sizeof(Variant *) * argc);
for (int i = 0; i < argc; i++) {
argptr_bind[i] = argptr[i];
}

add_do_method(p_object, Callable(p_object, p_method).bind(argptr_bind, argc));
}

void UndoRedo::add_undo_method_compat(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) {
VARIANT_ARGPTRS

int argc = 0;
for (; argc < VARIANT_ARG_MAX; argc++) {
if (argptr[argc]->get_type() == Variant::NIL) {
break;
}
}

const Variant **argptr_bind = (const Variant **)alloca(sizeof(Variant *) * argc);
for (int i = 0; i < argc; i++) {
argptr_bind[i] = argptr[i];
}

add_undo_method(p_object, Callable(p_object, p_method).bind(argptr_bind, argc));
}

void UndoRedo::add_do_method(Object *p_object, const Callable &p_callable) {
ERR_FAIL_COND(p_object == nullptr);
ERR_FAIL_COND(action_level <= 0);
ERR_FAIL_COND((current_action + 1) >= actions.size());

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

do_op.type = Operation::TYPE_METHOD;
do_op.name = p_method;
do_op.arg = p_callable;

for (int i = 0; i < VARIANT_ARG_MAX; i++) {
do_op.args[i] = *argptr[i];
}
actions.write[current_action + 1].do_ops.push_back(do_op);
}

void UndoRedo::add_undo_method(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) {
VARIANT_ARGPTRS
void UndoRedo::add_undo_method(Object *p_object, const Callable &p_callable) {
ERR_FAIL_COND(p_object == nullptr);
ERR_FAIL_COND(action_level <= 0);
ERR_FAIL_COND((current_action + 1) >= actions.size());

// No undo if the merge mode is MERGE_ENDS
// No undo if the merge mode is MERGE_ENDS.
if (merge_mode == MERGE_ENDS) {
return;
}
Expand All @@ -136,11 +168,8 @@ void UndoRedo::add_undo_method(Object *p_object, const StringName &p_method, VAR
}

undo_op.type = Operation::TYPE_METHOD;
undo_op.name = p_method;
undo_op.arg = p_callable;

for (int i = 0; i < VARIANT_ARG_MAX; i++) {
undo_op.args[i] = *argptr[i];
}
actions.write[current_action + 1].undo_ops.push_back(undo_op);
}

Expand All @@ -156,7 +185,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[0] = p_value;
do_op.arg = p_value;
actions.write[current_action + 1].do_ops.push_back(do_op);
}

Expand All @@ -178,7 +207,7 @@ void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property,

undo_op.type = Operation::TYPE_PROPERTY;
undo_op.name = p_property;
undo_op.args[0] = p_value;
undo_op.arg = p_value;
actions.write[current_action + 1].undo_ops.push_back(undo_op);
}

Expand Down Expand Up @@ -273,23 +302,13 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) {

switch (op.type) {
case Operation::TYPE_METHOD: {
Vector<const Variant *> argptrs;
argptrs.resize(VARIANT_ARG_MAX);
int argc = 0;

for (int i = 0; i < VARIANT_ARG_MAX; i++) {
if (op.args[i].get_type() == Variant::NIL) {
break;
}
argptrs.write[i] = &op.args[i];
argc++;
}
argptrs.resize(argc);
ERR_FAIL_COND(op.arg.get_type() != Variant::CALLABLE);
Callable c = (Callable)op.arg;

Callable::CallError ce;
obj->call(op.name, (const Variant **)argptrs.ptr(), argc, ce);
c.call(nullptr, 0, Variant(), ce);
if (ce.error != Callable::CallError::CALL_OK) {
ERR_PRINT("Error calling method from signal '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, (const Variant **)argptrs.ptr(), argc, ce));
ERR_PRINT("Error calling method from signal '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, nullptr, 0, ce));
}
#ifdef TOOLS_ENABLED
Resource *res = Object::cast_to<Resource>(obj);
Expand All @@ -300,19 +319,32 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) {
#endif

if (method_callback) {
method_callback(method_callbck_ud, obj, op.name, VARIANT_ARGS_FROM_ARRAY(op.args));
Vector<Variant> args;
if (c.is_custom()) {
CallableCustomBind *custom = dynamic_cast<CallableCustomBind *>(c.get_custom());
if (custom) {
args = custom->get_binds();
}
}

if (args.size() > VARIANT_ARG_MAX) {
WARN_PRINT("Callable for UndoRedo method " + op.name + " has too many arguments and won't work properly with live edit.");
}
args.resize(5);

method_callback(method_callbck_ud, obj, op.name, VARIANT_ARGS_FROM_ARRAY(args));
}
} break;
case Operation::TYPE_PROPERTY: {
obj->set(op.name, op.args[0]);
obj->set(op.name, op.arg);
#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.arg);
}
} break;
case Operation::TYPE_REFERENCE: {
Expand Down Expand Up @@ -404,103 +436,13 @@ UndoRedo::~UndoRedo() {
clear_history();
}

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

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

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

r_error.error = Callable::CallError::CALL_OK;

Object *object = *p_args[0];
StringName method = *p_args[1];

Variant v[VARIANT_ARG_MAX];

for (int i = 0; i < MIN(VARIANT_ARG_MAX, p_argcount - 2); ++i) {
v[i] = *p_args[i + 2];
}

static_assert(VARIANT_ARG_MAX == 5, "This code needs to be updated if VARIANT_ARG_MAX != 5");
add_do_method(object, method, v[0], v[1], v[2], v[3], v[4]);
return Variant();
}

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

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

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

r_error.error = Callable::CallError::CALL_OK;

Object *object = *p_args[0];
StringName method = *p_args[1];

Variant v[VARIANT_ARG_MAX];

for (int i = 0; i < MIN(VARIANT_ARG_MAX, p_argcount - 2); ++i) {
v[i] = *p_args[i + 2];
}

static_assert(VARIANT_ARG_MAX == 5, "This code needs to be updated if VARIANT_ARG_MAX != 5");
add_undo_method(object, method, v[0], v[1], v[2], v[3], v[4]);
return Variant();
}

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"), &UndoRedo::commit_action);
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
12 changes: 6 additions & 6 deletions core/object/undo_redo.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ class UndoRedo : public Object {
};

typedef void (*CommitNotifyCallback)(void *p_ud, const String &p_name);
Variant _add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
Variant _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, VARIANT_ARG_DECLARE);
typedef void (*PropertyNotifyCallback)(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value);

Expand All @@ -64,7 +61,7 @@ class UndoRedo : public Object {
Ref<Reference> ref;
ObjectID object;
StringName name;
Variant args[VARIANT_ARG_MAX];
Variant arg;
};

struct Action {
Expand Down Expand Up @@ -101,8 +98,11 @@ class UndoRedo : public Object {
public:
void create_action(const String &p_name = "", MergeMode p_mode = MERGE_DISABLE);

void add_do_method(Object *p_object, const StringName &p_method, VARIANT_ARG_LIST);
void add_undo_method(Object *p_object, const StringName &p_method, VARIANT_ARG_LIST);
void add_do_method_compat(Object *p_object, const StringName &p_method, VARIANT_ARG_LIST);
void add_undo_method_compat(Object *p_object, const StringName &p_method, VARIANT_ARG_LIST);

void add_do_method(Object *p_object, const Callable &p_callable);
void add_undo_method(Object *p_object, 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
2 changes: 2 additions & 0 deletions core/variant/callable_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ class CallableCustomBind : public CallableCustom {
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const;
virtual const Callable *get_base_comparator() const;

Vector<Variant> get_binds() { return binds; }

CallableCustomBind(const Callable &p_callable, const Vector<Variant> &p_binds);
virtual ~CallableCustomBind();
};
Expand Down
Loading