Files
UnrealEngineUWP/Engine/Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/StateTreePropertyHelpers.cpp
mikko mononen fc52c04579 StateTree: Fixed crash when removing a task from state
[CL 30073585 by mikko mononen in ue5-main branch]
2023-12-04 04:05:29 -05:00

207 lines
7.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "StateTreePropertyHelpers.h"
#include "StateTreeEditorNode.h"
namespace UE::StateTree::PropertyHelpers
{
void DispatchPostEditToNodes(UObject& Owner, FPropertyChangedChainEvent& InPropertyChangedEvent)
{
// Walk back from the changed property and look for first FStateTreeEditorNode, and call the node specific post edit methods.
const TDoubleLinkedList<FProperty*>::TDoubleLinkedListNode* CurrentPropNode = InPropertyChangedEvent.PropertyChain.GetHead();
const FProperty* HeadProperty = CurrentPropNode->GetValue();
check(HeadProperty);
if (HeadProperty->GetOwnerClass() != Owner.GetClass())
{
return;
}
uint8* CurrentAddress = reinterpret_cast<uint8*>(&Owner);
while (CurrentPropNode)
{
const FProperty* CurrentProperty = CurrentPropNode->GetValue();
check(CurrentProperty);
CurrentAddress = CurrentAddress + CurrentProperty->GetOffset_ForInternal();
while (const FArrayProperty* ArrayProperty = CastField<FArrayProperty>(CurrentProperty))
{
FScriptArrayHelper Helper(ArrayProperty, CurrentAddress);
const int32 Index = InPropertyChangedEvent.GetArrayIndex(ArrayProperty->GetName());
if (!Helper.IsValidIndex(Index))
{
return;
}
CurrentAddress = Helper.GetRawPtr(Index);
CurrentProperty = ArrayProperty->Inner;
}
if (const FStructProperty* StructProperty = CastField<FStructProperty>(CurrentProperty))
{
if (StructProperty->Struct == FStateTreeEditorNode::StaticStruct())
{
FStateTreeEditorNode& EditorNode = *reinterpret_cast<FStateTreeEditorNode*>(CurrentAddress);
if (FStateTreeNodeBase* StateTreeNode = EditorNode.Node.GetMutablePtr<FStateTreeNodeBase>())
{
// Check that the path contains EditorNode's: Node, Instance or Instance Object
if (const TDoubleLinkedList<FProperty*>::TDoubleLinkedListNode* EditorNodeMemberPropNode = CurrentPropNode->GetNextNode())
{
// Check that we have a changed property on one of the above properties.
if (const TDoubleLinkedList<FProperty*>::TDoubleLinkedListNode* ActiveMemberPropNode = EditorNodeMemberPropNode->GetNextNode())
{
// Update the event
const FProperty* EditorNodeChildMember = EditorNodeMemberPropNode->GetValue();
check(EditorNodeChildMember);
// Take copy of the event, we'll modify it.
FEditPropertyChain PropertyChainCopy;
for (const TDoubleLinkedList<FProperty*>::TDoubleLinkedListNode* Node = InPropertyChangedEvent.PropertyChain.GetHead(); Node->GetNextNode(); Node = Node->GetNextNode())
{
PropertyChainCopy.AddTail(Node->GetValue());
}
FPropertyChangedChainEvent PropertyChangedEvent(PropertyChainCopy, InPropertyChangedEvent);
PropertyChangedEvent.SetActiveMemberProperty(ActiveMemberPropNode->GetValue());
PropertyChangedEvent.PropertyChain.SetActiveMemberPropertyNode(PropertyChangedEvent.MemberProperty);
// To be consistent with the other property chain callbacks, do not cross object boundary.
const TDoubleLinkedList<FProperty*>::TDoubleLinkedListNode* ActivePropNode = ActiveMemberPropNode;
while (ActivePropNode->GetNextNode())
{
if (CastField<FObjectProperty>(ActivePropNode->GetValue()))
{
break;
}
ActivePropNode = ActivePropNode->GetNextNode();
}
PropertyChangedEvent.Property = ActivePropNode->GetValue();
PropertyChangedEvent.PropertyChain.SetActivePropertyNode(PropertyChangedEvent.Property);
if (EditorNodeChildMember->GetFName() == GET_MEMBER_NAME_CHECKED(FStateTreeEditorNode, Node))
{
StateTreeNode->PostEditNodeChangeChainProperty(PropertyChangedEvent, EditorNode.GetInstance());
}
else if (EditorNodeChildMember->GetFName() == GET_MEMBER_NAME_CHECKED(FStateTreeEditorNode, Instance))
{
if (EditorNode.Instance.IsValid())
{
StateTreeNode->PostEditInstanceDataChangeChainProperty(PropertyChangedEvent, FStateTreeDataView(EditorNode.Instance));
}
}
else if (EditorNodeChildMember->GetFName() == GET_MEMBER_NAME_CHECKED(FStateTreeEditorNode, InstanceObject))
{
if (EditorNode.InstanceObject)
{
StateTreeNode->PostEditInstanceDataChangeChainProperty(PropertyChangedEvent, FStateTreeDataView(EditorNode.InstanceObject));
}
}
}
}
}
break;
}
CurrentPropNode = CurrentPropNode->GetNextNode();
}
else
{
break;
}
}
}
}; // UE::StateTree::PropertyHelpers
// ------------------------------------------------------------------------------
// FStateTreeEditPropertyPath
// ------------------------------------------------------------------------------
FStateTreeEditPropertyPath::FStateTreeEditPropertyPath(const UStruct* BaseStruct, const FString& InPath)
{
TArray<FString> PathSegments;
InPath.ParseIntoArray(PathSegments, TEXT("."));
const UStruct* CurrBase = BaseStruct;
for (const FString& Segment : PathSegments)
{
const FName PropertyName(Segment);
if (const FProperty* Property = CurrBase->FindPropertyByName(PropertyName))
{
Path.Emplace(Property, PropertyName);
if (const FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property))
{
Property = ArrayProperty->Inner;
}
if (const FStructProperty* StructProperty = CastField<FStructProperty>(Property))
{
CurrBase = StructProperty->Struct;
}
else if (const FObjectProperty* ObjectProperty = CastField<FObjectProperty>(Property))
{
CurrBase = ObjectProperty->PropertyClass;
}
}
else
{
checkf(false, TEXT("Path %s id not part of type %s."), *InPath, *GetNameSafe(BaseStruct));
Path.Reset();
break;
}
}
}
FStateTreeEditPropertyPath::FStateTreeEditPropertyPath(const FPropertyChangedChainEvent& PropertyChangedEvent)
{
FEditPropertyChain::TDoubleLinkedListNode* PropertyNode = PropertyChangedEvent.PropertyChain.GetActiveMemberNode();
while (PropertyNode != nullptr)
{
if (FProperty* Property = PropertyNode->GetValue())
{
const FName PropertyName = Property->GetFName();
const int32 ArrayIndex = PropertyChangedEvent.GetArrayIndex(PropertyName.ToString());
Path.Emplace(Property, PropertyName, ArrayIndex);
}
PropertyNode = PropertyNode->GetNextNode();
}
}
bool FStateTreeEditPropertyPath::ContainsPath(const FStateTreeEditPropertyPath& InPath) const
{
if (InPath.Path.Num() > Path.Num())
{
return false;
}
for (TConstEnumerateRef<FStateTreeEditPropertySegment> Segment : EnumerateRange(InPath.Path))
{
if (Segment->PropertyName != Path[Segment.GetIndex()].PropertyName)
{
return false;
}
}
return true;
}
/** @return true if the property path is exactly the specified path. */
bool FStateTreeEditPropertyPath::IsPathExact(const FStateTreeEditPropertyPath& InPath) const
{
if (InPath.Path.Num() != Path.Num())
{
return false;
}
for (TConstEnumerateRef<FStateTreeEditPropertySegment> Segment : EnumerateRange(InPath.Path))
{
if (Segment->PropertyName != Path[Segment.GetIndex()].PropertyName)
{
return false;
}
}
return true;
}