Lots of refactoring
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingHandler.h"
|
#include "WingHandler.h"
|
||||||
#include "WingFactories.h"
|
#include "WingFactories.h"
|
||||||
#include "WingPropHandle.h"
|
#include "WingProperty.h"
|
||||||
#include "WingTypes.h"
|
#include "WingTypes.h"
|
||||||
#include "Factories/BlueprintFactory.h"
|
#include "Factories/BlueprintFactory.h"
|
||||||
#include "Factories/BlueprintMacroFactory.h"
|
#include "Factories/BlueprintMacroFactory.h"
|
||||||
@@ -80,18 +80,12 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the 'ParentClass' property.
|
// Set the 'ParentClass' property.
|
||||||
WingPropHandle Props;
|
TArray<FWingProperty> Props = FWingProperty::GetAll(Factory, CPF_Edit);
|
||||||
TSharedPtr<IPropertyHandle> PCProp = Props.NamedProperty(Factory, TEXT("ParentClass"), true, WingOut::Stdout);
|
FWingProperty *Prop = WingUtils::FindOneWithInternalID(
|
||||||
if (!PCProp) return;
|
TEXT("ParentClass"), Props, TEXT("property"), WingOut::Stdout);
|
||||||
|
if (!Prop) return;
|
||||||
// Store the parent class.
|
if (!Prop->SetObject(ParentClassObj, WingOut::Stdout)) return;
|
||||||
FPropertyAccess::Result SetResult = PCProp->SetValue(ParentClassObj);
|
|
||||||
if (SetResult != FPropertyAccess::Result::Success)
|
|
||||||
{
|
|
||||||
WingOut::Stdout.Printf(TEXT("ERROR: property does not allow value: %s\n"), *ParentClass);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the asset using the factory.
|
// Create the asset using the factory.
|
||||||
UObject *Blueprint = WingFactories::CreateAsset(Path, Factory, WingOut::Stdout);
|
UObject *Blueprint = WingFactories::CreateAsset(Path, Factory, WingOut::Stdout);
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingHandler.h"
|
#include "WingHandler.h"
|
||||||
#include "WingFactories.h"
|
#include "WingFactories.h"
|
||||||
#include "WingPropHandle.h"
|
|
||||||
#include "Factories/BlueprintFunctionLibraryFactory.h"
|
#include "Factories/BlueprintFunctionLibraryFactory.h"
|
||||||
#include "Create_UsingFactory.generated.h"
|
#include "Create_UsingFactory.generated.h"
|
||||||
|
|
||||||
@@ -22,17 +21,10 @@ public:
|
|||||||
{
|
{
|
||||||
TArray<UClass*> FactoryClasses;
|
TArray<UClass*> FactoryClasses;
|
||||||
GetDerivedClasses(UFactory::StaticClass(), FactoryClasses);
|
GetDerivedClasses(UFactory::StaticClass(), FactoryClasses);
|
||||||
|
|
||||||
for (UClass* Class : FactoryClasses)
|
for (UClass* Class : FactoryClasses)
|
||||||
{
|
{
|
||||||
if (Class->HasAnyClassFlags(CLASS_Abstract)) continue;
|
if (!WingFactories::CanCreate(Class)) continue;
|
||||||
|
if (WingFactories::GetParameterNames(Class).Num() > 0) continue;
|
||||||
UFactory* CDO = Class->GetDefaultObject<UFactory>();
|
|
||||||
if (!CDO->CanCreateNew() || !CDO->ShouldShowInNewMenu()) continue;
|
|
||||||
|
|
||||||
WingPropHandle Props;
|
|
||||||
TArray<TSharedPtr<IPropertyHandle>> ConfigProps = Props.AllProperties(CDO, true);
|
|
||||||
if (ConfigProps.Num() > 0) continue;
|
|
||||||
|
|
||||||
FString FactoryName = WingFactories::DeriveFactoryName(Class);
|
FString FactoryName = WingFactories::DeriveFactoryName(Class);
|
||||||
FString CommandName = FString::Printf(TEXT("Create_%s"), *FactoryName);
|
FString CommandName = FString::Printf(TEXT("Create_%s"), *FactoryName);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include "WingFetcher.h"
|
#include "WingFetcher.h"
|
||||||
#include "WingProperty.h"
|
#include "WingProperty.h"
|
||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
#include "WingTypes.h"
|
|
||||||
#include "Details_Dump.generated.h"
|
#include "Details_Dump.generated.h"
|
||||||
|
|
||||||
UCLASS()
|
UCLASS()
|
||||||
@@ -45,14 +44,7 @@ public:
|
|||||||
{
|
{
|
||||||
WingOut::Stdout.Printf(TEXT("\n%s:\n"), *Pair.Key);
|
WingOut::Stdout.Printf(TEXT("\n%s:\n"), *Pair.Key);
|
||||||
for (const FWingProperty& P : Pair.Value)
|
for (const FWingProperty& P : Pair.Value)
|
||||||
{
|
P.Print(WingOut::Stdout);
|
||||||
bool bEditable = !P->HasAnyPropertyFlags(CPF_EditConst);
|
|
||||||
WingOut::Stdout.Printf(TEXT(" %s %s %s = %s\n"),
|
|
||||||
bEditable ? TEXT("editable") : TEXT("readonly"),
|
|
||||||
*UWingTypes::TypeToText(P.Prop),
|
|
||||||
*WingUtils::FormatName(P.Prop),
|
|
||||||
*P.GetTruncatedText(100));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,6 +28,6 @@ public:
|
|||||||
}
|
}
|
||||||
virtual void Handle() override
|
virtual void Handle() override
|
||||||
{
|
{
|
||||||
WingManual::Commands(EWingHandlerKind::Normal, Query, Verbose);
|
WingManual::Commands(EWingHandlerKind::Create, Query, Verbose);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingHandler.h"
|
#include "WingHandler.h"
|
||||||
#include "WingProperty.h"
|
|
||||||
#include "WingFactories.h"
|
#include "WingFactories.h"
|
||||||
#include "SysInfo_Factories.generated.h"
|
#include "SysInfo_Factories.generated.h"
|
||||||
|
|
||||||
@@ -28,30 +27,45 @@ public:
|
|||||||
|
|
||||||
virtual void Handle() override
|
virtual void Handle() override
|
||||||
{
|
{
|
||||||
|
// Build a set of factory classes that are already supported by Create commands.
|
||||||
|
TSet<UClass*> SupportedFactories;
|
||||||
|
for (const FWingHandlerConfig& H : UWingServer::AllHandlers())
|
||||||
|
{
|
||||||
|
if (H.Kind != EWingHandlerKind::Create) continue;
|
||||||
|
UClass* FactoryClass = Cast<UClass>(H.Config.Get());
|
||||||
|
if (FactoryClass && FactoryClass->IsChildOf(UFactory::StaticClass()))
|
||||||
|
SupportedFactories.Add(FactoryClass);
|
||||||
|
}
|
||||||
|
|
||||||
TArray<UClass*> FactoryClasses;
|
TArray<UClass*> FactoryClasses;
|
||||||
GetDerivedClasses(UFactory::StaticClass(), FactoryClasses);
|
GetDerivedClasses(UFactory::StaticClass(), FactoryClasses);
|
||||||
|
|
||||||
TArray<FString> Results;
|
TArray<FString> Supported;
|
||||||
|
TArray<FString> Unsupported;
|
||||||
|
|
||||||
for (UClass *Factory : FactoryClasses)
|
for (UClass *Factory : FactoryClasses)
|
||||||
{
|
{
|
||||||
if (Factory->HasAnyClassFlags(CLASS_Abstract)) continue;
|
if (!WingFactories::CanCreate(Factory)) continue;
|
||||||
UFactory *CDO = Factory->GetDefaultObject<UFactory>();
|
TArray<FName> Params = WingFactories::GetParameterNames(Factory);
|
||||||
if (!CDO->CanCreateNew()) continue;
|
|
||||||
if (!CDO->ShouldShowInNewMenu()) continue;
|
|
||||||
TArray<FName> Params = FWingProperty::GetNames(Factory, CPF_Edit);
|
|
||||||
TStringBuilder<512> Line;
|
TStringBuilder<512> Line;
|
||||||
Line.Appendf(TEXT("%2d %s "), Params.Num(), *Factory->GetName());
|
Line.Appendf(TEXT("%2d %s "), Params.Num(), *Factory->GetName());
|
||||||
for (const FName& Prop : Params)
|
for (const FName& Prop : Params)
|
||||||
{
|
|
||||||
Line.Appendf(TEXT(" %s"), *Prop.ToString());
|
Line.Appendf(TEXT(" %s"), *Prop.ToString());
|
||||||
}
|
if (SupportedFactories.Contains(Factory))
|
||||||
Results.Add(Line.ToString());
|
Supported.Add(Line.ToString());
|
||||||
|
else
|
||||||
|
Unsupported.Add(Line.ToString());
|
||||||
}
|
}
|
||||||
Results.Sort();
|
|
||||||
for (const FString &Line : Results)
|
Supported.Sort();
|
||||||
{
|
Unsupported.Sort();
|
||||||
|
|
||||||
|
WingOut::Stdout.Print(TEXT("SUPPORTED:\n"));
|
||||||
|
for (const FString &Line : Supported)
|
||||||
|
WingOut::Stdout.Printf(TEXT("%s\n"), *Line);
|
||||||
|
|
||||||
|
WingOut::Stdout.Print(TEXT("\nUNSUPPORTED:\n"));
|
||||||
|
for (const FString &Line : Unsupported)
|
||||||
WingOut::Stdout.Printf(TEXT("%s\n"), *Line);
|
WingOut::Stdout.Printf(TEXT("%s\n"), *Line);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
#include "PackageTools.h"
|
#include "PackageTools.h"
|
||||||
|
#include "WingProperty.h"
|
||||||
#include "AssetRegistry/AssetRegistryModule.h"
|
#include "AssetRegistry/AssetRegistryModule.h"
|
||||||
#include "Kismet2/EnumEditorUtils.h"
|
#include "Kismet2/EnumEditorUtils.h"
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ bool WingFactories::CheckNewAssetPath(const FString& Path, WingOut Errors)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WingFactories::IsBlacklisted(UClass* FactoryClass)
|
bool WingFactories::IsBlacklisted(TSubclassOf<UFactory> FactoryClass)
|
||||||
{
|
{
|
||||||
FName Name = FactoryClass->GetFName();
|
FName Name = FactoryClass->GetFName();
|
||||||
if (Name == TEXT("PhysicsAssetFactory")) return true; // Pops a modal dialog
|
if (Name == TEXT("PhysicsAssetFactory")) return true; // Pops a modal dialog
|
||||||
@@ -38,6 +39,20 @@ bool WingFactories::IsBlacklisted(UClass* FactoryClass)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WingFactories::CanCreate(TSubclassOf<UFactory> FactoryClass)
|
||||||
|
{
|
||||||
|
if (FactoryClass == nullptr) return false;
|
||||||
|
if (FactoryClass->HasAnyClassFlags(CLASS_Abstract)) return false;
|
||||||
|
if (IsBlacklisted(FactoryClass)) return false;
|
||||||
|
UFactory* CDO = FactoryClass->GetDefaultObject<UFactory>();
|
||||||
|
return CDO->CanCreateNew() && CDO->ShouldShowInNewMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<FName> WingFactories::GetParameterNames(TSubclassOf<UFactory> FactoryClass)
|
||||||
|
{
|
||||||
|
return FWingProperty::GetNames(FactoryClass, CPF_Edit);
|
||||||
|
}
|
||||||
|
|
||||||
UObject* WingFactories::CreateAsset(const FString& Path, UFactory* Factory, WingOut Errors)
|
UObject* WingFactories::CreateAsset(const FString& Path, UFactory* Factory, WingOut Errors)
|
||||||
{
|
{
|
||||||
// Check the blacklist.
|
// Check the blacklist.
|
||||||
@@ -90,7 +105,7 @@ UObject* WingFactories::CreateAsset(const FString& Path, UFactory* Factory, Wing
|
|||||||
return NewAsset;
|
return NewAsset;
|
||||||
}
|
}
|
||||||
|
|
||||||
FString WingFactories::DeriveFactoryName(UClass* FactoryClass)
|
FString WingFactories::DeriveFactoryName(TSubclassOf<UFactory> FactoryClass)
|
||||||
{
|
{
|
||||||
FString Name = FactoryClass->GetName();
|
FString Name = FactoryClass->GetName();
|
||||||
int32 Index = Name.Find(TEXT("Factory"));
|
int32 Index = Name.Find(TEXT("Factory"));
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ void WingGraphExport::EmitNode(UEdGraphNode* Node)
|
|||||||
Output.Appendf(TEXT("\nnode %s: %s\n"), *WingUtils::FormatName(Node), *WingUtils::FormatNodeTitle(Node));
|
Output.Appendf(TEXT("\nnode %s: %s\n"), *WingUtils::FormatName(Node), *WingUtils::FormatNodeTitle(Node));
|
||||||
|
|
||||||
// Emit node properties (if applicable).
|
// Emit node properties (if applicable).
|
||||||
EmitNodeProperties(Node, Output, true);
|
EmitNodeProperties(Node, &Output, true);
|
||||||
|
|
||||||
// Emit input data pins.
|
// Emit input data pins.
|
||||||
for (UEdGraphPin* Pin : FilterPins(Node, EGPD_Input))
|
for (UEdGraphPin* Pin : FilterPins(Node, EGPD_Input))
|
||||||
@@ -248,26 +248,25 @@ void WingGraphExport::EmitNode(UEdGraphNode* Node)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WingGraphExport::EmitNodeProperties(UEdGraphNode* Node, FStringBuilderBase& Out, bool bPrimary)
|
void WingGraphExport::EmitNodeProperties(UEdGraphNode* Node, WingOut Out, bool bPrimary)
|
||||||
{
|
{
|
||||||
|
TArray<FWingProperty> Props = FWingProperty::GetDetails(Node, CPF_Edit, false);
|
||||||
|
|
||||||
FString PrimaryCategory;
|
FString PrimaryCategory;
|
||||||
if (UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(Node))
|
if (UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(Node))
|
||||||
{
|
{
|
||||||
WingPropHandle::Handles Handles = Props.GetDetails(Node, false, WingOut::Stdout);
|
|
||||||
PrimaryCategory = MatNode->MaterialExpression->GetClass()->GetName();
|
PrimaryCategory = MatNode->MaterialExpression->GetClass()->GetName();
|
||||||
for (const TSharedPtr<IPropertyHandle>& H : Handles)
|
for (const FWingProperty& P : Props)
|
||||||
{
|
{
|
||||||
FString Category = H->GetProperty()->GetMetaData(TEXT("Category"));
|
FString Category = P.GetCategory();
|
||||||
if ((Category == PrimaryCategory) == bPrimary)
|
if ((Category == PrimaryCategory) == bPrimary)
|
||||||
WingPropHandle::Print(*H, Out);
|
P.Print(Out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (bPrimary)
|
else if (bPrimary)
|
||||||
{
|
{
|
||||||
WingPropHandle::Handles Handles = Props.GetDetails(Node, false, WingOut::Stdout);
|
for (const FWingProperty& P : Props)
|
||||||
for (const TSharedPtr<IPropertyHandle>& H : Handles)
|
P.Print(Out);
|
||||||
WingPropHandle::Print(*H, Out);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,7 +295,7 @@ void WingGraphExport::EmitDetails()
|
|||||||
Details.Appendf(TEXT("\ndetails %s\n"), *WingUtils::FormatName(Node));
|
Details.Appendf(TEXT("\ndetails %s\n"), *WingUtils::FormatName(Node));
|
||||||
Details.Appendf(TEXT(" pos %d, %d\n"), Node->NodePosX, Node->NodePosY);
|
Details.Appendf(TEXT(" pos %d, %d\n"), Node->NodePosX, Node->NodePosY);
|
||||||
|
|
||||||
EmitNodeProperties(Node, Details, false);
|
EmitNodeProperties(Node, &Details, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,524 +0,0 @@
|
|||||||
#include "WingPropHandle.h"
|
|
||||||
#include "WingServer.h"
|
|
||||||
#include "WingActorComponent.h"
|
|
||||||
#include "WingUtils.h"
|
|
||||||
#include "WingTypes.h"
|
|
||||||
#include "Dom/JsonValue.h"
|
|
||||||
#include "PropertyEditorModule.h"
|
|
||||||
#include "IDetailTreeNode.h"
|
|
||||||
#include "Engine/Blueprint.h"
|
|
||||||
#include "Components/Widget.h"
|
|
||||||
#include "Components/PanelSlot.h"
|
|
||||||
#include "MaterialGraph/MaterialGraphNode.h"
|
|
||||||
#include "Materials/MaterialExpression.h"
|
|
||||||
#include "UObject/EnumProperty.h"
|
|
||||||
#include "EdGraph/EdGraphPin.h"
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Get Root
|
|
||||||
//
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
TSharedRef<IPropertyRowGenerator> WingPropHandle::CreateGenerator()
|
|
||||||
{
|
|
||||||
FPropertyEditorModule& Module = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
|
||||||
FPropertyRowGeneratorArgs Args;
|
|
||||||
Args.bShouldShowHiddenProperties = false;
|
|
||||||
return Module.CreatePropertyRowGenerator(Args);
|
|
||||||
}
|
|
||||||
|
|
||||||
WingPropHandle::Root& WingPropHandle::GetRootForObject(UObject* Obj)
|
|
||||||
{
|
|
||||||
for (Root& R : Roots)
|
|
||||||
{
|
|
||||||
if (R.Base == (uint8*)Obj) return R;
|
|
||||||
}
|
|
||||||
TSharedRef<IPropertyRowGenerator> Gen = CreateGenerator();
|
|
||||||
Gen->SetObjects({Obj});
|
|
||||||
Root& R = Roots.AddDefaulted_GetRef();
|
|
||||||
R.Struct = Obj->GetClass();
|
|
||||||
R.Base = (uint8*)Obj;
|
|
||||||
R.End = R.Base + R.Struct->GetStructureSize();
|
|
||||||
R.Generator = Gen;
|
|
||||||
return R;
|
|
||||||
}
|
|
||||||
|
|
||||||
WingPropHandle::Root& WingPropHandle::GetRootForStruct(const UStruct* ScriptStruct, uint8* Data)
|
|
||||||
{
|
|
||||||
for (Root& R : Roots)
|
|
||||||
{
|
|
||||||
if (R.Base == Data) return R;
|
|
||||||
}
|
|
||||||
TSharedRef<IPropertyRowGenerator> Gen = CreateGenerator();
|
|
||||||
TSharedPtr<FStructOnScope> Wrapper = MakeShared<FStructOnScope>(ScriptStruct, Data);
|
|
||||||
Gen->SetStructure(Wrapper);
|
|
||||||
Root& R = Roots.AddDefaulted_GetRef();
|
|
||||||
R.Struct = ScriptStruct;
|
|
||||||
R.Base = Data;
|
|
||||||
R.End = Data + ScriptStruct->GetStructureSize();
|
|
||||||
R.Generator = Gen;
|
|
||||||
return R;
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// IsInsideRootObject
|
|
||||||
//
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool WingPropHandle::IsInsideRootObject(const Root& Root, IPropertyHandle& Handle)
|
|
||||||
{
|
|
||||||
// Walk up to the topmost property handle that still has a property.
|
|
||||||
// Keep the shared pointers alive so the raw pointer stays valid.
|
|
||||||
TSharedPtr<IPropertyHandle> Held;
|
|
||||||
IPropertyHandle* Top = &Handle;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
TSharedPtr<IPropertyHandle> Parent = Top->GetParentHandle();
|
|
||||||
if (!Parent.IsValid() || !Parent->GetProperty()) break;
|
|
||||||
Held = Parent;
|
|
||||||
Top = Held.Get();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the address of the topmost property's data.
|
|
||||||
void* Addr = nullptr;
|
|
||||||
if (Top->GetValueData(Addr) != FPropertyAccess::Success || !Addr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
uint8* DataPtr = (uint8*)Addr;
|
|
||||||
return DataPtr >= Root.Base && DataPtr < Root.End;
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// All Tree Nodes
|
|
||||||
//
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void WingPropHandle::AllTreeNodesRecursive(const TSharedRef<IDetailTreeNode>& Node, FlatTree& Out)
|
|
||||||
{
|
|
||||||
if (Node->GetNodeType() == EDetailNodeType::Category)
|
|
||||||
{
|
|
||||||
TArray<TSharedRef<IDetailTreeNode>> Children;
|
|
||||||
Node->GetChildren(Children);
|
|
||||||
for (const TSharedRef<IDetailTreeNode>& Child : Children)
|
|
||||||
{
|
|
||||||
AllTreeNodesRecursive(Child, Out);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Out.Add(&*Node);
|
|
||||||
}
|
|
||||||
|
|
||||||
WingPropHandle::FlatTree WingPropHandle::AllTreeNodes(Root& Root)
|
|
||||||
{
|
|
||||||
FlatTree Result;
|
|
||||||
for (const TSharedRef<IDetailTreeNode>& TreeRoot : Root.Generator->GetRootTreeNodes())
|
|
||||||
{
|
|
||||||
AllTreeNodesRecursive(TreeRoot, Result);
|
|
||||||
}
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// AllProperties
|
|
||||||
//
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
WingPropHandle::Handles WingPropHandle::AllProperties(Root& Root, bool RootFilter)
|
|
||||||
{
|
|
||||||
Handles Result;
|
|
||||||
for (IDetailTreeNode* Node : AllTreeNodes(Root))
|
|
||||||
{
|
|
||||||
TSharedPtr<IPropertyHandle> Handle = Node->CreatePropertyHandle();
|
|
||||||
if (Handle.IsValid() && Handle->GetProperty())
|
|
||||||
{
|
|
||||||
if (RootFilter && !IsInsideRootObject(Root, *Handle))
|
|
||||||
continue;
|
|
||||||
Result.Add(Handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
WingPropHandle::Handles WingPropHandle::AllProperties(UObject* Obj, bool RootFilter)
|
|
||||||
{
|
|
||||||
if (!Obj) return {};
|
|
||||||
return AllProperties(GetRootForObject(Obj), RootFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
WingPropHandle::Handles WingPropHandle::AllProperties(const UStruct* ScriptStruct, uint8* Data, bool RootFilter)
|
|
||||||
{
|
|
||||||
if (!ScriptStruct || !Data) return {};
|
|
||||||
return AllProperties(GetRootForStruct(ScriptStruct, Data), RootFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Named Property
|
|
||||||
//
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
TSharedPtr<IPropertyHandle> WingPropHandle::TryNamedProperty(Root& Root, FName Name, bool RootFilter)
|
|
||||||
{
|
|
||||||
for (IDetailTreeNode* Node : AllTreeNodes(Root))
|
|
||||||
{
|
|
||||||
if (Node->GetNodeName() != Name) continue;
|
|
||||||
TSharedPtr<IPropertyHandle> Handle = Node->CreatePropertyHandle();
|
|
||||||
if (Handle.IsValid() && (!RootFilter || IsInsideRootObject(Root, *Handle)))
|
|
||||||
return Handle;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
TSharedPtr<IPropertyHandle> WingPropHandle::TryNamedProperty(UObject* Obj, FName Name, bool RootFilter)
|
|
||||||
{
|
|
||||||
if (!Obj) return nullptr;
|
|
||||||
return TryNamedProperty(GetRootForObject(Obj), Name, RootFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
TSharedPtr<IPropertyHandle> WingPropHandle::TryNamedProperty(const UStruct* ScriptStruct, uint8* Data, FName Name, bool RootFilter)
|
|
||||||
{
|
|
||||||
if (!ScriptStruct || !Data) return nullptr;
|
|
||||||
return TryNamedProperty(GetRootForStruct(ScriptStruct, Data), Name, RootFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(UObject* Obj, FName Name, bool RootFilter, WingOut Errors)
|
|
||||||
{
|
|
||||||
TSharedPtr<IPropertyHandle> Result = TryNamedProperty(Obj, Name, RootFilter);
|
|
||||||
if (!Result)
|
|
||||||
Errors.Printf(TEXT("ERROR: Property '%s' not found\n"), *Name.ToString());
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(const UStruct* ScriptStruct, uint8* Data, FName Name, bool RootFilter, WingOut Errors)
|
|
||||||
{
|
|
||||||
TSharedPtr<IPropertyHandle> Result = TryNamedProperty(ScriptStruct, Data, Name, RootFilter);
|
|
||||||
if (!Result)
|
|
||||||
Errors.Printf(TEXT("ERROR: Property '%s' not found\n"), *Name.ToString());
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// GetDetails
|
|
||||||
//
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
WingPropHandle::Handles WingPropHandle::GetDetails(UObject* Obj, bool Mutable, WingOut Errors)
|
|
||||||
{
|
|
||||||
bool RootFilter = false;
|
|
||||||
|
|
||||||
if (!Obj) return {};
|
|
||||||
|
|
||||||
// Blueprints: redirect to the generated class CDO.
|
|
||||||
if (UBlueprint* BP = Cast<UBlueprint>(Obj))
|
|
||||||
{
|
|
||||||
if (!BP->GeneratedClass)
|
|
||||||
{
|
|
||||||
Errors.Printf(TEXT("ERROR: Blueprint '%s' has no GeneratedClass\n"), *Obj->GetName());
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
Obj = BP->GeneratedClass->GetDefaultObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
// UWingComponentReference is a class of our own creation, containing
|
|
||||||
// a pointer to a blueprint and a component name. Sometimes, the
|
|
||||||
// component is inherited. If so, and if you want to mutate it, you
|
|
||||||
// first have to create the override template in the child. If you're
|
|
||||||
// not mutating, you can just use the existing inherited template.
|
|
||||||
if (UWingComponentReference* Ref = Cast<UWingComponentReference>(Obj))
|
|
||||||
{
|
|
||||||
Obj = Mutable ? Ref->GetMutableTemplate() : Ref->GetImmutableTemplate();
|
|
||||||
if (!Obj)
|
|
||||||
{
|
|
||||||
Errors.Printf(TEXT("ERROR: Component '%s' has no template\n"), *Ref->VariableName.ToString());
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actors have components, which flood the property listing with hundreds
|
|
||||||
// of confusing additional properties.
|
|
||||||
if (Cast<AActor>(Obj)) RootFilter = true;
|
|
||||||
|
|
||||||
// Fetch the handles.
|
|
||||||
Handles Result = AllProperties(Obj, RootFilter);
|
|
||||||
|
|
||||||
// Material graph nodes: also collect expression properties.
|
|
||||||
if (UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(Obj))
|
|
||||||
{
|
|
||||||
if (UMaterialExpression* Expr = MatNode->MaterialExpression)
|
|
||||||
{
|
|
||||||
Result.Append(AllProperties(Expr, true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Widgets: hide the Slot property, add slot properties.
|
|
||||||
// if (UWidget* Widget = Cast<UWidget>(Obj))
|
|
||||||
// {
|
|
||||||
// Result.RemoveAll([](const TSharedPtr<IPropertyHandle>& H)
|
|
||||||
// {
|
|
||||||
// return H->GetProperty()->GetFName() == TEXT("Slot");
|
|
||||||
// });
|
|
||||||
// if (UPanelSlot* Slot = Widget->Slot)
|
|
||||||
// {
|
|
||||||
// Result.Append(AllProperties(Slot, false));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Organize by Name
|
|
||||||
//
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool WingPropHandle::OrganizeByName(const Handles &HList, TMap<FName, TSharedPtr<IPropertyHandle>> &Result, WingOut Errors)
|
|
||||||
{
|
|
||||||
Result.Empty();
|
|
||||||
TSet<FName> DuplicateNames;
|
|
||||||
for (const TSharedPtr<IPropertyHandle> &H : HList)
|
|
||||||
{
|
|
||||||
FName Name = H->GetProperty()->GetFName();
|
|
||||||
if (Result.Contains(Name)) DuplicateNames.Add(Name);
|
|
||||||
else Result.Add(Name, H);
|
|
||||||
}
|
|
||||||
if (DuplicateNames.IsEmpty()) return true;
|
|
||||||
Errors.Print(TEXT("More than one property with name:"));
|
|
||||||
for (FName DupName : DuplicateNames)
|
|
||||||
{
|
|
||||||
Errors.Printf(TEXT(" %s"), *WingUtils::ExternalizeID(DupName));
|
|
||||||
}
|
|
||||||
Errors.Print(TEXT("\n"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// GetText / SetText
|
|
||||||
//
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool WingPropHandle::IsPinTypeProperty(FProperty* Prop)
|
|
||||||
{
|
|
||||||
FStructProperty* StructProp = CastField<FStructProperty>(Prop);
|
|
||||||
return StructProp && StructProp->Struct == FEdGraphPinType::StaticStruct();
|
|
||||||
}
|
|
||||||
|
|
||||||
FString WingPropHandle::GetText(IPropertyHandle& Handle)
|
|
||||||
{
|
|
||||||
// Pin types: use our human-readable format.
|
|
||||||
if (IsPinTypeProperty(Handle.GetProperty()))
|
|
||||||
{
|
|
||||||
void* Data = nullptr;
|
|
||||||
if (Handle.GetValueData(Data) != FPropertyAccess::Success || !Data)
|
|
||||||
UE_LOG(LogTemp, Fatal, TEXT("GetValueData failed for pin type property '%s'"),
|
|
||||||
*Handle.GetProperty()->GetName());
|
|
||||||
return UWingTypes::TypeToText(*static_cast<FEdGraphPinType*>(Data));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Class properties: use our human-readable type names.
|
|
||||||
if (CastField<FClassProperty>(Handle.GetProperty()))
|
|
||||||
{
|
|
||||||
UObject* ClassObj = nullptr;
|
|
||||||
if (Handle.GetValue(ClassObj) != FPropertyAccess::Success)
|
|
||||||
UE_LOG(LogTemp, Fatal, TEXT("GetValue failed for class property '%s'"),
|
|
||||||
*Handle.GetProperty()->GetName());
|
|
||||||
if (!ClassObj) return TEXT("None");
|
|
||||||
return UWingTypes::TypeToText(ClassObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
FString Result;
|
|
||||||
if (Handle.GetValueAsFormattedString(Result) != FPropertyAccess::Success)
|
|
||||||
UE_LOG(LogTemp, Fatal, TEXT("GetValueAsFormattedString failed for property '%s'"),
|
|
||||||
*Handle.GetProperty()->GetName());
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WingPropHandle::SetText(IPropertyHandle& Handle, const FString& Text, WingOut Errors)
|
|
||||||
{
|
|
||||||
FProperty* Prop = Handle.GetProperty();
|
|
||||||
|
|
||||||
// Pin types: parse with our type parser.
|
|
||||||
if (IsPinTypeProperty(Prop))
|
|
||||||
{
|
|
||||||
FEdGraphPinType PinType;
|
|
||||||
UWingTypes::Requirements Req;
|
|
||||||
Req.BlueprintType = true;
|
|
||||||
Req.Blueprintable = false;
|
|
||||||
Req.AllowContainer = true;
|
|
||||||
if (!UWingTypes::TextToType(Text, PinType, Req, Errors)) return false;
|
|
||||||
void* Data = nullptr;
|
|
||||||
if (Handle.GetValueData(Data) != FPropertyAccess::Success || !Data)
|
|
||||||
{
|
|
||||||
Errors.Printf(TEXT("ERROR: Cannot access data for property '%s'\n"),
|
|
||||||
*WingUtils::FormatName(Prop));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Handle.NotifyPreChange();
|
|
||||||
*static_cast<FEdGraphPinType*>(Data) = PinType;
|
|
||||||
Handle.NotifyPostChange(EPropertyChangeType::ValueSet);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Class properties: parse with our type parser.
|
|
||||||
if (FClassProperty* ClassProp = CastField<FClassProperty>(Prop))
|
|
||||||
{
|
|
||||||
UObject* Class = nullptr;
|
|
||||||
if (!Text.IsEmpty())
|
|
||||||
{
|
|
||||||
UWingTypes::Requirements Req;
|
|
||||||
Req.BlueprintType = true;
|
|
||||||
Req.Blueprintable = false;
|
|
||||||
Req.IsChildOf = ClassProp->MetaClass;
|
|
||||||
Class = UWingTypes::TextToOneObjectType(Text, Req, Errors);
|
|
||||||
if (!Class) return false;
|
|
||||||
}
|
|
||||||
if (Handle.SetValue(Class) != FPropertyAccess::Success)
|
|
||||||
{
|
|
||||||
Errors.Printf(TEXT("ERROR: Failed to set class property '%s'\n"),
|
|
||||||
*WingUtils::FormatName(Prop));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enums: canonicalize the string with our smarter prefix matching.
|
|
||||||
FString Value = Text;
|
|
||||||
UEnum* Enum = nullptr;
|
|
||||||
if (FByteProperty* ByteProp = CastField<FByteProperty>(Prop))
|
|
||||||
Enum = ByteProp->Enum;
|
|
||||||
if (FEnumProperty* EnumProp = CastField<FEnumProperty>(Prop))
|
|
||||||
Enum = EnumProp->GetEnum();
|
|
||||||
if (Enum != nullptr)
|
|
||||||
{
|
|
||||||
int64 EnumValue;
|
|
||||||
if (!WingUtils::StringToEnum(Enum, Value, EnumValue, Errors)) return false;
|
|
||||||
Value = Enum->GetNameStringByValue(EnumValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
FPropertyAccess::Result Result = Handle.SetValueFromFormattedString(Value);
|
|
||||||
if (Result != FPropertyAccess::Success)
|
|
||||||
{
|
|
||||||
Errors.Printf(TEXT("ERROR: Failed to parse '%s' for property '%s' (type: %s)\n"),
|
|
||||||
*Value, *WingUtils::FormatName(Prop), *Prop->GetCPPType());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// SetJson
|
|
||||||
//
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool WingPropHandle::SetJson(IPropertyHandle& Handle, const TSharedPtr<FJsonValue>& JsonValue, WingOut Errors)
|
|
||||||
{
|
|
||||||
FPropertyAccess::Result Result;
|
|
||||||
|
|
||||||
switch (JsonValue->Type)
|
|
||||||
{
|
|
||||||
case EJson::String:
|
|
||||||
return SetText(Handle, JsonValue->AsString(), Errors);
|
|
||||||
|
|
||||||
case EJson::Boolean:
|
|
||||||
Result = Handle.SetValue(JsonValue->AsBool());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EJson::Number:
|
|
||||||
Result = Handle.SetValue(JsonValue->AsNumber());
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
Result = FPropertyAccess::Fail;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Result != FPropertyAccess::Success)
|
|
||||||
{
|
|
||||||
Errors.Printf(TEXT("ERROR: Failed to set property '%s'\n"),
|
|
||||||
*WingUtils::FormatName(Handle.GetProperty()));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WingPropHandle::PopulateFromJson(TArray<TSharedPtr<IPropertyHandle>>& Props, const FJsonObject& Json, bool AllOptional, WingOut Errors)
|
|
||||||
{
|
|
||||||
bool Ok = true;
|
|
||||||
|
|
||||||
// Organize the properties by name.
|
|
||||||
TMap<FName, TSharedPtr<IPropertyHandle>> OrganizedProps;
|
|
||||||
if (!OrganizeByName(Props, OrganizedProps, Errors)) Ok = false;
|
|
||||||
|
|
||||||
// Parse the keys in the json, make sure they're syntactically valid and
|
|
||||||
// that they match the names of actual properties, and that there are no dups.
|
|
||||||
TSet<FName> Specified;
|
|
||||||
for (const auto& KV : Json.Values)
|
|
||||||
{
|
|
||||||
FName Name = WingUtils::CheckInternalizeID(KV.Key, Errors);
|
|
||||||
if (Name.IsNone()) { Ok = false; continue; }
|
|
||||||
if (!OrganizedProps.Contains(Name))
|
|
||||||
{
|
|
||||||
Errors.Printf(TEXT("ERROR: Unknown parameter '%s'\n"), *KV.Key);
|
|
||||||
Ok = false;
|
|
||||||
}
|
|
||||||
if (!WingUtils::FindNoDuplicateName(Specified, Name, TEXT("parameter"), Errors)) Ok = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that all required properties have been specified.
|
|
||||||
if (!AllOptional)
|
|
||||||
{
|
|
||||||
for (const TSharedPtr<IPropertyHandle> &H : Props)
|
|
||||||
{
|
|
||||||
if (H->HasMetaData(TEXT("Optional"))) continue;
|
|
||||||
FName Name = H->GetProperty()->GetFName();
|
|
||||||
if (!Specified.Contains(Name))
|
|
||||||
{
|
|
||||||
Errors.Printf(TEXT("Required parameter %s not specified\n"),
|
|
||||||
*WingUtils::ExternalizeID(Name));
|
|
||||||
Ok = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If anything is wrong, return early without setting anything.
|
|
||||||
if (!Ok) return false;
|
|
||||||
|
|
||||||
// Populate each property from JSON. This could fail too, but at this
|
|
||||||
// point, we're committed.
|
|
||||||
for (const auto& KV : Json.Values)
|
|
||||||
{
|
|
||||||
FName Name = WingUtils::CheckInternalizeID(KV.Key, Errors);
|
|
||||||
if (!SetJson(*OrganizedProps[Name], KV.Value, Errors)) Ok = false;
|
|
||||||
}
|
|
||||||
return Ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Print
|
|
||||||
//
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void WingPropHandle::Print(IPropertyHandle& Handle, FStringBuilderBase& Out)
|
|
||||||
{
|
|
||||||
FString Value = GetText(Handle);
|
|
||||||
Value.ReplaceInline(TEXT("\r"), TEXT(" "));
|
|
||||||
Value.ReplaceInline(TEXT("\n"), TEXT(" "));
|
|
||||||
if (Value.Len() > 100) Value = Value.Left(100) + TEXT("...");
|
|
||||||
|
|
||||||
bool bEditable = !Handle.IsEditConst();
|
|
||||||
|
|
||||||
Out.Appendf(TEXT(" %s %s %s = %s\n"),
|
|
||||||
bEditable ? TEXT("editable") : TEXT("readonly"),
|
|
||||||
*UWingTypes::TypeToText(Handle.GetProperty()),
|
|
||||||
*WingUtils::FormatName(Handle),
|
|
||||||
*Value);
|
|
||||||
}
|
|
||||||
@@ -338,6 +338,16 @@ FString FWingProperty::GetTruncatedText(int32 MaxLen) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FWingProperty::Print(WingOut Out) const
|
||||||
|
{
|
||||||
|
bool bEditable = !Prop->HasAnyPropertyFlags(CPF_EditConst);
|
||||||
|
Out.Printf(TEXT(" %s %s %s = %s\n"),
|
||||||
|
bEditable ? TEXT("editable") : TEXT("readonly"),
|
||||||
|
*UWingTypes::TypeToText(Prop),
|
||||||
|
*WingUtils::FormatName(Prop),
|
||||||
|
*GetTruncatedText(100));
|
||||||
|
}
|
||||||
|
|
||||||
FString FWingProperty::GetCategory() const
|
FString FWingProperty::GetCategory() const
|
||||||
{
|
{
|
||||||
FString Result = Prop->GetMetaData(TEXT("Category"));
|
FString Result = Prop->GetMetaData(TEXT("Category"));
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ void UWingServer::Initialize(FSubsystemCollectionBase& Collection)
|
|||||||
}
|
}
|
||||||
|
|
||||||
BuildWingHandlerRegistry();
|
BuildWingHandlerRegistry();
|
||||||
|
ModulesChangedHandle = FModuleManager::Get().OnModulesChanged().AddUObject(this, &UWingServer::OnModulesChanged);
|
||||||
LogCapture.bEnabled = false;
|
LogCapture.bEnabled = false;
|
||||||
LogCapture.Install();
|
LogCapture.Install();
|
||||||
bRunning = true;
|
bRunning = true;
|
||||||
@@ -154,6 +155,8 @@ void UWingServer::Initialize(FSubsystemCollectionBase& Collection)
|
|||||||
|
|
||||||
void UWingServer::Deinitialize()
|
void UWingServer::Deinitialize()
|
||||||
{
|
{
|
||||||
|
FModuleManager::Get().OnModulesChanged().Remove(ModulesChangedHandle);
|
||||||
|
|
||||||
if (!bRunning)
|
if (!bRunning)
|
||||||
{
|
{
|
||||||
Super::Deinitialize();
|
Super::Deinitialize();
|
||||||
@@ -472,6 +475,7 @@ void UWingServer::AddHandler(UObject* Obj, const FString& Name, UObject* Config,
|
|||||||
|
|
||||||
void UWingServer::BuildWingHandlerRegistry()
|
void UWingServer::BuildWingHandlerRegistry()
|
||||||
{
|
{
|
||||||
|
WingHandlerRegistry.Empty();
|
||||||
for (UClass* Class : WingUtils::CollectHandlerClasses())
|
for (UClass* Class : WingUtils::CollectHandlerClasses())
|
||||||
{
|
{
|
||||||
UWingHandler* CDO = Cast<UWingHandler>(Class->GetDefaultObject());
|
UWingHandler* CDO = Cast<UWingHandler>(Class->GetDefaultObject());
|
||||||
@@ -480,6 +484,14 @@ void UWingServer::BuildWingHandlerRegistry()
|
|||||||
WingHandlerRegistry.Sort([](const FWingHandlerConfig& A, const FWingHandlerConfig& B) { return A.Name < B.Name; });
|
WingHandlerRegistry.Sort([](const FWingHandlerConfig& A, const FWingHandlerConfig& B) { return A.Name < B.Name; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UWingServer::OnModulesChanged(FName ModuleName, EModuleChangeReason Reason)
|
||||||
|
{
|
||||||
|
if (Reason == EModuleChangeReason::ModuleLoaded)
|
||||||
|
{
|
||||||
|
BuildWingHandlerRegistry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FWingHandlerConfig* UWingServer::FindHandler(const FString& Name)
|
FWingHandlerConfig* UWingServer::FindHandler(const FString& Name)
|
||||||
{
|
{
|
||||||
int32 Index = Algo::LowerBoundBy(WingHandlerRegistry, Name, [](const FWingHandlerConfig& H) { return H.Name; });
|
int32 Index = Algo::LowerBoundBy(WingHandlerRegistry, Name, [](const FWingHandlerConfig& H) { return H.Name; });
|
||||||
|
|||||||
@@ -20,12 +20,20 @@ public:
|
|||||||
// pop up dialog boxes. In those cases, we deal with it
|
// pop up dialog boxes. In those cases, we deal with it
|
||||||
// primarily by adding those factories to the blacklist,
|
// primarily by adding those factories to the blacklist,
|
||||||
// and then implementing replacement factories.
|
// and then implementing replacement factories.
|
||||||
static bool IsBlacklisted(UClass* FactoryClass);
|
static bool IsBlacklisted(TSubclassOf<UFactory> FactoryClass);
|
||||||
|
|
||||||
|
// Check if the factory class can be used to create assets.
|
||||||
|
// makes sure it's not abstract, calls CanCreateNew,
|
||||||
|
// calls ShouldShowInNewMenu, and verifies not blacklisted.
|
||||||
|
static bool CanCreate(TSubclassOf<UFactory> FactoryClass);
|
||||||
|
|
||||||
|
// Get the names of the editable properties for a factory class.
|
||||||
|
static TArray<FName> GetParameterNames(TSubclassOf<UFactory> FactoryClass);
|
||||||
|
|
||||||
// This takes a factory name and turns it into a string
|
// This takes a factory name and turns it into a string
|
||||||
// that we can present to the user. Mainly, it removes
|
// that we can present to the user. Mainly, it removes
|
||||||
// the word 'Factory', and anything that comes after.
|
// the word 'Factory', and anything that comes after.
|
||||||
static FString DeriveFactoryName(UClass* FactoryClass);
|
static FString DeriveFactoryName(TSubclassOf<UFactory> FactoryClass);
|
||||||
|
|
||||||
// Verifies that the asset path is a valid path, and also
|
// Verifies that the asset path is a valid path, and also
|
||||||
// that there's not something already there at that path.
|
// that there's not something already there at that path.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "WingPropHandle.h"
|
#include "WingProperty.h"
|
||||||
#include "Engine/Blueprint.h"
|
#include "Engine/Blueprint.h"
|
||||||
#include "EdGraph/EdGraph.h"
|
#include "EdGraph/EdGraph.h"
|
||||||
#include "EdGraph/EdGraphNode.h"
|
#include "EdGraph/EdGraphNode.h"
|
||||||
@@ -62,7 +62,7 @@ private:
|
|||||||
void Traverse(UEdGraphNode* Node);
|
void Traverse(UEdGraphNode* Node);
|
||||||
void SortNodes();
|
void SortNodes();
|
||||||
void EmitNode(UEdGraphNode* Node);
|
void EmitNode(UEdGraphNode* Node);
|
||||||
void EmitNodeProperties(UEdGraphNode* Node, FStringBuilderBase& Out, bool bPrimary);
|
void EmitNodeProperties(UEdGraphNode* Node, WingOut Out, bool bPrimary);
|
||||||
void EmitLocalVariables();
|
void EmitLocalVariables();
|
||||||
void EmitGraph();
|
void EmitGraph();
|
||||||
void EmitDetails();
|
void EmitDetails();
|
||||||
@@ -76,7 +76,6 @@ private:
|
|||||||
|
|
||||||
|
|
||||||
UEdGraph* Graph;
|
UEdGraph* Graph;
|
||||||
WingPropHandle Props;
|
|
||||||
|
|
||||||
// Data populated by passes.
|
// Data populated by passes.
|
||||||
TArray<UEdGraphNode*> SortedNodes;
|
TArray<UEdGraphNode*> SortedNodes;
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
|
||||||
#include "IPropertyRowGenerator.h"
|
|
||||||
#include "PropertyHandle.h"
|
|
||||||
#include "WingHandler.h"
|
|
||||||
|
|
||||||
class FJsonObject;
|
|
||||||
class FJsonValue;
|
|
||||||
|
|
||||||
// WingPropHandle: A module that provides easy access to
|
|
||||||
// IPropertyHandle objects. To use this module, construct a
|
|
||||||
// WingPropHandle, then use it to fetch properties from
|
|
||||||
// objects. The property handles returned are valid until
|
|
||||||
// the WingPropHandle is destroyed.
|
|
||||||
//
|
|
||||||
// In UE Wingman, we are encouraging the use of
|
|
||||||
// IPropertyHandle over FProperty. The FProperty API sucks:
|
|
||||||
// it's buggy and unreliable. Use IPropertyHandle instead.
|
|
||||||
//
|
|
||||||
|
|
||||||
class WingPropHandle
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using Handles = TArray<TSharedPtr<IPropertyHandle>>;
|
|
||||||
using FlatTree = TArray<IDetailTreeNode *, TInlineAllocator<500>>;
|
|
||||||
|
|
||||||
WingPropHandle() {}
|
|
||||||
|
|
||||||
// Get all properties of a UObject. Returns the handles.
|
|
||||||
// If RootFilter is true, only properties inside the root object are returned.
|
|
||||||
Handles AllProperties(UObject* Obj, bool RootFilter);
|
|
||||||
|
|
||||||
// Get all properties of a struct. Does not copy — the data
|
|
||||||
// pointer must remain valid for the lifetime of this object.
|
|
||||||
// If RootFilter is true, only properties inside the root object are returned.
|
|
||||||
Handles AllProperties(const UStruct* ScriptStruct, uint8* Data, bool RootFilter);
|
|
||||||
|
|
||||||
// Get a single named property from a UObject.
|
|
||||||
// If RootFilter is true, only properties inside the root object are returned.
|
|
||||||
TSharedPtr<IPropertyHandle> TryNamedProperty(UObject* Obj, FName Name, bool RootFilter);
|
|
||||||
|
|
||||||
// Get a single named property from a struct.
|
|
||||||
// If RootFilter is true, only properties inside the root object are returned.
|
|
||||||
TSharedPtr<IPropertyHandle> TryNamedProperty(const UStruct* ScriptStruct, uint8* Data, FName Name, bool RootFilter);
|
|
||||||
|
|
||||||
// Like TryNamedProperty, but prints an error if the property is not found.
|
|
||||||
TSharedPtr<IPropertyHandle> NamedProperty(UObject* Obj, FName Name, bool RootFilter, WingOut Errors);
|
|
||||||
TSharedPtr<IPropertyHandle> NamedProperty(const UStruct* ScriptStruct, uint8* Data, FName Name, bool RootFilter, WingOut Errors);
|
|
||||||
|
|
||||||
// Get "details panel" properties for an object. Handles special
|
|
||||||
// cases: blueprints (redirects to CDO), component references
|
|
||||||
// (redirects to template), material graph nodes (includes
|
|
||||||
// expression properties), widgets (includes slot properties).
|
|
||||||
Handles GetDetails(UObject* Obj, bool Mutable, WingOut Errors);
|
|
||||||
|
|
||||||
// Organize properties by name. If there's more than one property
|
|
||||||
// of a given name, print an error and returns false, and returns a map
|
|
||||||
// that contains one of the two duplicates.
|
|
||||||
static bool OrganizeByName(const Handles &H, TMap<FName, TSharedPtr<IPropertyHandle>> &Result, WingOut Errors);
|
|
||||||
|
|
||||||
// Get/set text. Compared to the built in methods for getting and setting
|
|
||||||
// text, these support more concise enum values, FEdGraphPinType, and
|
|
||||||
// more concise Class properties.
|
|
||||||
static FString GetText(IPropertyHandle& Handle);
|
|
||||||
static bool SetText(IPropertyHandle& Handle, const FString& Text, WingOut Errors);
|
|
||||||
|
|
||||||
// Store a Json value into a property handle. The Json value must be
|
|
||||||
// a string, number, or boolean.
|
|
||||||
static bool SetJson(IPropertyHandle& Handle, const TSharedPtr<FJsonValue>& JsonValue, WingOut Errors);
|
|
||||||
|
|
||||||
// Populate a whole bunch of properties from a Json object. If
|
|
||||||
// AllOptional is true, the Json may supply a subset of the properties.
|
|
||||||
// If not, the Json must supply all of them, excepting properties that
|
|
||||||
// are explicitly marked Optional.
|
|
||||||
static bool PopulateFromJson(TArray<TSharedPtr<IPropertyHandle>>& Props, const FJsonObject& Json, bool AllOptional, WingOut Errors);
|
|
||||||
|
|
||||||
// Print a single property in a standardized format:
|
|
||||||
// editable|readonly Type Name = Value
|
|
||||||
static void Print(IPropertyHandle& Handle, FStringBuilderBase& Out);
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct Root
|
|
||||||
{
|
|
||||||
const UStruct* Struct = nullptr;
|
|
||||||
uint8* Base = nullptr;
|
|
||||||
uint8* End = nullptr;
|
|
||||||
TSharedPtr<IPropertyRowGenerator> Generator;
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool IsPinTypeProperty(FProperty* Prop);
|
|
||||||
static bool IsInsideRootObject(const Root& Root, IPropertyHandle& Handle);
|
|
||||||
TArray<Root> Roots;
|
|
||||||
|
|
||||||
static TSharedRef<IPropertyRowGenerator> CreateGenerator();
|
|
||||||
Root& GetRootForObject(UObject* Obj);
|
|
||||||
Root& GetRootForStruct(const UStruct* ScriptStruct, uint8* Data);
|
|
||||||
static void AllTreeNodesRecursive(const TSharedRef<IDetailTreeNode>& Node, FlatTree& Out);
|
|
||||||
static FlatTree AllTreeNodes(Root& Root);
|
|
||||||
|
|
||||||
Handles AllProperties(Root& Root, bool RootFilter);
|
|
||||||
static TSharedPtr<IPropertyHandle> TryNamedProperty(Root& Root, FName Name, bool RootFilter);
|
|
||||||
};
|
|
||||||
@@ -57,6 +57,10 @@ struct FWingProperty
|
|||||||
//
|
//
|
||||||
FString GetTruncatedText(int32 MaxLen) const;
|
FString GetTruncatedText(int32 MaxLen) const;
|
||||||
|
|
||||||
|
// Print the property's type, name, and value.
|
||||||
|
//
|
||||||
|
void Print(WingOut Out) const;
|
||||||
|
|
||||||
// Get the Category metadata.
|
// Get the Category metadata.
|
||||||
//
|
//
|
||||||
FString GetCategory() const;
|
FString GetCategory() const;
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ private:
|
|||||||
FLogCaptureOutputDevice LogCapture; // installed once at startup, enabled per-request
|
FLogCaptureOutputDevice LogCapture; // installed once at startup, enabled per-request
|
||||||
TArray<FWingHandlerConfig> WingHandlerRegistry; // sorted by Name
|
TArray<FWingHandlerConfig> WingHandlerRegistry; // sorted by Name
|
||||||
void BuildWingHandlerRegistry();
|
void BuildWingHandlerRegistry();
|
||||||
|
void OnModulesChanged(FName ModuleName, EModuleChangeReason Reason);
|
||||||
|
FDelegateHandle ModulesChangedHandle;
|
||||||
FWingHandlerConfig* FindHandler(const FString& Name);
|
FWingHandlerConfig* FindHandler(const FString& Name);
|
||||||
|
|
||||||
// Handle a complete JSON line and return the response JSON
|
// Handle a complete JSON line and return the response JSON
|
||||||
|
|||||||
Reference in New Issue
Block a user