Added unit tests for groups and toggle groups, fixed comments and syntax

Signed-off-by: Jose <jotamkin@amazon.com>
monroegm-disable-blank-issue-2
Jose 5 years ago
parent bbf9a06133
commit bc9d0eb0e1

@ -237,13 +237,13 @@ namespace AZ
/**
* Declare element with attributes that belong to the class SerializeContext::Class, this is a logical structure, you can have one or more ClassElements.
* \uiId is the logical element ID (for instance "Group" when you want to group certain elements this class.
* then in each DataElement you can attach the appropriate group attribute.
* \param memberVariable - reference to the member variable to we can bind to serializations data.
* Declare element with attributes that belong to the class SerializeContext::Class, this is a logical structure, you can have one or more GroupElementToggles.
* T must be a boolean variable that will enable and disable each DataElement attached to this structure.
* \param description - Descriptive name of the field that will typically appear in a tooltip.
* \param memberVariable - reference to the member variable so we can bind to serialization data.
*/
template<class T>
ClassBuilder* ClassElement(Crc32 elementIdCrc, const char* description, T memberVariable);
ClassBuilder* GroupElementToggle(const char* description, T memberVariable);
/**
@ -529,48 +529,9 @@ namespace AZ
// ClassElement
//=========================================================================
template<class T>
inline EditContext::ClassBuilder* EditContext::ClassBuilder::ClassElement(Crc32 elementIdCrc, const char* description, T memberVariable)
inline EditContext::ClassBuilder* EditContext::ClassBuilder::GroupElementToggle(const char* name, T memberVariable)
{
if (IsValid())
{
using ElementTypeInfo = typename SerializeInternal::ElementInfo<T>;
AZ_Assert(
m_classData->m_typeId == AzTypeInfo<typename ElementTypeInfo::ClassType>::Uuid(),
"Data element (%s) belongs to a different class!", description);
// Not really portable but works for the supported compilers
size_t offset =
reinterpret_cast<size_t>(&(reinterpret_cast<typename ElementTypeInfo::ClassType const volatile*>(0)->*memberVariable));
// offset = or pass it to the function with offsetof(typename ElementTypeInfo::ClassType,memberVariable);
SerializeContext::ClassElement* classElement = nullptr;
for (size_t i = 0; i < m_classData->m_elements.size(); ++i)
{
SerializeContext::ClassElement* element = &m_classData->m_elements[i];
if (element->m_offset == offset)
{
classElement = element;
break;
}
}
// We cannot continue past this point, we must alert the user to fix their serialization config and crash
AZ_Assert(
classElement,
"Class element for editor data element reflection '%s' was NOT found in the serialize context! This member MUST be "
"serializable to be editable!",
description);
m_classElement->m_elements.push_back();
Edit::ElementData& ed = m_classElement->m_elements.back();
classElement->m_editData = &ed;
m_editElement = &ed;
ed.m_elementId = elementIdCrc;
ed.m_name = description;
ed.m_description = description;
ed.m_serializeClassElement = classElement;
}
return this;
return DataElement(AZ::Edit::ClassElements::Group, memberVariable, name, name, "");
}
//=========================================================================

@ -1112,7 +1112,8 @@ namespace AzToolsFramework
const AZ::Edit::ElementData* groupData = nullptr;
for (const AZ::Edit::ElementData& elementData : parentEditData->m_elements)
{
if ((node->m_elementEditData == &elementData) && (elementData.m_elementId != AZ::Edit::ClassElements::Group)) // this element matches this node
// this element matches this node
if ((node->m_elementEditData == &elementData) && (elementData.m_elementId != AZ::Edit::ClassElements::Group))
{
// Record the last found group data
node->m_groupElementData = groupData;

@ -1120,10 +1120,10 @@ namespace AzToolsFramework
if (!m_toggleSwitch)
{
m_handlerName = AZ::Edit::UIHandlers::CheckBox;
EBUS_EVENT_RESULT(m_handler, PropertyTypeRegistrationMessages::Bus, ResolvePropertyHandler, m_handlerName, azrtti_typeid<bool>());
PropertyTypeRegistrationMessages::Bus::BroadcastResult(m_handler, &PropertyTypeRegistrationMessages::Bus::Events::ResolvePropertyHandler, m_handlerName, azrtti_typeid<bool>());
m_toggleSwitch = m_handler->CreateGUI(this);
m_middleLayout->insertWidget(0, m_toggleSwitch, 1);
auto checkBoxCtrl = reinterpret_cast<AzToolsFramework::PropertyCheckBoxCtrl*>(m_toggleSwitch);
auto checkBoxCtrl = static_cast<AzToolsFramework::PropertyCheckBoxCtrl*>(m_toggleSwitch);
QObject::connect(checkBoxCtrl, &AzToolsFramework::PropertyCheckBoxCtrl::valueChanged, this, &PropertyRowWidget::OnClickedToggleButton);
}
}
@ -1138,7 +1138,7 @@ namespace AzToolsFramework
void PropertyRowWidget::OnClickedToggleButton(bool checked)
{
if ((m_expanded && !checked) || (!m_expanded && checked))
if (m_expanded != checked)
{
DoExpandOrContract(!IsExpanded(), 0 != (QGuiApplication::keyboardModifiers() & Qt::ControlModifier));
}

@ -143,6 +143,7 @@ namespace AzToolsFramework
QToolButton* GetIndicatorButton() { return m_indicatorButton; }
QLabel* GetNameLabel() { return m_nameLabel; }
QWidget* GetToggle() { return m_toggleSwitch; }
const QWidget* GetToggle() const { return m_toggleSwitch; }
void SetIndentSize(int w);
void SetAsCustom(bool custom) { m_custom = custom; }

@ -167,7 +167,7 @@ namespace AzToolsFramework
InstanceDataHierarchyList m_instances; ///< List of instance sets to display, other one can aggregate other instances.
InstanceDataHierarchy::ValueComparisonFunction m_valueComparisonFunction;
ReflectedPropertyEditor::WidgetList m_widgets;
ReflectedPropertyEditor::SpecialGroupWidgetList m_specialGroupWidgets;
ReflectedPropertyEditor::WidgetList m_specialGroupWidgets;
InstanceDataNode* groupSourceNode = nullptr;
RowContainerType m_widgetsInDisplayOrder;
UserWidgetToDataMap m_userWidgetsToData;
@ -626,7 +626,7 @@ namespace AzToolsFramework
// creates and populates the GUI to edit the property if not already created
void ReflectedPropertyEditor::Impl::CreateEditorWidget(PropertyRowWidget* pWidget)
{
if ((!pWidget->HasChildWidgetAlready()) && (!pWidget->GetToggle()))
if (!pWidget->HasChildWidgetAlready() && !pWidget->GetToggle())
{
PropertyHandlerBase* pHandler = pWidget->GetHandler();
if (pHandler)
@ -753,7 +753,7 @@ namespace AzToolsFramework
}
}
}
if ((!node->GetElementEditMetadata()) || (node->GetElementEditMetadata()->m_elementId != AZ::Edit::ClassElements::Group))
if (!node->GetElementEditMetadata() || (node->GetElementEditMetadata()->m_elementId != AZ::Edit::ClassElements::Group))
{
pWidget = CreateOrPullFromPool();
pWidget->show();
@ -787,7 +787,7 @@ namespace AzToolsFramework
}
// Save the last InstanceDataNode that is a Group ClassElement so that we can use it as the source node for its widget.
if ((node->GetElementEditMetadata()) && (node->GetElementEditMetadata()->m_elementId == AZ::Edit::ClassElements::Group))
if (node->GetElementEditMetadata() && (node->GetElementEditMetadata()->m_elementId == AZ::Edit::ClassElements::Group))
{
groupSourceNode = node;
}
@ -1382,16 +1382,14 @@ namespace AzToolsFramework
return;
}
// get the property editor
// Get the property editor from either the widget map or the special toggle group widgets
auto rowWidget = m_widgets.find(it->second);
auto rowWidgetGroup = m_specialGroupWidgets.find(it->second);
if (rowWidget != m_widgets.end() || rowWidgetGroup != m_specialGroupWidgets.end())
if (rowWidget == m_widgets.end())
{
rowWidget = m_specialGroupWidgets.find(it->second);
}
if (rowWidget != m_widgets.end() || rowWidget != m_specialGroupWidgets.end())
{
if (rowWidget == m_widgets.end())
{
rowWidget = rowWidgetGroup;
}
InstanceDataNode* node = rowWidget->first;
PropertyRowWidget* widget = rowWidget->second;
PropertyHandlerBase* handler = widget->GetHandler();

@ -50,7 +50,7 @@ namespace AzToolsFramework
typedef AZStd::unordered_map<InstanceDataNode*, PropertyRowWidget*> WidgetList;
typedef AZStd::unordered_map<InstanceDataNode*, PropertyRowWidget*> SpecialGroupWidgetList;
ReflectedPropertyEditor::WidgetList m_specialGroupWidgets;
ReflectedPropertyEditor(QWidget* pParent);
virtual ~ReflectedPropertyEditor();

@ -20,6 +20,7 @@
#include <AzCore/Serialization/Utils.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <random>
#include <QDebug>
using namespace AZ;
@ -726,6 +727,101 @@ namespace UnitTest
};
class InstanceDataHierarchyGroupTestFixture
: public AllocatorsFixture
{
public:
InstanceDataHierarchyGroupTestFixture() = default;
};
class GroupTestComponent
: public AZ::Component
{
public:
AZ_COMPONENT(GroupTestComponent, "{C088C81D-D59D-43F1-85F8-B2E591BABA36}")
GroupTestComponent() = default;
struct SubData
{
AZ_TYPE_INFO(SubData, "{983316B5-17C0-476E-9CEB-CA749B3ABE5D}");
AZ_CLASS_ALLOCATOR(SubData, AZ::SystemAllocator, 0);
SubData() {}
SubData(int v) : m_int(v) {}
SubData(bool b) : m_bool(b) {}
SubData(float f) : m_float(f) {}
~SubData() = default;
float m_float = 0.f;
int m_int = 0;
bool m_bool = true;
};
static void Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<SubData>()
->Version(1)
->Field("SubInt", &SubData::m_int)
->Field("SubToggle", &SubData::m_bool)
->Field("SubFloat", &SubData::m_float)
;
serializeContext->Class<GroupTestComponent, AZ::Component>()
->Version(1)
->Field("Float", &GroupTestComponent::m_float)
->Field("GroupToggle", &GroupTestComponent::m_groupToggle)
->Field("GroupFloat", &GroupTestComponent::m_groupFloat)
->Field("ToggleGroupInt", &GroupTestComponent::m_toggleGroupInt)
->Field("SubDataNormal", &GroupTestComponent::m_subGroupForNormal)
->Field("SubDataToggle", &GroupTestComponent::m_subGroupForToggle)
;
if (AZ::EditContext* edit = serializeContext->GetEditContext())
{
edit->Class<GroupTestComponent>("Group Test Component", "Testing normal groups and toggle groups")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->DataElement(0, &GroupTestComponent::m_float, "Float Field", "A float field")
->ClassElement(AZ::Edit::ClassElements::Group, "Normal Group")
->DataElement(0, &GroupTestComponent::m_groupFloat, "Float Field", "A float field")
->DataElement(0, &GroupTestComponent::m_subGroupForNormal, "Struct Field", "A sub data type")
->GroupElementToggle("Group Toggle", &GroupTestComponent::m_groupToggle)
->DataElement(0, &GroupTestComponent::m_toggleGroupInt, "Normal Integer", "An Integer")
->DataElement(0, &GroupTestComponent::m_subGroupForToggle, "Struct Field", "A sub data type")
;
edit->Class<SubData>("SubGroup Test Component", "Testing nested normal groups and toggle groups")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->ClassElement(AZ::Edit::ClassElements::Group, "Normal SubGroup")
->DataElement(0, &SubData::m_int, "SubGroup Int Field", "An int")
->GroupElementToggle("SubGroup Toggle", &SubData::m_bool)
->DataElement(0, &SubData::m_float, "SubGroup Float Field", "An int")
;
}
}
}
void Activate() override
{
}
void Deactivate() override
{
}
float m_float = 0.f;
float m_groupFloat = 0.f;
int m_toggleGroupInt = 0;
AZStd::string m_string;
bool m_groupToggle = false;
SubData m_subGroupForNormal;
SubData m_subGroupForToggle;
};
class InstanceDataHierarchyKeyedContainerTest
: public AllocatorsFixture
{
@ -1314,4 +1410,243 @@ namespace UnitTest
run();
}
TEST_F(InstanceDataHierarchyGroupTestFixture, TestNormalGroups)
{
using namespace AzToolsFramework;
// Setting up the data node hierarchy
AZ::SerializeContext serializeContext;
serializeContext.CreateEditContext();
Entity::Reflect(&serializeContext);
GroupTestComponent::Reflect(&serializeContext);
AZStd::unique_ptr<AZ::Entity> testEntity1(new AZ::Entity());
testEntity1->CreateComponent<GroupTestComponent>();
InstanceDataHierarchy instanceDataHierarchy;
instanceDataHierarchy.AddRootInstance(testEntity1.get());
instanceDataHierarchy.Build(&serializeContext, 0);
// Adding the nodes to a node stack
auto rootNode = instanceDataHierarchy.GetRootNode();
AZStd::stack<InstanceDataNode*> nodeStack;
nodeStack.push(rootNode);
InstanceDataNode* componentNode1 = nullptr;
while (!nodeStack.empty())
{
InstanceDataNode* node = nodeStack.top();
nodeStack.pop();
if (node->GetClassMetadata()->m_typeId == AZ::AzTypeInfo<GroupTestComponent>::Uuid())
{
componentNode1 = node;
break;
}
for (InstanceDataNode& child : node->GetChildren())
{
nodeStack.push(&child);
}
}
// Iterating through the children in the instance data hierarchy to verify their properties
ASSERT_TRUE(componentNode1 != nullptr);
for (auto child : componentNode1->GetChildren())
{
AZStd::string childName(child.GetElementMetadata()->m_name);
if (childName.compare("GroupFloat") == 0)
{
// False for any child node with serializable data
ASSERT_FALSE(child.GetElementEditMetadata()->IsClassElement());
// Child node should never be a ClassElement::Group, unless it is the root node of a ToggleGroup
ASSERT_NE(child.GetElementEditMetadata()->m_elementId, AZ::Edit::ClassElements::Group);
// Ensuring that this node was assigned to the appropriate group
ASSERT_EQ(child.GetGroupElementMetadata()->m_description, "Normal Group");
// Ensuring that this node has the correct parent
ASSERT_EQ(child.GetParent()->GetClassMetadata()->m_name, "GroupTestComponent");
}
}
}
TEST_F(InstanceDataHierarchyGroupTestFixture, TestToggleGroups)
{
using namespace AzToolsFramework;
// Setting up the data node hierarchy
AZ::SerializeContext serializeContext;
serializeContext.CreateEditContext();
Entity::Reflect(&serializeContext);
GroupTestComponent::Reflect(&serializeContext);
AZStd::unique_ptr<AZ::Entity> testEntity1(new AZ::Entity());
testEntity1->CreateComponent<GroupTestComponent>();
InstanceDataHierarchy instanceDataHierarchy;
instanceDataHierarchy.AddRootInstance(testEntity1.get());
instanceDataHierarchy.Build(&serializeContext, 0);
// Adding the nodes to a node stack
auto rootNode = instanceDataHierarchy.GetRootNode();
AZStd::stack<InstanceDataNode*> nodeStack;
nodeStack.push(rootNode);
InstanceDataNode* componentNode1 = nullptr;
while (!nodeStack.empty())
{
InstanceDataNode* node = nodeStack.top();
nodeStack.pop();
if (node->GetClassMetadata()->m_typeId == AZ::AzTypeInfo<GroupTestComponent>::Uuid())
{
componentNode1 = node;
break;
}
for (InstanceDataNode& child : node->GetChildren())
{
nodeStack.push(&child);
}
}
// Iterating through the children in the instance data hierarchy to verify their properties
ASSERT_TRUE(componentNode1 != nullptr);
for (auto child : componentNode1->GetChildren())
{
AZStd::string childName(child.GetElementMetadata()->m_name);
if (childName.compare("GroupToggle") == 0)
{
// False for any child node with serializable data
ASSERT_FALSE(child.GetElementEditMetadata()->IsClassElement());
// Child node is the root node of a ToggleGroup, so it should be a ClassElement::Group
ASSERT_EQ(child.GetElementEditMetadata()->m_elementId, AZ::Edit::ClassElements::Group);
// Ensuring that this node has the correct parent
ASSERT_EQ(child.GetParent()->GetClassMetadata()->m_name, "GroupTestComponent");
}
if (childName.compare("ToggleGroupInt") == 0)
{
// False for any child node with serializable data
ASSERT_FALSE(child.GetElementEditMetadata()->IsClassElement());
// Child node should never be a ClassElement::Group, unless it is the root node of a ToggleGroup
ASSERT_NE(child.GetElementEditMetadata()->m_elementId, AZ::Edit::ClassElements::Group);
// Ensuring that this node was assigned to the appropriate group
ASSERT_EQ(child.GetGroupElementMetadata()->m_description, "Group Toggle");
// Ensuring that this node has the correct parent
ASSERT_EQ(child.GetParent()->GetClassMetadata()->m_name, "GroupTestComponent");
}
}
}
TEST_F(InstanceDataHierarchyGroupTestFixture, TestNestedGroups)
{
using namespace AzToolsFramework;
// Setting up the data node hierarchy
AZ::SerializeContext serializeContext;
serializeContext.CreateEditContext();
Entity::Reflect(&serializeContext);
GroupTestComponent::Reflect(&serializeContext);
AZStd::unique_ptr<AZ::Entity> testEntity1(new AZ::Entity());
testEntity1->CreateComponent<GroupTestComponent>();
InstanceDataHierarchy instanceDataHierarchy;
instanceDataHierarchy.AddRootInstance(testEntity1.get());
instanceDataHierarchy.Build(&serializeContext, 0);
// Adding the nodes to a node stack
auto rootNode = instanceDataHierarchy.GetRootNode();
AZStd::stack<InstanceDataNode*> nodeStack;
nodeStack.push(rootNode);
InstanceDataNode* componentNode1 = nullptr;
while (!nodeStack.empty())
{
InstanceDataNode* node = nodeStack.top();
nodeStack.pop();
if (node->GetClassMetadata()->m_typeId == AZ::AzTypeInfo<GroupTestComponent>::Uuid())
{
componentNode1 = node;
break;
}
for (InstanceDataNode& child : node->GetChildren())
{
nodeStack.push(&child);
}
}
// Iterating through the children in the instance data hierarchy to verify their properties
ASSERT_TRUE(componentNode1 != nullptr);
for (auto child : componentNode1->GetChildren())
{
AZStd::string childName(child.GetElementMetadata()->m_name);
if (childName.compare("SubDataNormal") == 0)
{
for (InstanceDataNode& subChild : child.GetChildren())
{
childName = subChild.GetElementMetadata()->m_name;
if (childName.compare("SubInt") == 0)
{
// False for any child node with serializable data
ASSERT_FALSE(subChild.GetElementEditMetadata()->IsClassElement());
// Child node should never be a ClassElement::Group, unless it is the root node of a ToggleGroup
ASSERT_NE(subChild.GetElementEditMetadata()->m_elementId, AZ::Edit::ClassElements::Group);
// Ensuring that this node was assigned to the appropriate group
ASSERT_EQ(subChild.GetGroupElementMetadata()->m_description, "Normal SubGroup");
// Ensuring that this node has the correct parent
ASSERT_EQ(subChild.GetParent()->GetClassMetadata()->m_name, "SubData");
}
if (childName.compare("SubToggle") == 0)
{
// False for any child node with serializable data
ASSERT_FALSE(subChild.GetElementEditMetadata()->IsClassElement());
// Child node is the root node of a ToggleGroup, so it should be a ClassElement::Group
ASSERT_EQ(subChild.GetElementEditMetadata()->m_elementId, AZ::Edit::ClassElements::Group);
// Ensuring that this node has the correct parent
ASSERT_EQ(subChild.GetParent()->GetClassMetadata()->m_name, "SubData");
}
if (childName.compare("SubFloat") == 0)
{
// False for any child node with serializable data
ASSERT_FALSE(subChild.GetElementEditMetadata()->IsClassElement());
// Child node should never be a ClassElement::Group, unless it is the root node of a ToggleGroup
ASSERT_NE(subChild.GetElementEditMetadata()->m_elementId, AZ::Edit::ClassElements::Group);
// Ensuring that this node was assigned to the appropriate group
ASSERT_EQ(subChild.GetGroupElementMetadata()->m_description, "SubGroup Toggle");
// Ensuring that this node has the correct parent
ASSERT_EQ(subChild.GetParent()->GetClassMetadata()->m_name, "SubData");
}
}
}
if (childName.compare("SubDataToggle") == 0)
{
for (InstanceDataNode& subChild : child.GetChildren())
{
childName = subChild.GetElementMetadata()->m_name;
if (childName.compare("SubInt") == 0)
{
// False for any child node with serializable data
ASSERT_FALSE(subChild.GetElementEditMetadata()->IsClassElement());
// Child node should never be a ClassElement::Group, unless it is the root node of a ToggleGroup
ASSERT_NE(subChild.GetElementEditMetadata()->m_elementId, AZ::Edit::ClassElements::Group);
// Ensuring that this node was assigned to the appropriate group
ASSERT_EQ(subChild.GetGroupElementMetadata()->m_description, "Normal SubGroup");
// Ensuring that this node has the correct parent
ASSERT_EQ(subChild.GetParent()->GetClassMetadata()->m_name, "SubData");
}
if (childName.compare("SubToggle") == 0)
{
// False for any child node with serializable data
ASSERT_FALSE(subChild.GetElementEditMetadata()->IsClassElement());
// Child node is the root node of a ToggleGroup, so it should be a ClassElement::Group
ASSERT_EQ(subChild.GetElementEditMetadata()->m_elementId, AZ::Edit::ClassElements::Group);
// Ensuring that this node has the correct parent
ASSERT_EQ(subChild.GetParent()->GetClassMetadata()->m_name, "SubData");
}
if (childName.compare("SubFloat") == 0)
{
// False for any child node with serializable data
ASSERT_FALSE(subChild.GetElementEditMetadata()->IsClassElement());
// Child node should never be a ClassElement::Group, unless it is the root node of a ToggleGroup
ASSERT_NE(subChild.GetElementEditMetadata()->m_elementId, AZ::Edit::ClassElements::Group);
// Ensuring that this node was assigned to the appropriate group
ASSERT_EQ(subChild.GetGroupElementMetadata()->m_description, "SubGroup Toggle");
// Ensuring that this node has the correct parent
ASSERT_EQ(subChild.GetParent()->GetClassMetadata()->m_name, "SubData");
}
}
}
}
}
} // namespace UnitTest

@ -62,8 +62,9 @@ namespace GradientSignal
->DataElement(0, &GradientSampler::m_invertInput, "Invert Input", "")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &GradientSampler::ChangeNotify)
->ClassElement(AZ::Edit::ClassElements::Group, "Enable Transform", &GradientSampler::m_enableTransform)
->GroupElementToggle("Enable Transform", &GradientSampler::m_enableTransform)
->Attribute(AZ::Edit::Attributes::AutoExpand, false)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &GradientSampler::ChangeNotify)
->DataElement(0, &GradientSampler::m_translate, "Translate", "")
->Attribute(AZ::Edit::Attributes::ReadOnly, &GradientSampler::AreTransformSettingsDisabled)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &GradientSampler::ChangeNotify)
@ -74,8 +75,9 @@ namespace GradientSignal
->Attribute(AZ::Edit::Attributes::ReadOnly, &GradientSampler::AreTransformSettingsDisabled)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &GradientSampler::ChangeNotify)
->ClassElement(AZ::Edit::ClassElements::Group, "Enable Levels", &GradientSampler::m_enableLevels)
->GroupElementToggle("Enable Levels", &GradientSampler::m_enableLevels)
->Attribute(AZ::Edit::Attributes::AutoExpand, false)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &GradientSampler::ChangeNotify)
->DataElement(AZ::Edit::UIHandlers::Slider, &GradientSampler::m_inputMid, "Input Mid", "")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Max, 10.0f)

Loading…
Cancel
Save