Reworked Undo-Redo system to be more flexible and stable
This commit is contained in:
parent
bdc7297937
commit
cf5cc41a3f
|
@ -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<SHBaseCommand>(std::make_shared<SHCLICommand>()));
|
||||
|
|
|
@ -30,7 +30,8 @@ namespace SHADE
|
|||
rangeAttrib = hasAttribute<RangeAttribute^>(fieldInfo);
|
||||
}
|
||||
|
||||
ManagedType val = safe_cast<ManagedType>(fieldInfo->GetValue(object));
|
||||
ManagedType oldVal = safe_cast<ManagedType>(fieldInfo->GetValue(object));
|
||||
ManagedType val = oldVal;
|
||||
if (renderFieldInInspector<NativeType, ManagedType>
|
||||
(
|
||||
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<NativeType>)
|
||||
{
|
||||
//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;
|
||||
|
|
|
@ -89,7 +89,7 @@ namespace SHADE
|
|||
/// <param name="object">
|
||||
/// The object that contains the data of the field to render.
|
||||
/// </param>
|
||||
static void renderFieldInInspector(System::Reflection::FieldInfo^ field, Object^ object);
|
||||
static void renderFieldInInspector(System::Reflection::FieldInfo^ field, System::Object^ object);
|
||||
/// <summary>
|
||||
/// Renders a context menu when right clicked for the scripts
|
||||
/// </summary>
|
||||
|
|
|
@ -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<FieldChangeCommand^>(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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,27 +15,52 @@ of DigiPen Institute of Technology is prohibited.
|
|||
|
||||
namespace SHADE
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for command that fits into the UndoRedoStack which can perform
|
||||
/// undo-able and redo-able operations.
|
||||
/// </summary>
|
||||
private interface class ICommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes an action. This is called when a "Redo" is performed.
|
||||
/// </summary>
|
||||
/// <returns>Whether the action was successful or not.</returns>
|
||||
bool Execute();
|
||||
/// <summary>
|
||||
/// Undoes an action. This is called when an "Undo" is performed.
|
||||
/// </summary>
|
||||
/// <returns>Whether the action was successful or not.</returns>
|
||||
bool Unexceute();
|
||||
/// <summary>
|
||||
/// Merges this command with another command.
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
/// <returns>Whether the merge was successful or not.</returns>
|
||||
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;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Class that is able to store a stack of actions that can be done and redone.
|
||||
/// </summary>
|
||||
private ref class UndoRedoStack sealed
|
||||
{
|
||||
public:
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Type Definitions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Command for the stack that represents a data modification.
|
||||
/// </summary>
|
||||
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.
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
void Add(Command command);
|
||||
void Add(ICommand^ command);
|
||||
/// <summary>
|
||||
/// Undos the last added command if it exists.
|
||||
/// </summary>
|
||||
|
@ -70,6 +95,6 @@ namespace SHADE
|
|||
/* Data Members */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
int latestActionIndex = -1;
|
||||
System::Collections::Generic::List<Command>^ commandStack = gcnew System::Collections::Generic::List<Command>();
|
||||
System::Collections::Generic::List<ICommand^>^ commandStack = gcnew System::Collections::Generic::List<ICommand^>();
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue