New infrastructure for property manipulation.
This commit is contained in:
BIN
Content/Testing/BP_T1.uasset
LFS
BIN
Content/Testing/BP_T1.uasset
LFS
Binary file not shown.
@@ -1,96 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "WingTypes.h"
|
||||
#include "WingPackageMaker.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "Kismet2/KismetEditorUtilities.h"
|
||||
#include "Blueprint_Create.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Blueprint_Create : public UWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Full asset path for the new Blueprint"))
|
||||
FString AssetPath;
|
||||
|
||||
UPROPERTY(meta=(Description="Parent class, expressed as a type"))
|
||||
FString ParentClass;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Normal, Interface, FunctionLibrary, or MacroLibrary"))
|
||||
TEnumAsByte<EBlueprintType> BlueprintType = BPTYPE_Normal;
|
||||
|
||||
virtual void Register() override
|
||||
{
|
||||
UWingServer::AddHandler(this,
|
||||
TEXT("Create a new Blueprint asset with a specified parent class and type."));
|
||||
}
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingPackageMaker Maker(AssetPath);
|
||||
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, Req);
|
||||
if (!ParentClassObj) return;
|
||||
break;
|
||||
case BPTYPE_MacroLibrary:
|
||||
ParentClassObj = UWingTypes::TextToOneObjectType(ParentClass, Req);
|
||||
if (!ParentClassObj) return;
|
||||
break;
|
||||
case BPTYPE_Interface:
|
||||
ParentClassObj = UInterface::StaticClass();
|
||||
break;
|
||||
case BPTYPE_FunctionLibrary:
|
||||
ParentClassObj = UBlueprintFunctionLibrary::StaticClass();
|
||||
break;
|
||||
default:
|
||||
UWingServer::Print(TEXT("ERROR: BlueprintType must be Normal, Interface, FunctionLibrary, or MacroLibrary\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the package and Blueprint
|
||||
if (!Maker.Make()) return;
|
||||
|
||||
UBlueprint* NewBP = FKismetEditorUtilities::CreateBlueprint(
|
||||
ParentClassObj,
|
||||
Maker.Package(),
|
||||
Maker.GetFName(),
|
||||
BlueprintType,
|
||||
UBlueprint::StaticClass(),
|
||||
UBlueprintGeneratedClass::StaticClass()
|
||||
);
|
||||
|
||||
if (!NewBP)
|
||||
{
|
||||
UWingServer::Print(TEXT("ERROR: FKismetEditorUtilities::CreateBlueprint returned null\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Compile
|
||||
FKismetEditorUtilities::CompileBlueprint(NewBP);
|
||||
|
||||
// Report result
|
||||
UWingServer::Printf(TEXT("Created: %s\n"), *WingUtils::FormatName(NewBP));
|
||||
}
|
||||
};
|
||||
105
Plugins/UEWingman/Source/UEWingman/Handlers/Create_Blueprint.h
Normal file
105
Plugins/UEWingman/Source/UEWingman/Handlers/Create_Blueprint.h
Normal file
@@ -0,0 +1,105 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Factories/Factory.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFactories.h"
|
||||
#include "WingTypes.h"
|
||||
#include "Factories/BlueprintFactory.h"
|
||||
#include "Factories/BlueprintMacroFactory.h"
|
||||
#include "Factories/AnimBlueprintFactory.h"
|
||||
#include "WidgetBlueprintFactory.h"
|
||||
#include "EditorUtilityBlueprintFactory.h"
|
||||
#include "EditorUtilityWidgetBlueprintFactory.h"
|
||||
#include "Create_Blueprint.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UWing_Create_Blueprint : public UWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Full asset path for the new asset (e.g. '/Game/MyFolder/MyAsset')"))
|
||||
FString Path;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="The parent class for the new blueprint"))
|
||||
FString ParentClass;
|
||||
|
||||
static UClass *BlueprintInterfaceFactoryStaticClass()
|
||||
{
|
||||
return FindObject<UClass>(nullptr, TEXT("/Script/UnrealEd.BlueprintInterfaceFactory"));
|
||||
}
|
||||
|
||||
virtual void Register() override
|
||||
{
|
||||
UWingServer::AddHandler(this, TEXT("Create_Blueprint"),
|
||||
UBlueprintFactory::StaticClass(), EWingHandlerKind::Create,
|
||||
TEXT("Create a normal blueprint"));
|
||||
UWingServer::AddHandler(this, TEXT("Create_BlueprintMacroLibrary"),
|
||||
UBlueprintMacroFactory::StaticClass(), EWingHandlerKind::Create,
|
||||
TEXT("Create a blueprint macro library"));
|
||||
UWingServer::AddHandler(this, TEXT("Create_WidgetBlueprint"),
|
||||
UWidgetBlueprintFactory::StaticClass(), EWingHandlerKind::Create,
|
||||
TEXT("Create a widget blueprint"));
|
||||
UWingServer::AddHandler(this, TEXT("Create_EditorUtilityBlueprint"),
|
||||
UEditorUtilityBlueprintFactory::StaticClass(), EWingHandlerKind::Create,
|
||||
TEXT("Create an editor utility blueprint"));
|
||||
UWingServer::AddHandler(this, TEXT("Create_EditorUtilityWidgetBlueprint"),
|
||||
UEditorUtilityWidgetBlueprintFactory::StaticClass(), EWingHandlerKind::Create,
|
||||
TEXT("Create an editor utility widget blueprint"));
|
||||
if (BlueprintInterfaceFactoryStaticClass() != nullptr)
|
||||
UWingServer::AddHandler(this, TEXT("Create_BlueprintInterface"),
|
||||
BlueprintInterfaceFactoryStaticClass(), EWingHandlerKind::Create,
|
||||
TEXT("Create a blueprint interface"));
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
if (!WingFactories::CheckNewAssetPath(Path)) return;
|
||||
|
||||
// Resolve parent class, if specified.
|
||||
UClass *ParentClassObj = nullptr;
|
||||
if (!ParentClass.IsEmpty())
|
||||
{
|
||||
UWingTypes::Requirements Req;
|
||||
Req.BlueprintType = false;
|
||||
Req.Blueprintable = true;
|
||||
Req.AllowContainer = false;
|
||||
ParentClassObj = UWingTypes::TextToOneObjectType(ParentClass, Req);
|
||||
if (!ParentClassObj) return;
|
||||
}
|
||||
|
||||
// Make the factory.
|
||||
UClass *FactoryClass = Cast<UClass>(ConfigurationObject);
|
||||
UFactory *Factory = NewObject<UFactory>(GetTransientPackage(), FactoryClass);
|
||||
|
||||
// Get the 'ParentClass' property.
|
||||
FProperty *Prop = FactoryClass->FindPropertyByName(FName(TEXT("ParentClass")));
|
||||
FClassProperty *CProp = CastField<FClassProperty>(Prop);
|
||||
|
||||
// Check that things are on track.
|
||||
if ((Factory == nullptr) || (CProp == nullptr))
|
||||
{
|
||||
UWingServer::Printf(TEXT("In Create_Blueprint, factory creation is buggy\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the parentclass, if specified.
|
||||
if (ParentClassObj)
|
||||
{
|
||||
if (!ParentClassObj->IsChildOf(CProp->MetaClass))
|
||||
{
|
||||
UWingServer::Printf(TEXT("ParentClass must be child of %s.\n"),
|
||||
*WingUtils::FormatName(CProp->MetaClass));
|
||||
return;
|
||||
}
|
||||
CProp->SetObjectPropertyValue_InContainer(Factory, ParentClassObj);
|
||||
}
|
||||
|
||||
// Create the asset.
|
||||
UObject *Blueprint = WingFactories::CreateAsset(Path, Factory);
|
||||
if (Blueprint == nullptr) return;
|
||||
UWingServer::Printf(TEXT("Created.\n"));
|
||||
}
|
||||
};
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "WingHandler.h"
|
||||
#include "WingFactories.h"
|
||||
#include "WingProperty.h"
|
||||
#include "Factories/BlueprintFunctionLibraryFactory.h"
|
||||
#include "Create_UsingFactory.generated.h"
|
||||
|
||||
UCLASS()
|
||||
@@ -15,7 +16,7 @@ class UWing_Create_UsingFactory : public UWingHandler
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Full asset path for the new asset (e.g. '/Game/MyFolder/MyAsset')"))
|
||||
FString AssetPath;
|
||||
FString Path;
|
||||
|
||||
virtual void Register() override
|
||||
{
|
||||
@@ -32,17 +33,22 @@ public:
|
||||
TArray<FName> ConfigProps = FWingProperty::GetNames(Class, CPF_Edit);
|
||||
if (ConfigProps.Num() > 0) continue;
|
||||
|
||||
FString FactoryName = UWingFactories::DeriveFactoryName(Class);
|
||||
FString FactoryName = WingFactories::DeriveFactoryName(Class);
|
||||
FString CommandName = FString::Printf(TEXT("Create_%s"), *FactoryName);
|
||||
FString Doc = FString::Printf(TEXT("Create a new %s asset."), *FactoryName);
|
||||
UWingServer::AddHandler(this, CommandName, Doc, Class, EWingHandlerKind::Create);
|
||||
UWingServer::AddHandler(this, CommandName, Class, EWingHandlerKind::Create, Doc);
|
||||
}
|
||||
|
||||
UWingServer::AddHandler(this, TEXT("Create_BlueprintFunctionLibrary"),
|
||||
UBlueprintFunctionLibraryFactory::StaticClass(), EWingHandlerKind::Create,
|
||||
TEXT("Create a blueprint function library"));
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
UClass* FactoryClass = Cast<UClass>(ConfigurationObject);
|
||||
UFactory* Factory = NewObject<UFactory>(GetTransientPackage(), FactoryClass);
|
||||
UWingFactories::CreateAsset(AssetPath, Factory);
|
||||
WingFactories::CreateAsset(Path, Factory);
|
||||
UWingServer::Printf(TEXT("Created.\n"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "MaterialDomain.h"
|
||||
#include "Factories/MaterialFactoryNew.h"
|
||||
#include "WingPackageMaker.h"
|
||||
#include "Material_Create.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Material_Create : public UWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Full asset path for the new material"))
|
||||
FString Material;
|
||||
|
||||
virtual void Register() override
|
||||
{
|
||||
UWingServer::AddHandler(this,
|
||||
TEXT("Create a new UMaterial asset"));
|
||||
}
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingPackageMaker Maker(Material);
|
||||
if (!Maker.Ok()) return;
|
||||
|
||||
// Create via IAssetTools + factory.
|
||||
UMaterial* MaterialObj = Maker.CreateAsset<UMaterial, UMaterialFactoryNew>();
|
||||
if (!MaterialObj) return;
|
||||
|
||||
UWingServer::Printf(TEXT("Created %s\n"), *MaterialObj->GetPathName());
|
||||
}
|
||||
};
|
||||
78
Plugins/UEWingman/Source/UEWingman/Handlers/Property_Dump2.h
Normal file
78
Plugins/UEWingman/Source/UEWingman/Handlers/Property_Dump2.h
Normal file
@@ -0,0 +1,78 @@
|
||||
#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, CPF_Edit);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingProperty.h"
|
||||
#include "WingFactories.h"
|
||||
#include "SysInfo_Factories.generated.h"
|
||||
|
||||
@@ -24,25 +25,33 @@ public:
|
||||
"is developing this plugin, they help him to understand. "
|
||||
"unreal's internals better."));
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
const TArray<UWingFactories::Info>& All = UWingFactories::AllFactories();
|
||||
TArray<UClass*> FactoryClasses;
|
||||
GetDerivedClasses(UFactory::StaticClass(), FactoryClasses);
|
||||
|
||||
for (int nparam = 0; nparam < 10; nparam++)
|
||||
TArray<FString> Results;
|
||||
|
||||
for (UClass *Factory : FactoryClasses)
|
||||
{
|
||||
for (const UWingFactories::Info& Entry : All)
|
||||
if (Factory->HasAnyClassFlags(CLASS_Abstract)) continue;
|
||||
UFactory *CDO = Factory->GetDefaultObject<UFactory>();
|
||||
if (!CDO->CanCreateNew()) continue;
|
||||
if (!CDO->ShouldShowInNewMenu()) continue;
|
||||
TArray<FName> Params = FWingProperty::GetNames(Factory, CPF_Edit);
|
||||
TStringBuilder<512> Line;
|
||||
Line.Appendf(TEXT("%2d %s "), Params.Num(), *Factory->GetName());
|
||||
for (const FName& Prop : Params)
|
||||
{
|
||||
if (Entry.Config.Num() != nparam) continue;
|
||||
if (!Entry.CanCreateNew()) continue;
|
||||
UWingServer::Printf(TEXT("%s"), *Entry.Name);
|
||||
|
||||
for (const FName& Prop : Entry.Config)
|
||||
{
|
||||
UWingServer::Printf(TEXT(" PARAM: %s"), *Prop.ToString());
|
||||
}
|
||||
UWingServer::Printf(TEXT("\n"));
|
||||
Line.Appendf(TEXT(" %s"), *Prop.ToString());
|
||||
}
|
||||
UWingServer::Printf(TEXT("\n"));
|
||||
Results.Add(Line.ToString());
|
||||
}
|
||||
Results.Sort();
|
||||
for (const FString &Line : Results)
|
||||
{
|
||||
UWingServer::Printf(TEXT("%s\n"), *Line);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,69 +1,11 @@
|
||||
#include "WingFactories.h"
|
||||
#include "WingServer.h"
|
||||
#include "Editor.h"
|
||||
#include "WingUtils.h"
|
||||
#include "WingProperty.h"
|
||||
#include "PackageTools.h"
|
||||
#include "Kismet2/KismetEditorUtilities.h"
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "Kismet2/EnumEditorUtils.h"
|
||||
#include "Factories/BlueprintFactory.h"
|
||||
#include "Factories/EnumFactory.h"
|
||||
#include "Algo/BinarySearch.h"
|
||||
|
||||
void UWingFactories::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
Super::Initialize(Collection);
|
||||
PopulateRegistry();
|
||||
}
|
||||
|
||||
const TArray<UWingFactories::Info>& UWingFactories::AllFactories()
|
||||
{
|
||||
return GEditor->GetEditorSubsystem<UWingFactories>()->Registry;
|
||||
}
|
||||
|
||||
bool UWingFactories::Info::CanCreateNew() const
|
||||
{
|
||||
UFactory *CDO = FactoryClass->GetDefaultObject<UFactory>();
|
||||
return CDO->CanCreateNew() && CDO->ShouldShowInNewMenu();
|
||||
}
|
||||
|
||||
void UWingFactories::PopulateRegistry()
|
||||
{
|
||||
TArray<UClass*> FactoryClasses;
|
||||
GetDerivedClasses(UFactory::StaticClass(), FactoryClasses);
|
||||
|
||||
// Populate it with initial data.
|
||||
for (UClass* Class : FactoryClasses)
|
||||
{
|
||||
if (Class->HasAnyClassFlags(CLASS_Abstract)) continue;
|
||||
|
||||
Info Entry;
|
||||
Entry.Name = DeriveFactoryName(Class);
|
||||
Entry.FactoryClass = Class;
|
||||
Entry.Config = FWingProperty::GetNames(Class, CPF_Edit);
|
||||
Registry.Add(MoveTemp(Entry));
|
||||
}
|
||||
|
||||
// Sort the registry.
|
||||
Registry.Sort([](const Info& A, const Info& B) { return A.Name < B.Name; });
|
||||
|
||||
// Blacklist certain bad factories.
|
||||
DisableFactory(TEXT("PhysicsAsset")); // PhysicsAsset factory pops a modal dialog
|
||||
}
|
||||
|
||||
void UWingFactories::DisableFactory(const TCHAR* Name)
|
||||
{
|
||||
Info* Entry = Find(Name);
|
||||
if (!Entry)
|
||||
{
|
||||
UE_LOG(LogTemp, Fatal, TEXT("UWingFactories::DisableFactory: factory '%s' not found"), Name);
|
||||
return;
|
||||
}
|
||||
Entry->Disabled = true;
|
||||
}
|
||||
|
||||
|
||||
bool UWingFactories::CheckNewAssetPath(const FString& Path)
|
||||
bool WingFactories::CheckNewAssetPath(const FString& Path)
|
||||
{
|
||||
if (UPackageTools::SanitizePackageName(Path) != Path)
|
||||
{
|
||||
@@ -88,34 +30,36 @@ bool UWingFactories::CheckNewAssetPath(const FString& Path)
|
||||
return true;
|
||||
}
|
||||
|
||||
UPackage *UWingFactories::CreateNewPackage(const FString &Path)
|
||||
bool WingFactories::IsBlacklisted(UClass* FactoryClass)
|
||||
{
|
||||
UPackage *Pkg = CreatePackage(*Path);
|
||||
if (!Pkg)
|
||||
FName Name = FactoryClass->GetFName();
|
||||
if (Name == TEXT("PhysicsAssetFactory")) return true; // Pops a modal dialog
|
||||
if (Name == TEXT("EnumFactory")) return true; // Pops a modal dialog
|
||||
return false;
|
||||
}
|
||||
|
||||
UObject* WingFactories::CreateAsset(const FString& Path, UFactory* Factory)
|
||||
{
|
||||
// Check the blacklist.
|
||||
if (IsBlacklisted(Factory->GetClass()))
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Factory '%s' is blacklisted\n"), *Factory->GetClass()->GetName());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Validate the path, and that there's not already something there.
|
||||
if (!CheckNewAssetPath(Path)) return nullptr;
|
||||
FName Name = FName(FPackageName::GetShortName(Path));
|
||||
|
||||
// Create the package.
|
||||
UPackage *Package = CreatePackage(*Path);
|
||||
if (!Package)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Failed to create package at '%s'\n"), *Path);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Pkg->ClearFlags(RF_Transient);
|
||||
Pkg->SetIsExternallyReferenceable(true);
|
||||
Pkg->MarkPackageDirty();
|
||||
return Pkg;
|
||||
}
|
||||
|
||||
|
||||
UObject* UWingFactories::CreateAsset(const FString& Path, UFactory* Factory)
|
||||
{
|
||||
// Validate the path, and that there's not already something there.
|
||||
if (!CheckNewAssetPath(Path)) return nullptr;
|
||||
FName Name = FName(FPackageName::GetShortName(Path));
|
||||
|
||||
// Pre-check: block factories that would cause problems.
|
||||
// In particular, this blocks those that would pop a dialog.
|
||||
if (!PreCheck(Factory, Name, Path)) return nullptr;
|
||||
|
||||
// Create the asset.
|
||||
UPackage *Package = CreateNewPackage(Path);
|
||||
UObject* NewAsset = Factory->FactoryCreateNew(
|
||||
Factory->GetSupportedClass(),
|
||||
Package,
|
||||
@@ -124,105 +68,50 @@ UObject* UWingFactories::CreateAsset(const FString& Path, UFactory* Factory)
|
||||
nullptr,
|
||||
GWarn
|
||||
);
|
||||
if (!NewAsset)
|
||||
|
||||
// If the asset was created, turn the package into a
|
||||
// permanent package and encourge the editor to save it
|
||||
// to disk. Otherwise, mark the package for deletion.
|
||||
if (NewAsset)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Factory '%s' returned null\n"), *Factory->GetClass()->GetName());
|
||||
Package->ClearFlags(RF_Transient);
|
||||
Package->SetIsExternallyReferenceable(true);
|
||||
Package->MarkPackageDirty();
|
||||
FAssetRegistryModule::AssetCreated(NewAsset);
|
||||
UWingServer::AddTouchedObject(NewAsset);
|
||||
}
|
||||
else
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Factory '%s' failed to create an object\n"), *Factory->GetClass()->GetName());
|
||||
Package->ClearDirtyFlag();
|
||||
Package->MarkAsGarbage();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UWingServer::Printf(TEXT("Created: %s\n"), *WingUtils::ExternalizeID(Name));
|
||||
return NewAsset;
|
||||
}
|
||||
|
||||
UObject* UWingFactories::CreateAsset(const FString& Path, const FString& FactoryName, FWingJsonObject& Config)
|
||||
{
|
||||
UWingFactories* Self = GEditor->GetEditorSubsystem<UWingFactories>();
|
||||
|
||||
// Look up the factory info.
|
||||
const Info* FactoryInfo = Self->Find(FactoryName);
|
||||
if (!FactoryInfo)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Unknown factory '%s'\n"), *FactoryName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Make sure this is a creation factory, as opposed to an import factory.
|
||||
if (!FactoryInfo->CanCreateNew())
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Factory '%s' cannot create objects from scratch\n"), *FactoryName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create the factory instance.
|
||||
UFactory* Factory = NewObject<UFactory>(GetTransientPackage(), FactoryInfo->FactoryClass);
|
||||
|
||||
// Get the editable properties
|
||||
TArray<FWingProperty> Props =
|
||||
FWingProperty::GetNamed(Factory->GetClass(), Factory, FactoryInfo->Config);
|
||||
|
||||
// if there is no config table, make a blank config table.
|
||||
TSharedPtr<FJsonObject> ConfigJson = Config.Json;
|
||||
if (ConfigJson == nullptr) ConfigJson = MakeShared<FJsonObject>();
|
||||
|
||||
// Populate the configuration properties from the json.
|
||||
if (!FWingProperty::PopulateFromJson(Props, ConfigJson.Get(), false))
|
||||
return nullptr;
|
||||
|
||||
return CreateAsset(Path, Factory);
|
||||
}
|
||||
|
||||
bool UWingFactories::PreCheck(UFactory* Factory, FName Name, const FString &Path)
|
||||
{
|
||||
// Blueprint factories: FactoryCreateNew pops FMessageDialog if ParentClass is invalid.
|
||||
if (UBlueprintFactory* BPFactory = Cast<UBlueprintFactory>(Factory))
|
||||
{
|
||||
if (!BPFactory->ParentClass)
|
||||
{
|
||||
UWingServer::Print(TEXT("ERROR: ParentClass must be set\n"));
|
||||
return false;
|
||||
}
|
||||
if (!FKismetEditorUtilities::CanCreateBlueprintOfClass(BPFactory->ParentClass))
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Cannot create a blueprint based on class '%s'\n"), *BPFactory->ParentClass->GetName());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Enum factory: FactoryCreateNew pops FMessageDialog if the name already exists.
|
||||
if (UEnumFactory* EFactory = Cast<UEnumFactory>(Factory))
|
||||
{
|
||||
if(!FEnumEditorUtils::IsNameAvailebleForUserDefinedEnum(Name))
|
||||
{
|
||||
UWingServer::Printf(TEXT("Enum name is already taken: %s"),
|
||||
*WingUtils::ExternalizeID(Name));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UWingFactories::Info* UWingFactories::Find(const FString& Name)
|
||||
{
|
||||
int32 Index = Algo::LowerBound(Registry, Name, [](Info& Entry, const FString& N) {
|
||||
return Entry.Name < N;
|
||||
});
|
||||
if (Index < Registry.Num() && Registry[Index].Name == Name)
|
||||
{
|
||||
return &Registry[Index];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FString UWingFactories::DeriveFactoryName(UClass* FactoryClass)
|
||||
FString WingFactories::DeriveFactoryName(UClass* FactoryClass)
|
||||
{
|
||||
FString Name = FactoryClass->GetName();
|
||||
if (Name.EndsWith(TEXT("FactoryNew")))
|
||||
int32 Index = Name.Find(TEXT("Factory"));
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
Name.LeftChopInline(10);
|
||||
Name.LeftInline(Index);
|
||||
}
|
||||
else if (Name.EndsWith(TEXT("Factory")))
|
||||
if (Name.EndsWith(TEXT("_")))
|
||||
{
|
||||
Name.LeftChopInline(7);
|
||||
Name.LeftChopInline(1);
|
||||
}
|
||||
return Name;
|
||||
}
|
||||
|
||||
UObject* UEnumFactoryWing::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
|
||||
{
|
||||
if (!FEnumEditorUtils::IsNameAvailebleForUserDefinedEnum(Name))
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Enum name is already taken: %s\n"), *Name.ToString());
|
||||
return nullptr;
|
||||
}
|
||||
return FEnumEditorUtils::CreateUserDefinedEnum(InParent, Name, Flags);
|
||||
}
|
||||
|
||||
|
||||
234
Plugins/UEWingman/Source/UEWingman/Private/WingPropHandle.cpp
Normal file
234
Plugins/UEWingman/Source/UEWingman/Private/WingPropHandle.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
#include "WingPropHandle.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingActorComponent.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 "DetailTreeNode.h"
|
||||
#include "PropertyNode.h"
|
||||
#include "ObjectPropertyNode.h"
|
||||
#include "PropertyNode.h"
|
||||
#include "ObjectPropertyNode.h"
|
||||
#include "ItemPropertyNode.h"
|
||||
#include "CategoryPropertyNode.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IsSubObject
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool WingPropHandle::IsSubObject(const TSharedRef<IDetailTreeNode>& Node)
|
||||
{
|
||||
FDetailTreeNode& DetailNode = static_cast<FDetailTreeNode&>(*Node);
|
||||
TSharedPtr<FPropertyNode> PropNode = DetailNode.GetPropertyNode();
|
||||
if (!PropNode.IsValid()) return false;
|
||||
|
||||
FPropertyNode* Current = PropNode.Get();
|
||||
while (true)
|
||||
{
|
||||
if (Current == nullptr) return false;
|
||||
FPropertyNode *Parent = Current->GetParentNode();
|
||||
if (Current->AsObjectNode()) return (Parent != nullptr);
|
||||
Current = Parent;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Get Generator
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TSharedRef<IPropertyRowGenerator> WingPropHandle::CreateGenerator()
|
||||
{
|
||||
FPropertyEditorModule& Module = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
||||
FPropertyRowGeneratorArgs Args;
|
||||
Args.bShouldShowHiddenProperties = false;
|
||||
return Module.CreatePropertyRowGenerator(Args);
|
||||
}
|
||||
|
||||
TSharedRef<IPropertyRowGenerator> WingPropHandle::GetGeneratorForObject(UObject* Obj)
|
||||
{
|
||||
for (auto& Pair : Generators)
|
||||
{
|
||||
if (Pair.Key == Obj) return Pair.Value.ToSharedRef();
|
||||
}
|
||||
TSharedRef<IPropertyRowGenerator> Gen = CreateGenerator();
|
||||
Gen->SetObjects({Obj});
|
||||
Generators.Add({Obj, Gen});
|
||||
return Gen;
|
||||
}
|
||||
|
||||
TSharedRef<IPropertyRowGenerator> WingPropHandle::GetGeneratorForStruct(const UStruct* ScriptStruct, uint8* Data)
|
||||
{
|
||||
for (auto& Pair : Generators)
|
||||
{
|
||||
if (Pair.Key == Data) return Pair.Value.ToSharedRef();
|
||||
}
|
||||
TSharedRef<IPropertyRowGenerator> Gen = CreateGenerator();
|
||||
TSharedPtr<FStructOnScope> Wrapper = MakeShared<FStructOnScope>(ScriptStruct, Data);
|
||||
Gen->SetStructure(Wrapper);
|
||||
Generators.Add({Data, Gen});
|
||||
return Gen;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// All Tree Nodes
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void WingPropHandle::AllTreeNodesRecursive(const TSharedRef<IDetailTreeNode>& Node, FlatTree& Out)
|
||||
{
|
||||
if (IsSubObject(Node)) return;
|
||||
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(TSharedRef<IPropertyRowGenerator> Generator)
|
||||
{
|
||||
FlatTree Result;
|
||||
for (const TSharedRef<IDetailTreeNode>& Root : Generator->GetRootTreeNodes())
|
||||
{
|
||||
AllTreeNodesRecursive(Root, Result);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// AllProperties
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
WingPropHandle::Handles WingPropHandle::AllProperties(TSharedRef<IPropertyRowGenerator> Generator, EPropertyFlags Filter)
|
||||
{
|
||||
Handles Result;
|
||||
for (IDetailTreeNode* Node : AllTreeNodes(Generator))
|
||||
{
|
||||
TSharedPtr<IPropertyHandle> Handle = Node->CreatePropertyHandle();
|
||||
if (Handle.IsValid() && Handle->GetProperty())
|
||||
{
|
||||
if (Filter == CPF_None || Handle->GetProperty()->HasAllPropertyFlags(Filter))
|
||||
{
|
||||
Result.Add(Handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
WingPropHandle::Handles WingPropHandle::AllProperties(UObject* Obj, EPropertyFlags Filter)
|
||||
{
|
||||
if (!Obj) return {};
|
||||
return AllProperties(GetGeneratorForObject(Obj), Filter);
|
||||
}
|
||||
|
||||
WingPropHandle::Handles WingPropHandle::AllProperties(const UStruct* ScriptStruct, uint8* Data, EPropertyFlags Filter)
|
||||
{
|
||||
if (!ScriptStruct || !Data) return {};
|
||||
return AllProperties(GetGeneratorForStruct(ScriptStruct, Data), Filter);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Named Property
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(TSharedRef<IPropertyRowGenerator> Generator, FName Name)
|
||||
{
|
||||
for (IDetailTreeNode* Node : AllTreeNodes(Generator))
|
||||
{
|
||||
if (Node->GetNodeName() == Name)
|
||||
return Node->CreatePropertyHandle();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(UObject* Obj, FName Name)
|
||||
{
|
||||
if (!Obj) return nullptr;
|
||||
return NamedProperty(GetGeneratorForObject(Obj), Name);
|
||||
}
|
||||
|
||||
TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(const UStruct* ScriptStruct, uint8* Data, FName Name)
|
||||
{
|
||||
if (!ScriptStruct || !Data) return nullptr;
|
||||
return NamedProperty(GetGeneratorForStruct(ScriptStruct, Data), Name);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// GetDetails
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
WingPropHandle::Handles WingPropHandle::GetDetails(UObject* Obj, bool Mutable, EPropertyFlags Filter)
|
||||
{
|
||||
if (!Obj) return {};
|
||||
|
||||
// Blueprints: redirect to the generated class CDO.
|
||||
if (UBlueprint* BP = Cast<UBlueprint>(Obj))
|
||||
{
|
||||
if (!BP->GeneratedClass)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Blueprint '%s' has no GeneratedClass\n"), *Obj->GetName());
|
||||
return {};
|
||||
}
|
||||
Obj = BP->GeneratedClass->GetDefaultObject();
|
||||
}
|
||||
|
||||
// Component references: redirect to the template.
|
||||
if (UWingComponentReference* Ref = Cast<UWingComponentReference>(Obj))
|
||||
{
|
||||
Obj = Mutable ? Ref->GetMutableTemplate() : Ref->GetImmutableTemplate();
|
||||
if (!Obj)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Component '%s' has no template\n"), *Ref->VariableName.ToString());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Handles Result = AllProperties(Obj, Filter);
|
||||
|
||||
// Material graph nodes: also collect expression properties.
|
||||
if (UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(Obj))
|
||||
{
|
||||
if (UMaterialExpression* Expr = MatNode->MaterialExpression)
|
||||
{
|
||||
Result.Append(AllProperties(Expr, Filter));
|
||||
}
|
||||
}
|
||||
|
||||
// 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, Filter));
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
@@ -454,10 +454,10 @@ void UWingServer::ClientThreadFunc(UWingServer* Server, TSharedPtr<FClientConnec
|
||||
|
||||
void UWingServer::AddHandler(UObject* Obj, const FString& Documentation)
|
||||
{
|
||||
AddHandler(Obj, WingUtils::GetHandlerName(Obj->GetClass()), Documentation, nullptr, EWingHandlerKind::Normal);
|
||||
AddHandler(Obj, WingUtils::GetHandlerName(Obj->GetClass()), nullptr, EWingHandlerKind::Normal, Documentation);
|
||||
}
|
||||
|
||||
void UWingServer::AddHandler(UObject* Obj, const FString& Name, const FString& Documentation, UObject* Config, EWingHandlerKind Kind)
|
||||
void UWingServer::AddHandler(UObject* Obj, const FString& Name, UObject* Config, EWingHandlerKind Kind, const FString& Documentation)
|
||||
{
|
||||
FWingHandlerConfig H;
|
||||
H.Name = Name;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingTokenizer.h"
|
||||
#include "PropertyHandle.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "Engine/MemberReference.h"
|
||||
#include "Engine/World.h"
|
||||
@@ -56,6 +57,7 @@
|
||||
// ============================================================
|
||||
|
||||
FName WingUtils::GetFName(const FWingProperty &Prop) { return Prop.Prop->GetFName(); }
|
||||
FName WingUtils::GetFName(const TSharedPtr<IPropertyHandle> &H) { return H->GetProperty()->GetFName(); }
|
||||
|
||||
FString WingUtils::ExternalizeID(FName Name)
|
||||
{
|
||||
|
||||
@@ -1,47 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "EditorSubsystem.h"
|
||||
#include "Factories/Factory.h"
|
||||
#include "WingHandler.h"
|
||||
#include "Factories/EnumFactory.h"
|
||||
#include "WingFactories.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UWingFactories : public UEditorSubsystem
|
||||
|
||||
|
||||
class WingFactories
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
struct Info
|
||||
{
|
||||
FString Name;
|
||||
UClass* FactoryClass = nullptr;
|
||||
TArray<FName> Config;
|
||||
bool Disabled = false;
|
||||
bool CanCreateNew() const;
|
||||
};
|
||||
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
virtual void Deinitialize() override {}
|
||||
|
||||
static const TArray<Info>& AllFactories();
|
||||
|
||||
// Create an asset on disk, using a factory instance. Returns the main object.
|
||||
// If there are problems, prints error messages and returns nullptr.
|
||||
// Create an asset on disk, using a factory instance.
|
||||
// Returns the main object. If there are problems,
|
||||
// prints error messages and returns nullptr.
|
||||
static UObject *CreateAsset(const FString &Path, UFactory *Factory);
|
||||
|
||||
// Create an asset on disk, looking up the factory by name and configuring it.
|
||||
static UObject *CreateAsset(const FString &Path, const FString &FactoryName, FWingJsonObject &Config);
|
||||
// Some factories are blacklisted, mainly because they
|
||||
// pop up dialog boxes. In those cases, we deal with it
|
||||
// primarily by adding those factories to the blacklist,
|
||||
// and then implementing replacement factories.
|
||||
static bool IsBlacklisted(UClass* FactoryClass);
|
||||
|
||||
// This takes a factory name and turns it into a string
|
||||
// that we can present to the user. Mainly, it removes
|
||||
// the word 'Factory', and anything that comes after.
|
||||
static FString DeriveFactoryName(UClass* FactoryClass);
|
||||
|
||||
private:
|
||||
TArray<Info> Registry;
|
||||
|
||||
Info* Find(const FString& Name);
|
||||
static UPackage *CreateNewPackage(const FString &Path);
|
||||
// Verifies that the asset path is a valid path, and also
|
||||
// that there's not something already there at that path.
|
||||
static bool CheckNewAssetPath(const FString &Path);
|
||||
void PopulateRegistry();
|
||||
void DisableFactory(const TCHAR* Name);
|
||||
static bool PreCheck(UFactory *Factory, FName Name, const FString &Path);
|
||||
};
|
||||
|
||||
// The original UEnumFactory may pop a dialog. We made a better one.
|
||||
UCLASS()
|
||||
class UEnumFactoryWing : public UEnumFactory
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
|
||||
};
|
||||
@@ -5,7 +5,6 @@
|
||||
struct FToolMenuEntry;
|
||||
struct FToolUIActionChoice;
|
||||
class FBlueprintEditor;
|
||||
|
||||
// Utility class that uses the C++ template explicit-instantiation
|
||||
// loophole to access private members of engine classes.
|
||||
// See WingHacks.cpp for details on the technique.
|
||||
|
||||
61
Plugins/UEWingman/Source/UEWingman/Public/WingPropHandle.h
Normal file
61
Plugins/UEWingman/Source/UEWingman/Public/WingPropHandle.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "IPropertyRowGenerator.h"
|
||||
#include "PropertyHandle.h"
|
||||
|
||||
// WingPropHandle: A wrapper around IPropertyRowGenerator that
|
||||
// provides easy access to IPropertyHandle objects.
|
||||
//
|
||||
// Usage:
|
||||
// WingPropHandle Props;
|
||||
// TArray<TSharedPtr<IPropertyHandle>> Handles = Props.AddObject(MyObject);
|
||||
//
|
||||
// The generators and all handles are valid for the lifetime of
|
||||
// this object.
|
||||
|
||||
class WingPropHandle
|
||||
{
|
||||
public:
|
||||
using Handles = TArray<TSharedPtr<IPropertyHandle>>;
|
||||
using FlatTree = TArray<IDetailTreeNode *, TInlineAllocator<500>>;
|
||||
|
||||
WingPropHandle() {}
|
||||
|
||||
// Get all properties of a UObject. Returns the handles.
|
||||
// Only properties that have all the specified flags are included.
|
||||
Handles AllProperties(UObject* Obj, EPropertyFlags Filter = CPF_None);
|
||||
|
||||
// Get all properties of a struct. Does not copy — the data
|
||||
// pointer must remain valid for the lifetime of this object.
|
||||
// Only properties that have all the specified flags are included.
|
||||
Handles AllProperties(const UStruct* ScriptStruct, uint8* Data, EPropertyFlags Filter = CPF_None);
|
||||
|
||||
// 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, EPropertyFlags Filter = CPF_None);
|
||||
|
||||
// Get a single named property from a UObject.
|
||||
TSharedPtr<IPropertyHandle> NamedProperty(UObject* Obj, FName Name);
|
||||
|
||||
// Get a single named property from a struct.
|
||||
TSharedPtr<IPropertyHandle> NamedProperty(const UStruct* ScriptStruct, uint8* Data, FName Name);
|
||||
|
||||
private:
|
||||
TArray<TPair<void*, TSharedPtr<IPropertyRowGenerator>>> Generators;
|
||||
|
||||
// Check whether a detail tree node's property comes from a sub-object.
|
||||
static bool IsSubObject(const TSharedRef<IDetailTreeNode>& Node);
|
||||
|
||||
static TSharedRef<IPropertyRowGenerator> CreateGenerator();
|
||||
TSharedRef<IPropertyRowGenerator> GetGeneratorForObject(UObject* Obj);
|
||||
TSharedRef<IPropertyRowGenerator> GetGeneratorForStruct(const UStruct* ScriptStruct, uint8* Data);
|
||||
|
||||
static void AllTreeNodesRecursive(const TSharedRef<IDetailTreeNode>& Node, FlatTree& Out);
|
||||
static FlatTree AllTreeNodes(TSharedRef<IPropertyRowGenerator> Generator);
|
||||
|
||||
Handles AllProperties(TSharedRef<IPropertyRowGenerator> Generator, EPropertyFlags Filter);
|
||||
static TSharedPtr<IPropertyHandle> NamedProperty(TSharedRef<IPropertyRowGenerator> Generator, FName Name);
|
||||
};
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
|
||||
// ----- Tool dispatch -----
|
||||
static void AddHandler(UObject* Obj, const FString& Documentation);
|
||||
static void AddHandler(UObject* Obj, const FString& Name, const FString& Documentation, UObject* Config, EWingHandlerKind Kind);
|
||||
static void AddHandler(UObject* Obj, const FString& Name, UObject* Config, EWingHandlerKind Kind, const FString& Documentation);
|
||||
static const TArray<FWingHandlerConfig>& AllHandlers() { return GWingServer->WingHandlerRegistry; }
|
||||
|
||||
private:
|
||||
|
||||
@@ -24,6 +24,7 @@ class UScriptStruct;
|
||||
class UEnum;
|
||||
struct FBPInterfaceDescription;
|
||||
struct FWingProperty;
|
||||
class IPropertyHandle;
|
||||
|
||||
#include "Engine/World.h"
|
||||
#include "Materials/Material.h"
|
||||
@@ -65,6 +66,7 @@ public:
|
||||
static FName GetFName(const UWingComponentReference *Ref) { return Ref->VariableName; }
|
||||
static FName GetFName(const UWidget *Widget) { return Widget->GetFName(); }
|
||||
static FName GetFName(const WingVariables::Var &Var) { return Var.Name; }
|
||||
static FName GetFName(const TSharedPtr<IPropertyHandle> &H);
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//
|
||||
|
||||
@@ -6,6 +6,10 @@ public class UEWingman : ModuleRules
|
||||
{
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
// Needed for debug dump of FPropertyNode tree (private headers)
|
||||
PrivateIncludePaths.Add(System.IO.Path.Combine(EngineDirectory, "Source/Editor/PropertyEditor/Private"));
|
||||
|
||||
|
||||
PublicDependencyModuleNames.AddRange(new string[]
|
||||
{
|
||||
"Core",
|
||||
@@ -34,7 +38,9 @@ public class UEWingman : ModuleRules
|
||||
"SlateCore",
|
||||
"ToolMenus",
|
||||
"UMG",
|
||||
"UMGEditor"
|
||||
"UMGEditor",
|
||||
"Blutility",
|
||||
"PropertyEditor"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user