diff --git a/Code/Framework/AzCore/AzCore/Serialization/EditContext.h b/Code/Framework/AzCore/AzCore/Serialization/EditContext.h index 019683ee81..492550f266 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/EditContext.h +++ b/Code/Framework/AzCore/AzCore/Serialization/EditContext.h @@ -236,6 +236,17 @@ namespace AZ */ ClassBuilder* ClassElement(Crc32 elementIdCrc, const char* description); + + /** + * 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 + ClassBuilder* GroupElementToggle(const char* description, T memberVariable); + + /** * Declare element with an associated UI handler that does not represent a specific class member variable. * \param uiId - name of a UI handler used to display the element @@ -515,6 +526,15 @@ namespace AZ return this; } + //========================================================================= + // ClassElement + //========================================================================= + template + inline EditContext::ClassBuilder* EditContext::ClassBuilder::GroupElementToggle(const char* name, T memberVariable) + { + return DataElement(AZ::Edit::ClassElements::Group, memberVariable, name, name, ""); + } + //========================================================================= // UIElement //========================================================================= diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.cpp index 39faf06f08..2e697a7398 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.cpp @@ -546,7 +546,7 @@ namespace AzToolsFramework for (auto& element : nodeEditData->m_elements) { - if (element.IsClassElement() && element.m_elementId == AZ::Edit::ClassElements::Group) + if (element.m_elementId == AZ::Edit::ClassElements::Group) { groupData = (element.m_description && element.m_description[0]) ? &element : nullptr; continue; @@ -1112,13 +1112,14 @@ namespace AzToolsFramework const AZ::Edit::ElementData* groupData = nullptr; for (const AZ::Edit::ElementData& elementData : parentEditData->m_elements) { - if (node->m_elementEditData == &elementData) // 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; break; } - else if (elementData.IsClassElement() && elementData.m_elementId == AZ::Edit::ClassElements::Group) + else if (elementData.m_elementId == AZ::Edit::ClassElements::Group) { if (!elementData.m_description || !elementData.m_description[0]) { // close the group diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp index b9578d4e3a..5ea36bb30e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp @@ -12,6 +12,7 @@ #include #include +#include AZ_PUSH_DISABLE_WARNING(4244 4251 4800, "-Wunknown-warning-option") // 4244: conversion from 'int' to 'float', possible loss of data // 4251: class '...' needs to have dll-interface to be used by clients of class 'QInputEvent' @@ -141,6 +142,11 @@ namespace AzToolsFramework m_treeDepth = 0; delete m_dropDownArrow; + if (m_toggleSwitch != nullptr) + { + m_handler->DestroyGUI(m_toggleSwitch); + m_toggleSwitch = nullptr; + } if (m_childWidget) { @@ -387,6 +393,13 @@ namespace AzToolsFramework setUpdatesEnabled(true); } + void PropertyRowWidget::InitializeToggleGroup(const char* groupName, PropertyRowWidget* pParent, int depth, InstanceDataNode* node, int labelWidth) + { + Initialize(groupName, pParent, depth, labelWidth); + ChangeSourceNode(node); + CreateGroupToggleSwitch(); + } + void PropertyRowWidget::Initialize(const char* groupName, PropertyRowWidget* pParent, int depth, int labelWidth) { Initialize(pParent, nullptr, depth, labelWidth); @@ -1102,6 +1115,19 @@ namespace AzToolsFramework } } + void PropertyRowWidget::CreateGroupToggleSwitch() + { + if (m_toggleSwitch == nullptr) + { + m_handlerName = AZ::Edit::UIHandlers::CheckBox; + PropertyTypeRegistrationMessages::Bus::BroadcastResult(m_handler, &PropertyTypeRegistrationMessages::Bus::Events::ResolvePropertyHandler, m_handlerName, azrtti_typeid()); + m_toggleSwitch = m_handler->CreateGUI(this); + m_middleLayout->insertWidget(0, m_toggleSwitch, 1); + auto checkBoxCtrl = static_cast(m_toggleSwitch); + QObject::connect(checkBoxCtrl, &AzToolsFramework::PropertyCheckBoxCtrl::valueChanged, this, &PropertyRowWidget::OnClickedToggleButton); + } + } + void PropertyRowWidget::SetIndentSize(int w) { m_indent->changeSize(w, 1, QSizePolicy::Fixed, QSizePolicy::Fixed); @@ -1110,6 +1136,18 @@ namespace AzToolsFramework m_leftHandSideLayout->activate(); } + void PropertyRowWidget::OnClickedToggleButton(bool checked) + { + if (m_expanded != checked) + { + DoExpandOrContract(!IsExpanded(), 0 != (QGuiApplication::keyboardModifiers() & Qt::ControlModifier)); + } + } + + void PropertyRowWidget::ChangeSourceNode(InstanceDataNode* node) + { + m_sourceNode = node; + } void PropertyRowWidget::SetExpanded(bool expanded) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx index 8248eb4b86..ebf7c67b21 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx @@ -50,6 +50,7 @@ namespace AzToolsFramework virtual void Initialize(PropertyRowWidget* pParent, InstanceDataNode* dataNode, int depth, int labelWidth = 200); virtual void Initialize(const char* groupName, PropertyRowWidget* pParent, int depth, int labelWidth = 200); + virtual void InitializeToggleGroup(const char* groupName, PropertyRowWidget* pParent, int depth, InstanceDataNode* node, int labelWidth = 200); virtual void Clear(); // for pooling // --- NOT A UNIQUE IDENTIFIER --- @@ -143,11 +144,14 @@ namespace AzToolsFramework QVBoxLayout* GetLeftHandSideLayoutParent() { return m_leftHandSideLayoutParent; } 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; } bool CanChildrenBeReordered() const; bool CanBeReordered() const; + protected: int CalculateLabelWidth() const; @@ -177,6 +181,8 @@ namespace AzToolsFramework QLabel* m_defaultLabel; // if there is no handler, we use a m_defaultLabel label InstanceDataNode* m_sourceNode; + QWidget* m_toggleSwitch = nullptr; + QString m_currentFilterString; struct ChangeNotification @@ -241,6 +247,8 @@ namespace AzToolsFramework void mouseDoubleClickEvent(QMouseEvent* event) override; void UpdateDropDownArrow(); + void CreateGroupToggleSwitch(); + void ChangeSourceNode(InstanceDataNode* node); void UpdateDefaultLabel(InstanceDataNode* node); void createContainerButtons(); @@ -259,6 +267,7 @@ namespace AzToolsFramework private slots: void OnClickedExpansionButton(); + void OnClickedToggleButton(bool checked); void OnClickedAddElementButton(); void OnClickedRemoveElementButton(); void OnClickedClearContainerButton(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.cpp index ae3bcd6849..36462bca66 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.cpp @@ -169,6 +169,8 @@ 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::WidgetList m_specialGroupWidgets; + InstanceDataNode* groupSourceNode = nullptr; RowContainerType m_widgetsInDisplayOrder; UserWidgetToDataMap m_userWidgetsToData; VisibilityCallback m_visibilityCallback; @@ -501,6 +503,7 @@ namespace AzToolsFramework // if the node is in a group then create the widget for the group if (groupElementData) { + bool isToggleGroup = false; const char* groupName = groupElementData->m_description; PropertyRowWidget*& widgetEntry = m_groupWidgets[{parent, groupName}]; @@ -509,14 +512,34 @@ namespace AzToolsFramework { widgetEntry = CreateOrPullFromPool(); widgetEntry->SetFilterString(m_editor->GetFilterString()); - widgetEntry->Initialize(groupName, parent, depth, m_propertyLabelWidth); + + // Initialized normally if the group does not have a member variable attached to it, + // otherwise initialize it as a group that will have a toggle switch. + if (groupElementData->IsClassElement()) + { + widgetEntry->Initialize(groupName, parent, depth, m_propertyLabelWidth); + } + else + { + widgetEntry->InitializeToggleGroup(groupName, parent, depth, groupSourceNode, m_propertyLabelWidth); + QWidget* toggleSwitch = widgetEntry->GetToggle(); + PropertyHandlerBase* pHandler = widgetEntry->GetHandler(); + m_userWidgetsToData[toggleSwitch] = groupSourceNode; + m_specialGroupWidgets[groupSourceNode] = widgetEntry; + pHandler->ConsumeAttributes_Internal(toggleSwitch, groupSourceNode); + pHandler->ReadValuesIntoGUI_Internal(toggleSwitch, groupSourceNode); + widgetEntry->OnValuesUpdated(); + isToggleGroup = true; + } + widgetEntry->SetLeafIndentation(m_leafIndentation); widgetEntry->SetTreeIndentation(m_treeIndentation); widgetEntry->setObjectName(groupName); for (const AZ::Edit::AttributePair& attribute : groupElementData->m_attributes) { - PropertyAttributeReader reader(node->GetParent()->FirstInstance(), attribute.second); + InstanceDataNode* readerNode = (isToggleGroup) ? groupSourceNode : node; + PropertyAttributeReader reader(readerNode->GetParent()->FirstInstance(), attribute.second); QString descriptionOut; bool foundDescription = false; widgetEntry->ConsumeAttribute(attribute.first, reader, true, &descriptionOut, &foundDescription); @@ -608,7 +631,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()) + if (!pWidget->HasChildWidgetAlready() && !pWidget->GetToggle()) { PropertyHandlerBase* pHandler = pWidget->GetHandler(); if (pHandler) @@ -735,36 +758,44 @@ namespace AzToolsFramework } } } - - pWidget = CreateOrPullFromPool(); - pWidget->show(); - - pWidget->SetFilterString(m_editor->GetFilterString()); - pWidget->Initialize(pParent, node, depth, m_propertyLabelWidth); - - if (labelOverride != "") + if (!node->GetElementEditMetadata() || (node->GetElementEditMetadata()->m_elementId != AZ::Edit::ClassElements::Group)) { - pWidget->SetNameLabel(labelOverride.data()); + pWidget = CreateOrPullFromPool(); + pWidget->show(); + + pWidget->SetFilterString(m_editor->GetFilterString()); + pWidget->Initialize(pParent, node, depth, m_propertyLabelWidth); + + if (labelOverride != "") + { + pWidget->SetNameLabel(labelOverride.data()); + } + + pWidget->setObjectName(pWidget->label()); + pWidget->SetSelectionEnabled(m_selectionEnabled); + pWidget->SetLeafIndentation(m_leafIndentation); + pWidget->SetTreeIndentation(m_treeIndentation); + + m_widgets[node] = pWidget; + m_widgetsInDisplayOrder.insert(widgetDisplayOrder, pWidget); + + if (pParent) + { + pParent->AddedChild(pWidget); + } + + if (pParent || !m_hideRootProperties) + { + depth += 1; + } + pParent = pWidget; } - pWidget->setObjectName(pWidget->label()); - pWidget->SetSelectionEnabled(m_selectionEnabled); - pWidget->SetLeafIndentation(m_leafIndentation); - pWidget->SetTreeIndentation(m_treeIndentation); - - m_widgets[node] = pWidget; - m_widgetsInDisplayOrder.insert(widgetDisplayOrder, pWidget); - - if (pParent) + // 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)) { - pParent->AddedChild(pWidget); + groupSourceNode = node; } - - if (pParent || !m_hideRootProperties) - { - depth += 1; - } - pParent = pWidget; } } @@ -1356,9 +1387,13 @@ 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); - if (rowWidget != m_widgets.end()) + if (rowWidget == m_widgets.end()) + { + rowWidget = m_specialGroupWidgets.find(it->second); + } + if (rowWidget != m_widgets.end() || rowWidget != m_specialGroupWidgets.end()) { InstanceDataNode* node = rowWidget->first; PropertyRowWidget* widget = rowWidget->second; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx index b95c2b927a..b10e153162 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx @@ -51,6 +51,8 @@ namespace AzToolsFramework typedef AZStd::unordered_map WidgetList; + ReflectedPropertyEditor::WidgetList m_specialGroupWidgets; + ReflectedPropertyEditor(QWidget* pParent); virtual ~ReflectedPropertyEditor(); @@ -62,6 +64,7 @@ namespace AzToolsFramework bool AddInstance(void* instance, const AZ::Uuid& classId, void* aggregateInstance = nullptr, void* compareInstance = nullptr); void SetCompareInstance(void* instance, const AZ::Uuid& classId); void ClearInstances(); + void ReadValuesIntoGui(QWidget* widget, InstanceDataNode* node); template bool AddInstance(T* instance, void* aggregateInstance = nullptr, void* compareInstance = nullptr) { diff --git a/Code/Framework/AzToolsFramework/Tests/InstanceDataHierarchy.cpp b/Code/Framework/AzToolsFramework/Tests/InstanceDataHierarchy.cpp index 61b512abb6..1b11479bff 100644 --- a/Code/Framework/AzToolsFramework/Tests/InstanceDataHierarchy.cpp +++ b/Code/Framework/AzToolsFramework/Tests/InstanceDataHierarchy.cpp @@ -21,6 +21,7 @@ #include #include #include +#include using namespace AZ; @@ -727,6 +728,153 @@ namespace UnitTest }; + 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() {} + explicit SubData(int v) : m_int(v) {} + explicit SubData(bool b) : m_bool(b) {} + explicit 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(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("SubInt", &SubData::m_int) + ->Field("SubToggle", &SubData::m_bool) + ->Field("SubFloat", &SubData::m_float) + ; + + serializeContext->Class() + ->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("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("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 InstanceDataHierarchyGroupTestFixture : public AllocatorsFixture + { + public: + InstanceDataHierarchyGroupTestFixture() = default; + + AZStd::unique_ptr m_serializeContext; + AZStd::unique_ptr testEntity1; + AzToolsFramework::InstanceDataHierarchy* instanceDataHierarchy; + AzToolsFramework::InstanceDataNode* componentNode1 = nullptr; + + void SetUp() override + { + AllocatorsFixture::SetUp(); + + using AzToolsFramework::InstanceDataHierarchy; + using AzToolsFramework::InstanceDataNode; + + AZ::AllocatorInstance::Create(); + + m_serializeContext.reset(aznew AZ::SerializeContext()); + m_serializeContext.get()->CreateEditContext(); + Entity::Reflect(m_serializeContext.get()); + GroupTestComponent::Reflect(m_serializeContext.get()); + + testEntity1.reset(new AZ::Entity()); + testEntity1->CreateComponent(); + + instanceDataHierarchy = aznew InstanceDataHierarchy(); + instanceDataHierarchy->AddRootInstance(testEntity1.get()); + instanceDataHierarchy->Build(m_serializeContext.get(), 0); + + // Adding the nodes to a node stack + auto rootNode = instanceDataHierarchy->GetRootNode(); + AZStd::stack nodeStack; + nodeStack.push(rootNode); + while (!nodeStack.empty()) + { + InstanceDataNode* node = nodeStack.top(); + nodeStack.pop(); + if (node->GetClassMetadata()->m_typeId == AZ::AzTypeInfo::Uuid()) + { + componentNode1 = node; + break; + } + for (InstanceDataNode& child : node->GetChildren()) + { + nodeStack.push(&child); + } + } + } + + void TearDown() override + { + m_serializeContext.reset(); + testEntity1.reset(); + delete instanceDataHierarchy; + AZ::AllocatorInstance::Destroy(); + AllocatorsFixture::TearDown(); + } + }; + class InstanceDataHierarchyKeyedContainerTest : public AllocatorsFixture { @@ -1315,4 +1463,108 @@ namespace UnitTest run(); } + // Test to validate that the only ClassElement::Group nodes are ToggleGroups + TEST_F(InstanceDataHierarchyGroupTestFixture, GroupToggleIsClassElementGroup) + { + using AzToolsFramework::InstanceDataHierarchy; + using AzToolsFramework::InstanceDataNode; + + for (auto child : componentNode1->GetChildren()) + { + AZStd::string childName(child.GetElementMetadata()->m_name); + if (childName.compare("GroupToggle") == 0) + { + EXPECT_EQ(child.GetElementEditMetadata()->m_elementId, AZ::Edit::ClassElements::Group); + } + if ((childName.compare("SubDataNormal") == 0) || (childName.compare("SubDataToggle") == 0)) + { + for (auto subChild : child.GetChildren()) + { + childName = subChild.GetElementMetadata()->m_name; + if (childName.compare("SubToggle") == 0) + { + EXPECT_EQ(subChild.GetElementEditMetadata()->m_elementId, AZ::Edit::ClassElements::Group); + } + else + { + EXPECT_NE(subChild.GetElementEditMetadata()->m_elementId, AZ::Edit::ClassElements::Group); + } + } + } + } + } + + // Test to ensure that each node has been assigned under the proper group and the group hierarchy is structured correctly + TEST_F(InstanceDataHierarchyGroupTestFixture, ValidatingGroupAndSubGroupHierarchy) + { + using AzToolsFramework::InstanceDataHierarchy; + using AzToolsFramework::InstanceDataNode; + + for (auto child : componentNode1->GetChildren()) + { + AZStd::string childName(child.GetElementMetadata()->m_name); + if (childName.compare("GroupFloat") == 0) + { + EXPECT_EQ(child.GetGroupElementMetadata()->m_description, "Normal Group"); + } + if (childName.compare("ToggleGroupInt") == 0) + { + EXPECT_EQ(child.GetGroupElementMetadata()->m_description, "Group Toggle"); + } + if ((childName.compare("SubDataNormal") == 0) || (childName.compare("SubDataToggle") == 0)) + { + for (auto subChild : child.GetChildren()) + { + childName = subChild.GetElementMetadata()->m_name; + if (childName.compare("SubInt") == 0) + { + EXPECT_EQ(subChild.GetGroupElementMetadata()->m_description, "Normal SubGroup"); + } + if (childName.compare("SubFloat") == 0) + { + EXPECT_EQ(subChild.GetGroupElementMetadata()->m_description, "SubGroup Toggle"); + } + } + } + } + } + + class InstanceDataHierarchyGroupTestFixtureParameterized + : public InstanceDataHierarchyGroupTestFixture + , public ::testing::WithParamInterface + { + }; + + INSTANTIATE_TEST_CASE_P( + InstanceDataHierarchyGroupTestFixture, + InstanceDataHierarchyGroupTestFixtureParameterized, + ::testing::Values("GroupFloat", "GroupToggle", "ToggleGroupInt", "SubInt", "SubToggle", "SubFloat")); + + // Test to validate that each node in a group and Subgroup has the correct parent + TEST_P(InstanceDataHierarchyGroupTestFixtureParameterized, ValidatingGroupAndSubGroupParents) + { + using AzToolsFramework::InstanceDataHierarchy; + using AzToolsFramework::InstanceDataNode; + + const char* paramName = GetParam(); + for (auto child : componentNode1->GetChildren()) + { + AZStd::string childName(child.GetElementMetadata()->m_name); + if (childName.compare(paramName) == 0) + { + EXPECT_EQ(child.GetParent()->GetClassMetadata()->m_name, "GroupTestComponent"); + } + if ((childName.compare("SubDataNormal") == 0) || (childName.compare("SubDataToggle") == 0)) + { + for (auto subChild : child.GetChildren()) + { + childName = subChild.GetElementMetadata()->m_name; + if (childName.compare(paramName) == 0) + { + EXPECT_EQ(subChild.GetParent()->GetClassMetadata()->m_name, "SubData"); + } + } + } + } + } } // namespace UnitTest diff --git a/Gems/GradientSignal/Code/Source/GradientSampler.cpp b/Gems/GradientSignal/Code/Source/GradientSampler.cpp index bb17ef3557..d224a93e6b 100644 --- a/Gems/GradientSignal/Code/Source/GradientSampler.cpp +++ b/Gems/GradientSignal/Code/Source/GradientSampler.cpp @@ -61,45 +61,38 @@ namespace GradientSignal ->DataElement(0, &GradientSampler::m_invertInput, "Invert Input", "") ->Attribute(AZ::Edit::Attributes::ChangeNotify, &GradientSampler::ChangeNotify) - ->DataElement(0, &GradientSampler::m_enableTransform, "Enable Transform", "") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &GradientSampler::ChangeNotify) + + ->GroupElementToggle("Enable Transform", &GradientSampler::m_enableTransform) + ->Attribute(AZ::Edit::Attributes::AutoExpand, false) ->DataElement(0, &GradientSampler::m_translate, "Translate", "") ->Attribute(AZ::Edit::Attributes::ReadOnly, &GradientSampler::AreTransformSettingsDisabled) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &GradientSampler::ChangeNotify) ->DataElement(0, &GradientSampler::m_scale, "Scale", "") ->Attribute(AZ::Edit::Attributes::ReadOnly, &GradientSampler::AreTransformSettingsDisabled) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &GradientSampler::ChangeNotify) ->DataElement(0, &GradientSampler::m_rotate, "Rotate", "Rotation in degrees.") ->Attribute(AZ::Edit::Attributes::ReadOnly, &GradientSampler::AreTransformSettingsDisabled) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &GradientSampler::ChangeNotify) - ->DataElement(0, &GradientSampler::m_enableLevels, "Enable Levels", "") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &GradientSampler::ChangeNotify) + ->GroupElementToggle("Enable Levels", &GradientSampler::m_enableLevels) + ->Attribute(AZ::Edit::Attributes::AutoExpand, false) ->DataElement(AZ::Edit::UIHandlers::Slider, &GradientSampler::m_inputMid, "Input Mid", "") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::Max, 10.0f) ->Attribute(AZ::Edit::Attributes::ReadOnly, &GradientSampler::AreLevelSettingsDisabled) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &GradientSampler::ChangeNotify) ->DataElement(AZ::Edit::UIHandlers::Slider, &GradientSampler::m_inputMin, "Input Min", "") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::Max, 1.0f) ->Attribute(AZ::Edit::Attributes::ReadOnly, &GradientSampler::AreLevelSettingsDisabled) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &GradientSampler::ChangeNotify) ->DataElement(AZ::Edit::UIHandlers::Slider, &GradientSampler::m_inputMax, "Input Max", "") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::Max, 1.0f) ->Attribute(AZ::Edit::Attributes::ReadOnly, &GradientSampler::AreLevelSettingsDisabled) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &GradientSampler::ChangeNotify) ->DataElement(AZ::Edit::UIHandlers::Slider, &GradientSampler::m_outputMin, "Output Min", "") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::Max, 1.0f) ->Attribute(AZ::Edit::Attributes::ReadOnly, &GradientSampler::AreLevelSettingsDisabled) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &GradientSampler::ChangeNotify) ->DataElement(AZ::Edit::UIHandlers::Slider, &GradientSampler::m_outputMax, "Output Max", "") ->Attribute(AZ::Edit::Attributes::Min, 0.0f) ->Attribute(AZ::Edit::Attributes::Max, 1.0f) ->Attribute(AZ::Edit::Attributes::ReadOnly, &GradientSampler::AreLevelSettingsDisabled) - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &GradientSampler::ChangeNotify) ->ClassElement(AZ::Edit::ClassElements::Group, "Preview (Inbound)") ->Attribute(AZ::Edit::Attributes::AutoExpand, false)