浏览代码

Merge pull request #106121 from KoBeWi/you_can_undo_but_you_can't_save

Allow undoredo actions to not make history unsaved
Thaddeus Crews 3 月之前
父节点
当前提交
006b986fbb

+ 2 - 0
doc/classes/EditorUndoRedoManager.xml

@@ -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">

+ 1 - 1
editor/editor_inspector.cpp

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

+ 2 - 0
editor/editor_inspector.h

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

+ 1 - 0
editor/editor_settings_dialog.cpp

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

+ 41 - 0
editor/editor_undo_redo_manager.compat.inc

@@ -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

+ 26 - 5
editor/editor_undo_redo_manager.cpp

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

+ 8 - 2
editor/editor_undo_redo_manager.h

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

+ 3 - 2
editor/export/project_export.cpp

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

+ 1 - 1
editor/plugins/tiles/tile_map_layer_editor.cpp

@@ -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)) {

+ 1 - 0
editor/project_settings_editor.cpp

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

+ 7 - 0
misc/extension_api_validation/4.4-stable.expected

@@ -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.