Stricter name validation, and better disambiguation of graph actions
This commit is contained in:
@@ -51,7 +51,7 @@ public:
|
||||
FName InternalID = WingUtils::CheckProposedName(Component, WingOut::Stdout);
|
||||
if (InternalID.IsNone()) return;
|
||||
TSet<FName> Names;
|
||||
FBlueprintEditorUtils::GetClassVariableList(BP, Names);
|
||||
WingUtils::GetAllInUseNames(BP, Names);
|
||||
if (!WingUtils::FindNoDuplicateName(Names, InternalID, TEXT("variable or component"), WingOut::Stdout)) return;
|
||||
|
||||
// Resolve the component class by name
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
FName InternalID = WingUtils::CheckProposedName(Dispatcher, WingOut::Stdout);
|
||||
if (InternalID.IsNone()) return;
|
||||
TSet<FName> Names;
|
||||
FBlueprintEditorUtils::GetClassVariableList(BP, Names);
|
||||
WingUtils::GetAllInUseNames(BP, Names);
|
||||
if (!WingUtils::FindNoDuplicateName(Names, InternalID, TEXT("variable or component"), WingOut::Stdout)) return;
|
||||
|
||||
// Parse the arguments.
|
||||
|
||||
@@ -51,7 +51,6 @@ public:
|
||||
WingFetcher F(WingOut::Stdout);
|
||||
UWidgetBlueprint* BP = F.Walk(Blueprint).Cast<UWidgetBlueprint>();
|
||||
if (!BP) return;
|
||||
UWidgetTree* Tree = BP->WidgetTree;
|
||||
|
||||
// Resolve the widget type.
|
||||
WingWidgets WidgetMenu;
|
||||
@@ -71,15 +70,14 @@ public:
|
||||
if (InternalID.IsNone()) return;
|
||||
|
||||
// Check that the name is unique among existing widgets.
|
||||
TArray<UWidget*> AllWidgets;
|
||||
Tree->GetAllWidgets(AllWidgets);
|
||||
TSet<FName> Names;
|
||||
FBlueprintEditorUtils::GetClassVariableList(BP, Names);
|
||||
if (!WingUtils::FindNoDuplicateNames(Names, AllWidgets, TEXT("widget or variable"), WingOut::Stdout)) return;
|
||||
WingUtils::GetAllInUseNames(BP, Names);
|
||||
if (!WingUtils::FindNoDuplicateName(Names, InternalID, TEXT("widget or variable"), WingOut::Stdout)) return;
|
||||
|
||||
// If a parent is specified, find it and verify it's a panel.
|
||||
UPanelWidget* ParentPanel = nullptr;
|
||||
TArray<UWidget*> AllWidgets;
|
||||
BP->WidgetTree->GetAllWidgets(AllWidgets);
|
||||
if (!Parent.IsEmpty())
|
||||
{
|
||||
UWidget* ParentWidget = WingUtils::FindOneWithExternalID(Parent, AllWidgets, TEXT("Widget"), WingOut::Stdout);
|
||||
@@ -89,7 +87,7 @@ public:
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Tree->RootWidget != nullptr)
|
||||
if (BP->WidgetTree->RootWidget != nullptr)
|
||||
{
|
||||
WingOut::Stdout.Printf(TEXT("ERROR: Widget tree already has a root widget. Specify a Parent.\n"));
|
||||
return;
|
||||
@@ -99,9 +97,9 @@ public:
|
||||
// Create the widget.
|
||||
UWidget* NewWidget;
|
||||
if (WidgetClass->IsChildOf(UUserWidget::StaticClass()))
|
||||
NewWidget = CreateWidget<UUserWidget>(Tree, WidgetClass, InternalID);
|
||||
NewWidget = CreateWidget<UUserWidget>(BP->WidgetTree, WidgetClass, InternalID);
|
||||
else
|
||||
NewWidget = NewObject<UWidget>(Tree, WidgetClass, InternalID, RF_Transactional);
|
||||
NewWidget = NewObject<UWidget>(BP->WidgetTree, WidgetClass, InternalID, RF_Transactional);
|
||||
|
||||
if (!NewWidget)
|
||||
{
|
||||
@@ -120,7 +118,7 @@ public:
|
||||
}
|
||||
else
|
||||
{
|
||||
Tree->RootWidget = NewWidget;
|
||||
BP->WidgetTree->RootWidget = NewWidget;
|
||||
}
|
||||
|
||||
WingOut::Stdout.Printf(TEXT("Created widget '%s' of type '%s'\n"), *Name, *Type);
|
||||
|
||||
@@ -150,7 +150,7 @@ bool UWingComponent::AddComponent(UBlueprint *BP, UClass *Class, UWingComponentR
|
||||
return false;
|
||||
}
|
||||
TSet<FName> Names;
|
||||
FBlueprintEditorUtils::GetClassVariableList(BP, Names);
|
||||
WingUtils::GetAllInUseNames(BP, Names);
|
||||
if (Names.Contains(Name))
|
||||
{
|
||||
Errors.Printf(TEXT("There is already a variable or component named %s in %s.\n"),
|
||||
|
||||
@@ -1,10 +1,75 @@
|
||||
#include "WingGraphActions.h"
|
||||
#include "EdGraph/EdGraphSchema.h"
|
||||
#include "AnimNotifyEventNodeSpawner.h"
|
||||
#include "BlueprintActionFilter.h"
|
||||
#include "BlueprintActionDatabase.h"
|
||||
#include "BlueprintBoundEventNodeSpawner.h"
|
||||
#include "BlueprintComponentNodeSpawner.h"
|
||||
#include "BlueprintDelegateNodeSpawner.h"
|
||||
#include "BlueprintEventNodeSpawner.h"
|
||||
#include "BlueprintFunctionNodeSpawner.h"
|
||||
#include "BlueprintNodeSpawner.h"
|
||||
#include "BlueprintVariableNodeSpawner.h"
|
||||
#include "EdGraph/EdGraphSchema.h"
|
||||
#include "EdGraphSchema_K2.h"
|
||||
#include "Animation/AnimInstance.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "Kismet2/BlueprintEditorUtils.h"
|
||||
#include "WingUtils.h"
|
||||
|
||||
FName FWingGraphAction::GetPropertyClassName(const FProperty *Prop)
|
||||
{
|
||||
if (Prop == nullptr) return FName();
|
||||
UClass *Class = Prop->GetOwnerClass();
|
||||
if (Class == nullptr) return FName();
|
||||
return Class->GetFName();
|
||||
}
|
||||
|
||||
FName FWingGraphAction::GetSpawnerOwnerClass(const UBlueprintNodeSpawner* Spawner)
|
||||
{
|
||||
if (const UBlueprintFunctionNodeSpawner* FunctionSpawner = Cast<UBlueprintFunctionNodeSpawner>(Spawner))
|
||||
{
|
||||
return FunctionSpawner->GetFunction()->GetOwnerClass()->GetFName();
|
||||
}
|
||||
|
||||
if (const UBlueprintVariableNodeSpawner* VariableSpawner = Cast<UBlueprintVariableNodeSpawner>(Spawner))
|
||||
{
|
||||
return GetPropertyClassName(VariableSpawner->GetVarProperty());
|
||||
}
|
||||
|
||||
if (const UBlueprintDelegateNodeSpawner* DelegateSpawner = Cast<UBlueprintDelegateNodeSpawner>(Spawner))
|
||||
{
|
||||
return GetPropertyClassName(DelegateSpawner->GetDelegateProperty());
|
||||
}
|
||||
|
||||
if (const UBlueprintBoundEventNodeSpawner* BoundEventSpawner = Cast<UBlueprintBoundEventNodeSpawner>(Spawner))
|
||||
{
|
||||
return GetPropertyClassName(BoundEventSpawner->GetEventDelegate());
|
||||
}
|
||||
|
||||
if (Cast<UAnimNotifyEventNodeSpawner>(Spawner))
|
||||
{
|
||||
return UAnimInstance::StaticClass()->GetFName();
|
||||
}
|
||||
|
||||
if (const UBlueprintEventNodeSpawner* EventSpawner = Cast<UBlueprintEventNodeSpawner>(Spawner))
|
||||
{
|
||||
if (EventSpawner->IsForCustomEvent()) return FName();
|
||||
return EventSpawner->GetEventFunction()->GetOwnerClass()->GetFName();
|
||||
}
|
||||
|
||||
if (const UBlueprintComponentNodeSpawner* ComponentSpawner = Cast<UBlueprintComponentNodeSpawner>(Spawner))
|
||||
{
|
||||
if (const UClass* ComponentClass = ComponentSpawner->GetComponentClass())
|
||||
{
|
||||
return ComponentClass->GetFName();
|
||||
}
|
||||
// todo: get a class name from the asset data.
|
||||
return FName();
|
||||
}
|
||||
|
||||
return FName();
|
||||
}
|
||||
|
||||
FWingGraphAction::FWingGraphAction(TSharedPtr<FEdGraphSchemaAction> &iAction, UEdGraph *iGraph)
|
||||
{
|
||||
Action = iAction;
|
||||
@@ -20,10 +85,13 @@ FWingGraphAction::FWingGraphAction(UBlueprintNodeSpawner *iSpawner, UEdGraph *iG
|
||||
Spawner = iSpawner;
|
||||
Graph = iGraph;
|
||||
const FBlueprintActionUiSpec& UiSpec = Spawner->PrimeDefaultUiSpec();
|
||||
FString Category = UiSpec.Category.ToString();
|
||||
FString MenuName = UiSpec.MenuName.ToString();
|
||||
Name = WingUtils::StandardizeMenuItem(Category + TEXT("|") + MenuName);
|
||||
Keywords = Spawner->PrimeDefaultUiSpec().Keywords.ToString();
|
||||
Name = UiSpec.Category.ToString() + TEXT("|") + UiSpec.MenuName.ToString();
|
||||
Keywords = UiSpec.Keywords.ToString();
|
||||
if (FName OwnerClass = GetSpawnerOwnerClass(Spawner); !OwnerClass.IsNone())
|
||||
{
|
||||
Name = OwnerClass.ToString() + TEXT("|") + Name;
|
||||
}
|
||||
Name = WingUtils::StandardizeMenuItem(Name);
|
||||
}
|
||||
|
||||
UEdGraphNode* FWingGraphAction::Execute(const FVector2D &Location) const
|
||||
@@ -72,18 +140,28 @@ void FWingGraphActions::CollectActions()
|
||||
|
||||
void FWingGraphActions::CollectSpawners()
|
||||
{
|
||||
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(Graph);
|
||||
if (!Blueprint) return;
|
||||
FBlueprintActionContext Context;
|
||||
Context.Blueprints.Add(Blueprint);
|
||||
Context.Graphs.Add(Graph);
|
||||
|
||||
FBlueprintActionFilter Filter(FBlueprintActionFilter::BPFILTER_NoFlags);
|
||||
Filter.Context = Context;
|
||||
|
||||
for (const auto& Pair : FBlueprintActionDatabase::Get().GetAllActions())
|
||||
{
|
||||
UObject* ActionObject = Pair.Key.ResolveObjectPtr();
|
||||
if (!ActionObject) continue;
|
||||
|
||||
for (UBlueprintNodeSpawner* Spawner : Pair.Value)
|
||||
{
|
||||
if (Spawner->NodeClass)
|
||||
{
|
||||
UEdGraphNode* NodeCDO = CastChecked<UEdGraphNode>(Spawner->NodeClass->ClassDefaultObject);
|
||||
if (NodeCDO->IsCompatibleWithGraph(Graph))
|
||||
{
|
||||
Actions.Emplace(Spawner, Graph);
|
||||
}
|
||||
}
|
||||
if (!Spawner->NodeClass) continue;
|
||||
|
||||
FBlueprintActionInfo ActionInfo(ActionObject, Spawner);
|
||||
if (Filter.IsFiltered(ActionInfo)) continue;
|
||||
|
||||
Actions.Emplace(Spawner, Graph);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,7 +474,8 @@ TArray<FWingProperty> FWingProperty::GetDetails(UObject* Obj, bool Mutable)
|
||||
}
|
||||
|
||||
// If it's a Widget, hide the slot property, and add the
|
||||
// slot properties.
|
||||
// slot properties. Then, add the 'bIsVariable' property and
|
||||
// make it editable.
|
||||
if (UWidget* Widget = Cast<UWidget>(Obj))
|
||||
{
|
||||
FWingProperty::Remove(Result, TEXT("Slot"));
|
||||
@@ -482,6 +483,8 @@ TArray<FWingProperty> FWingProperty::GetDetails(UObject* Obj, bool Mutable)
|
||||
{
|
||||
Result.Append(GetVisible(Slot));
|
||||
}
|
||||
FProperty *VarProp = Widget->GetClass()->FindPropertyByName(TEXT("bIsVariable"));
|
||||
if (VarProp) Result.Add(FWingProperty(VarProp, Widget, true));
|
||||
}
|
||||
|
||||
if (!Mutable) StripEditable(Result);
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "Kismet2/Kismet2NameValidators.h"
|
||||
#include "UObject/UObjectIterator.h"
|
||||
#include "UObject/UnrealType.h"
|
||||
#include "WidgetBlueprint.h"
|
||||
#include "Blueprint/WidgetTree.h"
|
||||
|
||||
// Reparent validation
|
||||
#include "Engine/LevelScriptActor.h"
|
||||
@@ -62,6 +64,11 @@ FName WingUtils::CheckInternalizeID(const FString &ExternalID, WingOut Errors)
|
||||
FName WingUtils::CheckProposedName(const FString &ExternalID, WingOut Errors)
|
||||
{
|
||||
FName InternalID = CheckInternalizeID(ExternalID, Errors);
|
||||
if (InternalID.ToString().Len() > 100)
|
||||
{
|
||||
Errors.Printf(TEXT("ERROR: id %s is too long"), *ExternalID);
|
||||
return FName();
|
||||
}
|
||||
if (!InternalID.IsNone() && !WingTokenizer::WouldExternalizeReadably(InternalID))
|
||||
{
|
||||
Errors.Printf(TEXT("ERROR: id %s would not be a readable id, may not create item with this name"),
|
||||
@@ -476,6 +483,21 @@ UObject *WingUtils::GetGeneratedCDO(UBlueprint *BP)
|
||||
return BP->GeneratedClass->GetDefaultObject();
|
||||
}
|
||||
|
||||
void WingUtils::GetAllInUseNames(UBlueprint *BP, TSet<FName> &Names)
|
||||
{
|
||||
FBlueprintEditorUtils::GetClassVariableList(BP, Names, true);
|
||||
FBlueprintEditorUtils::GetFunctionNameList(BP, Names);
|
||||
FBlueprintEditorUtils::GetAllGraphNames(BP, Names);
|
||||
FBlueprintEditorUtils::GetSCSVariableNameList(BP, Names);
|
||||
FBlueprintEditorUtils::GetImplementingBlueprintsFunctionNameList(BP, Names);
|
||||
if (UWidgetBlueprint *WBP = Cast<UWidgetBlueprint>(BP))
|
||||
{
|
||||
TArray<UWidget*> AllWidgets;
|
||||
WBP->WidgetTree->GetAllWidgets(AllWidgets);
|
||||
WingUtils::AddNamesToSet(Names, AllWidgets);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Graph Pin Helpers
|
||||
// ============================================================
|
||||
|
||||
@@ -612,7 +612,7 @@ bool WingVariables::CreateBlueprint(WingOut Errors)
|
||||
|
||||
// Check for name collisions against existing variables, components, and the like.
|
||||
TSet<FName> Names;
|
||||
FBlueprintEditorUtils::GetClassVariableList(Blueprint.Get(), Names);
|
||||
WingUtils::GetAllInUseNames(Blueprint.Get(), Names);
|
||||
if (!WingUtils::FindNoDuplicateNames(Names, BlueprintVariables.Variables, TEXT("variable or component"), Errors)) return false;
|
||||
|
||||
// Create the variables.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
class UBlueprintNodeSpawner;
|
||||
class UEdGraph;
|
||||
class UEdGraphNode;
|
||||
struct FBlueprintActionContext;
|
||||
struct FEdGraphSchemaAction;
|
||||
|
||||
|
||||
@@ -20,6 +21,10 @@ struct FWingGraphAction
|
||||
FWingGraphAction(UBlueprintNodeSpawner *iSpawner, UEdGraph *iGraph);
|
||||
|
||||
UEdGraphNode *Execute(const FVector2D &Location) const;
|
||||
|
||||
private:
|
||||
static FName GetSpawnerOwnerClass(const UBlueprintNodeSpawner* Spawner);
|
||||
static FName GetPropertyClassName(const FProperty *Prop);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -136,6 +136,12 @@ public:
|
||||
return Result;
|
||||
}
|
||||
|
||||
template<typename ArrayType>
|
||||
static void AddNamesToSet(TSet<FName> &Collection, ArrayType &&Array)
|
||||
{
|
||||
for (auto &Elt : Array) Collection.Add(GetFName(Elt));
|
||||
}
|
||||
|
||||
static bool FindNoDuplicateName(TSet<FName> &Collection, FName InternalID, const TCHAR *Kind, WingOut Errors)
|
||||
{
|
||||
if (Collection.Contains(InternalID))
|
||||
@@ -244,6 +250,7 @@ public:
|
||||
static TArray<UEdGraphNode*> AllNodes(UEdGraph *Graph);
|
||||
static TArray<UBlueprint*> GetAncestorBlueprints(UBlueprint *BP, bool OldestFirst = false);
|
||||
static UObject *GetGeneratedCDO(UBlueprint *BP);
|
||||
static void GetAllInUseNames(UBlueprint *BP, TSet<FName> &Names);
|
||||
|
||||
// ----- Graph pin helpers -----
|
||||
static UEdGraphPin* CheckGetPin(UEdGraphNode* Node, FName PinName, WingOut Errors);
|
||||
|
||||
Reference in New Issue
Block a user