Ok, so here’s how i did it:
Header:
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/Serialization/SerializeContext.h>
class Parameter {
public:
AZ_RTTI(Parameter, "<UUID>");
public:
virtual ~Parameter() {}
AZStd::string name;
float val;
public: //editor stuff
AZ::Edit::ElementData ed;
void InitEdit();
public: //RTTI stuff
static void Reflect(AZ::SerializeContext* serializeContext);
};
class ParameterGroup {
public:
AZ_TYPE_INFO(ParameterGroup, "<UUID>");
public:
AZStd::string m_name;
AZStd::vector<Parameter*> m_params;
void Clear();
ParameterGroup() : m_name("Parameters") {}
~ParameterGroup() { Clear(); }
// Disallow copying, only moving
ParameterGroup(const ParameterGroup& rhs) = delete;
ParameterGroup& operator=(ParameterGroup&) = delete;
ParameterGroup(ParameterGroup&& rhs) { *this = AZStd::move(rhs); }
ParameterGroup& operator=(ParameterGroup&& rhs);
public:
static void Reflect(AZ::SerializeContext* serializeContext);
};
class ParameterComponent
: public AZ::Component
{
public:
AZ_COMPONENT(ParameterComponent, "<UUID>", AZ::Component)
public:
ParameterComponent();
~ParameterComponent() override;
protected: // member functions
// AZ::Component
void Init() override;
void Activate() override;
void Deactivate() override;
// ~AZ::Component
public: // static member functions
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) {
(void)provided
}
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) {
(void)incompatible
}
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) {
(void)required
}
static void Reflect(AZ::ReflectContext* context);
private: //dynamic editor listing
static const AZ::Edit::ElementData* GetEditData(const void* handlerPtr, const void* elementPtr, const AZ::Uuid& elementType);
const AZ::Edit::ElementData* GetDataElement(const void* element, const AZ::Uuid& typeUuid) const;
struct ElementInfo {
AZ::Uuid m_uuid; // Type uuid for the class field that should use this edit data.
AZ::Edit::ElementData m_editData; // Edit metadata (name, description, attribs, etc).
};
AZStd::unordered_map<const void*, ElementInfo> m_dataElements;
private:
void LoadParams();
void FreeParams();
private:
ParameterGroup group;
};
Source:
#include "StdAfx.h"
#include <AzCore/Math/Crc.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include "DynamicAttributes.h"
ParameterComponent::ParameterComponent() {
}
ParameterComponent::~ParameterComponent() {
}
void ParameterComponent::Init() {
}
void ParameterComponent::Activate() {
}
void ParameterComponent::Deactivate() {
}
void ParameterComponent::Reflect(AZ::ReflectContext* context) {
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext) {
Parameter::Reflect(serializeContext);
ParameterGroup::Reflect(serializeContext);
serializeContext->Class<ParameterComponent, AZ::Component>()
->Version(1)
->Field("Params", &ParameterComponent::params)
AZ::EditContext* ec = serializeContext->GetEditContext();
if (ec) {
//basic editor data
auto editInfo = ec->Class<ParameterComponent>("ParameterComponent", "Example of how to list custom parameters");
editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/CharacterPhysics.png")
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/CharacterPhysics.png")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("UI", 0x27ff46b0))
->Attribute(AZ::Edit::Attributes::AutoExpand, true);
//set the dynamic edit data provider
editInfo->SetDynamicEditDataProvider(&ParameterComponent::GetEditData);
//parameters group
{
editInfo->DataElement(0, &ParameterComponent::params, "Parameters", "List of parameters.");
ec->Class<ParameterGroup>("Parameter Group", "The group of parameters")
->ClassElement(AZ::Edit::ClassElements::EditorData, "ParameterGroup's class attributes.")
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &ParameterGroup::m_name)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(0, &ParameterGroup::m_params, "m_params", "Parameters in this property group")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly);
ec->Class<Parameter>("Parameter", "A Parameter")
->ClassElement(AZ::Edit::ClassElements::EditorData, "Parameter Attribute.")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Slider, &Parameter::val, "val", "A float");
}
}
}
}
const AZ::Edit::ElementData* ParameterComponent::GetEditData(const void* handlerPtr, const void* elementPtr, const AZ::Uuid& elementType) {
const ParameterComponent* owner = reinterpret_cast<const ParameterComponent*>(handlerPtr);
return owner->GetDataElement(elementPtr, elementType);
}
const AZ::Edit::ElementData* ParameterComponent::GetDataElement(const void* element, const AZ::Uuid& typeUuid) const {
auto it = m_dataElements.find(element);
if (it != m_dataElements.end()) {
if (it->second.m_uuid == typeUuid) {
return &it->second.m_editData;
}
}
return nullptr;
}
void ParameterComponent::LoadParams() {
for (int i = 0; i < 10; i++) {
Parameter * p = new Parameter;
p->name = AZStd::string(itoa(i));
p->val = 1.0f;
this->params.m_params.push_back(p);
//editor data
p->InitEdit();
ElementInfo ei;
ei.m_editData = p->ed;
ei.m_uuid = AZ::SerializeTypeInfo<float>::GetUuid();
this->m_dataElements.insert(AZStd::make_pair(&p->val, ei));
}
this->group.m_params.shrink_to_fit(); //free up unused memory
}
void ParameterComponent::FreeParams() {
this->group.Clear();
this->m_dataElements.clear(); //clear all editor data
}
void Parameter::InitEdit() {
this->ed.m_elementId = AZ::Edit::UIHandlers::SpinBox;
this->ed.m_name = this->name.c_str();
this->ed.m_description = this->name.c_str();
}
void Parameter::Reflect(AZ::SerializeContext* serializeContext) {
serializeContext->Class<Parameter>()->
Version(1)
->Field("name", &Parameter::name)
->Field("value", &Parameter::val);
}
void ParameterGroup::Clear() {
if (this->m_params.size() != 0) {
this->m_params.clear(); //clear the parameters vector
}
}
void ParameterGroup::Reflect(AZ::SerializeContext* serializeContext) {
serializeContext->Class<ParameterGroup>()
->Field("Name", &ParameterGroup::m_name)
->Field("Params", &ParameterGroup::m_params);
}