diff --git a/SHADE_Engine/src/Editor/Command/SHCommand.hpp b/SHADE_Engine/src/Editor/Command/SHCommand.hpp index ae8834e9..7a526506 100644 --- a/SHADE_Engine/src/Editor/Command/SHCommand.hpp +++ b/SHADE_Engine/src/Editor/Command/SHCommand.hpp @@ -5,9 +5,13 @@ //#==============================================================# #include +#include "SH_API.h" +#include "Scripting/SHScriptEngine.h" +#include "ECS_Base/Managers/SHSystemManager.h" + namespace SHADE { - class SHBaseCommand + class SH_API SHBaseCommand { public: virtual ~SHBaseCommand() = default; @@ -48,4 +52,20 @@ namespace SHADE T newValue; SetterFunction set; }; + + class SH_API SHCLICommand : SHBaseCommand + { + public: + SHCLICommand() = default; + void Execute() override + { + SHScriptEngine* scriptEngine = static_cast(SHSystemManager::GetSystem()); + scriptEngine->RedoScriptInspectorChanges(); + } + void Undo() override + { + SHScriptEngine* scriptEngine = static_cast(SHSystemManager::GetSystem()); + scriptEngine->UndoScriptInspectorChanges(); + } + }; }//namespace SHADE diff --git a/SHADE_Engine/src/Editor/Command/SHCommandManager.cpp b/SHADE_Engine/src/Editor/Command/SHCommandManager.cpp index 67d6c2ee..ee2d316d 100644 --- a/SHADE_Engine/src/Editor/Command/SHCommandManager.cpp +++ b/SHADE_Engine/src/Editor/Command/SHCommandManager.cpp @@ -27,6 +27,11 @@ namespace SHADE } } + void SHCommandManager::RegisterCommand(CommandPtr commandPtr) + { + undoStack.push(commandPtr); + } + void SHCommandManager::UndoCommand() { if (undoStack.empty()) diff --git a/SHADE_Engine/src/Editor/Command/SHCommandManager.h b/SHADE_Engine/src/Editor/Command/SHCommandManager.h index 3ea42740..9152c3cb 100644 --- a/SHADE_Engine/src/Editor/Command/SHCommandManager.h +++ b/SHADE_Engine/src/Editor/Command/SHCommandManager.h @@ -9,10 +9,11 @@ //|| SHADE Includes || //#==============================================================# #include "SHCommand.hpp" +#include "SH_API.h" namespace SHADE { - class SHCommandManager + class SH_API SHCommandManager { public: //#==============================================================# @@ -22,6 +23,7 @@ namespace SHADE using CommandStack = std::stack; static void PerformCommand(CommandPtr commandPtr, bool const& overrideValue = false); + static void RegisterCommand(CommandPtr commandPtr); static void UndoCommand(); static void RedoCommand(); static std::size_t GetUndoStackSize(); diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp index d65eaf64..65f445a6 100644 --- a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp +++ b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp @@ -153,6 +153,16 @@ namespace SHADE csEditorRenderScripts(entity); } + void SHScriptEngine::UndoScriptInspectorChanges() const + { + csEditorUndo(); + } + + void SHScriptEngine::RedoScriptInspectorChanges() const + { + csEditorRedo(); + } + /*-----------------------------------------------------------------------------------*/ /* Static Utility Functions */ /*-----------------------------------------------------------------------------------*/ @@ -400,6 +410,18 @@ namespace SHADE DEFAULT_CSHARP_NAMESPACE + ".Editor", "RenderScriptsInInspector" ); + csEditorUndo = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".Editor", + "Undo" + ); + csEditorRedo = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".Editor", + "Redo" + ); } void SHScriptEngine::registerEvents() diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.h b/SHADE_Engine/src/Scripting/SHScriptEngine.h index 710421e8..239d6b90 100644 --- a/SHADE_Engine/src/Scripting/SHScriptEngine.h +++ b/SHADE_Engine/src/Scripting/SHScriptEngine.h @@ -171,6 +171,14 @@ namespace SHADE /// /// The Entity to render the Scripts of. void RenderScriptsInInspector(EntityID entity) const; + /// + /// Performs an undo for script inspector changes if it exists. + /// + void UndoScriptInspectorChanges() const; + /// + /// Performs a redo for script inspector changes if it exists. + /// + void RedoScriptInspectorChanges() const; /*-----------------------------------------------------------------------------*/ /* Static Utility Functions */ @@ -243,9 +251,8 @@ namespace SHADE CsScriptSerialiseYamlFuncPtr csScriptDeserialiseYaml = nullptr; // - Editor CsScriptEditorFuncPtr csEditorRenderScripts = nullptr; - // Delegates - /*ECS::EntityEvent::Delegate onEntityCreate; - ECS::EntityEvent::Delegate onEntityDestroy;*/ + CsFuncPtr csEditorUndo = nullptr; + CsFuncPtr csEditorRedo = nullptr; /*-----------------------------------------------------------------------------*/ /* Event Handler Functions */ diff --git a/SHADE_Managed/premake5.lua b/SHADE_Managed/premake5.lua index f0e06d4b..052be24a 100644 --- a/SHADE_Managed/premake5.lua +++ b/SHADE_Managed/premake5.lua @@ -30,7 +30,8 @@ project "SHADE_Managed" "%{IncludeDir.imguizmo}", "%{IncludeDir.imnodes}", "%{IncludeDir.yamlcpp}", - "%{IncludeDir.RTTR}/include", + "%{IncludeDir.RTTR}/include", + "%{IncludeDir.dotnet}\\include", "%{wks.location}/SHADE_Engine/src" } diff --git a/SHADE_Managed/src/Editor/Editor.cxx b/SHADE_Managed/src/Editor/Editor.cxx index 629c9e65..735f8c2c 100644 --- a/SHADE_Managed/src/Editor/Editor.cxx +++ b/SHADE_Managed/src/Editor/Editor.cxx @@ -3,7 +3,7 @@ \author Tng Kah Wei, kahwei.tng, 390009620 \par email: kahwei.tng\@digipen.edu \date Sep 27, 2022 -\brief Contains the definition of the functions for the ScriptStore managed +\brief Contains the definition of the functions for the Editor managed static class. Note: This file is written in C++17/CLI. @@ -16,6 +16,8 @@ of DigiPen Institute of Technology is prohibited. #include "SHpch.h" // Primary Header #include "Editor/Editor.hxx" +// STL Includes +#include // External Dependencies #include "Editor/SHEditorUI.h" // Project Headers @@ -25,6 +27,8 @@ of DigiPen Institute of Technology is prohibited. #include "Utility/Debug.hxx" #include "Serialisation/ReflectionUtilities.hxx" #include "Editor/IconsMaterialDesign.h" +#include "Editor/Command/SHCommandManager.h" +#include "Editor/Command/SHCommand.hpp" // Using Directives using namespace System; @@ -48,9 +52,11 @@ using namespace System::Collections::Generic; (field->FieldType == MANAGED_TYPE::typeid) \ { \ NATIVE_TYPE val = safe_cast(field->GetValue(object)); \ + NATIVE_TYPE oldVal = val; \ if (SHEditorUI::FUNC(Convert::ToNative(field->Name), val)) \ { \ field->SetValue(object, val); \ + registerUndoAction(object, field, val, oldVal); \ } \ } \ /// @@ -69,9 +75,11 @@ using namespace System::Collections::Generic; (field->FieldType == MANAGED_TYPE::typeid) \ { \ NATIVE_TYPE val = Convert::ToNative(safe_cast(field->GetValue(object))); \ + NATIVE_TYPE oldVal = val; \ if (SHEditorUI::FUNC(Convert::ToNative(field->Name), val)) \ { \ field->SetValue(object, Convert::ToCLI(val)); \ + registerUndoAction(object, field, Convert::ToCLI(val), Convert::ToCLI(oldVal)); \ } \ } \ @@ -105,26 +113,43 @@ namespace SHADE SAFE_NATIVE_CALL_END_N("SHADE_Managed.Editor.RenderScriptsInInspector") } - void Editor::RenderScriptAddButton(Entity entity) - { - // Get list of Scripts - auto scriptTypes = ScriptStore::GetAvailableScriptList(); + void Editor::RenderScriptAddButton(Entity entity) + { + // Get list of Scripts + auto scriptTypes = ScriptStore::GetAvailableScriptList(); - // Define pop up - if (SHEditorUI::BeginMenu("Add Script", ICON_MD_LIBRARY_ADD)) - { - for each (Type ^ type in scriptTypes) - { - if (SHEditorUI::Selectable(Convert::ToNative(type->Name))) - { - // Add the script - ScriptStore::AddScriptViaName(entity, type->Name); - } - } + // Define pop up + if (SHEditorUI::BeginMenu("Add Script", ICON_MD_LIBRARY_ADD)) + { + for each (Type ^ type in scriptTypes) + { + if (SHEditorUI::Selectable(Convert::ToNative(type->Name))) + { + // Add the script + ScriptStore::AddScriptViaName(entity, type->Name); + } + } - SHEditorUI::EndMenu(); - } - } + SHEditorUI::EndMenu(); + } + } + + /*---------------------------------------------------------------------------------*/ + /* UndoRedoStack Functions */ + /*---------------------------------------------------------------------------------*/ + void Editor::Undo() + { + SAFE_NATIVE_CALL_BEGIN + actionStack.Undo(); + SAFE_NATIVE_CALL_END_N("SHADE_Managed.Editor.Undo") + } + + void Editor::Redo() + { + SAFE_NATIVE_CALL_BEGIN + actionStack.Redo(); + SAFE_NATIVE_CALL_END_N("SHADE_Managed.Editor.Redo") + } /*---------------------------------------------------------------------------------*/ /* Helper Functions */ @@ -192,9 +217,11 @@ namespace SHADE } int val = safe_cast(field->GetValue(object)); + int oldVal = val; if (SHEditorUI::InputEnumCombo(Convert::ToNative(field->Name), val, nativeEnumNames)) { field->SetValue(object, val); + registerUndoAction(object, field, val, oldVal); } } else if RENDER_FIELD_CASTED(Vector2, SHVec2, InputVec2) @@ -210,9 +237,11 @@ namespace SHADE // Actual Field std::string val = Convert::ToNative(stringVal); + std::string oldVal = val; if (SHEditorUI::InputTextField(Convert::ToNative(field->Name), val)) { field->SetValue(object, Convert::ToCLI(val)); + registerUndoAction(object, field, Convert::ToCLI(val), Convert::ToCLI(oldVal)); } } } @@ -231,4 +260,18 @@ 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); + + // Inform the C++ Undo-Redo stack + SHCommandManager::RegisterCommand(std::reinterpret_pointer_cast(std::make_shared())); + } + } diff --git a/SHADE_Managed/src/Editor/Editor.hxx b/SHADE_Managed/src/Editor/Editor.hxx index 188da660..c4800645 100644 --- a/SHADE_Managed/src/Editor/Editor.hxx +++ b/SHADE_Managed/src/Editor/Editor.hxx @@ -16,6 +16,7 @@ of DigiPen Institute of Technology is prohibited. // Project Includes #include "Engine/Entity.hxx" #include "Scripts/Script.hxx" +#include "UndoRedoStack.hxx" namespace SHADE { @@ -36,15 +37,26 @@ namespace SHADE /// rendering code. /// /// The Entity to render the Scripts of. - static void RenderScriptsInInspector(Entity entity); - /// - /// Renders a dropdown button that allows for the addition of PlushieScripts - /// onto the specified Entity. - /// - /// The Entity to add PlushieScripts to. - static void RenderScriptAddButton(Entity entity); + static void RenderScriptsInInspector(Entity entity); + /// + /// Renders a dropdown button that allows for the addition of PlushieScripts + /// onto the specified Entity. + /// + /// The Entity to add PlushieScripts to. + static void RenderScriptAddButton(Entity entity); + + /*-----------------------------------------------------------------------------*/ + /* UndoRedoStack Functions */ + /*-----------------------------------------------------------------------------*/ + static void Undo(); + static void Redo(); + + private: + /*-----------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------*/ + static UndoRedoStack actionStack; - private: /*-----------------------------------------------------------------------------*/ /* Helper Functions */ /*-----------------------------------------------------------------------------*/ @@ -73,5 +85,6 @@ namespace SHADE /// The Entity to render the Scripts of. /// The Script to render the inspector for. static void renderScriptContextMenu(Entity entity, Script^ script); + static void registerUndoAction(System::Object^ object, System::Reflection::FieldInfo^ field, System::Object^ newData, System::Object^ oldData); }; } diff --git a/SHADE_Managed/src/Editor/UndoRedoStack.cxx b/SHADE_Managed/src/Editor/UndoRedoStack.cxx new file mode 100644 index 00000000..08e289cc --- /dev/null +++ b/SHADE_Managed/src/Editor/UndoRedoStack.cxx @@ -0,0 +1,69 @@ +/************************************************************************************//*! +\file UndoRedoStack.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Sep 29, 2022 +\brief Contains the definition of the functions for the UndoRedoStack managed + class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2022 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "UndoRedoStack.hxx" +// External Dependencies +#include "Editor/SHEditorUI.h" +// Project Headers + +namespace SHADE +{ + bool UndoRedoStack::UndoActionPresent::get() + { + return commandStack->Count > 0 && latestActionIndex >= 0; + } + + bool UndoRedoStack::RedoActionPresent::get() + { + return latestActionIndex >= 0 && latestActionIndex < commandStack->Count - 1; + } + + void UndoRedoStack::Add(Command command) + { + // Erase any other actions ahead of the current action + if (latestActionIndex >= 0 && latestActionIndex < commandStack->Count - 1) + { + commandStack->RemoveRange(latestActionIndex, commandStack->Count - latestActionIndex); + } + + // Add the command + commandStack->Add(command); + + // Set the latest command + latestActionIndex = commandStack->Count - 1; + } + + void UndoRedoStack::Undo() + { + if (!UndoActionPresent) + return; + + Command cmd = commandStack[latestActionIndex]; + cmd.Field->SetValue(cmd.Object, cmd.OldData); + --latestActionIndex; + } + + void UndoRedoStack::Redo() + { + if (!RedoActionPresent) + return; + + Command cmd = commandStack[latestActionIndex]; + cmd.Field->SetValue(cmd.Object, cmd.NewData); + ++latestActionIndex; + } +} diff --git a/SHADE_Managed/src/Editor/UndoRedoStack.hxx b/SHADE_Managed/src/Editor/UndoRedoStack.hxx new file mode 100644 index 00000000..4c525228 --- /dev/null +++ b/SHADE_Managed/src/Editor/UndoRedoStack.hxx @@ -0,0 +1,75 @@ +/************************************************************************************//*! +\file UndoRedoStack.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Sep 29, 2022 +\brief Contains the definition of the managed UndoRedoStack class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2022 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +#pragma once + +namespace SHADE +{ + /// + /// 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 */ + /*-----------------------------------------------------------------------------*/ + /// + /// True if there is an undoable action in the stack. + /// + property bool UndoActionPresent { bool get(); } + /// + /// True if there is a redoable action in the stack. + /// + property bool RedoActionPresent { bool get(); } + + /*-----------------------------------------------------------------------------*/ + /* Usage Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Adds a command onto the stack. + /// + /// + void Add(Command command); + /// + /// Undos the last added command if it exists. + /// + void Undo(); + /// + /// Redoes the last undo-ed command if it exists. + /// + void Redo(); + + private: + /*-----------------------------------------------------------------------------*/ + /* Data Members */ + /*-----------------------------------------------------------------------------*/ + int latestActionIndex = -1; + System::Collections::Generic::List^ commandStack = gcnew System::Collections::Generic::List(); + }; +}