// 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 It(Obj->GetClass()); It; ++It) { const FProperty* Prop = *It; if (Prop->HasAnyPropertyFlags(CPF_Transient | CPF_DuplicateTransient) || Prop->IsA() || Prop->IsA()) { continue; } if (Prop->Identical_InContainer(Obj, Defaults, 0, PPF_None)) { continue; } FString Value; Prop->ExportText_InContainer(0, Value, Obj, Defaults, const_cast(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(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 Tokens, Switches; TMap ParamsMap; ParseCommandLine(*Params, Tokens, Switches, ParamsMap); const FString WantedBP = ParamsMap.FindRef(TEXT("BP")); if (WantedBP.IsEmpty()) { UE_LOG(LogLPWidgetDump, Error, TEXT("Usage : -BP=")); return 1; } FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); AssetRegistryModule.Get().SearchAllAssets(true); TArray 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(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; }