Przeglądaj źródła

UndoRedo is no longer a singleton but can also be instantiated, for use in local undo/redo stacks

BearishSun 9 lat temu
rodzic
commit
3db090c24b

+ 1 - 1
Source/BansheeEditor/Include/BsCmdInputFieldValueChange.h

@@ -35,7 +35,7 @@ namespace BansheeEngine
 				new (bs_alloc<CmdInputFieldValueChange>()) CmdInputFieldValueChange(description, inputField, value);
 				new (bs_alloc<CmdInputFieldValueChange>()) CmdInputFieldValueChange(description, inputField, value);
 			SPtr<CmdInputFieldValueChange> commandPtr = bs_shared_ptr(command);
 			SPtr<CmdInputFieldValueChange> commandPtr = bs_shared_ptr(command);
 
 
-			UndoRedo::instance().registerCommand(commandPtr);
+			GlobalUndoRedo::instance().registerCommand(commandPtr);
 			commandPtr->commit();
 			commandPtr->commit();
 		}
 		}
 
 

+ 5 - 1
Source/BansheeEditor/Include/BsUndoRedo.h

@@ -12,7 +12,7 @@ namespace BansheeEngine
 	 */
 	 */
 
 
 	/**	Provides functionality to undo or redo recently performed operations in the editor. */
 	/**	Provides functionality to undo or redo recently performed operations in the editor. */
-	class BS_ED_EXPORT UndoRedo : public Module<UndoRedo>
+	class BS_ED_EXPORT UndoRedo
 	{
 	{
 		/**
 		/**
 		 * Contains data about a single undo/redo group. Groups allow you to create context sensitive undo/redo operations.
 		 * Contains data about a single undo/redo group. Groups allow you to create context sensitive undo/redo operations.
@@ -97,5 +97,9 @@ namespace BansheeEngine
 		Stack<GroupData> mGroups;
 		Stack<GroupData> mGroups;
 	};
 	};
 
 
+	/**	Provides access to the global undo/redo stack. See UndoRedo. */
+	class BS_ED_EXPORT GlobalUndoRedo : public Module<UndoRedo>
+	{ };
+
 	/** @} */
 	/** @} */
 }
 }

+ 1 - 1
Source/BansheeEditor/Source/BsCmdBreakPrefab.cpp

@@ -23,7 +23,7 @@ namespace BansheeEngine
 		CmdBreakPrefab* command = new (bs_alloc<CmdBreakPrefab>()) CmdBreakPrefab(description, sceneObject);
 		CmdBreakPrefab* command = new (bs_alloc<CmdBreakPrefab>()) CmdBreakPrefab(description, sceneObject);
 		SPtr<CmdBreakPrefab> commandPtr = bs_shared_ptr(command);
 		SPtr<CmdBreakPrefab> commandPtr = bs_shared_ptr(command);
 
 
-		UndoRedo::instance().registerCommand(commandPtr);
+		GlobalUndoRedo::instance().registerCommand(commandPtr);
 		commandPtr->commit();
 		commandPtr->commit();
 	}
 	}
 
 

+ 2 - 2
Source/BansheeEditor/Source/BsCmdCloneSO.cpp

@@ -22,7 +22,7 @@ namespace BansheeEngine
 		CmdCloneSO* command = new (bs_alloc<CmdCloneSO>()) CmdCloneSO(description, { sceneObject });
 		CmdCloneSO* command = new (bs_alloc<CmdCloneSO>()) CmdCloneSO(description, { sceneObject });
 		SPtr<CmdCloneSO> commandPtr = bs_shared_ptr(command);
 		SPtr<CmdCloneSO> commandPtr = bs_shared_ptr(command);
 
 
-		UndoRedo::instance().registerCommand(commandPtr);
+		GlobalUndoRedo::instance().registerCommand(commandPtr);
 		commandPtr->commit();
 		commandPtr->commit();
 
 
 		if (commandPtr->mClones.size() > 0)
 		if (commandPtr->mClones.size() > 0)
@@ -37,7 +37,7 @@ namespace BansheeEngine
 		CmdCloneSO* command = new (bs_alloc<CmdCloneSO>()) CmdCloneSO(description, sceneObjects);
 		CmdCloneSO* command = new (bs_alloc<CmdCloneSO>()) CmdCloneSO(description, sceneObjects);
 		SPtr<CmdCloneSO> commandPtr = bs_shared_ptr(command);
 		SPtr<CmdCloneSO> commandPtr = bs_shared_ptr(command);
 
 
-		UndoRedo::instance().registerCommand(commandPtr);
+		GlobalUndoRedo::instance().registerCommand(commandPtr);
 		commandPtr->commit();
 		commandPtr->commit();
 
 
 		return commandPtr->mClones;
 		return commandPtr->mClones;

+ 1 - 1
Source/BansheeEditor/Source/BsCmdCreateSO.cpp

@@ -22,7 +22,7 @@ namespace BansheeEngine
 		CmdCreateSO* command = new (bs_alloc<CmdCreateSO>()) CmdCreateSO(description, name, flags);
 		CmdCreateSO* command = new (bs_alloc<CmdCreateSO>()) CmdCreateSO(description, name, flags);
 		SPtr<CmdCreateSO> commandPtr = bs_shared_ptr(command);
 		SPtr<CmdCreateSO> commandPtr = bs_shared_ptr(command);
 
 
-		UndoRedo::instance().registerCommand(commandPtr);
+		GlobalUndoRedo::instance().registerCommand(commandPtr);
 		commandPtr->commit();
 		commandPtr->commit();
 
 
 		return commandPtr->mSceneObject;
 		return commandPtr->mSceneObject;

+ 1 - 1
Source/BansheeEditor/Source/BsCmdDeleteSO.cpp

@@ -38,7 +38,7 @@ namespace BansheeEngine
 		CmdDeleteSO* command = new (bs_alloc<CmdDeleteSO>()) CmdDeleteSO(description, sceneObject);
 		CmdDeleteSO* command = new (bs_alloc<CmdDeleteSO>()) CmdDeleteSO(description, sceneObject);
 		SPtr<CmdDeleteSO> commandPtr = bs_shared_ptr(command);
 		SPtr<CmdDeleteSO> commandPtr = bs_shared_ptr(command);
 
 
-		UndoRedo::instance().registerCommand(commandPtr);
+		GlobalUndoRedo::instance().registerCommand(commandPtr);
 		commandPtr->commit();
 		commandPtr->commit();
 	}
 	}
 
 

+ 1 - 1
Source/BansheeEditor/Source/BsCmdInstantiateSO.cpp

@@ -23,7 +23,7 @@ namespace BansheeEngine
 		CmdInstantiateSO* command = new (bs_alloc<CmdInstantiateSO>()) CmdInstantiateSO(description, prefab);
 		CmdInstantiateSO* command = new (bs_alloc<CmdInstantiateSO>()) CmdInstantiateSO(description, prefab);
 		SPtr<CmdInstantiateSO> commandPtr = bs_shared_ptr(command);
 		SPtr<CmdInstantiateSO> commandPtr = bs_shared_ptr(command);
 
 
-		UndoRedo::instance().registerCommand(commandPtr);
+		GlobalUndoRedo::instance().registerCommand(commandPtr);
 		commandPtr->commit();
 		commandPtr->commit();
 
 
 		return commandPtr->mSceneObject;
 		return commandPtr->mSceneObject;

+ 1 - 1
Source/BansheeEditor/Source/BsCmdRecordSO.cpp

@@ -37,7 +37,7 @@ namespace BansheeEngine
 		CmdRecordSO* command = new (bs_alloc<CmdRecordSO>()) CmdRecordSO(description, sceneObject, recordHierarchy);
 		CmdRecordSO* command = new (bs_alloc<CmdRecordSO>()) CmdRecordSO(description, sceneObject, recordHierarchy);
 		SPtr<CmdRecordSO> commandPtr = bs_shared_ptr(command);
 		SPtr<CmdRecordSO> commandPtr = bs_shared_ptr(command);
 
 
-		UndoRedo::instance().registerCommand(commandPtr);
+		GlobalUndoRedo::instance().registerCommand(commandPtr);
 		commandPtr->commit();
 		commandPtr->commit();
 	}
 	}
 
 

+ 2 - 2
Source/BansheeEditor/Source/BsCmdReparentSO.cpp

@@ -21,7 +21,7 @@ namespace BansheeEngine
 		CmdReparentSO* command = new (bs_alloc<CmdReparentSO>()) CmdReparentSO(description, sceneObjects, newParent);
 		CmdReparentSO* command = new (bs_alloc<CmdReparentSO>()) CmdReparentSO(description, sceneObjects, newParent);
 		SPtr<CmdReparentSO> commandPtr = bs_shared_ptr(command);
 		SPtr<CmdReparentSO> commandPtr = bs_shared_ptr(command);
 
 
-		UndoRedo::instance().registerCommand(commandPtr);
+		GlobalUndoRedo::instance().registerCommand(commandPtr);
 		commandPtr->commit();
 		commandPtr->commit();
 	}
 	}
 
 
@@ -31,7 +31,7 @@ namespace BansheeEngine
 		CmdReparentSO* command = new (bs_alloc<CmdReparentSO>()) CmdReparentSO(description, { sceneObject }, newParent);
 		CmdReparentSO* command = new (bs_alloc<CmdReparentSO>()) CmdReparentSO(description, { sceneObject }, newParent);
 		SPtr<CmdReparentSO> commandPtr = bs_shared_ptr(command);
 		SPtr<CmdReparentSO> commandPtr = bs_shared_ptr(command);
 
 
-		UndoRedo::instance().registerCommand(commandPtr);
+		GlobalUndoRedo::instance().registerCommand(commandPtr);
 		commandPtr->commit();
 		commandPtr->commit();
 	}
 	}
 
 

+ 3 - 3
Source/BansheeEditor/Source/BsEditorApplication.cpp

@@ -103,7 +103,7 @@ namespace BansheeEngine
 
 
 		ProjectLibrary::startUp();
 		ProjectLibrary::startUp();
 
 
-		UndoRedo::startUp();
+		GlobalUndoRedo::startUp();
 		EditorWindowManager::startUp();
 		EditorWindowManager::startUp();
 		EditorWidgetManager::startUp();
 		EditorWidgetManager::startUp();
 		DropDownWindowManager::startUp();
 		DropDownWindowManager::startUp();
@@ -133,7 +133,7 @@ namespace BansheeEngine
 		DropDownWindowManager::shutDown();
 		DropDownWindowManager::shutDown();
 		EditorWidgetManager::shutDown();
 		EditorWidgetManager::shutDown();
 		EditorWindowManager::shutDown();
 		EditorWindowManager::shutDown();
-		UndoRedo::shutDown();
+		GlobalUndoRedo::shutDown();
 
 
 		Application::onShutDown();
 		Application::onShutDown();
 	}
 	}
@@ -232,7 +232,7 @@ namespace BansheeEngine
 
 
 		mProjectSettings = bs_shared_ptr_new<ProjectSettings>();
 		mProjectSettings = bs_shared_ptr_new<ProjectSettings>();
 		BuildManager::instance().clear();
 		BuildManager::instance().clear();
-		UndoRedo::instance().clear();
+		GlobalUndoRedo::instance().clear();
 
 
 		EditorWidgetManager::instance().closeAll();
 		EditorWidgetManager::instance().closeAll();
 		gProjectLibrary().unloadLibrary();
 		gProjectLibrary().unloadLibrary();

+ 2 - 2
Source/BansheeEditor/Source/BsEditorTestSuite.cpp

@@ -441,7 +441,7 @@ namespace BansheeEngine
 		CmdRecordSO::execute(so0_0);
 		CmdRecordSO::execute(so0_0);
 		cmpB1_1->val1 = "ModifiedValue";
 		cmpB1_1->val1 = "ModifiedValue";
 		so0_0->setName("modified");
 		so0_0->setName("modified");
-		UndoRedo::instance().undo();
+		GlobalUndoRedo::instance().undo();
 
 
 		BS_TEST_ASSERT(!so0_0.isDestroyed());
 		BS_TEST_ASSERT(!so0_0.isDestroyed());
 		BS_TEST_ASSERT(!so1_0.isDestroyed());
 		BS_TEST_ASSERT(!so1_0.isDestroyed());
@@ -486,7 +486,7 @@ namespace BansheeEngine
 		cmpExternal->ref2 = cmpA1_1;
 		cmpExternal->ref2 = cmpA1_1;
 
 
 		CmdDeleteSO::execute(so0_0);
 		CmdDeleteSO::execute(so0_0);
-		UndoRedo::instance().undo();
+		GlobalUndoRedo::instance().undo();
 
 
 		BS_TEST_ASSERT(!so0_0.isDestroyed());
 		BS_TEST_ASSERT(!so0_0.isDestroyed());
 		BS_TEST_ASSERT(!so1_0.isDestroyed());
 		BS_TEST_ASSERT(!so1_0.isDestroyed());

+ 2 - 2
Source/BansheeEditor/Source/BsGUIFloatField.cpp

@@ -211,14 +211,14 @@ namespace BansheeEngine
 	{
 	{
 		if (focus)
 		if (focus)
 		{
 		{
-			UndoRedo::instance().pushGroup("InputBox");
+			GlobalUndoRedo::instance().pushGroup("InputBox");
 
 
 			mHasInputFocus = true;
 			mHasInputFocus = true;
 			onFocusChanged(true);
 			onFocusChanged(true);
 		}
 		}
 		else
 		else
 		{
 		{
-			UndoRedo::instance().popGroup("InputBox");
+			GlobalUndoRedo::instance().popGroup("InputBox");
 
 
 			setText(applyRangeAndStep(mValue));
 			setText(applyRangeAndStep(mValue));
 
 

+ 2 - 2
Source/BansheeEditor/Source/BsGUIIntField.cpp

@@ -229,14 +229,14 @@ namespace BansheeEngine
 	{
 	{
 		if (focus)
 		if (focus)
 		{
 		{
-			UndoRedo::instance().pushGroup("InputBox");
+			GlobalUndoRedo::instance().pushGroup("InputBox");
 
 
 			mHasInputFocus = true;
 			mHasInputFocus = true;
 			onFocusChanged(true);
 			onFocusChanged(true);
 		}
 		}
 		else
 		else
 		{
 		{
-			UndoRedo::instance().popGroup("InputBox");
+			GlobalUndoRedo::instance().popGroup("InputBox");
 
 
 			setText(applyRangeAndStep(mValue));
 			setText(applyRangeAndStep(mValue));
 
 

+ 2 - 2
Source/BansheeEditor/Source/BsGUISliderField.cpp

@@ -147,14 +147,14 @@ namespace BansheeEngine
 	{
 	{
 		if (focus)
 		if (focus)
 		{
 		{
-			UndoRedo::instance().pushGroup("InputBox");
+			GlobalUndoRedo::instance().pushGroup("InputBox");
 
 
 			mHasInputFocus = true;
 			mHasInputFocus = true;
 			onFocusChanged(true);
 			onFocusChanged(true);
 		}
 		}
 		else
 		else
 		{
 		{
-			UndoRedo::instance().popGroup("InputBox");
+			GlobalUndoRedo::instance().popGroup("InputBox");
 			inputBoxValueChanged();
 			inputBoxValueChanged();
 
 
 			mHasInputFocus = false;
 			mHasInputFocus = false;

+ 2 - 2
Source/BansheeEditor/Source/BsGUITextField.cpp

@@ -197,14 +197,14 @@ namespace BansheeEngine
 	{
 	{
 		if (focus)
 		if (focus)
 		{
 		{
-			UndoRedo::instance().pushGroup("InputBox");
+			GlobalUndoRedo::instance().pushGroup("InputBox");
 
 
 			mHasInputFocus = true;
 			mHasInputFocus = true;
 			onFocusChanged(true);
 			onFocusChanged(true);
 		}
 		}
 		else
 		else
 		{
 		{
-			UndoRedo::instance().popGroup("InputBox");
+			GlobalUndoRedo::instance().popGroup("InputBox");
 
 
 			mHasInputFocus = false;
 			mHasInputFocus = false;
 			onFocusChanged(false);
 			onFocusChanged(false);

+ 81 - 52
Source/MBansheeEditor/Utility/UndoRedo.cs

@@ -14,40 +14,90 @@ namespace BansheeEditor
     /// <summary>
     /// <summary>
     /// Provides functionality to undo or redo recently performed operations in the editor. All commands executed from this
     /// Provides functionality to undo or redo recently performed operations in the editor. All commands executed from this
     /// class are undoable/redoable.
     /// class are undoable/redoable.
+    /// 
+    /// The class provides static methods that access the global undo/redo stack, but can also be instantiated to provide
+    /// local undo/redo stacks.
     /// </summary>
     /// </summary>
-    public static class UndoRedo
+    public class UndoRedo : ScriptObject
     {
     {
+        /// <summary>
+        /// Constructor for internal runtime use.
+        /// </summary>
+        /// <param name="dummy">Dummy parameter to distinguish from public constructor.</param>
+        private UndoRedo(bool dummy)
+        { }
+
+        /// <summary>
+        /// Creates a new undo/redo stack.
+        /// </summary>
+        public UndoRedo()
+        {
+            Internal_CreateInstance(this);
+        }
+
+        /// <summary>
+        /// Returns the global undo/redo stack.
+        /// </summary>
+        public static UndoRedo Global
+        {
+            get { return Internal_GetGlobal(); }
+        }
+
         /// <summary>
         /// <summary>
         /// Returns the unique identifier of the command currently at the top of the undo stack.
         /// Returns the unique identifier of the command currently at the top of the undo stack.
         /// </summary>
         /// </summary>
-        public static int TopCommandId
+        public int TopCommandId
         {
         {
-            get { return Internal_GetTopCommandId(); }
+            get { return Internal_GetTopCommandId(mCachedPtr); }
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Executes the last command on the undo stack, undoing its operations.
         /// Executes the last command on the undo stack, undoing its operations.
         /// </summary>
         /// </summary>
-        [MenuItem("Edit/Undo", 9500, true)]
-        [ToolbarItem("Undo", ToolbarIcon.Undo, "Undo (Ctrl + Z)", 1900, true)]
-        public static void Undo()
+        public void Undo()
         {
         {
-            Internal_Undo();
+            Internal_Undo(mCachedPtr);
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Executes the last command on the redo stack (last command we called undo on), re-applying its operation.
         /// Executes the last command on the redo stack (last command we called undo on), re-applying its operation.
         /// </summary>
         /// </summary>
-        [MenuItem("Edit/Redo", 9499)]
-        [ToolbarItem("Redo", ToolbarIcon.Redo, "Redo (Ctrl + Y)", 1899)]
-        public static void Redo()
+        public void Redo()
         {
         {
-            Internal_Redo();
+            Internal_Redo(mCachedPtr);
+        }
+
+        /// <summary>
+        /// Creates a new undo/redo group. All new commands will be registered to this group. You may remove the group and 
+        /// all of its commands by calling <see cref="PopGroup"/>.
+        /// </summary>
+        /// <param name="name">Unique name of the group.</param>
+        public void PushGroup(string name)
+        {
+            Internal_PushGroup(mCachedPtr, name);
+        }
+
+        /// <summary>
+        /// Removes all the command registered to the current undo/redo group.
+        /// </summary>
+        /// <param name="name">Unique name of the group.</param>
+        public void PopGroup(string name)
+        {
+            Internal_PopGroup(mCachedPtr, name);
+        }
+
+        /// <summary>
+        /// Removes a command with the specified identifier from undo/redo stack without executing it.
+        /// </summary>
+        /// <param name="id">Identifier of the command as returned by <see cref="GetTopCommandId"/></param>
+        public void PopCommand(int id)
+        {
+            Internal_PopCommand(mCachedPtr, id);
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Records a state of the entire scene object at a specific point and allows you to restore it to its original 
         /// Records a state of the entire scene object at a specific point and allows you to restore it to its original 
-        /// values as needed.
+        /// values as needed. Undo operation recorded in global undo/redo stack.
         /// </summary>
         /// </summary>
         /// <param name="so">Scene object to record.</param>
         /// <param name="so">Scene object to record.</param>
         /// <param name="recordHierarchy">If true all children of this object will also be recorded.</param>
         /// <param name="recordHierarchy">If true all children of this object will also be recorded.</param>
@@ -59,7 +109,7 @@ namespace BansheeEditor
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Creates new scene object(s) by cloning existing objects.
+        /// Creates new scene object(s) by cloning existing objects. Undo operation recorded in global undo/redo stack.
         /// </summary>
         /// </summary>
         /// <param name="so">Scene object(s) to clone.</param>
         /// <param name="so">Scene object(s) to clone.</param>
         /// <param name="description">Optional description of what exactly the command does.</param>
         /// <param name="description">Optional description of what exactly the command does.</param>
@@ -82,7 +132,7 @@ namespace BansheeEditor
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Creates new a scene object by cloning an existing object.
+        /// Creates new a scene object by cloning an existing object. Undo operation recorded in global undo/redo stack.
         /// </summary>
         /// </summary>
         /// <param name="so">Scene object to clone.</param>
         /// <param name="so">Scene object to clone.</param>
         /// <param name="description">Optional description of what exactly the command does.</param>
         /// <param name="description">Optional description of what exactly the command does.</param>
@@ -96,7 +146,7 @@ namespace BansheeEditor
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Instantiates scene object(s) from a prefab.
+        /// Instantiates scene object(s) from a prefab. Undo operation recorded in global undo/redo stack.
         /// </summary>
         /// </summary>
         /// <param name="prefab">Prefab to instantiate.</param>
         /// <param name="prefab">Prefab to instantiate.</param>
         /// <param name="description">Optional description of what exactly the command does.</param>
         /// <param name="description">Optional description of what exactly the command does.</param>
@@ -110,7 +160,7 @@ namespace BansheeEditor
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Creates a new scene object.
+        /// Creates a new scene object. Undo operation recorded in global undo/redo stack.
         /// </summary>
         /// </summary>
         /// <param name="name">Name of the scene object.</param>
         /// <param name="name">Name of the scene object.</param>
         /// <param name="description">Optional description of what exactly the command does.</param>
         /// <param name="description">Optional description of what exactly the command does.</param>
@@ -121,7 +171,7 @@ namespace BansheeEditor
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Deletes a scene object.
+        /// Deletes a scene object. Undo operation recorded in global undo/redo stack.
         /// </summary>
         /// </summary>
         /// <param name="so">Scene object to delete.</param>
         /// <param name="so">Scene object to delete.</param>
         /// <param name="description">Optional description of what exactly the command does.</param>
         /// <param name="description">Optional description of what exactly the command does.</param>
@@ -132,7 +182,7 @@ namespace BansheeEditor
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Changes the parent of the scene object.
+        /// Changes the parent of the scene object. Undo operation recorded in global undo/redo stack.
         /// </summary>
         /// </summary>
         /// <param name="so">Scene object to change the parent of.</param>
         /// <param name="so">Scene object to change the parent of.</param>
         /// <param name="parent">New parent.</param>
         /// <param name="parent">New parent.</param>
@@ -150,7 +200,7 @@ namespace BansheeEditor
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Changes the parent of a set of scene objects.
+        /// Changes the parent of a set of scene objects. Undo operation recorded in global undo/redo stack.
         /// </summary>
         /// </summary>
         /// <param name="so">Scene objects to change the parent of.</param>
         /// <param name="so">Scene objects to change the parent of.</param>
         /// <param name="parent">New parent.</param>
         /// <param name="parent">New parent.</param>
@@ -178,7 +228,8 @@ namespace BansheeEditor
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Breaks the prefab link on the provided scene object and makes the operation undo-able. 
+        /// Breaks the prefab link on the provided scene object and makes the operation undo-able. Undo operation recorded 
+        /// in global undo/redo stack.
         /// See <see cref="PrefabUtility.BreakPrefab"/>.
         /// See <see cref="PrefabUtility.BreakPrefab"/>.
         /// </summary>
         /// </summary>
         /// <param name="so">Scene object whose prefab link to break.</param>
         /// <param name="so">Scene object whose prefab link to break.</param>
@@ -189,51 +240,29 @@ namespace BansheeEditor
                 Internal_BreakPrefab(so.GetCachedPtr(), description);
                 Internal_BreakPrefab(so.GetCachedPtr(), description);
         }
         }
 
 
-        /// <summary>
-        /// Creates a new undo/redo group. All new commands will be registered to this group. You may remove the group and 
-        /// all of its commands by calling <see cref="PopGroup"/>.
-        /// </summary>
-        /// <param name="name">Unique name of the group.</param>
-        public static void PushGroup(string name)
-        {
-            Internal_PushGroup(name);
-        }
-
-        /// <summary>
-        /// Removes all the command registered to the current undo/redo group.
-        /// </summary>
-        /// <param name="name">Unique name of the group.</param>
-        public static void PopGroup(string name)
-        {
-            Internal_PopGroup(name);
-        }
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern void Internal_CreateInstance(UndoRedo instance);
 
 
-        /// <summary>
-        /// Removes a command with the specified identifier from undo/redo stack without executing it.
-        /// </summary>
-        /// <param name="id">Identifier of the command as returned by <see cref="GetTopCommandId"/></param>
-        public static void PopCommand(int id)
-        {
-            Internal_PopCommand(id);
-        }
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern UndoRedo Internal_GetGlobal();
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal static extern void Internal_Undo();
+        internal static extern void Internal_Undo(IntPtr thisPtr);
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal static extern void Internal_Redo();
+        internal static extern void Internal_Redo(IntPtr thisPtr);
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal static extern void Internal_PushGroup(string name);
+        internal static extern void Internal_PushGroup(IntPtr thisPtr, string name);
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal static extern void Internal_PopGroup(string name);
+        internal static extern void Internal_PopGroup(IntPtr thisPtr, string name);
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal static extern void Internal_PopCommand(int id);
+        internal static extern void Internal_PopCommand(IntPtr thisPtr, int id);
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal static extern int Internal_GetTopCommandId();
+        internal static extern int Internal_GetTopCommandId(IntPtr thisPtr);
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal static extern void Internal_RecordSO(IntPtr soPtr, bool recordHierarchy, string description);
         internal static extern void Internal_RecordSO(IntPtr soPtr, bool recordHierarchy, string description);

+ 20 - 0
Source/MBansheeEditor/Window/MenuItems.cs

@@ -715,6 +715,26 @@ namespace BansheeEditor
             EditorApplication.TriggerGlobalShortcut(EditorApplication.DuplicateKey);
             EditorApplication.TriggerGlobalShortcut(EditorApplication.DuplicateKey);
         }
         }
 
 
+        /// <summary>
+        /// Executes the last command on the undo stack, undoing its operations.
+        /// </summary>
+        [MenuItem("Edit/Undo", 9500, true)]
+        [ToolbarItem("Undo", ToolbarIcon.Undo, "Undo (Ctrl + Z)", 1900, true)]
+        public static void Undo()
+        {
+            UndoRedo.Global.Undo();
+        }
+
+        /// <summary>
+        /// Executes the last command on the redo stack (last command we called undo on), re-applying its operation.
+        /// </summary>
+        [MenuItem("Edit/Redo", 9499)]
+        [ToolbarItem("Redo", ToolbarIcon.Redo, "Redo (Ctrl + Y)", 1899)]
+        public static void Redo()
+        {
+            UndoRedo.Global.Redo();
+        }
+
         /// <summary>
         /// <summary>
         /// Sets keyboard focus to the Hierarchy or Scene windows if open.
         /// Sets keyboard focus to the Hierarchy or Scene windows if open.
         /// </summary>
         /// </summary>

+ 2 - 2
Source/MBansheeEditor/Windows/Inspector/InspectorWindow.cs

@@ -608,7 +608,7 @@ namespace BansheeEditor
         private void OnSelectionChanged(SceneObject[] objects, string[] paths)
         private void OnSelectionChanged(SceneObject[] objects, string[] paths)
         {
         {
             if (currentType == InspectorType.SceneObject && modifyState == InspectableState.NotModified)
             if (currentType == InspectorType.SceneObject && modifyState == InspectableState.NotModified)
-                UndoRedo.PopCommand(undoCommandIdx);
+                UndoRedo.Global.PopCommand(undoCommandIdx);
 
 
             Clear();
             Clear();
             modifyState = InspectableState.NotModified;
             modifyState = InspectableState.NotModified;
@@ -646,7 +646,7 @@ namespace BansheeEditor
                 if (objects[0] != null)
                 if (objects[0] != null)
                 {
                 {
                     UndoRedo.RecordSO(objects[0]);
                     UndoRedo.RecordSO(objects[0]);
-                    undoCommandIdx = UndoRedo.TopCommandId;
+                    undoCommandIdx = UndoRedo.Global.TopCommandId;
 
 
                     SetObjectToInspect(objects[0]);
                     SetObjectToInspect(objects[0]);
                 }
                 }

+ 14 - 7
Source/SBansheeEditor/Include/BsScriptUndoRedo.h

@@ -7,6 +7,8 @@
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
+	class UndoRedo;
+
 	/** @addtogroup ScriptInteropEditor
 	/** @addtogroup ScriptInteropEditor
 	 *  @{
 	 *  @{
 	 */
 	 */
@@ -18,17 +20,22 @@ namespace BansheeEngine
 		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "UndoRedo");
 		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "UndoRedo");
 
 
 	private:
 	private:
-		ScriptUndoRedo(MonoObject* instance);
+		ScriptUndoRedo(MonoObject* instance, const SPtr<UndoRedo>& undoRedo);
+
+		SPtr<UndoRedo> mUndoRedo;
+		static ScriptUndoRedo* sGlobalUndoRedo;
 
 
 		/************************************************************************/
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
 		/************************************************************************/
-		static void internal_Undo();
-		static void internal_Redo();
-		static void internal_PushGroup(MonoString* name);
-		static void internal_PopGroup(MonoString* name);
-		static UINT32 internal_GetTopCommandId();
-		static void internal_PopCommand(UINT32 id);
+		static void internal_CreateInstance(MonoObject* instance);
+		static MonoObject* internal_GetGlobal();
+		static void internal_Undo(ScriptUndoRedo* thisPtr);
+		static void internal_Redo(ScriptUndoRedo* thisPtr);
+		static void internal_PushGroup(ScriptUndoRedo* thisPtr, MonoString* name);
+		static void internal_PopGroup(ScriptUndoRedo* thisPtr, MonoString* name);
+		static UINT32 internal_GetTopCommandId(ScriptUndoRedo* thisPtr);
+		static void internal_PopCommand(ScriptUndoRedo* thisPtr, UINT32 id);
 		static void internal_RecordSO(ScriptSceneObject* soPtr, bool recordHierarchy, MonoString* description);
 		static void internal_RecordSO(ScriptSceneObject* soPtr, bool recordHierarchy, MonoString* description);
 		static MonoObject* internal_CloneSO(ScriptSceneObject* soPtr, MonoString* description);
 		static MonoObject* internal_CloneSO(ScriptSceneObject* soPtr, MonoString* description);
 		static MonoArray* internal_CloneSOMulti(MonoArray* soPtrs, MonoString* description);
 		static MonoArray* internal_CloneSOMulti(MonoArray* soPtrs, MonoString* description);

+ 46 - 16
Source/SBansheeEditor/Source/BsScriptUndoRedo.cpp

@@ -20,14 +20,16 @@
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
-	ScriptUndoRedo::ScriptUndoRedo(MonoObject* instance)
-		:ScriptObject(instance)
-	{
+	ScriptUndoRedo* ScriptUndoRedo::sGlobalUndoRedo = nullptr;
 
 
-	}
+	ScriptUndoRedo::ScriptUndoRedo(MonoObject* instance, const SPtr<UndoRedo>& undoRedo)
+		:ScriptObject(instance), mUndoRedo(undoRedo)
+	{ }
 
 
 	void ScriptUndoRedo::initRuntimeData()
 	void ScriptUndoRedo::initRuntimeData()
 	{
 	{
+		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptUndoRedo::internal_CreateInstance);
+		metaData.scriptClass->addInternalCall("Internal_GetGlobal", &ScriptUndoRedo::internal_GetGlobal);
 		metaData.scriptClass->addInternalCall("Internal_Undo", &ScriptUndoRedo::internal_Undo);
 		metaData.scriptClass->addInternalCall("Internal_Undo", &ScriptUndoRedo::internal_Undo);
 		metaData.scriptClass->addInternalCall("Internal_Redo", &ScriptUndoRedo::internal_Redo);
 		metaData.scriptClass->addInternalCall("Internal_Redo", &ScriptUndoRedo::internal_Redo);
 		metaData.scriptClass->addInternalCall("Internal_PushGroup", &ScriptUndoRedo::internal_PushGroup);
 		metaData.scriptClass->addInternalCall("Internal_PushGroup", &ScriptUndoRedo::internal_PushGroup);
@@ -45,36 +47,64 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_BreakPrefab", &ScriptUndoRedo::internal_BreakPrefab);
 		metaData.scriptClass->addInternalCall("Internal_BreakPrefab", &ScriptUndoRedo::internal_BreakPrefab);
 	}
 	}
 
 
-	void ScriptUndoRedo::internal_Undo()
+	void ScriptUndoRedo::internal_CreateInstance(MonoObject* instance)
+	{
+		SPtr<UndoRedo> undoRedo = bs_shared_ptr_new<UndoRedo>();
+		new (bs_alloc<ScriptUndoRedo>()) ScriptUndoRedo(instance, undoRedo);
+	}
+
+	MonoObject* ScriptUndoRedo::internal_GetGlobal()
+	{
+		if(sGlobalUndoRedo == nullptr)
+		{
+			bool dummy = false;
+			void* params[1] = { &dummy };
+
+			MonoObject* instance = metaData.scriptClass->createInstance("bool", params);
+			sGlobalUndoRedo = new (bs_alloc<ScriptUndoRedo>()) ScriptUndoRedo(instance, nullptr);
+		}
+
+		return sGlobalUndoRedo->getManagedInstance();
+	}
+
+	void ScriptUndoRedo::internal_Undo(ScriptUndoRedo* thisPtr)
 	{
 	{
-		UndoRedo::instance().undo();
+		UndoRedo* undoRedo = thisPtr->mUndoRedo != nullptr ? thisPtr->mUndoRedo.get() : GlobalUndoRedo::instancePtr();
+		undoRedo->undo();
 	}
 	}
 
 
-	void ScriptUndoRedo::internal_Redo()
+	void ScriptUndoRedo::internal_Redo(ScriptUndoRedo* thisPtr)
 	{
 	{
-		UndoRedo::instance().redo();
+		UndoRedo* undoRedo = thisPtr->mUndoRedo != nullptr ? thisPtr->mUndoRedo.get() : GlobalUndoRedo::instancePtr();
+		undoRedo->redo();
 	}
 	}
 
 
-	void ScriptUndoRedo::internal_PushGroup(MonoString* name)
+	void ScriptUndoRedo::internal_PushGroup(ScriptUndoRedo* thisPtr, MonoString* name)
 	{
 	{
 		String nativeName = MonoUtil::monoToString(name);
 		String nativeName = MonoUtil::monoToString(name);
-		UndoRedo::instance().pushGroup(nativeName);
+
+		UndoRedo* undoRedo = thisPtr->mUndoRedo != nullptr ? thisPtr->mUndoRedo.get() : GlobalUndoRedo::instancePtr();
+		undoRedo->pushGroup(nativeName);
 	}
 	}
 
 
-	void ScriptUndoRedo::internal_PopGroup(MonoString* name)
+	void ScriptUndoRedo::internal_PopGroup(ScriptUndoRedo* thisPtr, MonoString* name)
 	{
 	{
 		String nativeName = MonoUtil::monoToString(name);
 		String nativeName = MonoUtil::monoToString(name);
-		UndoRedo::instance().popGroup(nativeName);
+
+		UndoRedo* undoRedo = thisPtr->mUndoRedo != nullptr ? thisPtr->mUndoRedo.get() : GlobalUndoRedo::instancePtr();
+		undoRedo->popGroup(nativeName);
 	}
 	}
 
 
-	UINT32 ScriptUndoRedo::internal_GetTopCommandId()
+	UINT32 ScriptUndoRedo::internal_GetTopCommandId(ScriptUndoRedo* thisPtr)
 	{
 	{
-		return UndoRedo::instance().getTopCommandId();
+		UndoRedo* undoRedo = thisPtr->mUndoRedo != nullptr ? thisPtr->mUndoRedo.get() : GlobalUndoRedo::instancePtr();
+		return undoRedo->getTopCommandId();
 	}
 	}
 
 
-	void ScriptUndoRedo::internal_PopCommand(UINT32 id)
+	void ScriptUndoRedo::internal_PopCommand(ScriptUndoRedo* thisPtr, UINT32 id)
 	{
 	{
-		UndoRedo::instance().popCommand(id);
+		UndoRedo* undoRedo = thisPtr->mUndoRedo != nullptr ? thisPtr->mUndoRedo.get() : GlobalUndoRedo::instancePtr();
+		undoRedo->popCommand(id);
 	}
 	}
 
 
 	void ScriptUndoRedo::internal_RecordSO(ScriptSceneObject* soPtr, bool recordHierarchy, MonoString* description)
 	void ScriptUndoRedo::internal_RecordSO(ScriptSceneObject* soPtr, bool recordHierarchy, MonoString* description)