More work on property
This commit is contained in:
59
Plugins/UEWingman/Source/UEWingman/Handlers/Details_Dump.h
Normal file
59
Plugins/UEWingman/Source/UEWingman/Handlers/Details_Dump.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingPropHandle.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Details_Dump.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UWing_Details_Dump : public UWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target object"))
|
||||
FString Object;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Substring filter for property names"))
|
||||
FString Query;
|
||||
|
||||
virtual void Register() override
|
||||
{
|
||||
UWingServer::AddHandler(this,
|
||||
TEXT("Test handler: dump properties using IPropertyHandle."));
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UObject* Target = F.Walk(Object).Cast<UObject>();
|
||||
if (!Target) return;
|
||||
|
||||
WingPropHandle Props;
|
||||
WingPropHandle::Handles Handles = Props.GetDetails(Target, false);
|
||||
|
||||
// Group by category, preserving within-category order.
|
||||
FString QueryLower = Query.ToLower();
|
||||
TSortedMap<FString, TArray<TSharedPtr<IPropertyHandle>>> Categories;
|
||||
for (const TSharedPtr<IPropertyHandle>& H : Handles)
|
||||
{
|
||||
FString Name = WingUtils::FormatName(H);
|
||||
if (!QueryLower.IsEmpty() && !Name.ToLower().Contains(QueryLower))
|
||||
continue;
|
||||
FString Category = H->GetDefaultCategoryName().ToString();
|
||||
if (Category.IsEmpty()) Category = TEXT("Unclassified");
|
||||
Categories.FindOrAdd(Category).Add(H);
|
||||
}
|
||||
|
||||
FStringBuilderBase& Out = UWingServer::GetPrintBuffer();
|
||||
for (const auto& Pair : Categories)
|
||||
{
|
||||
Out.Appendf(TEXT("\n%s:\n"), *Pair.Key);
|
||||
for (const TSharedPtr<IPropertyHandle>& H : Pair.Value)
|
||||
WingPropHandle::Print(*H, Out);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -6,10 +6,10 @@
|
||||
#include "WingFetcher.h"
|
||||
#include "WingPropHandle.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Property_Get2.generated.h"
|
||||
#include "Details_Get.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UWing_Property_Get2 : public UWingHandler
|
||||
class UWing_Details_Get : public UWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
46
Plugins/UEWingman/Source/UEWingman/Handlers/Details_Set.h
Normal file
46
Plugins/UEWingman/Source/UEWingman/Handlers/Details_Set.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingPropHandle.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Details_Set.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UWing_Details_Set : public UWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target object"))
|
||||
FString Object;
|
||||
|
||||
UPROPERTY(meta=(Description="Property name"))
|
||||
FString Property;
|
||||
|
||||
UPROPERTY(meta=(Description="New value in Unreal text format"))
|
||||
FString Value;
|
||||
|
||||
virtual void Register() override
|
||||
{
|
||||
UWingServer::AddHandler(this,
|
||||
TEXT("Set a single editable property. Value uses Unreal text format."));
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UObject* Obj = F.Walk(Object).Cast<UObject>();
|
||||
if (!Obj) return;
|
||||
|
||||
WingPropHandle Props;
|
||||
WingPropHandle::Handles Handles = Props.GetDetails(Obj, true);
|
||||
TSharedPtr<IPropertyHandle>* P = WingUtils::FindOneWithExternalID(Property, Handles, TEXT("Property"));
|
||||
if (!P) return;
|
||||
|
||||
if (WingPropHandle::SetText(**P, Value))
|
||||
UWingServer::Print(TEXT("OK\n"));
|
||||
}
|
||||
};
|
||||
@@ -6,10 +6,10 @@
|
||||
#include "WingFetcher.h"
|
||||
#include "WingPropHandle.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Property_Set2.generated.h"
|
||||
#include "Details_SetMany.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UWing_Property_Set2 : public UWingHandler
|
||||
class UWing_Details_SetMany : public UWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "Materials/MaterialInterface.h"
|
||||
#include "Materials/MaterialInstanceConstant.h"
|
||||
#include "Factories/MaterialInstanceConstantFactoryNew.h"
|
||||
#include "WingPackageMaker.h"
|
||||
#include "MaterialInstance_Create.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_MaterialInstance_Create : public UWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Full asset path for the new Material Instance (e.g. '/Game/Materials/MI_GoldShiny')"))
|
||||
FString AssetPath;
|
||||
|
||||
UPROPERTY(meta=(Description="Parent material package path (Material or Material Instance)"))
|
||||
FString ParentMaterial;
|
||||
|
||||
virtual void Register() override
|
||||
{
|
||||
UWingServer::AddHandler(this,
|
||||
TEXT("Create a new Material Instance Constant asset with a specified parent material."));
|
||||
}
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingPackageMaker Maker(AssetPath);
|
||||
if (!Maker.Ok()) return;
|
||||
|
||||
// Load parent material by package path.
|
||||
WingFetcher F;
|
||||
UObject* ParentObj = F.Asset(ParentMaterial).GetObj();
|
||||
UMaterialInterface* ParentMaterialObj = ParentObj ? Cast<UMaterialInterface>(ParentObj) : nullptr;
|
||||
if (!ParentMaterialObj)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Parent material '%s' not found or not a material\n"), *ParentMaterial);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create via factory + AssetTools.
|
||||
UMaterialInstanceConstant* MI = Maker.CreateAsset<UMaterialInstanceConstant, UMaterialInstanceConstantFactoryNew>();
|
||||
if (!MI) return;
|
||||
|
||||
// Set parent.
|
||||
MI->Parent = ParentMaterialObj;
|
||||
|
||||
UWingServer::Printf(TEXT("Created %s\n"), *MI->GetPathName());
|
||||
if (UMaterialInstance* ParentMI = Cast<UMaterialInstance>(ParentMaterialObj))
|
||||
UWingServer::Printf(TEXT("Parent: %s\n"), *WingUtils::FormatName(ParentMI));
|
||||
else if (UMaterial* ParentMat = Cast<UMaterial>(ParentMaterialObj))
|
||||
UWingServer::Printf(TEXT("Parent: %s\n"), *WingUtils::FormatName(ParentMat));
|
||||
else
|
||||
UWingServer::Printf(TEXT("Parent: %s\n"), *ParentMaterialObj->GetPathName());
|
||||
}
|
||||
};
|
||||
@@ -1,78 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingPropHandle.h"
|
||||
#include "WingUtils.h"
|
||||
#include "WingTypes.h"
|
||||
#include "Property_Dump2.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UWing_Property_Dump2 : public UWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target object"))
|
||||
FString Object;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Substring filter for property names"))
|
||||
FString Query;
|
||||
|
||||
virtual void Register() override
|
||||
{
|
||||
UWingServer::AddHandler(this,
|
||||
TEXT("Test handler: dump properties using IPropertyHandle."));
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UObject* Target = F.Walk(Object).Cast<UObject>();
|
||||
if (!Target) return;
|
||||
|
||||
WingPropHandle Props;
|
||||
WingPropHandle::Handles Handles = Props.GetDetails(Target, false);
|
||||
|
||||
// Sort by category name for consistent grouping.
|
||||
Handles.Sort([](const TSharedPtr<IPropertyHandle>& A, const TSharedPtr<IPropertyHandle>& B)
|
||||
{
|
||||
return A->GetProperty()->GetMetaData(TEXT("Category")) < B->GetProperty()->GetMetaData(TEXT("Category"));
|
||||
});
|
||||
|
||||
FString QueryLower = Query.ToLower();
|
||||
FString CurrentCategory;
|
||||
|
||||
for (const TSharedPtr<IPropertyHandle>& H : Handles)
|
||||
{
|
||||
FProperty* Prop = H->GetProperty();
|
||||
FString Name = WingUtils::FormatName(Prop);
|
||||
if (!QueryLower.IsEmpty() && !Name.ToLower().Contains(QueryLower))
|
||||
continue;
|
||||
|
||||
FString Category = Prop->GetMetaData(TEXT("Category"));
|
||||
if (Category.IsEmpty()) Category = TEXT("Unclassified");
|
||||
if (Category != CurrentCategory)
|
||||
{
|
||||
if (!CurrentCategory.IsEmpty())
|
||||
UWingServer::Print(TEXT("\n"));
|
||||
CurrentCategory = Category;
|
||||
UWingServer::Printf(TEXT("%s:\n"), *CurrentCategory);
|
||||
}
|
||||
|
||||
FString Value;
|
||||
H->GetValueAsFormattedString(Value);
|
||||
if (Value.Len() > 100) { Value.LeftInline(100); Value += TEXT("..."); }
|
||||
|
||||
bool bEditable = !H->IsEditConst();
|
||||
|
||||
UWingServer::Printf(TEXT(" %s %s %s = %s\n"),
|
||||
bEditable ? TEXT("editable") : TEXT("readonly"),
|
||||
*UWingTypes::TypeToText(Prop),
|
||||
*Name,
|
||||
*Value);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,58 +0,0 @@
|
||||
#include "WingPackageMaker.h"
|
||||
#include "PackageTools.h"
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
|
||||
WingPackageMaker::WingPackageMaker(const FString& InFullPath)
|
||||
: FullPath(InFullPath)
|
||||
{
|
||||
if (!CheckNewAssetPath(InFullPath))
|
||||
{
|
||||
bError = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool WingPackageMaker::CheckNewAssetPath(const FString& Path)
|
||||
{
|
||||
if (UPackageTools::SanitizePackageName(Path) != Path)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Package path '%s' is not a valid package name\n"), *Path);
|
||||
return false;
|
||||
}
|
||||
if (!FPackageName::IsValidTextForLongPackageName(Path))
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Package path '%s' is not a valid package name\n"), *Path);
|
||||
return false;
|
||||
}
|
||||
if (!Path.StartsWith(TEXT("/Game")))
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Package path '%s' must start with '/Game'\n"), *Path);
|
||||
return false;
|
||||
}
|
||||
if (FindObject<UPackage>(nullptr, *Path))
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: An asset already exists at '%s'\n"), *Path);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool WingPackageMaker::Make()
|
||||
{
|
||||
if (bError) return false;
|
||||
Pkg = CreatePackage(*FullPath);
|
||||
if (!Pkg)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Failed to create package at '%s'\n"), *FullPath);
|
||||
bError = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
Pkg->ClearFlags(RF_Transient);
|
||||
Pkg->SetIsExternallyReferenceable(true);
|
||||
Pkg->MarkPackageDirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -283,6 +283,17 @@ FString WingPropHandle::GetText(IPropertyHandle& Handle)
|
||||
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'"),
|
||||
@@ -316,6 +327,28 @@ bool WingPropHandle::SetText(IPropertyHandle& Handle, const FString& Text)
|
||||
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);
|
||||
if (!Class) return false;
|
||||
}
|
||||
if (Handle.SetValue(Class) != FPropertyAccess::Success)
|
||||
{
|
||||
UWingServer::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;
|
||||
@@ -385,8 +418,6 @@ bool WingPropHandle::SetJson(IPropertyHandle& Handle, const TSharedPtr<FJsonValu
|
||||
|
||||
void WingPropHandle::Print(IPropertyHandle& Handle, FStringBuilderBase& Out)
|
||||
{
|
||||
FProperty* Prop = Handle.GetProperty();
|
||||
|
||||
FString Value = GetText(Handle);
|
||||
Value.ReplaceInline(TEXT("\r"), TEXT(" "));
|
||||
Value.ReplaceInline(TEXT("\n"), TEXT(" "));
|
||||
@@ -396,7 +427,7 @@ void WingPropHandle::Print(IPropertyHandle& Handle, FStringBuilderBase& Out)
|
||||
|
||||
Out.Appendf(TEXT(" %s %s %s = %s\n"),
|
||||
bEditable ? TEXT("editable") : TEXT("readonly"),
|
||||
*UWingTypes::TypeToText(Prop),
|
||||
*WingUtils::FormatName(Prop),
|
||||
*UWingTypes::TypeToText(Handle.GetProperty()),
|
||||
*WingUtils::FormatName(Handle),
|
||||
*Value);
|
||||
}
|
||||
|
||||
@@ -242,6 +242,16 @@ FString WingUtils::FormatName(const FProperty *Prop)
|
||||
return ExternalizeID(Prop->GetFName());
|
||||
}
|
||||
|
||||
FString WingUtils::FormatName(IPropertyHandle &Handle)
|
||||
{
|
||||
return ExternalizeID(Handle.GetProperty()->GetFName());
|
||||
}
|
||||
|
||||
FString WingUtils::FormatName(const TSharedPtr<IPropertyHandle> &Handle)
|
||||
{
|
||||
return FormatName(*Handle);
|
||||
}
|
||||
|
||||
FString WingUtils::FormatName(const FUserPinInfo &Pin)
|
||||
{
|
||||
return ExternalizeID(Pin.PinName);
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "UObject/Package.h"
|
||||
#include "AssetToolsModule.h"
|
||||
#include "IAssetTools.h"
|
||||
|
||||
// Helper for creating new asset packages. Validates the path and checks for
|
||||
// conflicting assets in the constructor. Call Ok() to check, then Make() to
|
||||
// actually create the UPackage.
|
||||
class WingPackageMaker
|
||||
{
|
||||
public:
|
||||
WingPackageMaker(const FString& InFullPath);
|
||||
|
||||
bool Ok() const { return !bError; }
|
||||
bool Make();
|
||||
UPackage* Package() const { return Pkg; }
|
||||
FString GetName() const { return FPackageName::GetShortName(FullPath); }
|
||||
FName GetFName() const { return FName(GetName()); }
|
||||
|
||||
template<typename AssetClass, typename FactoryClass>
|
||||
AssetClass* CreateAsset()
|
||||
{
|
||||
if (bError) return nullptr;
|
||||
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
|
||||
FactoryClass* Factory = NewObject<FactoryClass>();
|
||||
FString PkgPath = FPackageName::GetLongPackagePath(FullPath);
|
||||
FString AssetName = FPackageName::GetShortName(FullPath);
|
||||
UObject* NewAsset = AssetTools.CreateAsset(AssetName, PkgPath, AssetClass::StaticClass(), Factory);
|
||||
AssetClass* Result = Cast<AssetClass>(NewAsset);
|
||||
if (!Result)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Failed to create asset at '%s'\n"), *FullPath);
|
||||
bError = true;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
private:
|
||||
FString FullPath;
|
||||
UPackage* Pkg = nullptr;
|
||||
bool bError = false;
|
||||
|
||||
static bool CheckNewAssetPath(const FString& Path);
|
||||
};
|
||||
@@ -20,6 +20,7 @@ struct FEdGraphSchemaAction;
|
||||
class UAnimationStateMachineGraph;
|
||||
class UAnimStateNode;
|
||||
class UAnimStateTransitionNode;
|
||||
class IPropertyHandle;
|
||||
class UScriptStruct;
|
||||
class UEnum;
|
||||
struct FBPInterfaceDescription;
|
||||
@@ -186,6 +187,8 @@ public:
|
||||
static FString FormatName(const UScriptStruct *Struct);
|
||||
static FString FormatName(const UEnum *Enum);
|
||||
static FString FormatName(const FProperty *Prop);
|
||||
static FString FormatName(IPropertyHandle &Handle);
|
||||
static FString FormatName(const TSharedPtr<IPropertyHandle> &Handle);
|
||||
static FString FormatName(const FUserPinInfo &Pin);
|
||||
static FString FormatName(const FBPInterfaceDescription &IFace);
|
||||
static FString FormatName(const UWingComponentReference *Ref);
|
||||
|
||||
Reference in New Issue
Block a user