// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "SCSDiff.h" #if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_5 #include "CoreMinimal.h" #include "Algo/Transform.h" #endif // UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_5 #include "DetailsDiff.h" #include "DiffResults.h" #include "IAssetTypeActions.h" #include "IDetailsView.h" #include "ReviewComments.h" #include "Widgets/SWidget.h" #include "DiffUtils.h" struct FReviewCommentsDiffControl; class SMultiLineEditableTextBox; struct FDiffResultItem; class SBlueprintDiff; class UEdGraph; struct FEdGraphEditAction; class SCheckBox; class FAsyncDetailViewDiff; /** Interface responsible for generating FBlueprintDifferenceTreeEntry's for visual diff tools */ class KISMET_API IDiffControl { public: virtual ~IDiffControl() {} virtual void Tick() {}; /** Adds widgets to the tree of differences to show */ virtual void GenerateTreeEntries(TArray< TSharedPtr >& OutTreeEntries, TArray< TSharedPtr >& OutRealDifferences) = 0; static FText RightRevision; static TSharedRef GenerateSimpleDiffWidget(FText DiffText); static TSharedRef GenerateObjectDiffWidget(FSingleObjectDiffEntry DiffEntry, FText ObjectName); // to support comment posting, set this to the tree view that contains the comments. void EnableComments(TWeakPtr>> TreeView, const UObject* OldObject, const UObject* NewObject); void EnableComments(TWeakPtr>> TreeView, const TArray& Objects); virtual void EnableComments(TWeakPtr>> TreeView) {} protected: // to be called at the end of GenerateTreeEntries if you want this diff control to support review comments void GenerateCategoryCommentTreeEntries(TArray< TSharedPtr >& , TArray< TSharedPtr >& OutRealDifferences, FString CategoryKey); TSharedPtr ReviewCommentsDiffControl; }; /** Shows all differences for the blueprint structure itself that aren't picked up elsewhere */ class FMyBlueprintDiffControl : public TSharedFromThis, public IDiffControl { public: FMyBlueprintDiffControl(const UBlueprint* InOldBlueprint, const UBlueprint* InNewBlueprint, FOnDiffEntryFocused InSelectionCallback); virtual void GenerateTreeEntries(TArray< TSharedPtr >& OutTreeEntries, TArray< TSharedPtr >& OutRealDifferences) override; // to support comment posting, set this to the tree view that contains the comments. virtual void EnableComments(TWeakPtr>> TreeView) override; private: FOnDiffEntryFocused SelectionCallback; const UBlueprint* OldBlueprint; const UBlueprint* NewBlueprint; }; /** * Each difference in the tree will either be a tree node that is added in one Blueprint * or a tree node and an FName of a property that has been added or edited in one Blueprint */ class FSCSDiffControl : public TSharedFromThis, public IDiffControl { public: FSCSDiffControl(const UBlueprint* InOldBlueprint, const UBlueprint* InNewBlueprint, FOnDiffEntryFocused InSelectionCallback); virtual void GenerateTreeEntries(TArray< TSharedPtr >& OutTreeEntries, TArray< TSharedPtr >& OutRealDifferences) override; TSharedRef OldTreeWidget() { return OldSCS.TreeWidget(); } TSharedRef NewTreeWidget() { return NewSCS.TreeWidget(); } // to support comment posting, set this to the tree view that contains the comments. virtual void EnableComments(TWeakPtr>> TreeView) override; private: FOnDiffEntryFocused SelectionCallback; FSCSDiffRoot DifferingProperties; FSCSDiff OldSCS; FSCSDiff NewSCS; }; /** Generic wrapper around a details view template parameter determines whether TreeEntries are populated */ class KISMET_API FDetailsDiffControl : public TSharedFromThis, public IDiffControl { public: FDetailsDiffControl(const UObject* InOldObject, const UObject* InNewObject, FOnDiffEntryFocused InSelectionCallback, bool bPopulateOutTreeEntries); ~FDetailsDiffControl(); virtual void Tick() override; virtual void GenerateTreeEntries(TArray< TSharedPtr >& OutTreeEntries, TArray< TSharedPtr >& OutRealDifferences) override; void GenerateTreeEntriesWithoutComments(TArray< TSharedPtr >& OutTreeEntries, TArray< TSharedPtr >& OutRealDifferences); TSharedRef InsertObject(const UObject* Object, bool bScrollbarOnLeft = false, int32 Index = INDEX_NONE); TSharedRef GetDetailsWidget(const UObject* Object) const; TSharedPtr TryGetDetailsWidget(const UObject* Object) const; TSharedPtr GetDifferencesWithLeft(const UObject* Object) const; TSharedPtr GetDifferencesWithRight(const UObject* Object) const; int32 IndexOfObject(const UObject* Object) const; // to support comment posting, set this to the tree view that contains the comments. virtual void EnableComments(TWeakPtr>> TreeView) override; DiffUtils::FOnGenerateCustomDiffEntries GenerateCustomEntriesCallback; DiffUtils::FOnGenerateCustomDiffEntryWidget GenerateCustomEntryWidgetCallback; DiffUtils::FOnOrganizeDiffEntries OrganizeEntriesCallback; protected: virtual void OnSelectDiffEntry(FPropertySoftPath PropertyName); TAttribute> GetLinkedScrollRateAttribute(const TSharedRef& OldDetailsView, const TSharedRef& NewDetailsView); // helper function that analyzes two details views and determines the rate they should scroll relative to one another to be in sync TArray GetLinkedScrollRate(TSharedRef LeftDetailsView, TSharedRef RightDetailsView) const; private: void HandlePropertiesChanged(); FDetailsDiffControl& operator=(const FDetailsDiffControl&) = delete; FDetailsDiffControl( const FDetailsDiffControl& ) = delete; FDetailsDiffControl& operator=(FDetailsDiffControl&&) = delete; FDetailsDiffControl( FDetailsDiffControl&& ) = delete; protected: FOnDiffEntryFocused SelectionCallback; TMap DetailsDiffs; TArray ObjectDisplayOrder; struct FPropertyTreeDiffPairs { TSharedPtr Left; TSharedPtr Right; }; TMap PropertyTreeDifferences; TArray< TSharedPtr > Children; const bool bPopulateOutTreeEntries; TSet PropertyAllowList; }; /** Override for CDO special case */ class FCDODiffControl : public FDetailsDiffControl { public: FCDODiffControl(const UObject* InOldObject, const UObject* InNewObject, FOnDiffEntryFocused InSelectionCallback); virtual void GenerateTreeEntries(TArray< TSharedPtr >& OutTreeEntries, TArray< TSharedPtr >& OutRealDifferences) override; }; /** Override for class class settings */ class FClassSettingsDiffControl : public FDetailsDiffControl { public: FClassSettingsDiffControl(const UObject* InOldObject, const UObject* InNewObject, FOnDiffEntryFocused InSelectionCallback); virtual void GenerateTreeEntries(TArray< TSharedPtr >& OutTreeEntries, TArray< TSharedPtr >& OutRealDifferences) override; }; /** Diff control to handle finding type-specific differences */ struct FBlueprintTypeDiffControl : public TSharedFromThis, public IDiffControl { struct FSubObjectDiff { FDiffSingleResult SourceResult; FDetailsDiff OldDetails; FDetailsDiff NewDetails; TArray> Diffs; FSubObjectDiff(const FDiffSingleResult& InSourceResult, const UObject* OldObject, const UObject* NewObject) : SourceResult(InSourceResult) , OldDetails(OldObject) , NewDetails(NewObject) {} }; FBlueprintTypeDiffControl(const UBlueprint* InBlueprintOld, const UBlueprint* InBlueprintNew, FOnDiffEntryFocused InSelectionCallback); /** Generate difference tree widgets */ virtual void GenerateTreeEntries(TArray< TSharedPtr >& OutTreeEntries, TArray< TSharedPtr >& OutRealDifferences) override; // to support comment posting, set this to the tree view that contains the comments. virtual void EnableComments(TWeakPtr>> TreeView) override; /** The old blueprint (left) */ const UBlueprint* BlueprintOld; /** The new blueprint(right) */ const UBlueprint* BlueprintNew; /** Boxes that will display the details diffs */ TSharedPtr OldDetailsBox; TSharedPtr NewDetailsBox; private: /** Generate Widget for top category */ TSharedRef GenerateCategoryWidget(bool bHasRealDiffs); /** Build up the Diff Source Array*/ void BuildDiffSourceArray(); /** Handle selecting a diff */ void OnSelectSubobjectDiff(FPropertySoftPath Identifier, TSharedPtr SubObjectDiff); /** List of objects with differences */ TArray> SubObjectDiffs; /** Source for list view */ TArray> DiffListSource; /** Selection callback */ FOnDiffEntryFocused SelectionCallback; /** Did diff generation succeed? */ bool bDiffSucceeded; }; /** Category list item for a graph*/ struct FGraphToDiff : public TSharedFromThis, IDiffControl { FGraphToDiff(SBlueprintDiff* DiffWidget, UEdGraph* GraphOld, UEdGraph* GraphNew, const FRevisionInfo& RevisionOld, const FRevisionInfo& RevisionNew); virtual ~FGraphToDiff() override; /** Add widgets to the differences tree */ virtual void GenerateTreeEntries(TArray< TSharedPtr >& OutTreeEntries, TArray< TSharedPtr >& OutRealDifferences) override; /** Get old(left) graph*/ UEdGraph* GetGraphOld() const; /** Get new(right) graph*/ UEdGraph* GetGraphNew() const; // to support comment posting, set this to the tree view that contains the comments. virtual void EnableComments(TWeakPtr>> TreeView) override; /** Source for list view */ TArray> DiffListSource; TSharedPtr> FoundDiffs; /** Index of the first item in RealDifferences that was generated by this graph */ int32 RealDifferencesStartIndex = INDEX_NONE; private: /** Get tooltip for category */ FText GetToolTip(); /** Generate Widget for category list */ TSharedRef GenerateCategoryWidget(); /** Called when the Newer Graph is modified*/ void OnGraphChanged(const FEdGraphEditAction& Action); /** Build up the Diff Source Array*/ void BuildDiffSourceArray(); /** Diff widget */ class SBlueprintDiff* DiffWidget; /** The old graph(left)*/ UEdGraph* GraphOld; /** The new graph(right)*/ UEdGraph* GraphNew; /** Description of Old and new graph*/ FRevisionInfo RevisionOld, RevisionNew; /** Handle to the registered OnGraphChanged delegate. */ FDelegateHandle OnGraphChangedDelegateHandle; }; class FCommentTreeEntry : public FBlueprintDifferenceTreeEntry, public TSharedFromThis { public: FCommentTreeEntry(TWeakPtr InCommentsControl, const FReviewComment& InComment, const TArray>& InChildren); virtual ~FCommentTreeEntry() override; static TSharedRef Make(TWeakPtr CommentsControl, const FReviewComment& Comment, const TArray>& Children = {}); int32 GetCommentIDChecked() const; void AwaitCommentPost(); private: // FBlueprintDifferenceTreeEntry callbacks TSharedRef CreateWidget(); // slate callbacks bool IsCommentTextBoxReadonly() const; FReply OnClickReply(); FReply OnClickEdit(); FReply OnLikeToggle(); FText GetLikeTooltip() const; const FSlateBrush* GetLikeIcon() const; FSlateColor GetUsernameColor() const; FSlateColor GetLikeIconColor() const; EVisibility GetEditReplyButtonGroupVisibility() const; EVisibility GetEditButtonVisibility() const; EVisibility GetSubmitCancelButtonGroupVisibility() const; bool IsSubmitButtonEnabled() const; FReply OnEditSubmitClicked(); FReply OnEditCancelClicked(); // comment api callbacks void OnCommentPosted(const FReviewComment& Comment); FString GetCommentString() const; void SetCommentString(const FString& NewComment); // during a pending edit, returns whether the comment is different from the saved version bool HasCommentStringChanged() const; bool IsEditMode() const; void SetEditMode(bool bIsEditMode); FReviewComment Comment; TSharedPtr Content; TSharedPtr EditReplyButtonGroup; TSharedPtr SubmitCancelButtonGroup; TSharedPtr CommentTextBox; TWeakPtr CommentsControl; FDelegateHandle OnCommentPostedHandle; // prefer the use of IsEditMode(). bool bExpectedEditMode = false; }; class FCommentDraftTreeEntry : public FBlueprintDifferenceTreeEntry, public TSharedFromThis { public: FCommentDraftTreeEntry(TWeakPtr InCommentsControl, TArray>* InSiblings, int32 InReplyID = -1); static TSharedRef MakeCommentDraft(TWeakPtr CommentsControl, TArray>* Siblings); static TSharedRef MakeReplyDraft(TWeakPtr CommentsControl, TSharedPtr InParent); void ReassignReplyParent(TSharedPtr InParent); TSharedPtr GetCommentTextBox() const {return CommentTextBox;} bool IsReply() const; private: // FBlueprintDifferenceTreeEntry callbacks TSharedRef CreateWidget(); // slate callbacks bool IsPostButtonEnabled() const; FReply OnCommentPostClicked(); void ReassignSiblings(TArray>* InSiblings, int32 InReplyID); TSharedPtr Content; TSharedPtr CommentTextBox; TSharedPtr FlagAsTaskCheckBox; TWeakPtr CommentsControl; TArray>* Siblings; // if this is a reply, store the comment id of it's parent int32 ReplyID = -1; }; /** Category list item for a graph*/ struct FReviewCommentsDiffControl : public TSharedFromThis, IDiffControl { FReviewCommentsDiffControl(const FString& InCommentFilePath, TWeakPtr>> TreeView); virtual void GenerateTreeEntries(TArray< TSharedPtr >& OutTreeEntries, TArray< TSharedPtr >& OutRealDifferences) override; void SetCategory(const FString& CategoryKey); void PostComment(FReviewComment& Comment); void DraftReply(TSharedPtr ParentComment); void RebuildListView() const; const FString& GetCommentFilePath() {return CommentFilePath;} const FString& GetCommentCategory() {return CommentCategory;} protected: void GenerateCommentThreadRecursive(const FReviewComment& Comment, const TMap>& CommentReplyMap, TArray>& OutTreeEntries); FString CommentFilePath; FString CommentCategory; TWeakPtr>> CommentsTreeView; // we only want one reply draft widget in existence so we can use a static to track it static TWeakPtr ReplyDraftEntry; };