241 lines
7.8 KiB
C++
241 lines
7.8 KiB
C++
|
|
// 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;
|
||
|
|
}
|