diff --git a/SHADE_Managed/src/Editor/Editor.cxx b/SHADE_Managed/src/Editor/Editor.cxx index bfd93401..d29f838d 100644 --- a/SHADE_Managed/src/Editor/Editor.cxx +++ b/SHADE_Managed/src/Editor/Editor.cxx @@ -149,7 +149,7 @@ namespace SHADE } SHEditorUI::PopID(); } - void Editor::renderFieldInInspector(Reflection::FieldInfo^ field, Object^ object) + void Editor::renderFieldInInspector(Reflection::FieldInfo^ field, System::Object^ object) { bool isHovered = false; @@ -343,12 +343,7 @@ namespace SHADE void Editor::registerUndoAction(System::Object^ object, System::Reflection::FieldInfo^ field, System::Object^ newData, System::Object^ oldData) { // Create command and add it into the undo stack - UndoRedoStack::Command cmd; - cmd.Field = field; - cmd.Object = object; - cmd.NewData = newData; - cmd.OldData = oldData; - actionStack.Add(cmd); + actionStack.Add(gcnew FieldChangeCommand(object, field, newData, oldData)); // Inform the C++ Undo-Redo stack SHCommandManager::RegisterCommand(std::reinterpret_pointer_cast(std::make_shared())); diff --git a/SHADE_Managed/src/Editor/Editor.h++ b/SHADE_Managed/src/Editor/Editor.h++ index 2cda78e7..b68b5da8 100644 --- a/SHADE_Managed/src/Editor/Editor.h++ +++ b/SHADE_Managed/src/Editor/Editor.h++ @@ -30,7 +30,8 @@ namespace SHADE rangeAttrib = hasAttribute(fieldInfo); } - ManagedType val = safe_cast(fieldInfo->GetValue(object)); + ManagedType oldVal = safe_cast(fieldInfo->GetValue(object)); + ManagedType val = oldVal; if (renderFieldInInspector ( Convert::ToNative(fieldInfo->Name), @@ -41,7 +42,7 @@ namespace SHADE )) { fieldInfo->SetValue(object, val); - // TODO: Register undo + registerUndoAction(object, fieldInfo, fieldInfo->GetValue(object), oldVal); } return true; @@ -90,15 +91,12 @@ namespace SHADE { if constexpr (IsPrimitiveTypeMatches_V) { - //field->SetValue(object, val); managedVal = val; - //registerUndoAction(object, field, val, oldVal); } else { managedVal = Convert::ToCLI(val); - //registerUndoAction(object, field, Convert::ToCLI(val), Convert::ToCLI(oldVal)); } return true; diff --git a/SHADE_Managed/src/Editor/Editor.hxx b/SHADE_Managed/src/Editor/Editor.hxx index c7e86622..1f2c1be7 100644 --- a/SHADE_Managed/src/Editor/Editor.hxx +++ b/SHADE_Managed/src/Editor/Editor.hxx @@ -89,7 +89,7 @@ namespace SHADE /// /// The object that contains the data of the field to render. /// - static void renderFieldInInspector(System::Reflection::FieldInfo^ field, Object^ object); + static void renderFieldInInspector(System::Reflection::FieldInfo^ field, System::Object^ object); /// /// Renders a context menu when right clicked for the scripts /// diff --git a/SHADE_Managed/src/Editor/UndoRedoStack.cxx b/SHADE_Managed/src/Editor/UndoRedoStack.cxx index 08e289cc..ae0a1dee 100644 --- a/SHADE_Managed/src/Editor/UndoRedoStack.cxx +++ b/SHADE_Managed/src/Editor/UndoRedoStack.cxx @@ -19,6 +19,8 @@ of DigiPen Institute of Technology is prohibited. // External Dependencies #include "Editor/SHEditorUI.h" // Project Headers +#include "Utility/Debug.hxx" +#include "Utility/Convert.hxx" namespace SHADE { @@ -32,7 +34,7 @@ namespace SHADE return latestActionIndex >= 0 && latestActionIndex < commandStack->Count - 1; } - void UndoRedoStack::Add(Command command) + void UndoRedoStack::Add(ICommand^ command) { // Erase any other actions ahead of the current action if (latestActionIndex >= 0 && latestActionIndex < commandStack->Count - 1) @@ -52,8 +54,8 @@ namespace SHADE if (!UndoActionPresent) return; - Command cmd = commandStack[latestActionIndex]; - cmd.Field->SetValue(cmd.Object, cmd.OldData); + ICommand^ cmd = commandStack[latestActionIndex]; + cmd->Unexceute(); --latestActionIndex; } @@ -62,8 +64,57 @@ namespace SHADE if (!RedoActionPresent) return; - Command cmd = commandStack[latestActionIndex]; - cmd.Field->SetValue(cmd.Object, cmd.NewData); + ICommand^ cmd = commandStack[latestActionIndex]; + cmd->Execute(); ++latestActionIndex; } + + FieldChangeCommand::FieldChangeCommand(System::Object^ obj, System::Reflection::FieldInfo^ field, System::Object^ newData, System::Object^ oldData) + : objectToChange { obj } + , field { field } + , newData { newData } + , oldData { oldData } + {} + + bool FieldChangeCommand::Execute() + { + if (field && objectToChange) + { + field->SetValue(objectToChange, newData); + return true; + } + + return false; + } + + bool FieldChangeCommand::Unexceute() + { + if (field && objectToChange) + { + field->SetValue(objectToChange, oldData); + return true; + } + + return false; + } + + bool FieldChangeCommand::Merge(ICommand^ command) + { + FieldChangeCommand^ otherCommand = safe_cast(command); + if (otherCommand == nullptr) + { + Debug::LogWarning("[Field Change Command] Attempted to merge two incompatible commands!"); + return false; + } + + // Only merge if they are workng on the same object and field + if (field == otherCommand->field && objectToChange == otherCommand->objectToChange) + { + newData = otherCommand->newData; + return true; + } + + return false; + } + } diff --git a/SHADE_Managed/src/Editor/UndoRedoStack.hxx b/SHADE_Managed/src/Editor/UndoRedoStack.hxx index 4c525228..69f462e3 100644 --- a/SHADE_Managed/src/Editor/UndoRedoStack.hxx +++ b/SHADE_Managed/src/Editor/UndoRedoStack.hxx @@ -15,27 +15,52 @@ of DigiPen Institute of Technology is prohibited. namespace SHADE { + /// + /// Interface for command that fits into the UndoRedoStack which can perform + /// undo-able and redo-able operations. + /// + private interface class ICommand + { + /// + /// Executes an action. This is called when a "Redo" is performed. + /// + /// Whether the action was successful or not. + bool Execute(); + /// + /// Undoes an action. This is called when an "Undo" is performed. + /// + /// Whether the action was successful or not. + bool Unexceute(); + /// + /// Merges this command with another command. + /// + /// + /// Whether the merge was successful or not. + bool Merge(ICommand^ command); + }; + + private ref class FieldChangeCommand sealed : public ICommand + { + public: + FieldChangeCommand(System::Object^ obj, System::Reflection::FieldInfo^ field, System::Object^ newData, System::Object^ oldData); + + bool Execute() override; + bool Unexceute() override; + bool Merge(ICommand^ command) override; + + private: + System::Object^ objectToChange; + System::Reflection::FieldInfo^ field; + System::Object^ newData; + System::Object^ oldData; + }; + /// /// Class that is able to store a stack of actions that can be done and redone. /// private ref class UndoRedoStack sealed { public: - /*-----------------------------------------------------------------------------*/ - /* Type Definitions */ - /*-----------------------------------------------------------------------------*/ - /// - /// Command for the stack that represents a data modification. - /// - value struct Command - { - public: - System::Object^ Object; - System::Reflection::FieldInfo^ Field; - System::Object^ NewData; - System::Object^ OldData; - }; - /*-----------------------------------------------------------------------------*/ /* Properties */ /*-----------------------------------------------------------------------------*/ @@ -55,7 +80,7 @@ namespace SHADE /// Adds a command onto the stack. /// /// - void Add(Command command); + void Add(ICommand^ command); /// /// Undos the last added command if it exists. /// @@ -70,6 +95,6 @@ namespace SHADE /* Data Members */ /*-----------------------------------------------------------------------------*/ int latestActionIndex = -1; - System::Collections::Generic::List^ commandStack = gcnew System::Collections::Generic::List(); + System::Collections::Generic::List^ commandStack = gcnew System::Collections::Generic::List(); }; }