Widget_Create is implemented
This commit is contained in:
BIN
Content/Testing/WB_Test.uasset
LFS
BIN
Content/Testing/WB_Test.uasset
LFS
Binary file not shown.
134
Plugins/UEWingman/Source/UEWingman/Handlers/Widget_Create.h
Normal file
134
Plugins/UEWingman/Source/UEWingman/Handlers/Widget_Create.h
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "WingServer.h"
|
||||||
|
#include "WingHandler.h"
|
||||||
|
#include "WingFetcher.h"
|
||||||
|
#include "WingWidgets.h"
|
||||||
|
#include "WingUtils.h"
|
||||||
|
#include "WidgetBlueprint.h"
|
||||||
|
#include "Blueprint/WidgetTree.h"
|
||||||
|
#include "Blueprint/UserWidget.h"
|
||||||
|
#include "Components/PanelWidget.h"
|
||||||
|
#include "Widget_Create.generated.h"
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
UCLASS()
|
||||||
|
class UWing_Widget_Create : public UObject, public IWingHandler
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UPROPERTY(meta=(Description="Widget blueprint to add the widget to"))
|
||||||
|
FString Blueprint;
|
||||||
|
|
||||||
|
UPROPERTY(meta=(Description="Widget type, from Widget_SearchTypes"))
|
||||||
|
FString Type;
|
||||||
|
|
||||||
|
UPROPERTY(meta=(Description="Name for the new widget"))
|
||||||
|
FString Name;
|
||||||
|
|
||||||
|
UPROPERTY(meta=(Optional, Description="Parent widget name. If omitted, sets as root."))
|
||||||
|
FString Parent;
|
||||||
|
|
||||||
|
UPROPERTY(meta=(Optional, Description="Whether to expose the widget as a variable in the blueprint (default false)"))
|
||||||
|
bool IsVariable = false;
|
||||||
|
|
||||||
|
virtual FString GetDescription() const override
|
||||||
|
{
|
||||||
|
return TEXT("Add a widget to a Widget Blueprint's widget tree. "
|
||||||
|
"Use Widget_SearchTypes to find available widget types.");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Handle() override
|
||||||
|
{
|
||||||
|
// Fetch the widget blueprint.
|
||||||
|
WingFetcher F;
|
||||||
|
UWidgetBlueprint* BP = F.Walk(Blueprint).Cast<UWidgetBlueprint>();
|
||||||
|
if (!BP) return;
|
||||||
|
UWidgetTree* Tree = BP->WidgetTree;
|
||||||
|
|
||||||
|
// Resolve the widget type.
|
||||||
|
WingWidgets WidgetMenu;
|
||||||
|
TArray<WingWidgets::Type> TypeResults = WidgetMenu.Search(Type, 2, true);
|
||||||
|
if (!WingUtils::CheckExactlyOneNamed(TypeResults.Num(), TEXT("Widget type"), Type)) return;
|
||||||
|
|
||||||
|
// Load the widget's class.
|
||||||
|
UClass* WidgetClass = TypeResults[0].Class.LoadSynchronous();
|
||||||
|
if (!WidgetClass)
|
||||||
|
{
|
||||||
|
UWingServer::Printf(TEXT("ERROR: Failed to load widget class for '%s'\n"), *Type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the proposed name.
|
||||||
|
FString UnsanitizedName = WingUtils::CheckProposedName(Name);
|
||||||
|
if (UnsanitizedName.IsEmpty()) return;
|
||||||
|
|
||||||
|
// Check that the name is unique among existing widgets.
|
||||||
|
TArray<UWidget*> AllWidgets;
|
||||||
|
Tree->GetAllWidgets(AllWidgets);
|
||||||
|
if (!WingUtils::FindExactlyNoneNamed(Name, AllWidgets, TEXT("Widget"))) return;
|
||||||
|
|
||||||
|
// If a parent is specified, find it and verify it's a panel.
|
||||||
|
UPanelWidget* ParentPanel = nullptr;
|
||||||
|
if (!Parent.IsEmpty())
|
||||||
|
{
|
||||||
|
UWidget* ParentWidget = WingUtils::FindExactlyOneNamed(Parent, AllWidgets, TEXT("Widget"));
|
||||||
|
if (!ParentWidget) return;
|
||||||
|
ParentPanel = Cast<UPanelWidget>(ParentWidget);
|
||||||
|
if (!ParentPanel)
|
||||||
|
{
|
||||||
|
UWingServer::Printf(TEXT("ERROR: '%s' is not a panel widget and cannot have children\n"), *Parent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ParentPanel->CanAddMoreChildren())
|
||||||
|
{
|
||||||
|
UWingServer::Printf(TEXT("ERROR: '%s' already has a child and cannot accept more\n"), *Parent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Tree->RootWidget != nullptr)
|
||||||
|
{
|
||||||
|
UWingServer::Printf(TEXT("ERROR: Widget tree already has a root widget. Specify a Parent.\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the widget.
|
||||||
|
UWidget* NewWidget;
|
||||||
|
FName WidgetFName(*UnsanitizedName);
|
||||||
|
if (WidgetClass->IsChildOf(UUserWidget::StaticClass()))
|
||||||
|
NewWidget = CreateWidget<UUserWidget>(Tree, WidgetClass, WidgetFName);
|
||||||
|
else
|
||||||
|
NewWidget = NewObject<UWidget>(Tree, WidgetClass, WidgetFName, RF_Transactional);
|
||||||
|
|
||||||
|
if (!NewWidget)
|
||||||
|
{
|
||||||
|
UWingServer::Printf(TEXT("ERROR: Failed to create widget\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize defaults and set variable flag.
|
||||||
|
NewWidget->CreatedFromPalette();
|
||||||
|
NewWidget->bIsVariable = IsVariable;
|
||||||
|
|
||||||
|
// Add to the tree.
|
||||||
|
if (ParentPanel)
|
||||||
|
{
|
||||||
|
ParentPanel->AddChild(NewWidget);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Tree->RootWidget = NewWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
UWingServer::Printf(TEXT("Created widget '%s' of type '%s'\n"), *Name, *Type);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "WingServer.h"
|
||||||
|
#include "WingHandler.h"
|
||||||
|
#include "WingWidgets.h"
|
||||||
|
#include "Widget_SearchTypes.generated.h"
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
UCLASS()
|
||||||
|
class UWing_Widget_SearchTypes : public UObject, public IWingHandler
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UPROPERTY(meta=(Description="Query string, can contain *"))
|
||||||
|
FString Query;
|
||||||
|
|
||||||
|
UPROPERTY(meta=(Optional, Description="Maximum number of results (default 50)"))
|
||||||
|
int32 MaxResults = 50;
|
||||||
|
|
||||||
|
virtual FString GetDescription() const override
|
||||||
|
{
|
||||||
|
return TEXT("Search for widget types that can be added to a Widget Blueprint. "
|
||||||
|
"Returns names for use with Widget_Create.");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Handle() override
|
||||||
|
{
|
||||||
|
WingWidgets Widgets;
|
||||||
|
TArray<WingWidgets::Type> Results = Widgets.Search(Query, MaxResults, false);
|
||||||
|
for (const WingWidgets::Type& Entry : Results)
|
||||||
|
{
|
||||||
|
UWingServer::Printf(TEXT("%s\n"), *Entry.MenuName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Results.Num() == 0)
|
||||||
|
{
|
||||||
|
UWingServer::Print(TEXT("No matching widget types found.\n"));
|
||||||
|
}
|
||||||
|
else if (Results.Num() >= MaxResults)
|
||||||
|
{
|
||||||
|
UWingServer::Printf(TEXT("WARNING: Reached limit of %d results. You may specify MaxResults.\n"), MaxResults);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -2,9 +2,93 @@
|
|||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
#include "Blueprint/WidgetTree.h"
|
#include "Blueprint/WidgetTree.h"
|
||||||
|
#include "WidgetBlueprint.h"
|
||||||
#include "Components/Widget.h"
|
#include "Components/Widget.h"
|
||||||
#include "Components/PanelWidget.h"
|
#include "Components/PanelWidget.h"
|
||||||
#include "Components/PanelSlot.h"
|
#include "Components/PanelSlot.h"
|
||||||
|
#include "Blueprint/UserWidget.h"
|
||||||
|
#include "UObject/UObjectIterator.h"
|
||||||
|
#include "AssetRegistry/AssetData.h"
|
||||||
|
#include "AssetRegistry/AssetRegistryModule.h"
|
||||||
|
#include "AssetRegistry/IAssetRegistry.h"
|
||||||
|
|
||||||
|
FString WingWidgets::WidgetMenuString(UClass* WidgetClass)
|
||||||
|
{
|
||||||
|
if (UObject* GeneratedBy = WidgetClass->ClassGeneratedBy)
|
||||||
|
return GeneratedBy->GetPathName();
|
||||||
|
UWidget* CDO = WidgetClass->GetDefaultObject<UWidget>();
|
||||||
|
FString Category = CDO->GetPaletteCategory().ToString();
|
||||||
|
FString Name = WidgetClass->GetName();
|
||||||
|
return WingUtils::StandardizeMenuItem(Category + TEXT("|") + Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
FString WingWidgets::WidgetMenuString(const FAssetData& Data)
|
||||||
|
{
|
||||||
|
return Data.GetObjectPathString();
|
||||||
|
}
|
||||||
|
|
||||||
|
WingWidgets::WingWidgets()
|
||||||
|
{
|
||||||
|
TSortedMap<FString, Type> Sorted;
|
||||||
|
|
||||||
|
// Collect loaded native widget classes.
|
||||||
|
for (TObjectIterator<UClass> It; It; ++It)
|
||||||
|
{
|
||||||
|
UClass* Class = *It;
|
||||||
|
if (!Class->IsChildOf(UWidget::StaticClass())) continue;
|
||||||
|
if (Class->HasAnyClassFlags(CLASS_Abstract | CLASS_Deprecated | CLASS_NewerVersionExists | CLASS_Hidden)) continue;
|
||||||
|
if (Class->HasAnyFlags(RF_Transient) && Class->HasAnyClassFlags(CLASS_CompiledFromBlueprint)) continue;
|
||||||
|
|
||||||
|
FString MenuName = WidgetMenuString(Class);
|
||||||
|
Sorted.Add(MenuName, { MenuName, Class });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect unloaded Widget Blueprint assets from the AssetRegistry.
|
||||||
|
IAssetRegistry& Registry = *IAssetRegistry::Get();
|
||||||
|
TArray<FAssetData> AssetResults;
|
||||||
|
Registry.GetAssetsByClass(UWidgetBlueprint::StaticClass()->GetClassPathName(), AssetResults, true);
|
||||||
|
for (const FAssetData& Data : AssetResults)
|
||||||
|
{
|
||||||
|
if (Data.IsAssetLoaded()) continue;
|
||||||
|
|
||||||
|
FString GeneratedClassPath;
|
||||||
|
if (!Data.GetTagValue(FBlueprintTags::GeneratedClassPath, GeneratedClassPath)) continue;
|
||||||
|
|
||||||
|
FString MenuName = WidgetMenuString(Data);
|
||||||
|
Type Entry;
|
||||||
|
Entry.MenuName = MenuName;
|
||||||
|
Entry.Class = TSoftClassPtr<UWidget>(FSoftClassPath(GeneratedClassPath));
|
||||||
|
Sorted.Add(MenuName, Entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flatten into the array.
|
||||||
|
AllWidgets.Reserve(Sorted.Num());
|
||||||
|
for (const auto& Pair : Sorted)
|
||||||
|
{
|
||||||
|
AllWidgets.Add(Pair.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<WingWidgets::Type> WingWidgets::Search(const FString& Query, int32 MaxResults, bool Exact)
|
||||||
|
{
|
||||||
|
FString ExtQuery = FString::Printf(TEXT("*%s*"), *Query.Replace(TEXT(" "), TEXT("*")));
|
||||||
|
TArray<Type> Results;
|
||||||
|
for (const Type& Entry : AllWidgets)
|
||||||
|
{
|
||||||
|
if (Results.Num() >= MaxResults) break;
|
||||||
|
if (Exact)
|
||||||
|
{
|
||||||
|
if (Entry.MenuName.Equals(Query, ESearchCase::IgnoreCase))
|
||||||
|
Results.Add(Entry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Entry.MenuName.MatchesWildcard(ExtQuery, ESearchCase::IgnoreCase))
|
||||||
|
Results.Add(Entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Results;
|
||||||
|
}
|
||||||
|
|
||||||
void WingWidgets::PrintWidgetTree(UWidget* Widget, int32 Depth)
|
void WingWidgets::PrintWidgetTree(UWidget* Widget, int32 Depth)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,13 +1,33 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
|
#include "Components/Widget.h"
|
||||||
|
|
||||||
class UWidgetTree;
|
class UWidgetTree;
|
||||||
class UWidget;
|
struct FAssetData;
|
||||||
|
|
||||||
|
|
||||||
// Utility functions for widget blueprint manipulation.
|
// Utility functions for widget blueprint manipulation.
|
||||||
class WingWidgets
|
class WingWidgets
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static void PrintWidgetTree(UWidget* Widget, int32 Depth);
|
struct Type
|
||||||
|
{
|
||||||
|
FString MenuName;
|
||||||
|
TSoftClassPtr<UWidget> Class;
|
||||||
|
};
|
||||||
|
|
||||||
|
TArray<Type> AllWidgets;
|
||||||
|
|
||||||
|
WingWidgets();
|
||||||
|
|
||||||
|
// Search for widget types whose name matches the query.
|
||||||
|
TArray<Type> Search(const FString& Query, int32 MaxResults, bool Exact);
|
||||||
|
|
||||||
|
// Print out a tree of widgets, just showing the names and types.
|
||||||
|
static void PrintWidgetTree(UWidget* Widget, int32 Depth);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static FString WidgetMenuString(UClass* WidgetClass);
|
||||||
|
static FString WidgetMenuString(const FAssetData &Data);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user