WingProperty now contains an object pointer. This made it possible to implement structprop a lot better, it now can dig into objects it couldn't dig into before, including structprop-within-structprop

This commit is contained in:
2026-04-27 19:06:57 -04:00
parent 9598004e6d
commit ae0defbad9
13 changed files with 68 additions and 101 deletions

View File

@@ -281,7 +281,7 @@ public:
}
// Set the 'Class' property.
TArray<FWingProperty> Props = FWingProperty::GetVisible(Factory);
TArray<FWingProperty> Props = FWingProperty::GetVisible(Factory, true);
FWingProperty::Remove(Props, TEXT("BlueprintType"));
if (Props.Num() != 1)
{

View File

@@ -65,7 +65,7 @@ public:
// Parse the json array, turning it into an array of spawn node entries.
TArray<FSpawnNodeEntry> Entries;
FSpawnNodeEntry Entry;
TArray<FWingProperty> Props = FWingProperty::GetAll(&Entry);
TArray<FWingProperty> Props = FWingProperty::GetAll(nullptr, &Entry, FSpawnNodeEntry::StaticStruct(), true);
for (const TSharedPtr<FJsonValue>& Elt : Nodes.Array)
{
if (!FWingProperty::PopulateFromJson(Props, *Elt, false, WingOut::Stdout)) return;

View File

@@ -123,7 +123,7 @@ public:
}
FSetNodeDefaultEntry Entry;
TArray<FWingProperty> Props = FWingProperty::GetAll(&Entry);
TArray<FWingProperty> Props = FWingProperty::GetAll(nullptr, &Entry, FSetNodeDefaultEntry::StaticStruct(), true);
for (const TSharedPtr<FJsonValue>& PinVal : Pins.Array)
{
if (!FWingProperty::PopulateFromJson(Props, *PinVal, false, WingOut::Stdout)) continue;

View File

@@ -56,7 +56,7 @@ public:
int32 SuccessCount = 0;
FMoveNodeEntry Entry;
TArray<FWingProperty> Props = FWingProperty::GetAll(&Entry);
TArray<FWingProperty> Props = FWingProperty::GetAll(nullptr, &Entry, FMoveNodeEntry::StaticStruct(), true);
for (const TSharedPtr<FJsonValue>& Elt : Nodes.Array)
{
if (!FWingProperty::PopulateFromJson(Props, *Elt, false, WingOut::Stdout)) continue;

View File

@@ -58,7 +58,7 @@ public:
int32 TotalCount = Connections.Array.Num();
FConnectPinsEntry Entry;
TArray<FWingProperty> EntryProps = FWingProperty::GetAll(&Entry);
TArray<FWingProperty> EntryProps = FWingProperty::GetAll(nullptr, &Entry, FConnectPinsEntry::StaticStruct(), true);
for (const TSharedPtr<FJsonValue>& ConnVal : Connections.Array)
{
if (!FWingProperty::PopulateFromJson(EntryProps, *ConnVal, false, WingOut::Stdout))

View File

@@ -1,6 +1,7 @@
#include "WingFetcher.h"
#include "WingServer.h"
#include "WingBasics.h"
#include "WingProperty.h"
#include "WingUtils.h"
#include "WingComponent.h"
#include "Engine/Blueprint.h"
@@ -368,36 +369,27 @@ WingFetcher& WingFetcher::StructProp(const FString& Value)
return SetError();
}
FStructProperty* StructProp = nullptr;
TArray<FWingProperty> Details =
FWingProperty::GetDetails(Obj.Get(), true);
// The "host" is the object containing this property.
UObject *HostObject = Obj.Get();
void *HostBase = Obj.Get();
UStruct *HostType = Obj.Get()->GetClass();
bool HostEditable = true;
FWingProperty *WProp = WingUtils::FindOneWithInternalID(
InternalID, Details, TEXT("Property"), WingOut::Stdout);
if (!WProp) return SetError();
// If we are *already* inside a UWingStructRef, update the host
// fields, to make it possible to navigate even further inside.
if (UWingStructRef *SPtr = ::Cast<UWingStructRef>(Obj.Get()))
if (FStructProperty *FSProp = CastField<FStructProperty>(WProp->Prop))
{
HostObject = SPtr->Object;
HostBase = SPtr->StructBase;
HostType = SPtr->StructType;
HostEditable = SPtr->Editable;
UWingStructRef* Ptr = NewObject<UWingStructRef>();
Ptr->Object = WProp->Object.Get();
Ptr->StructType = FSProp->Struct;
Ptr->StructBase = FSProp->ContainerPtrToValuePtr<void>(WProp->Container);
Ptr->Editable = WProp->Editable;
SetObj(Ptr);
return *this;
}
StructProp = FindFProperty<FStructProperty>(HostType, InternalID);
if (!StructProp)
else
{
Errors.Printf(TEXT("ERROR: No struct property '%s' found on %s\n"), *Value, *HostType->GetName());
Errors.Printf(TEXT("Property %s is not a struct property.\n"),
*WProp->Prop->GetName());
return SetError();
}
UWingStructRef* Ptr = NewObject<UWingStructRef>();
Ptr->Object = HostObject;
Ptr->StructType = StructProp->Struct;
Ptr->StructBase = StructProp->ContainerPtrToValuePtr<void>(HostBase);
Ptr->Editable = HostEditable && StructProp->HasAllPropertyFlags(CPF_Edit);
SetObj(Ptr);
return *this;
}

View File

@@ -265,7 +265,7 @@ bool FWingParameterEditor::AddOverride(
// Parse the value string.
FWingParameterEditor Editor;
if (!FWingProperty(GS->Property, &Editor, true).SetText(StrVal, Errors)) return false;
if (!FWingProperty(nullptr, &Editor, GS->Property, true).SetText(StrVal, Errors)) return false;
Meta->Value = GS->Getter(Editor);
// Apply the update.
@@ -308,7 +308,7 @@ void FWingParameterEditor::Print(const Info &ID, const Metadata &Meta)
}
FWingParameterEditor Editor;
GS->Setter(Editor, Meta.Value);
FString StrVal = FWingProperty(GS->Property, &Editor, false).GetText();
FString StrVal = FWingProperty(nullptr, &Editor, GS->Property, false).GetText();
WingOut::Stdout.Printf(TEXT(" %s %s\n"),
*StringID(ID), *StrVal);
}

View File

@@ -378,25 +378,25 @@ FString FWingProperty::GetCategory() const
return Result;
}
TArray<FWingProperty> FWingProperty::GetAll(FWingStructAndUStruct Obj)
TArray<FWingProperty> FWingProperty::GetAll(UObject *Obj, void *Container, UStruct *Struct, bool Mutable)
{
TArray<FWingProperty> Result;
for (TFieldIterator<FProperty> It(Obj.UStructPtr); It; ++It)
for (TFieldIterator<FProperty> It(Struct); It; ++It)
{
bool Editable = !It->HasAnyPropertyFlags(CPF_EditConst);
Result.Add(FWingProperty(*It, Obj.StructPtr, Editable));
bool Editable = Mutable && !It->HasAnyPropertyFlags(CPF_EditConst);
Result.Emplace(Obj, Container, *It, Editable);
}
return Result;
}
TArray<FWingProperty> FWingProperty::GetVisible(FWingStructAndUStruct Obj)
TArray<FWingProperty> FWingProperty::GetVisible(UObject *Obj, void *Container, UStruct *Struct, bool Mutable)
{
TArray<FWingProperty> Result;
for (TFieldIterator<FProperty> It(Obj.UStructPtr); It; ++It)
for (TFieldIterator<FProperty> It(Struct); It; ++It)
{
if (!It->HasAllPropertyFlags(CPF_Edit)) continue;
bool Editable = !It->HasAnyPropertyFlags(CPF_EditConst);
Result.Add(FWingProperty(*It, Obj.StructPtr, Editable));
bool Editable = Mutable && !It->HasAnyPropertyFlags(CPF_EditConst);
Result.Emplace(Obj, Container, *It, Editable);
}
return Result;
}
@@ -436,10 +436,8 @@ TArray<FWingProperty> FWingProperty::GetDetails(UObject* Obj, bool Mutable)
// of the struct instead. Propagate editability of the host.
if (UWingStructRef *SP = Cast<UWingStructRef>(Obj))
{
TArray<FWingProperty> Result =
GetVisible(FWingStructAndUStruct(SP->StructBase, SP->StructType));
if (!Mutable || (!SP->Editable)) StripEditable(Result);
return Result;
return GetVisible(SP->Object, SP->StructBase,
SP->StructType, Mutable && SP->Editable);
}
// Blueprints don't have editable properties. So
@@ -465,7 +463,7 @@ TArray<FWingProperty> FWingProperty::GetDetails(UObject* Obj, bool Mutable)
}
}
TArray<FWingProperty> Result = GetVisible(Obj);
TArray<FWingProperty> Result = GetVisible(Obj, Mutable);
// If it's a Material Graph node, also collect properties from
// the associated material expression.
@@ -474,7 +472,7 @@ TArray<FWingProperty> FWingProperty::GetDetails(UObject* Obj, bool Mutable)
{
if (UMaterialExpression* Expr = MatNode->MaterialExpression)
{
Result.Append(GetVisible(Expr));
Result.Append(GetVisible(Expr, Mutable));
}
}
@@ -486,13 +484,12 @@ TArray<FWingProperty> FWingProperty::GetDetails(UObject* Obj, bool Mutable)
FWingProperty::Remove(Result, TEXT("Slot"));
if (UPanelSlot* Slot = Widget->Slot)
{
Result.Append(GetVisible(Slot));
Result.Append(GetVisible(Slot, Mutable));
}
FProperty *VarProp = Widget->GetClass()->FindPropertyByName(TEXT("bIsVariable"));
if (VarProp) Result.Add(FWingProperty(VarProp, Widget, true));
if (VarProp) Result.Emplace(Widget, Widget, VarProp, true);
}
if (!Mutable) StripEditable(Result);
return Result;
}
@@ -580,11 +577,6 @@ bool FWingProperty::CheckImportTextResult(const FString &Value, WingOut Errors)
return true;
}
void FWingProperty::StripEditable(TArray<FWingProperty> &Props)
{
for (FWingProperty &Elt : Props) Elt.Editable = false;
}
bool FWingProperty::CheckEditable(WingOut Errors) const
{
if (!Editable)

View File

@@ -297,7 +297,7 @@ void UWingServer::TryCallHandler(TSharedPtr<FJsonObject> Request)
Handler->Configuration = Found;
// Populate the handler object with the request parameters.
TArray<FWingProperty> Props = FWingProperty::GetVisible(Handler);
TArray<FWingProperty> Props = FWingProperty::GetVisible(Handler, true);
if (!FWingProperty::PopulateFromJson(Props, *Request, false, WingOut::Stdout))
{
UWingServer::SuggestHandlerHelp();

View File

@@ -325,7 +325,7 @@ WingVariables::Var WingVariables::LoadBlueprintVariableDescription(FBPVariableDe
FProperty* Prop = CDO->GetClass()->FindPropertyByName(Desc.VarName);
if (Prop)
{
Result.DefaultValue = FWingProperty(Prop, CDO, false).GetText();
Result.DefaultValue = FWingProperty(CDO, CDO, Prop, false).GetText();
Result.DefaultSpecified = true;
}
}
@@ -500,7 +500,7 @@ bool WingVariables::ModifyBlueprintDefaults(WingOut Errors)
*WingTokenizer::ExternalizeID(Input.Name));
return false;
}
if (!FWingProperty(Prop, CDO, true).SetText(Input.DefaultValue, Errors)) return false;
if (!FWingProperty(CDO, CDO, Prop, true).SetText(Input.DefaultValue, Errors)) return false;
}
}
return true;

View File

@@ -129,38 +129,6 @@ private:
FStringBuilderBase *Buffer;
};
////////////////////////////////////////////////////////////
//
// FWingStructAndUStruct.
//
// A pointer to a struct, and also a pointer to a UStruct
// that describes the struct. This also can store a
// UObject and the UClass that describes the UObject.
//
////////////////////////////////////////////////////////////
// A FWingStructAndUStruct is a pointer to a struct and its associated ustruct.
//
struct FWingStructAndUStruct
{
void *StructPtr;
UStruct *UStructPtr;
// Explicit constructor.
explicit FWingStructAndUStruct(void *Base, UStruct *S) : StructPtr(Base), UStructPtr(S) {}
// Copy constructor.
FWingStructAndUStruct(FWingStructAndUStruct &Src) : StructPtr(Src.StructPtr), UStructPtr(Src.UStructPtr) {}
// Construct from a UObject.
FWingStructAndUStruct(UObject *Obj) : StructPtr(Obj), UStructPtr(Obj->GetClass()) {}
// Construct from a UStruct pointer.
template<class T, typename = std::enable_if_t<!std::is_base_of_v<UObject, T>>>
FWingStructAndUStruct(T *Struct) : StructPtr(Struct), UStructPtr(Struct->StaticStruct()) {}
};
////////////////////////////////////////////////////////////
//
// References.
@@ -224,11 +192,11 @@ class UWingStructRef : public UObject
public:
UPROPERTY()
UObject* Object;
void *StructBase;
UPROPERTY()
UStruct* StructType;
void *StructBase;
bool Editable;
};

View File

@@ -3,20 +3,24 @@
#include "CoreMinimal.h"
#include "WingBasics.h"
// A resolved property: the FProperty descriptor plus a pointer to
// the value's storage.
//
struct FWingProperty
{
// To understand the following fields, imagine an object
// that contains a struct, which contains another struct,
// which contains another struct, which contains a field F.
// In that case, Object points to the object that
// contains everything, whereas Container points to the
// innermost struct that contains the property.
FProperty* Prop = nullptr;
TStrongObjectPtr<UObject> Object = nullptr;
void* Container = nullptr;
FProperty* Prop = nullptr;
bool Editable = false;
// Construct a property reference.
//
FWingProperty(FProperty* InProp, void* InContainer, bool Edit)
: Prop(InProp), Container(InContainer), Editable(Edit) {}
FWingProperty(UObject *InObject, void* InContainer, FProperty* InProp, bool Edit)
: Object(InObject), Container(InContainer), Prop(InProp), Editable(Edit) {}
// Construct a null property reference.
//
@@ -72,14 +76,23 @@ struct FWingProperty
//
// This gets the properties that are literally present in the
// specified object or struct. No special interpretation is done.
// If mutable is false, all properties will be marked non-editable.
//
static TArray<FWingProperty> GetAll(FWingStructAndUStruct Obj);
static TArray<FWingProperty> GetAll(UObject *Obj, void *Container, UStruct *Struct, bool Mutable);
// Get all the visible properties of the specified object or struct.
//
// This gets the properties that have CPF_Edit marked on them.
// If mutable is false, all properties will be marked non-editable.
//
static TArray<FWingProperty> GetVisible(FWingStructAndUStruct Obj);
static TArray<FWingProperty> GetVisible(UObject *Obj, void *Container, UStruct *Struct, bool Mutable);
// Convenience versions of GetAll and GetVisible for UObjects.
//
static TArray<FWingProperty> GetAll(UObject *Obj, bool Mutable)
{ return GetAll(Obj, Obj, Obj->GetClass(), Mutable); }
static TArray<FWingProperty> GetVisible(UObject *Obj, bool Mutable)
{ return GetVisible(Obj, Obj, Obj->GetClass(), Mutable); }
// Get just the names of the properties of the specified struct/class.
//
@@ -119,7 +132,6 @@ struct FWingProperty
//
private:
static void StripEditable(TArray<FWingProperty> &Props);
static bool IsUnsigned(FNumericProperty* Prop);
static bool IsPinTypeProperty(FProperty *Prop);
void PrintExpectsReceived(const TCHAR *Type, WingOut Errors) const;