Add support for Blueprint Overrides, and make more progress on the hotkey widget.
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
* UE Wingman Widgets: cannot edit 'Is Variable' flag or widget name.
|
|
||||||
|
|
||||||
TSharedPtr<INameValidatorInterface> NameValidator = MakeShareable(new FKismetNameValidator(Blueprint, OldObjectName));
|
* UE Wingman Function Overrides.
|
||||||
|
|
||||||
|
|
||||||
* Keyboard Event Handling
|
* Keyboard Event Handling
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "WingServer.h"
|
||||||
|
#include "WingBasics.h"
|
||||||
|
#include "WingFetcher.h"
|
||||||
|
#include "WingUtils.h"
|
||||||
|
#include "Engine/Blueprint.h"
|
||||||
|
#include "EdGraphSchema_K2.h"
|
||||||
|
#include "Kismet2/BlueprintEditorUtils.h"
|
||||||
|
#include "BlueprintOverride_Add.generated.h"
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
UCLASS()
|
||||||
|
class UWing_BlueprintOverride_Add : public UWingHandler
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UPROPERTY(EditAnywhere, meta=(Description="Blueprint path"))
|
||||||
|
FString Blueprint;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, meta=(Description="Function to override, in Class|Function format"))
|
||||||
|
FString Function;
|
||||||
|
|
||||||
|
virtual void Register() override
|
||||||
|
{
|
||||||
|
UWingServer::AddHandler(this,
|
||||||
|
TEXT("Add a function override to a Blueprint."));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Handle() override
|
||||||
|
{
|
||||||
|
WingFetcher F(WingOut::Stdout);
|
||||||
|
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
|
||||||
|
if (!BP) return;
|
||||||
|
|
||||||
|
// Find the function by matching against the menu string format (Class|Function)
|
||||||
|
TArray<UFunction*> Overridable = WingUtils::GetOverridableFunctions(BP);
|
||||||
|
UFunction* OverrideFunc = nullptr;
|
||||||
|
for (UFunction* Candidate : Overridable)
|
||||||
|
{
|
||||||
|
FString MenuString = WingUtils::GetFunctionMenuString(Candidate);
|
||||||
|
if (MenuString.Equals(Function, ESearchCase::IgnoreCase))
|
||||||
|
{
|
||||||
|
OverrideFunc = Candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!OverrideFunc)
|
||||||
|
{
|
||||||
|
WingOut::Stdout.Printf(TEXT("ERROR: '%s' is not an overridable function in this Blueprint.\n"), *Function);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it already exists
|
||||||
|
FName FuncName = OverrideFunc->GetFName();
|
||||||
|
TSet<FName> ExistingNames;
|
||||||
|
FBlueprintEditorUtils::GetAllGraphNames(BP, ExistingNames);
|
||||||
|
if (ExistingNames.Contains(FuncName))
|
||||||
|
{
|
||||||
|
WingOut::Stdout.Printf(TEXT("ERROR: Override graph '%s' already exists.\n"), *Function);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UClass* OverrideFuncClass = WingUtils::GetFunctionClass(OverrideFunc);
|
||||||
|
|
||||||
|
UEdGraph* NewGraph = FBlueprintEditorUtils::CreateNewGraph(BP, FuncName,
|
||||||
|
UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
|
||||||
|
if (!NewGraph)
|
||||||
|
{
|
||||||
|
WingOut::Stdout.Print(TEXT("ERROR: Failed to create graph.\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FBlueprintEditorUtils::AddFunctionGraph(BP, NewGraph, /*bIsUserCreated=*/false, OverrideFuncClass);
|
||||||
|
|
||||||
|
WingOut::Stdout.Printf(TEXT("Created override: %s\n"), *WingUtils::FormatName(NewGraph));
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "WingServer.h"
|
||||||
|
#include "WingBasics.h"
|
||||||
|
#include "WingFetcher.h"
|
||||||
|
#include "WingUtils.h"
|
||||||
|
#include "Engine/Blueprint.h"
|
||||||
|
#include "Kismet2/BlueprintEditorUtils.h"
|
||||||
|
#include "BlueprintOverride_ShowMenu.generated.h"
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
UCLASS()
|
||||||
|
class UWing_BlueprintOverride_ShowMenu : public UWingHandler
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UPROPERTY(EditAnywhere, meta=(Description="Blueprint path"))
|
||||||
|
FString Blueprint;
|
||||||
|
|
||||||
|
virtual void Register() override
|
||||||
|
{
|
||||||
|
UWingServer::AddHandler(this,
|
||||||
|
TEXT("Show the functions that can be overridden in a Blueprint."));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Handle() override
|
||||||
|
{
|
||||||
|
WingFetcher F(WingOut::Stdout);
|
||||||
|
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
|
||||||
|
if (!BP) return;
|
||||||
|
|
||||||
|
TArray<UFunction*> Results = WingUtils::GetOverridableFunctions(BP);
|
||||||
|
for (UFunction *Func : Results)
|
||||||
|
{
|
||||||
|
UClass *Class = WingUtils::GetFunctionClass(Func);
|
||||||
|
WingOut::Stdout.Print(WingUtils::GetFunctionMenuString(Func));
|
||||||
|
WingOut::Stdout.Print(TEXT("\n"));
|
||||||
|
}
|
||||||
|
if (Results.IsEmpty())
|
||||||
|
WingOut::Stdout.Print(TEXT("No overridable functions.\n"));
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -33,6 +33,7 @@ FName FWingGraphAction::GetSpawnerOwnerClass(const UBlueprintNodeSpawner* Spawne
|
|||||||
|
|
||||||
if (const UBlueprintVariableNodeSpawner* VariableSpawner = Cast<UBlueprintVariableNodeSpawner>(Spawner))
|
if (const UBlueprintVariableNodeSpawner* VariableSpawner = Cast<UBlueprintVariableNodeSpawner>(Spawner))
|
||||||
{
|
{
|
||||||
|
if (VariableSpawner->IsLocalVariable()) return FName(TEXT("LocalVariable"));
|
||||||
return GetPropertyClassName(VariableSpawner->GetVarProperty());
|
return GetPropertyClassName(VariableSpawner->GetVarProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,10 @@
|
|||||||
#include "WidgetBlueprint.h"
|
#include "WidgetBlueprint.h"
|
||||||
#include "Blueprint/WidgetTree.h"
|
#include "Blueprint/WidgetTree.h"
|
||||||
|
|
||||||
|
// Blueprint override helpers
|
||||||
|
#include "EdGraphSchema_K2.h"
|
||||||
|
#include "ObjectEditorUtils.h"
|
||||||
|
|
||||||
// Reparent validation
|
// Reparent validation
|
||||||
#include "Engine/LevelScriptActor.h"
|
#include "Engine/LevelScriptActor.h"
|
||||||
#include "Animation/AnimInstance.h"
|
#include "Animation/AnimInstance.h"
|
||||||
@@ -403,6 +407,79 @@ bool WingUtils::CheckNewObjectNotNull(UObject *Obj, const TCHAR *Kind, WingOut E
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Blueprint override helpers
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
bool WingUtils::CanBlueprintOverride(UBlueprint* BP, const UFunction* Function)
|
||||||
|
{
|
||||||
|
if (!Function) return false;
|
||||||
|
if (!UEdGraphSchema_K2::CanKismetOverrideFunction(Function)) return false;
|
||||||
|
UClass* HiddenFromClass = BP->SkeletonGeneratedClass ? BP->SkeletonGeneratedClass->GetSuperClass() : BP->ParentClass.Get();
|
||||||
|
if (HiddenFromClass && FObjectEditorUtils::IsFunctionHiddenFromClass(Function, HiddenFromClass)) return false;
|
||||||
|
if (Function->HasAnyFunctionFlags(FUNC_Private)) return false;
|
||||||
|
if (!BP->AllowFunctionOverride(Function)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<UFunction*> WingUtils::GetOverridableFunctions(UBlueprint *BP)
|
||||||
|
{
|
||||||
|
TArray<UFunction *> Candidates, Results;
|
||||||
|
TSet<UFunction*> Seen;
|
||||||
|
|
||||||
|
UClass* ParentClass = BP->SkeletonGeneratedClass ? BP->SkeletonGeneratedClass->GetSuperClass() : BP->ParentClass.Get();
|
||||||
|
for (TFieldIterator<UFunction> FunctionIt(ParentClass, EFieldIteratorFlags::IncludeSuper); FunctionIt; ++FunctionIt)
|
||||||
|
{
|
||||||
|
Candidates.Add(*FunctionIt);
|
||||||
|
}
|
||||||
|
for (const FBPInterfaceDescription& InterfaceDesc : BP->ImplementedInterfaces)
|
||||||
|
{
|
||||||
|
UClass* InterfaceClass = InterfaceDesc.Interface.Get();
|
||||||
|
if (!InterfaceClass) continue;
|
||||||
|
for (TFieldIterator<UFunction> FunctionIt(InterfaceClass, EFieldIteratorFlags::IncludeSuper); FunctionIt; ++FunctionIt)
|
||||||
|
{
|
||||||
|
Candidates.Add(*FunctionIt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (UClass* TempClass = BP->ParentClass.Get(); TempClass; TempClass = TempClass->GetSuperClass())
|
||||||
|
{
|
||||||
|
for (const FImplementedInterface& Interface : TempClass->Interfaces)
|
||||||
|
{
|
||||||
|
for (TFieldIterator<UFunction> FunctionIt(Interface.Class, EFieldIteratorFlags::IncludeSuper); FunctionIt; ++FunctionIt)
|
||||||
|
{
|
||||||
|
Candidates.Add(*FunctionIt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (UFunction *Candidate : Candidates)
|
||||||
|
{
|
||||||
|
if (!Seen.Contains(Candidate))
|
||||||
|
{
|
||||||
|
Seen.Add(Candidate);
|
||||||
|
if (CanBlueprintOverride(BP, Candidate)) Results.Add(Candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Results;
|
||||||
|
}
|
||||||
|
|
||||||
|
UClass *WingUtils::GetFunctionClass(UFunction *Func)
|
||||||
|
{
|
||||||
|
UClass *Class = Func->GetOuterUClass();
|
||||||
|
UBlueprintGeneratedClass *BPGC = Cast<UBlueprintGeneratedClass>(Class);
|
||||||
|
if (BPGC) return BPGC->GetAuthoritativeClass();
|
||||||
|
else return Class;
|
||||||
|
}
|
||||||
|
|
||||||
|
FString WingUtils::GetFunctionMenuString(UFunction *Func)
|
||||||
|
{
|
||||||
|
UClass *Class = GetFunctionClass(Func);
|
||||||
|
TStringBuilder<256> Result;
|
||||||
|
Result.Append(FormatName(Class));
|
||||||
|
Result.AppendChar('|');
|
||||||
|
Result.Append(FormatName(Func));
|
||||||
|
return Result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Reparent validation
|
// Reparent validation
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|||||||
@@ -280,6 +280,12 @@ public:
|
|||||||
static FString GetHandlerName(UClass* HandlerClass);
|
static FString GetHandlerName(UClass* HandlerClass);
|
||||||
static FString GetHandlerGroup(UClass* HandlerClass);
|
static FString GetHandlerGroup(UClass* HandlerClass);
|
||||||
|
|
||||||
|
// ----- Blueprint override helpers -----
|
||||||
|
static bool CanBlueprintOverride(UBlueprint* BP, const UFunction* Function);
|
||||||
|
static TArray<UFunction*> GetOverridableFunctions(UBlueprint *BP);
|
||||||
|
static UClass *GetFunctionClass(UFunction *Func);
|
||||||
|
static FString GetFunctionMenuString(UFunction *Func);
|
||||||
|
|
||||||
// ----- Reparent validation -----
|
// ----- Reparent validation -----
|
||||||
static bool CanReparentBlueprint(UClass* CurrentGenerated, UClass* Proposed);
|
static bool CanReparentBlueprint(UClass* CurrentGenerated, UClass* Proposed);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user