Allow undoredo actions to not make history unsaved

This commit is contained in:
kobewi
2025-05-06 13:59:56 +02:00
parent 19bb18716e
commit e7e48cd58c
11 changed files with 93 additions and 11 deletions

View File

@@ -96,11 +96,13 @@
<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" />
<param index="4" name="mark_unsaved" type="bool" default="true" />
<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.
If [param mark_unsaved] is [code]false[/code], the action will not mark the history as unsaved. This is useful for example for actions that change a selection, or a setting that will be saved automatically. Otherwise, this should be left to [code]true[/code] if the action requires saving by the user or if it can cause data loss when left unsaved.
</description>
</method>
<method name="force_fixed_history">

View File

@@ -4661,7 +4661,7 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo
_edit_request_change(object, p_name);
emit_signal(_prop_edited, p_name);
} else {
undo_redo->create_action(vformat(TTR("Set %s"), p_name), UndoRedo::MERGE_ENDS);
undo_redo->create_action(vformat(TTR("Set %s"), p_name), UndoRedo::MERGE_ENDS, nullptr, false, mark_unsaved);
undo_redo->add_do_property(object, p_name, p_value);
bool valid = false;
Variant value = object->get(p_name, &valid);

View File

@@ -603,6 +603,7 @@ class EditorInspector : public ScrollContainer {
bool keying = false;
bool wide_editors = false;
bool deletable_properties = false;
bool mark_unsaved = true;
float refresh_countdown;
bool update_tree_pending = false;
@@ -703,6 +704,7 @@ public:
void set_keying(bool p_active);
void set_read_only(bool p_read_only);
void set_mark_unsaved(bool p_mark) { mark_unsaved = p_mark; }
EditorPropertyNameProcessor::Style get_property_name_style() const;
void set_property_name_style(EditorPropertyNameProcessor::Style p_style);

View File

@@ -915,6 +915,7 @@ EditorSettingsDialog::EditorSettingsDialog() {
inspector = memnew(SectionedInspector);
inspector->get_inspector()->set_use_filter(true);
inspector->get_inspector()->set_mark_unsaved(false);
inspector->register_search_box(search_box);
inspector->register_advanced_toggle(advanced_switch);
inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL);

View File

@@ -0,0 +1,41 @@
/**************************************************************************/
/* editor_undo_redo_manager.compat.inc */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DISABLE_DEPRECATED
void EditorUndoRedoManager::_create_action_bind_compat_106121(const String &p_name, UndoRedo::MergeMode p_mode, Object *p_custom_context, bool p_backward_undo_ops) {
create_action(p_name, p_mode, p_custom_context, p_backward_undo_ops, true);
}
void EditorUndoRedoManager::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("create_action", "name", "merge_mode", "custom_context", "backward_undo_ops"), &EditorUndoRedoManager::_create_action_bind_compat_106121, DEFVAL(UndoRedo::MERGE_DISABLE), DEFVAL((Object *)nullptr), DEFVAL(false));
}
#endif

View File

@@ -29,6 +29,7 @@
/**************************************************************************/
#include "editor_undo_redo_manager.h"
#include "editor_undo_redo_manager.compat.inc"
#include "core/io/resource.h"
#include "core/os/os.h"
@@ -125,7 +126,7 @@ void EditorUndoRedoManager::force_fixed_history() {
forced_history = true;
}
void EditorUndoRedoManager::create_action_for_history(const String &p_name, int p_history_id, UndoRedo::MergeMode p_mode, bool p_backward_undo_ops) {
void EditorUndoRedoManager::create_action_for_history(const String &p_name, int p_history_id, UndoRedo::MergeMode p_mode, bool p_backward_undo_ops, bool p_mark_unsaved) {
if (pending_action.history_id != INVALID_HISTORY) {
// Nested action.
p_history_id = pending_action.history_id;
@@ -134,6 +135,7 @@ void EditorUndoRedoManager::create_action_for_history(const String &p_name, int
pending_action.timestamp = OS::get_singleton()->get_unix_time();
pending_action.merge_mode = p_mode;
pending_action.backward_undo_ops = p_backward_undo_ops;
pending_action.mark_unsaved = p_mark_unsaved;
}
if (p_history_id != INVALID_HISTORY) {
@@ -143,8 +145,8 @@ void EditorUndoRedoManager::create_action_for_history(const String &p_name, int
}
}
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);
void EditorUndoRedoManager::create_action(const String &p_name, UndoRedo::MergeMode p_mode, Object *p_custom_context, bool p_backward_undo_ops, bool p_mark_unsaved) {
create_action_for_history(p_name, INVALID_HISTORY, p_mode, p_backward_undo_ops, p_mark_unsaved);
if (p_custom_context) {
// This assigns history to pending action.
@@ -380,7 +382,26 @@ void EditorUndoRedoManager::set_history_as_unsaved(int p_id) {
bool EditorUndoRedoManager::is_history_unsaved(int p_id) {
History &history = get_or_create_history(p_id);
return history.undo_redo->get_version() != history.saved_version;
int version_difference = history.undo_redo->get_version() - history.saved_version;
if (version_difference > 0) {
List<Action>::Element *E = history.undo_stack.back();
for (int i = 0; i < version_difference; i++) {
if (E->get().mark_unsaved) {
return true;
}
E = E->prev();
}
} else if (version_difference < 0) {
List<Action>::Element *E = history.redo_stack.back();
for (int i = 0; i > version_difference; i--) {
if (E->get().mark_unsaved) {
return true;
}
E = E->prev();
}
}
return false;
}
bool EditorUndoRedoManager::has_undo() {
@@ -492,7 +513,7 @@ EditorUndoRedoManager::History *EditorUndoRedoManager::_get_newest_undo() {
}
void EditorUndoRedoManager::_bind_methods() {
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("create_action", "name", "merge_mode", "custom_context", "backward_undo_ops", "mark_unsaved"), &EditorUndoRedoManager::create_action, DEFVAL(UndoRedo::MERGE_DISABLE), DEFVAL((Object *)nullptr), DEFVAL(false), DEFVAL(true));
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);
ClassDB::bind_method(D_METHOD("force_fixed_history"), &EditorUndoRedoManager::force_fixed_history);

View File

@@ -51,6 +51,7 @@ public:
String action_name;
UndoRedo::MergeMode merge_mode = UndoRedo::MERGE_DISABLE;
bool backward_undo_ops = false;
bool mark_unsaved = true;
};
struct History {
@@ -73,6 +74,11 @@ private:
protected:
static void _bind_methods();
#ifndef DISABLE_DEPRECATED
void _create_action_bind_compat_106121(const String &p_name = "", UndoRedo::MergeMode p_mode = UndoRedo::MERGE_DISABLE, Object *p_custom_context = nullptr, bool p_backward_undo_ops = false);
static void _bind_compatibility_methods();
#endif
public:
History &get_or_create_history(int p_idx);
UndoRedo *get_history_undo_redo(int p_idx) const;
@@ -80,8 +86,8 @@ public:
History &get_history_for_object(Object *p_object);
void force_fixed_history();
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 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, bool p_mark_unsaved = true);
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, bool p_mark_unsaved = true);
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);

View File

@@ -1521,10 +1521,11 @@ ProjectExportDialog::ProjectExportDialog() {
// Main preset parameters.
parameters = memnew(EditorInspector);
sections->add_child(parameters);
parameters->set_name(TTR("Options"));
parameters->set_name(TTRC("Options"));
parameters->set_mark_unsaved(false);
parameters->set_v_size_flags(Control::SIZE_EXPAND_FILL);
parameters->set_use_doc_hints(true);
sections->add_child(parameters);
parameters->connect("property_edited", callable_mp(this, &ProjectExportDialog::_update_parameters));
EditorExport::get_singleton()->connect("export_presets_updated", callable_mp(this, &ProjectExportDialog::_force_update_current_preset_parameters));

View File

@@ -1313,7 +1313,7 @@ void TileMapLayerEditorTilesPlugin::_stop_dragging() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
switch (drag_type) {
case DRAG_TYPE_SELECT: {
undo_redo->create_action_for_history(TTR("Change selection"), EditorNode::get_editor_data().get_current_edited_scene_history_id());
undo_redo->create_action_for_history(TTR("Change selection"), EditorNode::get_editor_data().get_current_edited_scene_history_id(), UndoRedo::MERGE_DISABLE, false, false);
undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
if (!Input::get_singleton()->is_key_pressed(Key::SHIFT) && !Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL)) {

View File

@@ -718,6 +718,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
general_settings_inspector->register_search_box(search_box);
general_settings_inspector->register_advanced_toggle(advanced);
general_settings_inspector->get_inspector()->set_use_filter(true);
general_settings_inspector->get_inspector()->set_mark_unsaved(false);
general_settings_inspector->get_inspector()->connect("property_selected", callable_mp(this, &ProjectSettingsEditor::_setting_selected));
general_settings_inspector->get_inspector()->connect("property_edited", callable_mp(this, &ProjectSettingsEditor::_setting_edited));
general_settings_inspector->get_inspector()->connect("restart_requested", callable_mp(this, &ProjectSettingsEditor::_editor_restart_request));

View File

@@ -104,3 +104,10 @@ GH-105570
Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_create_from_extension/arguments': size changed value in new API, from 9 to 10.
Argument added; p_mipmaps. Compatibility method registered.
GH-106121
--------
Validate extension JSON: Error: Field 'classes/EditorUndoRedoManager/methods/create_action/arguments': size changed value in new API, from 4 to 5.
Validate extension JSON: Error: Field 'classes/EditorUndoRedoManager/methods/create_action/arguments': size changed value in new API, from 3 to 5.
New argument added. Compatibility method registered.