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::SameLine();
SHEditorUI::Button("+");
SHEditorUI::Indent();
@ -187,9 +188,11 @@ namespace SHADE
{
SHEditorUI::PushID(i);
System::Object^ obj = iList[i];
System::Object^ oldObj = iList[i];
if (renderFieldEditor(std::to_string(i), obj, rangeAttrib))
{
iList[i] = obj;
registerUndoListChangeAction(listType, iList, i, obj, oldObj);
}
SHEditorUI::SameLine();
SHEditorUI::Button("-");
@ -281,24 +284,26 @@ namespace SHADE
bool Editor::renderFieldEditor(const std::string& fieldName, System::Object^% object, RangeAttribute^ rangeAttrib)
{
const bool MODIFIED_PRIMITIVE =
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);
bool modified;
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)
@ -345,6 +350,33 @@ namespace SHADE
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>
Attribute Editor::hasAttribute(System::Reflection::FieldInfo^ field)
{

View File

@ -80,13 +80,15 @@ namespace SHADE
}
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 (object->GetType()->IsSubclassOf(Enum::typeid))
{
renderEnumEditor(fieldName, object, isHovered);
modified = renderEnumEditor(fieldName, object, isHovered);
return true;
}
}
@ -99,6 +101,7 @@ namespace SHADE
if (renderFieldEditorInternal<NativeType, ManagedType>(fieldName, managedValPtr, fieldEditor, isHovered, rangeAttrib))
{
object = managedVal;
modified = true;
return true;
}
return false;

View File

@ -90,7 +90,49 @@ namespace SHADE
/// The object that contains the data of the field to render.
/// </param>
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);
/// <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);
/// <summary>
/// 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>
template<typename NativeType, typename ManagedType>
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>
/// 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="oldData">Data that was overriden.</param>
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>
/// Checks if a specific field has the specified attribute
/// </summary>

View File

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

View File

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