Files
2025-05-18 13:04:45 +08:00

206 lines
5.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "HierarchyTableType.h"
#include "StructUtils/InstancedStruct.h"
#include "HierarchyTable.generated.h"
#define UE_API HIERARCHYTABLERUNTIME_API
class USkeleton;
class UHierarchyTable;
/**
* The data associated with each item in a hierarchy table.
*/
USTRUCT()
struct FHierarchyTableEntryData
{
GENERATED_BODY()
public:
FHierarchyTableEntryData()
: Parent(INDEX_NONE)
{
}
friend UHierarchyTable;
// The hierarchy table that this entry resides in.
UPROPERTY()
TObjectPtr<UHierarchyTable> OwnerTable;
/**
* The actual user set data this element stores, matches the owner table's element type.
* If unset, will inherit from the parent.
* Is always set for entries with no parent.
*/
UPROPERTY()
TOptional<FInstancedStruct> Payload;
// This entry's display name and unique identifier.
UPROPERTY()
FName Identifier;
// The index of this entry's parent, is INDEX_NONE in the case of a root element.
UPROPERTY()
int32 Parent;
// A second data payload specific to the table's type and is read-only metadata.
UPROPERTY()
FInstancedStruct TablePayload;
public:
bool HasParent() const { return Parent != INDEX_NONE; }
bool IsOverridden() const { return Payload.IsSet(); }
UE_API bool HasOverriddenChildren() const;
UE_API void ToggleOverridden();
// Assumes payload is set, i.e. is overridden
UE_API const TOptional<FInstancedStruct>& GetPayload() const;
template <typename T>
const T* GetValue() const
{
const FInstancedStruct* StructPtr = GetActualValue();
check(StructPtr->IsValid());
return StructPtr->GetPtr<T>();
}
template <typename T>
const T& GetMetadata() const
{
return TablePayload.Get<T>();
}
template <typename T>
T* GetMutableValue()
{
check(IsOverridden());
return Payload.GetValue().GetMutablePtr<T>();
}
UE_API const FHierarchyTableEntryData* GetClosestAncestor() const;
private:
// TODO: This needs to be cached in some way as it potentially goes up the entire hierarchy until it finds an ancestor with an overridden value
// This is called each time a widget is ticked so grows exponentially with the height of the hierarchy. This needs addressing at some point
// but cached value must be updated when any of its direct ancestors' value is updated.
UE_API const FInstancedStruct* GetActualValue() const;
UE_API const FInstancedStruct* GetFromClosestAncestor() const;
UE_API bool IsOverriddenOrHasOverriddenChildren(const bool bIncludeSelf) const;
};
/**
* A general-purpose container asset for storing typed hierarchical data.
*/
UCLASS(MinimalAPI, EditInlineNew, BlueprintType)
class UHierarchyTable : public UObject
{
GENERATED_BODY()
public:
UE_API UHierarchyTable();
private:
// The table metadata stores any data dependent on the table type needed to create and maintain the hierarchy.
UPROPERTY(EditAnywhere, Category = Default)
FInstancedStruct TableMetadata;
// The element type is the type that each element is mapped into.
UPROPERTY(VisibleAnywhere, Category = Default)
TObjectPtr<const UScriptStruct> ElementType;
// The actual table data
UPROPERTY()
TArray<FHierarchyTableEntryData> TableData;
public:
template <typename InTableType>
bool IsTableType() const
{
return TableMetadata.GetScriptStruct() == InTableType::StaticStruct();
}
template <typename InTableType>
InTableType GetTableMetadata() const
{
check(IsTableType<InTableType>());
return TableMetadata.Get<InTableType>();
}
const UScriptStruct* GetTableMetadataStruct() const
{
return TableMetadata.GetScriptStruct();
}
template <typename InElementType>
bool IsElementType() const
{
return ElementType == InElementType::StaticStruct();
}
UE_API FInstancedStruct CreateDefaultValue() const;
void Initialize(const FInstancedStruct& InTableMetadata, const TObjectPtr<const UScriptStruct> InElementType)
{
TableMetadata = InTableMetadata;
ElementType = InElementType;
TableData.Reset();
}
const FInstancedStruct& GetTableMetadata() const { return TableMetadata; }
const TObjectPtr<const UScriptStruct>& GetElementType() const { return ElementType; }
const TArray<FHierarchyTableEntryData>& GetTableData() const { return TableData; }
UE_API void EmptyTable();
// TODO: Remove in the future to avoid API signatures using indices
UE_API int32 GetTableEntryIndex(const FName EntryIdentifier) const;
UE_API const FHierarchyTableEntryData* const GetTableEntry(const FName EntryIdentifier) const;
// TODO: Remove in the future to avoid API signatures using indices
UE_API const FHierarchyTableEntryData* const GetTableEntry(const int32 EntryIndex) const;
// TODO: Remove in the future to avoid API signatures using indices
UE_API FHierarchyTableEntryData* const GetMutableTableEntry(const int32 EntryIndex);
UE_API int32 AddEntry(const FHierarchyTableEntryData& Entry);
UE_API void AddBulkEntries(const TConstArrayView<FHierarchyTableEntryData> Entries);
UE_API void RemoveEntry(const int32 Index);
UE_API TArray<const FHierarchyTableEntryData*> GetChildren(const FHierarchyTableEntryData& Parent) const;
UE_API bool HasIdentifier(const FName Identifier) const;
template <typename T>
FHierarchyTableEntryData* FindEntry(const FName EntryIdentifier)
{
return TableData.FindByPredicate([EntryIdentifier](const FHierarchyTableEntryData& Entry)
{
return Entry.Identifier == EntryIdentifier;
});
}
UE_API FGuid GetHierarchyGuid();
private:
UE_API void RegenerateHierarchyGuid();
UPROPERTY()
FGuid HierarchyGuid;
};
#undef UE_API