More work on the new variable list functionality

This commit is contained in:
2026-03-27 19:27:49 -04:00
parent 656151ca3b
commit 5aef356199
6 changed files with 255 additions and 8 deletions

View File

@@ -15,6 +15,7 @@
#include "AnimationGraph.h"
#include "AnimationGraphSchema.h"
#include "AnimationStateMachineSchema.h"
#include "WingVariables.h"
#include "WingWidgets.h"
#include "WidgetBlueprint.h"
#include "Blueprint/WidgetTree.h"
@@ -82,6 +83,14 @@ public:
}
}
// Variables (new format)
WingVariables BlueprintVars = WingVariables::ParseBlueprintVariables(BP);
if (!BlueprintVars.IsEmpty())
{
UWingServer::Print(TEXT("\nVariables (new format):\n"));
BlueprintVars.PrintAll(2);
}
// Components
TArray<UWingComponentReference*> Components3 = UWingComponentReference::GetAll(BP);
if (!Components3.IsEmpty())

View File

@@ -11,6 +11,7 @@
#include "K2Node_CallFunction.h"
#include "K2Node_FunctionEntry.h"
#include "WingFunctionArgs.h"
#include "WingVariables.h"
#include "MaterialGraph/MaterialGraphNode.h"
WingGraphExport::WingGraphExport(UEdGraph* InGraph)
@@ -292,13 +293,11 @@ void WingGraphExport::EmitLocalVariables()
UK2Node_FunctionEntry* EntryNode = Cast<UK2Node_FunctionEntry>(Node);
if (!EntryNode) continue;
for (const FBPVariableDescription& Var : EntryNode->LocalVariables)
WingVariables Locals = WingVariables::ParseFunctionLocalVariables(EntryNode);
if (!Locals.IsEmpty())
{
FString Default = Var.DefaultValue.IsEmpty() ? TEXT("<default>") : Var.DefaultValue;
Output.Appendf(TEXT("local %s %s = %s\n"),
*UWingTypes::TypeToText(Var.VarType),
*WingUtils::FormatName(Var),
*Default);
Output.Appendf(TEXT("Function Local Variables:\n"));
Locals.PrintAll(Output, 4);
}
break;
}

View File

@@ -0,0 +1,172 @@
#include "WingVariables.h"
#include "WingServer.h"
#include "WingTypes.h"
#include "WingUtils.h"
#include "EdGraphSchema_K2.h"
#include "K2Node_FunctionEntry.h"
#include "Kismet2/BlueprintEditorUtils.h"
// Flag names used for blueprint variables.
static const FName Flag_InstanceEditable(TEXT("InstanceEditable"));
static const FName Flag_BlueprintReadOnly(TEXT("BlueprintReadOnly"));
static const FName Flag_ExposeOnSpawn(TEXT("ExposeOnSpawn"));
static const FName Flag_Private(TEXT("Private"));
static const FName Flag_ExposeToCinematics(TEXT("ExposeToCinematics"));
static TSet<FName> Flags_None = { };
static TSet<FName> Flags_BlueprintVariables = { Flag_InstanceEditable, Flag_BlueprintReadOnly, Flag_ExposeOnSpawn, Flag_Private, Flag_ExposeToCinematics };
static TSet<FName> Flags_FunctionLocalVariables = { };
const TSet<FName> &WingVariables::GetRelevantFlagSet(WingVariables::Cat C)
{
switch (C)
{
case Cat::Blueprint: return Flags_BlueprintVariables;
case Cat::FunctionLocal: return Flags_FunctionLocalVariables;
default: return Flags_None;
}
}
void WingVariables::Var::AddFlagIfRelevant(FName Flag, WingVariables::Cat C)
{
if (GetRelevantFlagSet(C).Contains(Flag)) Flags.Add(Flag);
}
WingVariables::Var WingVariables::ParseVariableDescription(const FBPVariableDescription &Desc, WingVariables::Cat Category)
{
Var Result;
Result.Name = Desc.VarName;
Result.Type = Desc.VarType;
if (!(Desc.PropertyFlags & CPF_DisableEditOnInstance))
Result.AddFlagIfRelevant(Flag_InstanceEditable, Category);
if (Desc.PropertyFlags & CPF_BlueprintReadOnly)
Result.AddFlagIfRelevant(Flag_BlueprintReadOnly, Category);
if (Desc.PropertyFlags & CPF_Interp)
Result.AddFlagIfRelevant(Flag_ExposeToCinematics, Category);
if (Desc.HasMetaData(FBlueprintMetadata::MD_ExposeOnSpawn))
Result.AddFlagIfRelevant(Flag_ExposeOnSpawn, Category);
if (Desc.HasMetaData(FBlueprintMetadata::MD_Private))
Result.AddFlagIfRelevant(Flag_Private, Category);
return Result;
}
bool WingVariables::CheckSanity(Cat Category)
{
const TSet<FName> &Relevant = GetRelevantFlagSet(Category);
TSet<FName> NamesUsed;
for (const Var &Variable : Variables)
{
FString VarName = WingUtils::SanitizeName(Variable.Name);
FString TypeText = UWingTypes::TypeToText(Variable.Type);
if (NamesUsed.Contains(Variable.Name))
{
UWingServer::Printf(TEXT("Variable name appears twice: %s\n"), *VarName);
return false;
}
if (TypeText.IsEmpty())
{
UWingServer::Printf(TEXT("Type of variable %s is not valid for unknown reasons\n"), *VarName);
return false;
}
if (!UWingTypes::IsBlueprintType(Variable.Type))
{
UWingServer::Printf(TEXT("Type is not a valid BlueprintType: %s %s\n"), *TypeText, *VarName);
return false;
}
for (FName Flag : Variable.Flags)
{
if (!Relevant.Contains(Flag))
{
UWingServer::Printf(TEXT("Flag %s is not valid here. Valid flags are:"), *Flag.ToString());
for (FName Rel : Relevant) UWingServer::Printf(TEXT(" %s\n"), *Rel.ToString());
return false;
}
}
}
return true;
}
WingVariables WingVariables::ParseBlueprintVariables(const UBlueprint *BP)
{
WingVariables Result;
for (const FBPVariableDescription& Desc : BP->NewVariables)
{
// Skip event dispatchers.
if (Desc.VarType.PinCategory == UEdGraphSchema_K2::PC_MCDelegate) continue;
// Parse the bulk of the flags.
Var V = ParseVariableDescription(Desc, Cat::Blueprint);
// Read default value from CDO if available.
if (BP->GeneratedClass)
{
UObject* CDO = BP->GeneratedClass->GetDefaultObject();
FProperty* Prop = BP->GeneratedClass->FindPropertyByName(Desc.VarName);
if (CDO && Prop)
V.DefaultValue = FWingProperty(Prop, CDO).GetText();
}
Result.Variables.Add(MoveTemp(V));
}
return Result;
}
WingVariables WingVariables::ParseFunctionLocalVariables(const UK2Node_FunctionEntry *Func)
{
WingVariables Result;
if (!Func) return Result;
for (const FBPVariableDescription& Desc : Func->LocalVariables)
{
Var V = ParseVariableDescription(Desc, Cat::FunctionLocal);
V.DefaultValue = Desc.DefaultValue;
Result.Variables.Add(MoveTemp(V));
}
return Result;
}
void WingVariables::PrintAll(FStringBuilderBase &Out, int32 Indent)
{
FString Prefix;
for (int32 i = 0; i < Indent; i++) Prefix += TEXT(" ");
for (const Var& V : Variables)
{
FString TypeStr = UWingTypes::TypeToText(V.Type);
FString NameStr = WingUtils::SanitizeName(V.Name);
// Build flags string.
FString FlagsStr;
if (V.Flags.Num() > 0)
{
TArray<FName> Sorted = V.Flags.Array();
Sorted.Sort(FNameLexicalLess());
for (const FName& Flag : Sorted)
{
if (!FlagsStr.IsEmpty()) FlagsStr += TEXT(", ");
FlagsStr += Flag.ToString();
}
}
// Print: type name (flags) = defaultvalue
Out.Appendf(TEXT("%s%s %s"), *Prefix, *TypeStr, *NameStr);
if (!FlagsStr.IsEmpty())
Out.Appendf(TEXT(" (%s)"), *FlagsStr);
if (!V.DefaultValue.IsEmpty())
Out.Appendf(TEXT(" = %s"), *V.DefaultValue);
Out.Append(TEXT("\n"));
}
}
void WingVariables::PrintAll(int32 Indent)
{
PrintAll(UWingServer::GetPrintBuffer(), Indent);
}

View File

@@ -60,6 +60,9 @@ public:
GWingServer->HandlerOutput.Appendf(Fmt, Forward<ArgTypes>(Args)...);
}
/** Get the handler output buffer directly. */
static FStringBuilderBase& GetPrintBuffer() { return GWingServer->HandlerOutput; }
/** Suggest that a manual section be printed after the handler finishes. */
static void SuggestManual(WingManual::Section Section) { GWingServer->SuggestedManualSections.Add(Section); }

View File

@@ -0,0 +1,64 @@
#pragma once
#include "CoreMinimal.h"
#include "WingProperty.h"
#include "Engine/Blueprint.h"
class UK2Node_FunctionEntry;
class WingVariables
{
enum class Cat
{
Blueprint,
FunctionLocal
};
private:
struct Var
{
// Internal name.
FName Name;
// Type. Must be a BlueprintType.
FEdGraphPinType Type;
// Default Value.
FString DefaultValue;
// Boolean flags.
TSet<FName> Flags;
// Add the flag if it's relevant to category C.
void AddFlagIfRelevant(FName Flag, Cat C);
};
private:
// A list of all the variables.
TArray<Var> Variables;
public:
// Returns the set of flags that are supported by this variable category.
static const TSet<FName> &GetRelevantFlagSet(Cat C);
// Check if the variable list is empty.
bool IsEmpty() const { return Variables.IsEmpty(); }
// Print all the variables to a string builder.
void PrintAll(FStringBuilderBase &Out, int32 Indent = 0);
// Print all the variables via UWingServer.
void PrintAll(int32 Indent = 0);
// Make sure that this variable list makes sense for the given context.
bool CheckSanity(Cat Category);
// Parse variables from a given source.
static WingVariables ParseBlueprintVariables(const UBlueprint *BP);
static WingVariables ParseFunctionLocalVariables(const UK2Node_FunctionEntry *F);
private:
static Var ParseVariableDescription(const FBPVariableDescription &V, Cat C);
};