// 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 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(); const int64 Value = Enum->GetValueByNameString(Text); if (Value == INDEX_NONE) { return false; } Out = static_cast(Value); return true; } } ULPEditWidgetCommandlet::ULPEditWidgetCommandlet() { IsClient = false; IsEditor = true; IsServer = false; LogToConsole = true; } int32 ULPEditWidgetCommandlet::Main(const FString& Params) { TArray Tokens, Switches; TMap P; ParseCommandLine(*Params, Tokens, Switches, P); const bool bDryRun = Switches.Contains(TEXT("DryRun")); FString Json; TSharedPtr Spec; if (!FFileHelper::LoadFileToString(Json, *P.FindRef(TEXT("Spec"))) || !FJsonSerializer::Deserialize(TJsonReaderFactory<>::Create(Json), Spec) || !Spec.IsValid()) { UE_LOG(LogLPEditWidget, Error, TEXT("Usage : -Spec= [-DryRun] (fichier illisible : %s)"), *P.FindRef(TEXT("Spec"))); return 1; } const FString BPPath = Spec->GetStringField(TEXT("bp")); UWidgetBlueprint* BP = LoadObject(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(); int32 Errors = 0, Changes = 0; // 1. Broches. const TArray>* Pins = nullptr; if (Spec->TryGetArrayField(TEXT("pins"), Pins)) { for (const TSharedPtr& V : *Pins) { const TSharedPtr& 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>* Widgets = nullptr; if (Spec->TryGetArrayField(TEXT("widgets"), Widgets)) { for (const TSharedPtr& V : *Widgets) { const TSharedPtr& 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(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()->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; }