|
@@ -148,6 +148,13 @@ namespace AZ::Reflection
|
|
|
// Commonly, it is the parent instance for property nodes, and the instance for UI element nodes.
|
|
|
void* m_instanceToInvoke = nullptr;
|
|
|
AZ::TypeId m_typeId;
|
|
|
+
|
|
|
+ //! Keeps tracks of how many pointers are attached to the instance type
|
|
|
+ //! For example if the actual instance being referenced is an AZ::Component, then m_pointerLevel is 0
|
|
|
+ //! But if the instance being referenced is an AZ::Component*, then the m_pointerLevel is 1
|
|
|
+ //! Finally if the instance being referenced is an AZ::Component**, then the m_pointerLevel is 2
|
|
|
+ AZ::u32 m_pointerLevel = 0;
|
|
|
+
|
|
|
const SerializeContext::ClassData* m_classData = nullptr;
|
|
|
const SerializeContext::ClassElement* m_classElement = nullptr;
|
|
|
AZStd::vector<AttributeData> m_cachedAttributes;
|
|
@@ -185,6 +192,24 @@ namespace AZ::Reflection
|
|
|
return nullptr;
|
|
|
}
|
|
|
|
|
|
+ //! Returns an address that can be casted to the Type of the instance via a "reinterpret_cast<Type*>" cast
|
|
|
+ //! If the instance is storing a pointer to a Type*, that is actually a Type**
|
|
|
+ //! So this code make sure that dereferences occurs before returning the address
|
|
|
+ const void* GetRawInstance() const
|
|
|
+ {
|
|
|
+ const void* directInstance = m_instance;
|
|
|
+ for (AZ::u32 pointersToDereference = m_pointerLevel; pointersToDereference > 0; --pointersToDereference)
|
|
|
+ {
|
|
|
+ directInstance = *reinterpret_cast<const void* const*>(directInstance);
|
|
|
+ }
|
|
|
+ return directInstance;
|
|
|
+ }
|
|
|
+
|
|
|
+ void* GetRawInstance()
|
|
|
+ {
|
|
|
+ return const_cast<void*>(AZStd::as_const(*this).GetRawInstance());
|
|
|
+ }
|
|
|
+
|
|
|
AZStd::vector<AZStd::pair<AZStd::string, AZStd::optional<StackEntry>>> m_groups;
|
|
|
AZStd::map<AZStd::string, AZStd::vector<StackEntry>> m_groupEntries;
|
|
|
AZStd::map<AZStd::string, AZStd::string> m_propertyToGroupMap;
|
|
@@ -212,7 +237,7 @@ namespace AZ::Reflection
|
|
|
, m_serializeContext(serializeContext)
|
|
|
{
|
|
|
// Push a dummy node into stack, which serves as the parent node for the first node.
|
|
|
- m_stack.push_back({ instance, nullptr, typeId });
|
|
|
+ m_stack.emplace_back(StackEntry{ instance, nullptr, typeId });
|
|
|
|
|
|
m_visitFromRoot = visitFromRoot;
|
|
|
|
|
@@ -257,7 +282,7 @@ namespace AZ::Reflection
|
|
|
{
|
|
|
m_handlers[azrtti_typeid<T>()] = [this, handler = AZStd::move(handler)]() -> bool
|
|
|
{
|
|
|
- return handler(*reinterpret_cast<T*>(m_stack.back().m_instance));
|
|
|
+ return handler(*reinterpret_cast<T*>(Get()));
|
|
|
};
|
|
|
}
|
|
|
|
|
@@ -283,7 +308,7 @@ namespace AZ::Reflection
|
|
|
|
|
|
// Note that this is the dummy parent node for the root node. It contains null classData and classElement.
|
|
|
const StackEntry& nodeData = m_stack.back();
|
|
|
- m_serializeContext->EnumerateInstance(m_enumerateContext, nodeData.m_instance, nodeData.m_typeId, nullptr, nullptr);
|
|
|
+ m_serializeContext->EnumerateInstanceConst(m_enumerateContext, nodeData.GetRawInstance(), nodeData.m_typeId, nullptr, nullptr);
|
|
|
}
|
|
|
|
|
|
void GenerateNodePath(const StackEntry& parentData, StackEntry& nodeData)
|
|
@@ -354,12 +379,17 @@ namespace AZ::Reflection
|
|
|
if (currElement.m_serializeClassElement)
|
|
|
{
|
|
|
// groups with a serialize class element are toggle groups, make an entry for their bool value
|
|
|
+ const AZ::Serialize::ClassElement* serializeClassElement = currElement.m_serializeClassElement;
|
|
|
void* boolAddress = reinterpret_cast<void*>(
|
|
|
- reinterpret_cast<size_t>(nodeData.m_instance) + currElement.m_serializeClassElement->m_offset);
|
|
|
+ reinterpret_cast<size_t>(nodeData.GetRawInstance()) + serializeClassElement->m_offset);
|
|
|
+ boolAddress = AZ::Utils::ResolvePointer(boolAddress, *serializeClassElement, *m_serializeContext);
|
|
|
+
|
|
|
+ constexpr AZ::u32 pointerLevel = 0;
|
|
|
|
|
|
StackEntry entry = { boolAddress,
|
|
|
nodeData.m_instance,
|
|
|
- currElement.m_serializeClassElement->m_typeId,
|
|
|
+ serializeClassElement->m_typeId,
|
|
|
+ pointerLevel,
|
|
|
nullptr,
|
|
|
currElement.m_serializeClassElement };
|
|
|
nodeData.m_groups.emplace_back(AZStd::make_pair(groupName, AZStd::move(entry)));
|
|
@@ -374,9 +404,12 @@ namespace AZ::Reflection
|
|
|
AZ::SerializeContext::ClassElement* UIElement = new AZ::SerializeContext::ClassElement();
|
|
|
UIElement->m_editData = &currElement;
|
|
|
UIElement->m_flags = SerializeContext::ClassElement::Flags::FLG_UI_ELEMENT;
|
|
|
+ // A UI node isn't backed with a real C++ type, so its pointer level is 0
|
|
|
+ constexpr AZ::u32 pointerLevel = 0;
|
|
|
StackEntry entry = { nodeData.m_instance,
|
|
|
nodeData.m_instance,
|
|
|
nodeData.m_classData->m_typeId,
|
|
|
+ pointerLevel,
|
|
|
nodeData.m_classData,
|
|
|
UIElement };
|
|
|
|
|
@@ -433,8 +466,10 @@ namespace AZ::Reflection
|
|
|
UIElement->m_editData = &*iter;
|
|
|
UIElement->m_flags = SerializeContext::ClassElement::Flags::FLG_UI_ELEMENT;
|
|
|
// Use the instance itself to retrieve parameter values that invoke functions on the UI Element.
|
|
|
+ constexpr AZ::u32 pointerLevel = 0;
|
|
|
StackEntry entry = {
|
|
|
- nodeData.m_instance, nodeData.m_instance, nodeData.m_classData->m_typeId, nodeData.m_classData, UIElement
|
|
|
+ nodeData.m_instance, nodeData.m_instance, nodeData.m_classData->m_typeId, pointerLevel,
|
|
|
+ nodeData.m_classData, UIElement
|
|
|
};
|
|
|
|
|
|
AZStd::string pathString = nodeData.m_path;
|
|
@@ -633,11 +668,15 @@ namespace AZ::Reflection
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // Set the pointerLevel to 0 as the pointer is resolved by the AZ::Utils::ResolvePointer
|
|
|
+ constexpr AZ::u32 pointerLevel = 0;
|
|
|
+
|
|
|
+
|
|
|
// search up the stack for the "true parent", which is the last entry created by the serialize enumerate itself
|
|
|
void* instanceToInvoke = instance;
|
|
|
for (auto rIter = m_stack.rbegin(), rEnd = m_stack.rend(); rIter != rEnd; ++rIter)
|
|
|
{
|
|
|
- auto* currInstance = rIter->m_instance;
|
|
|
+ auto* currInstance = rIter->GetRawInstance();
|
|
|
if (rIter->m_createdByEnumerate && currInstance)
|
|
|
{
|
|
|
instanceToInvoke = currInstance;
|
|
@@ -646,13 +685,12 @@ namespace AZ::Reflection
|
|
|
}
|
|
|
|
|
|
StackEntry newEntry = {
|
|
|
- instance, instanceToInvoke, classData ? classData->m_typeId : Uuid::CreateNull(), classData, classElement
|
|
|
+ instance, instanceToInvoke, classData ? classData->m_typeId : Uuid::CreateNull(), pointerLevel,
|
|
|
+ classData, classElement
|
|
|
};
|
|
|
newEntry.m_createdByEnumerate = true;
|
|
|
|
|
|
- m_stack.push_back(newEntry);
|
|
|
-
|
|
|
- StackEntry& nodeData = m_stack.back();
|
|
|
+ StackEntry& nodeData = m_stack.emplace_back(newEntry);
|
|
|
|
|
|
// Generate this node's path (will be stored in nodeData.m_path)
|
|
|
GenerateNodePath(parentData, nodeData);
|
|
@@ -738,14 +776,15 @@ namespace AZ::Reflection
|
|
|
continue;
|
|
|
}
|
|
|
auto& parentEntry = m_stack.back();
|
|
|
- m_stack.push_back({ groupEntry.m_instance, nullptr, AZ::TypeId() });
|
|
|
- InheritChangeNotify(parentEntry, m_stack.back());
|
|
|
+ StackEntry& newStackEntry = m_stack.emplace_back(StackEntry{ groupEntry.m_instance, nullptr, AZ::TypeId(), groupEntry.m_pointerLevel,
|
|
|
+ groupEntry.m_classData, groupEntry.m_classElement });
|
|
|
+ InheritChangeNotify(parentEntry, newStackEntry);
|
|
|
m_serializeContext->EnumerateInstance(
|
|
|
m_enumerateContext,
|
|
|
- groupEntry.m_instance,
|
|
|
- groupEntry.m_typeId,
|
|
|
- groupEntry.m_classData,
|
|
|
- groupEntry.m_classElement);
|
|
|
+ newStackEntry.m_instance,
|
|
|
+ newStackEntry.m_typeId,
|
|
|
+ newStackEntry.m_classData,
|
|
|
+ newStackEntry.m_classElement);
|
|
|
m_stack.pop_back();
|
|
|
}
|
|
|
|
|
@@ -797,14 +836,18 @@ namespace AZ::Reflection
|
|
|
return classData ? classData->m_name : "<unregistered type>";
|
|
|
}
|
|
|
|
|
|
+ // This returns a pointer to an instance that can be cast to a <TypeId>* for that instance
|
|
|
+ // Even if the StackEntry::m_instance is referencing a <TypeId>**, a pointer is returned that can be casted to <TypeId>*
|
|
|
void* Get() override
|
|
|
{
|
|
|
- return m_stack.back().m_instance;
|
|
|
+ StackEntry& topEntry = m_stack.back();
|
|
|
+ return topEntry.GetRawInstance();
|
|
|
}
|
|
|
|
|
|
const void* Get() const override
|
|
|
{
|
|
|
- return m_stack.back().m_instance;
|
|
|
+ const StackEntry& topEntry = m_stack.back();
|
|
|
+ return topEntry.GetRawInstance();
|
|
|
}
|
|
|
|
|
|
AZStd::string_view GetNodeDisplayLabel(StackEntry& nodeData, AZStd::fixed_string<128>& labelAttributeBuffer)
|
|
@@ -930,7 +973,7 @@ namespace AZ::Reflection
|
|
|
}
|
|
|
visitedAttributes.insert(name);
|
|
|
|
|
|
- // Handle visibility calculations internally, as we calculate and emit an aggregate visiblity value.
|
|
|
+ // Handle visibility calculations internally, as we calculate and emit an aggregate visibility value.
|
|
|
// We also need to handle special cases here, because the Visibility attribute supports 3 different value types:
|
|
|
// 1. AZ::Crc32 - This is the default
|
|
|
// 2. AZ::u32 - This allows the user to specify a value of 1/0 for Show/Hide, respectively
|