Converted macros for script field inspectors to use templates

This commit is contained in:
Kah Wei 2022-11-10 16:20:04 +08:00
parent e8d2179d76
commit bdc7297937
6 changed files with 361 additions and 240 deletions

View File

@ -266,10 +266,10 @@ namespace SHADE
static const std::vector<std::string> COMPONENT_LABELS = { "X", "Y" };
return SHEditorWidgets::DragN<float, 2>(label, COMPONENT_LABELS, { &value.x, &value.y }, 0.1f, "%.3f", float{}, float{}, 0, isHovered);
}
bool SHEditorUI::InputVec3(const std::string& label, SHVec3& value, bool* isHovered, float speed)
bool SHEditorUI::InputVec3(const std::string& label, SHVec3& value, bool* isHovered)
{
static const std::vector<std::string> COMPONENT_LABELS = { "X", "Y", "Z"};
return SHEditorWidgets::DragN<float, 3>(label, COMPONENT_LABELS, { &value.x, &value.y, &value.z }, speed, "%.3f", float{}, float{}, 0, isHovered);
return SHEditorWidgets::DragN<float, 3>(label, COMPONENT_LABELS, { &value.x, &value.y, &value.z }, 0.1f, "%.3f", float{}, float{}, 0, isHovered);
}
bool SHEditorUI::InputTextField(const std::string& label, std::string& value, bool* isHovered)

View File

@ -296,7 +296,7 @@ namespace SHADE
/// <param name="value">Reference to the variable to store the result.</param>
/// <param name="isHovered>If set, stores the hover state of this widget.</param>
/// <returns>True if the value was changed.</returns>
static bool InputVec3(const std::string& label, SHVec3& value, bool* isHovered = nullptr, float speed = 0.1f);
static bool InputVec3(const std::string& label, SHVec3& value, bool* isHovered = nullptr);
/// <summary>
/// Creates a text field widget for string input.
/// <br/>

View File

@ -31,98 +31,13 @@ of DigiPen Institute of Technology is prohibited.
#include "Editor/Command/SHCommand.hpp"
#include "TooltipAttribute.hxx"
#include "RangeAttribute.hxx"
#include "Math/Vector2.hxx"
#include "Math/Vector3.hxx"
// Using Directives
using namespace System;
using namespace System::Collections::Generic;
/*-------------------------------------------------------------------------------------*/
/* Macro Functions */
/*-------------------------------------------------------------------------------------*/
/// <summary>
/// Macro expansion that is used in renderFieldInInspector() to check the type of a field
/// named "field" against the specified type and if it matches, retrieves the value of
/// that field from an object named "object" and pass it into the specified SHEditorUI::
/// function named "FUNC" by casting it into the NATIVE_TYPE specified.
/// <br/>
/// This only works for primitive types that have the same types for managed and native.
/// </summary>
/// <param name="MANAGED_TYPE">The managed type of the object to edit.</param>
/// <param name="NATIVE_TYPE">The native type of the object to edit.</param>
/// <param name="FUNC">The SHEditorUI:: function to use for editing.</param>
#define RENDER_FIELD(MANAGED_TYPE, NATIVE_TYPE, FUNC) \
(field->FieldType == MANAGED_TYPE::typeid) \
{ \
NATIVE_TYPE val = safe_cast<NATIVE_TYPE>(field->GetValue(object)); \
NATIVE_TYPE oldVal = val; \
if (SHEditorUI::FUNC(Convert::ToNative(field->Name), val, &isHovered))\
{ \
field->SetValue(object, val); \
registerUndoAction(object, field, val, oldVal); \
} \
} \
/// <summary>
/// Alternative to RENDER_FIELD that checks for RangeAttribute and switches to a slider
/// instead.
/// </summary>
/// <param name="MANAGED_TYPE">The managed type of the object to edit.</param>
/// <param name="NATIVE_TYPE">The native type of the object to edit.</param>
/// <param name="FUNC">The SHEditorUI:: function to use for editing.</param>
#define RENDER_FIELD_RANGE(MANAGED_TYPE, NATIVE_TYPE, FUNC) \
(field->FieldType == MANAGED_TYPE::typeid) \
{ \
NATIVE_TYPE val = safe_cast<NATIVE_TYPE>(field->GetValue(object)); \
NATIVE_TYPE oldVal = val; \
\
RangeAttribute^ rangeAttrib = hasAttribute<RangeAttribute^>(field);\
const std::string FIELD_NAME = Convert::ToNative(field->Name); \
bool changed = false; \
if (rangeAttrib) \
{ \
changed = SHEditorUI::InputSlider \
( \
FIELD_NAME, \
static_cast<NATIVE_TYPE>(rangeAttrib->Min), \
static_cast<NATIVE_TYPE>(rangeAttrib->Max), \
val, &isHovered \
); \
} \
else \
{ \
changed = SHEditorUI::FUNC(FIELD_NAME, val, &isHovered); \
} \
\
if (changed) \
{ \
field->SetValue(object, val); \
registerUndoAction(object, field, val, oldVal); \
} \
} \
/// <summary>
/// Macro expansion that is used in renderFieldInInspector() to check the type of a field
/// named "field" against the specified type and if it matches, retrieves the value of
/// that field from an object named "object" and pass it into the specified SHEditorUI::
/// function named "FUNC" by casting it into the NATIVE_TYPE specified.
/// <br/>
/// This only works for types that have an implementation of Convert::ToNative and
/// Convert::ToCLI.
/// </summary>
/// <param name="MANAGED_TYPE">The managed type of the object to edit.</param>
/// <param name="NATIVE_TYPE">The native type of the object to edit.</param>
/// <param name="FUNC">The SHEditorUI:: function to use for editing.</param>
#define RENDER_FIELD_CASTED(MANAGED_TYPE, NATIVE_TYPE, FUNC) \
(field->FieldType == MANAGED_TYPE::typeid) \
{ \
NATIVE_TYPE val = Convert::ToNative(safe_cast<MANAGED_TYPE>(field->GetValue(object))); \
NATIVE_TYPE oldVal = val; \
\
if (SHEditorUI::FUNC(Convert::ToNative(field->Name), val, &isHovered)) \
{ \
field->SetValue(object, Convert::ToCLI(val)); \
registerUndoAction(object, field, Convert::ToCLI(val), Convert::ToCLI(oldVal)); \
} \
} \
/*-------------------------------------------------------------------------------------*/
/* Function Definitions */
/*-------------------------------------------------------------------------------------*/
@ -238,17 +153,23 @@ namespace SHADE
{
bool isHovered = false;
if RENDER_FIELD_RANGE (Int16, int, InputInt)
else if RENDER_FIELD_RANGE (Int32, int, InputInt)
else if RENDER_FIELD_RANGE (Int64, int, InputInt)
else if RENDER_FIELD_RANGE (UInt16, unsigned int, InputUnsignedInt)
else if RENDER_FIELD_RANGE (UInt32, unsigned int, InputUnsignedInt)
else if RENDER_FIELD_RANGE (UInt64, unsigned int, InputUnsignedInt)
else if RENDER_FIELD_RANGE (Byte, int, InputInt)
else if RENDER_FIELD (bool, bool, InputCheckbox)
else if RENDER_FIELD_RANGE (float, float, InputFloat)
else if RENDER_FIELD_RANGE (double, double, InputDouble)
else if (field->FieldType->IsSubclassOf(Enum::typeid))
const bool MODIFIED_PRIMITIVE =
renderFieldInInspector<int , Int16 >(field, object, SHEditorUI::InputInt , &isHovered) ||
renderFieldInInspector<int , Int32 >(field, object, SHEditorUI::InputInt , &isHovered) ||
renderFieldInInspector<int , Int64 >(field, object, SHEditorUI::InputInt , &isHovered) ||
renderFieldInInspector<int , UInt16 >(field, object, SHEditorUI::InputInt , &isHovered) ||
renderFieldInInspector<int , UInt32 >(field, object, SHEditorUI::InputInt , &isHovered) ||
renderFieldInInspector<int , UInt64 >(field, object, SHEditorUI::InputInt , &isHovered) ||
renderFieldInInspector<int , Byte >(field, object, SHEditorUI::InputInt , &isHovered) ||
renderFieldInInspector<bool , bool >(field, object, SHEditorUI::InputCheckbox, &isHovered) ||
renderFieldInInspector<float , float >(field, object, SHEditorUI::InputFloat , &isHovered) ||
renderFieldInInspector<double, double >(field, object, SHEditorUI::InputDouble , &isHovered) ||
renderFieldInInspector<SHVec2, Vector2>(field, object, SHEditorUI::InputVec2 , &isHovered) ||
renderFieldInInspector<SHVec3, Vector3>(field, object, SHEditorUI::InputVec3 , &isHovered);
if (!MODIFIED_PRIMITIVE)
{
if (field->FieldType->IsSubclassOf(Enum::typeid))
{
// Get all the names of the enums
const array<String^>^ ENUM_NAMES = field->FieldType->GetEnumNames();
@ -266,8 +187,6 @@ namespace SHADE
registerUndoAction(object, field, val, oldVal);
}
}
else if RENDER_FIELD_CASTED(Vector2, SHVec2, InputVec2)
else if RENDER_FIELD_CASTED(Vector3, SHVec3, InputVec3)
else if (field->FieldType == String::typeid)
{
// Prevent issues where String^ is null due to being empty
@ -395,6 +314,7 @@ namespace SHADE
return;
}
}
}
// Check if the field has a specific attribute
TooltipAttribute^ toolTip = hasAttribute<TooltipAttribute^>(field);

View File

@ -0,0 +1,109 @@
/************************************************************************************//*!
\file Editor.h++
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Nov 10, 2022
\brief Contains the definition of templated functions for the managed Editor
static 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
// Primary Include
#include "Editor.hxx"
namespace SHADE
{
template<typename NativeType, typename ManagedType>
bool Editor::renderFieldInInspector(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, EditorFieldFunc<NativeType> fieldEditor, bool* isHovered)
{
if (fieldInfo->FieldType == ManagedType::typeid)
{
RangeAttribute^ rangeAttrib;
if constexpr (std::is_arithmetic_v<NativeType> && !std::is_same_v<NativeType, bool>)
{
rangeAttrib = hasAttribute<RangeAttribute^>(fieldInfo);
}
ManagedType val = safe_cast<ManagedType>(fieldInfo->GetValue(object));
if (renderFieldInInspector<NativeType, ManagedType>
(
Convert::ToNative(fieldInfo->Name),
val,
fieldEditor,
isHovered,
rangeAttrib
))
{
fieldInfo->SetValue(object, val);
// TODO: Register undo
}
return true;
}
return false;
}
template<typename NativeType, typename ManagedType>
bool Editor::renderFieldInInspector(const std::string& fieldName, ManagedType% managedVal, EditorFieldFunc<NativeType> fieldEditor, bool* isHovered, RangeAttribute^ rangeAttrib)
{
// Retrieve the native version of the object
NativeType val;
if constexpr (IsPrimitiveTypeMatches_V<NativeType>)
{
val = safe_cast<NativeType>(managedVal);
}
else
{
val = Convert::ToNative(managedVal);
}
// Throw into the SHEditorUI function
NativeType oldVal = val;
bool changed = false;
if (rangeAttrib)
{
// Do not allow bools for Sliders just in case
if constexpr (std::is_arithmetic_v<NativeType> && !std::is_same_v<NativeType, bool>)
{
changed = SHEditorUI::InputSlider
(
fieldName,
static_cast<NativeType>(rangeAttrib->Min),
static_cast<NativeType>(rangeAttrib->Max),
val, isHovered
);
}
}
else
{
changed = fieldEditor(fieldName, val, isHovered);
}
if (changed)
{
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;
}
return false;
}
}

View File

@ -17,9 +17,14 @@ of DigiPen Institute of Technology is prohibited.
#include "Engine/Entity.hxx"
#include "Scripts/Script.hxx"
#include "UndoRedoStack.hxx"
#include "RangeAttribute.hxx"
namespace SHADE
{
template<typename NativeType>
using EditorFieldFunc = bool(*)(const std::string& label, NativeType& val, bool* isHovered);
/// <summary>
/// Static class for Editor-related functions
/// </summary>
@ -91,8 +96,59 @@ namespace SHADE
/// <param name="entity">The Entity to render the Scripts of.</param>
/// <param name="script">The Script to render the inspector for.</param>
static void renderScriptContextMenu(Entity entity, Script^ script);
/// <summary>
/// Adds changes to a variable as an undo-able/redo-able action on the Undo-Redo
/// stack.
/// </summary>
/// <param name="object">The object that changes are applied to.</param>
/// <param name="field">The field that was changed.</param>
/// <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);
/// <summary>
/// Checks if a specific field has the specified attribute
/// </summary>
/// <typeparam name="Attribute">Type of Attribute to check for.</typeparam>
/// <param name="field">The field to check.</param>
/// <returns>The attribute to check for if it exists. Null otherwise.</returns>
generic<typename Attribute> where Attribute : System::Attribute
static Attribute hasAttribute(System::Reflection::FieldInfo^ field);
/// <summary>
/// Checks if the specified field is of the specified native and managed type
/// equivalent and renders a ImGui field editor based on the specified field
/// editor function. Also handles fields that contain a RangeAttribute.
/// </summary>
/// <typeparam name="NativeType">Native type of the field.</typeparam>
/// <typeparam name="ManagedType">Managed type of the field.</typeparam>
/// <param name="fieldInfo">Describes the field to modify.</param>
/// <param name="object">Object to modify that has the specified field.</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>
/// <returns>True if the field is modified.</returns>
template<typename NativeType, typename ManagedType>
static bool renderFieldInInspector(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, EditorFieldFunc<NativeType> fieldEditor, bool* isHovered);
/// <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 renderFieldInInspector(const std::string& fieldName, ManagedType% managedVal, EditorFieldFunc<NativeType> fieldEditor, bool* isHovered, RangeAttribute^ rangeAttrib);
};
}
#include "Editor.h++"

View File

@ -152,6 +152,40 @@ namespace SHADE
};
/// <summary>
/// Checks if the specified type is matching between native C++ and the managed type.
/// </summary>
/// <typeparam name="T">Type to check.</typeparam>
template<typename T>
struct IsPrimitiveTypeMatches : public std::integral_constant
<
bool,
std::is_same_v<System::Int16 , typename std::remove_cv_t<T>> ||
std::is_same_v<System::Int32 , typename std::remove_cv_t<T>> ||
std::is_same_v<System::Int64 , typename std::remove_cv_t<T>> ||
std::is_same_v<System::UInt16, typename std::remove_cv_t<T>> ||
std::is_same_v<System::UInt32, typename std::remove_cv_t<T>> ||
std::is_same_v<System::UInt64, typename std::remove_cv_t<T>> ||
std::is_same_v<System::Byte , typename std::remove_cv_t<T>> ||
std::is_same_v<bool , typename std::remove_cv_t<T>> ||
std::is_same_v<double , typename std::remove_cv_t<T>> ||
std::is_same_v<float , typename std::remove_cv_t<T>> ||
std::is_same_v<int8_t , typename std::remove_cv_t<T>> ||
std::is_same_v<int16_t , typename std::remove_cv_t<T>> ||
std::is_same_v<int32_t , typename std::remove_cv_t<T>> ||
std::is_same_v<int64_t , typename std::remove_cv_t<T>> ||
std::is_same_v<uint16_t , typename std::remove_cv_t<T>> ||
std::is_same_v<uint32_t , typename std::remove_cv_t<T>> ||
std::is_same_v<uint64_t , typename std::remove_cv_t<T>>
>
{};
/// <summary>
/// Short hand for IsPrimitiveTypeMatches::value
/// </summary>
/// <typeparam name="T">Type to check.</typeparam>
template<typename T>
inline constexpr bool IsPrimitiveTypeMatches_V = IsPrimitiveTypeMatches<T>::value;
/// <summary>
/// Type Transformer for managed types to native types.
/// </summary>
@ -163,6 +197,7 @@ namespace SHADE
{
public:
using Value = void;
static bool IsDefined() { return is_same_v<ManagedType, Value>; }
};
template<> struct ToNativeType<System::Int16> { using Value = int16_t; };
template<> struct ToNativeType<System::Int32> { using Value = int32_t; };
@ -195,6 +230,7 @@ namespace SHADE
{
public:
using Value = void;
static bool IsDefined() { return is_same_v<NativeType, Value>; }
};
template<> struct ToManagedType<int8_t > { using Value = System::Byte; };
template<> struct ToManagedType<int16_t > { using Value = System::Int16; };