125 lines
3.6 KiB
C++
125 lines
3.6 KiB
C++
|
|
// Voir LPDumpWidgetTreeCommandlet.h
|
||
|
|
|
||
|
|
#include "LPDumpWidgetTreeCommandlet.h"
|
||
|
|
|
||
|
|
#include "AssetRegistry/AssetRegistryModule.h"
|
||
|
|
#include "Blueprint/UserWidget.h"
|
||
|
|
#include "Blueprint/WidgetTree.h"
|
||
|
|
#include "Components/PanelSlot.h"
|
||
|
|
#include "Components/PanelWidget.h"
|
||
|
|
#include "Components/Widget.h"
|
||
|
|
#include "WidgetBlueprint.h"
|
||
|
|
|
||
|
|
DEFINE_LOG_CATEGORY_STATIC(LogLPWidgetDump, Log, All);
|
||
|
|
|
||
|
|
namespace
|
||
|
|
{
|
||
|
|
/** Proprietes de Obj qui different du CDO de sa classe, une par ligne. */
|
||
|
|
void DumpNonDefault(const UObject* Obj, const FString& Indent, const TCHAR* Label)
|
||
|
|
{
|
||
|
|
if (!Obj)
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const UObject* Defaults = Obj->GetClass()->GetDefaultObject();
|
||
|
|
for (TFieldIterator<FProperty> It(Obj->GetClass()); It; ++It)
|
||
|
|
{
|
||
|
|
const FProperty* Prop = *It;
|
||
|
|
if (Prop->HasAnyPropertyFlags(CPF_Transient | CPF_DuplicateTransient) || Prop->IsA<FMulticastDelegateProperty>()
|
||
|
|
|| Prop->IsA<FDelegateProperty>())
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (Prop->Identical_InContainer(Obj, Defaults, 0, PPF_None))
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
FString Value;
|
||
|
|
Prop->ExportText_InContainer(0, Value, Obj, Defaults, const_cast<UObject*>(Obj), PPF_None);
|
||
|
|
if (Value.Len() > 4000)
|
||
|
|
{
|
||
|
|
Value = Value.Left(4000) + TEXT("...");
|
||
|
|
}
|
||
|
|
UE_LOG(LogLPWidgetDump, Warning, TEXT("%s %s.%s = %s"), *Indent, Label, *Prop->GetName(), *Value);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void DumpWidget(const UWidget* Widget, int32 Depth)
|
||
|
|
{
|
||
|
|
if (!Widget)
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const FString Indent = FString::ChrN(Depth * 2, TEXT(' '));
|
||
|
|
UE_LOG(LogLPWidgetDump, Warning, TEXT("%s[%s] %s"), *Indent, *Widget->GetClass()->GetName(), *Widget->GetName());
|
||
|
|
DumpNonDefault(Widget->Slot, Indent, TEXT("slot"));
|
||
|
|
DumpNonDefault(Widget, Indent, TEXT("w"));
|
||
|
|
|
||
|
|
if (const UPanelWidget* Panel = Cast<UPanelWidget>(Widget))
|
||
|
|
{
|
||
|
|
for (int32 i = 0; i < Panel->GetChildrenCount(); ++i)
|
||
|
|
{
|
||
|
|
DumpWidget(Panel->GetChildAt(i), Depth + 1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ULPDumpWidgetTreeCommandlet::ULPDumpWidgetTreeCommandlet()
|
||
|
|
{
|
||
|
|
IsClient = false;
|
||
|
|
IsServer = false;
|
||
|
|
IsEditor = true;
|
||
|
|
LogToConsole = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
int32 ULPDumpWidgetTreeCommandlet::Main(const FString& Params)
|
||
|
|
{
|
||
|
|
TArray<FString> Tokens, Switches;
|
||
|
|
TMap<FString, FString> ParamsMap;
|
||
|
|
ParseCommandLine(*Params, Tokens, Switches, ParamsMap);
|
||
|
|
|
||
|
|
const FString WantedBP = ParamsMap.FindRef(TEXT("BP"));
|
||
|
|
if (WantedBP.IsEmpty())
|
||
|
|
{
|
||
|
|
UE_LOG(LogLPWidgetDump, Error, TEXT("Usage : -BP=<WidgetBlueprint>"));
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
FAssetRegistryModule& AssetRegistryModule =
|
||
|
|
FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
|
||
|
|
AssetRegistryModule.Get().SearchAllAssets(true);
|
||
|
|
|
||
|
|
TArray<FAssetData> Assets;
|
||
|
|
AssetRegistryModule.Get().GetAssetsByClass(UWidgetBlueprint::StaticClass()->GetClassPathName(), Assets, true);
|
||
|
|
|
||
|
|
// -BP=* : tous les Widget Blueprints de /Game, tries par chemin (diff avant/apres d'un correctif).
|
||
|
|
const bool bAll = (WantedBP == TEXT("*"));
|
||
|
|
Assets.Sort([](const FAssetData& A, const FAssetData& B) { return A.PackageName.LexicalLess(B.PackageName); });
|
||
|
|
|
||
|
|
int32 Dumped = 0;
|
||
|
|
for (const FAssetData& AssetData : Assets)
|
||
|
|
{
|
||
|
|
if (!AssetData.PackageName.ToString().StartsWith(TEXT("/Game/"))
|
||
|
|
|| (!bAll && AssetData.AssetName.ToString() != WantedBP))
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
const UWidgetBlueprint* Target = Cast<UWidgetBlueprint>(AssetData.GetAsset());
|
||
|
|
if (!Target || !Target->WidgetTree)
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
++Dumped;
|
||
|
|
UE_LOG(LogLPWidgetDump, Warning, TEXT("=== %s : arbre de widgets ==="), *Target->GetPathName());
|
||
|
|
DumpWidget(Target->WidgetTree->RootWidget, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (Dumped == 0)
|
||
|
|
{
|
||
|
|
UE_LOG(LogLPWidgetDump, Error, TEXT("Widget Blueprint '%s' introuvable sous /Game."), *WantedBP);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|