More work on properties

This commit is contained in:
2026-04-03 15:52:07 -04:00
parent c19091ef1f
commit a0c5a56476
7 changed files with 44 additions and 212 deletions

View File

@@ -5,6 +5,7 @@
#include "WingServer.h" #include "WingServer.h"
#include "WingHandler.h" #include "WingHandler.h"
#include "WingFactories.h" #include "WingFactories.h"
#include "WingPropHandle.h"
#include "WingTypes.h" #include "WingTypes.h"
#include "Factories/BlueprintFactory.h" #include "Factories/BlueprintFactory.h"
#include "Factories/BlueprintMacroFactory.h" #include "Factories/BlueprintMacroFactory.h"
@@ -73,31 +74,26 @@ public:
// Make the factory. // Make the factory.
UClass *FactoryClass = Cast<UClass>(ConfigurationObject); UClass *FactoryClass = Cast<UClass>(ConfigurationObject);
UFactory *Factory = NewObject<UFactory>(GetTransientPackage(), FactoryClass); UFactory *Factory = NewObject<UFactory>(GetTransientPackage(), FactoryClass);
if (Factory == nullptr)
// 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")); UWingServer::Printf(TEXT("ERROR: factory creation failed (bug)\n"));
return; return;
} }
// Store the parentclass, if specified. // Get the 'ParentClass' property.
if (ParentClassObj) WingPropHandle Props;
TSharedPtr<IPropertyHandle> PCProp = Props.NamedProperty(Factory, TEXT("ParentClass"), true);
if (!PCProp) return;
// Store the parent class.
FPropertyAccess::Result SetResult = PCProp->SetValue(ParentClassObj);
if (SetResult != FPropertyAccess::Result::Success)
{ {
if (!ParentClassObj->IsChildOf(CProp->MetaClass)) UWingServer::Printf(TEXT("ERROR: property does not allow value: %s\n"), *ParentClass);
{ return;
UWingServer::Printf(TEXT("ParentClass must be child of %s.\n"),
*WingUtils::FormatName(CProp->MetaClass));
return;
}
CProp->SetObjectPropertyValue_InContainer(Factory, ParentClassObj);
} }
// Create the asset. // Create the asset using the factory.
UObject *Blueprint = WingFactories::CreateAsset(Path, Factory); UObject *Blueprint = WingFactories::CreateAsset(Path, Factory);
if (Blueprint == nullptr) return; if (Blueprint == nullptr) return;
UWingServer::Printf(TEXT("Created.\n")); UWingServer::Printf(TEXT("Created.\n"));

View File

@@ -5,7 +5,7 @@
#include "WingServer.h" #include "WingServer.h"
#include "WingHandler.h" #include "WingHandler.h"
#include "WingFactories.h" #include "WingFactories.h"
#include "WingProperty.h" #include "WingPropHandle.h"
#include "Factories/BlueprintFunctionLibraryFactory.h" #include "Factories/BlueprintFunctionLibraryFactory.h"
#include "Create_UsingFactory.generated.h" #include "Create_UsingFactory.generated.h"
@@ -30,7 +30,8 @@ public:
UFactory* CDO = Class->GetDefaultObject<UFactory>(); UFactory* CDO = Class->GetDefaultObject<UFactory>();
if (!CDO->CanCreateNew() || !CDO->ShouldShowInNewMenu()) continue; if (!CDO->CanCreateNew() || !CDO->ShouldShowInNewMenu()) continue;
TArray<FName> ConfigProps = FWingProperty::GetNames(Class, CPF_Edit); WingPropHandle Props;
TArray<TSharedPtr<IPropertyHandle>> ConfigProps = Props.AllProperties(CDO, true, CPF_Edit);
if (ConfigProps.Num() > 0) continue; if (ConfigProps.Num() > 0) continue;
FString FactoryName = WingFactories::DeriveFactoryName(Class); FString FactoryName = WingFactories::DeriveFactoryName(Class);

View File

@@ -1,75 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "WingServer.h"
#include "WingHandler.h"
#include "WingFetcher.h"
#include "WingProperty.h"
#include "WingTypes.h"
#include "WingUtils.h"
#include "Property_Dump.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_Property_Dump : public UWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Target object"))
FString Object;
UPROPERTY(meta=(Optional, Description="Substring filter for property names"))
FString Query;
UPROPERTY(meta=(Optional, Description="Only show properties declared on the object's own class, not inherited ones"))
bool Local = false;
virtual void Register() override
{
UWingServer::AddHandler(this,
TEXT("List all blueprint-visible properties, showing current values and which are editable."));
}
virtual void Handle() override
{
// Resolve the path to an object and get its editable template.
WingFetcher F;
UObject* Template = F.Walk(Object).Cast<UObject>();
if (!Template) return;
TArray<FWingProperty> AllProps = FWingProperty::GetDetailsImmutable(Template, CPF_Edit);
TArray<FWingProperty> Props = FWingProperty::FindAllSubstring(AllProps, Query);
if (Local)
{
UClass* ObjClass = Template->GetClass();
Props.RemoveAll([ObjClass](const FWingProperty& P) { return P->GetOwnerStruct() != ObjClass; });
}
if (Props.IsEmpty())
{
UWingServer::Print(TEXT(" (no blueprint-visible properties found)\n"));
return;
}
FString CurrentCategory;
for (FWingProperty& P : Props)
{
FString Category = P.GetCategory();
if (Category != CurrentCategory)
{
CurrentCategory = Category;
UWingServer::Printf(TEXT("\n%s:\n"), *CurrentCategory);
}
bool bEditable = !P->HasAnyPropertyFlags(CPF_EditConst);
UWingServer::Printf(TEXT(" %s %s %s = %s\n"),
bEditable ? TEXT("editable") : TEXT("readonly"),
*UWingTypes::TypeToText(P.Prop),
*WingUtils::FormatName(P.Prop),
*P.GetTruncatedText(100));
}
}
};

View File

@@ -1,46 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "WingServer.h"
#include "WingHandler.h"
#include "WingFetcher.h"
#include "WingProperty.h"
#include "WingUtils.h"
#include "Property_Get.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_Property_Get : public UWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Target object"))
FString Object;
UPROPERTY(meta=(Description="Property name"))
FString Property;
virtual void Register() override
{
UWingServer::AddHandler(this,
TEXT("Get the value of a single property."));
}
virtual void Handle() override
{
WingFetcher F;
UObject* Obj = F.Walk(Object).Cast<UObject>();
if (!Obj) return;
TArray<FWingProperty> All = FWingProperty::GetDetailsMutable(Obj, CPF_Edit);
FWingProperty *P = WingUtils::FindOneWithExternalID(Property, All, TEXT("Property"));
if (!P) return;
UWingServer::Print(P->GetText());
UWingServer::Print(TEXT("\n"));
}
};

View File

@@ -1,64 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "WingServer.h"
#include "WingHandler.h"
#include "WingFetcher.h"
#include "WingProperty.h"
#include "WingUtils.h"
#include "Property_Set.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_Property_Set : public UWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Target object"))
FString Object;
UPROPERTY(meta=(Description="Object mapping property names to new values in Unreal text format"))
FWingJsonObject Properties;
virtual void Register() override
{
UWingServer::AddHandler(this,
TEXT("Set one or more editable properties. Values use Unreal text format."));
}
virtual void Handle() override
{
// Resolve the path to an object and get its editable template.
WingFetcher F;
UObject* Obj = F.Walk(Object).Cast<UObject>();
if (!Obj) return;
if (!Properties.Json || Properties.Json->Values.Num() == 0)
{
UWingServer::Print(TEXT("Error: No properties specified\n"));
return;
}
TArray<FWingProperty> All = FWingProperty::GetDetailsMutable(Obj, CPF_Edit);
int SuccessCount = 0;
// Validation pass — resolve all properties and values before modifying anything.
for (const auto& Pair : Properties.Json->Values)
{
FWingProperty *P = WingUtils::FindOneWithExternalID(Pair.Key, All, TEXT("Property"));
if (!P) return;
}
// Assignment Pass - store the values.
for (const auto& Pair : Properties.Json->Values)
{
FWingProperty *P = WingUtils::FindOneWithExternalID(Pair.Key, All, TEXT("Property"));
if (P && P->SetJson(Pair.Value)) SuccessCount++;
}
UWingServer::Printf(TEXT("Set %d/%d properties.\n"), SuccessCount, Properties.Json->Values.Num());
}
};

View File

@@ -164,7 +164,7 @@ WingPropHandle::Handles WingPropHandle::AllProperties(const UStruct* ScriptStruc
// //
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(Root& Root, FName Name, bool RootFilter) TSharedPtr<IPropertyHandle> WingPropHandle::TryNamedProperty(Root& Root, FName Name, bool RootFilter)
{ {
for (IDetailTreeNode* Node : AllTreeNodes(Root)) for (IDetailTreeNode* Node : AllTreeNodes(Root))
{ {
@@ -176,16 +176,32 @@ TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(Root& Root, FName Name
return nullptr; return nullptr;
} }
TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(UObject* Obj, FName Name, bool RootFilter) TSharedPtr<IPropertyHandle> WingPropHandle::TryNamedProperty(UObject* Obj, FName Name, bool RootFilter)
{ {
if (!Obj) return nullptr; if (!Obj) return nullptr;
return NamedProperty(GetRootForObject(Obj), Name, RootFilter); 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)
{
TSharedPtr<IPropertyHandle> Result = TryNamedProperty(Obj, Name, RootFilter);
if (!Result)
UWingServer::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) TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(const UStruct* ScriptStruct, uint8* Data, FName Name, bool RootFilter)
{ {
if (!ScriptStruct || !Data) return nullptr; TSharedPtr<IPropertyHandle> Result = TryNamedProperty(ScriptStruct, Data, Name, RootFilter);
return NamedProperty(GetRootForStruct(ScriptStruct, Data), Name, RootFilter); if (!Result)
UWingServer::Printf(TEXT("ERROR: Property '%s' not found\n"), *Name.ToString());
return Result;
} }
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////

View File

@@ -36,10 +36,14 @@ public:
// Get a single named property from a UObject. // Get a single named property from a UObject.
// If RootFilter is true, only properties inside the root object are returned. // If RootFilter is true, only properties inside the root object are returned.
TSharedPtr<IPropertyHandle> NamedProperty(UObject* Obj, FName Name, bool RootFilter); TSharedPtr<IPropertyHandle> TryNamedProperty(UObject* Obj, FName Name, bool RootFilter);
// Get a single named property from a struct. // Get a single named property from a struct.
// If RootFilter is true, only properties inside the root object are returned. // 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);
TSharedPtr<IPropertyHandle> NamedProperty(const UStruct* ScriptStruct, uint8* Data, FName Name, bool RootFilter); TSharedPtr<IPropertyHandle> NamedProperty(const UStruct* ScriptStruct, uint8* Data, FName Name, bool RootFilter);
// Get "details panel" properties for an object. Handles special // Get "details panel" properties for an object. Handles special
@@ -79,5 +83,5 @@ private:
static FlatTree AllTreeNodes(Root& Root); static FlatTree AllTreeNodes(Root& Root);
Handles AllProperties(Root& Root, bool RootFilter, EPropertyFlags Filter); Handles AllProperties(Root& Root, bool RootFilter, EPropertyFlags Filter);
static TSharedPtr<IPropertyHandle> NamedProperty(Root& Root, FName Name, bool RootFilter); static TSharedPtr<IPropertyHandle> TryNamedProperty(Root& Root, FName Name, bool RootFilter);
}; };