Files
2025-05-18 13:04:45 +08:00

663 lines
22 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SStateTreeDiff.h"
#include "Customizations/StateTreeBindingExtension.h"
#include "DetailsDiff.h"
#include "DetailTreeNode.h"
#include "Editor.h"
#include "Framework/Application/SlateApplication.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "IDetailsView.h"
#include "SDetailsDiff.h"
#include "SlateOptMacros.h"
#include "SStateTreeSplitter.h"
#include "StateTreeDiffControl.h"
#include "StateTreeDiffHelper.h"
#include "StateTreeState.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Layout/SSpacer.h"
#define LOCTEXT_NAMESPACE "SStateTreeDif"
namespace UE::StateTree::Diff
{
SDiffWidget::SDiffWidget()
{
}
SDiffWidget::~SDiffWidget()
{
if (AssetEditorCloseHandle.IsValid())
{
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OnAssetEditorRequestClose().Remove(AssetEditorCloseHandle);
}
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SDiffWidget::Construct(const FArguments& InArgs)
{
check(InArgs._OldAsset || InArgs._NewAsset);
OldAssetPanel.StateTree = TStrongObjectPtr(InArgs._OldAsset);
NewAssetPanel.StateTree = TStrongObjectPtr(InArgs._NewAsset);
OldAssetPanel.RevisionInfo = InArgs._OldRevision;
NewAssetPanel.RevisionInfo = InArgs._NewRevision;
// sometimes we want to clearly identify the assets being diffed (when it's
// not the same asset in each panel)
OldAssetPanel.bShowAssetName = InArgs._ShowAssetNames;
NewAssetPanel.bShowAssetName = InArgs._ShowAssetNames;
if (InArgs._ParentWindow.IsValid())
{
WeakParentWindow = InArgs._ParentWindow;
AssetEditorCloseHandle = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OnAssetEditorRequestClose().AddSP(this, &SDiffWidget::HandleAssetEditorRequestClose);
}
FToolBarBuilder NavToolBarBuilder(TSharedPtr<const FUICommandList>(), FMultiBoxCustomization::None);
NavToolBarBuilder.AddToolBarButton(
FUIAction(
FExecuteAction::CreateSP(this, &SDiffWidget::PrevDiff),
FCanExecuteAction::CreateSP(this, &SDiffWidget::HasPrevDiff)),
NAME_None, LOCTEXT("PrevDiffLabel", "Prev"), LOCTEXT("PrevDiffTooltip", "Go to previous difference"), FSlateIcon(FAppStyle::GetAppStyleSetName(), "BlueprintDif.PrevDiff"));
NavToolBarBuilder.AddToolBarButton(
FUIAction(
FExecuteAction::CreateSP(this, &SDiffWidget::NextDiff),
FCanExecuteAction::CreateSP(this, &SDiffWidget::HasNextDiff)),
NAME_None, LOCTEXT("NextDiffLabel", "Next"), LOCTEXT("NextDiffTooltip", "Go to next difference"), FSlateIcon(FAppStyle::GetAppStyleSetName(), "BlueprintDif.NextDiff"));
DifferencesTreeView = DiffTreeView::CreateTreeView(&Differences);
GenerateDifferencesList();
const auto TextBlock = [](const FText Text) -> TSharedRef<SWidget>
{
return SNew(SBox)
.Padding(FMargin(4.0f, 10.0f))
.VAlign(VAlign_Center)
.HAlign(HAlign_Left)
[
SNew(STextBlock)
.Visibility(EVisibility::HitTestInvisible)
.TextStyle(FAppStyle::Get(), "DetailsView.CategoryTextStyle")
.Text(Text)
];
};
TopRevisionInfoWidget =
SNew(SSplitter)
.Visibility(EVisibility::HitTestInvisible)
+ SSplitter::Slot()
.Value(.2f)
[
SNew(SBox)
]
+ SSplitter::Slot()
.Value(.8f)
[
SNew(SSplitter)
.PhysicalSplitterHandleSize(10.0f)
+ SSplitter::Slot()
.Value(.5f)
[
TextBlock(DiffViewUtils::GetPanelLabel(OldAssetPanel.StateTree.Get(), OldAssetPanel.RevisionInfo, FText()))
]
+ SSplitter::Slot()
.Value(.5f)
[
TextBlock(DiffViewUtils::GetPanelLabel(NewAssetPanel.StateTree.Get(), NewAssetPanel.RevisionInfo, FText()))
]
];
this->ChildSlot
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("Docking.Tab", ".ContentAreaBrush"))
[
SNew(SOverlay)
+ SOverlay::Slot()
.VAlign(VAlign_Top)
[
TopRevisionInfoWidget.ToSharedRef()
]
+ SOverlay::Slot()
[
SNew(SSplitter)
.Orientation(EOrientation::Orient_Vertical)
+ SSplitter::Slot()
.Value(.55f)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 2.0f, 0.0f, 2.0f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.Padding(4.f)
.AutoWidth()
[
NavToolBarBuilder.MakeWidget()
]
+ SHorizontalBox::Slot()
[
SNew(SSpacer)
]
]
+ SVerticalBox::Slot()
[
SNew(SSplitter)
+ SSplitter::Slot()
.Value(.2f)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
[
DifferencesTreeView.ToSharedRef()
]
]
+ SSplitter::Slot()
.Value(.8f)
[
StateTreePanel.Splitter.ToSharedRef()
]
]
]
+ SSplitter::Slot()
.Value(.45f)
[
SAssignNew(DetailsViewContents, SBox)
]
]
]
];
SetDetailsDiff();
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SDiffWidget::HandleAssetEditorRequestClose(UObject* Asset, const EAssetEditorCloseReason CloseReason)
{
if (OldAssetPanel.StateTree.Get()== Asset || NewAssetPanel.StateTree.Get() == Asset || CloseReason == EAssetEditorCloseReason::CloseAllAssetEditors)
{
// Tell our window to close and set our selves to collapsed to try and stop it from ticking
SetVisibility(EVisibility::Collapsed);
if (AssetEditorCloseHandle.IsValid())
{
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OnAssetEditorRequestClose().Remove(AssetEditorCloseHandle);
}
if (const TSharedPtr<SWindow> ParentWindow = WeakParentWindow.Pin())
{
ParentWindow->RequestDestroyWindow();
}
}
}
TSharedRef<SDiffWidget> SDiffWidget::CreateDiffWindow(const FText WindowTitle, TNotNull<const UStateTree*> OldStateTree, TNotNull<const UStateTree*> NewStateTree, const FRevisionInfo& OldRevision, const FRevisionInfo& NewRevision)
{
// sometimes we're comparing different revisions of one single asset (other
// times we're comparing two completely separate assets altogether)
const bool bIsSingleAsset = (NewStateTree->GetName() == OldStateTree->GetName());
const TSharedPtr<SWindow> Window = SNew(SWindow)
.Title(WindowTitle)
.ClientSize(FVector2D(1000.f, 800.f));
TSharedRef<SDiffWidget> StateTreeDiff = SNew(SDiffWidget)
.OldAsset(OldStateTree)
.NewAsset(NewStateTree)
.OldRevision(OldRevision)
.NewRevision(NewRevision)
.ShowAssetNames(!bIsSingleAsset)
.ParentWindow(Window);
Window->SetContent(StateTreeDiff);
// Make this window a child of the modal window if we've been spawned while one is active.
const TSharedPtr<SWindow> ActiveModal = FSlateApplication::Get().GetActiveTopLevelWindow();
if (ActiveModal.IsValid())
{
FSlateApplication::Get().AddWindowAsNativeChild(Window.ToSharedRef(), ActiveModal.ToSharedRef());
}
else
{
FSlateApplication::Get().AddWindow(Window.ToSharedRef());
}
return StateTreeDiff;
}
TSharedRef<SDiffWidget> SDiffWidget::CreateDiffWindow(TNotNull<const UStateTree*> OldStateTree, TNotNull<const UStateTree*> NewStateTree,
const FRevisionInfo& OldRevision, const FRevisionInfo& NewRevision, const UClass* StateTreeClass)
{
// sometimes we're comparing different revisions of one single asset (other
// times we're comparing two completely separate assets altogether)
//@TODO use pathname instead of asset name.
const bool bIsSingleAsset = NewStateTree->GetFName() == OldStateTree->GetFName();
FText WindowTitle = FText::Format(LOCTEXT("NamelessStateTreeDiff", "{0} Diff (experimental)"), StateTreeClass->GetDisplayNameText());
// if we're diffing one asset against itself
if (bIsSingleAsset)
{
// identify the assumed single asset in the window's title
const FString STName = NewStateTree->GetName();
WindowTitle = FText::Format(LOCTEXT("NamedStateTreeDiff", "{0} - {1} Diff (experimental)"), FText::FromString(STName), StateTreeClass->GetDisplayNameText());
}
return CreateDiffWindow(WindowTitle, OldStateTree, NewStateTree, OldRevision, NewRevision);
}
void SDiffWidget::NextDiff() const
{
DiffTreeView::HighlightNextDifference(DifferencesTreeView.ToSharedRef(), Differences, Differences);
}
void SDiffWidget::PrevDiff() const
{
DiffTreeView::HighlightPrevDifference(DifferencesTreeView.ToSharedRef(), Differences, Differences);
}
bool SDiffWidget::HasNextDiff() const
{
return DiffTreeView::HasNextDifference(DifferencesTreeView.ToSharedRef(), Differences);
}
bool SDiffWidget::HasPrevDiff() const
{
return DiffTreeView::HasPrevDifference(DifferencesTreeView.ToSharedRef(), Differences);
}
void SDiffWidget::GenerateDifferencesList()
{
Differences.Empty();
GenerateDiffPanel();
DifferencesTreeView->RebuildList();
}
void SDiffWidget::GenerateDiffPanel()
{
const UStateTree* OldStateTree = OldAssetPanel.StateTree.Get();
const UStateTree* NewStateTree = NewAssetPanel.StateTree.Get();
StateTreePanel.DiffControl = MakeShared<FDiffControl>(
OldStateTree,
NewStateTree,
FOnDiffEntryFocused{});
StateTreePanel.DiffControl->GenerateTreeEntries(Differences);
StateTreePanel.DiffControl->GetOnStateDiffEntryFocused().AddSP(this, &SDiffWidget::HandleStateDiffEntryFocused);
TSharedPtr<SDiffSplitter> DiffSplitter = SNew(SDiffSplitter);
if (OldAssetPanel.StateTree)
{
DiffSplitter->AddSlot(
SDiffSplitter::Slot()
.Value(0.5f)
.StateTreeView(StateTreePanel.DiffControl->GetDetailsWidget(OldStateTree))
.StateTree(OldStateTree));
}
if (NewAssetPanel.StateTree)
{
DiffSplitter->AddSlot(
SDiffSplitter::Slot()
.Value(0.5f)
.StateTreeView(StateTreePanel.DiffControl->GetDetailsWidget(NewStateTree))
.StateTree(NewStateTree));
}
StateTreePanel.Splitter = DiffSplitter;
}
void SDiffWidget::HandleStateDiffEntryFocused(const FSingleDiffEntry& StateDiff)
{
const FStateSoftPath LeftStatePath = StateDiff.Identifier;
const FStateSoftPath RightStatePath = StateDiff.SecondaryIdentifier ? StateDiff.SecondaryIdentifier : StateDiff.Identifier;
StateTreePanel.Splitter->HandleSelectionChanged(LeftStatePath, RightStatePath);
const UStateTree* OldStateTree = OldAssetPanel.StateTree.Get();
const UStateTree* NewStateTree = NewAssetPanel.StateTree.Get();
const UStateTreeState* OldState = OldStateTree != nullptr ? LeftStatePath.ResolvePath(OldStateTree) : nullptr;
const UStateTreeState* NewState = NewStateTree != nullptr ? RightStatePath.ResolvePath(NewStateTree) : nullptr;
// If comparing states that exist in both state trees display them in the details diff view
if (OldState && NewState)
{
SetDetailsDiff(OldState, NewState);
}
// If we clear selection on both state trees we can display an empty details diff view
else if (!OldState && !NewState)
{
SetDetailsDiff();
}
// If the state only exists in one of the state trees (either added or removed), details diff view will not work.
else
{
// So the states are put into separate details views
const TSharedPtr<SBox> LeftWidget = SNew(SBox);
const TSharedPtr<SBox> RightWidget = SNew(SBox);
if (OldState)
{
const FDetailsDiff DetailsDiff(OldState, true);
LeftWidget->SetContent(DetailsDiff.DetailsWidget());
}
if (NewState)
{
const FDetailsDiff DetailsDiff(NewState, false);
RightWidget->SetContent(DetailsDiff.DetailsWidget());
}
// And displayed in a way that resembles the details diff view
DetailsViewContents->SetContent(
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("Docking.Tab", ".ContentAreaBrush"))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 2.0f, 0.0f, 2.0f)
+ SVerticalBox::Slot()
[
SNew(SSplitter)
+ SSplitter::Slot()
.Value(.2f)
+ SSplitter::Slot()
.Value(.8f)
[
SNew(SSplitter)
.PhysicalSplitterHandleSize(5.f)
+ SSplitter::Slot()
.Value(.5f)
[
SNew(SBox)
.Padding(15.f, 0.f, 15.f, 0.f)
[
LeftWidget.ToSharedRef()
]
]
+ SSplitter::Slot()
.Value(.5f)
[
SNew(SBox)
.Padding(15.f, 0.f, 15.f, 0.f)
[
RightWidget.ToSharedRef()
]
]
]
]
]
);
}
}
void SDiffWidget::SetDetailsDiff(const UStateTreeState* OldState, const UStateTreeState* NewState)
{
const UObject* OldAsset = OldState ? OldState : OldAssetPanel.StateTree ? static_cast<const UObject*>(OldAssetPanel.StateTree->EditorData) : nullptr;
const UObject* NewAsset = NewState ? NewState : NewAssetPanel.StateTree ? static_cast<const UObject*>(NewAssetPanel.StateTree->EditorData) : nullptr;
if (const bool bIsState = OldState || NewState)
{
StateBindingDiffs.Reset(StateTreePanel.DiffControl->GetBindingDifferences().Num());
for (const FSingleDiffEntry& BindingDiff : StateTreePanel.DiffControl->GetBindingDifferences())
{
const UStateTree* OldStateTree = OldAssetPanel.StateTree.Get();
const UStateTree* NewStateTree = NewAssetPanel.StateTree.Get();
if (OldStateTree != nullptr
&& NewStateTree != nullptr
&& BindingDiff.Identifier.ResolvePath(OldStateTree) == OldState
&& BindingDiff.SecondaryIdentifier.ResolvePath(NewStateTree) == NewState)
{
StateBindingDiffs.Push(BindingDiff);
}
}
}
else
{
StateBindingDiffs.Reset();
}
TArray<FSingleObjectDiffEntry> Entries;
const TSharedRef<SDetailsDiff> DetailsDiff = SNew(SDetailsDiff)
.OldAsset(OldAsset)
.NewAsset(NewAsset)
.OldRevision(OldAssetPanel.RevisionInfo)
.NewRevision(NewAssetPanel.RevisionInfo)
.ShowAssetNames(false)
.OnCustomizeDetailsWidget_Static(&SDiffWidget::AddStateTreeExtensionToDetailsView)
.OnGenerateCustomDiffEntries(this, &SDiffWidget::AddBindingDiffToDiffEntries)
.OnOrganizeDiffEntries_Static(&SDiffWidget::OrganizeDiffEntries, OldState, NewState)
.OnGenerateCustomDiffEntryWidget_Static(&SDiffWidget::GenerateCustomDiffEntryWidget, OldState, NewState)
.RowHighlightColor_Static(&SDiffWidget::GetRowHighlightColor)
.ShouldHighlightRow(this, &SDiffWidget::ShouldHighlightRow);
DetailsViewContents->SetContent(DetailsDiff);
}
void SDiffWidget::AddBindingDiffToDiffEntries(TArray<FSingleObjectDiffEntry>& OutEntries)
{
OutEntries.Reserve(StateBindingDiffs.Num());
for (const FSingleDiffEntry& BindingDiff : StateBindingDiffs)
{
EPropertyDiffType::Type DiffType = EPropertyDiffType::Invalid;
switch (BindingDiff.DiffType)
{
case EStateDiffType::BindingAddedToA:
case EStateDiffType::BindingAddedToB:
case EStateDiffType::BindingChanged:
DiffType = EPropertyDiffType::PropertyValueChanged;
break;
}
if (DiffType != EPropertyDiffType::Invalid)
{
FSingleObjectDiffEntry Entry(BindingDiff.BindingPath, DiffType);
OutEntries.Add(Entry);
}
}
}
void SDiffWidget::OrganizeDiffEntries(
TArray<TSharedPtr<FBlueprintDifferenceTreeEntry>>& OutDiffTreeEntries,
const TArray<FSingleObjectDiffEntry>& DiffEntries,
TFunctionRef<TSharedPtr<FBlueprintDifferenceTreeEntry>(const FSingleObjectDiffEntry&)> GenerateDiffTreeEntry,
TFunctionRef<TSharedPtr<FBlueprintDifferenceTreeEntry>(FText&)> GenerateCategoryEntry, const UStateTreeState* OldState, const UStateTreeState* NewState)
{
static FText RightRevision = LOCTEXT("NewRevisionIdentifier", "Right Revision");
static FText StateText = LOCTEXT("StateText", "State");
static FText ParameterText = LOCTEXT("ParametersText", "Parameters");
static FText ConditionText = LOCTEXT("EnterConditionsText", "Enter Conditions");
static FText TaskText = LOCTEXT("TasksText", "Tasks");
static FText TransitionText = LOCTEXT("TransitionsText", "Transitions");
static FText ConsiderationText = LOCTEXT("ConsiderationText", "Utility");
TSet<int32> ConditionIndices;
TSet<int32> TaskIndices;
TSet<int32> TransitionIndices;
TSet<int32> ConsiderationIndices;
TArray<TSharedPtr<FBlueprintDifferenceTreeEntry>> ParametersEntries;
TArray<TSharedPtr<FBlueprintDifferenceTreeEntry>> ConditionEntries;
TArray<TSharedPtr<FBlueprintDifferenceTreeEntry>> TaskEntries;
TArray<TSharedPtr<FBlueprintDifferenceTreeEntry>> TransitionEntries;
TArray<TSharedPtr<FBlueprintDifferenceTreeEntry>> ConsiderationEntries;
TArray<TSharedPtr<FBlueprintDifferenceTreeEntry>> StateEntries;
TArray<FSingleObjectDiffEntry> SortedEntries = DiffEntries;
SortedEntries.Sort([](const FSingleObjectDiffEntry& A, const FSingleObjectDiffEntry& B)
{
return A.Identifier.TryReadIndex(0) < B.Identifier.TryReadIndex(0);
});
for (const FSingleObjectDiffEntry& Difference : SortedEntries)
{
constexpr int32 PropertyCountFromRoot = 2; // 2 levels down from the root; first level being the category/parent collection and the second level the property that changed
FSingleObjectDiffEntry SimplifiedEntry(Difference.Identifier.GetRootProperty(PropertyCountFromRoot), Difference.DiffType);
TSharedPtr<FBlueprintDifferenceTreeEntry> Entry = GenerateDiffTreeEntry(SimplifiedEntry);
if (Difference.Identifier.IsSubPropertyMatch(ConditionName))
{
const int32 Index = Difference.Identifier.TryReadIndex(0);
if (!ConditionIndices.Contains(Index))
{
ConditionIndices.Add(Index);
ConditionEntries.Add(Entry);
}
}
else if (Difference.Identifier.IsSubPropertyMatch(TaskName))
{
const int32 Index = Difference.Identifier.TryReadIndex(0);
if (!TaskIndices.Contains(Index))
{
TaskIndices.Add(Index);
TaskEntries.Add(Entry);
}
}
else if (Difference.Identifier.IsSubPropertyMatch(TransitionName))
{
const int32 Index = Difference.Identifier.TryReadIndex(0);
if (!TransitionIndices.Contains(Index))
{
TransitionIndices.Add(Index);
TransitionEntries.Add(Entry);
}
}
else if (Difference.Identifier.IsSubPropertyMatch(ConsiderationName))
{
const int32 Index = Difference.Identifier.TryReadIndex(0);
if (!ConsiderationIndices.Contains(Index))
{
ConsiderationIndices.Add(Index);
ConsiderationEntries.Add(Entry);
}
}
else if (Difference.Identifier.IsSubPropertyMatch(ParameterName))
{
// @todo investigate: currently unable to resolve full property path (FInstancedPropertyBag issue?)
ParametersEntries.Add(GenerateDiffTreeEntry(Difference));
}
else
{
StateEntries.Add(Entry);
}
}
OutDiffTreeEntries.Append(StateEntries);
if (ParametersEntries.Num() > 0)
{
TSharedPtr<FBlueprintDifferenceTreeEntry> ParametersEntry = GenerateCategoryEntry(ParameterText);
ParametersEntry->Children = ParametersEntries;
OutDiffTreeEntries.Push(ParametersEntry);
}
if (ConditionEntries.Num() > 0)
{
TSharedPtr<FBlueprintDifferenceTreeEntry> ConditionEntry = GenerateCategoryEntry(ConditionText);
ConditionEntry->Children = ConditionEntries;
OutDiffTreeEntries.Push(ConditionEntry);
}
if (ConsiderationEntries.Num() > 0)
{
TSharedPtr<FBlueprintDifferenceTreeEntry> ConsiderationEntry = GenerateCategoryEntry(ConsiderationText);
ConsiderationEntry->Children = ConsiderationEntries;
OutDiffTreeEntries.Push(ConsiderationEntry);
}
if (TaskEntries.Num() > 0)
{
TSharedPtr<FBlueprintDifferenceTreeEntry> TaskEntry = GenerateCategoryEntry(TaskText);
TaskEntry->Children = TaskEntries;
OutDiffTreeEntries.Push(TaskEntry);
}
if (TransitionEntries.Num() > 0)
{
TSharedPtr<FBlueprintDifferenceTreeEntry> TransitionEntry = GenerateCategoryEntry(TransitionText);
TransitionEntry->Children = TransitionEntries;
OutDiffTreeEntries.Push(TransitionEntry);
}
}
TSharedRef<SWidget> SDiffWidget::GenerateCustomDiffEntryWidget(const FSingleObjectDiffEntry& DiffEntry, FText&, const UStateTreeState* OldState, const UStateTreeState* NewState)
{
const UStateTreeState* SourceState = DiffEntry.DiffType == EPropertyDiffType::PropertyAddedToB ? NewState : OldState;
FText PropertyName = FText::FromString(DiffEntry.Identifier.ToDisplayName());
if (DiffEntry.Identifier.IsSubPropertyMatch(ConditionName))
{
const int32 ConditionIndex = DiffEntry.Identifier.TryReadIndex(0);
const FStateTreeEditorNode& ConditionEntry = SourceState->EnterConditions[ConditionIndex];
PropertyName = FText::Format(FText::FromString(TEXT("[{0}]")), FText::FromName(ConditionEntry.GetName()));
}
else if (DiffEntry.Identifier.IsSubPropertyMatch(TaskName))
{
const int32 TaskIndex = DiffEntry.Identifier.TryReadIndex(0);
const FStateTreeEditorNode& TaskEntry = SourceState->Tasks[TaskIndex];
PropertyName = FText::Format(FText::FromString(TEXT("[{0}]")), FText::FromName(TaskEntry.GetName()));
}
else if (DiffEntry.Identifier.IsSubPropertyMatch(ConsiderationName))
{
const int32 ConsiderationIndex = DiffEntry.Identifier.TryReadIndex(0);
const FStateTreeEditorNode& ConsiderationEntry = SourceState->Considerations[ConsiderationIndex];
PropertyName = FText::Format(FText::FromString(TEXT("[{0}]")), FText::FromName(ConsiderationEntry.GetName()));
}
else if (DiffEntry.Identifier.IsSubPropertyMatch(ParameterName))
{
constexpr int32 NumberOfPathElements = 1;
PropertyName = FText::Format(FText::FromString(TEXT("[{0}]")), FText::FromString(DiffEntry.Identifier.ToDisplayName(NumberOfPathElements)));
}
return SNew(STextBlock)
.Text(GetStateDiffMessage(DiffEntry, PropertyName))
.ToolTipText(GetStateDiffMessage(DiffEntry, PropertyName))
.ColorAndOpacity(GetStateDiffMessageColor(DiffEntry));
}
bool SDiffWidget::ShouldHighlightRow(const TUniquePtr<FAsyncDetailViewDiff::DiffNodeType>& DiffNode)
{
if (DiffNode->DiffResult != ETreeDiffResult::Identical)
{
return true;
}
TSharedPtr<FDetailTreeNode> DetailNode = DiffNode->ValueA.Pin();
DetailNode = DetailNode ? DetailNode : DiffNode->ValueB.Pin();
const FPropertySoftPath PropertySoftPath(DetailNode->GetPropertyPath());
if (PropertySoftPath.ToDisplayName().Len() == 0)
{
return false;
}
if (StateBindingDiffs.Num() > 0)
{
const FSingleDiffEntry& BindingDiff = StateBindingDiffs[0];
return BindingDiff.BindingPath.IsSubPropertyMatch(PropertySoftPath) || BindingDiff.BindingPath == PropertySoftPath;
}
return false;
}
FLinearColor SDiffWidget::GetRowHighlightColor(const TUniquePtr<FAsyncDetailViewDiff::DiffNodeType>& DiffNode)
{
switch (DiffNode->DiffResult)
{
case ETreeDiffResult::MissingFromTree1:
return FLinearColor(0.f, 1.f, 0.f, .7f);
case ETreeDiffResult::MissingFromTree2:
return FLinearColor(1.f, 0.f, 0.f, .7f);
default:
return FLinearColor(1.f, 1.f, 0.f, .7f);
}
}
void SDiffWidget::AddStateTreeExtensionToDetailsView(const TSharedRef<IDetailsView>& DetailsView)
{
DetailsView->SetExtensionHandler(MakeShared<FStateTreeBindingExtension>().ToSharedPtr());
}
} // UE::StateTree::Diff
#undef LOCTEXT_NAMESPACE