add backward_undo_ops as property for action

This commit is contained in:
ajreckof 2023-06-02 23:03:39 +02:00
parent ae896bbd85
commit 81aa5ad999
6 changed files with 61 additions and 15 deletions

View File

@ -80,14 +80,14 @@ bool UndoRedo::_redo(bool p_execute) {
return true;
}
void UndoRedo::create_action(const String &p_name, MergeMode p_mode) {
void UndoRedo::create_action(const String &p_name, MergeMode p_mode, bool p_backward_undo_ops) {
uint64_t ticks = OS::get_singleton()->get_ticks_msec();
if (action_level == 0) {
_discard_redo();
// Check if the merge operation is valid
if (p_mode != MERGE_DISABLE && actions.size() && actions[actions.size() - 1].name == p_name && actions[actions.size() - 1].last_tick + 800 > ticks) {
if (p_mode != MERGE_DISABLE && actions.size() && actions[actions.size() - 1].name == p_name && actions[actions.size() - 1].backward_undo_ops == p_backward_undo_ops && actions[actions.size() - 1].last_tick + 800 > ticks) {
current_action = actions.size() - 2;
if (p_mode == MERGE_ENDS) {
@ -108,12 +108,18 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) {
actions.write[actions.size() - 1].last_tick = ticks;
// Revert reverse from previous commit.
if (actions[actions.size() - 1].backward_undo_ops) {
actions.write[actions.size() - 1].undo_ops.reverse();
}
merge_mode = p_mode;
merging = true;
} else {
Action new_action;
new_action.name = p_name;
new_action.last_tick = ticks;
new_action.backward_undo_ops = p_backward_undo_ops;
actions.push_back(new_action);
merge_mode = MERGE_DISABLE;
@ -292,6 +298,10 @@ void UndoRedo::commit_action(bool p_execute) {
merging = false;
}
if (actions[actions.size() - 1].backward_undo_ops) {
actions.write[actions.size() - 1].undo_ops.reverse();
}
committing++;
_redo(p_execute); // perform action
committing--;
@ -458,7 +468,7 @@ UndoRedo::~UndoRedo() {
}
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", "backward_undo_ops"), &UndoRedo::create_action, DEFVAL(MERGE_DISABLE), DEFVAL(false));
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);

View File

@ -72,7 +72,8 @@ private:
String name;
List<Operation> do_ops;
List<Operation> undo_ops;
uint64_t last_tick;
uint64_t last_tick = 0;
bool backward_undo_ops = false;
};
Vector<Action> actions;
@ -102,7 +103,7 @@ protected:
static void _bind_methods();
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, bool p_backward_undo_ops = false);
void add_do_method(const Callable &p_callable);
void add_undo_method(const Callable &p_callable);

View File

@ -80,10 +80,12 @@
<param index="0" name="name" type="String" />
<param index="1" name="merge_mode" type="int" enum="UndoRedo.MergeMode" default="0" />
<param index="2" name="custom_context" type="Object" default="null" />
<param index="3" name="backward_undo_ops" type="bool" default="false" />
<description>
Create a new action. After this is called, do all your calls to [method add_do_method], [method add_undo_method], [method add_do_property], and [method add_undo_property], then commit the action with [method commit_action].
The way actions are merged is dictated by the [param merge_mode] argument. See [enum UndoRedo.MergeMode] for details.
If [param custom_context] object is provided, it will be used for deducing target history (instead of using the first operation).
The way undo operation are ordered in actions is dictated by [param backward_undo_ops]. When [param backward_undo_ops] is [code]false[/code] undo option are ordered in the same order they were added. Which means the first operation to be added will be the first to be undone.
</description>
</method>
<method name="get_history_undo_redo" qualifiers="const">

View File

@ -56,9 +56,38 @@
}
[/csharp]
[/codeblocks]
[method create_action], [method add_do_method], [method add_undo_method], [method add_do_property], [method add_undo_property], and [method commit_action] should be called one after the other, like in the example. Not doing so could lead to crashes.
If you don't need to register a method, you can leave [method add_do_method] and [method add_undo_method] out; the same goes for properties. You can also register more than one method/property in the order they should run.
Before calling any of the [code]add_(un)do_*[/code] methods, you need to first call [method create_action]. Afterwards you need to call [method commit_action].
If you don't need to register a method, you can leave [method add_do_method] and [method add_undo_method] out; the same goes for properties. You can also register more than one method/property.
If you are making an [EditorPlugin] and want to integrate into the editor's undo history, use [EditorUndoRedoManager] instead.
If you are registering multiple properties/method which depend on one another, be aware that by default undo operation are called in the same order they have been added. Therefore instead of grouping do operation with their undo operations it is better to group do on one side and undo on the other as shown below.
[codeblocks]
[gdscript]
undo_redo.create_action("Add object")
# DO
undo_redo.add_do_method(_create_object)
undo_redo.add_do_method(_add_object_to_singleton)
# UNDO
undo_redo.add_undo_method(_remove_object_from_singleton)
undo_redo.add_undo_method(_destroy_that_object)
undo_redo.commit_action()
[/gdscript]
[csharp]
_undo_redo.CreateAction("Add object");
// DO
_undo_redo.AddDoMethod(new Callable(this, MethodName.CreateObject));
_undo_redo.AddDoMethod(new Callable(this, MethodName.AddObjectToSingleton));
// UNDO
_undo_redo.AddUndoMethod(new Callable(this, MethodName.RemoveObjectFromSingleton));
_undo_redo.AddUndoMethod(new Callable(this, MethodName.DestroyThatObject));
_undo_redo.CommitAction();
[/csharp]
[/codeblocks]
</description>
<tutorials>
</tutorials>
@ -144,9 +173,11 @@
<return type="void" />
<param index="0" name="name" type="String" />
<param index="1" name="merge_mode" type="int" enum="UndoRedo.MergeMode" default="0" />
<param index="2" name="backward_undo_ops" type="bool" default="false" />
<description>
Create a new action. After this is called, do all your calls to [method add_do_method], [method add_undo_method], [method add_do_property], and [method add_undo_property], then commit the action with [method commit_action].
The way actions are merged is dictated by [param merge_mode]. See [enum MergeMode] for details.
The way undo operation are ordered in actions is dictated by [param backward_undo_ops]. When [param backward_undo_ops] is [code]false[/code] undo option are ordered in the same order they were added. Which means the first operation to be added will be the first to be undone.
</description>
</method>
<method name="end_force_keep_in_merge_ends">

View File

@ -110,13 +110,13 @@ EditorUndoRedoManager::History &EditorUndoRedoManager::get_history_for_object(Ob
History &history = get_or_create_history(history_id);
if (pending_action.history_id == INVALID_HISTORY) {
pending_action.history_id = history_id;
history.undo_redo->create_action(pending_action.action_name, pending_action.merge_mode);
history.undo_redo->create_action(pending_action.action_name, pending_action.merge_mode, pending_action.backward_undo_ops);
}
return history;
}
void EditorUndoRedoManager::create_action_for_history(const String &p_name, int p_history_id, UndoRedo::MergeMode p_mode) {
void EditorUndoRedoManager::create_action_for_history(const String &p_name, int p_history_id, UndoRedo::MergeMode p_mode, bool p_backward_undo_ops) {
if (pending_action.history_id != INVALID_HISTORY) {
// Nested action.
p_history_id = pending_action.history_id;
@ -124,17 +124,18 @@ void EditorUndoRedoManager::create_action_for_history(const String &p_name, int
pending_action.action_name = p_name;
pending_action.timestamp = OS::get_singleton()->get_unix_time();
pending_action.merge_mode = p_mode;
pending_action.backward_undo_ops = p_backward_undo_ops;
}
if (p_history_id != INVALID_HISTORY) {
pending_action.history_id = p_history_id;
History &history = get_or_create_history(p_history_id);
history.undo_redo->create_action(pending_action.action_name, pending_action.merge_mode);
history.undo_redo->create_action(pending_action.action_name, pending_action.merge_mode, pending_action.backward_undo_ops);
}
}
void EditorUndoRedoManager::create_action(const String &p_name, UndoRedo::MergeMode p_mode, Object *p_custom_context) {
create_action_for_history(p_name, INVALID_HISTORY, p_mode);
void EditorUndoRedoManager::create_action(const String &p_name, UndoRedo::MergeMode p_mode, Object *p_custom_context, bool p_backward_undo_ops) {
create_action_for_history(p_name, INVALID_HISTORY, p_mode, p_backward_undo_ops);
if (p_custom_context) {
// This assigns history to pending action.
@ -487,7 +488,7 @@ EditorUndoRedoManager::History *EditorUndoRedoManager::_get_newest_undo() {
}
void EditorUndoRedoManager::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_action", "name", "merge_mode", "custom_context"), &EditorUndoRedoManager::create_action, DEFVAL(UndoRedo::MERGE_DISABLE), DEFVAL((Object *)nullptr));
ClassDB::bind_method(D_METHOD("create_action", "name", "merge_mode", "custom_context", "backward_undo_ops"), &EditorUndoRedoManager::create_action, DEFVAL(UndoRedo::MERGE_DISABLE), DEFVAL((Object *)nullptr), DEFVAL(false));
ClassDB::bind_method(D_METHOD("commit_action", "execute"), &EditorUndoRedoManager::commit_action, DEFVAL(true));
ClassDB::bind_method(D_METHOD("is_committing_action"), &EditorUndoRedoManager::is_committing_action);

View File

@ -52,6 +52,7 @@ public:
double timestamp = 0;
String action_name;
UndoRedo::MergeMode merge_mode = UndoRedo::MERGE_DISABLE;
bool backward_undo_ops = false;
};
struct History {
@ -79,8 +80,8 @@ public:
int get_history_id_for_object(Object *p_object) const;
History &get_history_for_object(Object *p_object);
void create_action_for_history(const String &p_name, int p_history_id, UndoRedo::MergeMode p_mode = UndoRedo::MERGE_DISABLE);
void create_action(const String &p_name = "", UndoRedo::MergeMode p_mode = UndoRedo::MERGE_DISABLE, Object *p_custom_context = nullptr);
void create_action_for_history(const String &p_name, int p_history_id, UndoRedo::MergeMode p_mode = UndoRedo::MERGE_DISABLE, bool p_backward_undo_ops = false);
void create_action(const String &p_name = "", UndoRedo::MergeMode p_mode = UndoRedo::MERGE_DISABLE, Object *p_custom_context = nullptr, bool p_backward_undo_ops = false);
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);