LifePlay-Revival/Source/LifePlayEditorTools/LPEditWidgetCommandlet.cpp

241 lines
7.8 KiB
C++
Raw Normal View History

// Portage UE 5.5 - voir LPEditWidgetCommandlet.h
#include "LPEditWidgetCommandlet.h"
#include "Blueprint/WidgetTree.h"
#include "Components/TextBlock.h"
#include "Components/Widget.h"
#include "Dom/JsonObject.h"
#include "EdGraph/EdGraph.h"
#include "EdGraphSchema_K2.h"
#include "K2Node.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "Misc/FileHelper.h"
#include "Misc/PackageName.h"
#include "Serialization/JsonReader.h"
#include "Serialization/JsonSerializer.h"
#include "UObject/Package.h"
#include "UObject/SavePackage.h"
#include "WidgetBlueprint.h"
DEFINE_LOG_CATEGORY_STATIC(LogLPEditWidget, Log, All);
namespace
{
UEdGraphNode* FindNode(UBlueprint* BP, const FString& GraphName, const FString& NodeName)
{
TArray<UEdGraph*> Graphs;
BP->GetAllGraphs(Graphs);
for (UEdGraph* Graph : Graphs)
{
if (Graph && Graph->GetName() == GraphName)
{
for (UEdGraphNode* Node : Graph->Nodes)
{
if (Node && Node->GetName() == NodeName)
{
return Node;
}
}
}
}
return nullptr;
}
/** Valeur par defaut d'une broche, texte ou non, telle qu'affichee. */
FString PinValue(const UEdGraphPin* Pin)
{
return Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Text ? Pin->DefaultTextValue.ToString() : Pin->DefaultValue;
}
bool ParseVisibility(const FString& Text, ESlateVisibility& Out)
{
const UEnum* Enum = StaticEnum<ESlateVisibility>();
const int64 Value = Enum->GetValueByNameString(Text);
if (Value == INDEX_NONE)
{
return false;
}
Out = static_cast<ESlateVisibility>(Value);
return true;
}
}
ULPEditWidgetCommandlet::ULPEditWidgetCommandlet()
{
IsClient = false;
IsEditor = true;
IsServer = false;
LogToConsole = true;
}
int32 ULPEditWidgetCommandlet::Main(const FString& Params)
{
TArray<FString> Tokens, Switches;
TMap<FString, FString> P;
ParseCommandLine(*Params, Tokens, Switches, P);
const bool bDryRun = Switches.Contains(TEXT("DryRun"));
FString Json;
TSharedPtr<FJsonObject> Spec;
if (!FFileHelper::LoadFileToString(Json, *P.FindRef(TEXT("Spec"))) || !FJsonSerializer::Deserialize(TJsonReaderFactory<>::Create(Json), Spec) || !Spec.IsValid())
{
UE_LOG(LogLPEditWidget, Error, TEXT("Usage : -Spec=<fichier.json> [-DryRun] (fichier illisible : %s)"), *P.FindRef(TEXT("Spec")));
return 1;
}
const FString BPPath = Spec->GetStringField(TEXT("bp"));
UWidgetBlueprint* BP = LoadObject<UWidgetBlueprint>(nullptr, *(BPPath + TEXT(".") + FPackageName::GetShortName(BPPath)));
if (!BP || !BP->WidgetTree)
{
UE_LOG(LogLPEditWidget, Error, TEXT("Blueprint de widget introuvable : %s"), *BPPath);
return 1;
}
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
int32 Errors = 0, Changes = 0;
// 1. Broches.
const TArray<TSharedPtr<FJsonValue>>* Pins = nullptr;
if (Spec->TryGetArrayField(TEXT("pins"), Pins))
{
for (const TSharedPtr<FJsonValue>& V : *Pins)
{
const TSharedPtr<FJsonObject>& O = V->AsObject();
const FString Where = O->GetStringField(TEXT("graph")) + TEXT(" / ") + O->GetStringField(TEXT("node")) + TEXT(" . ") + O->GetStringField(TEXT("pin"));
UEdGraphNode* Node = FindNode(BP, O->GetStringField(TEXT("graph")), O->GetStringField(TEXT("node")));
UEdGraphPin* Pin = Node ? Node->FindPin(*O->GetStringField(TEXT("pin"))) : nullptr;
if (!Pin)
{
UE_LOG(LogLPEditWidget, Error, TEXT("Broche introuvable : %s"), *Where);
++Errors;
continue;
}
const FString Before = PinValue(Pin);
const FString Expect = O->GetStringField(TEXT("expect"));
const FString Value = O->GetStringField(TEXT("value"));
if (Before == Value)
{
UE_LOG(LogLPEditWidget, Warning, TEXT(" %s : deja a la valeur cible"), *Where);
continue;
}
if (Before != Expect)
{
UE_LOG(LogLPEditWidget, Error, TEXT(" %s : valeur actuelle '%s', attendue '%s'"), *Where, *Before, *Expect);
++Errors;
continue;
}
UE_LOG(LogLPEditWidget, Warning, TEXT(" %s : '%s' -> '%s'"), *Where, *Before, *Value);
if (!bDryRun)
{
if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Text)
{
Schema->TrySetDefaultText(*Pin, FText::FromString(Value));
}
else
{
Schema->TrySetDefaultValue(*Pin, Value);
}
if (PinValue(Pin) != Value)
{
UE_LOG(LogLPEditWidget, Error, TEXT(" %s : valeur relue '%s' apres modification"), *Where, *PinValue(Pin));
++Errors;
}
}
++Changes;
}
}
// 2. Elements de l'arbre.
const TArray<TSharedPtr<FJsonValue>>* Widgets = nullptr;
if (Spec->TryGetArrayField(TEXT("widgets"), Widgets))
{
for (const TSharedPtr<FJsonValue>& V : *Widgets)
{
const TSharedPtr<FJsonObject>& O = V->AsObject();
const FString Name = O->GetStringField(TEXT("name"));
UWidget* Widget = BP->WidgetTree->FindWidget(FName(*Name));
if (!Widget)
{
UE_LOG(LogLPEditWidget, Error, TEXT("Element introuvable : %s"), *Name);
++Errors;
continue;
}
FString Text;
if (O->TryGetStringField(TEXT("text"), Text))
{
UTextBlock* TextBlock = Cast<UTextBlock>(Widget);
const FString Before = TextBlock ? TextBlock->GetText().ToString() : FString();
if (!TextBlock || Before != O->GetStringField(TEXT("expect_text")))
{
UE_LOG(LogLPEditWidget, Error, TEXT(" %s : texte actuel '%s', attendu '%s'"), *Name, *Before, *O->GetStringField(TEXT("expect_text")));
++Errors;
}
else
{
UE_LOG(LogLPEditWidget, Warning, TEXT(" %s.Text : '%s' -> '%s'"), *Name, *Before, *Text);
if (!bDryRun) { TextBlock->SetText(FText::FromString(Text)); }
++Changes;
}
}
FString Tooltip;
if (O->TryGetStringField(TEXT("tooltip"), Tooltip))
{
UE_LOG(LogLPEditWidget, Warning, TEXT(" %s.ToolTipText : '%s' -> '%s'"), *Name, *Widget->GetToolTipText().ToString(), *Tooltip);
if (!bDryRun) { Widget->SetToolTipText(FText::FromString(Tooltip)); }
++Changes;
}
FString VisibilityText;
if (O->TryGetStringField(TEXT("visibility"), VisibilityText))
{
ESlateVisibility Visibility;
if (!ParseVisibility(VisibilityText, Visibility))
{
UE_LOG(LogLPEditWidget, Error, TEXT(" %s : visibilite inconnue '%s'"), *Name, *VisibilityText);
++Errors;
continue;
}
const int32 Bound = BP->Bindings.IndexOfByPredicate([&](const FDelegateEditorBinding& B) { return B.ObjectName == Name && B.PropertyName == TEXT("Visibility"); });
UE_LOG(LogLPEditWidget, Warning, TEXT(" %s.Visibility : %s -> %s%s"), *Name, *StaticEnum<ESlateVisibility>()->GetNameStringByValue(int64(Widget->GetVisibility())),
*VisibilityText, Bound != INDEX_NONE ? TEXT(" (liaison de visibilite retiree)") : TEXT(""));
if (!bDryRun)
{
Widget->SetVisibility(Visibility);
if (Bound != INDEX_NONE) { BP->Bindings.RemoveAt(Bound); }
}
++Changes;
}
}
}
if (Errors > 0)
{
UE_LOG(LogLPEditWidget, Error, TEXT("%d erreur(s) : rien n'est sauvegarde"), Errors);
return 1;
}
if (bDryRun || Changes == 0)
{
UE_LOG(LogLPEditWidget, Warning, TEXT("%d modification(s) %s"), Changes, bDryRun ? TEXT("(essai a blanc, rien n'est sauvegarde)") : TEXT(": rien a faire"));
return 0;
}
// 3. Compilation et sauvegarde.
BP->Modify();
FKismetEditorUtilities::CompileBlueprint(BP);
if (BP->Status == BS_Error)
{
UE_LOG(LogLPEditWidget, Error, TEXT("Compilation en erreur : rien n'est sauvegarde"));
return 1;
}
UPackage* Package = BP->GetOutermost();
Package->MarkPackageDirty();
const FString Filename = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension());
FSavePackageArgs SaveArgs;
SaveArgs.TopLevelFlags = RF_Public | RF_Standalone;
SaveArgs.Error = GWarn;
if (!UPackage::SavePackage(Package, nullptr, *Filename, SaveArgs))
{
UE_LOG(LogLPEditWidget, Error, TEXT("Echec de sauvegarde de %s"), *Filename);
return 1;
}
UE_LOG(LogLPEditWidget, Warning, TEXT("%d modification(s), compile sans erreur, sauvegarde : %s"), Changes, *Filename);
return 0;
}