Fixed lists undo not working

This commit is contained in:
Kah Wei 2022-11-12 02:33:00 +08:00
parent 2d2cc532a5
commit 543c199b03
5 changed files with 129 additions and 84 deletions

View File

@ -180,6 +180,7 @@ namespace SHADE
SHEditorUI::Text(Convert::ToNative(field->Name)); SHEditorUI::Text(Convert::ToNative(field->Name));
SHEditorUI::SameLine(); SHEditorUI::SameLine();
SHEditorUI::Button("+"); SHEditorUI::Button("+");
SHEditorUI::Indent(); SHEditorUI::Indent();
@ -187,9 +188,11 @@ namespace SHADE
{ {
SHEditorUI::PushID(i); SHEditorUI::PushID(i);
System::Object^ obj = iList[i]; System::Object^ obj = iList[i];
System::Object^ oldObj = iList[i];
if (renderFieldEditor(std::to_string(i), obj, rangeAttrib)) if (renderFieldEditor(std::to_string(i), obj, rangeAttrib))
{ {
iList[i] = obj; iList[i] = obj;
registerUndoListChangeAction(listType, iList, i, obj, oldObj);
} }
SHEditorUI::SameLine(); SHEditorUI::SameLine();
SHEditorUI::Button("-"); SHEditorUI::Button("-");
@ -281,24 +284,26 @@ namespace SHADE
bool Editor::renderFieldEditor(const std::string& fieldName, System::Object^% object, RangeAttribute^ rangeAttrib) bool Editor::renderFieldEditor(const std::string& fieldName, System::Object^% object, RangeAttribute^ rangeAttrib)
{ {
const bool MODIFIED_PRIMITIVE = bool modified;
renderFieldEditor<int , Int16 >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib) ||
renderFieldEditor<int , Int32 >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib) ||
renderFieldEditor<int , Int64 >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib) ||
renderFieldEditor<int , UInt16 >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib) ||
renderFieldEditor<int , UInt32 >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib) ||
renderFieldEditor<int , UInt64 >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib) ||
renderFieldEditor<int , Byte >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib) ||
renderFieldEditor<bool , bool >(fieldName, object, SHEditorUI::InputCheckbox, nullptr, rangeAttrib) ||
renderFieldEditor<float , float >(fieldName, object, SHEditorUI::InputFloat , nullptr, rangeAttrib) ||
renderFieldEditor<double , double >(fieldName, object, SHEditorUI::InputDouble , nullptr, rangeAttrib) ||
renderFieldEditor<SHVec2 , Vector2 >(fieldName, object, SHEditorUI::InputVec2 , nullptr, rangeAttrib) ||
renderFieldEditor<SHVec3 , Vector3 >(fieldName, object, SHEditorUI::InputVec3 , nullptr, rangeAttrib) ||
renderFieldEditor<uint32_t , GameObject >(fieldName, object, nullptr , nullptr, rangeAttrib) ||
renderFieldEditor<std::string, System::String^>(fieldName, object, nullptr , nullptr, rangeAttrib) ||
renderFieldEditor<int , System::Enum >(fieldName, object, nullptr , nullptr, rangeAttrib);
return MODIFIED_PRIMITIVE; const bool RENDERED =
renderFieldEditor<int , Int16 >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) ||
renderFieldEditor<int , Int32 >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) ||
renderFieldEditor<int , Int64 >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) ||
renderFieldEditor<int , UInt16 >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) ||
renderFieldEditor<int , UInt32 >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) ||
renderFieldEditor<int , UInt64 >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) ||
renderFieldEditor<int , Byte >(fieldName, object, SHEditorUI::InputInt , nullptr, rangeAttrib, modified) ||
renderFieldEditor<bool , bool >(fieldName, object, SHEditorUI::InputCheckbox, nullptr, rangeAttrib, modified) ||
renderFieldEditor<float , float >(fieldName, object, SHEditorUI::InputFloat , nullptr, rangeAttrib, modified) ||
renderFieldEditor<double , double >(fieldName, object, SHEditorUI::InputDouble , nullptr, rangeAttrib, modified) ||
renderFieldEditor<SHVec2 , Vector2 >(fieldName, object, SHEditorUI::InputVec2 , nullptr, rangeAttrib, modified) ||
renderFieldEditor<SHVec3 , Vector3 >(fieldName, object, SHEditorUI::InputVec3 , nullptr, rangeAttrib, modified) ||
renderFieldEditor<uint32_t , GameObject >(fieldName, object, nullptr , nullptr, rangeAttrib, modified) ||
renderFieldEditor<std::string, System::String^>(fieldName, object, nullptr , nullptr, rangeAttrib, modified) ||
renderFieldEditor<int , System::Enum >(fieldName, object, nullptr , nullptr, rangeAttrib, modified);
return modified;
} }
bool Editor::renderEnumEditor(const std::string& fieldName, System::Object^% object, bool* isHovered) bool Editor::renderEnumEditor(const std::string& fieldName, System::Object^% object, bool* isHovered)
@ -345,6 +350,33 @@ namespace SHADE
SHCommandManager::RegisterCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCLICommand>())); SHCommandManager::RegisterCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCLICommand>()));
} }
void Editor::registerUndoListChangeAction(System::Type^ type, System::Collections::IList^ list, int index, System::Object^ newData, System::Object^ oldData)
{
if (list == nullptr)
return;
actionStack.Add(gcnew ListElementChangeCommand(list, index, newData, oldData));
// Inform the C++ Undo-Redo stack
SHCommandManager::RegisterCommand(std::reinterpret_pointer_cast<SHBaseCommand>(std::make_shared<SHCLICommand>()));
}
void Editor::registerUndoListAddAction(System::Type^ type, System::Collections::IList^ list, int index, System::Object^ data)
{
if (list == nullptr)
return;
actionStack.Add(gcnew ListElementAddCommand(list, index, data));
}
void Editor::registerUndoListRemoveAction(System::Type^ type, System::Collections::IList^ list, int index, System::Object^ data)
{
if (list == nullptr)
return;
actionStack.Add(gcnew ListElementRemoveCommand(list, index, data));
}
generic<typename Attribute> generic<typename Attribute>
Attribute Editor::hasAttribute(System::Reflection::FieldInfo^ field) Attribute Editor::hasAttribute(System::Reflection::FieldInfo^ field)
{ {

View File

@ -80,13 +80,15 @@ namespace SHADE
} }
template<typename NativeType, typename ManagedType> template<typename NativeType, typename ManagedType>
bool Editor::renderFieldEditor(const std::string& fieldName, System::Object^% object, EditorFieldFunc<NativeType> fieldEditor, bool* isHovered, RangeAttribute^ rangeAttrib) bool Editor::renderFieldEditor(const std::string& fieldName, System::Object^% object, EditorFieldFunc<NativeType> fieldEditor, bool* isHovered, RangeAttribute^ rangeAttrib, bool& modified)
{ {
modified = false;
if constexpr (std::is_same_v<ManagedType, System::Enum>) if constexpr (std::is_same_v<ManagedType, System::Enum>)
{ {
if (object->GetType()->IsSubclassOf(Enum::typeid)) if (object->GetType()->IsSubclassOf(Enum::typeid))
{ {
renderEnumEditor(fieldName, object, isHovered); modified = renderEnumEditor(fieldName, object, isHovered);
return true; return true;
} }
} }
@ -99,6 +101,7 @@ namespace SHADE
if (renderFieldEditorInternal<NativeType, ManagedType>(fieldName, managedValPtr, fieldEditor, isHovered, rangeAttrib)) if (renderFieldEditorInternal<NativeType, ManagedType>(fieldName, managedValPtr, fieldEditor, isHovered, rangeAttrib))
{ {
object = managedVal; object = managedVal;
modified = true;
return true; return true;
} }
return false; return false;

View File

@ -90,7 +90,49 @@ namespace SHADE
/// The object that contains the data of the field to render. /// The object that contains the data of the field to render.
/// </param> /// </param>
static void renderFieldInInspector(System::Reflection::FieldInfo^ field, System::Object^ object); static void renderFieldInInspector(System::Reflection::FieldInfo^ field, System::Object^ object);
/// <summary>
/// Renders a raw editor for a single value.
/// </summary>
/// <param name="fieldName">The name of the field to render.</param>
/// <param name="object">Tracking reference to the object to modify.</param>
/// <param name="rangeAttrib">
/// If specified, will be used to constrain values.
/// </param>
/// <returns>True if the value was modified.</returns>
static bool renderFieldEditor(const std::string& fieldName, System::Object^% object, RangeAttribute^ rangeAttrib); static bool renderFieldEditor(const std::string& fieldName, System::Object^% object, RangeAttribute^ rangeAttrib);
/// <summary>
/// Renders a ImGui field editor based on the type of parameters specified if the
/// type matches.
/// </summary>
/// <typeparam name="NativeType">Native type of the field.</typeparam>
/// <typeparam name="ManagedType">Managed type of the field.</typeparam>
/// <param name="fieldName">Label to use for the field editor.</param>
/// <param name="managedVal">
/// Tracking reference for the managed variable to modify.
/// </param>
/// <param name="fieldEditor">ImGui field editor function to use.</param>
/// <param name="isHovered">
/// Pointer to a bool that stores if the field editor was hovered over.
/// </param>
/// <param name="rangeAttrib">
/// If provided and the type supports it, the field will be rendered with a
/// slider instead.
/// </param>
/// <param name="modified"></param>
/// <returns>True if the field was rendered..</returns>
template<typename NativeType, typename ManagedType>
static bool renderFieldEditor(const std::string& fieldName, System::Object^% object, EditorFieldFunc<NativeType> fieldEditor, bool* isHovered, RangeAttribute^ rangeAttrib, bool& modified);
/// <summary>
/// Renders a raw editor for a single enum value.
/// </summary>
/// <param name="fieldName">The name of the field to render.</param>
/// <param name="object">
/// Tracking reference to the object to modify. Must be an enum.
/// </param>
/// <param name="isHovered">
/// Pointer to a bool that stores if the field editor was hovered over.
/// </param>
/// <returns>True if the value was modified.</returns>
static bool renderEnumEditor(const std::string& fieldName, System::Object^% object, bool* isHovered); static bool renderEnumEditor(const std::string& fieldName, System::Object^% object, bool* isHovered);
/// <summary> /// <summary>
/// Checks if the specified field is of the specified native and managed type /// Checks if the specified field is of the specified native and managed type
@ -128,26 +170,6 @@ namespace SHADE
/// <returns>True if the field is modified.</returns> /// <returns>True if the field is modified.</returns>
template<typename NativeType, typename ManagedType> template<typename NativeType, typename ManagedType>
static bool renderFieldEditorInternal(const std::string& fieldName, interior_ptr<ManagedType> managedValPtr, EditorFieldFunc<NativeType> fieldEditor, bool* isHovered, RangeAttribute^ rangeAttrib); static bool renderFieldEditorInternal(const std::string& fieldName, interior_ptr<ManagedType> managedValPtr, EditorFieldFunc<NativeType> fieldEditor, bool* isHovered, RangeAttribute^ rangeAttrib);
/// <summary>
/// Renders a ImGui field editor based on the type of parameters specified.
/// </summary>
/// <typeparam name="NativeType">Native type of the field.</typeparam>
/// <typeparam name="ManagedType">Managed type of the field.</typeparam>
/// <param name="fieldName">Label to use for the field editor.</param>
/// <param name="managedVal">
/// Tracking reference for the managed variable to modify.
/// </param>
/// <param name="fieldEditor">ImGui field editor function to use.</param>
/// <param name="isHovered">
/// Pointer to a bool that stores if the field editor was hovered over.
/// </param>
/// <param name="rangeAttrib">
/// If provided and the type supports it, the field will be rendered with a
/// slider instead.
/// </param>
/// <returns>True if the field is modified.</returns>
template<typename NativeType, typename ManagedType>
static bool renderFieldEditor(const std::string& fieldName, System::Object^% object, EditorFieldFunc<NativeType> fieldEditor, bool* isHovered, RangeAttribute^ rangeAttrib);
/// <summary> /// <summary>
/// Renders a context menu when right clicked for the scripts /// Renders a context menu when right clicked for the scripts
@ -164,6 +186,9 @@ namespace SHADE
/// <param name="newData">New data to set.</param> /// <param name="newData">New data to set.</param>
/// <param name="oldData">Data that was overriden.</param> /// <param name="oldData">Data that was overriden.</param>
static void registerUndoAction(System::Object^ object, System::Reflection::FieldInfo^ field, System::Object^ newData, System::Object^ oldData); static void registerUndoAction(System::Object^ object, System::Reflection::FieldInfo^ field, System::Object^ newData, System::Object^ oldData);
static void registerUndoListChangeAction(System::Type^ type, System::Collections::IList^ list, int index, System::Object^ newData, System::Object^ oldData);
static void registerUndoListAddAction(System::Type^ type, System::Collections::IList^ list, int index, System::Object^ data);
static void registerUndoListRemoveAction(System::Type^ type, System::Collections::IList^ list, int index, System::Object^ data);
/// <summary> /// <summary>
/// Checks if a specific field has the specified attribute /// Checks if a specific field has the specified attribute
/// </summary> /// </summary>

View File

@ -132,10 +132,9 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* ListElementChangeCommand - Constructor */ /* ListElementChangeCommand - Constructor */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
generic<typename T> ListElementChangeCommand::ListElementChangeCommand(System::Collections::IList^ list, int index, System::Object^ newData, System::Object^ oldData)
ListElementChangeCommand<T>::ListElementChangeCommand(System::Collections::Generic::List<T>^ list, int index, T newData, T oldData)
: list { list } : list { list }
, index{ index } , index { index }
, newData { newData } , newData { newData }
, oldData { oldData } , oldData { oldData }
{} {}
@ -143,10 +142,9 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* ListElementChangeCommand - ICommand Functions */ /* ListElementChangeCommand - ICommand Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
generic<typename T> bool ListElementChangeCommand::Execute()
bool ListElementChangeCommand<T>::Execute()
{ {
if (list && index < System::Linq::Enumerable::Count(list)) if (list && index < list->Count)
{ {
list[index] = newData; list[index] = newData;
return true; return true;
@ -154,11 +152,10 @@ namespace SHADE
return false; return false;
} }
generic<typename T> bool ListElementChangeCommand::Unexceute()
bool ListElementChangeCommand<T>::Unexceute()
{ {
if (list && index < System::Linq::Enumerable::Count(list)) if (list && index < list->Count)
{ {
list[index] = oldData; list[index] = oldData;
return true; return true;
@ -166,11 +163,9 @@ namespace SHADE
return false; return false;
} }
bool ListElementChangeCommand::Merge(ICommand^ command)
generic<typename T>
bool ListElementChangeCommand<T>::Merge(ICommand^ command)
{ {
ListElementChangeCommand<T>^ otherCommand = safe_cast<ListElementChangeCommand<T>^>(command); ListElementChangeCommand^ otherCommand = safe_cast<ListElementChangeCommand^>(command);
if (otherCommand == nullptr) if (otherCommand == nullptr)
{ {
Debug::LogWarning("[Field Change Command] Attempted to merge two incompatible commands!"); Debug::LogWarning("[Field Change Command] Attempted to merge two incompatible commands!");
@ -187,8 +182,7 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* ListElementAddCommand - ICommand Functions */ /* ListElementAddCommand - ICommand Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
generic<typename T> ListElementAddCommand::ListElementAddCommand(System::Collections::IList^ list, int addIndex, System::Object^ data)
ListElementAddCommand<T>::ListElementAddCommand(System::Collections::Generic::List<T>^ list, int addIndex, T data)
: list { list } : list { list }
, addIndex { addIndex } , addIndex { addIndex }
, data { data } , data { data }
@ -197,8 +191,7 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* ListElementAddCommand - ICommand Functions */ /* ListElementAddCommand - ICommand Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
generic<typename T> bool ListElementAddCommand::Execute()
bool ListElementAddCommand<T>::Execute()
{ {
if (list) if (list)
{ {
@ -209,10 +202,9 @@ namespace SHADE
return false; return false;
} }
generic<typename T> bool ListElementAddCommand::Unexceute()
bool ListElementAddCommand<T>::Unexceute()
{ {
if (list && addIndex < System::Linq::Enumerable::Count(list)) if (list && addIndex < list->Count)
{ {
list->RemoveAt(addIndex); list->RemoveAt(addIndex);
return true; return true;
@ -221,8 +213,7 @@ namespace SHADE
return false; return false;
} }
generic<typename T> bool ListElementAddCommand::Merge(ICommand^)
bool ListElementAddCommand<T>::Merge(ICommand^)
{ {
// Not allowed // Not allowed
return false; return false;
@ -231,8 +222,7 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* ListElementRemoveCommand - ICommand Functions */ /* ListElementRemoveCommand - ICommand Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
generic<typename T> ListElementRemoveCommand::ListElementRemoveCommand(System::Collections::IList^ list, int removeIndex, System::Object^ data)
ListElementRemoveCommand<T>::ListElementRemoveCommand(System::Collections::Generic::List<T>^ list, int removeIndex, T data)
: list { list } : list { list }
, removeIndex { removeIndex } , removeIndex { removeIndex }
, data { data } , data { data }
@ -241,10 +231,9 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
/* ListElementRemoveCommand - ICommand Functions */ /* ListElementRemoveCommand - ICommand Functions */
/*---------------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------------*/
generic<typename T> bool ListElementRemoveCommand::Execute()
bool ListElementRemoveCommand<T>::Execute()
{ {
if (list && removeIndex < System::Linq::Enumerable::Count(list)) if (list && removeIndex < list->Count)
{ {
list->RemoveAt(removeIndex); list->RemoveAt(removeIndex);
return true; return true;
@ -253,8 +242,7 @@ namespace SHADE
return false; return false;
} }
generic<typename T> bool ListElementRemoveCommand::Unexceute()
bool ListElementRemoveCommand<T>::Unexceute()
{ {
if (list) if (list)
{ {
@ -265,8 +253,7 @@ namespace SHADE
return false; return false;
} }
generic<typename T> bool ListElementRemoveCommand::Merge(ICommand^)
bool ListElementRemoveCommand<T>::Merge(ICommand^)
{ {
// Not allowed // Not allowed
return false; return false;

View File

@ -55,53 +55,51 @@ namespace SHADE
System::Object^ oldData; System::Object^ oldData;
}; };
generic<typename T>
private ref class ListElementChangeCommand sealed : public ICommand private ref class ListElementChangeCommand sealed : public ICommand
{ {
public: public:
ListElementChangeCommand(System::Collections::Generic::List<T>^ list, int index, T newData, T oldData); ListElementChangeCommand(System::Collections::IList^ list, int index, System::Object^ newData, System::Object^ oldData);
bool Execute() override; bool Execute() override;
bool Unexceute() override; bool Unexceute() override;
bool Merge(ICommand^ command) override; bool Merge(ICommand^ command) override;
private: private:
System::Collections::Generic::List<T>^ list; System::Collections::IList^ list;
int index; int index;
T newData; System::Object^ newData;
T oldData; System::Object^ oldData;
}; };
generic<typename T>
private ref class ListElementAddCommand sealed : public ICommand private ref class ListElementAddCommand sealed : public ICommand
{ {
public: public:
ListElementAddCommand(System::Collections::Generic::List<T>^ list, int addIndex, T data); ListElementAddCommand(System::Collections::IList^ list, int addIndex, System::Object^ data);
bool Execute() override; bool Execute() override;
bool Unexceute() override; bool Unexceute() override;
bool Merge(ICommand^ command) override; bool Merge(ICommand^ command) override;
private: private:
System::Collections::Generic::List<T>^ list; System::Collections::IList^ list;
int addIndex; // New index of the added element int addIndex; // New index of the added element
T data; System::Object^ data;
}; };
generic<typename T>
private ref class ListElementRemoveCommand sealed : public ICommand private ref class ListElementRemoveCommand sealed : public ICommand
{ {
public: public:
ListElementRemoveCommand(System::Collections::Generic::List<T>^ list, int removeIndex, T data); ListElementRemoveCommand(System::Collections::IList^ list, int removeIndex, System::Object^ data);
bool Execute() override; bool Execute() override;
bool Unexceute() override; bool Unexceute() override;
bool Merge(ICommand^ command) override; bool Merge(ICommand^ command) override;
private: private:
System::Collections::Generic::List<T>^ list; System::Collections::IList^ list;
int removeIndex; // Index of the element to remove at int removeIndex; // Index of the element to remove at
T data; System::Object^ data;
}; };
/// <summary> /// <summary>