From 0502475fa654d8b6fa4e1fce627275b210d8ac94 Mon Sep 17 00:00:00 2001 From: Jose Date: Wed, 7 Jul 2021 13:59:26 -0500 Subject: [PATCH 1/6] Created a toggle switch to enable and disable groups through the EditContext Signed-off-by: Jose --- .../AzCore/AzCore/Serialization/EditContext.h | 59 ++++++++++ .../PropertyEditor/InstanceDataHierarchy.cpp | 6 +- .../UI/PropertyEditor/PropertyRowWidget.cpp | 38 ++++++ .../UI/PropertyEditor/PropertyRowWidget.hxx | 8 ++ .../ReflectedPropertyEditor.cpp | 110 +++++++++++++----- .../ReflectedPropertyEditor.hxx | 3 + .../Code/Source/GradientSampler.cpp | 9 +- 7 files changed, 198 insertions(+), 35 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Serialization/EditContext.h b/Code/Framework/AzCore/AzCore/Serialization/EditContext.h index 12ec84161b..ba93d19a3d 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/EditContext.h +++ b/Code/Framework/AzCore/AzCore/Serialization/EditContext.h @@ -235,6 +235,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 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. + */ + template + ClassBuilder* ClassElement(Crc32 elementIdCrc, 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 @@ -514,6 +525,54 @@ namespace AZ return this; } + //========================================================================= + // ClassElement + //========================================================================= + template + inline EditContext::ClassBuilder* EditContext::ClassBuilder::ClassElement(Crc32 elementIdCrc, const char* description, T memberVariable) + { + if (IsValid()) + { + using ElementTypeInfo = typename SerializeInternal::ElementInfo; + AZ_Assert( + m_classData->m_typeId == AzTypeInfo::Uuid(), + "Data element (%s) belongs to a different class!", description); + + // Not really portable but works for the supported compilers + size_t offset = + reinterpret_cast(&(reinterpret_cast(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; + } + //========================================================================= // UIElement //========================================================================= diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.cpp index f411c60625..d6054a7937 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,13 @@ 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 + if ((node->m_elementEditData == &elementData) && (elementData.m_elementId != AZ::Edit::ClassElements::Group)) // this element matches this node { // 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 d66ee34c3b..b85fb9cb2e 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) + { + 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) + { + m_handlerName = AZ::Edit::UIHandlers::CheckBox; + EBUS_EVENT_RESULT(m_handler, PropertyTypeRegistrationMessages::Bus, ResolvePropertyHandler, m_handlerName, azrtti_typeid()); + m_toggleSwitch = m_handler->CreateGUI(this); + m_middleLayout->insertWidget(0, m_toggleSwitch, 1); + auto checkBoxCtrl = reinterpret_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) || (!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 b23691ea44..c5618b1f47 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx @@ -48,6 +48,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 --- @@ -141,11 +142,13 @@ namespace AzToolsFramework QVBoxLayout* GetLeftHandSideLayoutParent() { return m_leftHandSideLayoutParent; } QToolButton* GetIndicatorButton() { return m_indicatorButton; } QLabel* GetNameLabel() { return m_nameLabel; } + QWidget* GetToggle() { return m_toggleSwitch; } void SetIndentSize(int w); void SetAsCustom(bool custom) { m_custom = custom; } bool CanChildrenBeReordered() const; bool CanBeReordered() const; + protected: int CalculateLabelWidth() const; @@ -175,6 +178,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 @@ -239,6 +244,8 @@ namespace AzToolsFramework void mouseDoubleClickEvent(QMouseEvent* event) override; void UpdateDropDownArrow(); + void CreateGroupToggleSwitch(); + void ChangeSourceNode(InstanceDataNode* node); void UpdateDefaultLabel(InstanceDataNode* node); void createContainerButtons(); @@ -257,6 +264,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 1e7b0395c8..a1f67489e9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.cpp @@ -167,6 +167,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::SpecialGroupWidgetList m_specialGroupWidgets; + InstanceDataNode* groupSourceNode = nullptr; RowContainerType m_widgetsInDisplayOrder; UserWidgetToDataMap m_userWidgetsToData; VisibilityCallback m_visibilityCallback; @@ -507,7 +509,25 @@ 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(); + } + widgetEntry->SetLeafIndentation(m_leafIndentation); widgetEntry->SetTreeIndentation(m_treeIndentation); widgetEntry->setObjectName(groupName); @@ -606,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()) + if ((!pWidget->HasChildWidgetAlready()) && (!pWidget->GetToggle())) { PropertyHandlerBase* pHandler = pWidget->GetHandler(); if (pHandler) @@ -733,36 +753,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; } } @@ -1000,6 +1028,26 @@ namespace AzToolsFramework pWidget->UpdateIndicator(m_impl->m_indicatorQueryFunction(pWidget->GetNode())); } } + + for (auto it = m_impl->m_specialGroupWidgets.begin(); it != m_impl->m_specialGroupWidgets.end(); ++it) + { + PropertyRowWidget* pWidget = it->second; + + QWidget* childWidget = pWidget->GetChildWidget(); + + if (pWidget->GetHandler() && childWidget) + { + pWidget->GetHandler()->ConsumeAttributes_Internal(childWidget, it->first); + pWidget->GetHandler()->ReadValuesIntoGUI_Internal(childWidget, it->first); + pWidget->OnValuesUpdated(); + } + pWidget->RefreshAttributesFromNode(false); + + if (m_impl->m_indicatorQueryFunction) + { + pWidget->UpdateIndicator(m_impl->m_indicatorQueryFunction(pWidget->GetNode())); + } + } } void ReflectedPropertyEditor::InvalidateValues() @@ -1356,8 +1404,14 @@ namespace AzToolsFramework // get the property editor auto rowWidget = m_widgets.find(it->second); - if (rowWidget != m_widgets.end()) + auto rowWidgetGroup = m_specialGroupWidgets.find(it->second); + if (rowWidget != m_widgets.end() || rowWidgetGroup != m_specialGroupWidgets.end()) { + if (rowWidget == m_widgets.end()) + { + rowWidget = rowWidgetGroup; + } + InstanceDataNode* node = rowWidget->first; PropertyRowWidget* widget = rowWidget->second; PropertyHandlerBase* handler = widget->GetHandler(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx index c27acaa374..42ca0b6d92 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx @@ -50,6 +50,8 @@ namespace AzToolsFramework typedef AZStd::unordered_map WidgetList; + typedef AZStd::unordered_map SpecialGroupWidgetList; + ReflectedPropertyEditor(QWidget* pParent); virtual ~ReflectedPropertyEditor(); @@ -61,6 +63,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/Gems/GradientSignal/Code/Source/GradientSampler.cpp b/Gems/GradientSignal/Code/Source/GradientSampler.cpp index 944eef2e42..a0a23ffe68 100644 --- a/Gems/GradientSignal/Code/Source/GradientSampler.cpp +++ b/Gems/GradientSignal/Code/Source/GradientSampler.cpp @@ -61,8 +61,9 @@ 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) + + ->ClassElement(AZ::Edit::ClassElements::Group, "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) @@ -73,8 +74,8 @@ namespace GradientSignal ->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) + ->ClassElement(AZ::Edit::ClassElements::Group, "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) From e18bcc63f2a2b294e87f4328ea2f787cc8e1b82e Mon Sep 17 00:00:00 2001 From: Jose Date: Wed, 7 Jul 2021 15:12:43 -0500 Subject: [PATCH 2/6] Fixed a bug in the ReflectedPropertyError that was preventing groups from opening correctly Signed-off-by: Jose --- .../ReflectedPropertyEditor.cpp | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.cpp index a1f67489e9..02048a1161 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.cpp @@ -1028,26 +1028,6 @@ namespace AzToolsFramework pWidget->UpdateIndicator(m_impl->m_indicatorQueryFunction(pWidget->GetNode())); } } - - for (auto it = m_impl->m_specialGroupWidgets.begin(); it != m_impl->m_specialGroupWidgets.end(); ++it) - { - PropertyRowWidget* pWidget = it->second; - - QWidget* childWidget = pWidget->GetChildWidget(); - - if (pWidget->GetHandler() && childWidget) - { - pWidget->GetHandler()->ConsumeAttributes_Internal(childWidget, it->first); - pWidget->GetHandler()->ReadValuesIntoGUI_Internal(childWidget, it->first); - pWidget->OnValuesUpdated(); - } - pWidget->RefreshAttributesFromNode(false); - - if (m_impl->m_indicatorQueryFunction) - { - pWidget->UpdateIndicator(m_impl->m_indicatorQueryFunction(pWidget->GetNode())); - } - } } void ReflectedPropertyEditor::InvalidateValues() From bc9d0eb0e1c70843c097a88fe720a5bd4d5ca524 Mon Sep 17 00:00:00 2001 From: Jose Date: Tue, 13 Jul 2021 14:55:05 -0500 Subject: [PATCH 3/6] Added unit tests for groups and toggle groups, fixed comments and syntax Signed-off-by: Jose --- .../AzCore/AzCore/Serialization/EditContext.h | 53 +-- .../PropertyEditor/InstanceDataHierarchy.cpp | 3 +- .../UI/PropertyEditor/PropertyRowWidget.cpp | 6 +- .../UI/PropertyEditor/PropertyRowWidget.hxx | 1 + .../ReflectedPropertyEditor.cpp | 22 +- .../ReflectedPropertyEditor.hxx | 2 +- .../Framework/Tests/InstanceDataHierarchy.cpp | 335 ++++++++++++++++++ .../Code/Source/GradientSampler.cpp | 6 +- 8 files changed, 363 insertions(+), 65 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Serialization/EditContext.h b/Code/Framework/AzCore/AzCore/Serialization/EditContext.h index ba93d19a3d..61c7df4471 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/EditContext.h +++ b/Code/Framework/AzCore/AzCore/Serialization/EditContext.h @@ -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 - ClassBuilder* ClassElement(Crc32 elementIdCrc, const char* description, T memberVariable); + ClassBuilder* GroupElementToggle(const char* description, T memberVariable); /** @@ -529,48 +529,9 @@ namespace AZ // ClassElement //========================================================================= template - 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; - AZ_Assert( - m_classData->m_typeId == AzTypeInfo::Uuid(), - "Data element (%s) belongs to a different class!", description); - - // Not really portable but works for the supported compilers - size_t offset = - reinterpret_cast(&(reinterpret_cast(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, ""); } //========================================================================= diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.cpp index d6054a7937..9b69701a27 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.cpp @@ -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; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp index b85fb9cb2e..e6f2af6a52 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp @@ -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()); + 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 = reinterpret_cast(m_toggleSwitch); + auto checkBoxCtrl = static_cast(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)); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx index c5618b1f47..93c1fc4d03 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx @@ -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; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.cpp index 02048a1161..d16fbb2776 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.cpp @@ -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(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx index 42ca0b6d92..bd5a6ab891 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx @@ -50,7 +50,7 @@ namespace AzToolsFramework typedef AZStd::unordered_map WidgetList; - typedef AZStd::unordered_map SpecialGroupWidgetList; + ReflectedPropertyEditor::WidgetList m_specialGroupWidgets; ReflectedPropertyEditor(QWidget* pParent); virtual ~ReflectedPropertyEditor(); diff --git a/Code/Framework/Tests/InstanceDataHierarchy.cpp b/Code/Framework/Tests/InstanceDataHierarchy.cpp index db0223c0cf..29cf42fb6b 100644 --- a/Code/Framework/Tests/InstanceDataHierarchy.cpp +++ b/Code/Framework/Tests/InstanceDataHierarchy.cpp @@ -20,6 +20,7 @@ #include #include #include +#include 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(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 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 testEntity1(new AZ::Entity()); + testEntity1->CreateComponent(); + + InstanceDataHierarchy instanceDataHierarchy; + instanceDataHierarchy.AddRootInstance(testEntity1.get()); + instanceDataHierarchy.Build(&serializeContext, 0); + + // Adding the nodes to a node stack + auto rootNode = instanceDataHierarchy.GetRootNode(); + AZStd::stack nodeStack; + nodeStack.push(rootNode); + InstanceDataNode* componentNode1 = nullptr; + 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); + } + } + // 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 testEntity1(new AZ::Entity()); + testEntity1->CreateComponent(); + + InstanceDataHierarchy instanceDataHierarchy; + instanceDataHierarchy.AddRootInstance(testEntity1.get()); + instanceDataHierarchy.Build(&serializeContext, 0); + + // Adding the nodes to a node stack + auto rootNode = instanceDataHierarchy.GetRootNode(); + AZStd::stack nodeStack; + nodeStack.push(rootNode); + InstanceDataNode* componentNode1 = nullptr; + 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); + } + } + // 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 testEntity1(new AZ::Entity()); + testEntity1->CreateComponent(); + + InstanceDataHierarchy instanceDataHierarchy; + instanceDataHierarchy.AddRootInstance(testEntity1.get()); + instanceDataHierarchy.Build(&serializeContext, 0); + + // Adding the nodes to a node stack + auto rootNode = instanceDataHierarchy.GetRootNode(); + AZStd::stack nodeStack; + nodeStack.push(rootNode); + InstanceDataNode* componentNode1 = nullptr; + 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); + } + } + // 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 diff --git a/Gems/GradientSignal/Code/Source/GradientSampler.cpp b/Gems/GradientSignal/Code/Source/GradientSampler.cpp index a0a23ffe68..c89a63790a 100644 --- a/Gems/GradientSignal/Code/Source/GradientSampler.cpp +++ b/Gems/GradientSignal/Code/Source/GradientSampler.cpp @@ -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) From fed37e8e6de37fc9f15700f2a2e33f5d000edef7 Mon Sep 17 00:00:00 2001 From: dtamkin1 Date: Tue, 13 Jul 2021 18:02:25 -0500 Subject: [PATCH 4/6] Removed the ChangeNotify event in each attribute Signed-off-by: dtamkin1 --- Gems/GradientSignal/Code/Source/GradientSampler.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Gems/GradientSignal/Code/Source/GradientSampler.cpp b/Gems/GradientSignal/Code/Source/GradientSampler.cpp index c89a63790a..bf0260374b 100644 --- a/Gems/GradientSignal/Code/Source/GradientSampler.cpp +++ b/Gems/GradientSignal/Code/Source/GradientSampler.cpp @@ -64,45 +64,35 @@ namespace GradientSignal ->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) ->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) ->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) ->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) From fcb2a0f95c76e3a55cc93b9b21aa34e88c07b63c Mon Sep 17 00:00:00 2001 From: dtamkin1 Date: Fri, 23 Jul 2021 16:21:07 -0500 Subject: [PATCH 5/6] Reformatted Unit tests to give more information and be more concise, also changed the position of the toggle switch Signed-off-by: dtamkin1 --- .../UI/PropertyEditor/PropertyRowWidget.cpp | 7 +- .../ReflectedPropertyEditor.cpp | 5 +- .../Framework/Tests/InstanceDataHierarchy.cpp | 355 +++++++----------- 3 files changed, 144 insertions(+), 223 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp index e6f2af6a52..7e20642bb8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp @@ -142,7 +142,7 @@ namespace AzToolsFramework m_treeDepth = 0; delete m_dropDownArrow; - if (m_toggleSwitch) + if (m_toggleSwitch != nullptr) { m_handler->DestroyGUI(m_toggleSwitch); m_toggleSwitch = nullptr; @@ -1117,12 +1117,13 @@ namespace AzToolsFramework void PropertyRowWidget::CreateGroupToggleSwitch() { - if (!m_toggleSwitch) + 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); + m_toggleSwitch->setFixedWidth(38); + m_middleLayout->addWidget(m_toggleSwitch, 1, Qt::AlignRight); auto checkBoxCtrl = static_cast(m_toggleSwitch); QObject::connect(checkBoxCtrl, &AzToolsFramework::PropertyCheckBoxCtrl::valueChanged, this, &PropertyRowWidget::OnClickedToggleButton); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.cpp index d16fbb2776..f7c663747e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.cpp @@ -501,6 +501,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}]; @@ -526,6 +527,7 @@ namespace AzToolsFramework pHandler->ConsumeAttributes_Internal(toggleSwitch, groupSourceNode); pHandler->ReadValuesIntoGUI_Internal(toggleSwitch, groupSourceNode); widgetEntry->OnValuesUpdated(); + isToggleGroup = true; } widgetEntry->SetLeafIndentation(m_leafIndentation); @@ -534,7 +536,8 @@ namespace AzToolsFramework 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); diff --git a/Code/Framework/Tests/InstanceDataHierarchy.cpp b/Code/Framework/Tests/InstanceDataHierarchy.cpp index 29cf42fb6b..312b057715 100644 --- a/Code/Framework/Tests/InstanceDataHierarchy.cpp +++ b/Code/Framework/Tests/InstanceDataHierarchy.cpp @@ -727,30 +727,22 @@ namespace UnitTest }; - class InstanceDataHierarchyGroupTestFixture - : public AllocatorsFixture - { - public: - InstanceDataHierarchyGroupTestFixture() = default; - }; - - class GroupTestComponent - : public AZ::Component + class GroupTestComponent : public AZ::Component { public: AZ_COMPONENT(GroupTestComponent, "{C088C81D-D59D-43F1-85F8-B2E591BABA36}") GroupTestComponent() = default; - struct SubData + 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) {} + 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; @@ -803,7 +795,7 @@ namespace UnitTest } } - void Activate() override + void Activate() override { } @@ -821,6 +813,66 @@ namespace UnitTest 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 @@ -1410,243 +1462,108 @@ namespace UnitTest run(); } - TEST_F(InstanceDataHierarchyGroupTestFixture, TestNormalGroups) + // Test to validate that the only ClassElement::Group nodes are ToggleGroups + TEST_F(InstanceDataHierarchyGroupTestFixture, GroupToggleIsClassElementGroup) { - using namespace AzToolsFramework; + using AzToolsFramework::InstanceDataHierarchy; + using AzToolsFramework::InstanceDataNode; - // Setting up the data node hierarchy - AZ::SerializeContext serializeContext; - serializeContext.CreateEditContext(); - Entity::Reflect(&serializeContext); - GroupTestComponent::Reflect(&serializeContext); - - AZStd::unique_ptr testEntity1(new AZ::Entity()); - testEntity1->CreateComponent(); - - InstanceDataHierarchy instanceDataHierarchy; - instanceDataHierarchy.AddRootInstance(testEntity1.get()); - instanceDataHierarchy.Build(&serializeContext, 0); - - // Adding the nodes to a node stack - auto rootNode = instanceDataHierarchy.GetRootNode(); - AZStd::stack nodeStack; - nodeStack.push(rootNode); - InstanceDataNode* componentNode1 = nullptr; - 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); - } - } - // 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 testEntity1(new AZ::Entity()); - testEntity1->CreateComponent(); - - InstanceDataHierarchy instanceDataHierarchy; - instanceDataHierarchy.AddRootInstance(testEntity1.get()); - instanceDataHierarchy.Build(&serializeContext, 0); - - // Adding the nodes to a node stack - auto rootNode = instanceDataHierarchy.GetRootNode(); - AZStd::stack nodeStack; - nodeStack.push(rootNode); - InstanceDataNode* componentNode1 = nullptr; - 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); - } - } - // 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"); + EXPECT_EQ(child.GetElementEditMetadata()->m_elementId, AZ::Edit::ClassElements::Group); } - if (childName.compare("ToggleGroupInt") == 0) + if ((childName.compare("SubDataNormal") == 0) || (childName.compare("SubDataToggle") == 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"); + 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_F(InstanceDataHierarchyGroupTestFixture, TestNestedGroups) + // 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 namespace AzToolsFramework; + using AzToolsFramework::InstanceDataHierarchy; + using AzToolsFramework::InstanceDataNode; - // Setting up the data node hierarchy - AZ::SerializeContext serializeContext; - serializeContext.CreateEditContext(); - Entity::Reflect(&serializeContext); - GroupTestComponent::Reflect(&serializeContext); - - AZStd::unique_ptr testEntity1(new AZ::Entity()); - testEntity1->CreateComponent(); - - InstanceDataHierarchy instanceDataHierarchy; - instanceDataHierarchy.AddRootInstance(testEntity1.get()); - instanceDataHierarchy.Build(&serializeContext, 0); - - // Adding the nodes to a node stack - auto rootNode = instanceDataHierarchy.GetRootNode(); - AZStd::stack nodeStack; - nodeStack.push(rootNode); - InstanceDataNode* componentNode1 = nullptr; - 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); - } - } - // 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) + if (childName.compare("GroupFloat") == 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"); - } - } + EXPECT_EQ(child.GetGroupElementMetadata()->m_description, "Normal Group"); } - if (childName.compare("SubDataToggle") == 0) + if (childName.compare("ToggleGroupInt") == 0) { - for (InstanceDataNode& subChild : child.GetChildren()) + 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) { - // 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"); + EXPECT_EQ(subChild.GetGroupElementMetadata()->m_description, "Normal SubGroup"); } 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"); + 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 From ce3ec0d49c45f212972660d62c986a69fa95d49d Mon Sep 17 00:00:00 2001 From: dtamkin1 Date: Tue, 27 Jul 2021 15:26:22 -0500 Subject: [PATCH 6/6] Moved toggle swtich back to the left side per UX's request Signed-off-by: dtamkin1 --- .../AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp index 49f7ba3885..5ea36bb30e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp @@ -1122,8 +1122,7 @@ namespace AzToolsFramework 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_toggleSwitch->setFixedWidth(38); - m_middleLayout->addWidget(m_toggleSwitch, 1, Qt::AlignRight); + m_middleLayout->insertWidget(0, m_toggleSwitch, 1); auto checkBoxCtrl = static_cast(m_toggleSwitch); QObject::connect(checkBoxCtrl, &AzToolsFramework::PropertyCheckBoxCtrl::valueChanged, this, &PropertyRowWidget::OnClickedToggleButton); }