You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Implement diffing for Widget tree default values
Refactored SBlueprintDiff significantly to avoid code duplication and add comments
Change DiffUtils to not recurse into instanced objects unless they are EditInlineNew and not a component
#codereview dan.oconnor
#rb none
[CL 6334975 by Ben Zeigler in Dev-Framework branch]
738 lines
25 KiB
C++
738 lines
25 KiB
C++
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "GraphDiffControl.h"
|
|
#include "EdGraph/EdGraphNode.h"
|
|
#include "EdGraph/EdGraphPin.h"
|
|
#include "EdGraph/EdGraph.h"
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "GraphDiffControl"
|
|
|
|
//If we are collecting all Diff results, keep going. If we just want to know if there *is* any diffs, we can early out
|
|
#define KEEP_GOING_IF_RESULTS() bHasResult = true; \
|
|
if(!Results.CanStoreResults()) { break; }
|
|
|
|
|
|
/*******************************************************************************
|
|
* Static helper functions
|
|
*******************************************************************************/
|
|
|
|
/** Diff result when a node was added to the graph */
|
|
static void DiffR_NodeAdded( const FGraphDiffControl::FNodeDiffContext& DiffContext, FDiffResults& Results, UEdGraphNode* Node )
|
|
{
|
|
FDiffSingleResult Diff;
|
|
Diff.Diff = EDiffType::NODE_ADDED;
|
|
Diff.Node1 = Node;
|
|
|
|
// Only bother setting up the display data if we're storing the result
|
|
if(Results.CanStoreResults())
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("NodeType"), DiffContext.NodeTypeDisplayName);
|
|
Args.Add(TEXT("NodeTitle"), Node->GetNodeTitle(ENodeTitleType::ListView));
|
|
Diff.ToolTip = FText::Format(LOCTEXT("DIF_AddNode", "Added {NodeType} '{NodeTitle}'"), Args);
|
|
Diff.DisplayString = Diff.ToolTip;
|
|
Diff.DisplayColor = FLinearColor(0.3f,1.0f,0.4f);
|
|
}
|
|
|
|
Results.Add(Diff);
|
|
}
|
|
|
|
/** Diff result when a node was removed from the graph */
|
|
static void DiffR_NodeRemoved( const FGraphDiffControl::FNodeDiffContext& DiffContext, FDiffResults& Results, UEdGraphNode* Node )
|
|
{
|
|
FDiffSingleResult Diff;
|
|
Diff.Diff = EDiffType::NODE_REMOVED;
|
|
Diff.Node1 = Node;
|
|
|
|
// Only bother setting up the display data if we're storing the result
|
|
if(Results.CanStoreResults())
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("NodeType"), DiffContext.NodeTypeDisplayName);
|
|
Args.Add(TEXT("NodeTitle"), Node->GetNodeTitle(ENodeTitleType::ListView));
|
|
Diff.ToolTip = FText::Format(LOCTEXT("DIF_RemoveNode", "Removed {NodeType} '{NodeTitle}'"), Args);
|
|
Diff.DisplayString = Diff.ToolTip;
|
|
Diff.DisplayColor = FLinearColor(1.f,0.4f,0.4f);
|
|
}
|
|
|
|
Results.Add(Diff);
|
|
}
|
|
|
|
/** Diff result when a node comment was changed */
|
|
static void DiffR_NodeCommentChanged(const FGraphDiffControl::FNodeDiffContext& DiffContext, FDiffResults& Results, UEdGraphNode* NewNode, UEdGraphNode* OldNode)
|
|
{
|
|
FDiffSingleResult Diff;
|
|
Diff.Diff = EDiffType::NODE_COMMENT;
|
|
Diff.Node1 = OldNode;
|
|
Diff.Node2 = NewNode;
|
|
|
|
// Only bother setting up the display data if we're storing the result
|
|
if(Results.CanStoreResults())
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("NodeType"), DiffContext.NodeTypeDisplayName);
|
|
Args.Add(TEXT("NodeTitle"), NewNode->GetNodeTitle(ENodeTitleType::ListView));
|
|
Diff.ToolTip = FText::Format(LOCTEXT("DIF_CommentModified", "Comment Modified {NodeType} '{NodeTitle}'"), Args);
|
|
Diff.DisplayString = Diff.ToolTip;
|
|
Diff.DisplayColor = FLinearColor(0.25f,0.4f,0.5f);
|
|
}
|
|
|
|
Results.Add(Diff);
|
|
}
|
|
|
|
/** Diff result when a node was moved on the graph */
|
|
static void DiffR_NodeMoved(const FGraphDiffControl::FNodeDiffContext& DiffContext, FDiffResults& Results, UEdGraphNode* NewNode, UEdGraphNode* OldNode)
|
|
{
|
|
FDiffSingleResult Diff;
|
|
Diff.Diff = EDiffType::NODE_MOVED;
|
|
Diff.Node1 = OldNode;
|
|
Diff.Node2 = NewNode;
|
|
|
|
// Only bother setting up the display data if we're storing the result
|
|
if(Results.CanStoreResults())
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("NodeType"), DiffContext.NodeTypeDisplayName);
|
|
Args.Add(TEXT("NodeTitle"), NewNode->GetNodeTitle(ENodeTitleType::ListView));
|
|
Diff.ToolTip = FText::Format(LOCTEXT("DIF_MoveNode", "Moved {NodeType} '{NodeTitle}'"), Args);
|
|
Diff.DisplayString = Diff.ToolTip;
|
|
Diff.DisplayColor = FLinearColor(0.9f, 0.84f, 0.43f);
|
|
}
|
|
|
|
Results.Add(Diff);
|
|
}
|
|
|
|
/** Diff result when a pin type was changed */
|
|
static void DiffR_PinTypeChanged(FDiffResults& Results, UEdGraphPin* NewPin, UEdGraphPin* OldPin)
|
|
{
|
|
FEdGraphPinType Type1 = OldPin->PinType;
|
|
FEdGraphPinType Type2 = NewPin->PinType;
|
|
|
|
FDiffSingleResult Diff;
|
|
|
|
const UObject* T1Obj = Type1.PinSubCategoryObject.Get();
|
|
const UObject* T2Obj = Type2.PinSubCategoryObject.Get();
|
|
|
|
if(Type1.PinCategory != Type2.PinCategory)
|
|
{
|
|
Diff.Diff = EDiffType::PIN_TYPE_CATEGORY;
|
|
|
|
// Only bother setting up the display data if we're storing the result
|
|
if(Results.CanStoreResults())
|
|
{
|
|
Diff.ToolTip = FText::Format(LOCTEXT("DIF_PinCategoryToolTipFmt", "Pin '{0}' Category was '{1}', but is now '{2}"), FText::FromName(NewPin->PinName), FText::FromName(OldPin->PinType.PinCategory), FText::FromName(NewPin->PinType.PinCategory));
|
|
Diff.DisplayColor = FLinearColor(0.15f,0.53f,0.15f);
|
|
Diff.DisplayString = FText::Format(LOCTEXT("DIF_PinCategoryFmt", "Pin Category '{0}' ['{1}' -> '{2}']"), FText::FromName(NewPin->PinName), FText::FromName(OldPin->PinType.PinCategory), FText::FromName(NewPin->PinType.PinCategory));
|
|
}
|
|
}
|
|
else if(Type1.PinSubCategory != Type2.PinSubCategory)
|
|
{
|
|
Diff.Diff = EDiffType::PIN_TYPE_SUBCATEGORY;
|
|
|
|
// Only bother setting up the display data if we're storing the result
|
|
if(Results.CanStoreResults())
|
|
{
|
|
Diff.ToolTip = FText::Format(LOCTEXT("DIF_PinSubCategoryToolTipFmt", "Pin '{0}' SubCategory was '{1}', but is now '{2}"), FText::FromName(NewPin->PinName), FText::FromName(OldPin->PinType.PinSubCategory), FText::FromName(NewPin->PinType.PinSubCategory));
|
|
Diff.DisplayColor = FLinearColor(0.45f,0.53f,0.65f);
|
|
Diff.DisplayString = FText::Format(LOCTEXT("DIF_PinSubCategoryFmt", "Pin SubCategory '{0}' ['{1}' -> '{2}']"), FText::FromName(NewPin->PinName), FText::FromName(OldPin->PinType.PinSubCategory), FText::FromName(NewPin->PinType.PinSubCategory));
|
|
}
|
|
}
|
|
else if(T1Obj != T2Obj && (T1Obj && T2Obj ) &&
|
|
(T1Obj->GetFName() != T2Obj->GetFName()))
|
|
{
|
|
Diff.Diff = EDiffType::PIN_TYPE_SUBCATEGORY_OBJECT;
|
|
|
|
// Only bother setting up the display data if we're storing the result
|
|
if(Results.CanStoreResults())
|
|
{
|
|
const FName Obj1 = T1Obj->GetFName();
|
|
const FName Obj2 = T2Obj->GetFName();
|
|
|
|
Diff.ToolTip = FText::Format(LOCTEXT("DIF_PinSubCategorObjToolTipFmt", "Pin '{0}' was SubCategoryObject '{1}', but is now '{2}"), FText::FromName(NewPin->PinName), FText::FromName(Obj1), FText::FromName(Obj2));
|
|
Diff.DisplayColor = FLinearColor(0.45f,0.13f,0.25f);
|
|
Diff.DisplayString = FText::Format(LOCTEXT("DIF_PinSubCategoryObjFmt", "Pin SubCategoryObject '{0}' ['{1}' -> '{2}']"), FText::FromName(NewPin->PinName), FText::FromName(Obj1), FText::FromName(Obj2));
|
|
}
|
|
}
|
|
else if(Type1.ContainerType != Type2.ContainerType)
|
|
{
|
|
// TODO: Make the messaging correct about the nature of the diff
|
|
Diff.Diff = EDiffType::PIN_TYPE_IS_ARRAY;
|
|
|
|
// Only bother setting up the display data if we're storing the result
|
|
if(Results.CanStoreResults())
|
|
{
|
|
FText IsArray1 = OldPin->PinType.IsArray() ? LOCTEXT("true", "true") : LOCTEXT("false", "false");
|
|
FText IsArray2 = NewPin->PinType.IsArray() ? LOCTEXT("true", "true") : LOCTEXT("false", "false");
|
|
|
|
Diff.ToolTip = FText::Format(LOCTEXT("DIF_PinIsArrayToolTipFmt", "PinType IsArray for '{0}' modified. Was '{1}', but is now '{2}"), FText::FromName(NewPin->PinName), IsArray1, IsArray2);
|
|
Diff.DisplayString = FText::Format(LOCTEXT("DIF_PinIsArrayFmt", "Pin IsArray '{0}' ['{1}' -> '{2}']"), FText::FromName(NewPin->PinName), IsArray1, IsArray2);
|
|
Diff.DisplayColor = FLinearColor(0.45f,0.33f,0.35f);
|
|
}
|
|
}
|
|
else if(Type1.bIsReference != Type2.bIsReference)
|
|
{
|
|
Diff.Diff = EDiffType::PIN_TYPE_IS_REF;
|
|
|
|
// Only bother setting up the display data if we're storing the result
|
|
if(Results.CanStoreResults())
|
|
{
|
|
FText IsRef1 = OldPin->PinType.bIsReference ? LOCTEXT("true", "true") : LOCTEXT("false", "false");
|
|
FText IsRef2 = NewPin->PinType.bIsReference ? LOCTEXT("true", "true") : LOCTEXT("false", "false");
|
|
|
|
Diff.ToolTip = FText::Format(LOCTEXT("DIF_PinIsRefToolTipFmt", "PinType IsReference for '{0}' modified. Was '{1}', but is now '{2}"), FText::FromName(NewPin->PinName), IsRef1, IsRef2);
|
|
Diff.DisplayColor = FLinearColor(0.25f,0.43f,0.35f);
|
|
Diff.DisplayString = FText::Format(LOCTEXT("DIF_PinIsRefFmt", "Pin IsReference '{0}' ['{1}' -> '{2}']"), FText::FromName(NewPin->PinName), IsRef1, IsRef2);
|
|
}
|
|
}
|
|
|
|
Diff.Pin1 = OldPin;
|
|
Diff.Pin2 = NewPin;
|
|
Results.Add(Diff);
|
|
}
|
|
|
|
/** Diff result when the # of links to a pin was changed */
|
|
static void DiffR_PinLinkCountChanged(FDiffResults& Results, UEdGraphPin* NewPin, UEdGraphPin* OldPin)
|
|
{
|
|
FDiffSingleResult Diff;
|
|
Diff.Diff = NewPin->LinkedTo.Num() > OldPin->LinkedTo.Num() ? EDiffType::PIN_LINKEDTO_NUM_INC : EDiffType::PIN_LINKEDTO_NUM_DEC;
|
|
Diff.Pin2 = NewPin;
|
|
Diff.Pin1 = OldPin;
|
|
|
|
// Only bother setting up the display data if we're storing the result
|
|
if(Results.CanStoreResults())
|
|
{
|
|
if(Diff.Diff == EDiffType::PIN_LINKEDTO_NUM_INC)
|
|
{
|
|
Diff.ToolTip = FText::Format(LOCTEXT("DIF_PinLinkCountIncToolTipFmt", "Pin '{0}' has more links (was {1} now {2})"), FText::FromName(OldPin->PinName), FText::AsNumber(OldPin->LinkedTo.Num()), FText::AsNumber(NewPin->LinkedTo.Num()));
|
|
Diff.DisplayColor = FLinearColor(0.5f,0.3f,0.85f);
|
|
Diff.DisplayString = FText::Format(LOCTEXT("DIF_PinLinkCountIncFmt", "Added Link to '{0}'"), FText::FromName(OldPin->PinName));
|
|
}
|
|
else
|
|
{
|
|
Diff.ToolTip = FText::Format(LOCTEXT("DIF_PinLinkCountDecToolTipFmt", "Pin '{0}' has fewer links (was {1} now {2})"), FText::FromName(OldPin->PinName), FText::AsNumber(OldPin->LinkedTo.Num()), FText::AsNumber(NewPin->LinkedTo.Num()));
|
|
Diff.DisplayColor = FLinearColor(0.5f,0.3f,0.85f);
|
|
Diff.DisplayString = FText::Format(LOCTEXT("DIF_PinLinkCountDecFmt", "Removed Link to '{0}'"), FText::FromName(OldPin->PinName));
|
|
}
|
|
}
|
|
|
|
Results.Add(Diff);
|
|
}
|
|
|
|
/** Diff result when a pin to relinked to a different node */
|
|
static void DiffR_LinkedToNode(FDiffResults& Results, UEdGraphPin* OldPin, UEdGraphPin* NewPin, UEdGraphNode* OldNode, UEdGraphNode* NewNode)
|
|
{
|
|
FDiffSingleResult Diff;
|
|
Diff.Diff = EDiffType::PIN_LINKEDTO_NODE;
|
|
Diff.Pin1 = OldPin;
|
|
Diff.Pin2 = NewPin;
|
|
Diff.Node1 = OldNode;
|
|
Diff.Node2 = NewNode;
|
|
|
|
// Only bother setting up the display data if we're storing the result
|
|
if(Results.CanStoreResults())
|
|
{
|
|
FText Node1Name = OldNode->GetNodeTitle(ENodeTitleType::ListView);
|
|
FText Node2Name = NewNode->GetNodeTitle(ENodeTitleType::ListView);
|
|
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("PinNameForNode1"), FText::FromName(OldPin->PinName));
|
|
Args.Add(TEXT("NodeName1"), Node1Name);
|
|
Args.Add(TEXT("NodeName2"), Node2Name);
|
|
Diff.ToolTip = FText::Format(LOCTEXT("DIF_PinLinkMovedToolTip", "Pin '{PinNameForNode1}' was linked to Node '{NodeName1}', but is now linked to Node '{NodeName2}'"), Args);
|
|
|
|
Diff.DisplayColor = FLinearColor(0.85f,0.71f,0.25f);
|
|
Diff.DisplayString = FText::Format(LOCTEXT("DIF_PinLinkMoved", "Link Moved '{PinNameForNode1}' ['{NodeName1}' -> '{NodeName2}']"), Args);
|
|
}
|
|
|
|
Results.Add(Diff);
|
|
}
|
|
|
|
/** Diff result when a pin default value was changed, and is in use*/
|
|
static void DiffR_PinDefaultValueChanged(FDiffResults& Results, UEdGraphPin* NewPin, UEdGraphPin* OldPin)
|
|
{
|
|
FDiffSingleResult Diff;
|
|
Diff.Diff = EDiffType::PIN_DEFAULT_VALUE;
|
|
Diff.Pin1 = OldPin;
|
|
Diff.Pin2 = NewPin;
|
|
|
|
// Only bother setting up the display data if we're storing the result
|
|
if(Results.CanStoreResults())
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("PinNameForValue1"), FText::FromName(NewPin->PinName));
|
|
Args.Add(TEXT("PinValue1"), FText::FromString(OldPin->GetDefaultAsString()));
|
|
Args.Add(TEXT("PinValue2"), FText::FromString(NewPin->GetDefaultAsString()));
|
|
Diff.ToolTip = FText::Format(LOCTEXT("DIF_PinDefaultValueToolTip", "Pin '{PinNameForValue1}' Default Value was '{PinValue1}', but is now '{PinValue2}"), Args);
|
|
Diff.DisplayColor = FLinearColor(0.665f,0.13f,0.455f);
|
|
Diff.DisplayString = FText::Format(LOCTEXT("DIF_PinDefaultValue", "Pin Default '{PinNameForValue1}' '{PinValue1}' -> '{PinValue2}']"), Args);
|
|
}
|
|
|
|
Results.Add(Diff);
|
|
}
|
|
|
|
/** Diff result when pin count is not the same */
|
|
static void DiffR_NodePinCount(FDiffResults& Results, UEdGraphNode* NewNode, UEdGraphNode* OldNode, const TArray<UEdGraphPin*>& NewPins, const TArray<UEdGraphPin*>& OldPins)
|
|
{
|
|
FText NodeName = NewNode->GetNodeTitle(ENodeTitleType::ListView);
|
|
int32 OriginalCount = OldPins.Num();
|
|
int32 NewCount = NewPins.Num();
|
|
FDiffSingleResult Diff;
|
|
Diff.Diff = EDiffType::NODE_PIN_COUNT;
|
|
Diff.Node1 = OldNode;
|
|
Diff.Node2 = NewNode;
|
|
|
|
// Only bother setting up the display data if we're storing the result
|
|
if(Results.CanStoreResults())
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("NodeName"), NodeName);
|
|
Args.Add(TEXT("OriginalCount"), OriginalCount);
|
|
Args.Add(TEXT("NewCount"), NewCount);
|
|
Diff.DisplayColor = FLinearColor(0.45f,0.4f,0.4f);
|
|
|
|
struct FMatchName
|
|
{
|
|
FMatchName(const FName InPinName)
|
|
: PinName(InPinName)
|
|
{
|
|
}
|
|
|
|
const FName PinName;
|
|
|
|
bool operator()(const UEdGraphPin* Entry )
|
|
{
|
|
return PinName == Entry->PinName;
|
|
}
|
|
};
|
|
|
|
FText ListOfPins;
|
|
TArray< FText > RemovedPins;
|
|
TArray< FText > AddedPins;
|
|
|
|
for (UEdGraphPin* SearchPin : OldPins)
|
|
{
|
|
const UEdGraphPin* const* FoundPin = NewPins.FindByPredicate(FMatchName(SearchPin->PinName));
|
|
if (FoundPin == nullptr)
|
|
{
|
|
RemovedPins.Add(SearchPin->GetDisplayName());
|
|
}
|
|
}
|
|
|
|
for (UEdGraphPin* SearchPin : NewPins)
|
|
{
|
|
const UEdGraphPin* const* FoundPin = OldPins.FindByPredicate(FMatchName(SearchPin->PinName));
|
|
if (FoundPin == nullptr)
|
|
{
|
|
AddedPins.Add(SearchPin->GetDisplayName());
|
|
}
|
|
}
|
|
|
|
if (RemovedPins.Num() > 0 && AddedPins.Num() > 0)
|
|
{
|
|
Diff.DisplayString = FText::Format(LOCTEXT("DIF_NodePinsAddedAndRemoved", "Added and removed Pins from '{NodeName}'"), Args);
|
|
}
|
|
else if (AddedPins.Num() > 0)
|
|
{
|
|
if (AddedPins.Num() == 1)
|
|
{
|
|
Diff.DisplayString = FText::Format(LOCTEXT("DIF_NodePinCountIncreased", "Added Pin to '{NodeName}'"), Args);
|
|
}
|
|
else
|
|
{
|
|
Diff.DisplayString = FText::Format(LOCTEXT("DIF_NodePinCountIncreasedSeveral", "Added Pins to '{NodeName}'"), Args);
|
|
}
|
|
}
|
|
else if (RemovedPins.Num() > 0)
|
|
{
|
|
if (RemovedPins.Num() == 1)
|
|
{
|
|
Diff.DisplayString = FText::Format(LOCTEXT("DIF_NodePinCountDecreased", "Removed Pin from '{NodeName}'"), Args);
|
|
}
|
|
else
|
|
{
|
|
Diff.DisplayString = FText::Format(LOCTEXT("DIF_NodePinCountDecreasedSeveral", "Removed Pins from '{NodeName}'"), Args);
|
|
}
|
|
}
|
|
|
|
FTextBuilder Builder;
|
|
Builder.AppendLine(FText::Format(LOCTEXT("DIF_NodePinCountChangedToolTip", "Node '{NodeName}' had {OriginalCount} Pins, now has {NewCount} Pins"), Args));
|
|
if (AddedPins.Num() > 0)
|
|
{
|
|
Builder.AppendLine(LOCTEXT("DIF_PinsAddedList", "Pins Added:"));
|
|
for (const FText& Added : AddedPins)
|
|
{
|
|
Builder.AppendLine(Added);
|
|
}
|
|
}
|
|
|
|
if (RemovedPins.Num() > 0)
|
|
{
|
|
Builder.AppendLine(LOCTEXT("DIF_PinsRemovedList", "Pins Removed:"));
|
|
for (const FText& Removed : RemovedPins)
|
|
{
|
|
Builder.AppendLine(Removed);
|
|
}
|
|
}
|
|
Diff.ToolTip = Builder.ToText();
|
|
}
|
|
|
|
Results.Add(Diff);
|
|
}
|
|
|
|
/**
|
|
* Populate an array one of pins from another disregarding irrelevant ones (EG invisible).
|
|
*
|
|
* @param InPins Pins
|
|
* @param OutRelevanPins Output of Relevant pins
|
|
*/
|
|
static void BuildArrayOfRelevantPins(const TArray<UEdGraphPin*>& InPins, TArray< UEdGraphPin* >& OutRelevantPins)
|
|
{
|
|
for(int32 i = 0;i<InPins.Num();++i)
|
|
{
|
|
UEdGraphPin* EachPin = InPins[i];
|
|
if( EachPin != nullptr )
|
|
{
|
|
if( EachPin->bHidden == false )
|
|
{
|
|
OutRelevantPins.Add( EachPin );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool IsPinTypeDifferent(const FEdGraphPinType& T1, const FEdGraphPinType& T2)
|
|
{
|
|
bool bIsDifferent = (T1.PinCategory != T2.PinCategory)
|
|
|| (T1.PinSubCategory != T2.PinSubCategory)
|
|
|| (T1.ContainerType != T2.ContainerType)
|
|
|| (T1.bIsReference != T2.bIsReference);
|
|
|
|
const UObject* T1Obj = T1.PinSubCategoryObject.Get();
|
|
const UObject* T2Obj = T2.PinSubCategoryObject.Get();
|
|
//TODO: fix, this code makes no sense
|
|
if((T1Obj != T2Obj) && (T1Obj && T2Obj) && (T1Obj->GetFName() != T2Obj->GetFName()))
|
|
{
|
|
bIsDifferent |= T1Obj->GetFName() == T2Obj->GetFName();
|
|
}
|
|
return bIsDifferent;
|
|
}
|
|
|
|
/** Find Pin in array that matches pin */
|
|
static UEdGraphPin* FindOtherLink(TArray<UEdGraphPin*>& Links2, int32 OriginalIndex, UEdGraphPin* PinToFind)
|
|
{
|
|
//sometimes the order of the pins is different between revisions, although the pins themselves are unchanged, so we have to look at all of them
|
|
UEdGraphNode* Node1 = PinToFind->GetOwningNode();
|
|
for (UEdGraphPin* Other : Links2)
|
|
{
|
|
UEdGraphNode* Node2 = Other->GetOwningNode();
|
|
if(FGraphDiffControl::IsNodeMatch(Node1, Node2))
|
|
{
|
|
return Other;
|
|
}
|
|
}
|
|
return Links2[OriginalIndex];
|
|
}
|
|
|
|
/** Determine if the LinkedTo pins are the same */
|
|
static bool LinkedToDifferent(UEdGraphPin* OldPin, UEdGraphPin* NewPin, const TArray<UEdGraphPin*>& OldLinks, TArray<UEdGraphPin*>& NewLinks, FDiffResults& Results)
|
|
{
|
|
const int32 Size = OldLinks.Num();
|
|
bool bHasResult = false;
|
|
for(int32 i = 0;i<Size;++i)
|
|
{
|
|
UEdGraphPin* OldLinkedPin = OldLinks[i];
|
|
UEdGraphPin* NewLinkedPin = FindOtherLink(NewLinks, i, OldLinkedPin);
|
|
|
|
UEdGraphNode* OldNode = OldLinkedPin->GetOwningNode();
|
|
UEdGraphNode* NewNode = NewLinkedPin->GetOwningNode();
|
|
if(!FGraphDiffControl::IsNodeMatch(OldNode, NewNode))
|
|
{
|
|
DiffR_LinkedToNode(Results, OldPin, NewPin, OldNode, NewNode);
|
|
KEEP_GOING_IF_RESULTS()
|
|
}
|
|
}
|
|
return bHasResult;
|
|
}
|
|
|
|
/**
|
|
* Determine of two Arrays of Pins are different.
|
|
*
|
|
* @param OldPins First set of pins to compare.
|
|
* @param NewPins Second set of pins to compare.
|
|
* @param Results Difference results.
|
|
*
|
|
* returns true if any pins are different and populates the Results array
|
|
*/
|
|
static bool ArePinsDifferent(const TArray<UEdGraphPin*>& OldPins, TArray<UEdGraphPin*>& NewPins, FDiffResults& Results)
|
|
{
|
|
const int32 Size = OldPins.Num();
|
|
bool bHasResult = false;
|
|
for(int32 i = 0;i<Size;++i)
|
|
{
|
|
UEdGraphPin* OldPin = OldPins[i];
|
|
UEdGraphPin* NewPin = NewPins[i];
|
|
|
|
if(IsPinTypeDifferent(OldPin->PinType, NewPin->PinType))
|
|
{
|
|
DiffR_PinTypeChanged(Results, NewPin, OldPin);
|
|
KEEP_GOING_IF_RESULTS()
|
|
}
|
|
if(OldPin->LinkedTo.Num() != NewPin->LinkedTo.Num())
|
|
{
|
|
DiffR_PinLinkCountChanged(Results, NewPin, OldPin);
|
|
KEEP_GOING_IF_RESULTS()
|
|
}
|
|
else if(LinkedToDifferent(OldPin, NewPin, OldPin->LinkedTo, NewPin->LinkedTo, Results))
|
|
{
|
|
KEEP_GOING_IF_RESULTS()
|
|
}
|
|
|
|
if(NewPin->LinkedTo.Num() == 0 && (NewPin->GetDefaultAsString() != OldPin->GetDefaultAsString()))
|
|
{
|
|
DiffR_PinDefaultValueChanged(Results, NewPin, OldPin); //note: some issues with how floating point is stored as string format(0.0 vs 0.00) can cause false diffs
|
|
KEEP_GOING_IF_RESULTS()
|
|
}
|
|
}
|
|
return bHasResult;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* FGraphDiffControl::FNodeMatch
|
|
*******************************************************************************/
|
|
|
|
bool FGraphDiffControl::FNodeMatch::IsValid() const
|
|
{
|
|
return ((NewNode != nullptr) && (OldNode != nullptr));
|
|
}
|
|
|
|
bool FGraphDiffControl::FNodeMatch::Diff(const FNodeDiffContext& DiffContext, TArray<FDiffSingleResult>* OptionalDiffsArray /* = nullptr*/) const
|
|
{
|
|
FDiffResults DiffsOut(OptionalDiffsArray);
|
|
return Diff(DiffContext, DiffsOut);
|
|
}
|
|
|
|
bool FGraphDiffControl::FNodeMatch::Diff(const FNodeDiffContext& DiffContext, FDiffResults& DiffsOut) const
|
|
{
|
|
bool bIsDifferent = false;
|
|
|
|
if (IsValid())
|
|
{
|
|
//has comment changed?
|
|
if((DiffContext.DiffFlags & EDiffFlags::NodeComment) && NewNode->NodeComment != OldNode->NodeComment)
|
|
{
|
|
DiffR_NodeCommentChanged(DiffContext, DiffsOut, NewNode, OldNode);
|
|
bIsDifferent = true;
|
|
}
|
|
|
|
//has it moved?
|
|
if( (DiffContext.DiffFlags & EDiffFlags::NodeMovement) && ( (NewNode->NodePosX != OldNode->NodePosX) || (NewNode->NodePosY != OldNode->NodePosY) ) )
|
|
{
|
|
//same node, different position--
|
|
DiffR_NodeMoved(DiffContext, DiffsOut, NewNode, OldNode);
|
|
bIsDifferent = true;
|
|
}
|
|
|
|
if(DiffContext.DiffFlags & EDiffFlags::NodePins)
|
|
{
|
|
// Build arrays of pins that we care about
|
|
TArray< UEdGraphPin* > OldRelevantPins;
|
|
TArray< UEdGraphPin* > RelevantPins;
|
|
BuildArrayOfRelevantPins(OldNode->Pins, OldRelevantPins);
|
|
BuildArrayOfRelevantPins(NewNode->Pins, RelevantPins);
|
|
|
|
if(OldRelevantPins.Num() == RelevantPins.Num())
|
|
{
|
|
//checks contents of pins
|
|
bIsDifferent |= ArePinsDifferent(OldRelevantPins, RelevantPins, DiffsOut);
|
|
}
|
|
else//# of pins changed
|
|
{
|
|
DiffR_NodePinCount(DiffsOut, NewNode, OldNode, RelevantPins, OldRelevantPins);
|
|
bIsDifferent = true;
|
|
}
|
|
}
|
|
|
|
//Find internal node diffs; skip this if we don't need the result data
|
|
if((DiffContext.DiffFlags & EDiffFlags::NodeSpecificDiffs) && (!bIsDifferent || DiffsOut.CanStoreResults()))
|
|
{
|
|
OldNode->FindDiffs(NewNode, DiffsOut);
|
|
bIsDifferent |= DiffsOut.HasFoundDiffs();
|
|
}
|
|
}
|
|
else if(DiffContext.DiffFlags & EDiffFlags::NodeExistance)
|
|
// one of the nodes is nullptr
|
|
{
|
|
bIsDifferent = true;
|
|
switch (DiffContext.DiffMode)
|
|
{
|
|
case EDiffMode::Additive:
|
|
DiffR_NodeAdded(DiffContext, DiffsOut, NewNode);
|
|
break;
|
|
|
|
case EDiffMode::Subtractive:
|
|
DiffR_NodeRemoved(DiffContext, DiffsOut, NewNode);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return bIsDifferent;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* FGraphDiffControl
|
|
*******************************************************************************/
|
|
|
|
FGraphDiffControl::FNodeMatch FGraphDiffControl::FindNodeMatch(UEdGraph* Graph, UEdGraphNode* Node, TArray<FNodeMatch> const& PriorMatches)
|
|
{
|
|
FNodeMatch Match;
|
|
Match.NewNode = Node;
|
|
|
|
if (Graph)
|
|
{
|
|
// attempt to find a node matching 'Node'
|
|
for (UEdGraphNode* GraphNode : Graph->Nodes)
|
|
{
|
|
if (GraphNode && IsNodeMatch(Node, GraphNode, &PriorMatches))
|
|
{
|
|
Match.OldNode = GraphNode;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Match;
|
|
}
|
|
|
|
bool FGraphDiffControl::IsNodeMatch(UEdGraphNode* Node1, UEdGraphNode* Node2, TArray<FGraphDiffControl::FNodeMatch> const* Exclusions)
|
|
{
|
|
if(Node2->GetClass() != Node1->GetClass())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(Node1->NodeGuid == Node2->NodeGuid)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// we could be diffing two completely separate assets, this makes sure both
|
|
// nodes historically belong to the same graph
|
|
bool bIsIntraAssetDiff = (Node1->GetGraph()->GraphGuid == Node2->GetGraph()->GraphGuid);
|
|
|
|
// if both nodes are from the same graph
|
|
if (bIsIntraAssetDiff)
|
|
{
|
|
return (Node1->GetFName() == Node2->GetFName());
|
|
}
|
|
|
|
if (Exclusions)
|
|
{
|
|
// have to see if this node has already been matched with another
|
|
for (const FGraphDiffControl::FNodeMatch& PriorMatch : *Exclusions)
|
|
{
|
|
if (!PriorMatch.IsValid())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// if one of these nodes has already been matched to a different node
|
|
if (((PriorMatch.OldNode == Node1) && (PriorMatch.NewNode != Node2)) ||
|
|
((PriorMatch.OldNode == Node2) && (PriorMatch.NewNode != Node1)) ||
|
|
((PriorMatch.NewNode == Node1) && (PriorMatch.OldNode != Node2)) ||
|
|
((PriorMatch.NewNode == Node2) && (PriorMatch.OldNode != Node1)))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// the name hashes won't match for nodes from separate graph assets, so we
|
|
// need to look for some kind of semblance between the two... title? (what's displayed to the user)
|
|
FText Title1 = Node1->GetNodeTitle(ENodeTitleType::FullTitle);
|
|
FText Title2 = Node2->GetNodeTitle(ENodeTitleType::FullTitle);
|
|
|
|
return Title1.CompareTo(Title2) == 0;
|
|
}
|
|
|
|
bool FGraphDiffControl::DiffGraphs(UEdGraph* const LhsGraph, UEdGraph* const RhsGraph, TArray<FDiffSingleResult>& DiffsOut)
|
|
{
|
|
bool bFoundDifferences = false;
|
|
|
|
if (LhsGraph && RhsGraph)
|
|
{
|
|
TArray<FGraphDiffControl::FNodeMatch> NodeMatches;
|
|
TSet<UEdGraphNode const*> MatchedRhsNodes;
|
|
|
|
FGraphDiffControl::FNodeDiffContext AdditiveDiffContext;
|
|
AdditiveDiffContext.NodeTypeDisplayName = LOCTEXT("NodeDiffDisplayName", "Node");
|
|
|
|
// march through the all the nodes in the rhs graph and look for matches
|
|
for (UEdGraphNode* const RhsNode : RhsGraph->Nodes)
|
|
{
|
|
if (RhsNode)
|
|
{
|
|
FGraphDiffControl::FNodeMatch NodeMatch = FGraphDiffControl::FindNodeMatch(LhsGraph, RhsNode, NodeMatches);
|
|
// if we found a corresponding node in the lhs graph, track it (so we
|
|
// can prevent future matches with the same nodes)
|
|
if (NodeMatch.IsValid())
|
|
{
|
|
NodeMatches.Add(NodeMatch);
|
|
MatchedRhsNodes.Add(NodeMatch.OldNode);
|
|
}
|
|
|
|
bFoundDifferences |= NodeMatch.Diff(AdditiveDiffContext, &DiffsOut);
|
|
}
|
|
}
|
|
|
|
FGraphDiffControl::FNodeDiffContext SubtractiveDiffContext = AdditiveDiffContext;
|
|
SubtractiveDiffContext.DiffMode = EDiffMode::Subtractive;
|
|
SubtractiveDiffContext.DiffFlags = EDiffFlags::NodeExistance;
|
|
|
|
// go through the lhs nodes to catch ones that may have been missing from the rhs graph
|
|
for (UEdGraphNode* const LhsNode : LhsGraph->Nodes)
|
|
{
|
|
// if this node has already been matched, move on
|
|
if ((LhsNode == nullptr) || MatchedRhsNodes.Find(LhsNode))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FGraphDiffControl::FNodeMatch NodeMatch = FGraphDiffControl::FindNodeMatch(RhsGraph, LhsNode, NodeMatches);
|
|
bFoundDifferences |= NodeMatch.Diff(SubtractiveDiffContext, &DiffsOut);
|
|
}
|
|
}
|
|
|
|
// storing the graph name for all diff entries:
|
|
const FString GraphPath = LhsGraph ? GetGraphPath(LhsGraph) : GetGraphPath(RhsGraph);
|
|
for( FDiffSingleResult& Entry : DiffsOut )
|
|
{
|
|
Entry.OwningObjectPath = GraphPath;
|
|
}
|
|
|
|
return bFoundDifferences;
|
|
}
|
|
|
|
FString FGraphDiffControl::GetGraphPath(UEdGraph* Graph)
|
|
{
|
|
FString GraphPath;
|
|
if (UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(Graph))
|
|
{
|
|
return Graph->GetPathName(Blueprint);
|
|
}
|
|
else if (UPackage* Package = Graph->GetOutermost())
|
|
{
|
|
return Graph->GetPathName(Package);
|
|
}
|
|
else if (Graph)
|
|
{
|
|
return Graph->GetName();
|
|
}
|
|
return FString();
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|