Browse Source

Merge pull request #59564 from KoBeWi/FINALLY,_ULTIMATE_UNDO_REDO

Rémi Verschelde 3 years ago
parent
commit
b8a64313f0
100 changed files with 1252 additions and 412 deletions
  1. 1 1
      doc/classes/EditorPlugin.xml
  2. 118 0
      doc/classes/EditorUndoRedoManager.xml
  3. 2 1
      editor/animation_bezier_editor.cpp
  4. 3 2
      editor/animation_bezier_editor.h
  5. 12 7
      editor/animation_track_editor.cpp
  6. 6 6
      editor/animation_track_editor.h
  7. 1 0
      editor/animation_track_editor_plugins.cpp
  8. 4 3
      editor/array_property_edit.cpp
  9. 5 0
      editor/connections_dialog.cpp
  10. 3 3
      editor/connections_dialog.h
  11. 6 2
      editor/debugger/editor_debugger_node.cpp
  12. 2 0
      editor/debugger/editor_debugger_node.h
  13. 3 2
      editor/dictionary_property_edit.cpp
  14. 20 19
      editor/editor_audio_buses.cpp
  15. 6 5
      editor/editor_autoload_settings.cpp
  16. 53 25
      editor/editor_data.cpp
  17. 14 6
      editor/editor_data.h
  18. 9 10
      editor/editor_inspector.cpp
  19. 5 6
      editor/editor_inspector.h
  20. 1 0
      editor/editor_locale_dialog.cpp
  21. 2 2
      editor/editor_locale_dialog.h
  22. 4 2
      editor/editor_log.cpp
  23. 3 0
      editor/editor_log.h
  24. 37 62
      editor/editor_node.cpp
  25. 2 5
      editor/editor_node.h
  26. 6 1
      editor/editor_plugin.cpp
  27. 3 4
      editor/editor_plugin.h
  28. 5 5
      editor/editor_settings_dialog.cpp
  29. 3 1
      editor/editor_settings_dialog.h
  30. 442 0
      editor/editor_undo_redo_manager.cpp
  31. 134 0
      editor/editor_undo_redo_manager.h
  32. 9 0
      editor/groups_editor.cpp
  33. 6 5
      editor/groups_editor.h
  34. 3 2
      editor/inspector_dock.cpp
  35. 1 0
      editor/localization_editor.cpp
  36. 1 1
      editor/localization_editor.h
  37. 2 1
      editor/multi_node_edit.cpp
  38. 1 1
      editor/node_dock.cpp
  39. 1 0
      editor/plugins/abstract_polygon_2d_editor.cpp
  40. 2 1
      editor/plugins/abstract_polygon_2d_editor.h
  41. 1 0
      editor/plugins/animation_blend_space_1d_editor.cpp
  42. 3 1
      editor/plugins/animation_blend_space_1d_editor.h
  43. 1 0
      editor/plugins/animation_blend_space_2d_editor.cpp
  44. 3 1
      editor/plugins/animation_blend_space_2d_editor.h
  45. 1 0
      editor/plugins/animation_blend_tree_editor_plugin.cpp
  46. 2 1
      editor/plugins/animation_blend_tree_editor_plugin.h
  47. 13 12
      editor/plugins/animation_library_editor.cpp
  48. 5 1
      editor/plugins/animation_player_editor_plugin.cpp
  49. 3 2
      editor/plugins/animation_player_editor_plugin.h
  50. 1 0
      editor/plugins/animation_state_machine_editor.cpp
  51. 2 1
      editor/plugins/animation_state_machine_editor.h
  52. 3 2
      editor/plugins/audio_stream_randomizer_editor_plugin.cpp
  53. 30 25
      editor/plugins/canvas_item_editor_plugin.cpp
  54. 3 2
      editor/plugins/canvas_item_editor_plugin.h
  55. 1 0
      editor/plugins/cast_2d_editor_plugin.cpp
  56. 2 1
      editor/plugins/cast_2d_editor_plugin.h
  57. 1 0
      editor/plugins/collision_shape_2d_editor_plugin.cpp
  58. 2 1
      editor/plugins/collision_shape_2d_editor_plugin.h
  59. 1 0
      editor/plugins/control_editor_plugin.cpp
  60. 3 1
      editor/plugins/control_editor_plugin.h
  61. 1 0
      editor/plugins/cpu_particles_2d_editor_plugin.cpp
  62. 2 1
      editor/plugins/cpu_particles_2d_editor_plugin.h
  63. 27 27
      editor/plugins/curve_editor_plugin.cpp
  64. 2 1
      editor/plugins/gpu_particles_2d_editor_plugin.cpp
  65. 2 1
      editor/plugins/gpu_particles_2d_editor_plugin.h
  66. 3 2
      editor/plugins/gpu_particles_3d_editor_plugin.cpp
  67. 2 1
      editor/plugins/gradient_editor_plugin.cpp
  68. 3 2
      editor/plugins/gradient_texture_2d_editor_plugin.cpp
  69. 4 2
      editor/plugins/gradient_texture_2d_editor_plugin.h
  70. 3 4
      editor/plugins/material_editor_plugin.cpp
  71. 8 7
      editor/plugins/mesh_instance_3d_editor_plugin.cpp
  72. 22 21
      editor/plugins/node_3d_editor_gizmos.cpp
  73. 25 17
      editor/plugins/node_3d_editor_plugin.cpp
  74. 5 4
      editor/plugins/node_3d_editor_plugin.h
  75. 1 0
      editor/plugins/path_2d_editor_plugin.cpp
  76. 2 1
      editor/plugins/path_2d_editor_plugin.h
  77. 7 6
      editor/plugins/path_3d_editor_plugin.cpp
  78. 1 0
      editor/plugins/polygon_3d_editor_plugin.cpp
  79. 2 1
      editor/plugins/polygon_3d_editor_plugin.h
  80. 5 1
      editor/plugins/resource_preloader_editor_plugin.cpp
  81. 3 2
      editor/plugins/resource_preloader_editor_plugin.h
  82. 0 2
      editor/plugins/script_editor_plugin.cpp
  83. 3 2
      editor/plugins/skeleton_2d_editor_plugin.cpp
  84. 6 5
      editor/plugins/skeleton_3d_editor_plugin.cpp
  85. 2 1
      editor/plugins/skeleton_3d_editor_plugin.h
  86. 5 4
      editor/plugins/sprite_2d_editor_plugin.cpp
  87. 6 1
      editor/plugins/sprite_frames_editor_plugin.cpp
  88. 3 2
      editor/plugins/sprite_frames_editor_plugin.h
  89. 1 0
      editor/plugins/texture_region_editor_plugin.cpp
  90. 2 1
      editor/plugins/texture_region_editor_plugin.h
  91. 25 24
      editor/plugins/theme_editor_plugin.cpp
  92. 1 0
      editor/plugins/tiles/atlas_merging_dialog.cpp
  93. 2 1
      editor/plugins/tiles/atlas_merging_dialog.h
  94. 17 9
      editor/plugins/tiles/tile_data_editors.cpp
  95. 8 6
      editor/plugins/tiles/tile_data_editors.h
  96. 3 2
      editor/plugins/tiles/tile_map_editor.cpp
  97. 4 4
      editor/plugins/tiles/tile_map_editor.h
  98. 1 0
      editor/plugins/tiles/tile_proxies_manager_dialog.cpp
  99. 1 1
      editor/plugins/tiles/tile_proxies_manager_dialog.h
  100. 10 4
      editor/plugins/tiles/tile_set_atlas_source_editor.cpp

+ 1 - 1
doc/classes/EditorPlugin.xml

@@ -532,7 +532,7 @@
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="get_undo_redo">
 		<method name="get_undo_redo">
-			<return type="UndoRedo" />
+			<return type="EditorUndoRedoManager" />
 			<description>
 			<description>
 				Gets the undo/redo object. Most actions in the editor can be undoable, so use this object to make sure this happens when it's worth it.
 				Gets the undo/redo object. Most actions in the editor can be undoable, so use this object to make sure this happens when it's worth it.
 			</description>
 			</description>

+ 118 - 0
doc/classes/EditorUndoRedoManager.xml

@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="EditorUndoRedoManager" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+	<brief_description>
+		Manages undo history of scenes opened in the editor.
+	</brief_description>
+	<description>
+		[EditorUndoRedoManager] is a manager for [UndoRedo] objects associated with edited scenes. Each scene has its own undo history and [EditorUndoRedoManager] ensures that each action performed in the editor gets associated with a proper scene. For actions not related to scenes ([ProjectSettings] edits, external resources, etc.), a separate global history is used.
+		The usage is mostly the same as [UndoRedo]. You create and commit actions and the manager automatically decides under-the-hood what scenes it belongs to. The scene is deduced based on the first operation in an action, using the object from the operation. The rules are as follows:
+		- If the object is a [Node], use the currently edited scene;
+		- If the object is a built-in resource, use the scene from its path;
+		- If the object is external resource or anything else, use global history.
+		This guessing can sometimes yield false results, so you can provide a custom context object when creating an action.
+	</description>
+	<tutorials>
+	</tutorials>
+	<methods>
+		<method name="add_do_method" qualifiers="vararg">
+			<return type="void" />
+			<param index="0" name="object" type="Object" />
+			<param index="1" name="method" type="StringName" />
+			<description>
+				Register a method that will be called when the action is committed (i.e. the "do" action).
+				If this is the first operation, the [param object] will be used to deduce target undo history.
+			</description>
+		</method>
+		<method name="add_do_property">
+			<return type="void" />
+			<param index="0" name="object" type="Object" />
+			<param index="1" name="property" type="StringName" />
+			<param index="2" name="value" type="Variant" />
+			<description>
+				Register a property value change for "do".
+				If this is the first operation, the [param object] will be used to deduce target undo history.
+			</description>
+		</method>
+		<method name="add_do_reference">
+			<return type="void" />
+			<param index="0" name="object" type="Object" />
+			<description>
+				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">
+			<return type="void" />
+			<param index="0" name="object" type="Object" />
+			<param index="1" name="method" type="StringName" />
+			<description>
+				Register a method that will be called when the action is undone (i.e. the "undo" action).
+				If this is the first operation, the [param object] will be used to deduce target undo history.
+			</description>
+		</method>
+		<method name="add_undo_property">
+			<return type="void" />
+			<param index="0" name="object" type="Object" />
+			<param index="1" name="property" type="StringName" />
+			<param index="2" name="value" type="Variant" />
+			<description>
+				Register a property value change for "undo".
+				If this is the first operation, the [param object] will be used to deduce target undo history.
+			</description>
+		</method>
+		<method name="add_undo_reference">
+			<return type="void" />
+			<param index="0" name="object" type="Object" />
+			<description>
+				Register a reference for "undo" that will be erased if the "undo" history is lost. This is useful mostly for nodes removed with the "do" call (not the "undo" call!).
+			</description>
+		</method>
+		<method name="commit_action">
+			<return type="void" />
+			<param index="0" name="execute" type="bool" default="true" />
+			<description>
+				Commit the action. If [param execute] is true (default), all "do" methods/properties are called/set when this function is called.
+			</description>
+		</method>
+		<method name="create_action">
+			<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="custom_context" type="Object" default="null" />
+			<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).
+			</description>
+		</method>
+		<method name="get_history_undo_redo" qualifiers="const">
+			<return type="UndoRedo" />
+			<param index="0" name="id" type="int" />
+			<description>
+				Returns the [UndoRedo] object associated with the given history [param id].
+				[param id] above [code]0[/code] are mapped to the opened scene tabs (but it doesn't match their order). [param id] of [code]0[/code] or lower have special meaning (see [enum SpecialHistory]).
+				Best used with [method get_object_history_id]. This method is only provided in case you need some more advanced methods of [UndoRedo] (but keep in mind that directly operating on the [UndoRedo] object might affect editor's stability).
+			</description>
+		</method>
+		<method name="get_object_history_id" qualifiers="const">
+			<return type="int" />
+			<param index="0" name="object" type="Object" />
+			<description>
+				Returns the history ID deduced from the given [param object]. It can be used with [method get_history_undo_redo].
+			</description>
+		</method>
+		<method name="is_committing_action" qualifiers="const">
+			<return type="bool" />
+			<description>
+				Returns [code]true[/code] if the [EditorUndoRedoManager] is currently committing the action, i.e. running its "do" method or property change (see [method commit_action]).
+			</description>
+		</method>
+	</methods>
+	<constants>
+		<constant name="GLOBAL_HISTORY" value="0" enum="SpecialHistory">
+			Global history not associated with any scene, but with external resources etc.
+		</constant>
+		<constant name="INVALID_HISTORY" value="-99" enum="SpecialHistory">
+			Invalid "null" history. It's a special value, not associated with any object.
+		</constant>
+	</constants>
+</class>

+ 2 - 1
editor/animation_bezier_editor.cpp

@@ -32,6 +32,7 @@
 
 
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "scene/gui/view_panner.h"
 #include "scene/gui/view_panner.h"
 #include "scene/resources/text_line.h"
 #include "scene/resources/text_line.h"
 
 
@@ -649,7 +650,7 @@ Size2 AnimationBezierTrackEdit::get_minimum_size() const {
 	return Vector2(1, 1);
 	return Vector2(1, 1);
 }
 }
 
 
-void AnimationBezierTrackEdit::set_undo_redo(UndoRedo *p_undo_redo) {
+void AnimationBezierTrackEdit::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) {
 	undo_redo = p_undo_redo;
 	undo_redo = p_undo_redo;
 }
 }
 
 

+ 3 - 2
editor/animation_bezier_editor.h

@@ -34,6 +34,7 @@
 #include "animation_track_editor.h"
 #include "animation_track_editor.h"
 #include "core/templates/rb_set.h"
 #include "core/templates/rb_set.h"
 
 
+class EditorUndoRedoManager;
 class ViewPanner;
 class ViewPanner;
 
 
 class AnimationBezierTrackEdit : public Control {
 class AnimationBezierTrackEdit : public Control {
@@ -48,7 +49,7 @@ class AnimationBezierTrackEdit : public Control {
 	};
 	};
 
 
 	AnimationTimelineEdit *timeline = nullptr;
 	AnimationTimelineEdit *timeline = nullptr;
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 	Node *root = nullptr;
 	Node *root = nullptr;
 	Control *play_position = nullptr; //separate control used to draw so updates for only position changed are much faster
 	Control *play_position = nullptr; //separate control used to draw so updates for only position changed are much faster
 	float play_position_pos = 0;
 	float play_position_pos = 0;
@@ -180,7 +181,7 @@ public:
 	void set_animation_and_track(const Ref<Animation> &p_animation, int p_track, bool p_read_only);
 	void set_animation_and_track(const Ref<Animation> &p_animation, int p_track, bool p_read_only);
 	virtual Size2 get_minimum_size() const override;
 	virtual Size2 get_minimum_size() const override;
 
 
-	void set_undo_redo(UndoRedo *p_undo_redo);
+	void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo);
 	void set_timeline(AnimationTimelineEdit *p_timeline);
 	void set_timeline(AnimationTimelineEdit *p_timeline);
 	void set_editor(AnimationTrackEditor *p_editor);
 	void set_editor(AnimationTrackEditor *p_editor);
 	void set_root(Node *p_root);
 	void set_root(Node *p_root);

+ 12 - 7
editor/animation_track_editor.cpp

@@ -35,6 +35,7 @@
 #include "editor/animation_bezier_editor.h"
 #include "editor/animation_bezier_editor.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "editor/plugins/animation_player_editor_plugin.h"
 #include "editor/plugins/animation_player_editor_plugin.h"
 #include "scene/animation/animation_player.h"
 #include "scene/animation/animation_player.h"
 #include "scene/gui/separator.h"
 #include "scene/gui/separator.h"
@@ -680,7 +681,7 @@ public:
 		}
 		}
 	}
 	}
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 	Ref<Animation> animation;
 	Ref<Animation> animation;
 	int track = -1;
 	int track = -1;
 	float key_ofs = 0;
 	float key_ofs = 0;
@@ -1374,7 +1375,7 @@ public:
 
 
 	bool use_fps = false;
 	bool use_fps = false;
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	void notify_change() {
 	void notify_change() {
 		notify_property_list_changed();
 		notify_property_list_changed();
@@ -1708,7 +1709,7 @@ Size2 AnimationTimelineEdit::get_minimum_size() const {
 	return ms;
 	return ms;
 }
 }
 
 
-void AnimationTimelineEdit::set_undo_redo(UndoRedo *p_undo_redo) {
+void AnimationTimelineEdit::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) {
 	undo_redo = p_undo_redo;
 	undo_redo = p_undo_redo;
 }
 }
 
 
@@ -2507,10 +2508,14 @@ Size2 AnimationTrackEdit::get_minimum_size() const {
 	return Vector2(1, max_h + separation);
 	return Vector2(1, max_h + separation);
 }
 }
 
 
-void AnimationTrackEdit::set_undo_redo(UndoRedo *p_undo_redo) {
+void AnimationTrackEdit::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) {
 	undo_redo = p_undo_redo;
 	undo_redo = p_undo_redo;
 }
 }
 
 
+Ref<EditorUndoRedoManager> AnimationTrackEdit::get_undo_redo() const {
+	return undo_redo;
+}
+
 void AnimationTrackEdit::set_timeline(AnimationTimelineEdit *p_timeline) {
 void AnimationTrackEdit::set_timeline(AnimationTimelineEdit *p_timeline) {
 	timeline = p_timeline;
 	timeline = p_timeline;
 	timeline->set_track_edit(this);
 	timeline->set_track_edit(this);
@@ -6065,7 +6070,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
 		case EDIT_OPTIMIZE_ANIMATION_CONFIRM: {
 		case EDIT_OPTIMIZE_ANIMATION_CONFIRM: {
 			animation->optimize(optimize_velocity_error->get_value(), optimize_angular_error->get_value(), optimize_precision_error->get_value());
 			animation->optimize(optimize_velocity_error->get_value(), optimize_angular_error->get_value(), optimize_precision_error->get_value());
 			_update_tracks();
 			_update_tracks();
-			undo_redo->clear_history();
+			undo_redo->clear_history(true, undo_redo->get_history_for_object(animation.ptr()).id);
 
 
 		} break;
 		} break;
 		case EDIT_CLEAN_UP_ANIMATION: {
 		case EDIT_CLEAN_UP_ANIMATION: {
@@ -6133,7 +6138,7 @@ void AnimationTrackEditor::_cleanup_animation(Ref<Animation> p_animation) {
 		}
 		}
 	}
 	}
 
 
-	undo_redo->clear_history();
+	undo_redo->clear_history(true, undo_redo->get_history_for_object(animation.ptr()).id);
 	_update_tracks();
 	_update_tracks();
 }
 }
 
 
@@ -6303,7 +6308,7 @@ void AnimationTrackEditor::_pick_track_filter_input(const Ref<InputEvent> &p_ie)
 }
 }
 
 
 AnimationTrackEditor::AnimationTrackEditor() {
 AnimationTrackEditor::AnimationTrackEditor() {
-	undo_redo = EditorNode::get_singleton()->get_undo_redo();
+	undo_redo = EditorNode::get_undo_redo();
 
 
 	main_panel = memnew(PanelContainer);
 	main_panel = memnew(PanelContainer);
 	main_panel->set_focus_mode(FOCUS_ALL); // Allow panel to have focus so that shortcuts work as expected.
 	main_panel->set_focus_mode(FOCUS_ALL); // Allow panel to have focus so that shortcuts work as expected.

+ 6 - 6
editor/animation_track_editor.h

@@ -78,7 +78,7 @@ class AnimationTimelineEdit : public Range {
 	void _anim_loop_pressed();
 	void _anim_loop_pressed();
 
 
 	void _play_position_draw();
 	void _play_position_draw();
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 	Rect2 hsize_rect;
 	Rect2 hsize_rect;
 
 
 	bool editing = false;
 	bool editing = false;
@@ -112,7 +112,7 @@ public:
 	void set_track_edit(AnimationTrackEdit *p_track_edit);
 	void set_track_edit(AnimationTrackEdit *p_track_edit);
 	void set_zoom(Range *p_zoom);
 	void set_zoom(Range *p_zoom);
 	Range *get_zoom() const { return zoom; }
 	Range *get_zoom() const { return zoom; }
-	void set_undo_redo(UndoRedo *p_undo_redo);
+	void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo);
 
 
 	void set_play_position(float p_pos);
 	void set_play_position(float p_pos);
 	float get_play_position() const;
 	float get_play_position() const;
@@ -153,7 +153,7 @@ class AnimationTrackEdit : public Control {
 	};
 	};
 
 
 	AnimationTimelineEdit *timeline = nullptr;
 	AnimationTimelineEdit *timeline = nullptr;
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 	Popup *path_popup = nullptr;
 	Popup *path_popup = nullptr;
 	LineEdit *path = nullptr;
 	LineEdit *path = nullptr;
 	Node *root = nullptr;
 	Node *root = nullptr;
@@ -234,12 +234,12 @@ public:
 	Ref<Animation> get_animation() const;
 	Ref<Animation> get_animation() const;
 	AnimationTimelineEdit *get_timeline() const { return timeline; }
 	AnimationTimelineEdit *get_timeline() const { return timeline; }
 	AnimationTrackEditor *get_editor() const { return editor; }
 	AnimationTrackEditor *get_editor() const { return editor; }
-	UndoRedo *get_undo_redo() const { return undo_redo; }
+	Ref<EditorUndoRedoManager> get_undo_redo() const;
 	NodePath get_path() const;
 	NodePath get_path() const;
 	void set_animation_and_track(const Ref<Animation> &p_animation, int p_track, bool p_read_only);
 	void set_animation_and_track(const Ref<Animation> &p_animation, int p_track, bool p_read_only);
 	virtual Size2 get_minimum_size() const override;
 	virtual Size2 get_minimum_size() const override;
 
 
-	void set_undo_redo(UndoRedo *p_undo_redo);
+	void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo);
 	void set_timeline(AnimationTimelineEdit *p_timeline);
 	void set_timeline(AnimationTimelineEdit *p_timeline);
 	void set_editor(AnimationTrackEditor *p_editor);
 	void set_editor(AnimationTrackEditor *p_editor);
 	void set_root(Node *p_root);
 	void set_root(Node *p_root);
@@ -334,7 +334,7 @@ class AnimationTrackEditor : public VBoxContainer {
 	void _animation_track_remove_request(int p_track, Ref<Animation> p_from_animation);
 	void _animation_track_remove_request(int p_track, Ref<Animation> p_from_animation);
 	void _track_grab_focus(int p_track);
 	void _track_grab_focus(int p_track);
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	void _update_scroll(double);
 	void _update_scroll(double);
 	void _update_step(double p_new_step);
 	void _update_step(double p_new_step);

+ 1 - 0
editor/animation_track_editor_plugins.cpp

@@ -33,6 +33,7 @@
 #include "editor/audio_stream_preview.h"
 #include "editor/audio_stream_preview.h"
 #include "editor/editor_resource_preview.h"
 #include "editor/editor_resource_preview.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "scene/2d/animated_sprite_2d.h"
 #include "scene/2d/animated_sprite_2d.h"
 #include "scene/2d/sprite_2d.h"
 #include "scene/2d/sprite_2d.h"
 #include "scene/3d/sprite_3d.h"
 #include "scene/3d/sprite_3d.h"

+ 4 - 3
editor/array_property_edit.cpp

@@ -32,6 +32,7 @@
 
 
 #include "core/io/marshalls.h"
 #include "core/io/marshalls.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
+#include "editor/editor_undo_redo_manager.h"
 
 
 #define ITEMS_PER_PAGE 100
 #define ITEMS_PER_PAGE 100
 
 
@@ -87,7 +88,7 @@ bool ArrayPropertyEdit::_set(const StringName &p_name, const Variant &p_value) {
 				return true;
 				return true;
 			}
 			}
 
 
-			UndoRedo *ur = EditorNode::get_undo_redo();
+			Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 			ur->create_action(TTR("Resize Array"));
 			ur->create_action(TTR("Resize Array"));
 			ur->add_do_method(this, "_set_size", newsize);
 			ur->add_do_method(this, "_set_size", newsize);
 			ur->add_undo_method(this, "_set_size", size);
 			ur->add_undo_method(this, "_set_size", size);
@@ -134,7 +135,7 @@ bool ArrayPropertyEdit::_set(const StringName &p_name, const Variant &p_value) {
 				Callable::CallError ce;
 				Callable::CallError ce;
 				Variant new_value;
 				Variant new_value;
 				Variant::construct(Variant::Type(type), new_value, nullptr, 0, ce);
 				Variant::construct(Variant::Type(type), new_value, nullptr, 0, ce);
-				UndoRedo *ur = EditorNode::get_undo_redo();
+				Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 
 
 				ur->create_action(TTR("Change Array Value Type"));
 				ur->create_action(TTR("Change Array Value Type"));
 				ur->add_do_method(this, "_set_value", idx, new_value);
 				ur->add_do_method(this, "_set_value", idx, new_value);
@@ -150,7 +151,7 @@ bool ArrayPropertyEdit::_set(const StringName &p_name, const Variant &p_value) {
 			Variant arr = get_array();
 			Variant arr = get_array();
 
 
 			Variant value = arr.get(idx);
 			Variant value = arr.get(idx);
-			UndoRedo *ur = EditorNode::get_undo_redo();
+			Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 
 
 			ur->create_action(TTR("Change Array Value"));
 			ur->create_action(TTR("Change Array Value"));
 			ur->add_do_method(this, "_set_value", idx, p_value);
 			ur->add_do_method(this, "_set_value", idx, p_value);

+ 5 - 0
editor/connections_dialog.cpp

@@ -34,6 +34,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "editor/scene_tree_dock.h"
 #include "editor/scene_tree_dock.h"
 #include "plugins/script_editor_plugin.h"
 #include "plugins/script_editor_plugin.h"
 
 
@@ -924,6 +925,10 @@ void ConnectionsDock::_bind_methods() {
 	ClassDB::bind_method("update_tree", &ConnectionsDock::update_tree);
 	ClassDB::bind_method("update_tree", &ConnectionsDock::update_tree);
 }
 }
 
 
+void ConnectionsDock::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) {
+	undo_redo = p_undo_redo;
+}
+
 void ConnectionsDock::set_node(Node *p_node) {
 void ConnectionsDock::set_node(Node *p_node) {
 	selected_node = p_node;
 	selected_node = p_node;
 	update_tree();
 	update_tree();

+ 3 - 3
editor/connections_dialog.h

@@ -31,7 +31,6 @@
 #ifndef CONNECTIONS_DIALOG_H
 #ifndef CONNECTIONS_DIALOG_H
 #define CONNECTIONS_DIALOG_H
 #define CONNECTIONS_DIALOG_H
 
 
-#include "core/object/undo_redo.h"
 #include "editor/editor_inspector.h"
 #include "editor/editor_inspector.h"
 #include "editor/scene_tree_editor.h"
 #include "editor/scene_tree_editor.h"
 #include "scene/gui/button.h"
 #include "scene/gui/button.h"
@@ -48,6 +47,7 @@
 #include "scene/gui/tree.h"
 #include "scene/gui/tree.h"
 
 
 class ConnectDialogBinds;
 class ConnectDialogBinds;
+class EditorUndoRedoManager;
 
 
 class ConnectDialog : public ConfirmationDialog {
 class ConnectDialog : public ConfirmationDialog {
 	GDCLASS(ConnectDialog, ConfirmationDialog);
 	GDCLASS(ConnectDialog, ConfirmationDialog);
@@ -194,7 +194,7 @@ class ConnectionsDock : public VBoxContainer {
 	Button *connect_button = nullptr;
 	Button *connect_button = nullptr;
 	PopupMenu *signal_menu = nullptr;
 	PopupMenu *signal_menu = nullptr;
 	PopupMenu *slot_menu = nullptr;
 	PopupMenu *slot_menu = nullptr;
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 	LineEdit *search_box = nullptr;
 	LineEdit *search_box = nullptr;
 
 
 	HashMap<StringName, HashMap<StringName, String>> descr_cache;
 	HashMap<StringName, HashMap<StringName, String>> descr_cache;
@@ -225,7 +225,7 @@ protected:
 	static void _bind_methods();
 	static void _bind_methods();
 
 
 public:
 public:
-	void set_undoredo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; }
+	void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo);
 	void set_node(Node *p_node);
 	void set_node(Node *p_node);
 	void update_tree();
 	void update_tree();
 
 

+ 6 - 2
editor/debugger/editor_debugger_node.cpp

@@ -30,6 +30,7 @@
 
 
 #include "editor_debugger_node.h"
 #include "editor_debugger_node.h"
 
 
+#include "core/object/undo_redo.h"
 #include "editor/debugger/editor_debugger_tree.h"
 #include "editor/debugger/editor_debugger_tree.h"
 #include "editor/debugger/script_editor_debugger.h"
 #include "editor/debugger/script_editor_debugger.h"
 #include "editor/editor_log.h"
 #include "editor/editor_log.h"
@@ -83,8 +84,6 @@ EditorDebuggerNode::EditorDebuggerNode() {
 	inspect_edited_object_timeout = EDITOR_DEF("debugger/remote_inspect_refresh_interval", 0.2);
 	inspect_edited_object_timeout = EDITOR_DEF("debugger/remote_inspect_refresh_interval", 0.2);
 
 
 	EditorNode *editor = EditorNode::get_singleton();
 	EditorNode *editor = EditorNode::get_singleton();
-	editor->get_undo_redo()->set_method_notify_callback(_method_changeds, this);
-	editor->get_undo_redo()->set_property_notify_callback(_property_changeds, this);
 	editor->get_pause_button()->connect("pressed", callable_mp(this, &EditorDebuggerNode::_paused));
 	editor->get_pause_button()->connect("pressed", callable_mp(this, &EditorDebuggerNode::_paused));
 }
 }
 
 
@@ -181,6 +180,11 @@ void EditorDebuggerNode::_bind_methods() {
 	ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::BOOL, "enabled")));
 	ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::BOOL, "enabled")));
 }
 }
 
 
+void EditorDebuggerNode::register_undo_redo(UndoRedo *p_undo_redo) {
+	p_undo_redo->set_method_notify_callback(_method_changeds, this);
+	p_undo_redo->set_property_notify_callback(_property_changeds, this);
+}
+
 EditorDebuggerRemoteObject *EditorDebuggerNode::get_inspected_remote_object() {
 EditorDebuggerRemoteObject *EditorDebuggerNode::get_inspected_remote_object() {
 	return Object::cast_to<EditorDebuggerRemoteObject>(ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_selection_history()->get_current()));
 	return Object::cast_to<EditorDebuggerRemoteObject>(ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_selection_history()->get_current()));
 }
 }

+ 2 - 0
editor/debugger/editor_debugger_node.h

@@ -41,6 +41,7 @@ class EditorDebuggerRemoteObject;
 class MenuButton;
 class MenuButton;
 class ScriptEditorDebugger;
 class ScriptEditorDebugger;
 class TabContainer;
 class TabContainer;
+class UndoRedo;
 
 
 class EditorDebuggerNode : public MarginContainer {
 class EditorDebuggerNode : public MarginContainer {
 	GDCLASS(EditorDebuggerNode, MarginContainer);
 	GDCLASS(EditorDebuggerNode, MarginContainer);
@@ -152,6 +153,7 @@ protected:
 
 
 public:
 public:
 	static EditorDebuggerNode *get_singleton() { return singleton; }
 	static EditorDebuggerNode *get_singleton() { return singleton; }
+	void register_undo_redo(UndoRedo *p_undo_redo);
 
 
 	ScriptEditorDebugger *get_current_debugger() const;
 	ScriptEditorDebugger *get_current_debugger() const;
 	ScriptEditorDebugger *get_default_debugger() const;
 	ScriptEditorDebugger *get_default_debugger() const;

+ 3 - 2
editor/dictionary_property_edit.cpp

@@ -30,6 +30,7 @@
 
 
 #include "dictionary_property_edit.h"
 #include "dictionary_property_edit.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
+#include "editor/editor_undo_redo_manager.h"
 
 
 void DictionaryPropertyEdit::_notif_change() {
 void DictionaryPropertyEdit::_notif_change() {
 	notify_property_list_changed();
 	notify_property_list_changed();
@@ -118,7 +119,7 @@ bool DictionaryPropertyEdit::_set(const StringName &p_name, const Variant &p_val
 		int index = pn.substr(0, slash).to_int();
 		int index = pn.substr(0, slash).to_int();
 		if (type == "key" && index < keys.size()) {
 		if (type == "key" && index < keys.size()) {
 			const Variant &key = keys[index];
 			const Variant &key = keys[index];
-			UndoRedo *ur = EditorNode::get_undo_redo();
+			Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 
 
 			ur->create_action(TTR("Change Dictionary Key"));
 			ur->create_action(TTR("Change Dictionary Key"));
 			ur->add_do_method(this, "_set_key", key, p_value);
 			ur->add_do_method(this, "_set_key", key, p_value);
@@ -130,7 +131,7 @@ bool DictionaryPropertyEdit::_set(const StringName &p_name, const Variant &p_val
 			const Variant &key = keys[index];
 			const Variant &key = keys[index];
 			if (dict.has(key)) {
 			if (dict.has(key)) {
 				Variant value = dict[key];
 				Variant value = dict[key];
-				UndoRedo *ur = EditorNode::get_undo_redo();
+				Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 
 
 				ur->create_action(TTR("Change Dictionary Value"));
 				ur->create_action(TTR("Change Dictionary Value"));
 				ur->add_do_method(this, "_set_value", key, p_value);
 				ur->add_do_method(this, "_set_value", key, p_value);

+ 20 - 19
editor/editor_audio_buses.cpp

@@ -38,6 +38,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "filesystem_dock.h"
 #include "filesystem_dock.h"
 #include "scene/resources/font.h"
 #include "scene/resources/font.h"
 #include "servers/audio_server.h"
 #include "servers/audio_server.h"
@@ -280,7 +281,7 @@ void EditorAudioBus::_name_changed(const String &p_new_name) {
 	}
 	}
 	updating_bus = true;
 	updating_bus = true;
 
 
-	UndoRedo *ur = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 
 
 	StringName current = AudioServer::get_singleton()->get_bus_name(get_index());
 	StringName current = AudioServer::get_singleton()->get_bus_name(get_index());
 	ur->create_action(TTR("Rename Audio Bus"));
 	ur->create_action(TTR("Rename Audio Bus"));
@@ -321,7 +322,7 @@ void EditorAudioBus::_volume_changed(float p_normalized) {
 		slider->set_value(_scaled_db_to_normalized_volume(Math::round(p_db)));
 		slider->set_value(_scaled_db_to_normalized_volume(Math::round(p_db)));
 	}
 	}
 
 
-	UndoRedo *ur = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Change Audio Bus Volume"), UndoRedo::MERGE_ENDS);
 	ur->create_action(TTR("Change Audio Bus Volume"), UndoRedo::MERGE_ENDS);
 	ur->add_do_method(AudioServer::get_singleton(), "set_bus_volume_db", get_index(), p_db);
 	ur->add_do_method(AudioServer::get_singleton(), "set_bus_volume_db", get_index(), p_db);
 	ur->add_undo_method(AudioServer::get_singleton(), "set_bus_volume_db", get_index(), AudioServer::get_singleton()->get_bus_volume_db(get_index()));
 	ur->add_undo_method(AudioServer::get_singleton(), "set_bus_volume_db", get_index(), AudioServer::get_singleton()->get_bus_volume_db(get_index()));
@@ -415,7 +416,7 @@ void EditorAudioBus::_hide_value_preview() {
 void EditorAudioBus::_solo_toggled() {
 void EditorAudioBus::_solo_toggled() {
 	updating_bus = true;
 	updating_bus = true;
 
 
-	UndoRedo *ur = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Toggle Audio Bus Solo"));
 	ur->create_action(TTR("Toggle Audio Bus Solo"));
 	ur->add_do_method(AudioServer::get_singleton(), "set_bus_solo", get_index(), solo->is_pressed());
 	ur->add_do_method(AudioServer::get_singleton(), "set_bus_solo", get_index(), solo->is_pressed());
 	ur->add_undo_method(AudioServer::get_singleton(), "set_bus_solo", get_index(), AudioServer::get_singleton()->is_bus_solo(get_index()));
 	ur->add_undo_method(AudioServer::get_singleton(), "set_bus_solo", get_index(), AudioServer::get_singleton()->is_bus_solo(get_index()));
@@ -429,7 +430,7 @@ void EditorAudioBus::_solo_toggled() {
 void EditorAudioBus::_mute_toggled() {
 void EditorAudioBus::_mute_toggled() {
 	updating_bus = true;
 	updating_bus = true;
 
 
-	UndoRedo *ur = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Toggle Audio Bus Mute"));
 	ur->create_action(TTR("Toggle Audio Bus Mute"));
 	ur->add_do_method(AudioServer::get_singleton(), "set_bus_mute", get_index(), mute->is_pressed());
 	ur->add_do_method(AudioServer::get_singleton(), "set_bus_mute", get_index(), mute->is_pressed());
 	ur->add_undo_method(AudioServer::get_singleton(), "set_bus_mute", get_index(), AudioServer::get_singleton()->is_bus_mute(get_index()));
 	ur->add_undo_method(AudioServer::get_singleton(), "set_bus_mute", get_index(), AudioServer::get_singleton()->is_bus_mute(get_index()));
@@ -443,7 +444,7 @@ void EditorAudioBus::_mute_toggled() {
 void EditorAudioBus::_bypass_toggled() {
 void EditorAudioBus::_bypass_toggled() {
 	updating_bus = true;
 	updating_bus = true;
 
 
-	UndoRedo *ur = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Toggle Audio Bus Bypass Effects"));
 	ur->create_action(TTR("Toggle Audio Bus Bypass Effects"));
 	ur->add_do_method(AudioServer::get_singleton(), "set_bus_bypass_effects", get_index(), bypass->is_pressed());
 	ur->add_do_method(AudioServer::get_singleton(), "set_bus_bypass_effects", get_index(), bypass->is_pressed());
 	ur->add_undo_method(AudioServer::get_singleton(), "set_bus_bypass_effects", get_index(), AudioServer::get_singleton()->is_bus_bypassing_effects(get_index()));
 	ur->add_undo_method(AudioServer::get_singleton(), "set_bus_bypass_effects", get_index(), AudioServer::get_singleton()->is_bus_bypassing_effects(get_index()));
@@ -457,7 +458,7 @@ void EditorAudioBus::_bypass_toggled() {
 void EditorAudioBus::_send_selected(int p_which) {
 void EditorAudioBus::_send_selected(int p_which) {
 	updating_bus = true;
 	updating_bus = true;
 
 
-	UndoRedo *ur = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Select Audio Bus Send"));
 	ur->create_action(TTR("Select Audio Bus Send"));
 	ur->add_do_method(AudioServer::get_singleton(), "set_bus_send", get_index(), send->get_item_text(p_which));
 	ur->add_do_method(AudioServer::get_singleton(), "set_bus_send", get_index(), send->get_item_text(p_which));
 	ur->add_undo_method(AudioServer::get_singleton(), "set_bus_send", get_index(), AudioServer::get_singleton()->get_bus_send(get_index()));
 	ur->add_undo_method(AudioServer::get_singleton(), "set_bus_send", get_index(), AudioServer::get_singleton()->get_bus_send(get_index()));
@@ -507,7 +508,7 @@ void EditorAudioBus::_effect_edited() {
 		int index = effect->get_metadata(0);
 		int index = effect->get_metadata(0);
 		updating_bus = true;
 		updating_bus = true;
 
 
-		UndoRedo *ur = EditorNode::get_undo_redo();
+		Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 		ur->create_action(TTR("Select Audio Bus Send"));
 		ur->create_action(TTR("Select Audio Bus Send"));
 		ur->add_do_method(AudioServer::get_singleton(), "set_bus_effect_enabled", get_index(), index, effect->is_checked(0));
 		ur->add_do_method(AudioServer::get_singleton(), "set_bus_effect_enabled", get_index(), index, effect->is_checked(0));
 		ur->add_undo_method(AudioServer::get_singleton(), "set_bus_effect_enabled", get_index(), index, AudioServer::get_singleton()->is_bus_effect_enabled(get_index(), index));
 		ur->add_undo_method(AudioServer::get_singleton(), "set_bus_effect_enabled", get_index(), index, AudioServer::get_singleton()->is_bus_effect_enabled(get_index(), index));
@@ -534,7 +535,7 @@ void EditorAudioBus::_effect_add(int p_which) {
 
 
 	afxr->set_name(effect_options->get_item_text(p_which));
 	afxr->set_name(effect_options->get_item_text(p_which));
 
 
-	UndoRedo *ur = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Add Audio Bus Effect"));
 	ur->create_action(TTR("Add Audio Bus Effect"));
 	ur->add_do_method(AudioServer::get_singleton(), "add_bus_effect", get_index(), afxr, -1);
 	ur->add_do_method(AudioServer::get_singleton(), "add_bus_effect", get_index(), afxr, -1);
 	ur->add_undo_method(AudioServer::get_singleton(), "remove_bus_effect", get_index(), AudioServer::get_singleton()->get_bus_effect_count(get_index()));
 	ur->add_undo_method(AudioServer::get_singleton(), "remove_bus_effect", get_index(), AudioServer::get_singleton()->get_bus_effect_count(get_index()));
@@ -688,7 +689,7 @@ void EditorAudioBus::drop_data_fw(const Point2 &p_point, const Variant &p_data,
 
 
 	bool enabled = AudioServer::get_singleton()->is_bus_effect_enabled(bus, effect);
 	bool enabled = AudioServer::get_singleton()->is_bus_effect_enabled(bus, effect);
 
 
-	UndoRedo *ur = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Move Bus Effect"));
 	ur->create_action(TTR("Move Bus Effect"));
 	ur->add_do_method(AudioServer::get_singleton(), "remove_bus_effect", bus, effect);
 	ur->add_do_method(AudioServer::get_singleton(), "remove_bus_effect", bus, effect);
 	ur->add_do_method(AudioServer::get_singleton(), "add_bus_effect", get_index(), AudioServer::get_singleton()->get_bus_effect(bus, effect), paste_at);
 	ur->add_do_method(AudioServer::get_singleton(), "add_bus_effect", get_index(), AudioServer::get_singleton()->get_bus_effect(bus, effect), paste_at);
@@ -730,7 +731,7 @@ void EditorAudioBus::_delete_effect_pressed(int p_option) {
 
 
 	int index = item->get_metadata(0);
 	int index = item->get_metadata(0);
 
 
-	UndoRedo *ur = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Delete Bus Effect"));
 	ur->create_action(TTR("Delete Bus Effect"));
 	ur->add_do_method(AudioServer::get_singleton(), "remove_bus_effect", get_index(), index);
 	ur->add_do_method(AudioServer::get_singleton(), "remove_bus_effect", get_index(), index);
 	ur->add_undo_method(AudioServer::get_singleton(), "add_bus_effect", get_index(), AudioServer::get_singleton()->get_bus_effect(get_index(), index), index);
 	ur->add_undo_method(AudioServer::get_singleton(), "add_bus_effect", get_index(), AudioServer::get_singleton()->get_bus_effect(get_index(), index), index);
@@ -1063,7 +1064,7 @@ void EditorAudioBuses::_notification(int p_what) {
 }
 }
 
 
 void EditorAudioBuses::_add_bus() {
 void EditorAudioBuses::_add_bus() {
-	UndoRedo *ur = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 
 
 	ur->create_action(TTR("Add Audio Bus"));
 	ur->create_action(TTR("Add Audio Bus"));
 	ur->add_do_method(AudioServer::get_singleton(), "set_bus_count", AudioServer::get_singleton()->get_bus_count() + 1);
 	ur->add_do_method(AudioServer::get_singleton(), "set_bus_count", AudioServer::get_singleton()->get_bus_count() + 1);
@@ -1095,7 +1096,7 @@ void EditorAudioBuses::_delete_bus(Object *p_which) {
 		return;
 		return;
 	}
 	}
 
 
-	UndoRedo *ur = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 
 
 	ur->create_action(TTR("Delete Audio Bus"));
 	ur->create_action(TTR("Delete Audio Bus"));
 	ur->add_do_method(AudioServer::get_singleton(), "remove_bus", index);
 	ur->add_do_method(AudioServer::get_singleton(), "remove_bus", index);
@@ -1117,7 +1118,7 @@ void EditorAudioBuses::_delete_bus(Object *p_which) {
 
 
 void EditorAudioBuses::_duplicate_bus(int p_which) {
 void EditorAudioBuses::_duplicate_bus(int p_which) {
 	int add_at_pos = p_which + 1;
 	int add_at_pos = p_which + 1;
-	UndoRedo *ur = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Duplicate Audio Bus"));
 	ur->create_action(TTR("Duplicate Audio Bus"));
 	ur->add_do_method(AudioServer::get_singleton(), "add_bus", add_at_pos);
 	ur->add_do_method(AudioServer::get_singleton(), "add_bus", add_at_pos);
 	ur->add_do_method(AudioServer::get_singleton(), "set_bus_name", add_at_pos, AudioServer::get_singleton()->get_bus_name(p_which) + " Copy");
 	ur->add_do_method(AudioServer::get_singleton(), "set_bus_name", add_at_pos, AudioServer::get_singleton()->get_bus_name(p_which) + " Copy");
@@ -1140,7 +1141,7 @@ void EditorAudioBuses::_reset_bus_volume(Object *p_which) {
 	EditorAudioBus *bus = Object::cast_to<EditorAudioBus>(p_which);
 	EditorAudioBus *bus = Object::cast_to<EditorAudioBus>(p_which);
 	int index = bus->get_index();
 	int index = bus->get_index();
 
 
-	UndoRedo *ur = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Reset Bus Volume"));
 	ur->create_action(TTR("Reset Bus Volume"));
 	ur->add_do_method(AudioServer::get_singleton(), "set_bus_volume_db", index, 0.f);
 	ur->add_do_method(AudioServer::get_singleton(), "set_bus_volume_db", index, 0.f);
 	ur->add_undo_method(AudioServer::get_singleton(), "set_bus_volume_db", index, AudioServer::get_singleton()->get_bus_volume_db(index));
 	ur->add_undo_method(AudioServer::get_singleton(), "set_bus_volume_db", index, AudioServer::get_singleton()->get_bus_volume_db(index));
@@ -1160,7 +1161,7 @@ void EditorAudioBuses::_request_drop_end() {
 }
 }
 
 
 void EditorAudioBuses::_drop_at_index(int p_bus, int p_index) {
 void EditorAudioBuses::_drop_at_index(int p_bus, int p_index) {
-	UndoRedo *ur = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Move Audio Bus"));
 	ur->create_action(TTR("Move Audio Bus"));
 
 
 	ur->add_do_method(AudioServer::get_singleton(), "move_bus", p_bus, p_index);
 	ur->add_do_method(AudioServer::get_singleton(), "move_bus", p_bus, p_index);
@@ -1219,7 +1220,7 @@ void EditorAudioBuses::_load_default_layout() {
 	file->set_text(String(TTR("Layout:")) + " " + layout_path.get_file());
 	file->set_text(String(TTR("Layout:")) + " " + layout_path.get_file());
 	AudioServer::get_singleton()->set_bus_layout(state);
 	AudioServer::get_singleton()->set_bus_layout(state);
 	_update_buses();
 	_update_buses();
-	EditorNode::get_singleton()->get_undo_redo()->clear_history();
+	EditorNode::get_undo_redo()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
 	call_deferred(SNAME("_select_layout"));
 	call_deferred(SNAME("_select_layout"));
 }
 }
 
 
@@ -1235,7 +1236,7 @@ void EditorAudioBuses::_file_dialog_callback(const String &p_string) {
 		file->set_text(String(TTR("Layout:")) + " " + p_string.get_file());
 		file->set_text(String(TTR("Layout:")) + " " + p_string.get_file());
 		AudioServer::get_singleton()->set_bus_layout(state);
 		AudioServer::get_singleton()->set_bus_layout(state);
 		_update_buses();
 		_update_buses();
-		EditorNode::get_singleton()->get_undo_redo()->clear_history();
+		EditorNode::get_undo_redo()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
 		call_deferred(SNAME("_select_layout"));
 		call_deferred(SNAME("_select_layout"));
 
 
 	} else if (file_dialog->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) {
 	} else if (file_dialog->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) {
@@ -1255,7 +1256,7 @@ void EditorAudioBuses::_file_dialog_callback(const String &p_string) {
 		edited_path = p_string;
 		edited_path = p_string;
 		file->set_text(String(TTR("Layout:")) + " " + p_string.get_file());
 		file->set_text(String(TTR("Layout:")) + " " + p_string.get_file());
 		_update_buses();
 		_update_buses();
-		EditorNode::get_singleton()->get_undo_redo()->clear_history();
+		EditorNode::get_undo_redo()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
 		call_deferred(SNAME("_select_layout"));
 		call_deferred(SNAME("_select_layout"));
 	}
 	}
 }
 }
@@ -1354,7 +1355,7 @@ void EditorAudioBuses::open_layout(const String &p_path) {
 	file->set_text(p_path.get_file());
 	file->set_text(p_path.get_file());
 	AudioServer::get_singleton()->set_bus_layout(state);
 	AudioServer::get_singleton()->set_bus_layout(state);
 	_update_buses();
 	_update_buses();
-	EditorNode::get_singleton()->get_undo_redo()->clear_history();
+	EditorNode::get_undo_redo()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
 	call_deferred(SNAME("_select_layout"));
 	call_deferred(SNAME("_select_layout"));
 }
 }
 
 

+ 6 - 5
editor/editor_autoload_settings.cpp

@@ -35,6 +35,7 @@
 #include "editor/editor_file_dialog.h"
 #include "editor/editor_file_dialog.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "editor/filesystem_dock.h"
 #include "editor/filesystem_dock.h"
 #include "project_settings_editor.h"
 #include "project_settings_editor.h"
 #include "scene/main/window.h"
 #include "scene/main/window.h"
@@ -193,7 +194,7 @@ void EditorAutoloadSettings::_autoload_edited() {
 	TreeItem *ti = tree->get_edited();
 	TreeItem *ti = tree->get_edited();
 	int column = tree->get_edited_column();
 	int column = tree->get_edited_column();
 
 
-	UndoRedo *undo_redo = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo();
 
 
 	if (column == 0) {
 	if (column == 0) {
 		String name = ti->get_text(0);
 		String name = ti->get_text(0);
@@ -288,7 +289,7 @@ void EditorAutoloadSettings::_autoload_button_pressed(Object *p_item, int p_colu
 
 
 	String name = "autoload/" + ti->get_text(0);
 	String name = "autoload/" + ti->get_text(0);
 
 
-	UndoRedo *undo_redo = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo();
 
 
 	switch (p_button) {
 	switch (p_button) {
 		case BUTTON_OPEN: {
 		case BUTTON_OPEN: {
@@ -713,7 +714,7 @@ void EditorAutoloadSettings::drop_data_fw(const Point2 &p_point, const Variant &
 
 
 	orders.sort();
 	orders.sort();
 
 
-	UndoRedo *undo_redo = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo();
 
 
 	undo_redo->create_action(TTR("Rearrange Autoloads"));
 	undo_redo->create_action(TTR("Rearrange Autoloads"));
 
 
@@ -757,7 +758,7 @@ bool EditorAutoloadSettings::autoload_add(const String &p_name, const String &p_
 
 
 	name = "autoload/" + name;
 	name = "autoload/" + name;
 
 
-	UndoRedo *undo_redo = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo();
 
 
 	undo_redo->create_action(TTR("Add Autoload"));
 	undo_redo->create_action(TTR("Add Autoload"));
 	// Singleton autoloads are represented with a leading "*" in their path.
 	// Singleton autoloads are represented with a leading "*" in their path.
@@ -783,7 +784,7 @@ bool EditorAutoloadSettings::autoload_add(const String &p_name, const String &p_
 void EditorAutoloadSettings::autoload_remove(const String &p_name) {
 void EditorAutoloadSettings::autoload_remove(const String &p_name) {
 	String name = "autoload/" + p_name;
 	String name = "autoload/" + p_name;
 
 
-	UndoRedo *undo_redo = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo();
 
 
 	int order = ProjectSettings::get_singleton()->get_order(name);
 	int order = ProjectSettings::get_singleton()->get_order(name);
 
 

+ 53 - 25
editor/editor_data.cpp

@@ -35,6 +35,7 @@
 #include "core/io/resource_loader.h"
 #include "core/io/resource_loader.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_plugin.h"
 #include "editor/editor_plugin.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "editor/plugins/script_editor_plugin.h"
 #include "editor/plugins/script_editor_plugin.h"
 #include "scene/resources/packed_scene.h"
 #include "scene/resources/packed_scene.h"
 
 
@@ -364,13 +365,13 @@ void EditorData::restore_editor_global_states() {
 
 
 void EditorData::paste_object_params(Object *p_object) {
 void EditorData::paste_object_params(Object *p_object) {
 	ERR_FAIL_NULL(p_object);
 	ERR_FAIL_NULL(p_object);
-	undo_redo.create_action(TTR("Paste Params"));
+	undo_redo_manager->create_action(TTR("Paste Params"));
 	for (const PropertyData &E : clipboard) {
 	for (const PropertyData &E : clipboard) {
 		String name = E.name;
 		String name = E.name;
-		undo_redo.add_do_property(p_object, name, E.value);
-		undo_redo.add_undo_property(p_object, name, p_object->get(name));
+		undo_redo_manager->add_do_property(p_object, name, E.value);
+		undo_redo_manager->add_undo_property(p_object, name, p_object->get(name));
 	}
 	}
-	undo_redo.commit_action();
+	undo_redo_manager->commit_action();
 }
 }
 
 
 bool EditorData::call_build() {
 bool EditorData::call_build() {
@@ -383,8 +384,49 @@ bool EditorData::call_build() {
 	return result;
 	return result;
 }
 }
 
 
-UndoRedo &EditorData::get_undo_redo() {
-	return undo_redo;
+void EditorData::set_scene_as_saved(int p_idx) {
+	if (p_idx == -1) {
+		p_idx = current_edited_scene;
+	}
+	ERR_FAIL_INDEX(p_idx, edited_scene.size());
+
+	get_undo_redo()->set_history_as_saved(edited_scene[p_idx].history_id);
+}
+
+bool EditorData::is_scene_changed(int p_idx) {
+	if (p_idx == -1) {
+		p_idx = current_edited_scene;
+	}
+	ERR_FAIL_INDEX_V(p_idx, edited_scene.size(), false);
+
+	uint64_t current_scene_version = get_undo_redo()->get_or_create_history(edited_scene[p_idx].history_id).undo_redo->get_version();
+	bool is_changed = edited_scene[p_idx].last_checked_version != current_scene_version;
+	edited_scene.write[p_idx].last_checked_version = current_scene_version;
+	return is_changed;
+}
+
+int EditorData::get_scene_history_id_from_path(const String &p_path) const {
+	for (const EditedScene &E : edited_scene) {
+		if (E.path == p_path) {
+			return E.history_id;
+		}
+	}
+	return 0;
+}
+
+int EditorData::get_current_edited_scene_history_id() const {
+	if (current_edited_scene != -1) {
+		return edited_scene[current_edited_scene].history_id;
+	}
+	return 0;
+}
+
+int EditorData::get_scene_history_id(int p_idx) const {
+	return edited_scene[p_idx].history_id;
+}
+
+Ref<EditorUndoRedoManager> &EditorData::get_undo_redo() {
+	return undo_redo_manager;
 }
 }
 
 
 void EditorData::add_undo_redo_inspector_hook_callback(Callable p_callable) {
 void EditorData::add_undo_redo_inspector_hook_callback(Callable p_callable) {
@@ -415,12 +457,12 @@ Callable EditorData::get_move_array_element_function(const StringName &p_class)
 }
 }
 
 
 void EditorData::remove_editor_plugin(EditorPlugin *p_plugin) {
 void EditorData::remove_editor_plugin(EditorPlugin *p_plugin) {
-	p_plugin->undo_redo = nullptr;
+	p_plugin->undo_redo = Ref<EditorUndoRedoManager>();
 	editor_plugins.erase(p_plugin);
 	editor_plugins.erase(p_plugin);
 }
 }
 
 
 void EditorData::add_editor_plugin(EditorPlugin *p_plugin) {
 void EditorData::add_editor_plugin(EditorPlugin *p_plugin) {
-	p_plugin->undo_redo = &undo_redo;
+	p_plugin->undo_redo = undo_redo_manager;
 	editor_plugins.push_back(p_plugin);
 	editor_plugins.push_back(p_plugin);
 }
 }
 
 
@@ -505,8 +547,8 @@ int EditorData::add_edited_scene(int p_at_pos) {
 	es.path = String();
 	es.path = String();
 	es.file_modified_time = 0;
 	es.file_modified_time = 0;
 	es.history_current = -1;
 	es.history_current = -1;
-	es.version = 0;
 	es.live_edit_root = NodePath(String("/root"));
 	es.live_edit_root = NodePath(String("/root"));
+	es.history_id = last_created_scene++;
 
 
 	if (p_at_pos == edited_scene.size()) {
 	if (p_at_pos == edited_scene.size()) {
 		edited_scene.push_back(es);
 		edited_scene.push_back(es);
@@ -547,6 +589,7 @@ void EditorData::remove_scene(int p_idx) {
 		ScriptEditor::get_singleton()->close_builtin_scripts_from_scene(edited_scene[p_idx].path);
 		ScriptEditor::get_singleton()->close_builtin_scripts_from_scene(edited_scene[p_idx].path);
 	}
 	}
 
 
+	undo_redo_manager->discard_history(edited_scene[p_idx].history_id);
 	edited_scene.remove_at(p_idx);
 	edited_scene.remove_at(p_idx);
 }
 }
 
 
@@ -679,26 +722,10 @@ Vector<EditorData::EditedScene> EditorData::get_edited_scenes() const {
 	return out_edited_scenes_list;
 	return out_edited_scenes_list;
 }
 }
 
 
-void EditorData::set_edited_scene_version(uint64_t version, int p_scene_idx) {
-	ERR_FAIL_INDEX(current_edited_scene, edited_scene.size());
-	if (p_scene_idx < 0) {
-		edited_scene.write[current_edited_scene].version = version;
-	} else {
-		ERR_FAIL_INDEX(p_scene_idx, edited_scene.size());
-		edited_scene.write[p_scene_idx].version = version;
-	}
-}
-
-uint64_t EditorData::get_scene_version(int p_idx) const {
-	ERR_FAIL_INDEX_V(p_idx, edited_scene.size(), 0);
-	return edited_scene[p_idx].version;
-}
-
 void EditorData::set_scene_modified_time(int p_idx, uint64_t p_time) {
 void EditorData::set_scene_modified_time(int p_idx, uint64_t p_time) {
 	if (p_idx == -1) {
 	if (p_idx == -1) {
 		p_idx = current_edited_scene;
 		p_idx = current_edited_scene;
 	}
 	}
-
 	ERR_FAIL_INDEX(p_idx, edited_scene.size());
 	ERR_FAIL_INDEX(p_idx, edited_scene.size());
 
 
 	edited_scene.write[p_idx].file_modified_time = p_time;
 	edited_scene.write[p_idx].file_modified_time = p_time;
@@ -991,6 +1018,7 @@ void EditorData::script_class_load_icon_paths() {
 
 
 EditorData::EditorData() {
 EditorData::EditorData() {
 	current_edited_scene = -1;
 	current_edited_scene = -1;
+	undo_redo_manager.instantiate();
 	script_class_load_icon_paths();
 	script_class_load_icon_paths();
 }
 }
 
 

+ 14 - 6
editor/editor_data.h

@@ -31,12 +31,12 @@
 #ifndef EDITOR_DATA_H
 #ifndef EDITOR_DATA_H
 #define EDITOR_DATA_H
 #define EDITOR_DATA_H
 
 
-#include "core/object/undo_redo.h"
 #include "core/templates/list.h"
 #include "core/templates/list.h"
 #include "scene/resources/texture.h"
 #include "scene/resources/texture.h"
 
 
 class ConfigFile;
 class ConfigFile;
 class EditorPlugin;
 class EditorPlugin;
+class EditorUndoRedoManager;
 
 
 /**
 /**
  * Stores the history of objects which have been selected for editing in the Editor & the Inspector.
  * Stores the history of objects which have been selected for editing in the Editor & the Inspector.
@@ -118,8 +118,9 @@ public:
 		Vector<EditorSelectionHistory::HistoryElement> history_stored;
 		Vector<EditorSelectionHistory::HistoryElement> history_stored;
 		int history_current = 0;
 		int history_current = 0;
 		Dictionary custom_state;
 		Dictionary custom_state;
-		uint64_t version = 0;
 		NodePath live_edit_root;
 		NodePath live_edit_root;
+		int history_id = 0;
+		uint64_t last_checked_version = 0;
 	};
 	};
 
 
 private:
 private:
@@ -132,12 +133,13 @@ private:
 	HashMap<String, Vector<CustomType>> custom_types;
 	HashMap<String, Vector<CustomType>> custom_types;
 
 
 	List<PropertyData> clipboard;
 	List<PropertyData> clipboard;
-	UndoRedo undo_redo;
+	Ref<EditorUndoRedoManager> undo_redo_manager;
 	Vector<Callable> undo_redo_callbacks;
 	Vector<Callable> undo_redo_callbacks;
 	HashMap<StringName, Callable> move_element_functions;
 	HashMap<StringName, Callable> move_element_functions;
 
 
 	Vector<EditedScene> edited_scene;
 	Vector<EditedScene> edited_scene;
-	int current_edited_scene;
+	int current_edited_scene = -1;
+	int last_created_scene = 1;
 
 
 	bool _find_updated_instances(Node *p_root, Node *p_node, HashSet<String> &checked_paths);
 	bool _find_updated_instances(Node *p_root, Node *p_node, HashSet<String> &checked_paths);
 
 
@@ -166,7 +168,7 @@ public:
 	int get_editor_plugin_count() const;
 	int get_editor_plugin_count() const;
 	EditorPlugin *get_editor_plugin(int p_idx);
 	EditorPlugin *get_editor_plugin(int p_idx);
 
 
-	UndoRedo &get_undo_redo();
+	Ref<EditorUndoRedoManager> &get_undo_redo();
 	void add_undo_redo_inspector_hook_callback(Callable p_callable); // Callbacks should have this signature: void (Object* undo_redo, Object *modified_object, String property, Variant new_value)
 	void add_undo_redo_inspector_hook_callback(Callable p_callable); // Callbacks should have this signature: void (Object* undo_redo, Object *modified_object, String property, Variant new_value)
 	void remove_undo_redo_inspector_hook_callback(Callable p_callable);
 	void remove_undo_redo_inspector_hook_callback(Callable p_callable);
 	const Vector<Callable> get_undo_redo_inspector_hook_callback();
 	const Vector<Callable> get_undo_redo_inspector_hook_callback();
@@ -200,7 +202,6 @@ public:
 	void set_scene_path(int p_idx, const String &p_path);
 	void set_scene_path(int p_idx, const String &p_path);
 	Ref<Script> get_scene_root_script(int p_idx) const;
 	Ref<Script> get_scene_root_script(int p_idx) const;
 	void set_edited_scene_version(uint64_t version, int p_scene_idx = -1);
 	void set_edited_scene_version(uint64_t version, int p_scene_idx = -1);
-	uint64_t get_scene_version(int p_idx) const;
 	void set_scene_modified_time(int p_idx, uint64_t p_time);
 	void set_scene_modified_time(int p_idx, uint64_t p_time);
 	uint64_t get_scene_modified_time(int p_idx) const;
 	uint64_t get_scene_modified_time(int p_idx) const;
 	void clear_edited_scenes();
 	void clear_edited_scenes();
@@ -210,6 +211,13 @@ public:
 	void move_edited_scene_to_index(int p_idx);
 	void move_edited_scene_to_index(int p_idx);
 	bool call_build();
 	bool call_build();
 
 
+	void set_scene_as_saved(int p_idx);
+	bool is_scene_changed(int p_idx);
+
+	int get_scene_history_id_from_path(const String &p_path) const;
+	int get_current_edited_scene_history_id() const;
+	int get_scene_history_id(int p_idx) const;
+
 	void set_plugin_window_layout(Ref<ConfigFile> p_layout);
 	void set_plugin_window_layout(Ref<ConfigFile> p_layout);
 	void get_plugin_window_layout(Ref<ConfigFile> p_layout);
 	void get_plugin_window_layout(Ref<ConfigFile> p_layout);
 
 

+ 9 - 10
editor/editor_inspector.cpp

@@ -1701,7 +1701,7 @@ void EditorInspectorArray::_move_element(int p_element_index, int p_to_pos) {
 		// Call the function.
 		// Call the function.
 		Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name());
 		Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name());
 		if (move_function.is_valid()) {
 		if (move_function.is_valid()) {
-			Variant args[] = { (Object *)undo_redo, object, array_element_prefix, p_element_index, p_to_pos };
+			Variant args[] = { undo_redo.ptr(), object, array_element_prefix, p_element_index, p_to_pos };
 			const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] };
 			const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] };
 			Variant return_value;
 			Variant return_value;
 			Callable::CallError call_error;
 			Callable::CallError call_error;
@@ -1845,7 +1845,7 @@ void EditorInspectorArray::_clear_array() {
 			// Call the function.
 			// Call the function.
 			Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name());
 			Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name());
 			if (move_function.is_valid()) {
 			if (move_function.is_valid()) {
-				Variant args[] = { (Object *)undo_redo, object, array_element_prefix, i, -1 };
+				Variant args[] = { undo_redo.ptr(), object, array_element_prefix, i, -1 };
 				const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] };
 				const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] };
 				Variant return_value;
 				Variant return_value;
 				Callable::CallError call_error;
 				Callable::CallError call_error;
@@ -1898,7 +1898,7 @@ void EditorInspectorArray::_resize_array(int p_size) {
 				// Call the function.
 				// Call the function.
 				Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name());
 				Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name());
 				if (move_function.is_valid()) {
 				if (move_function.is_valid()) {
-					Variant args[] = { (Object *)undo_redo, object, array_element_prefix, -1, -1 };
+					Variant args[] = { undo_redo.ptr(), object, array_element_prefix, -1, -1 };
 					const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] };
 					const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] };
 					Variant return_value;
 					Variant return_value;
 					Callable::CallError call_error;
 					Callable::CallError call_error;
@@ -1917,7 +1917,7 @@ void EditorInspectorArray::_resize_array(int p_size) {
 				// Call the function.
 				// Call the function.
 				Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name());
 				Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name());
 				if (move_function.is_valid()) {
 				if (move_function.is_valid()) {
-					Variant args[] = { (Object *)undo_redo, object, array_element_prefix, i, -1 };
+					Variant args[] = { undo_redo.ptr(), object, array_element_prefix, i, -1 };
 					const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] };
 					const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] };
 					Variant return_value;
 					Variant return_value;
 					Callable::CallError call_error;
 					Callable::CallError call_error;
@@ -2240,7 +2240,7 @@ void EditorInspectorArray::_bind_methods() {
 	ADD_SIGNAL(MethodInfo("page_change_request"));
 	ADD_SIGNAL(MethodInfo("page_change_request"));
 }
 }
 
 
-void EditorInspectorArray::set_undo_redo(UndoRedo *p_undo_redo) {
+void EditorInspectorArray::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) {
 	undo_redo = p_undo_redo;
 	undo_redo = p_undo_redo;
 }
 }
 
 
@@ -2502,7 +2502,7 @@ Button *EditorInspector::create_inspector_action_button(const String &p_text) {
 	return button;
 	return button;
 }
 }
 
 
-void EditorInspector::set_undo_redo(UndoRedo *p_undo_redo) {
+void EditorInspector::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) {
 	undo_redo = p_undo_redo;
 	undo_redo = p_undo_redo;
 }
 }
 
 
@@ -3525,7 +3525,7 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo
 		}
 		}
 	}
 	}
 
 
-	if (!undo_redo || bool(object->call("_dont_undo_redo"))) {
+	if (!undo_redo.is_valid() || bool(object->call("_dont_undo_redo"))) {
 		object->set(p_name, p_value);
 		object->set(p_name, p_value);
 		if (p_refresh_all) {
 		if (p_refresh_all) {
 			_edit_request_change(object, "");
 			_edit_request_change(object, "");
@@ -3568,7 +3568,7 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo
 			}
 			}
 		}
 		}
 
 
-		Variant v_undo_redo = (Object *)undo_redo;
+		Variant v_undo_redo = undo_redo;
 		Variant v_object = object;
 		Variant v_object = object;
 		Variant v_name = p_name;
 		Variant v_name = p_name;
 		for (int i = 0; i < EditorNode::get_singleton()->get_editor_data().get_undo_redo_inspector_hook_callback().size(); i++) {
 		for (int i = 0; i < EditorNode::get_singleton()->get_editor_data().get_undo_redo_inspector_hook_callback().size(); i++) {
@@ -3744,7 +3744,7 @@ void EditorInspector::_property_pinned(const String &p_path, bool p_pinned) {
 	Node *node = Object::cast_to<Node>(object);
 	Node *node = Object::cast_to<Node>(object);
 	ERR_FAIL_COND(!node);
 	ERR_FAIL_COND(!node);
 
 
-	if (undo_redo) {
+	if (undo_redo.is_valid()) {
 		undo_redo->create_action(vformat(p_pinned ? TTR("Pinned %s") : TTR("Unpinned %s"), p_path));
 		undo_redo->create_action(vformat(p_pinned ? TTR("Pinned %s") : TTR("Unpinned %s"), p_path));
 		undo_redo->add_do_method(node, "_set_property_pinned", p_path, p_pinned);
 		undo_redo->add_do_method(node, "_set_property_pinned", p_path, p_pinned);
 		undo_redo->add_undo_method(node, "_set_property_pinned", p_path, !p_pinned);
 		undo_redo->add_undo_method(node, "_set_property_pinned", p_path, !p_pinned);
@@ -4026,7 +4026,6 @@ void EditorInspector::_bind_methods() {
 
 
 EditorInspector::EditorInspector() {
 EditorInspector::EditorInspector() {
 	object = nullptr;
 	object = nullptr;
-	undo_redo = nullptr;
 	main_vbox = memnew(VBoxContainer);
 	main_vbox = memnew(VBoxContainer);
 	main_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
 	main_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
 	main_vbox->add_theme_constant_override("separation", 0);
 	main_vbox->add_theme_constant_override("separation", 0);

+ 5 - 6
editor/editor_inspector.h

@@ -31,6 +31,7 @@
 #ifndef EDITOR_INSPECTOR_H
 #ifndef EDITOR_INSPECTOR_H
 #define EDITOR_INSPECTOR_H
 #define EDITOR_INSPECTOR_H
 
 
+#include "editor/editor_undo_redo_manager.h"
 #include "editor_property_name_processor.h"
 #include "editor_property_name_processor.h"
 #include "scene/gui/box_container.h"
 #include "scene/gui/box_container.h"
 #include "scene/gui/button.h"
 #include "scene/gui/button.h"
@@ -42,8 +43,6 @@
 #include "scene/gui/spin_box.h"
 #include "scene/gui/spin_box.h"
 #include "scene/gui/texture_rect.h"
 #include "scene/gui/texture_rect.h"
 
 
-class UndoRedo;
-
 class EditorPropertyRevert {
 class EditorPropertyRevert {
 public:
 public:
 	static bool get_instantiated_node_original_property(Node *p_node, const StringName &p_prop, Variant &value, bool p_check_class_default = true);
 	static bool get_instantiated_node_original_property(Node *p_node, const StringName &p_prop, Variant &value, bool p_check_class_default = true);
@@ -313,7 +312,7 @@ public:
 class EditorInspectorArray : public EditorInspectorSection {
 class EditorInspectorArray : public EditorInspectorSection {
 	GDCLASS(EditorInspectorArray, EditorInspectorSection);
 	GDCLASS(EditorInspectorArray, EditorInspectorSection);
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	enum Mode {
 	enum Mode {
 		MODE_NONE,
 		MODE_NONE,
@@ -408,7 +407,7 @@ protected:
 	static void _bind_methods();
 	static void _bind_methods();
 
 
 public:
 public:
-	void set_undo_redo(UndoRedo *p_undo_redo);
+	void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo);
 
 
 	void setup_with_move_element_function(Object *p_object, String p_label, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable = true, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = "");
 	void setup_with_move_element_function(Object *p_object, String p_label, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable = true, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = "");
 	void setup_with_count_property(Object *p_object, String p_label, const StringName &p_count_property, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable = true, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = "", const String &p_swap_method = "");
 	void setup_with_count_property(Object *p_object, String p_label, const StringName &p_count_property, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable = true, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = "", const String &p_swap_method = "");
@@ -448,7 +447,7 @@ public:
 class EditorInspector : public ScrollContainer {
 class EditorInspector : public ScrollContainer {
 	GDCLASS(EditorInspector, ScrollContainer);
 	GDCLASS(EditorInspector, ScrollContainer);
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 	enum {
 	enum {
 		MAX_PLUGINS = 1024
 		MAX_PLUGINS = 1024
 	};
 	};
@@ -562,7 +561,7 @@ public:
 
 
 	static EditorProperty *instantiate_property_editor(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide = false);
 	static EditorProperty *instantiate_property_editor(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide = false);
 
 
-	void set_undo_redo(UndoRedo *p_undo_redo);
+	void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo);
 
 
 	String get_selected_path() const;
 	String get_selected_path() const;
 
 

+ 1 - 0
editor/editor_locale_dialog.cpp

@@ -33,6 +33,7 @@
 #include "core/config/project_settings.h"
 #include "core/config/project_settings.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "scene/gui/check_button.h"
 #include "scene/gui/check_button.h"
 #include "scene/gui/line_edit.h"
 #include "scene/gui/line_edit.h"
 #include "scene/gui/option_button.h"
 #include "scene/gui/option_button.h"

+ 2 - 2
editor/editor_locale_dialog.h

@@ -40,7 +40,7 @@ class VBoxContainer;
 class LineEdit;
 class LineEdit;
 class Tree;
 class Tree;
 class OptionButton;
 class OptionButton;
-class UndoRedo;
+class EditorUndoRedoManager;
 
 
 class EditorLocaleDialog : public ConfirmationDialog {
 class EditorLocaleDialog : public ConfirmationDialog {
 	GDCLASS(EditorLocaleDialog, ConfirmationDialog);
 	GDCLASS(EditorLocaleDialog, ConfirmationDialog);
@@ -63,7 +63,7 @@ class EditorLocaleDialog : public ConfirmationDialog {
 	Tree *script_list = nullptr;
 	Tree *script_list = nullptr;
 	Tree *cnt_list = nullptr;
 	Tree *cnt_list = nullptr;
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	bool locale_set = false;
 	bool locale_set = false;
 	bool updating_lists = false;
 	bool updating_lists = false;

+ 4 - 2
editor/editor_log.cpp

@@ -224,6 +224,10 @@ void EditorLog::set_tool_button(Button *p_tool_button) {
 	tool_button = p_tool_button;
 	tool_button = p_tool_button;
 }
 }
 
 
+void EditorLog::register_undo_redo(UndoRedo *p_undo_redo) {
+	p_undo_redo->set_commit_notify_callback(_undo_redo_cbk, this);
+}
+
 void EditorLog::_undo_redo_cbk(void *p_self, const String &p_name) {
 void EditorLog::_undo_redo_cbk(void *p_self, const String &p_name) {
 	EditorLog *self = static_cast<EditorLog *>(p_self);
 	EditorLog *self = static_cast<EditorLog *>(p_self);
 	self->add_message(p_name, EditorLog::MSG_TYPE_EDITOR);
 	self->add_message(p_name, EditorLog::MSG_TYPE_EDITOR);
@@ -458,8 +462,6 @@ EditorLog::EditorLog() {
 	add_error_handler(&eh);
 	add_error_handler(&eh);
 
 
 	current = Thread::get_caller_id();
 	current = Thread::get_caller_id();
-
-	EditorNode::get_undo_redo()->set_commit_notify_callback(_undo_redo_cbk, this);
 }
 }
 
 
 void EditorLog::deinit() {
 void EditorLog::deinit() {

+ 3 - 0
editor/editor_log.h

@@ -41,6 +41,8 @@
 #include "scene/gui/texture_button.h"
 #include "scene/gui/texture_button.h"
 #include "scene/gui/texture_rect.h"
 #include "scene/gui/texture_rect.h"
 
 
+class UndoRedo;
+
 class EditorLog : public HBoxContainer {
 class EditorLog : public HBoxContainer {
 	GDCLASS(EditorLog, HBoxContainer);
 	GDCLASS(EditorLog, HBoxContainer);
 
 
@@ -182,6 +184,7 @@ protected:
 public:
 public:
 	void add_message(const String &p_msg, MessageType p_type = MSG_TYPE_STD);
 	void add_message(const String &p_msg, MessageType p_type = MSG_TYPE_STD);
 	void set_tool_button(Button *p_tool_button);
 	void set_tool_button(Button *p_tool_button);
+	void register_undo_redo(UndoRedo *p_undo_redo);
 	void deinit();
 	void deinit();
 
 
 	void clear();
 	void clear();

+ 37 - 62
editor/editor_node.cpp

@@ -105,6 +105,7 @@
 #include "editor/editor_themes.h"
 #include "editor/editor_themes.h"
 #include "editor/editor_toaster.h"
 #include "editor/editor_toaster.h"
 #include "editor/editor_translation_parser.h"
 #include "editor/editor_translation_parser.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "editor/export/editor_export.h"
 #include "editor/export/editor_export.h"
 #include "editor/export/export_template_manager.h"
 #include "editor/export/export_template_manager.h"
 #include "editor/export/project_export.h"
 #include "editor/export/project_export.h"
@@ -349,8 +350,7 @@ void EditorNode::_update_scene_tabs() {
 			icon = EditorNode::get_singleton()->get_object_icon(type_node, "Node");
 			icon = EditorNode::get_singleton()->get_object_icon(type_node, "Node");
 		}
 		}
 
 
-		int current = editor_data.get_edited_scene();
-		bool unsaved = (i == current) ? saved_version != editor_data.get_undo_redo().get_version() : editor_data.get_scene_version(i) != 0;
+		bool unsaved = get_undo_redo()->is_history_unsaved(editor_data.get_scene_history_id(i));
 		scene_tabs->add_tab(disambiguated_scene_names[i] + (unsaved ? "(*)" : ""), icon);
 		scene_tabs->add_tab(disambiguated_scene_names[i] + (unsaved ? "(*)" : ""), icon);
 
 
 		if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) {
 		if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) {
@@ -597,15 +597,15 @@ void EditorNode::_notification(int p_what) {
 				opening_prev = false;
 				opening_prev = false;
 			}
 			}
 
 
-			bool unsaved_cache_changed = false;
-			if (unsaved_cache != (saved_version != editor_data.get_undo_redo().get_version())) {
-				unsaved_cache = (saved_version != editor_data.get_undo_redo().get_version());
-				unsaved_cache_changed = true;
+			bool global_unsaved = get_undo_redo()->is_history_unsaved(EditorUndoRedoManager::GLOBAL_HISTORY);
+			bool scene_or_global_unsaved = global_unsaved || get_undo_redo()->is_history_unsaved(editor_data.get_current_edited_scene_history_id());
+			if (unsaved_cache != scene_or_global_unsaved) {
+				unsaved_cache = scene_or_global_unsaved;
+				_update_title();
 			}
 			}
 
 
-			if (last_checked_version != editor_data.get_undo_redo().get_version()) {
+			if (editor_data.is_scene_changed(-1)) {
 				_update_scene_tabs();
 				_update_scene_tabs();
-				last_checked_version = editor_data.get_undo_redo().get_version();
 			}
 			}
 
 
 			// Update the animation frame of the update spinner.
 			// Update the animation frame of the update spinner.
@@ -631,7 +631,7 @@ void EditorNode::_notification(int p_what) {
 
 
 			ResourceImporterTexture::get_singleton()->update_imports();
 			ResourceImporterTexture::get_singleton()->update_imports();
 
 
-			if (settings_changed || unsaved_cache_changed) {
+			if (settings_changed) {
 				_update_title();
 				_update_title();
 			}
 			}
 
 
@@ -1138,7 +1138,6 @@ void EditorNode::_reload_modified_scenes() {
 		}
 		}
 	}
 	}
 
 
-	get_undo_redo()->clear_history(false);
 	set_current_scene(current_idx);
 	set_current_scene(current_idx);
 	_update_scene_tabs();
 	_update_scene_tabs();
 	disk_changed->hide();
 	disk_changed->hide();
@@ -1692,6 +1691,8 @@ int EditorNode::_save_external_resources() {
 		saved++;
 		saved++;
 	}
 	}
 
 
+	get_undo_redo()->set_history_as_saved(EditorUndoRedoManager::GLOBAL_HISTORY);
+
 	return saved;
 	return saved;
 }
 }
 
 
@@ -1773,11 +1774,7 @@ void EditorNode::_save_scene(String p_file, int idx) {
 
 
 	if (err == OK) {
 	if (err == OK) {
 		scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(p_file));
 		scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(p_file));
-		if (idx < 0 || idx == editor_data.get_edited_scene()) {
-			set_current_version(editor_data.get_undo_redo().get_version());
-		} else {
-			editor_data.set_edited_scene_version(0, idx);
-		}
+		editor_data.set_scene_as_saved(idx);
 		editor_data.set_scene_modified_time(idx, FileAccess::get_modified_time(p_file));
 		editor_data.set_scene_modified_time(idx, FileAccess::get_modified_time(p_file));
 
 
 		editor_folding.save_scene_folding(scene, p_file);
 		editor_folding.save_scene_folding(scene, p_file);
@@ -1869,12 +1866,9 @@ void EditorNode::_mark_unsaved_scenes() {
 		}
 		}
 
 
 		String path = node->get_scene_file_path();
 		String path = node->get_scene_file_path();
-		if (!(path.is_empty() || FileAccess::exists(path))) {
-			if (i == editor_data.get_edited_scene()) {
-				set_current_version(-1);
-			} else {
-				editor_data.set_edited_scene_version(-1, i);
-			}
+		if (!path.is_empty() && !FileAccess::exists(path)) {
+			// Mark scene tab as unsaved if the file is gone.
+			get_undo_redo()->set_history_as_unsaved(editor_data.get_scene_history_id(i));
 		}
 		}
 	}
 	}
 
 
@@ -2732,9 +2726,9 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
 			if ((int)Input::get_singleton()->get_mouse_button_mask() & 0x7) {
 			if ((int)Input::get_singleton()->get_mouse_button_mask() & 0x7) {
 				log->add_message(TTR("Can't undo while mouse buttons are pressed."), EditorLog::MSG_TYPE_EDITOR);
 				log->add_message(TTR("Can't undo while mouse buttons are pressed."), EditorLog::MSG_TYPE_EDITOR);
 			} else {
 			} else {
-				String action = editor_data.get_undo_redo().get_current_action_name();
+				String action = editor_data.get_undo_redo()->get_current_action_name();
 
 
-				if (!editor_data.get_undo_redo().undo()) {
+				if (!editor_data.get_undo_redo()->undo()) {
 					log->add_message(TTR("Nothing to undo."), EditorLog::MSG_TYPE_EDITOR);
 					log->add_message(TTR("Nothing to undo."), EditorLog::MSG_TYPE_EDITOR);
 				} else if (!action.is_empty()) {
 				} else if (!action.is_empty()) {
 					log->add_message(vformat(TTR("Undo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
 					log->add_message(vformat(TTR("Undo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
@@ -2745,10 +2739,10 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
 			if ((int)Input::get_singleton()->get_mouse_button_mask() & 0x7) {
 			if ((int)Input::get_singleton()->get_mouse_button_mask() & 0x7) {
 				log->add_message(TTR("Can't redo while mouse buttons are pressed."), EditorLog::MSG_TYPE_EDITOR);
 				log->add_message(TTR("Can't redo while mouse buttons are pressed."), EditorLog::MSG_TYPE_EDITOR);
 			} else {
 			} else {
-				if (!editor_data.get_undo_redo().redo()) {
+				if (!editor_data.get_undo_redo()->redo()) {
 					log->add_message(TTR("Nothing to redo."), EditorLog::MSG_TYPE_EDITOR);
 					log->add_message(TTR("Nothing to redo."), EditorLog::MSG_TYPE_EDITOR);
 				} else {
 				} else {
-					String action = editor_data.get_undo_redo().get_current_action_name();
+					String action = editor_data.get_undo_redo()->get_current_action_name();
 					log->add_message(vformat(TTR("Redo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
 					log->add_message(vformat(TTR("Redo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
 				}
 				}
 			}
 			}
@@ -2783,7 +2777,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
 				ERR_PRINT("Failed to load scene");
 				ERR_PRINT("Failed to load scene");
 			}
 			}
 			editor_data.move_edited_scene_to_index(cur_idx);
 			editor_data.move_edited_scene_to_index(cur_idx);
-			get_undo_redo()->clear_history(false);
+			get_undo_redo()->clear_history(false, editor_data.get_current_edited_scene_history_id());
 			scene_tabs->set_current_tab(cur_idx);
 			scene_tabs->set_current_tab(cur_idx);
 
 
 		} break;
 		} break;
@@ -3096,8 +3090,7 @@ int EditorNode::_next_unsaved_scene(bool p_valid_filename, int p_start) {
 		if (!editor_data.get_edited_scene_root(i)) {
 		if (!editor_data.get_edited_scene_root(i)) {
 			continue;
 			continue;
 		}
 		}
-		int current = editor_data.get_edited_scene();
-		bool unsaved = (i == current) ? saved_version != editor_data.get_undo_redo().get_version() : editor_data.get_scene_version(i) != 0;
+		bool unsaved = get_undo_redo()->is_history_unsaved(editor_data.get_scene_history_id(i));
 		if (unsaved) {
 		if (unsaved) {
 			String scene_filename = editor_data.get_edited_scene_root(i)->get_scene_file_path();
 			String scene_filename = editor_data.get_edited_scene_root(i)->get_scene_file_path();
 			if (p_valid_filename && scene_filename.length() == 0) {
 			if (p_valid_filename && scene_filename.length() == 0) {
@@ -3207,9 +3200,9 @@ void EditorNode::_update_file_menu_opened() {
 
 
 	file_menu->set_item_disabled(file_menu->get_item_index(FILE_OPEN_PREV), previous_scenes.is_empty());
 	file_menu->set_item_disabled(file_menu->get_item_index(FILE_OPEN_PREV), previous_scenes.is_empty());
 
 
-	const UndoRedo &undo_redo = editor_data.get_undo_redo();
-	file_menu->set_item_disabled(file_menu->get_item_index(EDIT_UNDO), !undo_redo.has_undo());
-	file_menu->set_item_disabled(file_menu->get_item_index(EDIT_REDO), !undo_redo.has_redo());
+	Ref<EditorUndoRedoManager> undo_redo = editor_data.get_undo_redo();
+	file_menu->set_item_disabled(file_menu->get_item_index(EDIT_UNDO), !undo_redo->has_undo());
+	file_menu->set_item_disabled(file_menu->get_item_index(EDIT_REDO), !undo_redo->has_redo());
 }
 }
 
 
 void EditorNode::_update_file_menu_closed() {
 void EditorNode::_update_file_menu_closed() {
@@ -3465,7 +3458,6 @@ void EditorNode::_remove_edited_scene(bool p_change_tab) {
 		_scene_tab_changed(new_index);
 		_scene_tab_changed(new_index);
 	}
 	}
 	editor_data.remove_scene(old_index);
 	editor_data.remove_scene(old_index);
-	editor_data.get_undo_redo().clear_history(false);
 	_update_title();
 	_update_title();
 	_update_scene_tabs();
 	_update_scene_tabs();
 }
 }
@@ -3521,7 +3513,6 @@ Dictionary EditorNode::_get_main_scene_state() {
 	state["main_tab"] = _get_current_main_editor();
 	state["main_tab"] = _get_current_main_editor();
 	state["scene_tree_offset"] = SceneTreeDock::get_singleton()->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->get_value();
 	state["scene_tree_offset"] = SceneTreeDock::get_singleton()->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->get_value();
 	state["property_edit_offset"] = InspectorDock::get_inspector_singleton()->get_scroll_offset();
 	state["property_edit_offset"] = InspectorDock::get_inspector_singleton()->get_scroll_offset();
-	state["saved_version"] = saved_version;
 	state["node_filter"] = SceneTreeDock::get_singleton()->get_filter();
 	state["node_filter"] = SceneTreeDock::get_singleton()->get_filter();
 	return state;
 	return state;
 }
 }
@@ -3581,11 +3572,6 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) {
 	editor_data.notify_edited_scene_changed();
 	editor_data.notify_edited_scene_changed();
 }
 }
 
 
-void EditorNode::set_current_version(uint64_t p_version) {
-	saved_version = p_version;
-	editor_data.set_edited_scene_version(p_version);
-}
-
 bool EditorNode::is_changing_scene() const {
 bool EditorNode::is_changing_scene() const {
 	return changing_scene;
 	return changing_scene;
 }
 }
@@ -3605,7 +3591,7 @@ void EditorNode::set_current_scene(int p_idx) {
 			editor_folding.load_scene_folding(editor_data.get_edited_scene_root(p_idx), editor_data.get_scene_path(p_idx));
 			editor_folding.load_scene_folding(editor_data.get_edited_scene_root(p_idx), editor_data.get_scene_path(p_idx));
 		}
 		}
 
 
-		call_deferred(SNAME("_clear_undo_history"));
+		get_undo_redo()->clear_history(false, editor_data.get_scene_history_id(p_idx));
 	}
 	}
 
 
 	changing_scene = true;
 	changing_scene = true;
@@ -3622,8 +3608,8 @@ void EditorNode::set_current_scene(int p_idx) {
 
 
 	Node *new_scene = editor_data.get_edited_scene_root();
 	Node *new_scene = editor_data.get_edited_scene_root();
 
 
-	if (Object::cast_to<Popup>(new_scene)) {
-		Object::cast_to<Popup>(new_scene)->show();
+	if (Popup *p = Object::cast_to<Popup>(new_scene)) {
+		p->show();
 	}
 	}
 
 
 	SceneTreeDock::get_singleton()->set_edited_scene(new_scene);
 	SceneTreeDock::get_singleton()->set_edited_scene(new_scene);
@@ -3641,6 +3627,7 @@ void EditorNode::set_current_scene(int p_idx) {
 	_edit_current(true);
 	_edit_current(true);
 
 
 	_update_title();
 	_update_title();
+	_update_scene_tabs();
 
 
 	call_deferred(SNAME("_set_main_scene_state"), state, get_edited_scene()); // Do after everything else is done setting up.
 	call_deferred(SNAME("_set_main_scene_state"), state, get_edited_scene()); // Do after everything else is done setting up.
 }
 }
@@ -3799,7 +3786,6 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
 	set_edited_scene(new_scene);
 	set_edited_scene(new_scene);
 	_get_scene_metadata(p_scene);
 	_get_scene_metadata(p_scene);
 
 
-	saved_version = editor_data.get_undo_redo().get_version();
 	_update_title();
 	_update_title();
 	_update_scene_tabs();
 	_update_scene_tabs();
 	_add_to_recent_scenes(lpath);
 	_add_to_recent_scenes(lpath);
@@ -3850,6 +3836,10 @@ void EditorNode::request_instantiate_scenes(const Vector<String> &p_files) {
 	SceneTreeDock::get_singleton()->instantiate_scenes(p_files);
 	SceneTreeDock::get_singleton()->instantiate_scenes(p_files);
 }
 }
 
 
+Ref<EditorUndoRedoManager> &EditorNode::get_undo_redo() {
+	return singleton->editor_data.get_undo_redo();
+}
+
 void EditorNode::_inherit_request(String p_file) {
 void EditorNode::_inherit_request(String p_file) {
 	current_menu_option = FILE_NEW_INHERITED_SCENE;
 	current_menu_option = FILE_NEW_INHERITED_SCENE;
 	_dialog_action(p_file);
 	_dialog_action(p_file);
@@ -4010,6 +4000,7 @@ void EditorNode::register_editor_types() {
 	GDREGISTER_CLASS(EditorSpinSlider);
 	GDREGISTER_CLASS(EditorSpinSlider);
 	GDREGISTER_CLASS(EditorResourcePicker);
 	GDREGISTER_CLASS(EditorResourcePicker);
 	GDREGISTER_CLASS(EditorScriptPicker);
 	GDREGISTER_CLASS(EditorScriptPicker);
+	GDREGISTER_ABSTRACT_CLASS(EditorUndoRedoManager);
 
 
 	GDREGISTER_ABSTRACT_CLASS(FileSystemDock);
 	GDREGISTER_ABSTRACT_CLASS(FileSystemDock);
 	GDREGISTER_VIRTUAL_CLASS(EditorFileSystemImportFormatSupportQuery);
 	GDREGISTER_VIRTUAL_CLASS(EditorFileSystemImportFormatSupportQuery);
@@ -5151,9 +5142,7 @@ void EditorNode::_scene_tab_closed(int p_tab, int option) {
 		return;
 		return;
 	}
 	}
 
 
-	bool unsaved = (p_tab == editor_data.get_edited_scene())
-			? saved_version != editor_data.get_undo_redo().get_version()
-			: editor_data.get_scene_version(p_tab) != 0;
+	bool unsaved = get_undo_redo()->is_history_unsaved(editor_data.get_scene_history_id(p_tab));
 	if (unsaved) {
 	if (unsaved) {
 		save_confirmation->set_ok_button_text(TTR("Save & Close"));
 		save_confirmation->set_ok_button_text(TTR("Save & Close"));
 		save_confirmation->set_text(vformat(TTR("Save changes to '%s' before closing?"), !scene->get_scene_file_path().is_empty() ? scene->get_scene_file_path() : "unsaved scene"));
 		save_confirmation->set_text(vformat(TTR("Save changes to '%s' before closing?"), !scene->get_scene_file_path().is_empty() ? scene->get_scene_file_path() : "unsaved scene"));
@@ -5265,23 +5254,10 @@ void EditorNode::_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_p
 void EditorNode::_scene_tab_changed(int p_tab) {
 void EditorNode::_scene_tab_changed(int p_tab) {
 	tab_preview_panel->hide();
 	tab_preview_panel->hide();
 
 
-	bool unsaved = (saved_version != editor_data.get_undo_redo().get_version());
-
 	if (p_tab == editor_data.get_edited_scene()) {
 	if (p_tab == editor_data.get_edited_scene()) {
 		return; // Pointless.
 		return; // Pointless.
 	}
 	}
-
-	uint64_t next_scene_version = editor_data.get_scene_version(p_tab);
-
-	editor_data.get_undo_redo().create_action(TTR("Switch Scene Tab"));
-	editor_data.get_undo_redo().add_do_method(this, "set_current_version", unsaved ? saved_version : 0);
-	editor_data.get_undo_redo().add_do_method(this, "set_current_scene", p_tab);
-	editor_data.get_undo_redo().add_do_method(this, "set_current_version", next_scene_version == 0 ? editor_data.get_undo_redo().get_version() + 1 : next_scene_version);
-
-	editor_data.get_undo_redo().add_undo_method(this, "set_current_version", next_scene_version);
-	editor_data.get_undo_redo().add_undo_method(this, "set_current_scene", editor_data.get_edited_scene());
-	editor_data.get_undo_redo().add_undo_method(this, "set_current_version", saved_version);
-	editor_data.get_undo_redo().commit_action();
+	set_current_scene(p_tab);
 }
 }
 
 
 Button *EditorNode::add_bottom_panel_item(String p_text, Control *p_item) {
 Button *EditorNode::add_bottom_panel_item(String p_text, Control *p_item) {
@@ -5667,7 +5643,7 @@ void EditorNode::reload_scene(const String &p_path) {
 	if (scene_idx == -1) {
 	if (scene_idx == -1) {
 		if (get_edited_scene()) {
 		if (get_edited_scene()) {
 			// Scene is not open, so at it might be instantiated. We'll refresh the whole scene later.
 			// Scene is not open, so at it might be instantiated. We'll refresh the whole scene later.
-			editor_data.get_undo_redo().clear_history();
+			editor_data.get_undo_redo()->clear_history(false, editor_data.get_current_edited_scene_history_id());
 		}
 		}
 		return;
 		return;
 	}
 	}
@@ -5683,7 +5659,7 @@ void EditorNode::reload_scene(const String &p_path) {
 
 
 	// Adjust index so tab is back a the previous position.
 	// Adjust index so tab is back a the previous position.
 	editor_data.move_edited_scene_to_index(scene_idx);
 	editor_data.move_edited_scene_to_index(scene_idx);
-	get_undo_redo()->clear_history();
+	get_undo_redo()->clear_history(false, editor_data.get_scene_history_id(scene_idx));
 
 
 	// Recover the tab.
 	// Recover the tab.
 	scene_tabs->set_current_tab(current_tab);
 	scene_tabs->set_current_tab(current_tab);
@@ -5867,7 +5843,6 @@ void EditorNode::_bind_methods() {
 	ClassDB::bind_method("stop_child_process", &EditorNode::stop_child_process);
 	ClassDB::bind_method("stop_child_process", &EditorNode::stop_child_process);
 
 
 	ClassDB::bind_method("set_current_scene", &EditorNode::set_current_scene);
 	ClassDB::bind_method("set_current_scene", &EditorNode::set_current_scene);
-	ClassDB::bind_method("set_current_version", &EditorNode::set_current_version);
 	ClassDB::bind_method("_thumbnail_done", &EditorNode::_thumbnail_done);
 	ClassDB::bind_method("_thumbnail_done", &EditorNode::_thumbnail_done);
 	ClassDB::bind_method("_set_main_scene_state", &EditorNode::_set_main_scene_state);
 	ClassDB::bind_method("_set_main_scene_state", &EditorNode::_set_main_scene_state);
 	ClassDB::bind_method("_update_recent_scenes", &EditorNode::_update_recent_scenes);
 	ClassDB::bind_method("_update_recent_scenes", &EditorNode::_update_recent_scenes);

+ 2 - 5
editor/editor_node.h

@@ -72,6 +72,7 @@ class EditorRun;
 class EditorRunNative;
 class EditorRunNative;
 class EditorSettingsDialog;
 class EditorSettingsDialog;
 class EditorToaster;
 class EditorToaster;
+class EditorUndoRedoManager;
 class ExportTemplateManager;
 class ExportTemplateManager;
 class FileDialog;
 class FileDialog;
 class FileSystemDock;
 class FileSystemDock;
@@ -471,9 +472,6 @@ private:
 	String open_navigate;
 	String open_navigate;
 	String run_custom_filename;
 	String run_custom_filename;
 
 
-	uint64_t saved_version = 1;
-	uint64_t last_checked_version = 0;
-
 	DynamicFontImportSettings *fontdata_import_settings = nullptr;
 	DynamicFontImportSettings *fontdata_import_settings = nullptr;
 	SceneImportSettings *scene_import_settings = nullptr;
 	SceneImportSettings *scene_import_settings = nullptr;
 	AudioStreamImportSettings *audio_stream_import_settings = nullptr;
 	AudioStreamImportSettings *audio_stream_import_settings = nullptr;
@@ -709,7 +707,7 @@ public:
 	static EditorLog *get_log() { return singleton->log; }
 	static EditorLog *get_log() { return singleton->log; }
 	static EditorData &get_editor_data() { return singleton->editor_data; }
 	static EditorData &get_editor_data() { return singleton->editor_data; }
 	static EditorFolding &get_editor_folding() { return singleton->editor_folding; }
 	static EditorFolding &get_editor_folding() { return singleton->editor_folding; }
-	static UndoRedo *get_undo_redo() { return &singleton->editor_data.get_undo_redo(); }
+	static Ref<EditorUndoRedoManager> &get_undo_redo();
 
 
 	static HBoxContainer *get_menu_hb() { return singleton->menu_hb; }
 	static HBoxContainer *get_menu_hb() { return singleton->menu_hb; }
 	static VSplitContainer *get_top_split() { return singleton->top_split; }
 	static VSplitContainer *get_top_split() { return singleton->top_split; }
@@ -793,7 +791,6 @@ public:
 
 
 	bool is_scene_open(const String &p_path);
 	bool is_scene_open(const String &p_path);
 
 
-	void set_current_version(uint64_t p_version);
 	void set_current_scene(int p_idx);
 	void set_current_scene(int p_idx);
 
 
 	void setup_color_picker(ColorPicker *picker);
 	void setup_color_picker(ColorPicker *picker);

+ 6 - 1
editor/editor_plugin.cpp

@@ -35,6 +35,7 @@
 #include "editor/editor_paths.h"
 #include "editor/editor_paths.h"
 #include "editor/editor_resource_preview.h"
 #include "editor/editor_resource_preview.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "editor/export/editor_export.h"
 #include "editor/export/editor_export.h"
 #include "editor/filesystem_dock.h"
 #include "editor/filesystem_dock.h"
 #include "editor/plugins/canvas_item_editor_plugin.h"
 #include "editor/plugins/canvas_item_editor_plugin.h"
@@ -893,7 +894,7 @@ void EditorPlugin::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("make_bottom_panel_item_visible", "item"), &EditorPlugin::make_bottom_panel_item_visible);
 	ClassDB::bind_method(D_METHOD("make_bottom_panel_item_visible", "item"), &EditorPlugin::make_bottom_panel_item_visible);
 	ClassDB::bind_method(D_METHOD("hide_bottom_panel"), &EditorPlugin::hide_bottom_panel);
 	ClassDB::bind_method(D_METHOD("hide_bottom_panel"), &EditorPlugin::hide_bottom_panel);
 
 
-	ClassDB::bind_method(D_METHOD("get_undo_redo"), &EditorPlugin::_get_undo_redo);
+	ClassDB::bind_method(D_METHOD("get_undo_redo"), &EditorPlugin::get_undo_redo);
 	ClassDB::bind_method(D_METHOD("add_undo_redo_inspector_hook_callback", "callable"), &EditorPlugin::add_undo_redo_inspector_hook_callback);
 	ClassDB::bind_method(D_METHOD("add_undo_redo_inspector_hook_callback", "callable"), &EditorPlugin::add_undo_redo_inspector_hook_callback);
 	ClassDB::bind_method(D_METHOD("remove_undo_redo_inspector_hook_callback", "callable"), &EditorPlugin::remove_undo_redo_inspector_hook_callback);
 	ClassDB::bind_method(D_METHOD("remove_undo_redo_inspector_hook_callback", "callable"), &EditorPlugin::remove_undo_redo_inspector_hook_callback);
 	ClassDB::bind_method(D_METHOD("queue_save_layout"), &EditorPlugin::queue_save_layout);
 	ClassDB::bind_method(D_METHOD("queue_save_layout"), &EditorPlugin::queue_save_layout);
@@ -973,6 +974,10 @@ void EditorPlugin::_bind_methods() {
 	BIND_ENUM_CONSTANT(DOCK_SLOT_MAX);
 	BIND_ENUM_CONSTANT(DOCK_SLOT_MAX);
 }
 }
 
 
+Ref<EditorUndoRedoManager> EditorPlugin::get_undo_redo() {
+	return undo_redo;
+}
+
 EditorPluginCreateFunc EditorPlugins::creation_funcs[MAX_CREATE_FUNCS];
 EditorPluginCreateFunc EditorPlugins::creation_funcs[MAX_CREATE_FUNCS];
 
 
 int EditorPlugins::creation_func_count = 0;
 int EditorPlugins::creation_func_count = 0;

+ 3 - 4
editor/editor_plugin.h

@@ -53,6 +53,7 @@ class EditorImportPlugin;
 class EditorExportPlugin;
 class EditorExportPlugin;
 class EditorNode3DGizmoPlugin;
 class EditorNode3DGizmoPlugin;
 class EditorResourcePreview;
 class EditorResourcePreview;
+class EditorUndoRedoManager;
 class EditorFileSystem;
 class EditorFileSystem;
 class EditorToolAddons;
 class EditorToolAddons;
 class EditorPaths;
 class EditorPaths;
@@ -130,9 +131,7 @@ public:
 class EditorPlugin : public Node {
 class EditorPlugin : public Node {
 	GDCLASS(EditorPlugin, Node);
 	GDCLASS(EditorPlugin, Node);
 	friend class EditorData;
 	friend class EditorData;
-	UndoRedo *undo_redo = nullptr;
-
-	UndoRedo *_get_undo_redo() { return undo_redo; }
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	bool input_event_forwarding_always_enabled = false;
 	bool input_event_forwarding_always_enabled = false;
 	bool force_draw_over_forwarding_enabled = false;
 	bool force_draw_over_forwarding_enabled = false;
@@ -145,7 +144,7 @@ protected:
 	void _notification(int p_what);
 	void _notification(int p_what);
 
 
 	static void _bind_methods();
 	static void _bind_methods();
-	UndoRedo &get_undo_redo() { return *undo_redo; }
+	Ref<EditorUndoRedoManager> get_undo_redo();
 
 
 	void add_custom_type(const String &p_type, const String &p_base, const Ref<Script> &p_script, const Ref<Texture2D> &p_icon);
 	void add_custom_type(const String &p_type, const String &p_base, const Ref<Script> &p_script, const Ref<Texture2D> &p_icon);
 	void remove_custom_type(const String &p_type);
 	void remove_custom_type(const String &p_type);

+ 5 - 5
editor/editor_settings_dialog.cpp

@@ -40,6 +40,7 @@
 #include "editor/editor_property_name_processor.h"
 #include "editor/editor_property_name_processor.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "scene/gui/margin_container.h"
 #include "scene/gui/margin_container.h"
 
 
 void EditorSettingsDialog::ok_pressed() {
 void EditorSettingsDialog::ok_pressed() {
@@ -124,9 +125,9 @@ void EditorSettingsDialog::_notification(int p_what) {
 		} break;
 		} break;
 
 
 		case NOTIFICATION_READY: {
 		case NOTIFICATION_READY: {
-			undo_redo->set_method_notify_callback(EditorDebuggerNode::_method_changeds, nullptr);
-			undo_redo->set_property_notify_callback(EditorDebuggerNode::_property_changeds, nullptr);
-			undo_redo->set_commit_notify_callback(_undo_redo_callback, this);
+			undo_redo->get_or_create_history(EditorUndoRedoManager::GLOBAL_HISTORY).undo_redo->set_method_notify_callback(EditorDebuggerNode::_method_changeds, nullptr);
+			undo_redo->get_or_create_history(EditorUndoRedoManager::GLOBAL_HISTORY).undo_redo->set_property_notify_callback(EditorDebuggerNode::_property_changeds, nullptr);
+			undo_redo->get_or_create_history(EditorUndoRedoManager::GLOBAL_HISTORY).undo_redo->set_commit_notify_callback(_undo_redo_callback, this);
 		} break;
 		} break;
 
 
 		case NOTIFICATION_ENTER_TREE: {
 		case NOTIFICATION_ENTER_TREE: {
@@ -680,7 +681,7 @@ void EditorSettingsDialog::_bind_methods() {
 EditorSettingsDialog::EditorSettingsDialog() {
 EditorSettingsDialog::EditorSettingsDialog() {
 	set_title(TTR("Editor Settings"));
 	set_title(TTR("Editor Settings"));
 
 
-	undo_redo = memnew(UndoRedo);
+	undo_redo = EditorNode::get_undo_redo();
 
 
 	tabs = memnew(TabContainer);
 	tabs = memnew(TabContainer);
 	tabs->set_theme_type_variation("TabContainerOdd");
 	tabs->set_theme_type_variation("TabContainerOdd");
@@ -776,5 +777,4 @@ EditorSettingsDialog::EditorSettingsDialog() {
 }
 }
 
 
 EditorSettingsDialog::~EditorSettingsDialog() {
 EditorSettingsDialog::~EditorSettingsDialog() {
-	memdelete(undo_redo);
 }
 }

+ 3 - 1
editor/editor_settings_dialog.h

@@ -40,6 +40,8 @@
 #include "scene/gui/tab_container.h"
 #include "scene/gui/tab_container.h"
 #include "scene/gui/texture_rect.h"
 #include "scene/gui/texture_rect.h"
 
 
+class EditorUndoRedoManager;
+
 class EditorSettingsDialog : public AcceptDialog {
 class EditorSettingsDialog : public AcceptDialog {
 	GDCLASS(EditorSettingsDialog, AcceptDialog);
 	GDCLASS(EditorSettingsDialog, AcceptDialog);
 
 
@@ -73,7 +75,7 @@ class EditorSettingsDialog : public AcceptDialog {
 
 
 	Timer *timer = nullptr;
 	Timer *timer = nullptr;
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	virtual void cancel_pressed() override;
 	virtual void cancel_pressed() override;
 	virtual void ok_pressed() override;
 	virtual void ok_pressed() override;

+ 442 - 0
editor/editor_undo_redo_manager.cpp

@@ -0,0 +1,442 @@
+/*************************************************************************/
+/*  editor_undo_redo_manager.cpp                                         */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* 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.                */
+/*************************************************************************/
+
+#include "editor_undo_redo_manager.h"
+
+#include "core/io/resource.h"
+#include "core/os/os.h"
+#include "core/templates/local_vector.h"
+#include "editor/editor_log.h"
+#include "editor/editor_node.h"
+#include "scene/main/node.h"
+
+EditorUndoRedoManager::History &EditorUndoRedoManager::get_or_create_history(int p_idx) {
+	if (!history_map.has(p_idx)) {
+		History history;
+		history.undo_redo = memnew(UndoRedo);
+		history.id = p_idx;
+		history_map[p_idx] = history;
+
+		EditorNode::get_singleton()->get_log()->register_undo_redo(history.undo_redo);
+		EditorDebuggerNode::get_singleton()->register_undo_redo(history.undo_redo);
+	}
+	return history_map[p_idx];
+}
+
+UndoRedo *EditorUndoRedoManager::get_history_undo_redo(int p_idx) const {
+	ERR_FAIL_COND_V(!history_map.has(p_idx), nullptr);
+	return history_map[p_idx].undo_redo;
+}
+
+int EditorUndoRedoManager::get_history_id_for_object(Object *p_object) const {
+	int history_id = INVALID_HISTORY;
+
+	if (Node *node = Object::cast_to<Node>(p_object)) {
+		Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
+
+		if (edited_scene && (node == edited_scene || edited_scene->is_ancestor_of(node))) {
+			int idx = EditorNode::get_singleton()->get_editor_data().get_current_edited_scene_history_id();
+			if (idx > 0) {
+				history_id = idx;
+			}
+		}
+	}
+
+	if (Resource *res = Object::cast_to<Resource>(p_object)) {
+		if (res->is_built_in()) {
+			if (res->get_path().is_empty()) {
+				int idx = EditorNode::get_singleton()->get_editor_data().get_current_edited_scene_history_id();
+				if (idx > 0) {
+					history_id = idx;
+				}
+			} else {
+				int idx = EditorNode::get_singleton()->get_editor_data().get_scene_history_id_from_path(res->get_path().get_slice("::", 0));
+				if (idx > 0) {
+					history_id = idx;
+				}
+			}
+		}
+	}
+
+	if (history_id == INVALID_HISTORY) {
+		if (pending_action.history_id != INVALID_HISTORY) {
+			history_id = pending_action.history_id;
+		} else {
+			history_id = GLOBAL_HISTORY;
+		}
+	}
+	return history_id;
+}
+
+EditorUndoRedoManager::History &EditorUndoRedoManager::get_history_for_object(Object *p_object) {
+	int history_id = get_history_id_for_object(p_object);
+	ERR_FAIL_COND_V_MSG(pending_action.history_id != INVALID_HISTORY && history_id != pending_action.history_id, get_or_create_history(pending_action.history_id), vformat("UndoRedo history mismatch: expected %d, got %d.", pending_action.history_id, history_id));
+
+	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);
+	}
+
+	return history;
+}
+
+void EditorUndoRedoManager::create_action_for_history(const String &p_name, int p_history_id, UndoRedo::MergeMode p_mode) {
+	pending_action.action_name = p_name;
+	pending_action.timestamp = OS::get_singleton()->get_unix_time();
+	pending_action.merge_mode = p_mode;
+
+	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);
+	}
+}
+
+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);
+
+	if (p_custom_context) {
+		// This assigns context to pending action.
+		get_history_for_object(p_custom_context);
+	}
+}
+
+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);
+}
+
+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);
+}
+
+void EditorUndoRedoManager::_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 EditorUndoRedoManager::_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 EditorUndoRedoManager::add_do_property(Object *p_object, const StringName &p_property, const Variant &p_value) {
+	UndoRedo *undo_redo = get_history_for_object(p_object).undo_redo;
+	undo_redo->add_do_property(p_object, p_property, p_value);
+}
+
+void EditorUndoRedoManager::add_undo_property(Object *p_object, const StringName &p_property, const Variant &p_value) {
+	UndoRedo *undo_redo = get_history_for_object(p_object).undo_redo;
+	undo_redo->add_undo_property(p_object, p_property, p_value);
+}
+
+void EditorUndoRedoManager::add_do_reference(Object *p_object) {
+	UndoRedo *undo_redo = get_history_for_object(p_object).undo_redo;
+	undo_redo->add_do_reference(p_object);
+}
+
+void EditorUndoRedoManager::add_undo_reference(Object *p_object) {
+	UndoRedo *undo_redo = get_history_for_object(p_object).undo_redo;
+	undo_redo->add_undo_reference(p_object);
+}
+
+void EditorUndoRedoManager::commit_action(bool p_execute) {
+	ERR_FAIL_COND(pending_action.history_id == INVALID_HISTORY);
+	is_committing = true;
+
+	History &history = get_or_create_history(pending_action.history_id);
+	history.undo_redo->commit_action(p_execute);
+	history.redo_stack.clear();
+
+	if (!history.undo_stack.is_empty()) {
+		const Action &prev_action = history.undo_stack.back()->get();
+		if (pending_action.merge_mode != UndoRedo::MERGE_DISABLE && pending_action.merge_mode == prev_action.merge_mode && pending_action.action_name == prev_action.action_name) {
+			// Discard action if it should be merged (UndoRedo handles merging internally).
+			pending_action = Action();
+			is_committing = false;
+			return;
+		}
+	}
+
+	history.undo_stack.push_back(pending_action);
+	pending_action = Action();
+	is_committing = false;
+}
+
+bool EditorUndoRedoManager::is_committing_action() const {
+	return is_committing;
+}
+
+bool EditorUndoRedoManager::undo() {
+	if (!has_undo()) {
+		return false;
+	}
+
+	History *selected_history = nullptr;
+	double global_timestamp = 0;
+
+	// Pick the history with greatest last action timestamp (either global or current scene).
+	{
+		History &history = get_or_create_history(GLOBAL_HISTORY);
+		if (!history.undo_stack.is_empty()) {
+			selected_history = &history;
+			global_timestamp = history.undo_stack.back()->get().timestamp;
+		}
+	}
+
+	{
+		History &history = get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id());
+		if (!history.undo_stack.is_empty() && history.undo_stack.back()->get().timestamp > global_timestamp) {
+			selected_history = &history;
+		}
+	}
+
+	if (selected_history) {
+		Action action = selected_history->undo_stack.back()->get();
+		selected_history->undo_stack.pop_back();
+		selected_history->redo_stack.push_back(action);
+		return selected_history->undo_redo->undo();
+	}
+	return false;
+}
+
+bool EditorUndoRedoManager::redo() {
+	if (!has_redo()) {
+		return false;
+	}
+
+	History *selected_history = nullptr;
+	double global_timestamp = INFINITY;
+
+	// Pick the history with lowest last action timestamp (either global or current scene).
+	{
+		History &history = get_or_create_history(GLOBAL_HISTORY);
+		if (!history.redo_stack.is_empty()) {
+			selected_history = &history;
+			global_timestamp = history.redo_stack.back()->get().timestamp;
+		}
+	}
+
+	{
+		History &history = get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id());
+		if (!history.redo_stack.is_empty() && history.redo_stack.back()->get().timestamp < global_timestamp) {
+			selected_history = &history;
+		}
+	}
+
+	if (selected_history) {
+		Action action = selected_history->redo_stack.back()->get();
+		selected_history->redo_stack.pop_back();
+		selected_history->undo_stack.push_back(action);
+		return selected_history->undo_redo->redo();
+	}
+	return false;
+}
+
+void EditorUndoRedoManager::set_history_as_saved(int p_id) {
+	History &history = get_or_create_history(p_id);
+	history.saved_version = history.undo_redo->get_version();
+}
+
+void EditorUndoRedoManager::set_history_as_unsaved(int p_id) {
+	History &history = get_or_create_history(p_id);
+	history.saved_version = -1;
+}
+
+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;
+}
+
+bool EditorUndoRedoManager::has_undo() {
+	for (const KeyValue<int, History> &E : history_map) {
+		if ((E.key == GLOBAL_HISTORY || E.key == EditorNode::get_editor_data().get_current_edited_scene_history_id()) && !E.value.undo_stack.is_empty()) {
+			return true;
+		}
+	}
+	return false;
+}
+
+bool EditorUndoRedoManager::has_redo() {
+	for (const KeyValue<int, History> &E : history_map) {
+		if ((E.key == GLOBAL_HISTORY || E.key == EditorNode::get_editor_data().get_current_edited_scene_history_id()) && !E.value.redo_stack.is_empty()) {
+			return true;
+		}
+	}
+	return false;
+}
+
+void EditorUndoRedoManager::clear_history(bool p_increase_version, int p_idx) {
+	if (p_idx != INVALID_HISTORY) {
+		get_or_create_history(p_idx).undo_redo->clear_history(p_increase_version);
+		if (!p_increase_version) {
+			set_history_as_saved(p_idx);
+		}
+		return;
+	}
+
+	for (const KeyValue<int, History> &E : history_map) {
+		E.value.undo_redo->clear_history(p_increase_version);
+		set_history_as_saved(E.key);
+	}
+}
+
+String EditorUndoRedoManager::get_current_action_name() {
+	if (has_undo()) {
+		History *selected_history = nullptr;
+		double global_timestamp = 0;
+
+		// Pick the history with greatest last action timestamp (either global or current scene).
+		{
+			History &history = get_or_create_history(GLOBAL_HISTORY);
+			if (!history.undo_stack.is_empty()) {
+				selected_history = &history;
+				global_timestamp = history.undo_stack.back()->get().timestamp;
+			}
+		}
+
+		{
+			History &history = get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id());
+			if (!history.undo_stack.is_empty() && history.undo_stack.back()->get().timestamp > global_timestamp) {
+				selected_history = &history;
+			}
+		}
+
+		if (selected_history) {
+			return selected_history->undo_redo->get_current_action_name();
+		}
+	}
+	return "";
+}
+
+void EditorUndoRedoManager::discard_history(int p_idx, bool p_erase_from_map) {
+	ERR_FAIL_COND(!history_map.has(p_idx));
+	History &history = history_map[p_idx];
+
+	if (history.undo_redo) {
+		memdelete(history.undo_redo);
+		history.undo_redo = nullptr;
+	}
+
+	if (p_erase_from_map) {
+		history_map.erase(p_idx);
+	}
+}
+
+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("commit_action", "execute"), &EditorUndoRedoManager::commit_action, DEFVAL(true));
+	ClassDB::bind_method(D_METHOD("is_committing_action"), &EditorUndoRedoManager::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", &EditorUndoRedoManager::_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", &EditorUndoRedoManager::_add_undo_method, mi, varray(), false);
+	}
+
+	ClassDB::bind_method(D_METHOD("add_do_property", "object", "property", "value"), &EditorUndoRedoManager::add_do_property);
+	ClassDB::bind_method(D_METHOD("add_undo_property", "object", "property", "value"), &EditorUndoRedoManager::add_undo_property);
+	ClassDB::bind_method(D_METHOD("add_do_reference", "object"), &EditorUndoRedoManager::add_do_reference);
+	ClassDB::bind_method(D_METHOD("add_undo_reference", "object"), &EditorUndoRedoManager::add_undo_reference);
+
+	ClassDB::bind_method(D_METHOD("get_object_history_id", "object"), &EditorUndoRedoManager::get_history_id_for_object);
+	ClassDB::bind_method(D_METHOD("get_history_undo_redo", "id"), &EditorUndoRedoManager::get_history_undo_redo);
+
+	BIND_ENUM_CONSTANT(GLOBAL_HISTORY);
+	BIND_ENUM_CONSTANT(INVALID_HISTORY);
+}
+
+EditorUndoRedoManager::~EditorUndoRedoManager() {
+	for (const KeyValue<int, History> &E : history_map) {
+		discard_history(E.key, false);
+	}
+}

+ 134 - 0
editor/editor_undo_redo_manager.h

@@ -0,0 +1,134 @@
+/*************************************************************************/
+/*  editor_undo_redo_manager.h                                           */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* 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 EDITOR_UNDO_REDO_MANAGER_H
+#define EDITOR_UNDO_REDO_MANAGER_H
+
+#include "core/object/class_db.h"
+#include "core/object/ref_counted.h"
+#include "core/object/undo_redo.h"
+
+class EditorUndoRedoManager : public RefCounted {
+	GDCLASS(EditorUndoRedoManager, RefCounted);
+
+public:
+	enum SpecialHistory {
+		GLOBAL_HISTORY = 0,
+		INVALID_HISTORY = -99,
+	};
+
+private:
+	struct Action {
+		int history_id = INVALID_HISTORY;
+		double timestamp = 0;
+		String action_name;
+		UndoRedo::MergeMode merge_mode = UndoRedo::MERGE_DISABLE;
+	};
+
+	struct History {
+		int id = INVALID_HISTORY;
+		UndoRedo *undo_redo = nullptr;
+		uint64_t saved_version = 1;
+		List<Action> undo_stack;
+		List<Action> redo_stack;
+	};
+
+	HashMap<int, History> history_map;
+	Action pending_action;
+
+	bool is_committing = false;
+
+protected:
+	static void _bind_methods();
+
+public:
+	History &get_or_create_history(int p_idx);
+	UndoRedo *get_history_undo_redo(int p_idx) const;
+	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 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 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);
+
+	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);
+	void add_undo_reference(Object *p_object);
+
+	void commit_action(bool p_execute = true);
+	bool is_committing_action() const;
+
+	bool undo();
+	bool redo();
+	void clear_history(bool p_increase_version = true, int p_idx = INVALID_HISTORY);
+
+	void set_history_as_saved(int p_idx);
+	void set_history_as_unsaved(int p_idx);
+	bool is_history_unsaved(int p_idx);
+	bool has_undo();
+	bool has_redo();
+
+	String get_current_action_name();
+
+	void discard_history(int p_idx, bool p_erase_from_map = true);
+	~EditorUndoRedoManager();
+};
+
+VARIANT_ENUM_CAST(EditorUndoRedoManager::SpecialHistory);
+
+#endif // EDITOR_UNDO_REDO_MANAGER_H

+ 9 - 0
editor/groups_editor.cpp

@@ -32,6 +32,7 @@
 
 
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "editor/scene_tree_dock.h"
 #include "editor/scene_tree_dock.h"
 #include "editor/scene_tree_editor.h"
 #include "editor/scene_tree_editor.h"
 #include "scene/gui/box_container.h"
 #include "scene/gui/box_container.h"
@@ -397,6 +398,10 @@ void GroupDialog::_notification(int p_what) {
 	}
 	}
 }
 }
 
 
+void GroupDialog::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) {
+	undo_redo = p_undo_redo;
+}
+
 void GroupDialog::edit() {
 void GroupDialog::edit() {
 	popup_centered();
 	popup_centered();
 
 
@@ -696,6 +701,10 @@ void GroupsEditor::update_tree() {
 	}
 	}
 }
 }
 
 
+void GroupsEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) {
+	undo_redo = p_undo_redo;
+}
+
 void GroupsEditor::set_current(Node *p_node) {
 void GroupsEditor::set_current(Node *p_node) {
 	node = p_node;
 	node = p_node;
 	update_tree();
 	update_tree();

+ 6 - 5
editor/groups_editor.h

@@ -31,7 +31,6 @@
 #ifndef GROUPS_EDITOR_H
 #ifndef GROUPS_EDITOR_H
 #define GROUPS_EDITOR_H
 #define GROUPS_EDITOR_H
 
 
-#include "core/object/undo_redo.h"
 #include "editor/scene_tree_editor.h"
 #include "editor/scene_tree_editor.h"
 #include "scene/gui/button.h"
 #include "scene/gui/button.h"
 #include "scene/gui/dialogs.h"
 #include "scene/gui/dialogs.h"
@@ -40,6 +39,8 @@
 #include "scene/gui/popup.h"
 #include "scene/gui/popup.h"
 #include "scene/gui/tree.h"
 #include "scene/gui/tree.h"
 
 
+class EditorUndoRedoManager;
+
 class GroupDialog : public AcceptDialog {
 class GroupDialog : public AcceptDialog {
 	GDCLASS(GroupDialog, AcceptDialog);
 	GDCLASS(GroupDialog, AcceptDialog);
 
 
@@ -68,7 +69,7 @@ class GroupDialog : public AcceptDialog {
 
 
 	String selected_group;
 	String selected_group;
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	void _group_selected();
 	void _group_selected();
 
 
@@ -103,7 +104,7 @@ public:
 	};
 	};
 
 
 	void edit();
 	void edit();
-	void set_undo_redo(UndoRedo *p_undoredo) { undo_redo = p_undoredo; }
+	void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo);
 
 
 	GroupDialog();
 	GroupDialog();
 };
 };
@@ -119,7 +120,7 @@ class GroupsEditor : public VBoxContainer {
 	Button *add = nullptr;
 	Button *add = nullptr;
 	Tree *tree = nullptr;
 	Tree *tree = nullptr;
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	void update_tree();
 	void update_tree();
 	void _add_group(const String &p_group = "");
 	void _add_group(const String &p_group = "");
@@ -137,7 +138,7 @@ public:
 		COPY_GROUP,
 		COPY_GROUP,
 	};
 	};
 
 
-	void set_undo_redo(UndoRedo *p_undoredo) { undo_redo = p_undoredo; }
+	void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo);
 	void set_current(Node *p_node);
 	void set_current(Node *p_node);
 
 
 	GroupsEditor();
 	GroupsEditor();

+ 3 - 2
editor/inspector_dock.cpp

@@ -178,7 +178,8 @@ void InspectorDock::_menu_option_confirm(int p_option, bool p_confirmed) {
 					}
 					}
 				}
 				}
 
 
-				editor_data->get_undo_redo().clear_history();
+				int history_id = editor_data->get_undo_redo()->get_history_for_object(current).id;
+				editor_data->get_undo_redo()->clear_history(true, history_id);
 
 
 				EditorNode::get_singleton()->get_editor_plugins_over()->edit(nullptr);
 				EditorNode::get_singleton()->get_editor_plugins_over()->edit(nullptr);
 				EditorNode::get_singleton()->get_editor_plugins_over()->edit(current);
 				EditorNode::get_singleton()->get_editor_plugins_over()->edit(current);
@@ -755,7 +756,7 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) {
 	inspector->set_property_name_style(EditorPropertyNameProcessor::get_default_inspector_style());
 	inspector->set_property_name_style(EditorPropertyNameProcessor::get_default_inspector_style());
 	inspector->set_use_folding(!bool(EDITOR_GET("interface/inspector/disable_folding")));
 	inspector->set_use_folding(!bool(EDITOR_GET("interface/inspector/disable_folding")));
 	inspector->register_text_enter(search);
 	inspector->register_text_enter(search);
-	inspector->set_undo_redo(&editor_data->get_undo_redo());
+	inspector->set_undo_redo(editor_data->get_undo_redo());
 
 
 	inspector->set_use_filter(true); // TODO: check me
 	inspector->set_use_filter(true); // TODO: check me
 
 

+ 1 - 0
editor/localization_editor.cpp

@@ -36,6 +36,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_translation_parser.h"
 #include "editor/editor_translation_parser.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "editor/filesystem_dock.h"
 #include "editor/filesystem_dock.h"
 #include "editor/pot_generator.h"
 #include "editor/pot_generator.h"
 #include "scene/gui/control.h"
 #include "scene/gui/control.h"

+ 1 - 1
editor/localization_editor.h

@@ -56,7 +56,7 @@ class LocalizationEditor : public VBoxContainer {
 	EditorFileDialog *pot_file_open_dialog = nullptr;
 	EditorFileDialog *pot_file_open_dialog = nullptr;
 	EditorFileDialog *pot_generate_dialog = nullptr;
 	EditorFileDialog *pot_generate_dialog = nullptr;
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 	bool updating_translations = false;
 	bool updating_translations = false;
 	String localization_changed;
 	String localization_changed;
 
 

+ 2 - 1
editor/multi_node_edit.cpp

@@ -32,6 +32,7 @@
 
 
 #include "core/math/math_fieldwise.h"
 #include "core/math/math_fieldwise.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
+#include "editor/editor_undo_redo_manager.h"
 
 
 bool MultiNodeEdit::_set(const StringName &p_name, const Variant &p_value) {
 bool MultiNodeEdit::_set(const StringName &p_name, const Variant &p_value) {
 	return _set_impl(p_name, p_value, "");
 	return _set_impl(p_name, p_value, "");
@@ -54,7 +55,7 @@ bool MultiNodeEdit::_set_impl(const StringName &p_name, const Variant &p_value,
 		node_path_target = es->get_node(p_value);
 		node_path_target = es->get_node(p_value);
 	}
 	}
 
 
-	UndoRedo *ur = EditorNode::get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 
 
 	ur->create_action(TTR("MultiNode Set") + " " + String(name), UndoRedo::MERGE_ENDS);
 	ur->create_action(TTR("MultiNode Set") + " " + String(name), UndoRedo::MERGE_ENDS);
 	for (const NodePath &E : nodes) {
 	for (const NodePath &E : nodes) {

+ 1 - 1
editor/node_dock.cpp

@@ -117,7 +117,7 @@ NodeDock::NodeDock() {
 	groups_button->connect("pressed", callable_mp(this, &NodeDock::show_groups));
 	groups_button->connect("pressed", callable_mp(this, &NodeDock::show_groups));
 
 
 	connections = memnew(ConnectionsDock);
 	connections = memnew(ConnectionsDock);
-	connections->set_undoredo(EditorNode::get_undo_redo());
+	connections->set_undo_redo(EditorNode::get_undo_redo());
 	add_child(connections);
 	add_child(connections);
 	connections->set_v_size_flags(SIZE_EXPAND_FILL);
 	connections->set_v_size_flags(SIZE_EXPAND_FILL);
 	connections->hide();
 	connections->hide();

+ 1 - 0
editor/plugins/abstract_polygon_2d_editor.cpp

@@ -36,6 +36,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "scene/gui/separator.h"
 #include "scene/gui/separator.h"
 
 
 bool AbstractPolygon2DEditor::Vertex::operator==(const AbstractPolygon2DEditor::Vertex &p_vertex) const {
 bool AbstractPolygon2DEditor::Vertex::operator==(const AbstractPolygon2DEditor::Vertex &p_vertex) const {

+ 2 - 1
editor/plugins/abstract_polygon_2d_editor.h

@@ -36,6 +36,7 @@
 #include "scene/gui/box_container.h"
 #include "scene/gui/box_container.h"
 
 
 class CanvasItemEditor;
 class CanvasItemEditor;
+class EditorUndoRedoManager;
 
 
 class AbstractPolygon2DEditor : public HBoxContainer {
 class AbstractPolygon2DEditor : public HBoxContainer {
 	GDCLASS(AbstractPolygon2DEditor, HBoxContainer);
 	GDCLASS(AbstractPolygon2DEditor, HBoxContainer);
@@ -99,7 +100,7 @@ protected:
 
 
 	int mode = MODE_EDIT;
 	int mode = MODE_EDIT;
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	virtual void _menu_option(int p_option);
 	virtual void _menu_option(int p_option);
 	void _wip_changed();
 	void _wip_changed();

+ 1 - 0
editor/plugins/animation_blend_space_1d_editor.cpp

@@ -35,6 +35,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "scene/animation/animation_blend_tree.h"
 #include "scene/animation/animation_blend_tree.h"
 
 
 StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const {
 StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const {

+ 3 - 1
editor/plugins/animation_blend_space_1d_editor.h

@@ -40,6 +40,8 @@
 #include "scene/gui/separator.h"
 #include "scene/gui/separator.h"
 #include "scene/gui/tree.h"
 #include "scene/gui/tree.h"
 
 
+class EditorUndoRedoManager;
+
 class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin {
 class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin {
 	GDCLASS(AnimationNodeBlendSpace1DEditor, AnimationTreeNodeEditorPlugin);
 	GDCLASS(AnimationNodeBlendSpace1DEditor, AnimationTreeNodeEditorPlugin);
 
 
@@ -76,7 +78,7 @@ class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin {
 
 
 	bool updating = false;
 	bool updating = false;
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	static AnimationNodeBlendSpace1DEditor *singleton;
 	static AnimationNodeBlendSpace1DEditor *singleton;
 
 

+ 1 - 0
editor/plugins/animation_blend_space_2d_editor.cpp

@@ -39,6 +39,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "scene/animation/animation_blend_tree.h"
 #include "scene/animation/animation_blend_tree.h"
 #include "scene/animation/animation_player.h"
 #include "scene/animation/animation_player.h"
 #include "scene/gui/menu_button.h"
 #include "scene/gui/menu_button.h"

+ 3 - 1
editor/plugins/animation_blend_space_2d_editor.h

@@ -40,6 +40,8 @@
 #include "scene/gui/separator.h"
 #include "scene/gui/separator.h"
 #include "scene/gui/tree.h"
 #include "scene/gui/tree.h"
 
 
+class EditorUndoRedoManager;
+
 class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin {
 class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin {
 	GDCLASS(AnimationNodeBlendSpace2DEditor, AnimationTreeNodeEditorPlugin);
 	GDCLASS(AnimationNodeBlendSpace2DEditor, AnimationTreeNodeEditorPlugin);
 
 
@@ -82,7 +84,7 @@ class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin {
 
 
 	bool updating;
 	bool updating;
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	static AnimationNodeBlendSpace2DEditor *singleton;
 	static AnimationNodeBlendSpace2DEditor *singleton;
 
 

+ 1 - 0
editor/plugins/animation_blend_tree_editor_plugin.cpp

@@ -39,6 +39,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "scene/animation/animation_player.h"
 #include "scene/animation/animation_player.h"
 #include "scene/gui/menu_button.h"
 #include "scene/gui/menu_button.h"
 #include "scene/gui/panel.h"
 #include "scene/gui/panel.h"

+ 2 - 1
editor/plugins/animation_blend_tree_editor_plugin.h

@@ -41,6 +41,7 @@
 
 
 class ProgressBar;
 class ProgressBar;
 class EditorFileDialog;
 class EditorFileDialog;
+class EditorUndoRedoManager;
 
 
 class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin {
 class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin {
 	GDCLASS(AnimationNodeBlendTreeEditor, AnimationTreeNodeEditorPlugin);
 	GDCLASS(AnimationNodeBlendTreeEditor, AnimationTreeNodeEditorPlugin);
@@ -54,7 +55,7 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin {
 	PanelContainer *error_panel = nullptr;
 	PanelContainer *error_panel = nullptr;
 	Label *error_label = nullptr;
 	Label *error_label = nullptr;
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	AcceptDialog *filter_dialog = nullptr;
 	AcceptDialog *filter_dialog = nullptr;
 	Tree *filters = nullptr;
 	Tree *filters = nullptr;

+ 13 - 12
editor/plugins/animation_library_editor.cpp

@@ -32,6 +32,7 @@
 #include "editor/editor_file_dialog.h"
 #include "editor/editor_file_dialog.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
+#include "editor/editor_undo_redo_manager.h"
 
 
 void AnimationLibraryEditor::set_animation_player(Object *p_player) {
 void AnimationLibraryEditor::set_animation_player(Object *p_player) {
 	player = p_player;
 	player = p_player;
@@ -92,7 +93,7 @@ void AnimationLibraryEditor::_add_library_validate(const String &p_name) {
 void AnimationLibraryEditor::_add_library_confirm() {
 void AnimationLibraryEditor::_add_library_confirm() {
 	if (adding_animation) {
 	if (adding_animation) {
 		String anim_name = add_library_name->get_text();
 		String anim_name = add_library_name->get_text();
-		UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+		Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo();
 
 
 		Ref<AnimationLibrary> al = player->call("get_animation_library", adding_animation_to_library);
 		Ref<AnimationLibrary> al = player->call("get_animation_library", adding_animation_to_library);
 		ERR_FAIL_COND(!al.is_valid());
 		ERR_FAIL_COND(!al.is_valid());
@@ -109,7 +110,7 @@ void AnimationLibraryEditor::_add_library_confirm() {
 
 
 	} else {
 	} else {
 		String lib_name = add_library_name->get_text();
 		String lib_name = add_library_name->get_text();
-		UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+		Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo();
 
 
 		Ref<AnimationLibrary> al;
 		Ref<AnimationLibrary> al;
 		al.instantiate();
 		al.instantiate();
@@ -203,7 +204,7 @@ void AnimationLibraryEditor::_file_popup_selected(int p_id) {
 			// TODO: should probably make all foreign animations assigned to this library
 			// TODO: should probably make all foreign animations assigned to this library
 			// unique too.
 			// unique too.
 
 
-			UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+			Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo();
 			undo_redo->create_action(vformat(TTR("Make Animation Library Unique: %s"), lib_name));
 			undo_redo->create_action(vformat(TTR("Make Animation Library Unique: %s"), lib_name));
 			undo_redo->add_do_method(player, "remove_animation_library", lib_name);
 			undo_redo->add_do_method(player, "remove_animation_library", lib_name);
 			undo_redo->add_do_method(player, "add_animation_library", lib_name, ald);
 			undo_redo->add_do_method(player, "add_animation_library", lib_name, ald);
@@ -272,7 +273,7 @@ void AnimationLibraryEditor::_file_popup_selected(int p_id) {
 
 
 			Ref<Animation> animd = anim->duplicate();
 			Ref<Animation> animd = anim->duplicate();
 
 
-			UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+			Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo();
 			undo_redo->create_action(vformat(TTR("Make Animation Unique: %s"), anim_name));
 			undo_redo->create_action(vformat(TTR("Make Animation Unique: %s"), anim_name));
 			undo_redo->add_do_method(al.ptr(), "remove_animation", anim_name);
 			undo_redo->add_do_method(al.ptr(), "remove_animation", anim_name);
 			undo_redo->add_do_method(al.ptr(), "add_animation", anim_name, animd);
 			undo_redo->add_do_method(al.ptr(), "add_animation", anim_name, animd);
@@ -320,7 +321,7 @@ void AnimationLibraryEditor::_load_file(String p_path) {
 				name = p_path.get_file().get_basename() + " " + itos(attempt);
 				name = p_path.get_file().get_basename() + " " + itos(attempt);
 			}
 			}
 
 
-			UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+			Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo();
 
 
 			undo_redo->create_action(vformat(TTR("Add Animation Library: %s"), name));
 			undo_redo->create_action(vformat(TTR("Add Animation Library: %s"), name));
 			undo_redo->add_do_method(player, "add_animation_library", name, al);
 			undo_redo->add_do_method(player, "add_animation_library", name, al);
@@ -358,7 +359,7 @@ void AnimationLibraryEditor::_load_file(String p_path) {
 				name = p_path.get_file().get_basename() + " " + itos(attempt);
 				name = p_path.get_file().get_basename() + " " + itos(attempt);
 			}
 			}
 
 
-			UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+			Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo();
 
 
 			undo_redo->create_action(vformat(TTR("Load Animation into Library: %s"), name));
 			undo_redo->create_action(vformat(TTR("Load Animation into Library: %s"), name));
 			undo_redo->add_do_method(al.ptr(), "add_animation", name, anim);
 			undo_redo->add_do_method(al.ptr(), "add_animation", name, anim);
@@ -374,7 +375,7 @@ void AnimationLibraryEditor::_load_file(String p_path) {
 			EditorNode::get_singleton()->save_resource_in_path(al, p_path);
 			EditorNode::get_singleton()->save_resource_in_path(al, p_path);
 
 
 			if (al->get_path() != prev_path) { // Save successful.
 			if (al->get_path() != prev_path) { // Save successful.
-				UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+				Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo();
 
 
 				undo_redo->create_action(vformat(TTR("Save Animation library to File: %s"), file_dialog_library));
 				undo_redo->create_action(vformat(TTR("Save Animation library to File: %s"), file_dialog_library));
 				undo_redo->add_do_method(al.ptr(), "set_path", al->get_path());
 				undo_redo->add_do_method(al.ptr(), "set_path", al->get_path());
@@ -395,7 +396,7 @@ void AnimationLibraryEditor::_load_file(String p_path) {
 			String prev_path = anim->get_path();
 			String prev_path = anim->get_path();
 			EditorNode::get_singleton()->save_resource_in_path(anim, p_path);
 			EditorNode::get_singleton()->save_resource_in_path(anim, p_path);
 			if (anim->get_path() != prev_path) { // Save successful.
 			if (anim->get_path() != prev_path) { // Save successful.
-				UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+				Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo();
 
 
 				undo_redo->create_action(vformat(TTR("Save Animation to File: %s"), file_dialog_animation));
 				undo_redo->create_action(vformat(TTR("Save Animation to File: %s"), file_dialog_animation));
 				undo_redo->add_do_method(anim.ptr(), "set_path", anim->get_path());
 				undo_redo->add_do_method(anim.ptr(), "set_path", anim->get_path());
@@ -413,7 +414,7 @@ void AnimationLibraryEditor::_item_renamed() {
 	String text = ti->get_text(0);
 	String text = ti->get_text(0);
 	String old_text = ti->get_metadata(0);
 	String old_text = ti->get_metadata(0);
 	bool restore_text = false;
 	bool restore_text = false;
-	UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo();
 
 
 	if (String(text).contains("/") || String(text).contains(":") || String(text).contains(",") || String(text).contains("[")) {
 	if (String(text).contains("/") || String(text).contains(":") || String(text).contains(",") || String(text).contains("[")) {
 		restore_text = true;
 		restore_text = true;
@@ -527,7 +528,7 @@ void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int
 					name = base_name + " (" + itos(attempt) + ")";
 					name = base_name + " (" + itos(attempt) + ")";
 				}
 				}
 
 
-				UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+				Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo();
 
 
 				undo_redo->create_action(vformat(TTR("Add Animation to Library: %s"), name));
 				undo_redo->create_action(vformat(TTR("Add Animation to Library: %s"), name));
 				undo_redo->add_do_method(al.ptr(), "add_animation", name, anim);
 				undo_redo->add_do_method(al.ptr(), "add_animation", name, anim);
@@ -553,7 +554,7 @@ void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int
 				file_dialog_library = lib_name;
 				file_dialog_library = lib_name;
 			} break;
 			} break;
 			case LIB_BUTTON_DELETE: {
 			case LIB_BUTTON_DELETE: {
-				UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+				Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo();
 				undo_redo->create_action(vformat(TTR("Remove Animation Library: %s"), lib_name));
 				undo_redo->create_action(vformat(TTR("Remove Animation Library: %s"), lib_name));
 				undo_redo->add_do_method(player, "remove_animation_library", lib_name);
 				undo_redo->add_do_method(player, "remove_animation_library", lib_name);
 				undo_redo->add_undo_method(player, "add_animation_library", lib_name, al);
 				undo_redo->add_undo_method(player, "add_animation_library", lib_name, al);
@@ -594,7 +595,7 @@ void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int
 
 
 			} break;
 			} break;
 			case ANIM_BUTTON_DELETE: {
 			case ANIM_BUTTON_DELETE: {
-				UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+				Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo();
 				undo_redo->create_action(vformat(TTR("Remove Animation from Library: %s"), anim_name));
 				undo_redo->create_action(vformat(TTR("Remove Animation from Library: %s"), anim_name));
 				undo_redo->add_do_method(al.ptr(), "remove_animation", anim_name);
 				undo_redo->add_do_method(al.ptr(), "remove_animation", anim_name);
 				undo_redo->add_undo_method(al.ptr(), "add_animation", anim_name, anim);
 				undo_redo->add_undo_method(al.ptr(), "add_animation", anim_name, anim);

+ 5 - 1
editor/plugins/animation_player_editor_plugin.cpp

@@ -1045,6 +1045,10 @@ void AnimationPlayerEditor::_update_name_dialog_library_dropdown() {
 	}
 	}
 }
 }
 
 
+void AnimationPlayerEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) {
+	undo_redo = p_undo_redo;
+}
+
 void AnimationPlayerEditor::edit(AnimationPlayer *p_player) {
 void AnimationPlayerEditor::edit(AnimationPlayer *p_player) {
 	if (player && pin->is_pressed()) {
 	if (player && pin->is_pressed()) {
 		return; // Ignore, pinned.
 		return; // Ignore, pinned.
@@ -1925,7 +1929,7 @@ void AnimationPlayerEditorPlugin::_update_keying() {
 }
 }
 
 
 void AnimationPlayerEditorPlugin::edit(Object *p_object) {
 void AnimationPlayerEditorPlugin::edit(Object *p_object) {
-	anim_editor->set_undo_redo(&get_undo_redo());
+	anim_editor->set_undo_redo(get_undo_redo());
 	if (!p_object) {
 	if (!p_object) {
 		return;
 		return;
 	}
 	}

+ 3 - 2
editor/plugins/animation_player_editor_plugin.h

@@ -41,6 +41,7 @@
 #include "scene/gui/texture_button.h"
 #include "scene/gui/texture_button.h"
 #include "scene/gui/tree.h"
 #include "scene/gui/tree.h"
 
 
+class EditorUndoRedoManager;
 class AnimationPlayerEditorPlugin;
 class AnimationPlayerEditorPlugin;
 
 
 class AnimationPlayerEditor : public VBoxContainer {
 class AnimationPlayerEditor : public VBoxContainer {
@@ -100,7 +101,7 @@ class AnimationPlayerEditor : public VBoxContainer {
 	LineEdit *name = nullptr;
 	LineEdit *name = nullptr;
 	OptionButton *library = nullptr;
 	OptionButton *library = nullptr;
 	Label *name_title = nullptr;
 	Label *name_title = nullptr;
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	Ref<Texture2D> autoplay_icon;
 	Ref<Texture2D> autoplay_icon;
 	Ref<Texture2D> reset_icon;
 	Ref<Texture2D> reset_icon;
@@ -233,7 +234,7 @@ public:
 
 
 	void ensure_visibility();
 	void ensure_visibility();
 
 
-	void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; }
+	void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo);
 	void edit(AnimationPlayer *p_player);
 	void edit(AnimationPlayer *p_player);
 	void forward_force_draw_over_viewport(Control *p_overlay);
 	void forward_force_draw_over_viewport(Control *p_overlay);
 
 

+ 1 - 0
editor/plugins/animation_state_machine_editor.cpp

@@ -39,6 +39,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "scene/animation/animation_blend_tree.h"
 #include "scene/animation/animation_blend_tree.h"
 #include "scene/animation/animation_player.h"
 #include "scene/animation/animation_player.h"
 #include "scene/gui/menu_button.h"
 #include "scene/gui/menu_button.h"

+ 2 - 1
editor/plugins/animation_state_machine_editor.h

@@ -40,6 +40,7 @@
 #include "scene/gui/tree.h"
 #include "scene/gui/tree.h"
 
 
 class EditorFileDialog;
 class EditorFileDialog;
+class EditorUndoRedoManager;
 
 
 class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin {
 class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin {
 	GDCLASS(AnimationNodeStateMachineEditor, AnimationTreeNodeEditorPlugin);
 	GDCLASS(AnimationNodeStateMachineEditor, AnimationTreeNodeEditorPlugin);
@@ -76,7 +77,7 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin {
 
 
 	bool updating = false;
 	bool updating = false;
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	static AnimationNodeStateMachineEditor *singleton;
 	static AnimationNodeStateMachineEditor *singleton;
 
 

+ 3 - 2
editor/plugins/audio_stream_randomizer_editor_plugin.cpp

@@ -31,6 +31,7 @@
 #include "audio_stream_randomizer_editor_plugin.h"
 #include "audio_stream_randomizer_editor_plugin.h"
 
 
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
+#include "editor/editor_undo_redo_manager.h"
 
 
 void AudioStreamRandomizerEditorPlugin::edit(Object *p_object) {
 void AudioStreamRandomizerEditorPlugin::edit(Object *p_object) {
 }
 }
@@ -43,8 +44,8 @@ void AudioStreamRandomizerEditorPlugin::make_visible(bool p_visible) {
 }
 }
 
 
 void AudioStreamRandomizerEditorPlugin::_move_stream_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) {
 void AudioStreamRandomizerEditorPlugin::_move_stream_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) {
-	UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo);
-	ERR_FAIL_COND(!undo_redo);
+	Ref<EditorUndoRedoManager> undo_redo = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
+	ERR_FAIL_COND(undo_redo.is_null());
 
 
 	AudioStreamRandomizer *randomizer = Object::cast_to<AudioStreamRandomizer>(p_edited);
 	AudioStreamRandomizer *randomizer = Object::cast_to<AudioStreamRandomizer>(p_edited);
 	if (!randomizer) {
 	if (!randomizer) {

+ 30 - 25
editor/plugins/canvas_item_editor_plugin.cpp

@@ -40,6 +40,7 @@
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_toaster.h"
 #include "editor/editor_toaster.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "editor/plugins/animation_player_editor_plugin.h"
 #include "editor/plugins/animation_player_editor_plugin.h"
 #include "editor/plugins/script_editor_plugin.h"
 #include "editor/plugins/script_editor_plugin.h"
 #include "editor/scene_tree_dock.h"
 #include "editor/scene_tree_dock.h"
@@ -3988,6 +3989,10 @@ void CanvasItemEditor::_selection_changed() {
 	selected_from_canvas = false;
 	selected_from_canvas = false;
 }
 }
 
 
+void CanvasItemEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) {
+	undo_redo = p_undo_redo;
+}
+
 void CanvasItemEditor::edit(CanvasItem *p_canvas_item) {
 void CanvasItemEditor::edit(CanvasItem *p_canvas_item) {
 	Array selection = editor_selection->get_selected_nodes();
 	Array selection = editor_selection->get_selected_nodes();
 	if (selection.size() != 1 || Object::cast_to<Node>(selection[0]) != p_canvas_item) {
 	if (selection.size() != 1 || Object::cast_to<Node>(selection[0]) != p_canvas_item) {
@@ -5419,7 +5424,7 @@ CanvasItemEditor::CanvasItemEditor() {
 CanvasItemEditor *CanvasItemEditor::singleton = nullptr;
 CanvasItemEditor *CanvasItemEditor::singleton = nullptr;
 
 
 void CanvasItemEditorPlugin::edit(Object *p_object) {
 void CanvasItemEditorPlugin::edit(Object *p_object) {
-	canvas_item_editor->set_undo_redo(&get_undo_redo());
+	canvas_item_editor->set_undo_redo(EditorNode::get_undo_redo());
 	canvas_item_editor->edit(Object::cast_to<CanvasItem>(p_object));
 	canvas_item_editor->edit(Object::cast_to<CanvasItem>(p_object));
 }
 }
 
 
@@ -5572,34 +5577,34 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &
 	Ref<Texture2D> texture = ResourceCache::get_ref(path);
 	Ref<Texture2D> texture = ResourceCache::get_ref(path);
 
 
 	if (parent) {
 	if (parent) {
-		editor_data->get_undo_redo().add_do_method(parent, "add_child", child, true);
-		editor_data->get_undo_redo().add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
-		editor_data->get_undo_redo().add_do_reference(child);
-		editor_data->get_undo_redo().add_undo_method(parent, "remove_child", child);
+		editor_data->get_undo_redo()->add_do_method(parent, "add_child", child, true);
+		editor_data->get_undo_redo()->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
+		editor_data->get_undo_redo()->add_do_reference(child);
+		editor_data->get_undo_redo()->add_undo_method(parent, "remove_child", child);
 	} else { // If no parent is selected, set as root node of the scene.
 	} else { // If no parent is selected, set as root node of the scene.
-		editor_data->get_undo_redo().add_do_method(EditorNode::get_singleton(), "set_edited_scene", child);
-		editor_data->get_undo_redo().add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
-		editor_data->get_undo_redo().add_do_reference(child);
-		editor_data->get_undo_redo().add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
+		editor_data->get_undo_redo()->add_do_method(EditorNode::get_singleton(), "set_edited_scene", child);
+		editor_data->get_undo_redo()->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
+		editor_data->get_undo_redo()->add_do_reference(child);
+		editor_data->get_undo_redo()->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
 	}
 	}
 
 
 	if (parent) {
 	if (parent) {
 		String new_name = parent->validate_child_name(child);
 		String new_name = parent->validate_child_name(child);
 		EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
 		EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
-		editor_data->get_undo_redo().add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), child->get_class(), new_name);
-		editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
+		editor_data->get_undo_redo()->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), child->get_class(), new_name);
+		editor_data->get_undo_redo()->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
 	}
 	}
 
 
 	if (Object::cast_to<TouchScreenButton>(child) || Object::cast_to<TextureButton>(child)) {
 	if (Object::cast_to<TouchScreenButton>(child) || Object::cast_to<TextureButton>(child)) {
-		editor_data->get_undo_redo().add_do_property(child, "texture_normal", texture);
+		editor_data->get_undo_redo()->add_do_property(child, "texture_normal", texture);
 	} else {
 	} else {
-		editor_data->get_undo_redo().add_do_property(child, "texture", texture);
+		editor_data->get_undo_redo()->add_do_property(child, "texture", texture);
 	}
 	}
 
 
 	// make visible for certain node type
 	// make visible for certain node type
 	if (Object::cast_to<Control>(child)) {
 	if (Object::cast_to<Control>(child)) {
 		Size2 texture_size = texture->get_size();
 		Size2 texture_size = texture->get_size();
-		editor_data->get_undo_redo().add_do_property(child, "rect_size", texture_size);
+		editor_data->get_undo_redo()->add_do_property(child, "rect_size", texture_size);
 	} else if (Object::cast_to<Polygon2D>(child)) {
 	} else if (Object::cast_to<Polygon2D>(child)) {
 		Size2 texture_size = texture->get_size();
 		Size2 texture_size = texture->get_size();
 		Vector<Vector2> list = {
 		Vector<Vector2> list = {
@@ -5608,7 +5613,7 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &
 			Vector2(texture_size.width, texture_size.height),
 			Vector2(texture_size.width, texture_size.height),
 			Vector2(0, texture_size.height)
 			Vector2(0, texture_size.height)
 		};
 		};
-		editor_data->get_undo_redo().add_do_property(child, "polygon", list);
+		editor_data->get_undo_redo()->add_do_property(child, "polygon", list);
 	}
 	}
 
 
 	// Compute the global position
 	// Compute the global position
@@ -5617,7 +5622,7 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &
 
 
 	// there's nothing to be used as source position so snapping will work as absolute if enabled
 	// there's nothing to be used as source position so snapping will work as absolute if enabled
 	target_position = canvas_item_editor->snap_point(target_position);
 	target_position = canvas_item_editor->snap_point(target_position);
-	editor_data->get_undo_redo().add_do_method(child, "set_global_position", target_position);
+	editor_data->get_undo_redo()->add_do_method(child, "set_global_position", target_position);
 }
 }
 
 
 bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) {
 bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) {
@@ -5642,15 +5647,15 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons
 
 
 	instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path));
 	instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path));
 
 
-	editor_data->get_undo_redo().add_do_method(parent, "add_child", instantiated_scene, true);
-	editor_data->get_undo_redo().add_do_method(instantiated_scene, "set_owner", edited_scene);
-	editor_data->get_undo_redo().add_do_reference(instantiated_scene);
-	editor_data->get_undo_redo().add_undo_method(parent, "remove_child", instantiated_scene);
+	editor_data->get_undo_redo()->add_do_method(parent, "add_child", instantiated_scene, true);
+	editor_data->get_undo_redo()->add_do_method(instantiated_scene, "set_owner", edited_scene);
+	editor_data->get_undo_redo()->add_do_reference(instantiated_scene);
+	editor_data->get_undo_redo()->add_undo_method(parent, "remove_child", instantiated_scene);
 
 
 	String new_name = parent->validate_child_name(instantiated_scene);
 	String new_name = parent->validate_child_name(instantiated_scene);
 	EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
 	EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
-	editor_data->get_undo_redo().add_do_method(ed, "live_debug_instance_node", edited_scene->get_path_to(parent), path, new_name);
-	editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)) + "/" + new_name));
+	editor_data->get_undo_redo()->add_do_method(ed, "live_debug_instance_node", edited_scene->get_path_to(parent), path, new_name);
+	editor_data->get_undo_redo()->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)) + "/" + new_name));
 
 
 	CanvasItem *instance_ci = Object::cast_to<CanvasItem>(instantiated_scene);
 	CanvasItem *instance_ci = Object::cast_to<CanvasItem>(instantiated_scene);
 	if (instance_ci) {
 	if (instance_ci) {
@@ -5664,7 +5669,7 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons
 		// Preserve instance position of the original scene.
 		// Preserve instance position of the original scene.
 		target_pos += instance_ci->_edit_get_position();
 		target_pos += instance_ci->_edit_get_position();
 
 
-		editor_data->get_undo_redo().add_do_method(instantiated_scene, "set_position", target_pos);
+		editor_data->get_undo_redo()->add_do_method(instantiated_scene, "set_position", target_pos);
 	}
 	}
 
 
 	return true;
 	return true;
@@ -5682,7 +5687,7 @@ void CanvasItemEditorViewport::_perform_drop_data() {
 
 
 	Vector<String> error_files;
 	Vector<String> error_files;
 
 
-	editor_data->get_undo_redo().create_action(TTR("Create Node"));
+	editor_data->get_undo_redo()->create_action(TTR("Create Node"));
 
 
 	for (int i = 0; i < selected_files.size(); i++) {
 	for (int i = 0; i < selected_files.size(); i++) {
 		String path = selected_files[i];
 		String path = selected_files[i];
@@ -5713,7 +5718,7 @@ void CanvasItemEditorViewport::_perform_drop_data() {
 		}
 		}
 	}
 	}
 
 
-	editor_data->get_undo_redo().commit_action();
+	editor_data->get_undo_redo()->commit_action();
 
 
 	if (error_files.size() > 0) {
 	if (error_files.size() > 0) {
 		String files_str;
 		String files_str;

+ 3 - 2
editor/plugins/canvas_item_editor_plugin.h

@@ -45,6 +45,7 @@
 class EditorData;
 class EditorData;
 class CanvasItemEditorViewport;
 class CanvasItemEditorViewport;
 class ViewPanner;
 class ViewPanner;
+class EditorUndoRedoManager;
 
 
 class CanvasItemEditorSelectedItem : public Object {
 class CanvasItemEditorSelectedItem : public Object {
 	GDCLASS(CanvasItemEditorSelectedItem, Object);
 	GDCLASS(CanvasItemEditorSelectedItem, Object);
@@ -400,7 +401,7 @@ private:
 	void _prepare_grid_menu();
 	void _prepare_grid_menu();
 	void _on_grid_menu_id_pressed(int p_id);
 	void _on_grid_menu_id_pressed(int p_id);
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	List<CanvasItem *> _get_edited_canvas_items(bool retrieve_locked = false, bool remove_canvas_item_if_parent_in_selection = true);
 	List<CanvasItem *> _get_edited_canvas_items(bool retrieve_locked = false, bool remove_canvas_item_if_parent_in_selection = true);
 	Rect2 _get_encompassing_rect_from_list(List<CanvasItem *> p_list);
 	Rect2 _get_encompassing_rect_from_list(List<CanvasItem *> p_list);
@@ -547,7 +548,7 @@ public:
 	Tool get_current_tool() { return tool; }
 	Tool get_current_tool() { return tool; }
 	void set_current_tool(Tool p_tool);
 	void set_current_tool(Tool p_tool);
 
 
-	void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; }
+	void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo);
 	void edit(CanvasItem *p_canvas_item);
 	void edit(CanvasItem *p_canvas_item);
 
 
 	void focus_selection();
 	void focus_selection();

+ 1 - 0
editor/plugins/cast_2d_editor_plugin.cpp

@@ -32,6 +32,7 @@
 
 
 #include "canvas_item_editor_plugin.h"
 #include "canvas_item_editor_plugin.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "scene/2d/ray_cast_2d.h"
 #include "scene/2d/ray_cast_2d.h"
 #include "scene/2d/shape_cast_2d.h"
 #include "scene/2d/shape_cast_2d.h"
 
 

+ 2 - 1
editor/plugins/cast_2d_editor_plugin.h

@@ -35,11 +35,12 @@
 #include "scene/2d/node_2d.h"
 #include "scene/2d/node_2d.h"
 
 
 class CanvasItemEditor;
 class CanvasItemEditor;
+class EditorUndoRedoManager;
 
 
 class Cast2DEditor : public Control {
 class Cast2DEditor : public Control {
 	GDCLASS(Cast2DEditor, Control);
 	GDCLASS(Cast2DEditor, Control);
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 	CanvasItemEditor *canvas_item_editor = nullptr;
 	CanvasItemEditor *canvas_item_editor = nullptr;
 	Node2D *node;
 	Node2D *node;
 
 

+ 1 - 0
editor/plugins/collision_shape_2d_editor_plugin.cpp

@@ -33,6 +33,7 @@
 #include "canvas_item_editor_plugin.h"
 #include "canvas_item_editor_plugin.h"
 #include "core/os/keyboard.h"
 #include "core/os/keyboard.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "scene/resources/capsule_shape_2d.h"
 #include "scene/resources/capsule_shape_2d.h"
 #include "scene/resources/circle_shape_2d.h"
 #include "scene/resources/circle_shape_2d.h"
 #include "scene/resources/concave_polygon_shape_2d.h"
 #include "scene/resources/concave_polygon_shape_2d.h"

+ 2 - 1
editor/plugins/collision_shape_2d_editor_plugin.h

@@ -35,6 +35,7 @@
 #include "scene/2d/collision_shape_2d.h"
 #include "scene/2d/collision_shape_2d.h"
 
 
 class CanvasItemEditor;
 class CanvasItemEditor;
+class EditorUndoRedoManager;
 
 
 class CollisionShape2DEditor : public Control {
 class CollisionShape2DEditor : public Control {
 	GDCLASS(CollisionShape2DEditor, Control);
 	GDCLASS(CollisionShape2DEditor, Control);
@@ -61,7 +62,7 @@ class CollisionShape2DEditor : public Control {
 		Point2(1, -1),
 		Point2(1, -1),
 	};
 	};
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 	CanvasItemEditor *canvas_item_editor = nullptr;
 	CanvasItemEditor *canvas_item_editor = nullptr;
 	CollisionShape2D *node = nullptr;
 	CollisionShape2D *node = nullptr;
 
 

+ 1 - 0
editor/plugins/control_editor_plugin.cpp

@@ -33,6 +33,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "editor/plugins/canvas_item_editor_plugin.h"
 #include "editor/plugins/canvas_item_editor_plugin.h"
 #include "scene/gui/separator.h"
 #include "scene/gui/separator.h"
 
 

+ 3 - 1
editor/plugins/control_editor_plugin.h

@@ -44,6 +44,8 @@
 #include "scene/gui/separator.h"
 #include "scene/gui/separator.h"
 #include "scene/gui/texture_rect.h"
 #include "scene/gui/texture_rect.h"
 
 
+class EditorUndoRedoManager;
+
 // Inspector controls.
 // Inspector controls.
 class ControlPositioningWarning : public MarginContainer {
 class ControlPositioningWarning : public MarginContainer {
 	GDCLASS(ControlPositioningWarning, MarginContainer);
 	GDCLASS(ControlPositioningWarning, MarginContainer);
@@ -203,7 +205,7 @@ public:
 class ControlEditorToolbar : public HBoxContainer {
 class ControlEditorToolbar : public HBoxContainer {
 	GDCLASS(ControlEditorToolbar, HBoxContainer);
 	GDCLASS(ControlEditorToolbar, HBoxContainer);
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 	EditorSelection *editor_selection = nullptr;
 	EditorSelection *editor_selection = nullptr;
 
 
 	ControlEditorPopupButton *anchors_button = nullptr;
 	ControlEditorPopupButton *anchors_button = nullptr;

+ 1 - 0
editor/plugins/cpu_particles_2d_editor_plugin.cpp

@@ -34,6 +34,7 @@
 #include "core/io/image_loader.h"
 #include "core/io/image_loader.h"
 #include "editor/editor_file_dialog.h"
 #include "editor/editor_file_dialog.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "scene/2d/cpu_particles_2d.h"
 #include "scene/2d/cpu_particles_2d.h"
 #include "scene/gui/separator.h"
 #include "scene/gui/separator.h"
 #include "scene/resources/particles_material.h"
 #include "scene/resources/particles_material.h"

+ 2 - 1
editor/plugins/cpu_particles_2d_editor_plugin.h

@@ -39,6 +39,7 @@
 class EditorPlugin;
 class EditorPlugin;
 class SpinBox;
 class SpinBox;
 class EditorFileDialog;
 class EditorFileDialog;
+class EditorUndoRedoManager;
 
 
 class CPUParticles2DEditorPlugin : public EditorPlugin {
 class CPUParticles2DEditorPlugin : public EditorPlugin {
 	GDCLASS(CPUParticles2DEditorPlugin, EditorPlugin);
 	GDCLASS(CPUParticles2DEditorPlugin, EditorPlugin);
@@ -70,7 +71,7 @@ class CPUParticles2DEditorPlugin : public EditorPlugin {
 
 
 	String source_emission_file;
 	String source_emission_file;
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 	void _file_selected(const String &p_file);
 	void _file_selected(const String &p_file);
 	void _menu_callback(int p_idx);
 	void _menu_callback(int p_idx);
 	void _generate_emission_mask();
 	void _generate_emission_mask();

+ 27 - 27
editor/plugins/curve_editor_plugin.cpp

@@ -139,14 +139,14 @@ void CurveEditor::gui_input(const Ref<InputEvent> &p_event) {
 		if (!mb.is_pressed() && _dragging && mb.get_button_index() == MouseButton::LEFT) {
 		if (!mb.is_pressed() && _dragging && mb.get_button_index() == MouseButton::LEFT) {
 			_dragging = false;
 			_dragging = false;
 			if (_has_undo_data) {
 			if (_has_undo_data) {
-				UndoRedo &ur = *EditorNode::get_singleton()->get_undo_redo();
+				Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 
 
-				ur.create_action(_selected_tangent == TANGENT_NONE ? TTR("Modify Curve Point") : TTR("Modify Curve Tangent"));
-				ur.add_do_method(*_curve_ref, "_set_data", _curve_ref->get_data());
-				ur.add_undo_method(*_curve_ref, "_set_data", _undo_data);
+				ur->create_action(_selected_tangent == TANGENT_NONE ? TTR("Modify Curve Point") : TTR("Modify Curve Tangent"));
+				ur->add_do_method(*_curve_ref, "_set_data", _curve_ref->get_data());
+				ur->add_undo_method(*_curve_ref, "_set_data", _undo_data);
 				// Note: this will trigger one more "changed" signal even if nothing changes,
 				// Note: this will trigger one more "changed" signal even if nothing changes,
 				// but it's ok since it would have fired every frame during the drag anyways
 				// but it's ok since it would have fired every frame during the drag anyways
-				ur.commit_action();
+				ur->commit_action();
 
 
 				_has_undo_data = false;
 				_has_undo_data = false;
 			}
 			}
@@ -301,13 +301,13 @@ void CurveEditor::on_preset_item_selected(int preset_id) {
 			break;
 			break;
 	}
 	}
 
 
-	UndoRedo &ur = *EditorNode::get_singleton()->get_undo_redo();
-	ur.create_action(TTR("Load Curve Preset"));
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+	ur->create_action(TTR("Load Curve Preset"));
 
 
-	ur.add_do_method(&curve, "_set_data", curve.get_data());
-	ur.add_undo_method(&curve, "_set_data", previous_data);
+	ur->add_do_method(&curve, "_set_data", curve.get_data());
+	ur->add_undo_method(&curve, "_set_data", previous_data);
 
 
-	ur.commit_action();
+	ur->commit_action();
 }
 }
 
 
 void CurveEditor::_curve_changed() {
 void CurveEditor::_curve_changed() {
@@ -435,8 +435,8 @@ CurveEditor::TangentIndex CurveEditor::get_tangent_at(Vector2 pos) const {
 void CurveEditor::add_point(Vector2 pos) {
 void CurveEditor::add_point(Vector2 pos) {
 	ERR_FAIL_COND(_curve_ref.is_null());
 	ERR_FAIL_COND(_curve_ref.is_null());
 
 
-	UndoRedo &ur = *EditorNode::get_singleton()->get_undo_redo();
-	ur.create_action(TTR("Remove Curve Point"));
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+	ur->create_action(TTR("Remove Curve Point"));
 
 
 	Vector2 point_pos = get_world_pos(pos);
 	Vector2 point_pos = get_world_pos(pos);
 	if (point_pos.y < 0.0) {
 	if (point_pos.y < 0.0) {
@@ -449,22 +449,22 @@ void CurveEditor::add_point(Vector2 pos) {
 	int i = _curve_ref->add_point(point_pos);
 	int i = _curve_ref->add_point(point_pos);
 	_curve_ref->remove_point(i);
 	_curve_ref->remove_point(i);
 
 
-	ur.add_do_method(*_curve_ref, "add_point", point_pos);
-	ur.add_undo_method(*_curve_ref, "remove_point", i);
+	ur->add_do_method(*_curve_ref, "add_point", point_pos);
+	ur->add_undo_method(*_curve_ref, "remove_point", i);
 
 
-	ur.commit_action();
+	ur->commit_action();
 }
 }
 
 
 void CurveEditor::remove_point(int index) {
 void CurveEditor::remove_point(int index) {
 	ERR_FAIL_COND(_curve_ref.is_null());
 	ERR_FAIL_COND(_curve_ref.is_null());
 
 
-	UndoRedo &ur = *EditorNode::get_singleton()->get_undo_redo();
-	ur.create_action(TTR("Remove Curve Point"));
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+	ur->create_action(TTR("Remove Curve Point"));
 
 
 	Curve::Point p = _curve_ref->get_point(index);
 	Curve::Point p = _curve_ref->get_point(index);
 
 
-	ur.add_do_method(*_curve_ref, "remove_point", index);
-	ur.add_undo_method(*_curve_ref, "add_point", p.position, p.left_tangent, p.right_tangent, p.left_mode, p.right_mode);
+	ur->add_do_method(*_curve_ref, "remove_point", index);
+	ur->add_undo_method(*_curve_ref, "add_point", p.position, p.left_tangent, p.right_tangent, p.left_mode, p.right_mode);
 
 
 	if (index == _selected_point) {
 	if (index == _selected_point) {
 		set_selected_point(-1);
 		set_selected_point(-1);
@@ -474,14 +474,14 @@ void CurveEditor::remove_point(int index) {
 		set_hover_point_index(-1);
 		set_hover_point_index(-1);
 	}
 	}
 
 
-	ur.commit_action();
+	ur->commit_action();
 }
 }
 
 
 void CurveEditor::toggle_linear(TangentIndex tangent) {
 void CurveEditor::toggle_linear(TangentIndex tangent) {
 	ERR_FAIL_COND(_curve_ref.is_null());
 	ERR_FAIL_COND(_curve_ref.is_null());
 
 
-	UndoRedo &ur = *EditorNode::get_singleton()->get_undo_redo();
-	ur.create_action(TTR("Toggle Curve Linear Tangent"));
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+	ur->create_action(TTR("Toggle Curve Linear Tangent"));
 
 
 	if (tangent == TANGENT_NONE) {
 	if (tangent == TANGENT_NONE) {
 		tangent = _selected_tangent;
 		tangent = _selected_tangent;
@@ -493,8 +493,8 @@ void CurveEditor::toggle_linear(TangentIndex tangent) {
 		Curve::TangentMode prev_mode = _curve_ref->get_point_left_mode(_selected_point);
 		Curve::TangentMode prev_mode = _curve_ref->get_point_left_mode(_selected_point);
 		Curve::TangentMode mode = is_linear ? Curve::TANGENT_FREE : Curve::TANGENT_LINEAR;
 		Curve::TangentMode mode = is_linear ? Curve::TANGENT_FREE : Curve::TANGENT_LINEAR;
 
 
-		ur.add_do_method(*_curve_ref, "set_point_left_mode", _selected_point, mode);
-		ur.add_undo_method(*_curve_ref, "set_point_left_mode", _selected_point, prev_mode);
+		ur->add_do_method(*_curve_ref, "set_point_left_mode", _selected_point, mode);
+		ur->add_undo_method(*_curve_ref, "set_point_left_mode", _selected_point, prev_mode);
 
 
 	} else {
 	} else {
 		bool is_linear = _curve_ref->get_point_right_mode(_selected_point) == Curve::TANGENT_LINEAR;
 		bool is_linear = _curve_ref->get_point_right_mode(_selected_point) == Curve::TANGENT_LINEAR;
@@ -502,11 +502,11 @@ void CurveEditor::toggle_linear(TangentIndex tangent) {
 		Curve::TangentMode prev_mode = _curve_ref->get_point_right_mode(_selected_point);
 		Curve::TangentMode prev_mode = _curve_ref->get_point_right_mode(_selected_point);
 		Curve::TangentMode mode = is_linear ? Curve::TANGENT_FREE : Curve::TANGENT_LINEAR;
 		Curve::TangentMode mode = is_linear ? Curve::TANGENT_FREE : Curve::TANGENT_LINEAR;
 
 
-		ur.add_do_method(*_curve_ref, "set_point_right_mode", _selected_point, mode);
-		ur.add_undo_method(*_curve_ref, "set_point_right_mode", _selected_point, prev_mode);
+		ur->add_do_method(*_curve_ref, "set_point_right_mode", _selected_point, mode);
+		ur->add_undo_method(*_curve_ref, "set_point_right_mode", _selected_point, prev_mode);
 	}
 	}
 
 
-	ur.commit_action();
+	ur->commit_action();
 }
 }
 
 
 void CurveEditor::set_selected_point(int index) {
 void CurveEditor::set_selected_point(int index) {

+ 2 - 1
editor/plugins/gpu_particles_2d_editor_plugin.cpp

@@ -34,6 +34,7 @@
 #include "core/io/image_loader.h"
 #include "core/io/image_loader.h"
 #include "editor/editor_file_dialog.h"
 #include "editor/editor_file_dialog.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "editor/scene_tree_dock.h"
 #include "editor/scene_tree_dock.h"
 #include "scene/2d/cpu_particles_2d.h"
 #include "scene/2d/cpu_particles_2d.h"
 #include "scene/gui/separator.h"
 #include "scene/gui/separator.h"
@@ -111,7 +112,7 @@ void GPUParticles2DEditorPlugin::_menu_callback(int p_idx) {
 			cpu_particles->set_process_mode(particles->get_process_mode());
 			cpu_particles->set_process_mode(particles->get_process_mode());
 			cpu_particles->set_z_index(particles->get_z_index());
 			cpu_particles->set_z_index(particles->get_z_index());
 
 
-			UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+			Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
 			ur->create_action(TTR("Convert to CPUParticles2D"));
 			ur->create_action(TTR("Convert to CPUParticles2D"));
 			ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", particles, cpu_particles, true, false);
 			ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", particles, cpu_particles, true, false);
 			ur->add_do_reference(cpu_particles);
 			ur->add_do_reference(cpu_particles);

+ 2 - 1
editor/plugins/gpu_particles_2d_editor_plugin.h

@@ -38,6 +38,7 @@
 #include "scene/gui/spin_box.h"
 #include "scene/gui/spin_box.h"
 
 
 class EditorFileDialog;
 class EditorFileDialog;
+class EditorUndoRedoManager;
 
 
 class GPUParticles2DEditorPlugin : public EditorPlugin {
 class GPUParticles2DEditorPlugin : public EditorPlugin {
 	GDCLASS(GPUParticles2DEditorPlugin, EditorPlugin);
 	GDCLASS(GPUParticles2DEditorPlugin, EditorPlugin);
@@ -75,7 +76,7 @@ class GPUParticles2DEditorPlugin : public EditorPlugin {
 
 
 	String source_emission_file;
 	String source_emission_file;
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 	void _file_selected(const String &p_file);
 	void _file_selected(const String &p_file);
 	void _menu_callback(int p_idx);
 	void _menu_callback(int p_idx);
 	void _generate_visibility_rect();
 	void _generate_visibility_rect();

+ 3 - 2
editor/plugins/gpu_particles_3d_editor_plugin.cpp

@@ -32,6 +32,7 @@
 
 
 #include "core/io/resource_loader.h"
 #include "core/io/resource_loader.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "editor/plugins/node_3d_editor_plugin.h"
 #include "editor/plugins/node_3d_editor_plugin.h"
 #include "editor/scene_tree_dock.h"
 #include "editor/scene_tree_dock.h"
 #include "scene/3d/cpu_particles_3d.h"
 #include "scene/3d/cpu_particles_3d.h"
@@ -271,7 +272,7 @@ void GPUParticles3DEditor::_menu_option(int p_option) {
 			cpu_particles->set_visible(node->is_visible());
 			cpu_particles->set_visible(node->is_visible());
 			cpu_particles->set_process_mode(node->get_process_mode());
 			cpu_particles->set_process_mode(node->get_process_mode());
 
 
-			UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+			Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
 			ur->create_action(TTR("Convert to CPUParticles3D"));
 			ur->create_action(TTR("Convert to CPUParticles3D"));
 			ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, cpu_particles, true, false);
 			ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, cpu_particles, true, false);
 			ur->add_do_reference(cpu_particles);
 			ur->add_do_reference(cpu_particles);
@@ -321,7 +322,7 @@ void GPUParticles3DEditor::_generate_aabb() {
 		node->set_emitting(false);
 		node->set_emitting(false);
 	}
 	}
 
 
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
 	ur->create_action(TTR("Generate Visibility AABB"));
 	ur->create_action(TTR("Generate Visibility AABB"));
 	ur->add_do_method(node, "set_visibility_aabb", rect);
 	ur->add_do_method(node, "set_visibility_aabb", rect);
 	ur->add_undo_method(node, "set_visibility_aabb", node->get_visibility_aabb());
 	ur->add_undo_method(node, "set_visibility_aabb", node->get_visibility_aabb());

+ 2 - 1
editor/plugins/gradient_editor_plugin.cpp

@@ -34,6 +34,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "node_3d_editor_plugin.h"
 #include "node_3d_editor_plugin.h"
 
 
 Size2 GradientEditor::get_minimum_size() const {
 Size2 GradientEditor::get_minimum_size() const {
@@ -55,7 +56,7 @@ void GradientEditor::_gradient_changed() {
 
 
 void GradientEditor::_ramp_changed() {
 void GradientEditor::_ramp_changed() {
 	editing = true;
 	editing = true;
-	UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo();
 	undo_redo->create_action(TTR("Gradient Edited"), UndoRedo::MERGE_ENDS);
 	undo_redo->create_action(TTR("Gradient Edited"), UndoRedo::MERGE_ENDS);
 	undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets());
 	undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets());
 	undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors());
 	undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors());

+ 3 - 2
editor/plugins/gradient_texture_2d_editor_plugin.cpp

@@ -32,6 +32,7 @@
 
 
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "scene/gui/box_container.h"
 #include "scene/gui/box_container.h"
 #include "scene/gui/flow_container.h"
 #include "scene/gui/flow_container.h"
 #include "scene/gui/separator.h"
 #include "scene/gui/separator.h"
@@ -175,7 +176,7 @@ void GradientTexture2DEditorRect::_notification(int p_what) {
 }
 }
 
 
 GradientTexture2DEditorRect::GradientTexture2DEditorRect() {
 GradientTexture2DEditorRect::GradientTexture2DEditorRect() {
-	undo_redo = EditorNode::get_singleton()->get_undo_redo();
+	undo_redo = EditorNode::get_undo_redo();
 
 
 	checkerboard = memnew(TextureRect);
 	checkerboard = memnew(TextureRect);
 	checkerboard->set_stretch_mode(TextureRect::STRETCH_TILE);
 	checkerboard->set_stretch_mode(TextureRect::STRETCH_TILE);
@@ -222,7 +223,7 @@ void GradientTexture2DEditor::_notification(int p_what) {
 }
 }
 
 
 GradientTexture2DEditor::GradientTexture2DEditor() {
 GradientTexture2DEditor::GradientTexture2DEditor() {
-	undo_redo = EditorNode::get_singleton()->get_undo_redo();
+	undo_redo = EditorNode::get_undo_redo();
 
 
 	HFlowContainer *toolbar = memnew(HFlowContainer);
 	HFlowContainer *toolbar = memnew(HFlowContainer);
 	add_child(toolbar);
 	add_child(toolbar);

+ 4 - 2
editor/plugins/gradient_texture_2d_editor_plugin.h

@@ -34,6 +34,8 @@
 #include "editor/editor_plugin.h"
 #include "editor/editor_plugin.h"
 #include "editor/editor_spin_slider.h"
 #include "editor/editor_spin_slider.h"
 
 
+class EditorUndoRedoManager;
+
 class GradientTexture2DEditorRect : public Control {
 class GradientTexture2DEditorRect : public Control {
 	GDCLASS(GradientTexture2DEditorRect, Control);
 	GDCLASS(GradientTexture2DEditorRect, Control);
 
 
@@ -44,7 +46,7 @@ class GradientTexture2DEditorRect : public Control {
 	};
 	};
 
 
 	Ref<GradientTexture2D> texture;
 	Ref<GradientTexture2D> texture;
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 	bool snap_enabled = false;
 	bool snap_enabled = false;
 	float snap_size = 0;
 	float snap_size = 0;
 
 
@@ -74,7 +76,7 @@ class GradientTexture2DEditor : public VBoxContainer {
 	GDCLASS(GradientTexture2DEditor, VBoxContainer);
 	GDCLASS(GradientTexture2DEditor, VBoxContainer);
 
 
 	Ref<GradientTexture2D> texture;
 	Ref<GradientTexture2D> texture;
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	Button *reverse_button = nullptr;
 	Button *reverse_button = nullptr;
 	Button *snap_button = nullptr;
 	Button *snap_button = nullptr;

+ 3 - 4
editor/plugins/material_editor_plugin.cpp

@@ -33,6 +33,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "scene/gui/subviewport_container.h"
 #include "scene/gui/subviewport_container.h"
 #include "scene/resources/fog_material.h"
 #include "scene/resources/fog_material.h"
 #include "scene/resources/particles_material.h"
 #include "scene/resources/particles_material.h"
@@ -261,10 +262,8 @@ void EditorInspectorPluginMaterial::parse_begin(Object *p_object) {
 }
 }
 
 
 void EditorInspectorPluginMaterial::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) {
 void EditorInspectorPluginMaterial::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) {
-	UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo);
-	if (!undo_redo) {
-		return;
-	}
+	Ref<EditorUndoRedoManager> undo_redo = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
+	ERR_FAIL_COND(!undo_redo.is_valid());
 
 
 	// For BaseMaterial3D, if a roughness or metallic textures is being assigned to an empty slot,
 	// For BaseMaterial3D, if a roughness or metallic textures is being assigned to an empty slot,
 	// set the respective metallic or roughness factor to 1.0 as a convenience feature
 	// set the respective metallic or roughness factor to 1.0 as a convenience feature

+ 8 - 7
editor/plugins/mesh_instance_3d_editor_plugin.cpp

@@ -32,6 +32,7 @@
 
 
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "node_3d_editor_plugin.h"
 #include "node_3d_editor_plugin.h"
 #include "scene/3d/collision_shape_3d.h"
 #include "scene/3d/collision_shape_3d.h"
 #include "scene/3d/navigation_region_3d.h"
 #include "scene/3d/navigation_region_3d.h"
@@ -60,7 +61,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
 	switch (p_option) {
 	switch (p_option) {
 		case MENU_OPTION_CREATE_STATIC_TRIMESH_BODY: {
 		case MENU_OPTION_CREATE_STATIC_TRIMESH_BODY: {
 			EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
 			EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
-			UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+			Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
 
 
 			List<Node *> selection = editor_selection->get_selected_node_list();
 			List<Node *> selection = editor_selection->get_selected_node_list();
 
 
@@ -147,7 +148,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
 
 
 			Node *owner = get_tree()->get_edited_scene_root();
 			Node *owner = get_tree()->get_edited_scene_root();
 
 
-			UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+			Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
 
 
 			ur->create_action(TTR("Create Trimesh Static Shape"));
 			ur->create_action(TTR("Create Trimesh Static Shape"));
 
 
@@ -177,7 +178,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
 				err_dialog->popup_centered();
 				err_dialog->popup_centered();
 				return;
 				return;
 			}
 			}
-			UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+			Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
 
 
 			if (simplify) {
 			if (simplify) {
 				ur->create_action(TTR("Create Simplified Convex Shape"));
 				ur->create_action(TTR("Create Simplified Convex Shape"));
@@ -217,7 +218,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
 				err_dialog->popup_centered();
 				err_dialog->popup_centered();
 				return;
 				return;
 			}
 			}
-			UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+			Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
 
 
 			ur->create_action(TTR("Create Multiple Convex Shapes"));
 			ur->create_action(TTR("Create Multiple Convex Shapes"));
 
 
@@ -254,7 +255,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
 
 
 			Node *owner = get_tree()->get_edited_scene_root();
 			Node *owner = get_tree()->get_edited_scene_root();
 
 
-			UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+			Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
 			ur->create_action(TTR("Create Navigation Mesh"));
 			ur->create_action(TTR("Create Navigation Mesh"));
 
 
 			ur->add_do_method(node, "add_child", nmi, true);
 			ur->add_do_method(node, "add_child", nmi, true);
@@ -311,7 +312,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
 				return;
 				return;
 			}
 			}
 
 
-			UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+			Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
 			ur->create_action(TTR("Unwrap UV2"));
 			ur->create_action(TTR("Unwrap UV2"));
 
 
 			ur->add_do_method(node, "set_mesh", unwrapped_mesh);
 			ur->add_do_method(node, "set_mesh", unwrapped_mesh);
@@ -470,7 +471,7 @@ void MeshInstance3DEditor::_create_outline_mesh() {
 	mi->set_mesh(mesho);
 	mi->set_mesh(mesho);
 	Node *owner = get_tree()->get_edited_scene_root();
 	Node *owner = get_tree()->get_edited_scene_root();
 
 
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo();
 
 
 	ur->create_action(TTR("Create Outline"));
 	ur->create_action(TTR("Create Outline"));
 
 

+ 22 - 21
editor/plugins/node_3d_editor_gizmos.cpp

@@ -35,6 +35,7 @@
 #include "core/math/geometry_3d.h"
 #include "core/math/geometry_3d.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "editor/plugins/node_3d_editor_plugin.h"
 #include "editor/plugins/node_3d_editor_plugin.h"
 #include "scene/3d/audio_listener_3d.h"
 #include "scene/3d/audio_listener_3d.h"
 #include "scene/3d/audio_stream_player_3d.h"
 #include "scene/3d/audio_stream_player_3d.h"
@@ -1347,13 +1348,13 @@ void Light3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_i
 		light->set_param(p_id == 0 ? Light3D::PARAM_RANGE : Light3D::PARAM_SPOT_ANGLE, p_restore);
 		light->set_param(p_id == 0 ? Light3D::PARAM_RANGE : Light3D::PARAM_SPOT_ANGLE, p_restore);
 
 
 	} else if (p_id == 0) {
 	} else if (p_id == 0) {
-		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+		Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 		ur->create_action(TTR("Change Light Radius"));
 		ur->create_action(TTR("Change Light Radius"));
 		ur->add_do_method(light, "set_param", Light3D::PARAM_RANGE, light->get_param(Light3D::PARAM_RANGE));
 		ur->add_do_method(light, "set_param", Light3D::PARAM_RANGE, light->get_param(Light3D::PARAM_RANGE));
 		ur->add_undo_method(light, "set_param", Light3D::PARAM_RANGE, p_restore);
 		ur->add_undo_method(light, "set_param", Light3D::PARAM_RANGE, p_restore);
 		ur->commit_action();
 		ur->commit_action();
 	} else if (p_id == 1) {
 	} else if (p_id == 1) {
-		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+		Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 		ur->create_action(TTR("Change Light Radius"));
 		ur->create_action(TTR("Change Light Radius"));
 		ur->add_do_method(light, "set_param", Light3D::PARAM_SPOT_ANGLE, light->get_param(Light3D::PARAM_SPOT_ANGLE));
 		ur->add_do_method(light, "set_param", Light3D::PARAM_SPOT_ANGLE, light->get_param(Light3D::PARAM_SPOT_ANGLE));
 		ur->add_undo_method(light, "set_param", Light3D::PARAM_SPOT_ANGLE, p_restore);
 		ur->add_undo_method(light, "set_param", Light3D::PARAM_SPOT_ANGLE, p_restore);
@@ -1571,7 +1572,7 @@ void AudioStreamPlayer3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gi
 		player->set_emission_angle(p_restore);
 		player->set_emission_angle(p_restore);
 
 
 	} else {
 	} else {
-		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+		Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 		ur->create_action(TTR("Change AudioStreamPlayer3D Emission Angle"));
 		ur->create_action(TTR("Change AudioStreamPlayer3D Emission Angle"));
 		ur->add_do_method(player, "set_emission_angle", player->get_emission_angle());
 		ur->add_do_method(player, "set_emission_angle", player->get_emission_angle());
 		ur->add_undo_method(player, "set_emission_angle", p_restore);
 		ur->add_undo_method(player, "set_emission_angle", p_restore);
@@ -1814,7 +1815,7 @@ void Camera3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_
 		if (p_cancel) {
 		if (p_cancel) {
 			camera->set("fov", p_restore);
 			camera->set("fov", p_restore);
 		} else {
 		} else {
-			UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+			Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 			ur->create_action(TTR("Change Camera FOV"));
 			ur->create_action(TTR("Change Camera FOV"));
 			ur->add_do_property(camera, "fov", camera->get_fov());
 			ur->add_do_property(camera, "fov", camera->get_fov());
 			ur->add_undo_property(camera, "fov", p_restore);
 			ur->add_undo_property(camera, "fov", p_restore);
@@ -1825,7 +1826,7 @@ void Camera3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_
 		if (p_cancel) {
 		if (p_cancel) {
 			camera->set("size", p_restore);
 			camera->set("size", p_restore);
 		} else {
 		} else {
-			UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+			Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 			ur->create_action(TTR("Change Camera Size"));
 			ur->create_action(TTR("Change Camera Size"));
 			ur->add_do_property(camera, "size", camera->get_size());
 			ur->add_do_property(camera, "size", camera->get_size());
 			ur->add_undo_property(camera, "size", p_restore);
 			ur->add_undo_property(camera, "size", p_restore);
@@ -2141,7 +2142,7 @@ void OccluderInstance3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_giz
 			return;
 			return;
 		}
 		}
 
 
-		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+		Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 		ur->create_action(TTR("Change Sphere Shape Radius"));
 		ur->create_action(TTR("Change Sphere Shape Radius"));
 		ur->add_do_method(so.ptr(), "set_radius", so->get_radius());
 		ur->add_do_method(so.ptr(), "set_radius", so->get_radius());
 		ur->add_undo_method(so.ptr(), "set_radius", p_restore);
 		ur->add_undo_method(so.ptr(), "set_radius", p_restore);
@@ -2155,7 +2156,7 @@ void OccluderInstance3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_giz
 			return;
 			return;
 		}
 		}
 
 
-		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+		Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 		ur->create_action(TTR("Change Box Shape Size"));
 		ur->create_action(TTR("Change Box Shape Size"));
 		ur->add_do_method(bo.ptr(), "set_size", bo->get_size());
 		ur->add_do_method(bo.ptr(), "set_size", bo->get_size());
 		ur->add_undo_method(bo.ptr(), "set_size", p_restore);
 		ur->add_undo_method(bo.ptr(), "set_size", p_restore);
@@ -2169,7 +2170,7 @@ void OccluderInstance3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_giz
 			return;
 			return;
 		}
 		}
 
 
-		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+		Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 		ur->create_action(TTR("Change Box Shape Size"));
 		ur->create_action(TTR("Change Box Shape Size"));
 		ur->add_do_method(qo.ptr(), "set_size", qo->get_size());
 		ur->add_do_method(qo.ptr(), "set_size", qo->get_size());
 		ur->add_undo_method(qo.ptr(), "set_size", p_restore);
 		ur->add_undo_method(qo.ptr(), "set_size", p_restore);
@@ -2870,7 +2871,7 @@ void VisibleOnScreenNotifier3DGizmoPlugin::commit_handle(const EditorNode3DGizmo
 		return;
 		return;
 	}
 	}
 
 
-	UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Change Notifier AABB"));
 	ur->create_action(TTR("Change Notifier AABB"));
 	ur->add_do_method(notifier, "set_aabb", notifier->get_aabb());
 	ur->add_do_method(notifier, "set_aabb", notifier->get_aabb());
 	ur->add_undo_method(notifier, "set_aabb", p_restore);
 	ur->add_undo_method(notifier, "set_aabb", p_restore);
@@ -3061,7 +3062,7 @@ void GPUParticles3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo,
 		return;
 		return;
 	}
 	}
 
 
-	UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Change Particles AABB"));
 	ur->create_action(TTR("Change Particles AABB"));
 	ur->add_do_method(particles, "set_visibility_aabb", particles->get_visibility_aabb());
 	ur->add_do_method(particles, "set_visibility_aabb", particles->get_visibility_aabb());
 	ur->add_undo_method(particles, "set_visibility_aabb", p_restore);
 	ur->add_undo_method(particles, "set_visibility_aabb", p_restore);
@@ -3227,7 +3228,7 @@ void GPUParticlesCollision3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *
 			return;
 			return;
 		}
 		}
 
 
-		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+		Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 		ur->create_action(TTR("Change Radius"));
 		ur->create_action(TTR("Change Radius"));
 		ur->add_do_method(sn, "set_radius", sn->call("get_radius"));
 		ur->add_do_method(sn, "set_radius", sn->call("get_radius"));
 		ur->add_undo_method(sn, "set_radius", p_restore);
 		ur->add_undo_method(sn, "set_radius", p_restore);
@@ -3240,7 +3241,7 @@ void GPUParticlesCollision3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *
 			return;
 			return;
 		}
 		}
 
 
-		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+		Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 		ur->create_action(TTR("Change Box Shape Extents"));
 		ur->create_action(TTR("Change Box Shape Extents"));
 		ur->add_do_method(sn, "set_extents", sn->call("get_extents"));
 		ur->add_do_method(sn, "set_extents", sn->call("get_extents"));
 		ur->add_undo_method(sn, "set_extents", p_restore);
 		ur->add_undo_method(sn, "set_extents", p_restore);
@@ -3499,7 +3500,7 @@ void ReflectionProbeGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo,
 		return;
 		return;
 	}
 	}
 
 
-	UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Change Probe Extents"));
 	ur->create_action(TTR("Change Probe Extents"));
 	ur->add_do_method(probe, "set_extents", probe->get_extents());
 	ur->add_do_method(probe, "set_extents", probe->get_extents());
 	ur->add_do_method(probe, "set_origin_offset", probe->get_origin_offset());
 	ur->add_do_method(probe, "set_origin_offset", probe->get_origin_offset());
@@ -3651,7 +3652,7 @@ void DecalGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id,
 		return;
 		return;
 	}
 	}
 
 
-	UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Change Decal Extents"));
 	ur->create_action(TTR("Change Decal Extents"));
 	ur->add_do_method(decal, "set_extents", decal->get_extents());
 	ur->add_do_method(decal, "set_extents", decal->get_extents());
 	ur->add_undo_method(decal, "set_extents", restore);
 	ur->add_undo_method(decal, "set_extents", restore);
@@ -3791,7 +3792,7 @@ void VoxelGIGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_i
 		return;
 		return;
 	}
 	}
 
 
-	UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Change Probe Extents"));
 	ur->create_action(TTR("Change Probe Extents"));
 	ur->add_do_method(probe, "set_extents", probe->get_extents());
 	ur->add_do_method(probe, "set_extents", probe->get_extents());
 	ur->add_undo_method(probe, "set_extents", restore);
 	ur->add_undo_method(probe, "set_extents", restore);
@@ -4406,7 +4407,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo
 			return;
 			return;
 		}
 		}
 
 
-		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+		Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 		ur->create_action(TTR("Change Sphere Shape Radius"));
 		ur->create_action(TTR("Change Sphere Shape Radius"));
 		ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
 		ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
 		ur->add_undo_method(ss.ptr(), "set_radius", p_restore);
 		ur->add_undo_method(ss.ptr(), "set_radius", p_restore);
@@ -4420,7 +4421,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo
 			return;
 			return;
 		}
 		}
 
 
-		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+		Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 		ur->create_action(TTR("Change Box Shape Size"));
 		ur->create_action(TTR("Change Box Shape Size"));
 		ur->add_do_method(ss.ptr(), "set_size", ss->get_size());
 		ur->add_do_method(ss.ptr(), "set_size", ss->get_size());
 		ur->add_undo_method(ss.ptr(), "set_size", p_restore);
 		ur->add_undo_method(ss.ptr(), "set_size", p_restore);
@@ -4437,7 +4438,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo
 			return;
 			return;
 		}
 		}
 
 
-		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+		Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 		if (p_id == 0) {
 		if (p_id == 0) {
 			ur->create_action(TTR("Change Capsule Shape Radius"));
 			ur->create_action(TTR("Change Capsule Shape Radius"));
 			ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
 			ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
@@ -4462,7 +4463,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo
 			return;
 			return;
 		}
 		}
 
 
-		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+		Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 		if (p_id == 0) {
 		if (p_id == 0) {
 			ur->create_action(TTR("Change Cylinder Shape Radius"));
 			ur->create_action(TTR("Change Cylinder Shape Radius"));
 			ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
 			ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());
@@ -4487,7 +4488,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo
 			return;
 			return;
 		}
 		}
 
 
-		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+		Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 		ur->create_action(TTR("Change Separation Ray Shape Length"));
 		ur->create_action(TTR("Change Separation Ray Shape Length"));
 		ur->add_do_method(ss.ptr(), "set_length", ss->get_length());
 		ur->add_do_method(ss.ptr(), "set_length", ss->get_length());
 		ur->add_undo_method(ss.ptr(), "set_length", p_restore);
 		ur->add_undo_method(ss.ptr(), "set_length", p_restore);
@@ -5745,7 +5746,7 @@ void FogVolumeGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p
 		return;
 		return;
 	}
 	}
 
 
-	UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Change Fog Volume Extents"));
 	ur->create_action(TTR("Change Fog Volume Extents"));
 	ur->add_do_method(sn, "set_extents", sn->call("get_extents"));
 	ur->add_do_method(sn, "set_extents", sn->call("get_extents"));
 	ur->add_undo_method(sn, "set_extents", p_restore);
 	ur->add_undo_method(sn, "set_extents", p_restore);

+ 25 - 17
editor/plugins/node_3d_editor_plugin.cpp

@@ -4003,15 +4003,15 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po
 		instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path));
 		instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path));
 	}
 	}
 
 
-	editor_data->get_undo_redo().add_do_method(parent, "add_child", instantiated_scene, true);
-	editor_data->get_undo_redo().add_do_method(instantiated_scene, "set_owner", EditorNode::get_singleton()->get_edited_scene());
-	editor_data->get_undo_redo().add_do_reference(instantiated_scene);
-	editor_data->get_undo_redo().add_undo_method(parent, "remove_child", instantiated_scene);
+	editor_data->get_undo_redo()->add_do_method(parent, "add_child", instantiated_scene, true);
+	editor_data->get_undo_redo()->add_do_method(instantiated_scene, "set_owner", EditorNode::get_singleton()->get_edited_scene());
+	editor_data->get_undo_redo()->add_do_reference(instantiated_scene);
+	editor_data->get_undo_redo()->add_undo_method(parent, "remove_child", instantiated_scene);
 
 
 	String new_name = parent->validate_child_name(instantiated_scene);
 	String new_name = parent->validate_child_name(instantiated_scene);
 	EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
 	EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
-	editor_data->get_undo_redo().add_do_method(ed, "live_debug_instance_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), path, new_name);
-	editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
+	editor_data->get_undo_redo()->add_do_method(ed, "live_debug_instance_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), path, new_name);
+	editor_data->get_undo_redo()->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
 
 
 	Node3D *node3d = Object::cast_to<Node3D>(instantiated_scene);
 	Node3D *node3d = Object::cast_to<Node3D>(instantiated_scene);
 	if (node3d) {
 	if (node3d) {
@@ -4024,7 +4024,7 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po
 		global_transform.origin = spatial_editor->snap_point(_get_instance_position(p_point));
 		global_transform.origin = spatial_editor->snap_point(_get_instance_position(p_point));
 		global_transform.basis *= node3d->get_transform().basis;
 		global_transform.basis *= node3d->get_transform().basis;
 
 
-		editor_data->get_undo_redo().add_do_method(instantiated_scene, "set_global_transform", global_transform);
+		editor_data->get_undo_redo()->add_do_method(instantiated_scene, "set_global_transform", global_transform);
 	}
 	}
 
 
 	return true;
 	return true;
@@ -4035,15 +4035,15 @@ void Node3DEditorViewport::_perform_drop_data() {
 		GeometryInstance3D *geometry_instance = Object::cast_to<GeometryInstance3D>(ObjectDB::get_instance(spatial_editor->get_preview_material_target()));
 		GeometryInstance3D *geometry_instance = Object::cast_to<GeometryInstance3D>(ObjectDB::get_instance(spatial_editor->get_preview_material_target()));
 		MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(ObjectDB::get_instance(spatial_editor->get_preview_material_target()));
 		MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(ObjectDB::get_instance(spatial_editor->get_preview_material_target()));
 		if (mesh_instance && spatial_editor->get_preview_material_surface() != -1) {
 		if (mesh_instance && spatial_editor->get_preview_material_surface() != -1) {
-			editor_data->get_undo_redo().create_action(vformat(TTR("Set Surface %d Override Material"), spatial_editor->get_preview_material_surface()));
-			editor_data->get_undo_redo().add_do_method(geometry_instance, "set_surface_override_material", spatial_editor->get_preview_material_surface(), spatial_editor->get_preview_material());
-			editor_data->get_undo_redo().add_undo_method(geometry_instance, "set_surface_override_material", spatial_editor->get_preview_material_surface(), spatial_editor->get_preview_reset_material());
-			editor_data->get_undo_redo().commit_action();
+			editor_data->get_undo_redo()->create_action(vformat(TTR("Set Surface %d Override Material"), spatial_editor->get_preview_material_surface()));
+			editor_data->get_undo_redo()->add_do_method(geometry_instance, "set_surface_override_material", spatial_editor->get_preview_material_surface(), spatial_editor->get_preview_material());
+			editor_data->get_undo_redo()->add_undo_method(geometry_instance, "set_surface_override_material", spatial_editor->get_preview_material_surface(), spatial_editor->get_preview_reset_material());
+			editor_data->get_undo_redo()->commit_action();
 		} else if (geometry_instance) {
 		} else if (geometry_instance) {
-			editor_data->get_undo_redo().create_action(TTR("Set Material Override"));
-			editor_data->get_undo_redo().add_do_method(geometry_instance, "set_material_override", spatial_editor->get_preview_material());
-			editor_data->get_undo_redo().add_undo_method(geometry_instance, "set_material_override", spatial_editor->get_preview_reset_material());
-			editor_data->get_undo_redo().commit_action();
+			editor_data->get_undo_redo()->create_action(TTR("Set Material Override"));
+			editor_data->get_undo_redo()->add_do_method(geometry_instance, "set_material_override", spatial_editor->get_preview_material());
+			editor_data->get_undo_redo()->add_undo_method(geometry_instance, "set_material_override", spatial_editor->get_preview_reset_material());
+			editor_data->get_undo_redo()->commit_action();
 		}
 		}
 
 
 		_remove_preview_material();
 		_remove_preview_material();
@@ -4054,7 +4054,7 @@ void Node3DEditorViewport::_perform_drop_data() {
 
 
 	Vector<String> error_files;
 	Vector<String> error_files;
 
 
-	editor_data->get_undo_redo().create_action(TTR("Create Node"));
+	editor_data->get_undo_redo()->create_action(TTR("Create Node"));
 
 
 	for (int i = 0; i < selected_files.size(); i++) {
 	for (int i = 0; i < selected_files.size(); i++) {
 		String path = selected_files[i];
 		String path = selected_files[i];
@@ -4072,7 +4072,7 @@ void Node3DEditorViewport::_perform_drop_data() {
 		}
 		}
 	}
 	}
 
 
-	editor_data->get_undo_redo().commit_action();
+	editor_data->get_undo_redo()->commit_action();
 
 
 	if (error_files.size() > 0) {
 	if (error_files.size() > 0) {
 		String files_str;
 		String files_str;
@@ -7247,6 +7247,14 @@ Vector<int> Node3DEditor::get_subgizmo_selection() {
 	return ret;
 	return ret;
 }
 }
 
 
+void Node3DEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) {
+	undo_redo = p_undo_redo;
+}
+
+Ref<EditorUndoRedoManager> Node3DEditor::get_undo_redo() {
+	return undo_redo;
+}
+
 void Node3DEditor::add_control_to_menu_panel(Control *p_control) {
 void Node3DEditor::add_control_to_menu_panel(Control *p_control) {
 	context_menu_hbox->add_child(p_control);
 	context_menu_hbox->add_child(p_control);
 }
 }

+ 5 - 4
editor/plugins/node_3d_editor_plugin.h

@@ -53,6 +53,7 @@ class Node3DEditorViewport;
 class SubViewportContainer;
 class SubViewportContainer;
 class DirectionalLight3D;
 class DirectionalLight3D;
 class WorldEnvironment;
 class WorldEnvironment;
+class EditorUndoRedoManager;
 
 
 class ViewportRotationControl : public Control {
 class ViewportRotationControl : public Control {
 	GDCLASS(ViewportRotationControl, Control);
 	GDCLASS(ViewportRotationControl, Control);
@@ -201,7 +202,7 @@ private:
 
 
 	EditorData *editor_data = nullptr;
 	EditorData *editor_data = nullptr;
 	EditorSelection *editor_selection = nullptr;
 	EditorSelection *editor_selection = nullptr;
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	CheckBox *preview_camera = nullptr;
 	CheckBox *preview_camera = nullptr;
 	SubViewportContainer *subviewport_container = nullptr;
 	SubViewportContainer *subviewport_container = nullptr;
@@ -682,7 +683,7 @@ private:
 	HBoxContainer *context_menu_hbox = nullptr;
 	HBoxContainer *context_menu_hbox = nullptr;
 
 
 	void _generate_selection_boxes();
 	void _generate_selection_boxes();
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	int camera_override_viewport_id;
 	int camera_override_viewport_id;
 
 
@@ -820,13 +821,13 @@ public:
 	void select_gizmo_highlight_axis(int p_axis);
 	void select_gizmo_highlight_axis(int p_axis);
 	void set_custom_camera(Node *p_camera) { custom_camera = p_camera; }
 	void set_custom_camera(Node *p_camera) { custom_camera = p_camera; }
 
 
-	void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; }
 	Dictionary get_state() const;
 	Dictionary get_state() const;
 	void set_state(const Dictionary &p_state);
 	void set_state(const Dictionary &p_state);
 
 
 	Ref<Environment> get_viewport_environment() { return viewport_environment; }
 	Ref<Environment> get_viewport_environment() { return viewport_environment; }
 
 
-	UndoRedo *get_undo_redo() { return undo_redo; }
+	void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo);
+	Ref<EditorUndoRedoManager> get_undo_redo();
 
 
 	void add_control_to_menu_panel(Control *p_control);
 	void add_control_to_menu_panel(Control *p_control);
 	void remove_control_from_menu_panel(Control *p_control);
 	void remove_control_from_menu_panel(Control *p_control);

+ 1 - 0
editor/plugins/path_2d_editor_plugin.cpp

@@ -36,6 +36,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 
 
 void Path2DEditor::_notification(int p_what) {
 void Path2DEditor::_notification(int p_what) {
 	switch (p_what) {
 	switch (p_what) {

+ 2 - 1
editor/plugins/path_2d_editor_plugin.h

@@ -36,11 +36,12 @@
 #include "scene/gui/separator.h"
 #include "scene/gui/separator.h"
 
 
 class CanvasItemEditor;
 class CanvasItemEditor;
+class EditorUndoRedoManager;
 
 
 class Path2DEditor : public HBoxContainer {
 class Path2DEditor : public HBoxContainer {
 	GDCLASS(Path2DEditor, HBoxContainer);
 	GDCLASS(Path2DEditor, HBoxContainer);
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	CanvasItemEditor *canvas_item_editor = nullptr;
 	CanvasItemEditor *canvas_item_editor = nullptr;
 	Panel *panel = nullptr;
 	Panel *panel = nullptr;

+ 7 - 6
editor/plugins/path_3d_editor_plugin.cpp

@@ -35,6 +35,7 @@
 #include "core/os/keyboard.h"
 #include "core/os/keyboard.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "node_3d_editor_plugin.h"
 #include "node_3d_editor_plugin.h"
 #include "scene/resources/curve.h"
 #include "scene/resources/curve.h"
 
 
@@ -172,7 +173,7 @@ void Path3DGizmo::commit_handle(int p_id, bool p_secondary, const Variant &p_res
 		return;
 		return;
 	}
 	}
 
 
-	UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 
 
 	if (!p_secondary) {
 	if (!p_secondary) {
 		if (p_cancel) {
 		if (p_cancel) {
@@ -385,7 +386,7 @@ EditorPlugin::AfterGUIInput Path3DEditorPlugin::forward_spatial_gui_input(Camera
 				}
 				}
 			}
 			}
 
 
-			UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+			Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 			if (closest_seg != -1) {
 			if (closest_seg != -1) {
 				//subdivide
 				//subdivide
 
 
@@ -427,21 +428,21 @@ EditorPlugin::AfterGUIInput Path3DEditorPlugin::forward_spatial_gui_input(Camera
 				// Find the offset and point index of the place to break up.
 				// Find the offset and point index of the place to break up.
 				// Also check for the control points.
 				// Also check for the control points.
 				if (dist_to_p < click_dist) {
 				if (dist_to_p < click_dist) {
-					UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+					Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 					ur->create_action(TTR("Remove Path Point"));
 					ur->create_action(TTR("Remove Path Point"));
 					ur->add_do_method(c.ptr(), "remove_point", i);
 					ur->add_do_method(c.ptr(), "remove_point", i);
 					ur->add_undo_method(c.ptr(), "add_point", c->get_point_position(i), c->get_point_in(i), c->get_point_out(i), i);
 					ur->add_undo_method(c.ptr(), "add_point", c->get_point_position(i), c->get_point_in(i), c->get_point_out(i), i);
 					ur->commit_action();
 					ur->commit_action();
 					return EditorPlugin::AFTER_GUI_INPUT_STOP;
 					return EditorPlugin::AFTER_GUI_INPUT_STOP;
 				} else if (dist_to_p_out < click_dist) {
 				} else if (dist_to_p_out < click_dist) {
-					UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+					Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 					ur->create_action(TTR("Remove Out-Control Point"));
 					ur->create_action(TTR("Remove Out-Control Point"));
 					ur->add_do_method(c.ptr(), "set_point_out", i, Vector3());
 					ur->add_do_method(c.ptr(), "set_point_out", i, Vector3());
 					ur->add_undo_method(c.ptr(), "set_point_out", i, c->get_point_out(i));
 					ur->add_undo_method(c.ptr(), "set_point_out", i, c->get_point_out(i));
 					ur->commit_action();
 					ur->commit_action();
 					return EditorPlugin::AFTER_GUI_INPUT_STOP;
 					return EditorPlugin::AFTER_GUI_INPUT_STOP;
 				} else if (dist_to_p_in < click_dist) {
 				} else if (dist_to_p_in < click_dist) {
-					UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+					Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 					ur->create_action(TTR("Remove In-Control Point"));
 					ur->create_action(TTR("Remove In-Control Point"));
 					ur->add_do_method(c.ptr(), "set_point_in", i, Vector3());
 					ur->add_do_method(c.ptr(), "set_point_in", i, Vector3());
 					ur->add_undo_method(c.ptr(), "set_point_in", i, c->get_point_in(i));
 					ur->add_undo_method(c.ptr(), "set_point_in", i, c->get_point_in(i));
@@ -520,7 +521,7 @@ void Path3DEditorPlugin::_close_curve() {
 	if (c->get_point_position(0) == c->get_point_position(c->get_point_count() - 1)) {
 	if (c->get_point_position(0) == c->get_point_position(c->get_point_count() - 1)) {
 		return;
 		return;
 	}
 	}
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Close Curve"));
 	ur->create_action(TTR("Close Curve"));
 	ur->add_do_method(c.ptr(), "add_point", c->get_point_position(0), c->get_point_in(0), c->get_point_out(0), -1);
 	ur->add_do_method(c.ptr(), "add_point", c->get_point_position(0), c->get_point_in(0), c->get_point_out(0), -1);
 	ur->add_undo_method(c.ptr(), "remove_point", c->get_point_count());
 	ur->add_undo_method(c.ptr(), "remove_point", c->get_point_count());

+ 1 - 0
editor/plugins/polygon_3d_editor_plugin.cpp

@@ -38,6 +38,7 @@
 #include "core/os/keyboard.h"
 #include "core/os/keyboard.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "node_3d_editor_plugin.h"
 #include "node_3d_editor_plugin.h"
 #include "scene/3d/camera_3d.h"
 #include "scene/3d/camera_3d.h"
 
 

+ 2 - 1
editor/plugins/polygon_3d_editor_plugin.h

@@ -37,11 +37,12 @@
 #include "scene/resources/immediate_mesh.h"
 #include "scene/resources/immediate_mesh.h"
 
 
 class CanvasItemEditor;
 class CanvasItemEditor;
+class EditorUndoRedoManager;
 
 
 class Polygon3DEditor : public HBoxContainer {
 class Polygon3DEditor : public HBoxContainer {
 	GDCLASS(Polygon3DEditor, HBoxContainer);
 	GDCLASS(Polygon3DEditor, HBoxContainer);
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 	enum Mode {
 	enum Mode {
 		MODE_CREATE,
 		MODE_CREATE,
 		MODE_EDIT,
 		MODE_EDIT,

+ 5 - 1
editor/plugins/resource_preloader_editor_plugin.cpp

@@ -234,6 +234,10 @@ void ResourcePreloaderEditor::_cell_button_pressed(Object *p_item, int p_column,
 	}
 	}
 }
 }
 
 
+void ResourcePreloaderEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) {
+	undo_redo = p_undo_redo;
+}
+
 void ResourcePreloaderEditor::edit(ResourcePreloader *p_preloader) {
 void ResourcePreloaderEditor::edit(ResourcePreloader *p_preloader) {
 	preloader = p_preloader;
 	preloader = p_preloader;
 
 
@@ -387,7 +391,7 @@ ResourcePreloaderEditor::ResourcePreloaderEditor() {
 }
 }
 
 
 void ResourcePreloaderEditorPlugin::edit(Object *p_object) {
 void ResourcePreloaderEditorPlugin::edit(Object *p_object) {
-	preloader_editor->set_undo_redo(&get_undo_redo());
+	preloader_editor->set_undo_redo(EditorNode::get_undo_redo());
 	ResourcePreloader *s = Object::cast_to<ResourcePreloader>(p_object);
 	ResourcePreloader *s = Object::cast_to<ResourcePreloader>(p_object);
 	if (!s) {
 	if (!s) {
 		return;
 		return;

+ 3 - 2
editor/plugins/resource_preloader_editor_plugin.h

@@ -37,6 +37,7 @@
 #include "scene/main/resource_preloader.h"
 #include "scene/main/resource_preloader.h"
 
 
 class EditorFileDialog;
 class EditorFileDialog;
+class EditorUndoRedoManager;
 
 
 class ResourcePreloaderEditor : public PanelContainer {
 class ResourcePreloaderEditor : public PanelContainer {
 	GDCLASS(ResourcePreloaderEditor, PanelContainer);
 	GDCLASS(ResourcePreloaderEditor, PanelContainer);
@@ -66,7 +67,7 @@ class ResourcePreloaderEditor : public PanelContainer {
 	void _cell_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
 	void _cell_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
 	void _item_edited();
 	void _item_edited();
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
 	Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
 	bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
 	bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
@@ -78,7 +79,7 @@ protected:
 	static void _bind_methods();
 	static void _bind_methods();
 
 
 public:
 public:
-	void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; }
+	void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo);
 
 
 	void edit(ResourcePreloader *p_preloader);
 	void edit(ResourcePreloader *p_preloader);
 	ResourcePreloaderEditor();
 	ResourcePreloaderEditor();

+ 0 - 2
editor/plugins/script_editor_plugin.cpp

@@ -1408,8 +1408,6 @@ void ScriptEditor::_menu_option(int p_option) {
 				es->set_editor(EditorNode::get_singleton());
 				es->set_editor(EditorNode::get_singleton());
 
 
 				es->_run();
 				es->_run();
-
-				EditorNode::get_undo_redo()->clear_history();
 			} break;
 			} break;
 			case FILE_CLOSE: {
 			case FILE_CLOSE: {
 				if (current->is_unsaved()) {
 				if (current->is_unsaved()) {

+ 3 - 2
editor/plugins/skeleton_2d_editor_plugin.cpp

@@ -32,6 +32,7 @@
 
 
 #include "canvas_item_editor_plugin.h"
 #include "canvas_item_editor_plugin.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "scene/2d/mesh_instance_2d.h"
 #include "scene/2d/mesh_instance_2d.h"
 #include "scene/gui/box_container.h"
 #include "scene/gui/box_container.h"
 #include "thirdparty/misc/clipper.hpp"
 #include "thirdparty/misc/clipper.hpp"
@@ -59,7 +60,7 @@ void Skeleton2DEditor::_menu_option(int p_option) {
 				err_dialog->popup_centered();
 				err_dialog->popup_centered();
 				return;
 				return;
 			}
 			}
-			UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+			Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 			ur->create_action(TTR("Set Rest Pose to Bones"));
 			ur->create_action(TTR("Set Rest Pose to Bones"));
 			for (int i = 0; i < node->get_bone_count(); i++) {
 			for (int i = 0; i < node->get_bone_count(); i++) {
 				Bone2D *bone = node->get_bone(i);
 				Bone2D *bone = node->get_bone(i);
@@ -75,7 +76,7 @@ void Skeleton2DEditor::_menu_option(int p_option) {
 				err_dialog->popup_centered();
 				err_dialog->popup_centered();
 				return;
 				return;
 			}
 			}
-			UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+			Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 			ur->create_action(TTR("Create Rest Pose from Bones"));
 			ur->create_action(TTR("Create Rest Pose from Bones"));
 			for (int i = 0; i < node->get_bone_count(); i++) {
 			for (int i = 0; i < node->get_bone_count(); i++) {
 				Bone2D *bone = node->get_bone(i);
 				Bone2D *bone = node->get_bone(i);

+ 6 - 5
editor/plugins/skeleton_3d_editor_plugin.cpp

@@ -35,6 +35,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_properties.h"
 #include "editor/editor_properties.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "editor/plugins/animation_player_editor_plugin.h"
 #include "editor/plugins/animation_player_editor_plugin.h"
 #include "node_3d_editor_plugin.h"
 #include "node_3d_editor_plugin.h"
 #include "scene/3d/collision_shape_3d.h"
 #include "scene/3d/collision_shape_3d.h"
@@ -267,7 +268,7 @@ void Skeleton3DEditor::reset_pose(const bool p_all_bones) {
 		return;
 		return;
 	}
 	}
 
 
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
 	ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
 	if (p_all_bones) {
 	if (p_all_bones) {
 		for (int i = 0; i < bone_len; i++) {
 		for (int i = 0; i < bone_len; i++) {
@@ -334,7 +335,7 @@ void Skeleton3DEditor::pose_to_rest(const bool p_all_bones) {
 		return;
 		return;
 	}
 	}
 
 
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Set Bone Rest"), UndoRedo::MERGE_ENDS);
 	ur->create_action(TTR("Set Bone Rest"), UndoRedo::MERGE_ENDS);
 	if (p_all_bones) {
 	if (p_all_bones) {
 		for (int i = 0; i < bone_len; i++) {
 		for (int i = 0; i < bone_len; i++) {
@@ -354,7 +355,7 @@ void Skeleton3DEditor::pose_to_rest(const bool p_all_bones) {
 }
 }
 
 
 void Skeleton3DEditor::create_physical_skeleton() {
 void Skeleton3DEditor::create_physical_skeleton() {
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ERR_FAIL_COND(!get_tree());
 	ERR_FAIL_COND(!get_tree());
 	Node *owner = get_tree()->get_edited_scene_root();
 	Node *owner = get_tree()->get_edited_scene_root();
 
 
@@ -587,7 +588,7 @@ void Skeleton3DEditor::move_skeleton_bone(NodePath p_skeleton_path, int32_t p_se
 	Node *node = get_node_or_null(p_skeleton_path);
 	Node *node = get_node_or_null(p_skeleton_path);
 	Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
 	Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
 	ERR_FAIL_NULL(skeleton);
 	ERR_FAIL_NULL(skeleton);
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Set Bone Parentage"));
 	ur->create_action(TTR("Set Bone Parentage"));
 	// If the target is a child of ourselves, we move only *us* and not our children.
 	// If the target is a child of ourselves, we move only *us* and not our children.
 	if (skeleton->is_bone_parent_of(p_target_boneidx, p_selected_boneidx)) {
 	if (skeleton->is_bone_parent_of(p_target_boneidx, p_selected_boneidx)) {
@@ -1309,7 +1310,7 @@ void Skeleton3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, c
 	Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
 	Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
 	Node3DEditor *ne = Node3DEditor::get_singleton();
 	Node3DEditor *ne = Node3DEditor::get_singleton();
 
 
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Set Bone Transform"));
 	ur->create_action(TTR("Set Bone Transform"));
 	if (ne->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || ne->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE) {
 	if (ne->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || ne->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE) {
 		for (int i = 0; i < p_ids.size(); i++) {
 		for (int i = 0; i < p_ids.size(); i++) {

+ 2 - 1
editor/plugins/skeleton_3d_editor_plugin.h

@@ -40,6 +40,7 @@
 #include "scene/resources/immediate_mesh.h"
 #include "scene/resources/immediate_mesh.h"
 
 
 class EditorInspectorPluginSkeleton;
 class EditorInspectorPluginSkeleton;
+class EditorUndoRedoManager;
 class Joint;
 class Joint;
 class PhysicalBone3D;
 class PhysicalBone3D;
 class Skeleton3DEditorPlugin;
 class Skeleton3DEditorPlugin;
@@ -63,7 +64,7 @@ class BoneTransformEditor : public VBoxContainer {
 	Skeleton3D *skeleton = nullptr;
 	Skeleton3D *skeleton = nullptr;
 	// String property;
 	// String property;
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	bool toggle_enabled = false;
 	bool toggle_enabled = false;
 	bool updating = false;
 	bool updating = false;

+ 5 - 4
editor/plugins/sprite_2d_editor_plugin.cpp

@@ -34,6 +34,7 @@
 #include "core/math/geometry_2d.h"
 #include "core/math/geometry_2d.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "editor/scene_tree_dock.h"
 #include "editor/scene_tree_dock.h"
 #include "scene/2d/collision_polygon_2d.h"
 #include "scene/2d/collision_polygon_2d.h"
 #include "scene/2d/light_occluder_2d.h"
 #include "scene/2d/light_occluder_2d.h"
@@ -342,7 +343,7 @@ void Sprite2DEditor::_convert_to_mesh_2d_node() {
 	MeshInstance2D *mesh_instance = memnew(MeshInstance2D);
 	MeshInstance2D *mesh_instance = memnew(MeshInstance2D);
 	mesh_instance->set_mesh(mesh);
 	mesh_instance->set_mesh(mesh);
 
 
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Convert to MeshInstance2D"));
 	ur->create_action(TTR("Convert to MeshInstance2D"));
 	ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, mesh_instance, true, false);
 	ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, mesh_instance, true, false);
 	ur->add_do_reference(mesh_instance);
 	ur->add_do_reference(mesh_instance);
@@ -400,7 +401,7 @@ void Sprite2DEditor::_convert_to_polygon_2d_node() {
 	polygon_2d_instance->set_polygon(polygon);
 	polygon_2d_instance->set_polygon(polygon);
 	polygon_2d_instance->set_polygons(polys);
 	polygon_2d_instance->set_polygons(polys);
 
 
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Convert to Polygon2D"));
 	ur->create_action(TTR("Convert to Polygon2D"));
 	ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, polygon_2d_instance, true, false);
 	ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, polygon_2d_instance, true, false);
 	ur->add_do_reference(polygon_2d_instance);
 	ur->add_do_reference(polygon_2d_instance);
@@ -422,7 +423,7 @@ void Sprite2DEditor::_create_collision_polygon_2d_node() {
 		CollisionPolygon2D *collision_polygon_2d_instance = memnew(CollisionPolygon2D);
 		CollisionPolygon2D *collision_polygon_2d_instance = memnew(CollisionPolygon2D);
 		collision_polygon_2d_instance->set_polygon(outline);
 		collision_polygon_2d_instance->set_polygon(outline);
 
 
-		UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+		Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 		ur->create_action(TTR("Create CollisionPolygon2D Sibling"));
 		ur->create_action(TTR("Create CollisionPolygon2D Sibling"));
 		ur->add_do_method(this, "_add_as_sibling_or_child", node, collision_polygon_2d_instance);
 		ur->add_do_method(this, "_add_as_sibling_or_child", node, collision_polygon_2d_instance);
 		ur->add_do_reference(collision_polygon_2d_instance);
 		ur->add_do_reference(collision_polygon_2d_instance);
@@ -455,7 +456,7 @@ void Sprite2DEditor::_create_light_occluder_2d_node() {
 		LightOccluder2D *light_occluder_2d_instance = memnew(LightOccluder2D);
 		LightOccluder2D *light_occluder_2d_instance = memnew(LightOccluder2D);
 		light_occluder_2d_instance->set_occluder_polygon(polygon);
 		light_occluder_2d_instance->set_occluder_polygon(polygon);
 
 
-		UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+		Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 		ur->create_action(TTR("Create LightOccluder2D Sibling"));
 		ur->create_action(TTR("Create LightOccluder2D Sibling"));
 		ur->add_do_method(this, "_add_as_sibling_or_child", node, light_occluder_2d_instance);
 		ur->add_do_method(this, "_add_as_sibling_or_child", node, light_occluder_2d_instance);
 		ur->add_do_reference(light_occluder_2d_instance);
 		ur->add_do_reference(light_occluder_2d_instance);

+ 6 - 1
editor/plugins/sprite_frames_editor_plugin.cpp

@@ -37,6 +37,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "editor/scene_tree_dock.h"
 #include "editor/scene_tree_dock.h"
 #include "scene/3d/sprite_3d.h"
 #include "scene/3d/sprite_3d.h"
 #include "scene/gui/center_container.h"
 #include "scene/gui/center_container.h"
@@ -1010,6 +1011,10 @@ void SpriteFramesEditor::edit(SpriteFrames *p_frames) {
 	}
 	}
 }
 }
 
 
+void SpriteFramesEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) {
+	undo_redo = p_undo_redo;
+}
+
 Variant SpriteFramesEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
 Variant SpriteFramesEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
 	if (!frames->has_animation(edited_anim)) {
 	if (!frames->has_animation(edited_anim)) {
 		return false;
 		return false;
@@ -1471,7 +1476,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
 }
 }
 
 
 void SpriteFramesEditorPlugin::edit(Object *p_object) {
 void SpriteFramesEditorPlugin::edit(Object *p_object) {
-	frames_editor->set_undo_redo(&get_undo_redo());
+	frames_editor->set_undo_redo(get_undo_redo());
 
 
 	SpriteFrames *s;
 	SpriteFrames *s;
 	AnimatedSprite2D *animated_sprite = Object::cast_to<AnimatedSprite2D>(p_object);
 	AnimatedSprite2D *animated_sprite = Object::cast_to<AnimatedSprite2D>(p_object);

+ 3 - 2
editor/plugins/sprite_frames_editor_plugin.h

@@ -45,6 +45,7 @@
 #include "scene/gui/tree.h"
 #include "scene/gui/tree.h"
 
 
 class EditorFileDialog;
 class EditorFileDialog;
+class EditorUndoRedoManager;
 
 
 class SpriteFramesEditor : public HSplitContainer {
 class SpriteFramesEditor : public HSplitContainer {
 	GDCLASS(SpriteFramesEditor, HSplitContainer);
 	GDCLASS(SpriteFramesEditor, HSplitContainer);
@@ -151,7 +152,7 @@ class SpriteFramesEditor : public HSplitContainer {
 	bool updating;
 	bool updating;
 	bool updating_split_settings = false; // Skip SpinBox/Range callback when setting value by code.
 	bool updating_split_settings = false; // Skip SpinBox/Range callback when setting value by code.
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
 	Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
 	bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
 	bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
@@ -176,7 +177,7 @@ protected:
 	static void _bind_methods();
 	static void _bind_methods();
 
 
 public:
 public:
-	void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; }
+	void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo);
 
 
 	void edit(SpriteFrames *p_frames);
 	void edit(SpriteFrames *p_frames);
 	SpriteFramesEditor();
 	SpriteFramesEditor();

+ 1 - 0
editor/plugins/texture_region_editor_plugin.cpp

@@ -36,6 +36,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "scene/gui/check_box.h"
 #include "scene/gui/check_box.h"
 #include "scene/gui/separator.h"
 #include "scene/gui/separator.h"
 #include "scene/gui/view_panner.h"
 #include "scene/gui/view_panner.h"

+ 2 - 1
editor/plugins/texture_region_editor_plugin.h

@@ -40,6 +40,7 @@
 #include "scene/resources/texture.h"
 #include "scene/resources/texture.h"
 
 
 class ViewPanner;
 class ViewPanner;
+class EditorUndoRedoManager;
 
 
 class TextureRegionEditor : public AcceptDialog {
 class TextureRegionEditor : public AcceptDialog {
 	GDCLASS(TextureRegionEditor, AcceptDialog);
 	GDCLASS(TextureRegionEditor, AcceptDialog);
@@ -68,7 +69,7 @@ class TextureRegionEditor : public AcceptDialog {
 	VScrollBar *vscroll = nullptr;
 	VScrollBar *vscroll = nullptr;
 	HScrollBar *hscroll = nullptr;
 	HScrollBar *hscroll = nullptr;
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	Vector2 draw_ofs;
 	Vector2 draw_ofs;
 	float draw_zoom = 0.0;
 	float draw_zoom = 0.0;

+ 25 - 24
editor/plugins/theme_editor_plugin.cpp

@@ -35,6 +35,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_resource_picker.h"
 #include "editor/editor_resource_picker.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "editor/progress_dialog.h"
 #include "editor/progress_dialog.h"
 #include "scene/gui/color_picker.h"
 #include "scene/gui/color_picker.h"
 
 
@@ -796,7 +797,7 @@ void ThemeItemImportTree::_import_selected() {
 
 
 	ProgressDialog::get_singleton()->end_task("import_theme_items");
 	ProgressDialog::get_singleton()->end_task("import_theme_items");
 
 
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Import Theme Items"));
 	ur->create_action(TTR("Import Theme Items"));
 
 
 	ur->add_do_method(*edited_theme, "clear");
 	ur->add_do_method(*edited_theme, "clear");
@@ -1494,7 +1495,7 @@ void ThemeItemEditorDialog::_item_tree_button_pressed(Object *p_item, int p_colu
 			String item_name = item->get_text(0);
 			String item_name = item->get_text(0);
 			int data_type = item->get_parent()->get_metadata(0);
 			int data_type = item->get_parent()->get_metadata(0);
 
 
-			UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+			Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 			ur->create_action(TTR("Remove Theme Item"));
 			ur->create_action(TTR("Remove Theme Item"));
 			ur->add_do_method(*edited_theme, "clear_theme_item", (Theme::DataType)data_type, item_name, edited_item_type);
 			ur->add_do_method(*edited_theme, "clear_theme_item", (Theme::DataType)data_type, item_name, edited_item_type);
 			ur->add_undo_method(*edited_theme, "set_theme_item", (Theme::DataType)data_type, item_name, edited_item_type, edited_theme->get_theme_item((Theme::DataType)data_type, item_name, edited_item_type));
 			ur->add_undo_method(*edited_theme, "set_theme_item", (Theme::DataType)data_type, item_name, edited_item_type, edited_theme->get_theme_item((Theme::DataType)data_type, item_name, edited_item_type));
@@ -1513,7 +1514,7 @@ void ThemeItemEditorDialog::_add_theme_type(const String &p_new_text) {
 	const String new_type = edit_add_type_value->get_text().strip_edges();
 	const String new_type = edit_add_type_value->get_text().strip_edges();
 	edit_add_type_value->clear();
 	edit_add_type_value->clear();
 
 
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Add Theme Type"));
 	ur->create_action(TTR("Add Theme Type"));
 
 
 	ur->add_do_method(*edited_theme, "add_type", new_type);
 	ur->add_do_method(*edited_theme, "add_type", new_type);
@@ -1525,7 +1526,7 @@ void ThemeItemEditorDialog::_add_theme_type(const String &p_new_text) {
 }
 }
 
 
 void ThemeItemEditorDialog::_add_theme_item(Theme::DataType p_data_type, String p_item_name, String p_item_type) {
 void ThemeItemEditorDialog::_add_theme_item(Theme::DataType p_data_type, String p_item_name, String p_item_type) {
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Create Theme Item"));
 	ur->create_action(TTR("Create Theme Item"));
 
 
 	switch (p_data_type) {
 	switch (p_data_type) {
@@ -1570,7 +1571,7 @@ void ThemeItemEditorDialog::_remove_theme_type(const String &p_theme_type) {
 	Ref<Theme> old_snapshot = edited_theme->duplicate();
 	Ref<Theme> old_snapshot = edited_theme->duplicate();
 	Ref<Theme> new_snapshot = edited_theme->duplicate();
 	Ref<Theme> new_snapshot = edited_theme->duplicate();
 
 
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Remove Theme Type"));
 	ur->create_action(TTR("Remove Theme Type"));
 
 
 	new_snapshot->remove_type(p_theme_type);
 	new_snapshot->remove_type(p_theme_type);
@@ -1593,7 +1594,7 @@ void ThemeItemEditorDialog::_remove_data_type_items(Theme::DataType p_data_type,
 	Ref<Theme> old_snapshot = edited_theme->duplicate();
 	Ref<Theme> old_snapshot = edited_theme->duplicate();
 	Ref<Theme> new_snapshot = edited_theme->duplicate();
 	Ref<Theme> new_snapshot = edited_theme->duplicate();
 
 
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Remove Data Type Items From Theme"));
 	ur->create_action(TTR("Remove Data Type Items From Theme"));
 
 
 	new_snapshot->get_theme_item_list(p_data_type, p_item_type, &names);
 	new_snapshot->get_theme_item_list(p_data_type, p_item_type, &names);
@@ -1622,7 +1623,7 @@ void ThemeItemEditorDialog::_remove_class_items() {
 	Ref<Theme> old_snapshot = edited_theme->duplicate();
 	Ref<Theme> old_snapshot = edited_theme->duplicate();
 	Ref<Theme> new_snapshot = edited_theme->duplicate();
 	Ref<Theme> new_snapshot = edited_theme->duplicate();
 
 
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Remove Class Items From Theme"));
 	ur->create_action(TTR("Remove Class Items From Theme"));
 
 
 	for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) {
 	for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) {
@@ -1658,7 +1659,7 @@ void ThemeItemEditorDialog::_remove_custom_items() {
 	Ref<Theme> old_snapshot = edited_theme->duplicate();
 	Ref<Theme> old_snapshot = edited_theme->duplicate();
 	Ref<Theme> new_snapshot = edited_theme->duplicate();
 	Ref<Theme> new_snapshot = edited_theme->duplicate();
 
 
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Remove Custom Items From Theme"));
 	ur->create_action(TTR("Remove Custom Items From Theme"));
 
 
 	for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) {
 	for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) {
@@ -1694,7 +1695,7 @@ void ThemeItemEditorDialog::_remove_all_items() {
 	Ref<Theme> old_snapshot = edited_theme->duplicate();
 	Ref<Theme> old_snapshot = edited_theme->duplicate();
 	Ref<Theme> new_snapshot = edited_theme->duplicate();
 	Ref<Theme> new_snapshot = edited_theme->duplicate();
 
 
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Remove All Items From Theme"));
 	ur->create_action(TTR("Remove All Items From Theme"));
 
 
 	for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) {
 	for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) {
@@ -1798,7 +1799,7 @@ void ThemeItemEditorDialog::_confirm_edit_theme_item() {
 	if (item_popup_mode == CREATE_THEME_ITEM) {
 	if (item_popup_mode == CREATE_THEME_ITEM) {
 		_add_theme_item(edit_item_data_type, theme_item_name->get_text(), edited_item_type);
 		_add_theme_item(edit_item_data_type, theme_item_name->get_text(), edited_item_type);
 	} else if (item_popup_mode == RENAME_THEME_ITEM) {
 	} else if (item_popup_mode == RENAME_THEME_ITEM) {
-		UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+		Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 		ur->create_action(TTR("Rename Theme Item"));
 		ur->create_action(TTR("Rename Theme Item"));
 
 
 		ur->add_do_method(*edited_theme, "rename_theme_item", edit_item_data_type, edit_item_old_name, theme_item_name->get_text(), edited_item_type);
 		ur->add_do_method(*edited_theme, "rename_theme_item", edit_item_data_type, edit_item_old_name, theme_item_name->get_text(), edited_item_type);
@@ -2824,7 +2825,7 @@ void ThemeTypeEditor::_add_default_type_items() {
 
 
 	updating = false;
 	updating = false;
 
 
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Override All Default Theme Items"));
 	ur->create_action(TTR("Override All Default Theme Items"));
 
 
 	ur->add_do_method(*edited_theme, "merge_with", new_snapshot);
 	ur->add_do_method(*edited_theme, "merge_with", new_snapshot);
@@ -2844,7 +2845,7 @@ void ThemeTypeEditor::_item_add_cbk(int p_data_type, Control *p_control) {
 	}
 	}
 
 
 	String item_name = le->get_text().strip_edges();
 	String item_name = le->get_text().strip_edges();
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Add Theme Item"));
 	ur->create_action(TTR("Add Theme Item"));
 
 
 	switch (p_data_type) {
 	switch (p_data_type) {
@@ -2889,7 +2890,7 @@ void ThemeTypeEditor::_item_add_lineedit_cbk(String p_value, int p_data_type, Co
 }
 }
 
 
 void ThemeTypeEditor::_item_override_cbk(int p_data_type, String p_item_name) {
 void ThemeTypeEditor::_item_override_cbk(int p_data_type, String p_item_name) {
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Override Theme Item"));
 	ur->create_action(TTR("Override Theme Item"));
 
 
 	switch (p_data_type) {
 	switch (p_data_type) {
@@ -2928,7 +2929,7 @@ void ThemeTypeEditor::_item_override_cbk(int p_data_type, String p_item_name) {
 }
 }
 
 
 void ThemeTypeEditor::_item_remove_cbk(int p_data_type, String p_item_name) {
 void ThemeTypeEditor::_item_remove_cbk(int p_data_type, String p_item_name) {
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Remove Theme Item"));
 	ur->create_action(TTR("Remove Theme Item"));
 
 
 	switch (p_data_type) {
 	switch (p_data_type) {
@@ -3002,7 +3003,7 @@ void ThemeTypeEditor::_item_rename_confirmed(int p_data_type, String p_item_name
 		return;
 		return;
 	}
 	}
 
 
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Rename Theme Item"));
 	ur->create_action(TTR("Rename Theme Item"));
 
 
 	switch (p_data_type) {
 	switch (p_data_type) {
@@ -3058,7 +3059,7 @@ void ThemeTypeEditor::_item_rename_canceled(int p_data_type, String p_item_name,
 }
 }
 
 
 void ThemeTypeEditor::_color_item_changed(Color p_value, String p_item_name) {
 void ThemeTypeEditor::_color_item_changed(Color p_value, String p_item_name) {
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Set Color Item in Theme"), UndoRedo::MERGE_ENDS);
 	ur->create_action(TTR("Set Color Item in Theme"), UndoRedo::MERGE_ENDS);
 	ur->add_do_method(*edited_theme, "set_color", p_item_name, edited_type, p_value);
 	ur->add_do_method(*edited_theme, "set_color", p_item_name, edited_type, p_value);
 	ur->add_undo_method(*edited_theme, "set_color", p_item_name, edited_type, edited_theme->get_color(p_item_name, edited_type));
 	ur->add_undo_method(*edited_theme, "set_color", p_item_name, edited_type, edited_theme->get_color(p_item_name, edited_type));
@@ -3066,7 +3067,7 @@ void ThemeTypeEditor::_color_item_changed(Color p_value, String p_item_name) {
 }
 }
 
 
 void ThemeTypeEditor::_constant_item_changed(float p_value, String p_item_name) {
 void ThemeTypeEditor::_constant_item_changed(float p_value, String p_item_name) {
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Set Constant Item in Theme"));
 	ur->create_action(TTR("Set Constant Item in Theme"));
 	ur->add_do_method(*edited_theme, "set_constant", p_item_name, edited_type, p_value);
 	ur->add_do_method(*edited_theme, "set_constant", p_item_name, edited_type, p_value);
 	ur->add_undo_method(*edited_theme, "set_constant", p_item_name, edited_type, edited_theme->get_constant(p_item_name, edited_type));
 	ur->add_undo_method(*edited_theme, "set_constant", p_item_name, edited_type, edited_theme->get_constant(p_item_name, edited_type));
@@ -3074,7 +3075,7 @@ void ThemeTypeEditor::_constant_item_changed(float p_value, String p_item_name)
 }
 }
 
 
 void ThemeTypeEditor::_font_size_item_changed(float p_value, String p_item_name) {
 void ThemeTypeEditor::_font_size_item_changed(float p_value, String p_item_name) {
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Set Font Size Item in Theme"));
 	ur->create_action(TTR("Set Font Size Item in Theme"));
 	ur->add_do_method(*edited_theme, "set_font_size", p_item_name, edited_type, p_value);
 	ur->add_do_method(*edited_theme, "set_font_size", p_item_name, edited_type, p_value);
 	ur->add_undo_method(*edited_theme, "set_font_size", p_item_name, edited_type, edited_theme->get_font_size(p_item_name, edited_type));
 	ur->add_undo_method(*edited_theme, "set_font_size", p_item_name, edited_type, edited_theme->get_font_size(p_item_name, edited_type));
@@ -3086,7 +3087,7 @@ void ThemeTypeEditor::_edit_resource_item(Ref<Resource> p_resource, bool p_edit)
 }
 }
 
 
 void ThemeTypeEditor::_font_item_changed(Ref<Font> p_value, String p_item_name) {
 void ThemeTypeEditor::_font_item_changed(Ref<Font> p_value, String p_item_name) {
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Set Font Item in Theme"));
 	ur->create_action(TTR("Set Font Item in Theme"));
 
 
 	ur->add_do_method(*edited_theme, "set_font", p_item_name, edited_type, p_value.is_valid() ? p_value : Ref<Font>());
 	ur->add_do_method(*edited_theme, "set_font", p_item_name, edited_type, p_value.is_valid() ? p_value : Ref<Font>());
@@ -3103,7 +3104,7 @@ void ThemeTypeEditor::_font_item_changed(Ref<Font> p_value, String p_item_name)
 }
 }
 
 
 void ThemeTypeEditor::_icon_item_changed(Ref<Texture2D> p_value, String p_item_name) {
 void ThemeTypeEditor::_icon_item_changed(Ref<Texture2D> p_value, String p_item_name) {
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Set Icon Item in Theme"));
 	ur->create_action(TTR("Set Icon Item in Theme"));
 
 
 	ur->add_do_method(*edited_theme, "set_icon", p_item_name, edited_type, p_value.is_valid() ? p_value : Ref<Texture2D>());
 	ur->add_do_method(*edited_theme, "set_icon", p_item_name, edited_type, p_value.is_valid() ? p_value : Ref<Texture2D>());
@@ -3120,7 +3121,7 @@ void ThemeTypeEditor::_icon_item_changed(Ref<Texture2D> p_value, String p_item_n
 }
 }
 
 
 void ThemeTypeEditor::_stylebox_item_changed(Ref<StyleBox> p_value, String p_item_name) {
 void ThemeTypeEditor::_stylebox_item_changed(Ref<StyleBox> p_value, String p_item_name) {
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Set Stylebox Item in Theme"));
 	ur->create_action(TTR("Set Stylebox Item in Theme"));
 
 
 	ur->add_do_method(*edited_theme, "set_stylebox", p_item_name, edited_type, p_value.is_valid() ? p_value : Ref<StyleBox>());
 	ur->add_do_method(*edited_theme, "set_stylebox", p_item_name, edited_type, p_value.is_valid() ? p_value : Ref<StyleBox>());
@@ -3163,7 +3164,7 @@ void ThemeTypeEditor::_on_pin_leader_button_pressed(Control *p_editor, String p_
 		stylebox = Object::cast_to<EditorResourcePicker>(p_editor)->get_edited_resource();
 		stylebox = Object::cast_to<EditorResourcePicker>(p_editor)->get_edited_resource();
 	}
 	}
 
 
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Pin Stylebox"));
 	ur->create_action(TTR("Pin Stylebox"));
 	ur->add_do_method(this, "_pin_leading_stylebox", p_item_name, stylebox);
 	ur->add_do_method(this, "_pin_leading_stylebox", p_item_name, stylebox);
 
 
@@ -3196,7 +3197,7 @@ void ThemeTypeEditor::_pin_leading_stylebox(String p_item_name, Ref<StyleBox> p_
 }
 }
 
 
 void ThemeTypeEditor::_on_unpin_leader_button_pressed() {
 void ThemeTypeEditor::_on_unpin_leader_button_pressed() {
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Unpin Stylebox"));
 	ur->create_action(TTR("Unpin Stylebox"));
 	ur->add_do_method(this, "_unpin_leading_stylebox");
 	ur->add_do_method(this, "_unpin_leading_stylebox");
 	ur->add_undo_method(this, "_pin_leading_stylebox", leading_stylebox.item_name, leading_stylebox.stylebox);
 	ur->add_undo_method(this, "_pin_leading_stylebox", leading_stylebox.item_name, leading_stylebox.stylebox);
@@ -3265,7 +3266,7 @@ void ThemeTypeEditor::_update_stylebox_from_leading() {
 }
 }
 
 
 void ThemeTypeEditor::_type_variation_changed(const String p_value) {
 void ThemeTypeEditor::_type_variation_changed(const String p_value) {
-	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
 	ur->create_action(TTR("Set Theme Type Variation"));
 	ur->create_action(TTR("Set Theme Type Variation"));
 
 
 	if (p_value.is_empty()) {
 	if (p_value.is_empty()) {

+ 1 - 0
editor/plugins/tiles/atlas_merging_dialog.cpp

@@ -33,6 +33,7 @@
 #include "editor/editor_file_dialog.h"
 #include "editor/editor_file_dialog.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
+#include "editor/editor_undo_redo_manager.h"
 
 
 #include "scene/gui/control.h"
 #include "scene/gui/control.h"
 #include "scene/gui/split_container.h"
 #include "scene/gui/split_container.h"

+ 2 - 1
editor/plugins/tiles/atlas_merging_dialog.h

@@ -38,6 +38,7 @@
 #include "scene/resources/tile_set.h"
 #include "scene/resources/tile_set.h"
 
 
 class EditorFileDialog;
 class EditorFileDialog;
+class EditorUndoRedoManager;
 
 
 class AtlasMergingDialog : public ConfirmationDialog {
 class AtlasMergingDialog : public ConfirmationDialog {
 	GDCLASS(AtlasMergingDialog, ConfirmationDialog);
 	GDCLASS(AtlasMergingDialog, ConfirmationDialog);
@@ -49,7 +50,7 @@ private:
 	LocalVector<HashMap<Vector2i, Vector2i>> merged_mapping;
 	LocalVector<HashMap<Vector2i, Vector2i>> merged_mapping;
 	Ref<TileSet> tile_set;
 	Ref<TileSet> tile_set;
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	// Settings.
 	// Settings.
 	int next_line_after_column = 30;
 	int next_line_after_column = 30;

+ 17 - 9
editor/plugins/tiles/tile_data_editors.cpp

@@ -38,6 +38,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_properties.h"
 #include "editor/editor_properties.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
+#include "editor/editor_undo_redo_manager.h"
 
 
 void TileDataEditor::_tile_set_changed_plan_update() {
 void TileDataEditor::_tile_set_changed_plan_update() {
 	_tile_set_changed_update_needed = true;
 	_tile_set_changed_update_needed = true;
@@ -248,7 +249,14 @@ void GenericTilePolygonEditor::_zoom_changed() {
 }
 }
 
 
 void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) {
 void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) {
-	UndoRedo *undo_redo = use_undo_redo ? editor_undo_redo : memnew(UndoRedo);
+	Ref<EditorUndoRedoManager> undo_redo;
+	if (use_undo_redo) {
+		undo_redo = editor_undo_redo;
+	} else {
+		// This nice hack allows for discarding undo actions without making code too complex.
+		undo_redo.instantiate();
+	}
+
 	switch (p_item_pressed) {
 	switch (p_item_pressed) {
 		case RESET_TO_DEFAULT_TILE: {
 		case RESET_TO_DEFAULT_TILE: {
 			undo_redo->create_action(TTR("Reset Polygons"));
 			undo_redo->create_action(TTR("Reset Polygons"));
@@ -322,9 +330,6 @@ void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) {
 		default:
 		default:
 			break;
 			break;
 	}
 	}
-	if (!use_undo_redo) {
-		memdelete(undo_redo);
-	}
 }
 }
 
 
 void GenericTilePolygonEditor::_grab_polygon_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_point_index) {
 void GenericTilePolygonEditor::_grab_polygon_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_point_index) {
@@ -409,7 +414,14 @@ void GenericTilePolygonEditor::_snap_to_half_pixel(Point2 &r_point) {
 }
 }
 
 
 void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event) {
 void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event) {
-	UndoRedo *undo_redo = use_undo_redo ? editor_undo_redo : memnew(UndoRedo);
+	Ref<EditorUndoRedoManager> undo_redo;
+	if (use_undo_redo) {
+		undo_redo = editor_undo_redo;
+	} else {
+		// This nice hack allows for discarding undo actions without making code too complex.
+		undo_redo.instantiate();
+	}
+
 	real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
 	real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
 
 
 	hovered_polygon_index = -1;
 	hovered_polygon_index = -1;
@@ -600,10 +612,6 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event)
 	}
 	}
 
 
 	base_control->update();
 	base_control->update();
-
-	if (!use_undo_redo) {
-		memdelete(undo_redo);
-	}
 }
 }
 
 
 void GenericTilePolygonEditor::set_use_undo_redo(bool p_use_undo_redo) {
 void GenericTilePolygonEditor::set_use_undo_redo(bool p_use_undo_redo) {

+ 8 - 6
editor/plugins/tiles/tile_data_editors.h

@@ -39,6 +39,8 @@
 #include "scene/gui/control.h"
 #include "scene/gui/control.h"
 #include "scene/gui/label.h"
 #include "scene/gui/label.h"
 
 
+class EditorUndoRedoManager;
+
 class TileDataEditor : public VBoxContainer {
 class TileDataEditor : public VBoxContainer {
 	GDCLASS(TileDataEditor, VBoxContainer);
 	GDCLASS(TileDataEditor, VBoxContainer);
 
 
@@ -93,7 +95,7 @@ private:
 	bool multiple_polygon_mode = false;
 	bool multiple_polygon_mode = false;
 
 
 	bool use_undo_redo = true;
 	bool use_undo_redo = true;
-	UndoRedo *editor_undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> editor_undo_redo;
 
 
 	// UI
 	// UI
 	int hovered_polygon_index = -1;
 	int hovered_polygon_index = -1;
@@ -214,7 +216,7 @@ private:
 protected:
 protected:
 	DummyObject *dummy_object = memnew(DummyObject);
 	DummyObject *dummy_object = memnew(DummyObject);
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	StringName type;
 	StringName type;
 	String property;
 	String property;
@@ -279,7 +281,7 @@ private:
 	virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) override;
 	virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) override;
 
 
 protected:
 protected:
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	virtual void _tile_set_changed() override;
 	virtual void _tile_set_changed() override;
 
 
@@ -314,7 +316,7 @@ class TileDataCollisionEditor : public TileDataDefaultEditor {
 	virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) override;
 	virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) override;
 
 
 protected:
 protected:
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	virtual void _tile_set_changed() override;
 	virtual void _tile_set_changed() override;
 
 
@@ -366,7 +368,7 @@ protected:
 
 
 	void _notification(int p_what);
 	void _notification(int p_what);
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 public:
 public:
 	virtual Control *get_toolbar() override { return toolbar; };
 	virtual Control *get_toolbar() override { return toolbar; };
@@ -399,7 +401,7 @@ private:
 	virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) override;
 	virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) override;
 
 
 protected:
 protected:
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	virtual void _tile_set_changed() override;
 	virtual void _tile_set_changed() override;
 
 

+ 3 - 2
editor/plugins/tiles/tile_map_editor.cpp

@@ -35,6 +35,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_resource_preview.h"
 #include "editor/editor_resource_preview.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
+#include "editor/editor_undo_redo_manager.h"
 #include "editor/plugins/canvas_item_editor_plugin.h"
 #include "editor/plugins/canvas_item_editor_plugin.h"
 
 
 #include "scene/2d/camera_2d.h"
 #include "scene/2d/camera_2d.h"
@@ -3697,8 +3698,8 @@ void TileMapEditor::_update_layers_selection() {
 }
 }
 
 
 void TileMapEditor::_move_tile_map_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) {
 void TileMapEditor::_move_tile_map_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) {
-	UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo);
-	ERR_FAIL_COND(!undo_redo);
+	Ref<EditorUndoRedoManager> undo_redo = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
+	ERR_FAIL_COND(undo_redo.is_null());
 
 
 	TileMap *tile_map = Object::cast_to<TileMap>(p_edited);
 	TileMap *tile_map = Object::cast_to<TileMap>(p_edited);
 	if (!tile_map) {
 	if (!tile_map) {

+ 4 - 4
editor/plugins/tiles/tile_map_editor.h

@@ -47,7 +47,7 @@
 #include "scene/gui/tab_bar.h"
 #include "scene/gui/tab_bar.h"
 #include "scene/gui/tree.h"
 #include "scene/gui/tree.h"
 
 
-class UndoRedo;
+class EditorUndoRedoManager;
 
 
 class TileMapEditorPlugin : public Object {
 class TileMapEditorPlugin : public Object {
 public:
 public:
@@ -70,7 +70,7 @@ class TileMapEditorTilesPlugin : public TileMapEditorPlugin {
 	GDCLASS(TileMapEditorTilesPlugin, TileMapEditorPlugin);
 	GDCLASS(TileMapEditorTilesPlugin, TileMapEditorPlugin);
 
 
 private:
 private:
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 	ObjectID tile_map_id;
 	ObjectID tile_map_id;
 	int tile_map_layer = -1;
 	int tile_map_layer = -1;
 	virtual void edit(ObjectID p_tile_map_id, int p_tile_map_layer) override;
 	virtual void edit(ObjectID p_tile_map_id, int p_tile_map_layer) override;
@@ -223,7 +223,7 @@ class TileMapEditorTerrainsPlugin : public TileMapEditorPlugin {
 	GDCLASS(TileMapEditorTerrainsPlugin, TileMapEditorPlugin);
 	GDCLASS(TileMapEditorTerrainsPlugin, TileMapEditorPlugin);
 
 
 private:
 private:
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 	ObjectID tile_map_id;
 	ObjectID tile_map_id;
 	int tile_map_layer = -1;
 	int tile_map_layer = -1;
 	virtual void edit(ObjectID p_tile_map_id, int p_tile_map_layer) override;
 	virtual void edit(ObjectID p_tile_map_id, int p_tile_map_layer) override;
@@ -317,7 +317,7 @@ class TileMapEditor : public VBoxContainer {
 	GDCLASS(TileMapEditor, VBoxContainer);
 	GDCLASS(TileMapEditor, VBoxContainer);
 
 
 private:
 private:
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 	bool tileset_changed_needs_update = false;
 	bool tileset_changed_needs_update = false;
 	ObjectID tile_map_id;
 	ObjectID tile_map_id;
 	int tile_map_layer = -1;
 	int tile_map_layer = -1;

+ 1 - 0
editor/plugins/tiles/tile_proxies_manager_dialog.cpp

@@ -32,6 +32,7 @@
 
 
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
+#include "editor/editor_undo_redo_manager.h"
 
 
 void TileProxiesManagerDialog::_right_clicked(int p_item, Vector2 p_local_mouse_pos, Object *p_item_list, MouseButton p_mouse_button_index) {
 void TileProxiesManagerDialog::_right_clicked(int p_item, Vector2 p_local_mouse_pos, Object *p_item_list, MouseButton p_mouse_button_index) {
 	if (p_mouse_button_index != MouseButton::RIGHT) {
 	if (p_mouse_button_index != MouseButton::RIGHT) {

+ 1 - 1
editor/plugins/tiles/tile_proxies_manager_dialog.h

@@ -43,7 +43,7 @@ private:
 	int commited_actions_count = 0;
 	int commited_actions_count = 0;
 	Ref<TileSet> tile_set;
 	Ref<TileSet> tile_set;
 
 
-	UndoRedo *undo_redo = nullptr;
+	Ref<EditorUndoRedoManager> undo_redo;
 
 
 	TileMapCell from;
 	TileMapCell from;
 	TileMapCell to;
 	TileMapCell to;

+ 10 - 4
editor/plugins/tiles/tile_set_atlas_source_editor.cpp

@@ -2058,14 +2058,16 @@ void TileSetAtlasSourceEditor::_atlas_source_proxy_object_changed(String p_what)
 }
 }
 
 
 void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) {
 void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) {
-	UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo);
-	ERR_FAIL_COND(!undo_redo);
+	Ref<EditorUndoRedoManager> undo_redo = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
+	ERR_FAIL_COND(!undo_redo.is_valid());
 
 
 #define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, obj->get(property));
 #define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, obj->get(property));
 
 
-	undo_redo->start_force_keep_in_merge_ends();
 	AtlasTileProxyObject *tile_data_proxy = Object::cast_to<AtlasTileProxyObject>(p_edited);
 	AtlasTileProxyObject *tile_data_proxy = Object::cast_to<AtlasTileProxyObject>(p_edited);
 	if (tile_data_proxy) {
 	if (tile_data_proxy) {
+		UndoRedo *internal_undo_redo = undo_redo->get_history_for_object(tile_data_proxy).undo_redo;
+		internal_undo_redo->start_force_keep_in_merge_ends();
+
 		Vector<String> components = String(p_property).split("/", true, 2);
 		Vector<String> components = String(p_property).split("/", true, 2);
 		if (components.size() == 2 && components[1] == "polygons_count") {
 		if (components.size() == 2 && components[1] == "polygons_count") {
 			int layer_index = components[0].trim_prefix("physics_layer_").to_int();
 			int layer_index = components[0].trim_prefix("physics_layer_").to_int();
@@ -2088,6 +2090,7 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo
 				}
 				}
 			}
 			}
 		}
 		}
+		internal_undo_redo->end_force_keep_in_merge_ends();
 	}
 	}
 
 
 	TileSetAtlasSourceProxyObject *atlas_source_proxy = Object::cast_to<TileSetAtlasSourceProxyObject>(p_edited);
 	TileSetAtlasSourceProxyObject *atlas_source_proxy = Object::cast_to<TileSetAtlasSourceProxyObject>(p_edited);
@@ -2095,6 +2098,9 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo
 		TileSetAtlasSource *atlas_source = atlas_source_proxy->get_edited();
 		TileSetAtlasSource *atlas_source = atlas_source_proxy->get_edited();
 		ERR_FAIL_COND(!atlas_source);
 		ERR_FAIL_COND(!atlas_source);
 
 
+		UndoRedo *internal_undo_redo = undo_redo->get_history_for_object(atlas_source).undo_redo;
+		internal_undo_redo->start_force_keep_in_merge_ends();
+
 		PackedVector2Array arr;
 		PackedVector2Array arr;
 		if (p_property == "texture") {
 		if (p_property == "texture") {
 			arr = atlas_source->get_tiles_to_be_removed_on_change(p_new_value, atlas_source->get_margins(), atlas_source->get_separation(), atlas_source->get_texture_region_size());
 			arr = atlas_source->get_tiles_to_be_removed_on_change(p_new_value, atlas_source->get_margins(), atlas_source->get_separation(), atlas_source->get_texture_region_size());
@@ -2121,8 +2127,8 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo
 				}
 				}
 			}
 			}
 		}
 		}
+		internal_undo_redo->end_force_keep_in_merge_ends();
 	}
 	}
-	undo_redo->end_force_keep_in_merge_ends();
 
 
 #undef ADD_UNDO
 #undef ADD_UNDO
 }
 }

Some files were not shown because too many files changed in this diff