Lots of work on WingTypes
This commit is contained in:
@@ -63,7 +63,12 @@ public:
|
||||
if (InternalName.IsEmpty()) return;
|
||||
|
||||
// Resolve the component class by name
|
||||
UClass* ComponentClass = UWingTypes::TextToOneObjectType(Class);
|
||||
UWingTypes::Requirements Req;
|
||||
Req.BlueprintType = false;
|
||||
Req.Blueprintable = false;
|
||||
Req.AllowContainer = false;
|
||||
Req.IsChildOf = UActorComponent::StaticClass();
|
||||
UClass* ComponentClass = UWingTypes::TextToOneObjectType(Class, Req);
|
||||
if (!ComponentClass) return;
|
||||
|
||||
// Find the specified parent component
|
||||
|
||||
@@ -51,7 +51,11 @@ public:
|
||||
|
||||
if (!Type.IsEmpty())
|
||||
{
|
||||
UClass* TypeClass = UWingTypes::TextToOneObjectType(Type);
|
||||
UWingTypes::Requirements Req;
|
||||
Req.BlueprintType = false;
|
||||
Req.Blueprintable = false;
|
||||
Req.AllowContainer = false;
|
||||
UClass* TypeClass = UWingTypes::TextToOneObjectType(Type, Req);
|
||||
if (!TypeClass) return;
|
||||
Filter.ClassPaths.Add(TypeClass->GetClassPathName());
|
||||
}
|
||||
|
||||
@@ -39,7 +39,11 @@ public:
|
||||
if (!BP) return;
|
||||
|
||||
// Resolve the interface class
|
||||
UClass* InterfaceClass = UWingTypes::TextToOneInterfaceType(Interface);
|
||||
UWingTypes::Requirements Req;
|
||||
Req.BlueprintType = false;
|
||||
Req.Blueprintable = false;
|
||||
Req.AllowContainer = false;
|
||||
UClass* InterfaceClass = UWingTypes::TextToOneInterfaceType(Interface, Req);
|
||||
if (!InterfaceClass) return;
|
||||
|
||||
// Check for duplicates
|
||||
|
||||
@@ -43,15 +43,19 @@ public:
|
||||
if (!Maker.Ok()) return;
|
||||
|
||||
// Resolve parent class based on blueprint type
|
||||
UWingTypes::Requirements Req;
|
||||
Req.BlueprintType = false;
|
||||
Req.Blueprintable = true;
|
||||
Req.AllowContainer = false;
|
||||
UClass* ParentClassObj = nullptr;
|
||||
switch (BlueprintType)
|
||||
{
|
||||
case BPTYPE_Normal:
|
||||
ParentClassObj = UWingTypes::TextToOneObjectType(ParentClass);
|
||||
ParentClassObj = UWingTypes::TextToOneObjectType(ParentClass, Req);
|
||||
if (!ParentClassObj) return;
|
||||
break;
|
||||
case BPTYPE_MacroLibrary:
|
||||
ParentClassObj = UWingTypes::TextToOneObjectType(ParentClass);
|
||||
ParentClassObj = UWingTypes::TextToOneObjectType(ParentClass, Req);
|
||||
if (!ParentClassObj) return;
|
||||
break;
|
||||
case BPTYPE_Interface:
|
||||
|
||||
@@ -41,7 +41,11 @@ public:
|
||||
if (!BP) return;
|
||||
|
||||
// Find the new parent class by short type name
|
||||
UClass* NewParentClassObj = UWingTypes::TextToOneObjectType(Parent);
|
||||
UWingTypes::Requirements Req;
|
||||
Req.BlueprintType = false;
|
||||
Req.Blueprintable = true;
|
||||
Req.AllowContainer = false;
|
||||
UClass* NewParentClassObj = UWingTypes::TextToOneObjectType(Parent, Req);
|
||||
if (!NewParentClassObj) return;
|
||||
|
||||
// Validate reparent
|
||||
|
||||
@@ -4,13 +4,6 @@
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingTypes.h"
|
||||
#include "WingUtils.h"
|
||||
#include "AssetRegistry/AssetData.h"
|
||||
#include "AssetRegistry/IAssetRegistry.h"
|
||||
#include "UObject/UObjectIterator.h"
|
||||
#include "StructUtils/UserDefinedStruct.h"
|
||||
#include "Engine/UserDefinedEnum.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "TypeName_Search.generated.h"
|
||||
|
||||
|
||||
@@ -30,73 +23,46 @@ public:
|
||||
UPROPERTY(meta=(Optional, Description="Maximum number of results"))
|
||||
int32 Limit = 100;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="If true, include all types, not just BlueprintType/Blueprintable ones"))
|
||||
bool Exhaustive = false;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Search for type names usable in pin type specifications. "
|
||||
"Returns short names that can be used with commands like Blueprint_ChangeVariableType.");
|
||||
}
|
||||
|
||||
void TryMatchObject(TSet<UObject*> &Matches, UObject *Obj)
|
||||
{
|
||||
if (!Obj) return;
|
||||
FString Name = Obj->GetName();
|
||||
if (!Name.Contains(Query, ESearchCase::IgnoreCase)) return;
|
||||
Matches.Add(Obj);
|
||||
}
|
||||
|
||||
void TryMatchObjects(TSet<UObject*> &Matches, UClass *Class)
|
||||
{
|
||||
ForEachObjectOfClass(Class, [&](UObject *Obj){
|
||||
if (Matches.Num() == Limit) return;
|
||||
TryMatchObject(Matches, Obj);
|
||||
}, true);
|
||||
}
|
||||
|
||||
void TryMatchAssets(TSet<UObject*> &Matches, UClass *Class)
|
||||
{
|
||||
IAssetRegistry& Registry = *IAssetRegistry::Get();
|
||||
TArray<FAssetData> AssetResults;
|
||||
Registry.GetAssetsByClass(Class->GetClassPathName(), AssetResults, true);
|
||||
for (const FAssetData& Data : AssetResults)
|
||||
{
|
||||
if (Matches.Num() == Limit) return;
|
||||
FString Name = Data.AssetName.ToString();
|
||||
if (!Name.Contains(Query, ESearchCase::IgnoreCase)) continue;
|
||||
UObject *Obj = Data.GetAsset();
|
||||
if (UBlueprint* BP = Cast<UBlueprint>(Obj))
|
||||
Obj = BP->GeneratedClass;
|
||||
TryMatchObject(Matches, Obj);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
TSet<UObject*> Matches;
|
||||
TryMatchObjects(Matches, UScriptStruct::StaticClass());
|
||||
TryMatchObjects(Matches, UClass::StaticClass());
|
||||
TryMatchObjects(Matches, UEnum::StaticClass());
|
||||
TryMatchAssets(Matches, UBlueprint::StaticClass());
|
||||
TryMatchAssets(Matches, UUserDefinedStruct::StaticClass());
|
||||
TryMatchAssets(Matches, UUserDefinedEnum::StaticClass());
|
||||
const TMap<FString, UWingTypes::Info>& AllTypes = UWingTypes::GetAllTypes();
|
||||
|
||||
TArray<FString> Results;
|
||||
for (const UObject *Obj : Matches)
|
||||
for (const auto& Pair : AllTypes)
|
||||
{
|
||||
const TCHAR *Kind = TEXT("Unknown");
|
||||
if (Cast<UEnum>(Obj))
|
||||
Kind = TEXT("Enum");
|
||||
else if (Cast<UScriptStruct>(Obj))
|
||||
Kind = TEXT("Struct");
|
||||
else if (const UClass* Class = Cast<UClass>(Obj))
|
||||
Kind = Class->IsChildOf(UInterface::StaticClass()) ? TEXT("Interface") : TEXT("Class");
|
||||
Results.Add(FString::Printf(TEXT("%s %s\n"), Kind, *UWingTypes::TypeToText(Obj)));
|
||||
if (Results.Num() >= Limit) break;
|
||||
|
||||
const UWingTypes::Info& Info = Pair.Value;
|
||||
if (!Info.Short.Contains(Query, ESearchCase::IgnoreCase)) continue;
|
||||
if (!Exhaustive && !UWingTypes::IsBlueprintType(Info) && !UWingTypes::IsBlueprintable(Info)) continue;
|
||||
|
||||
FString Line = FString::Printf(TEXT("%s %s %s"),
|
||||
*Info.PinCategory.ToString(), *Info.Short, *Info.PinSubCategoryObject);
|
||||
if (Info.NativeParent)
|
||||
Line += FString::Printf(TEXT(" Nat=%s"), *Info.NativeParent->GetName());
|
||||
if (!Info.PinSubCategory.IsNone())
|
||||
Line += FString::Printf(TEXT(" PSC=%s"), *Info.PinSubCategory.ToString());
|
||||
if (Info.IsUserDefined)
|
||||
Line += TEXT(" (UserDefined)");
|
||||
Line += TEXT("\n");
|
||||
Results.Add(MoveTemp(Line));
|
||||
}
|
||||
|
||||
Results.Sort();
|
||||
for (const auto &Result : Results)
|
||||
for (const auto& Result : Results)
|
||||
{
|
||||
UWingServer::Print(Result);
|
||||
}
|
||||
if (Results.Num() == Limit)
|
||||
if (Results.Num() >= Limit)
|
||||
{
|
||||
UWingServer::Printf(TEXT("Search limit reached, raise it with Limit=\n"));
|
||||
}
|
||||
|
||||
@@ -69,7 +69,11 @@ bool WingFunctionArgs::ParseArgs(const FString& Args, TArray<FParsedArg>& OutArg
|
||||
}
|
||||
|
||||
FParsedArg Arg;
|
||||
if (!UWingTypes::TextToType(TypeStr, Arg.PinType)) return false;
|
||||
UWingTypes::Requirements Req;
|
||||
Req.BlueprintType = true;
|
||||
Req.Blueprintable = false;
|
||||
Req.AllowContainer = true;
|
||||
if (!UWingTypes::TextToType(TypeStr, Arg.PinType, Req)) return false;
|
||||
Arg.PinName = FName(*NameStr);
|
||||
OutArgs.Add(MoveTemp(Arg));
|
||||
}
|
||||
|
||||
@@ -62,7 +62,11 @@ bool FWingProperty::SetText(const FString &Value)
|
||||
if (IsPinTypeProperty(Prop))
|
||||
{
|
||||
FEdGraphPinType PinType;
|
||||
if (!UWingTypes::TextToType(Value, PinType)) return false;
|
||||
UWingTypes::Requirements Req;
|
||||
Req.BlueprintType = true;
|
||||
Req.Blueprintable = false;
|
||||
Req.AllowContainer = true;
|
||||
if (!UWingTypes::TextToType(Value, PinType, Req)) return false;
|
||||
Prop->SetValue_InContainer(Container, &PinType);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,46 +8,182 @@
|
||||
#include "Engine/UserDefinedEnum.h"
|
||||
#include "UObject/UObjectIterator.h"
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "Blueprint/BlueprintSupport.h"
|
||||
#include "Kismet2/KismetEditorUtilities.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Simple Accessors
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const TMap<FString, UWingTypes::Info>& UWingTypes::GetAllTypes()
|
||||
{
|
||||
UWingTypes* Types = GEditor->GetEditorSubsystem<UWingTypes>();
|
||||
check(Types);
|
||||
return Types->ShortToInfo;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Simple Utility Functions.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
bool UWingTypes::IsBlueprintType(const UObject *Obj)
|
||||
{
|
||||
if (const UEnum* Enum = Cast<UEnum>(Obj))
|
||||
return UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Enum);
|
||||
if (const UScriptStruct* Struct = Cast<UScriptStruct>(Obj))
|
||||
return UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Struct);
|
||||
if (const UClass* Class = Cast<UClass>(Obj))
|
||||
return UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Class);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UWingTypes::IsBlueprintType(const FEdGraphPinType &Type)
|
||||
{
|
||||
if ((Type.PinCategory == UEdGraphSchema_K2::PC_Delegate) ||
|
||||
(Type.PinCategory == UEdGraphSchema_K2::PC_MCDelegate) ||
|
||||
(Type.PinValueType.TerminalCategory == UEdGraphSchema_K2::PC_Delegate) ||
|
||||
(Type.PinValueType.TerminalCategory == UEdGraphSchema_K2::PC_MCDelegate))
|
||||
return false;
|
||||
|
||||
if ((Type.PinSubCategoryObject != nullptr) &&
|
||||
!IsBlueprintType(Type.PinSubCategoryObject.Get()))
|
||||
return false;
|
||||
|
||||
if ((Type.PinValueType.TerminalSubCategoryObject != nullptr) &&
|
||||
!IsBlueprintType(Type.PinValueType.TerminalSubCategoryObject.Get()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UWingTypes::IsChildOf(const FEdGraphPinType &Type, UClass *Parent)
|
||||
{
|
||||
if (Type.IsContainer()) return false;
|
||||
|
||||
if ((Type.PinCategory != UEdGraphSchema_K2::PC_Object) &&
|
||||
(Type.PinCategory != UEdGraphSchema_K2::PC_Interface))
|
||||
return false;
|
||||
|
||||
UClass *Class = Cast<UClass>(Type.PinSubCategoryObject.Get());
|
||||
if (!Class) return false;
|
||||
|
||||
return Class->IsChildOf(Parent);
|
||||
}
|
||||
|
||||
bool UWingTypes::IsBlueprintable(const FEdGraphPinType &Type)
|
||||
{
|
||||
if (Type.IsContainer()) return false;
|
||||
|
||||
if ((Type.PinCategory != UEdGraphSchema_K2::PC_Object) &&
|
||||
(Type.PinCategory != UEdGraphSchema_K2::PC_Interface))
|
||||
return false;
|
||||
|
||||
UClass *Class = Cast<UClass>(Type.PinSubCategoryObject.Get());
|
||||
if (!Class) return false;
|
||||
|
||||
return FKismetEditorUtilities::CanCreateBlueprintOfClass(Class);
|
||||
}
|
||||
|
||||
bool UWingTypes::IsBlueprintType(const Info &TypeInfo)
|
||||
{
|
||||
// Blocked types (delegates).
|
||||
if (TypeInfo.PinCategory.IsNone()) return false;
|
||||
|
||||
// User-defined types are always valid blueprint variable types.
|
||||
if (TypeInfo.IsUserDefined) return true;
|
||||
|
||||
// Primitives have no PinSubCategoryObject.
|
||||
if (TypeInfo.PinSubCategoryObject.IsEmpty()) return true;
|
||||
|
||||
// Load and check.
|
||||
UObject* Obj = LoadObject<UObject>(nullptr, *TypeInfo.PinSubCategoryObject);
|
||||
if (!Obj) return false;
|
||||
return IsBlueprintType(Obj);
|
||||
}
|
||||
|
||||
bool UWingTypes::IsBlueprintable(const Info &TypeInfo)
|
||||
{
|
||||
// Only classes can be blueprintable.
|
||||
if (TypeInfo.PinCategory != UEdGraphSchema_K2::PC_Object &&
|
||||
TypeInfo.PinCategory != UEdGraphSchema_K2::PC_Interface)
|
||||
return false;
|
||||
|
||||
// User-defined classes are already blueprints, so yes.
|
||||
if (TypeInfo.IsUserDefined) return true;
|
||||
|
||||
// Load and check.
|
||||
UClass* Class = LoadObject<UClass>(nullptr, *TypeInfo.PinSubCategoryObject);
|
||||
if (!Class) return false;
|
||||
return FKismetEditorUtilities::CanCreateBlueprintOfClass(Class);
|
||||
}
|
||||
|
||||
const UClass *UWingTypes::FindNativeParent(const UClass *Obj)
|
||||
{
|
||||
const UClass* Native = Obj;
|
||||
while (Native && Native->ClassGeneratedBy != nullptr)
|
||||
Native = Native->GetSuperClass();
|
||||
return Native;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Choose Short Name
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
FString UWingTypes::GetProposedName(const UObject *Obj)
|
||||
void UWingTypes::ReserveShortName(FName Name, FName PinCategory, FName PinSubCategory)
|
||||
{
|
||||
FString Name = Obj->GetName();
|
||||
if (Name.EndsWith(TEXT("_C")))
|
||||
{
|
||||
if (const UClass* Class = Cast<UClass>(Obj))
|
||||
{
|
||||
if (Class->ClassGeneratedBy != nullptr)
|
||||
Name.LeftChopInline(2);
|
||||
}
|
||||
}
|
||||
return WingUtils::SanitizeName(Name);
|
||||
FString NameStr = Name.ToString();
|
||||
Info Dummy;
|
||||
Dummy.Short = NameStr;
|
||||
Dummy.PinCategory = PinCategory;
|
||||
Dummy.PinSubCategory = PinSubCategory;
|
||||
Dummy.PinSubCategoryObject.Empty();
|
||||
Dummy.NativeParent = nullptr;
|
||||
Dummy.IsUserDefined = false;
|
||||
ShortToInfo.Add(NameStr.ToLower(), MoveTemp(Dummy));
|
||||
}
|
||||
|
||||
void UWingTypes::ReserveShortName(FName Name)
|
||||
{
|
||||
FString NameStr = Name.ToString();
|
||||
ShortToPath.Add(NameStr.ToLower(), FString(TEXT("PRIMITIVE")));
|
||||
ReserveShortName(Name, Name, FName());
|
||||
}
|
||||
|
||||
FString UWingTypes::ChooseShortName(const FString &Proposal, const FString &FullObjectPath)
|
||||
FString UWingTypes::GetShortName(const FString &Path)
|
||||
{
|
||||
// You must call this with an object path, not an asset path.
|
||||
check(FullObjectPath.Contains(TEXT(".")));
|
||||
FString *Result = PathToShort.Find(Path);
|
||||
if (Result) return *Result;
|
||||
return FString();
|
||||
}
|
||||
|
||||
// Look to see if we've already assigned a short name to this path.
|
||||
FString *OldShort = PathToShort.Find(FullObjectPath);
|
||||
if (OldShort != nullptr) return *OldShort;
|
||||
FString UWingTypes::NewShortName(const FString &Path, FName PinCategory, const UClass *NativeParent, bool IsUserDefined)
|
||||
{
|
||||
// Verify that the path is not already associated.
|
||||
check(!PathToShort.Find(Path));
|
||||
|
||||
// Derive the name proposal from the path.
|
||||
int32 DotIndex;
|
||||
check(Path.FindLastChar('.', DotIndex));
|
||||
FString Proposal = Path.Mid(DotIndex + 1);
|
||||
if ((PinCategory == UEdGraphSchema_K2::PC_Object || PinCategory == UEdGraphSchema_K2::PC_Interface)
|
||||
&& IsUserDefined && Proposal.EndsWith(TEXT("_C")))
|
||||
Proposal.LeftChopInline(2);
|
||||
Proposal = WingUtils::SanitizeName(Proposal);
|
||||
check(!Proposal.IsEmpty());
|
||||
|
||||
// Construct the Info struct.
|
||||
Info TypeInfo;
|
||||
TypeInfo.PinCategory = PinCategory;
|
||||
TypeInfo.PinSubCategoryObject = Path;
|
||||
TypeInfo.NativeParent = NativeParent;
|
||||
TypeInfo.IsUserDefined = IsUserDefined;
|
||||
|
||||
// Check if the proposed name is available.
|
||||
FString Lower = Proposal.ToLower();
|
||||
if (!ShortToPath.Contains(Lower))
|
||||
if (!ShortToInfo.Contains(Lower))
|
||||
{
|
||||
ShortToPath.Add(Lower, FullObjectPath);
|
||||
PathToShort.Add(FullObjectPath, Proposal);
|
||||
TypeInfo.Short = Proposal;
|
||||
PathToShort.Add(Path, Proposal);
|
||||
ShortToInfo.Add(Lower, TypeInfo);
|
||||
return Proposal;
|
||||
}
|
||||
|
||||
@@ -56,11 +192,12 @@ FString UWingTypes::ChooseShortName(const FString &Proposal, const FString &Full
|
||||
for (int32 i = 2; ; ++i)
|
||||
{
|
||||
FString NumberedLower = FString::Printf(TEXT("%s%d"), *Lower, i);
|
||||
if (!ShortToPath.Contains(NumberedLower))
|
||||
if (!ShortToInfo.Contains(NumberedLower))
|
||||
{
|
||||
FString NumberedProposal = FString::Printf(TEXT("%s_%d"), *Proposal, i);
|
||||
ShortToPath.Add(NumberedLower, FullObjectPath);
|
||||
PathToShort.Add(FullObjectPath, NumberedProposal);
|
||||
TypeInfo.Short = NumberedProposal;
|
||||
PathToShort.Add(Path, NumberedProposal);
|
||||
ShortToInfo.Add(NumberedLower, TypeInfo);
|
||||
return NumberedProposal;
|
||||
}
|
||||
}
|
||||
@@ -70,17 +207,88 @@ FString UWingTypes::ChooseShortName(const UObject* Obj)
|
||||
{
|
||||
// If it's a blueprint, register its generated class instead.
|
||||
if (const UBlueprint* BP = Cast<UBlueprint>(Obj))
|
||||
Obj = BP->GeneratedClass;
|
||||
|
||||
// Don't register null pointer.
|
||||
if (Obj == nullptr) return FString();
|
||||
|
||||
FString Path = Obj->GetPathName();
|
||||
FString Existing = GetShortName(Path);
|
||||
if (!Existing.IsEmpty()) return Existing;
|
||||
|
||||
if (Cast<UEnum>(Obj))
|
||||
{
|
||||
if (BP->GeneratedClass)
|
||||
return ChooseShortName(BP->GeneratedClass);
|
||||
return FString();
|
||||
return NewShortName(Path, UEdGraphSchema_K2::PC_Enum, nullptr, Cast<UUserDefinedEnum>(Obj) != nullptr);
|
||||
}
|
||||
|
||||
if (!Cast<UScriptStruct>(Obj) && !Cast<UClass>(Obj) && !Cast<UEnum>(Obj))
|
||||
return FString();
|
||||
if (Cast<UScriptStruct>(Obj))
|
||||
{
|
||||
return NewShortName(Path, UEdGraphSchema_K2::PC_Struct, nullptr, Cast<UUserDefinedStruct>(Obj) != nullptr);
|
||||
}
|
||||
|
||||
FString ProposedName = GetProposedName(Obj);
|
||||
return ChooseShortName(ProposedName, Obj->GetPathName());
|
||||
if (const UClass* Class = Cast<UClass>(Obj))
|
||||
{
|
||||
bool IsInterface = Class->IsChildOf(UInterface::StaticClass());
|
||||
bool IsUserDefined = (Class->ClassGeneratedBy != nullptr);
|
||||
FName PinCategory = IsInterface ? UEdGraphSchema_K2::PC_Interface : UEdGraphSchema_K2::PC_Object;
|
||||
return NewShortName(Path, PinCategory, FindNativeParent(Class), IsUserDefined);
|
||||
}
|
||||
|
||||
return FString();
|
||||
}
|
||||
|
||||
void UWingTypes::ChooseShortName(const FAssetData &Data)
|
||||
{
|
||||
FString AssetName = Data.AssetName.ToString();
|
||||
FString PackageName = Data.PackageName.ToString();
|
||||
FTopLevelAssetPath ClassPath = Data.AssetClassPath;
|
||||
|
||||
// Handle structs.
|
||||
if (ClassPath == UUserDefinedStruct::StaticClass()->GetClassPathName())
|
||||
{
|
||||
FString Path = FString::Printf(TEXT("%s.%s"), *PackageName, *AssetName);
|
||||
if (!GetShortName(Path).IsEmpty()) return;
|
||||
NewShortName(Path, UEdGraphSchema_K2::PC_Struct, nullptr, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle enums.
|
||||
if (ClassPath == UUserDefinedEnum::StaticClass()->GetClassPathName())
|
||||
{
|
||||
FString Path = FString::Printf(TEXT("%s.%s"), *PackageName, *AssetName);
|
||||
if (!GetShortName(Path).IsEmpty()) return;
|
||||
NewShortName(Path, UEdGraphSchema_K2::PC_Enum, nullptr, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle Blueprints. Get the generated class path.
|
||||
FString Path;
|
||||
if (!Data.GetTagValue(FBlueprintTags::GeneratedClassPath, Path)) return;
|
||||
Path = FPackageName::ExportTextPathToObjectPath(Path);
|
||||
if (!GetShortName(Path).IsEmpty()) return;
|
||||
|
||||
// Get the Native Parent.
|
||||
FString NativeParentPath;
|
||||
if (!Data.GetTagValue(FBlueprintTags::NativeParentClassPath, NativeParentPath)) return;
|
||||
NativeParentPath = FPackageName::ExportTextPathToObjectPath(NativeParentPath);
|
||||
UClass *NativeParent = FindObject<UClass>(nullptr, *NativeParentPath);
|
||||
if (NativeParent == nullptr) return;
|
||||
|
||||
// Get the blueprint type.
|
||||
FString BPType;
|
||||
if (!Data.GetTagValue(FBlueprintTags::BlueprintType, BPType)) return;
|
||||
|
||||
if (BPType == TEXT("BPTYPE_Interface"))
|
||||
{
|
||||
check(NativeParent->IsChildOf(UInterface::StaticClass()));
|
||||
NewShortName(Path, UEdGraphSchema_K2::PC_Interface, NativeParent, true);
|
||||
return;
|
||||
}
|
||||
else if (BPType == TEXT("BPTYPE_Normal") || BPType == TEXT("BPTYPE_Const"))
|
||||
{
|
||||
NewShortName(Path, UEdGraphSchema_K2::PC_Object, NativeParent, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void UWingTypes::ChooseShortNames(UPackage* Package)
|
||||
@@ -93,31 +301,11 @@ void UWingTypes::ChooseShortNames(UPackage* Package)
|
||||
}, false);
|
||||
}
|
||||
|
||||
void UWingTypes::ChooseShortName(const FAssetData &Data)
|
||||
{
|
||||
FString AssetName = Data.AssetName.ToString();
|
||||
FString PackageName = Data.PackageName.ToString();
|
||||
|
||||
FTopLevelAssetPath ClassPath = Data.AssetClassPath;
|
||||
if (ClassPath == UBlueprint::StaticClass()->GetClassPathName())
|
||||
{
|
||||
// Blueprint: the generated class is AssetName_C
|
||||
FString ObjectPath = FString::Printf(TEXT("%s.%s_C"), *PackageName, *AssetName);
|
||||
ChooseShortName(WingUtils::SanitizeName(AssetName), ObjectPath);
|
||||
}
|
||||
else if (ClassPath == UUserDefinedStruct::StaticClass()->GetClassPathName() ||
|
||||
ClassPath == UUserDefinedEnum::StaticClass()->GetClassPathName())
|
||||
{
|
||||
FString ObjectPath = FString::Printf(TEXT("%s.%s"), *PackageName, *AssetName);
|
||||
ChooseShortName(WingUtils::SanitizeName(AssetName), ObjectPath);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// TypeToText
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
FString UWingTypes::TypeToTextInner(FName Category, FName SubCategory, UObject* SubCategoryObject)
|
||||
FString UWingTypes::TypeToText(FName Category, FName SubCategory, UObject* SubCategoryObject)
|
||||
{
|
||||
if ((Category == UEdGraphSchema_K2::PC_Delegate) ||
|
||||
(Category == UEdGraphSchema_K2::PC_MCDelegate) ||
|
||||
@@ -177,7 +365,7 @@ FString UWingTypes::TypeToText(const FEdGraphPinType& PinType)
|
||||
UWingTypes* Types = GEditor->GetEditorSubsystem<UWingTypes>();
|
||||
if (!Types) return FString();
|
||||
|
||||
FString Inner = Types->TypeToTextInner(PinType.PinCategory, PinType.PinSubCategory, PinType.PinSubCategoryObject.Get());
|
||||
FString Inner = Types->TypeToText(PinType.PinCategory, PinType.PinSubCategory, PinType.PinSubCategoryObject.Get());
|
||||
if (Inner.IsEmpty())
|
||||
return FString();
|
||||
|
||||
@@ -187,7 +375,7 @@ FString UWingTypes::TypeToText(const FEdGraphPinType& PinType)
|
||||
return FString::Printf(TEXT("Set<%s>"), *Inner);
|
||||
if (PinType.IsMap())
|
||||
{
|
||||
FString ValueInner = Types->TypeToTextInner(
|
||||
FString ValueInner = Types->TypeToText(
|
||||
PinType.PinValueType.TerminalCategory,
|
||||
PinType.PinValueType.TerminalSubCategory,
|
||||
PinType.PinValueType.TerminalSubCategoryObject.Get());
|
||||
@@ -229,66 +417,7 @@ FString UWingTypes::TypeToTextOrDie(const UObject* Obj)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Subsystem lifecycle
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void UWingTypes::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
Super::Initialize(Collection);
|
||||
|
||||
// Collect all packages and sort by name for stable short-name assignment.
|
||||
TArray<UPackage*> Packages;
|
||||
for (TObjectIterator<UPackage> It; It; ++It)
|
||||
Packages.Add(*It);
|
||||
Packages.Sort([](const UPackage& A, const UPackage& B) { return A.GetName() < B.GetName(); });
|
||||
|
||||
// Reserve the short names of the primitives.
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_Delegate);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_MCDelegate);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_Boolean);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_Int);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_Int64);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_Float);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_Double);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_Byte);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_Name);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_String);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_Text);
|
||||
|
||||
// Scan priority packages first, then everything else in sorted order.
|
||||
ChooseShortNames(FindPackage(nullptr, TEXT("/Script/CoreUObject")));
|
||||
ChooseShortNames(FindPackage(nullptr, TEXT("/Script/Engine")));
|
||||
ChooseShortNames(FindPackage(nullptr, TEXT("/Script/Integration")));
|
||||
|
||||
// Choose short names for everything else.
|
||||
for (UPackage* Pkg : Packages)
|
||||
ChooseShortNames(Pkg);
|
||||
|
||||
// Scan the asset registry for unloaded assets.
|
||||
IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
|
||||
TArray<FAssetData> AllAssets;
|
||||
AssetRegistry.GetAllAssets(AllAssets, true);
|
||||
for (const FAssetData& Data : AllAssets)
|
||||
ChooseShortName(Data);
|
||||
|
||||
// Register for future asset discoveries.
|
||||
AssetRegistry.OnAssetAdded().AddUObject(this, &UWingTypes::ChooseShortName);
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("WingTypes: Registered %d types"), ShortToPath.Num());
|
||||
}
|
||||
|
||||
void UWingTypes::Deinitialize()
|
||||
{
|
||||
if (FModuleManager::Get().IsModuleLoaded(TEXT("AssetRegistry")))
|
||||
{
|
||||
IAssetRegistry& AssetRegistry = FModuleManager::GetModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
|
||||
AssetRegistry.OnAssetAdded().RemoveAll(this);
|
||||
}
|
||||
Super::Deinitialize();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tokenizer
|
||||
// Tokenizer, Parser, and Resolve Short Name
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void UWingTypes::Tokenize(const FString& Input)
|
||||
@@ -325,69 +454,6 @@ void UWingTypes::Tokenize(const FString& Input)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Short Name Resolution
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
bool UWingTypes::ResolveShortName(const FString &Name, FEdGraphPinType &OutType)
|
||||
{
|
||||
FString *Path = ShortToPath.Find(Name.ToLower());
|
||||
if (!Path)
|
||||
{
|
||||
Error = FString::Printf(TEXT("Unrecognized type '%s'"), *Name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Primitives (int, float, etc.) are registered with the path "PRIMITIVE".
|
||||
if (*Path == TEXT("PRIMITIVE"))
|
||||
{
|
||||
OutType.PinCategory = FName(*Name);
|
||||
if ((OutType.PinCategory == UEdGraphSchema_K2::PC_Double) ||
|
||||
(OutType.PinCategory == UEdGraphSchema_K2::PC_Float))
|
||||
{
|
||||
OutType.PinSubCategory = OutType.PinCategory;
|
||||
OutType.PinCategory = UEdGraphSchema_K2::PC_Real;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load the object.
|
||||
UObject* Obj = LoadObject<UObject>(nullptr, **Path);
|
||||
if (!Obj)
|
||||
{
|
||||
Error = FString::Printf(TEXT("Failed to load type '%s' at path '%s'"), *Name, **Path);
|
||||
return false;
|
||||
}
|
||||
|
||||
// The short name registry only contains UClass, UScriptStruct, and UEnum.
|
||||
checkf(Cast<UClass>(Obj) || Cast<UScriptStruct>(Obj) || Cast<UEnum>(Obj),
|
||||
TEXT("Short name '%s' resolved to unexpected type '%s'"), *Name, *Obj->GetClass()->GetName());
|
||||
|
||||
// Determine the category from the object type.
|
||||
if (Cast<UScriptStruct>(Obj))
|
||||
{
|
||||
OutType.PinCategory = UEdGraphSchema_K2::PC_Struct;
|
||||
}
|
||||
else if (UClass* Class = Cast<UClass>(Obj))
|
||||
{
|
||||
if (Class->IsChildOf(UInterface::StaticClass()))
|
||||
OutType.PinCategory = UEdGraphSchema_K2::PC_Interface;
|
||||
else
|
||||
OutType.PinCategory = UEdGraphSchema_K2::PC_Object;
|
||||
}
|
||||
else
|
||||
{
|
||||
OutType.PinCategory = UEdGraphSchema_K2::PC_Byte;
|
||||
}
|
||||
|
||||
OutType.PinSubCategoryObject = Obj;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Parsing Types
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
bool UWingTypes::TokenIs(const TCHAR* Text) const
|
||||
{
|
||||
if (Cursor >= Tokens.Num()) return false;
|
||||
@@ -452,9 +518,9 @@ bool UWingTypes::ParseWrapped(FName Wrapper, FEdGraphPinType& OutType)
|
||||
Cursor++;
|
||||
if (!ParseChar('<')) return false;
|
||||
if (!ParsePlainIdentifier(OutType)) return false;
|
||||
if (!Cast<UClass>(OutType.PinSubCategoryObject))
|
||||
if (OutType.PinCategory != UEdGraphSchema_K2::PC_Object)
|
||||
{
|
||||
Error = FString::Printf(TEXT("%s is not a Class"), *OutType.PinSubCategoryObject->GetName());
|
||||
Error = FString::Printf(TEXT("%s is not an object type"), *OutType.PinSubCategoryObject->GetName());
|
||||
return false;
|
||||
}
|
||||
if (!ParseChar('>')) return false;
|
||||
@@ -528,43 +594,154 @@ bool UWingTypes::ParseType(FEdGraphPinType& OutType)
|
||||
return true;
|
||||
}
|
||||
|
||||
FString UWingTypes::TryTextToType(const FString& Text, FEdGraphPinType& OutPinType)
|
||||
bool UWingTypes::ResolveShortName(const FString &Name, FEdGraphPinType &OutType)
|
||||
{
|
||||
Info* TypeInfo = ShortToInfo.Find(Name.ToLower());
|
||||
if (!TypeInfo)
|
||||
{
|
||||
Error = FString::Printf(TEXT("Unrecognized type '%s'"), *Name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle primitives.
|
||||
if (TypeInfo->PinSubCategoryObject.IsEmpty())
|
||||
{
|
||||
OutType.PinCategory = TypeInfo->PinCategory;
|
||||
OutType.PinSubCategory = TypeInfo->PinSubCategory;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load the PinSubCategoryObject.
|
||||
UObject* Obj = LoadObject<UObject>(nullptr, *TypeInfo->PinSubCategoryObject);
|
||||
if (!Obj)
|
||||
{
|
||||
Error = FString::Printf(TEXT("Failed to load type '%s' at path '%s'"), *Name, *TypeInfo->PinSubCategoryObject);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sanity check the result.
|
||||
if (UClass *Class = Cast<UClass>(Obj))
|
||||
{
|
||||
if (Class->IsChildOf(UInterface::StaticClass()))
|
||||
{
|
||||
check(TypeInfo->PinCategory == UEdGraphSchema_K2::PC_Interface);
|
||||
}
|
||||
else
|
||||
{
|
||||
check(TypeInfo->PinCategory == UEdGraphSchema_K2::PC_Object);
|
||||
}
|
||||
}
|
||||
else if (Cast<UScriptStruct>(Obj))
|
||||
{
|
||||
check(TypeInfo->PinCategory == UEdGraphSchema_K2::PC_Struct);
|
||||
}
|
||||
else if (Cast<UEnum>(Obj))
|
||||
{
|
||||
check(TypeInfo->PinCategory == UEdGraphSchema_K2::PC_Enum);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogTemp, Fatal, TEXT("PinSubCategoryObject '%s' doesn't belong in type registry"), *Obj->GetClass()->GetName());
|
||||
}
|
||||
|
||||
OutType.PinCategory = TypeInfo->PinCategory;
|
||||
OutType.PinSubCategoryObject = Obj;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Text to Type
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
bool UWingTypes::TextToType(const FString& Text, FEdGraphPinType& OutPinType, const Requirements &Require)
|
||||
{
|
||||
if (!Require.BlueprintType.IsSet() ||
|
||||
!Require.Blueprintable.IsSet() ||
|
||||
!Require.AllowContainer.IsSet())
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: TextToType called with underspecified Requirements list.\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
UWingTypes* Types = GEditor->GetEditorSubsystem<UWingTypes>();
|
||||
check(Types);
|
||||
Types->Error.Empty();
|
||||
Types->Tokenize(Text);
|
||||
OutPinType = FEdGraphPinType();
|
||||
if (!Types->ParseType(OutPinType)) return Types->Error;
|
||||
return FString();
|
||||
if (!Types->ParseType(OutPinType))
|
||||
{
|
||||
UWingServer::Printf(TEXT("%s\n"), *Types->Error);
|
||||
OutPinType = FEdGraphPinType(); return false;
|
||||
}
|
||||
|
||||
if (!Require.AllowContainer.GetValue())
|
||||
{
|
||||
if (OutPinType.IsContainer())
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Type %s is a container, not allowed here\n"), *Text);
|
||||
OutPinType = FEdGraphPinType(); return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Require.PinCategory.IsNone())
|
||||
{
|
||||
if (OutPinType.PinCategory != Require.PinCategory)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Need a type which is an %s, got a %s instead."),
|
||||
*Require.PinCategory.ToString(), *OutPinType.PinCategory.ToString());
|
||||
OutPinType = FEdGraphPinType(); return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Require.IsChildOf)
|
||||
{
|
||||
if (!IsChildOf(OutPinType, Require.IsChildOf))
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Type must derive from %s\n"), *WingUtils::FormatName(Require.IsChildOf));
|
||||
OutPinType = FEdGraphPinType(); return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Require.BlueprintType.GetValue())
|
||||
{
|
||||
if (!IsBlueprintType(OutPinType))
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Not a blueprint type: %s\n"), *Text);
|
||||
OutPinType = FEdGraphPinType(); return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Require.Blueprintable.GetValue())
|
||||
{
|
||||
if (!IsBlueprintable(OutPinType))
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Not a blueprintable type: %s\n"), *Text);
|
||||
OutPinType = FEdGraphPinType(); return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UWingTypes::TextToType(const FString& Text, FEdGraphPinType& OutPinType)
|
||||
{
|
||||
FString Error = TryTextToType(Text, OutPinType);
|
||||
if (Error.IsEmpty()) return true;
|
||||
UWingServer::Print(Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
UClass* UWingTypes::TextToOneObjectType(const FString& Text)
|
||||
UClass* UWingTypes::TextToOneObjectType(const FString& Text, const Requirements &Require)
|
||||
{
|
||||
FEdGraphPinType PinType;
|
||||
if (!TextToType(Text, PinType)) return nullptr;
|
||||
if (!TextToType(Text, PinType, Require)) return nullptr;
|
||||
UClass* Class = Cast<UClass>(PinType.PinSubCategoryObject.Get());
|
||||
if ((!Class) || (PinType.PinCategory != UEdGraphSchema_K2::PC_Object) ||
|
||||
(PinType.IsContainer()))
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: '%s' is not a plain object class\n"), *Text);
|
||||
UWingServer::Printf(TEXT("ERROR: '%s' is not an object class\n"), *Text);
|
||||
return nullptr;
|
||||
}
|
||||
return Class;
|
||||
}
|
||||
|
||||
UClass* UWingTypes::TextToOneInterfaceType(const FString& Text)
|
||||
UClass* UWingTypes::TextToOneInterfaceType(const FString& Text, const Requirements &Require)
|
||||
{
|
||||
FEdGraphPinType PinType;
|
||||
if (!TextToType(Text, PinType)) return nullptr;
|
||||
if (!TextToType(Text, PinType, Require)) return nullptr;
|
||||
UClass* Class = Cast<UClass>(PinType.PinSubCategoryObject.Get());
|
||||
if ((!Class) || (PinType.PinCategory != UEdGraphSchema_K2::PC_Interface) ||
|
||||
(PinType.IsContainer()))
|
||||
@@ -575,3 +752,60 @@ UClass* UWingTypes::TextToOneInterfaceType(const FString& Text)
|
||||
return Class;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Subsystem lifecycle
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void UWingTypes::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
Super::Initialize(Collection);
|
||||
|
||||
// Collect all packages and sort by name for stable short-name assignment.
|
||||
TArray<UPackage*> Packages;
|
||||
for (TObjectIterator<UPackage> It; It; ++It)
|
||||
Packages.Add(*It);
|
||||
Packages.Sort([](const UPackage& A, const UPackage& B) { return A.GetName() < B.GetName(); });
|
||||
|
||||
// Reserve the short names of the primitives.
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_Delegate);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_MCDelegate);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_Boolean);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_Int);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_Int64);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_Float, UEdGraphSchema_K2::PC_Real, UEdGraphSchema_K2::PC_Float);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_Double, UEdGraphSchema_K2::PC_Real, UEdGraphSchema_K2::PC_Double);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_Byte);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_Name);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_String);
|
||||
ReserveShortName(UEdGraphSchema_K2::PC_Text);
|
||||
|
||||
// Scan priority packages first, then everything else in sorted order.
|
||||
ChooseShortNames(FindPackage(nullptr, TEXT("/Script/CoreUObject")));
|
||||
ChooseShortNames(FindPackage(nullptr, TEXT("/Script/Engine")));
|
||||
ChooseShortNames(FindPackage(nullptr, TEXT("/Script/Integration")));
|
||||
|
||||
// Choose short names for everything else.
|
||||
for (UPackage* Pkg : Packages)
|
||||
ChooseShortNames(Pkg);
|
||||
|
||||
// Scan the asset registry for unloaded assets.
|
||||
IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
|
||||
TArray<FAssetData> AllAssets;
|
||||
AssetRegistry.GetAllAssets(AllAssets, true);
|
||||
for (const FAssetData& Data : AllAssets)
|
||||
ChooseShortName(Data);
|
||||
|
||||
// Register for future asset discoveries.
|
||||
AssetRegistry.OnAssetAdded().AddUObject(this, &UWingTypes::ChooseShortName);
|
||||
}
|
||||
|
||||
void UWingTypes::Deinitialize()
|
||||
{
|
||||
if (FModuleManager::Get().IsModuleLoaded(TEXT("AssetRegistry")))
|
||||
{
|
||||
IAssetRegistry& AssetRegistry = FModuleManager::GetModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
|
||||
AssetRegistry.OnAssetAdded().RemoveAll(this);
|
||||
}
|
||||
Super::Deinitialize();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,78 +15,106 @@ class UWingTypes : public UEditorSubsystem
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
virtual void Deinitialize() override;
|
||||
// ---------------------------------------------------------------------------
|
||||
// Simple Accessors
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Convert a pin type to a type string. Returns empty string
|
||||
// on failure.
|
||||
// This struct is used to record the information about a type
|
||||
struct Info
|
||||
{
|
||||
FString Short;
|
||||
FName PinCategory;
|
||||
FName PinSubCategory;
|
||||
FString PinSubCategoryObject;
|
||||
// The following are only set for interfaces and Objects.
|
||||
const UClass *NativeParent = nullptr;
|
||||
bool IsUserDefined = false;
|
||||
};
|
||||
|
||||
// Get the full short-name-to-info map.
|
||||
static const TMap<FString, Info>& GetAllTypes();
|
||||
|
||||
public:
|
||||
// ---------------------------------------------------------------------------
|
||||
// Simple Utility Functions.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// True if the UClass, UStruct, or UEnum is a BlueprintType
|
||||
static bool IsBlueprintType(const UObject *Obj);
|
||||
|
||||
// True if all parts of the pintype are blueprinttypes.
|
||||
static bool IsBlueprintType(const FEdGraphPinType &Type);
|
||||
|
||||
// True if the pintype is a blueprintable interface or object.
|
||||
static bool IsBlueprintable(const FEdGraphPinType &Type);
|
||||
|
||||
// True if the pintype is a child of the specified uclass.
|
||||
static bool IsChildOf(const FEdGraphPinType &Type, UClass *Class);
|
||||
|
||||
// True if the info struct represents a BlueprintType
|
||||
static bool IsBlueprintType(const Info &TypeInfo);
|
||||
|
||||
// True if the info struct represents a Blueprintable type.
|
||||
static bool IsBlueprintable(const Info &TypeInfo);
|
||||
|
||||
// Get the native parent of any uclass.
|
||||
const UClass *FindNativeParent(const UClass *Class);
|
||||
|
||||
private:
|
||||
// ---------------------------------------------------------------------------
|
||||
// Choose Short Name
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Reserve the short name for a primitive type.
|
||||
void ReserveShortName(FName Name, FName PinCategory, FName PinSubCategory);
|
||||
void ReserveShortName(FName Name);
|
||||
|
||||
// Get the short name if it already exists.
|
||||
FString GetShortName(const FString &Path);
|
||||
|
||||
// Core version: choose a short name for a path that doesn't already
|
||||
// have a short name. Records all the associated information.
|
||||
FString NewShortName(const FString &Path, FName PinCategory, const UClass *NativeParent, bool IsUserDefined);
|
||||
|
||||
// Choose a short name for an already-loaded UObject.
|
||||
FString ChooseShortName(const UObject* Obj);
|
||||
|
||||
// Choose a short name for an FAssetData.
|
||||
void ChooseShortName(const FAssetData &Data);
|
||||
|
||||
// Choose short names for every type in the package.
|
||||
void ChooseShortNames(UPackage* Package);
|
||||
|
||||
private:
|
||||
// ---------------------------------------------------------------------------
|
||||
// TypeToText
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Return a type string for a primitive or wrapped type (No Containers)
|
||||
FString TypeToText(FName Category, FName SubCategory, UObject* SubCategoryObject);
|
||||
|
||||
public:
|
||||
// Convert a pin type to a type string.
|
||||
// Returns empty string on failure.
|
||||
static FString TypeToText(const FEdGraphPinType& PinType);
|
||||
|
||||
// Convert a Property to a type string.
|
||||
// Returns empty string on failure.
|
||||
static FString TypeToText(const FProperty *Property);
|
||||
|
||||
// Get the type name for a UClass, UScriptStruct, or UEnum.
|
||||
// Returns empty string if the object is not one of those types.
|
||||
// Returns empty string on failure.
|
||||
static FString TypeToText(const UObject* Obj);
|
||||
|
||||
// Get the type name for a UClass, UScriptStruct, or UEnum.
|
||||
// Check fail if the result is empty.
|
||||
static FString TypeToTextOrDie(const UObject* Obj);
|
||||
|
||||
// Try to parse a type. If there's a problem, returns an error
|
||||
// message. If all goes well, returns empty string.
|
||||
static FString TryTextToType(const FString& Text, FEdGraphPinType& OutPinType);
|
||||
|
||||
// Try to parse a type. If there's a problem, prints an error
|
||||
// message and returns false.
|
||||
static bool TextToType(const FString& Text, FEdGraphPinType& OutPinType);
|
||||
|
||||
// Parse a type string and verify it's a single object class (PC_Object,
|
||||
// no container, no wrapper). Returns nullptr and prints error on failure.
|
||||
static UClass* TextToOneObjectType(const FString& Text);
|
||||
|
||||
// Parse a type string and verify it's a single interface class (PC_Interface,
|
||||
// no container, no wrapper). Returns nullptr and prints error on failure.
|
||||
static UClass* TextToOneInterfaceType(const FString& Text);
|
||||
|
||||
|
||||
private:
|
||||
FString TypeToTextInner(FName Category, FName SubCategory, UObject* SubCategoryObject);
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tokenizer, Parser, and ResolveShortName
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
bool ResolveShortName(const FString &Name, FEdGraphPinType &OutType);
|
||||
|
||||
// Get the object's name, not including the _C generated
|
||||
// by the blueprint compiler for generated classes.
|
||||
static FString GetProposedName(const UObject *Obj);
|
||||
|
||||
// Reserve the short name for a primitive type.
|
||||
// The value stored in the map is just "PRIMITIVE".
|
||||
void ReserveShortName(FName Name);
|
||||
|
||||
// Choose a short name for the type at the specified
|
||||
// full object path. Never reuses a short name, so every
|
||||
// type will have a unique name, at least as long as the
|
||||
// editor is up. The path must be the complete path of
|
||||
// an object, including the .FOO_C at the end. The name
|
||||
// chosen will either be the proposed name, or some
|
||||
// small variation of that.
|
||||
FString ChooseShortName(const FString &Proposed, const FString &FullObjectPath);
|
||||
|
||||
// Chooses a short name for the specified type. If the
|
||||
// object is not a class, struct, interface, or enum, returns
|
||||
// null string.
|
||||
FString ChooseShortName(const UObject* Obj);
|
||||
|
||||
// Choose short names for every type in the package.
|
||||
void ChooseShortNames(UPackage* Package);
|
||||
|
||||
// Choose a name for the asset's primary object, without
|
||||
// loading the object.
|
||||
void ChooseShortName(const FAssetData &Data);
|
||||
|
||||
// Short name registry: bidirectional mapping between short names and full paths.
|
||||
TMap<FString, FString> ShortToPath; // e.g. "vector" -> "/Script/CoreUObject.Vector"
|
||||
TMap<FString, FString> PathToShort; // e.g. "/Script/CoreUObject.Vector" -> "Vector"
|
||||
|
||||
// Tokenizer and parser (used by TextToType)
|
||||
void Tokenize(const FString& Input);
|
||||
bool TokenIs(const TCHAR* Text) const;
|
||||
bool TokenIs(const TCHAR* Text, TCHAR ch) const;
|
||||
@@ -102,6 +130,57 @@ private:
|
||||
bool ParseMap(FEdGraphPinType& OutType);
|
||||
bool ParseType(FEdGraphPinType& OutType);
|
||||
|
||||
bool ResolveShortName(const FString &Name, FEdGraphPinType &OutType);
|
||||
|
||||
public:
|
||||
// ---------------------------------------------------------------------------
|
||||
// TextToType
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
struct Requirements
|
||||
{
|
||||
TOptional<bool> BlueprintType;
|
||||
TOptional<bool> Blueprintable;
|
||||
TOptional<bool> AllowContainer;
|
||||
UClass *IsChildOf = nullptr;
|
||||
FName PinCategory = FName();
|
||||
};
|
||||
|
||||
|
||||
// Parse a type. If it doesn't parse, or if the type doesn't satisfy the
|
||||
// requirements, prints an error and returns false.
|
||||
static bool TextToType(const FString& Text, FEdGraphPinType& OutPinType, const Requirements &Require);
|
||||
|
||||
// Parse a type. If it doesn't parse, or if the type doesn't satisfy the
|
||||
// requirements, prints an error and returns false. Requires that the type
|
||||
// is an object type (even if that's not specified in the requirements struct).
|
||||
static UClass* TextToOneObjectType(const FString& Text, const Requirements &Require);
|
||||
|
||||
// Parse a type. If it doesn't parse, or if the type doesn't satisfy the
|
||||
// requirements, prints an error and returns false. Requires that the type
|
||||
// is an interface type (even if that's not specified in the requirements struct).
|
||||
static UClass* TextToOneInterfaceType(const FString& Text, const Requirements &Require);
|
||||
|
||||
private:
|
||||
// ---------------------------------------------------------------------------
|
||||
// Subsystem lifecycle
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
virtual void Deinitialize() override;
|
||||
|
||||
private:
|
||||
// ---------------------------------------------------------------------------
|
||||
// State
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Given a short name, get all the information about that type.
|
||||
TMap<FString, Info> ShortToInfo;
|
||||
|
||||
// Given a class path, return the short name, e.g. "/Script/CoreUObject.Vector" -> "Vector"
|
||||
TMap<FString, FString> PathToShort;
|
||||
|
||||
// These fields are only used during the parsing of a type.
|
||||
TArray<FString> Tokens;
|
||||
int32 Cursor = 0;
|
||||
FString Error;
|
||||
|
||||
Reference in New Issue
Block a user