L6e : outils LPDumpWidgetTree et LPFixButtonPadding (CX-B09, ERR-034) ; photographie avant et simulation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
ae358cd249
commit
935d11f554
4 changed files with 356 additions and 0 deletions
124
Source/LifePlayEditorTools/LPDumpWidgetTreeCommandlet.cpp
Normal file
124
Source/LifePlayEditorTools/LPDumpWidgetTreeCommandlet.cpp
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
// 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;
|
||||
}
|
||||
25
Source/LifePlayEditorTools/LPDumpWidgetTreeCommandlet.h
Normal file
25
Source/LifePlayEditorTools/LPDumpWidgetTreeCommandlet.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// Portage UE 5.5 - diagnostic de mise en page UMG (ERR-034, ERR-031).
|
||||
//
|
||||
// Liste l'arbre d'un Widget Blueprint : pour chaque widget, sa classe, son nom, puis les
|
||||
// proprietes du widget et de son slot qui different des valeurs par defaut de leur classe.
|
||||
// Lecture seule : rien n'est modifie ni sauvegarde.
|
||||
//
|
||||
// Usage :
|
||||
// UnrealEditor-Cmd.exe <projet> -run=LPDumpWidgetTree -BP=<WidgetBlueprint>
|
||||
// UnrealEditor-Cmd.exe <projet> -run=LPDumpWidgetTree -BP=* (tous, tries par chemin)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Commandlets/Commandlet.h"
|
||||
#include "LPDumpWidgetTreeCommandlet.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class ULPDumpWidgetTreeCommandlet : public UCommandlet
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ULPDumpWidgetTreeCommandlet();
|
||||
virtual int32 Main(const FString& Params) override;
|
||||
};
|
||||
177
Source/LifePlayEditorTools/LPFixButtonPaddingCommandlet.cpp
Normal file
177
Source/LifePlayEditorTools/LPFixButtonPaddingCommandlet.cpp
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
// Portage UE 5.5 - voir LPFixButtonPaddingCommandlet.h
|
||||
|
||||
#include "LPFixButtonPaddingCommandlet.h"
|
||||
|
||||
#include "AssetRegistry/AssetData.h"
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "Blueprint/WidgetTree.h"
|
||||
#include "Components/Button.h"
|
||||
#include "Kismet2/BlueprintEditorUtils.h"
|
||||
#include "Kismet2/KismetEditorUtilities.h"
|
||||
#include "Misc/PackageName.h"
|
||||
#include "UObject/Package.h"
|
||||
#include "UObject/SavePackage.h"
|
||||
#include "WidgetBlueprint.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogLPFixButton, Log, All);
|
||||
|
||||
namespace
|
||||
{
|
||||
// Valeurs de UE 4.27 : Engine/Source/Runtime/SlateCore/Private/Styling/CoreStyle.cpp
|
||||
const FMargin UE4NormalPadding(2.f, 2.f, 2.f, 2.f);
|
||||
const FMargin UE4PressedPadding(2.f, 3.f, 2.f, 1.f);
|
||||
|
||||
FString Str(const FMargin& M)
|
||||
{
|
||||
return FString::Printf(TEXT("(%g,%g,%g,%g)"), M.Left, M.Top, M.Right, M.Bottom);
|
||||
}
|
||||
}
|
||||
|
||||
ULPFixButtonPaddingCommandlet::ULPFixButtonPaddingCommandlet()
|
||||
{
|
||||
IsClient = false;
|
||||
IsServer = false;
|
||||
IsEditor = true;
|
||||
LogToConsole = true;
|
||||
}
|
||||
|
||||
int32 ULPFixButtonPaddingCommandlet::Main(const FString& Params)
|
||||
{
|
||||
const bool bDryRun = Params.Contains(TEXT("DryRun"), ESearchCase::IgnoreCase);
|
||||
|
||||
// Defaut UE5 lu sur le CDO plutot que recopie : c'est lui que recoivent les boutons 4.27.
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
const FButtonStyle& UE5Style = GetDefault<UButton>()->WidgetStyle;
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
const FMargin UE5Normal = UE5Style.NormalPadding;
|
||||
const FMargin UE5Pressed = UE5Style.PressedPadding;
|
||||
|
||||
UE_LOG(LogLPFixButton, Warning, TEXT("=== Marges de UButton : defaut UE5 Normal=%s Pressed=%s -> UE4 Normal=%s Pressed=%s%s ==="),
|
||||
*Str(UE5Normal), *Str(UE5Pressed), *Str(UE4NormalPadding), *Str(UE4PressedPadding),
|
||||
bDryRun ? TEXT(" [DRY RUN]") : TEXT(""));
|
||||
|
||||
FAssetRegistryModule& AssetRegistryModule =
|
||||
FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
|
||||
AssetRegistryModule.Get().SearchAllAssets(true);
|
||||
|
||||
TArray<FAssetData> Assets;
|
||||
AssetRegistryModule.Get().GetAssetsByClass(UWidgetBlueprint::StaticClass()->GetClassPathName(), Assets, true);
|
||||
|
||||
int32 ButtonsSeen = 0;
|
||||
int32 ButtonsFixed = 0;
|
||||
int32 ButtonsKept = 0;
|
||||
int32 BlueprintsModified = 0;
|
||||
int32 SaveFailures = 0;
|
||||
|
||||
for (const FAssetData& AssetData : Assets)
|
||||
{
|
||||
if (!AssetData.PackageName.ToString().StartsWith(TEXT("/Game/")))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
UWidgetBlueprint* Blueprint = Cast<UWidgetBlueprint>(AssetData.GetAsset());
|
||||
if (!Blueprint || !Blueprint->WidgetTree)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TArray<UWidget*> Widgets;
|
||||
Blueprint->WidgetTree->GetAllWidgets(Widgets);
|
||||
|
||||
int32 FixedHere = 0;
|
||||
for (UWidget* Widget : Widgets)
|
||||
{
|
||||
UButton* Button = Cast<UButton>(Widget);
|
||||
if (!Button)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
++ButtonsSeen;
|
||||
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
FButtonStyle Style = Button->WidgetStyle;
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
const bool bNormal = Style.NormalPadding == UE5Normal;
|
||||
const bool bPressed = Style.PressedPadding == UE5Pressed;
|
||||
if (!bNormal && !bPressed)
|
||||
{
|
||||
++ButtonsKept;
|
||||
UE_LOG(LogLPFixButton, Display, TEXT(" garde %s :: %s Normal=%s Pressed=%s"),
|
||||
*Blueprint->GetName(), *Button->GetName(), *Str(Style.NormalPadding), *Str(Style.PressedPadding));
|
||||
continue;
|
||||
}
|
||||
|
||||
UE_LOG(LogLPFixButton, Warning, TEXT(" %s :: %s Normal %s%s | Pressed %s%s"),
|
||||
*Blueprint->GetName(), *Button->GetName(),
|
||||
*Str(Style.NormalPadding), bNormal ? TEXT(" -> UE4") : TEXT(" (garde)"),
|
||||
*Str(Style.PressedPadding), bPressed ? TEXT(" -> UE4") : TEXT(" (garde)"));
|
||||
++ButtonsFixed;
|
||||
++FixedHere;
|
||||
|
||||
if (bDryRun)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (bNormal)
|
||||
{
|
||||
Style.NormalPadding = UE4NormalPadding;
|
||||
}
|
||||
if (bPressed)
|
||||
{
|
||||
Style.PressedPadding = UE4PressedPadding;
|
||||
}
|
||||
Button->Modify();
|
||||
Button->SetStyle(Style);
|
||||
}
|
||||
|
||||
if (FixedHere == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (bDryRun)
|
||||
{
|
||||
// Liste des paquets a sauvegarder (Snapshot) avant l'application reelle.
|
||||
UE_LOG(LogLPFixButton, Warning, TEXT(" paquet %s (%d bouton(s))"), *Blueprint->GetOutermost()->GetName(), FixedHere);
|
||||
continue;
|
||||
}
|
||||
|
||||
++BlueprintsModified;
|
||||
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
|
||||
FKismetEditorUtilities::CompileBlueprint(Blueprint);
|
||||
|
||||
UPackage* Package = Blueprint->GetOutermost();
|
||||
FString Filename;
|
||||
if (!FPackageName::TryConvertLongPackageNameToFilename(
|
||||
Package->GetName(), Filename, FPackageName::GetAssetPackageExtension()))
|
||||
{
|
||||
++SaveFailures;
|
||||
UE_LOG(LogLPFixButton, Error, TEXT(" chemin introuvable pour %s"), *Package->GetName());
|
||||
continue;
|
||||
}
|
||||
|
||||
ResetLoaders(Package);
|
||||
|
||||
FSavePackageArgs SaveArgs;
|
||||
SaveArgs.TopLevelFlags = RF_Public | RF_Standalone;
|
||||
SaveArgs.SaveFlags = SAVE_None;
|
||||
SaveArgs.bWarnOfLongFilename = true;
|
||||
// GWarn et non GError : en commandlet, GError rend toute erreur de save fatale.
|
||||
SaveArgs.Error = GWarn;
|
||||
|
||||
if (UPackage::SavePackage(Package, nullptr, *Filename, SaveArgs))
|
||||
{
|
||||
UE_LOG(LogLPFixButton, Warning, TEXT(" -> %s sauvegarde (%d bouton(s))."), *Package->GetName(), FixedHere);
|
||||
}
|
||||
else
|
||||
{
|
||||
++SaveFailures;
|
||||
UE_LOG(LogLPFixButton, Error, TEXT(" -> ECHEC de sauvegarde de %s"), *Package->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
UE_LOG(LogLPFixButton, Warning,
|
||||
TEXT("=== Termine : %d bouton(s) vu(s), %d a la marge UE5 %s, %d garde(s), %d Widget Blueprint(s) modifie(s), %d echec(s) de sauvegarde. ==="),
|
||||
ButtonsSeen, ButtonsFixed, bDryRun ? TEXT("(a corriger)") : TEXT("corrige(s)"), ButtonsKept, BlueprintsModified, SaveFailures);
|
||||
|
||||
return SaveFailures > 0 ? 1 : 0;
|
||||
}
|
||||
30
Source/LifePlayEditorTools/LPFixButtonPaddingCommandlet.h
Normal file
30
Source/LifePlayEditorTools/LPFixButtonPaddingCommandlet.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// Portage UE 5.5 - correctif CX-B09 (ERR-034).
|
||||
//
|
||||
// Le style par defaut d'un UButton a change entre les moteurs :
|
||||
// UE 4.27 : FCoreStyle "Button" -> NormalPadding (2,2,2,2), PressedPadding (2,3,2,1)
|
||||
// UE 5.5 : FUMGCoreStyle "Button" -> NormalPadding (12,1.5,12,1.5), PressedPadding (12,1.5,12,1.5)
|
||||
// Les assets 4.27 n'enregistrent la marge que si elle differe du defaut UE4 : charges en 5.5,
|
||||
// leurs boutons prennent donc 20 px de large en plus (barre de temps de UI/TopRight decalee).
|
||||
//
|
||||
// La commandlet parcourt les Widget Blueprints de /Game et, pour chaque UButton dont une marge
|
||||
// vaut exactement le defaut UE5, remet le defaut UE4. Une marge differente (choisie par
|
||||
// l'auteur) n'est pas touchee.
|
||||
//
|
||||
// Usage :
|
||||
// UnrealEditor-Cmd.exe <projet> -run=LPFixButtonPadding [-DryRun]
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Commandlets/Commandlet.h"
|
||||
#include "LPFixButtonPaddingCommandlet.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class ULPFixButtonPaddingCommandlet : public UCommandlet
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ULPFixButtonPaddingCommandlet();
|
||||
virtual int32 Main(const FString& Params) override;
|
||||
};
|
||||
Loading…
Reference in a new issue