You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Moved SmartObjects out of restricted folder Moved StateTree out of restricted folder Moved ZoneGraph out of restricted folder Moved ZoneGraphAnnotations out of restricted folder #jira UE-115297 #ROBOMERGE-OWNER: mieszko.zielinski #ROBOMERGE-AUTHOR: mieszko.zielinski #ROBOMERGE-SOURCE: CL 17648223 via CL 17648246 via CL 17648261 via CL 17648385 via CL 17648390 via CL 17648742 #ROBOMERGE-BOT: STARSHIP (Release-Engine-Test -> Main) (v875-17642767) [CL 17648750 by mieszko zielinski in ue5-main branch]
1056 lines
28 KiB
C++
1056 lines
28 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SStateTreeViewRow.h"
|
|
#include "SStateTreeView.h"
|
|
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
#include "ScopedTransaction.h"
|
|
#include "Templates/SharedPointer.h"
|
|
|
|
#include "EditorStyleSet.h"
|
|
#include "EditorFontGlyphs.h"
|
|
#include "StateTreeEditorStyle.h"
|
|
|
|
#include "Widgets/DeclarativeSyntaxSupport.h"
|
|
#include "Widgets/SCompoundWidget.h"
|
|
#include "Widgets/Input/SButton.h"
|
|
#include "Widgets/Text/STextBlock.h"
|
|
#include "Widgets/Layout/SBorder.h"
|
|
#include "Widgets/Layout/SScrollBorder.h"
|
|
#include "Widgets/Images/SImage.h"
|
|
#include "Widgets/Text/SInlineEditableTextBlock.h"
|
|
#include "Widgets/Layout/SScrollBox.h"
|
|
|
|
#include "StateTree.h"
|
|
#include "StateTreeState.h"
|
|
#include "StateTreeViewModel.h"
|
|
#include "Algo/ForEach.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "StateTreeEditor"
|
|
|
|
namespace UE { namespace StateTreeEditor {
|
|
|
|
static void AddUnique(TArray<FText>& Array, const FText& NewItem)
|
|
{
|
|
if (!Array.FindByPredicate([&NewItem](const FText& Item) { return Item.IdenticalTo(NewItem); }))
|
|
{
|
|
Array.Add(NewItem);
|
|
}
|
|
}
|
|
|
|
}} // UE::StateTreeEditor
|
|
|
|
void SStateTreeViewRow::Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView, UStateTreeState* InState, TSharedRef<FStateTreeViewModel> InStateTreeViewModel)
|
|
{
|
|
StateTreeViewModel = InStateTreeViewModel;
|
|
WeakState = InState;
|
|
|
|
STableRow<UStateTreeState*>::ConstructInternal(STableRow::FArguments()
|
|
.Padding(5.f)
|
|
.OnDragDetected(this, &SStateTreeViewRow::HandleDragDetected)
|
|
.OnCanAcceptDrop(this, &SStateTreeViewRow::HandleCanAcceptDrop)
|
|
.OnAcceptDrop(this, &SStateTreeViewRow::HandleAcceptDrop)
|
|
.Style(&FStateTreeEditorStyle::Get()->GetWidgetStyle<FTableRowStyle>("StateTree.Selection"))
|
|
, InOwnerTableView);
|
|
|
|
static const FLinearColor TasksBackground = FLinearColor(FColor(17, 117, 131));
|
|
static const FLinearColor EvaluatorsBackground = FLinearColor(FColor(48, 48, 48));
|
|
|
|
this->ChildSlot
|
|
.HAlign(HAlign_Fill)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Fill)
|
|
.HAlign(HAlign_Left)
|
|
.AutoWidth()
|
|
[
|
|
SNew(SExpanderArrow, SharedThis(this))
|
|
.ShouldDrawWires(true)
|
|
.IndentAmount(32)
|
|
.BaseIndentLevel(0)
|
|
]
|
|
|
|
// State Box
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(FMargin(0.0f, 4.0f))
|
|
.AutoWidth()
|
|
[
|
|
SNew(SBox)
|
|
.HeightOverride(28.0f)
|
|
.VAlign(VAlign_Fill)
|
|
[
|
|
SNew(SBorder)
|
|
.BorderImage(FEditorStyle::GetBrush("WhiteBrush"))
|
|
.BorderBackgroundColor(this, &SStateTreeViewRow::GetTitleColor)
|
|
.Padding(FMargin(16.0f, 0.0f, 16.0f, 0.0f))
|
|
[
|
|
// Conditions icon
|
|
SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
[
|
|
SNew(SBox)
|
|
.Padding(FMargin(0.0f, 0.0f, 4.0f, 0.0f))
|
|
.Visibility(this, &SStateTreeViewRow::GetConditionVisibility)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(FEditorFontGlyphs::Question)
|
|
.TextStyle(FStateTreeEditorStyle::Get(), "StateTree.Icon")
|
|
]
|
|
]
|
|
|
|
// Selector icon
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
[
|
|
SNew(SBox)
|
|
.Padding(FMargin(0.0f, 0.0f, 4.0f, 0.0f))
|
|
.Visibility(this, &SStateTreeViewRow::GetSelectorVisibility)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(FEditorFontGlyphs::Level_Down)
|
|
.TextStyle(FStateTreeEditorStyle::Get(), "StateTree.Icon")
|
|
]
|
|
]
|
|
|
|
// State Name
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
[
|
|
SAssignNew(NameTextBlock, SInlineEditableTextBlock)
|
|
.Style(FStateTreeEditorStyle::Get(), "StateTree.State.TitleInlineEditableText")
|
|
.OnVerifyTextChanged(this, &SStateTreeViewRow::VerifyNodeTextChanged)
|
|
.OnTextCommitted(this, &SStateTreeViewRow::HandleNodeLabelTextCommitted)
|
|
.Text(this, &SStateTreeViewRow::GetStateDesc)
|
|
.Clipping(EWidgetClipping::ClipToBounds)
|
|
.IsSelected(this, &SStateTreeViewRow::IsSelected)
|
|
]
|
|
]
|
|
]
|
|
]
|
|
|
|
// Evaluators Box
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(FMargin(0.0f, 4.0f))
|
|
.AutoWidth()
|
|
[
|
|
SNew(SBox)
|
|
.HeightOverride(28.0f)
|
|
.VAlign(VAlign_Fill)
|
|
.Visibility(this, &SStateTreeViewRow::GetEvaluatorsVisibility)
|
|
[
|
|
SNew(SBorder)
|
|
.BorderImage(FEditorStyle::GetBrush("WhiteBrush"))
|
|
.BorderBackgroundColor(EvaluatorsBackground)
|
|
.Padding(FMargin(12.0f, 0.0f, 16.0f, 0.0f))
|
|
[
|
|
// Evaluator icon
|
|
SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(FMargin(0.0f, 0.0f, 4.0f, 0.0f))
|
|
.AutoWidth()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(FEditorFontGlyphs::Crosshairs)
|
|
.TextStyle(FStateTreeEditorStyle::Get(), "StateTree.DetailsIcon")
|
|
]
|
|
|
|
// Evalutors
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SStateTreeViewRow::GetEvaluatorsDesc)
|
|
.TextStyle(FStateTreeEditorStyle::Get(), "StateTree.Details")
|
|
]
|
|
]
|
|
]
|
|
]
|
|
|
|
// Tasks Box
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(FMargin(0.0f, 4.0f))
|
|
.AutoWidth()
|
|
[
|
|
SNew(SBox)
|
|
.HeightOverride(28.0f)
|
|
.VAlign(VAlign_Fill)
|
|
.Visibility(this, &SStateTreeViewRow::GetTasksVisibility)
|
|
[
|
|
SNew(SBorder)
|
|
.BorderImage(FEditorStyle::GetBrush("WhiteBrush"))
|
|
.BorderBackgroundColor(TasksBackground)
|
|
.Padding(FMargin(12.0f, 0.0f, 16.0f, 0.0f))
|
|
[
|
|
// Task icon
|
|
SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(FMargin(0.0f, 0.0f, 4.0f, 0.0f))
|
|
.AutoWidth()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(FEditorFontGlyphs::Paper_Plane)
|
|
.TextStyle(FStateTreeEditorStyle::Get(), "StateTree.DetailsIcon")
|
|
]
|
|
|
|
// Tasks list
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SStateTreeViewRow::GetTasksDesc)
|
|
.TextStyle(FStateTreeEditorStyle::Get(), "StateTree.Details")
|
|
]
|
|
]
|
|
]
|
|
]
|
|
|
|
// Completed transitions
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
.Padding(FMargin(8.0f, 0.0f, 0, 0.0f))
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SStateTreeViewRow::GetCompletedTransitionsIcon)
|
|
.TextStyle(FStateTreeEditorStyle::Get(), "StateTree.Icon")
|
|
.Visibility(this, &SStateTreeViewRow::GetCompletedTransitionVisibility)
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
.Padding(FMargin(4.0f, 0, 0, 0))
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SStateTreeViewRow::GetCompletedTransitionsDesc)
|
|
.TextStyle(FStateTreeEditorStyle::Get(), "StateTree.Details")
|
|
.Visibility(this, &SStateTreeViewRow::GetCompletedTransitionVisibility)
|
|
]
|
|
]
|
|
|
|
// Succeeded transitions
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(FMargin(8.0f, 0.0f, 0, 0))
|
|
.AutoWidth()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(FEditorFontGlyphs::Check_Circle)
|
|
.ColorAndOpacity(FLinearColor(FColor(110,143,67)))
|
|
.TextStyle(FStateTreeEditorStyle::Get(), "StateTree.Icon")
|
|
.Visibility(this, &SStateTreeViewRow::GetSucceededTransitionVisibility)
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(FMargin(4.0f, 0.0f, 0, 0))
|
|
.AutoWidth()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SStateTreeViewRow::GetSucceededTransitionIcon)
|
|
.TextStyle(FStateTreeEditorStyle::Get(), "StateTree.Icon")
|
|
.Visibility(this, &SStateTreeViewRow::GetSucceededTransitionVisibility)
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
.Padding(FMargin(4.0f, 0, 0, 0))
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SStateTreeViewRow::GetSucceededTransitionDesc)
|
|
.TextStyle(FStateTreeEditorStyle::Get(), "StateTree.Details")
|
|
.Visibility(this, &SStateTreeViewRow::GetSucceededTransitionVisibility)
|
|
]
|
|
]
|
|
|
|
// Failed transitions
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(FMargin(8.0f, 0.0f, 0, 0))
|
|
.AutoWidth()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(FEditorFontGlyphs::Times_Circle)
|
|
.ColorAndOpacity(FLinearColor(FColor(187,77,42)))
|
|
.TextStyle(FStateTreeEditorStyle::Get(), "StateTree.Icon")
|
|
.Visibility(this, &SStateTreeViewRow::GetFailedTransitionVisibility)
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(FMargin(4.0f, 0.0f, 0, 0))
|
|
.AutoWidth()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SStateTreeViewRow::GetFailedTransitionIcon)
|
|
.TextStyle(FStateTreeEditorStyle::Get(), "StateTree.Icon")
|
|
.Visibility(this, &SStateTreeViewRow::GetFailedTransitionVisibility)
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
.Padding(FMargin(4.0f, 0, 0, 0))
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SStateTreeViewRow::GetFailedTransitionDesc)
|
|
.TextStyle(FStateTreeEditorStyle::Get(), "StateTree.Details")
|
|
.Visibility(this, &SStateTreeViewRow::GetFailedTransitionVisibility)
|
|
]
|
|
]
|
|
// Transitions
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(FMargin(8.0f, 0.0f, 0, 0))
|
|
.AutoWidth()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(FEditorFontGlyphs::Question_Circle)
|
|
.ColorAndOpacity(FLinearColor(FColor(31,151,167)))
|
|
.TextStyle(FStateTreeEditorStyle::Get(), "StateTree.Icon")
|
|
.Visibility(this, &SStateTreeViewRow::GetConditionalTransitionsVisibility)
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(FMargin(4.0f, 0.0f, 0, 0))
|
|
.AutoWidth()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(FEditorFontGlyphs::Long_Arrow_Right)
|
|
.TextStyle(FStateTreeEditorStyle::Get(), "StateTree.Icon")
|
|
.Visibility(this, &SStateTreeViewRow::GetConditionalTransitionsVisibility)
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
.Padding(FMargin(4.0f, 0, 0, 0))
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SStateTreeViewRow::GetConditionalTransitionsDesc)
|
|
.TextStyle(FStateTreeEditorStyle::Get(), "StateTree.Details")
|
|
.Visibility(this, &SStateTreeViewRow::GetConditionalTransitionsVisibility)
|
|
]
|
|
]
|
|
];
|
|
}
|
|
|
|
void SStateTreeViewRow::RequestRename()
|
|
{
|
|
if (NameTextBlock)
|
|
{
|
|
NameTextBlock->EnterEditingMode();
|
|
}
|
|
}
|
|
|
|
FSlateColor SStateTreeViewRow::GetTitleColor() const
|
|
{
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
if (StateTreeViewModel && StateTreeViewModel->IsSelected(State))
|
|
{
|
|
return FLinearColor(FColor(236, 134, 39));
|
|
}
|
|
if (IsRoutine())
|
|
{
|
|
return FLinearColor(FColor(17, 117, 131));
|
|
}
|
|
}
|
|
|
|
return FLinearColor(FColor(31, 151, 167));
|
|
}
|
|
|
|
FText SStateTreeViewRow::GetStateDesc() const
|
|
{
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
return FText::FromName(State->Name);
|
|
}
|
|
return FText::FromName(FName());
|
|
}
|
|
|
|
EVisibility SStateTreeViewRow::GetConditionVisibility() const
|
|
{
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
if (IsV2())
|
|
{
|
|
return State->EnterConditions2.Num() > 0 ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
else
|
|
{
|
|
return State->EnterConditions.Num() > 0 ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
EVisibility SStateTreeViewRow::GetSelectorVisibility() const
|
|
{
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
return State->Children.Num() > 0 ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
EVisibility SStateTreeViewRow::GetEvaluatorsVisibility() const
|
|
{
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
int32 ValidCount = 0;
|
|
if (IsV2())
|
|
{
|
|
for (int32 i = 0; i < State->Evaluators2.Num(); i++)
|
|
{
|
|
if (const FStateTreeEvaluator2Base* Eval = State->Evaluators2[i].Type.GetPtr<FStateTreeEvaluator2Base>())
|
|
{
|
|
ValidCount++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int32 i = 0; i < State->Evaluators.Num(); i++)
|
|
{
|
|
if (UStateTreeEvaluatorBase* Eval = State->Evaluators[i])
|
|
{
|
|
ValidCount++;
|
|
}
|
|
}
|
|
}
|
|
return ValidCount > 0 ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
FText SStateTreeViewRow::GetEvaluatorsDesc() const
|
|
{
|
|
const UStateTreeState* State = WeakState.Get();
|
|
if (!State)
|
|
{
|
|
return FText::GetEmpty();
|
|
}
|
|
|
|
TArray<FText> Names;
|
|
|
|
if (IsV2())
|
|
{
|
|
for (int32 i = 0; i < State->Evaluators2.Num(); i++)
|
|
{
|
|
if (const FStateTreeEvaluator2Base* Eval = State->Evaluators2[i].Type.GetPtr<FStateTreeEvaluator2Base>())
|
|
{
|
|
Names.Add(FText::FromName(Eval->Name));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int32 i = 0; i < State->Evaluators.Num(); i++)
|
|
{
|
|
if (UStateTreeEvaluatorBase* Eval = State->Evaluators[i])
|
|
{
|
|
Names.Add(FText::FromName(Eval->GetName()));
|
|
}
|
|
}
|
|
}
|
|
|
|
return FText::Join((FText::FromString(TEXT(", "))), Names);
|
|
}
|
|
|
|
EVisibility SStateTreeViewRow::GetTasksVisibility() const
|
|
{
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
int32 ValidCount = 0;
|
|
if (IsV2())
|
|
{
|
|
for (int32 i = 0; i < State->Tasks2.Num(); i++)
|
|
{
|
|
if (const FStateTreeTask2Base* Task = State->Tasks2[i].Type.GetPtr<FStateTreeTask2Base>())
|
|
{
|
|
ValidCount++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int32 i = 0; i < State->Tasks.Num(); i++)
|
|
{
|
|
if (UStateTreeTaskBase* Task = State->Tasks[i])
|
|
{
|
|
ValidCount++;
|
|
}
|
|
}
|
|
}
|
|
return ValidCount > 0 ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
FText SStateTreeViewRow::GetTasksDesc() const
|
|
{
|
|
const UStateTreeState* State = WeakState.Get();
|
|
if (!State)
|
|
{
|
|
return FText::GetEmpty();
|
|
}
|
|
|
|
TArray<FText> Names;
|
|
|
|
if (IsV2())
|
|
{
|
|
for (int32 i = 0; i < State->Tasks2.Num(); i++)
|
|
{
|
|
if (const FStateTreeTask2Base* Task = State->Tasks2[i].Type.GetPtr<FStateTreeTask2Base>())
|
|
{
|
|
Names.Add(FText::FromName(Task->Name));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int32 i = 0; i < State->Tasks.Num(); i++)
|
|
{
|
|
if (UStateTreeTaskBase* Task = State->Tasks[i])
|
|
{
|
|
Names.Add(FText::FromName(Task->Name));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IsV2())
|
|
{
|
|
return FText::Join((FText::FromString(TEXT(" & "))), Names);
|
|
}
|
|
|
|
return FText::Join((FText::FromString(TEXT(", "))), Names);
|
|
}
|
|
|
|
bool SStateTreeViewRow::HasParentTransitionForEvent(const UStateTreeState& State, const EStateTreeTransitionEvent Event) const
|
|
{
|
|
EStateTreeTransitionEvent CombinedEvents = EStateTreeTransitionEvent::None;
|
|
for (const UStateTreeState* ParentState = State.Parent; ParentState != nullptr; ParentState = ParentState->Parent)
|
|
{
|
|
for (const FStateTreeTransition2& Transition : ParentState->Transitions2)
|
|
{
|
|
CombinedEvents |= Transition.Event;
|
|
}
|
|
}
|
|
return EnumHasAllFlags(CombinedEvents, Event);
|
|
}
|
|
|
|
FText SStateTreeViewRow::GetTransitionsDesc(const UStateTreeState& State, const EStateTreeTransitionEvent Event) const
|
|
{
|
|
TArray<FText> DescItems;
|
|
for (const FStateTreeTransition2& Transition : State.Transitions2)
|
|
{
|
|
if (Transition.Event == Event)
|
|
{
|
|
switch (Transition.State.Type)
|
|
{
|
|
case EStateTreeTransitionType::NotSet:
|
|
DescItems.Add(LOCTEXT("TransitionNoneStyled", "[None]"));
|
|
break;
|
|
case EStateTreeTransitionType::Succeeded:
|
|
DescItems.Add(LOCTEXT("TransitionTreeSucceededStyled", "[Succeeded]"));
|
|
break;
|
|
case EStateTreeTransitionType::Failed:
|
|
DescItems.Add(LOCTEXT("TransitionTreeFailedStyled", "[Failed]"));
|
|
break;
|
|
case EStateTreeTransitionType::NextState:
|
|
DescItems.Add(LOCTEXT("TransitionNextStateStyled", "[Next]"));
|
|
break;
|
|
case EStateTreeTransitionType::GotoState:
|
|
DescItems.Add(FText::FromName(Transition.State.Name));
|
|
break;
|
|
default:
|
|
ensureMsgf(false, TEXT("Unhandled transition type."));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (State.Children.Num() == 0
|
|
&& DescItems.Num() == 0
|
|
&& Event != EStateTreeTransitionEvent::OnCondition)
|
|
{
|
|
if (HasParentTransitionForEvent(State, Event))
|
|
{
|
|
DescItems.Add(LOCTEXT("TransitionActionHandleInParentStyled", "[Parent]"));
|
|
}
|
|
else
|
|
{
|
|
DescItems.Add(LOCTEXT("TransitionActionMissingTransition", "Missing Transition"));
|
|
}
|
|
}
|
|
|
|
return FText::Join(FText::FromString(TEXT(", ")), DescItems);
|
|
}
|
|
|
|
FText SStateTreeViewRow::GetTransitionsIcon(const UStateTreeState& State, const EStateTreeTransitionEvent Event) const
|
|
{
|
|
enum EIconType
|
|
{
|
|
IconNone = 0,
|
|
IconRightArrow = 1 << 0,
|
|
IconDownArrow = 1 << 1,
|
|
IconLevelUp = 1 << 2,
|
|
IconWarning = 1 << 3,
|
|
};
|
|
uint8 IconType = IconNone;
|
|
|
|
for (const FStateTreeTransition2& Transition : State.Transitions2)
|
|
{
|
|
// The icons here depict "transition direction", not the type specifically.
|
|
if (Transition.Event == Event)
|
|
{
|
|
switch (Transition.State.Type)
|
|
{
|
|
case EStateTreeTransitionType::NotSet:
|
|
IconType |= IconRightArrow;
|
|
break;
|
|
case EStateTreeTransitionType::Succeeded:
|
|
IconType |= IconRightArrow;
|
|
break;
|
|
case EStateTreeTransitionType::Failed:
|
|
IconType |= IconRightArrow;
|
|
break;
|
|
case EStateTreeTransitionType::NextState:
|
|
IconType |= IconDownArrow;
|
|
break;
|
|
case EStateTreeTransitionType::GotoState:
|
|
IconType |= IconRightArrow;
|
|
break;
|
|
default:
|
|
ensureMsgf(false, TEXT("Unhandled transition type."));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FMath::CountBits(static_cast<uint64>(IconType)) > 1)
|
|
{
|
|
// Prune down to just one icon.
|
|
IconType = IconRightArrow;
|
|
}
|
|
|
|
if (State.Children.Num() == 0
|
|
&& IconType == IconNone
|
|
&& Event != EStateTreeTransitionEvent::OnCondition)
|
|
{
|
|
if (HasParentTransitionForEvent(State, Event))
|
|
{
|
|
IconType = IconLevelUp;
|
|
}
|
|
else
|
|
{
|
|
IconType = IconWarning;
|
|
}
|
|
}
|
|
|
|
switch (IconType)
|
|
{
|
|
case IconRightArrow:
|
|
return FEditorFontGlyphs::Long_Arrow_Right;
|
|
case IconDownArrow:
|
|
return FEditorFontGlyphs::Long_Arrow_Down;
|
|
case IconLevelUp:
|
|
return FEditorFontGlyphs::Level_Up;
|
|
case IconWarning:
|
|
return FEditorFontGlyphs::Exclamation_Triangle;
|
|
default:
|
|
return FText::GetEmpty();
|
|
}
|
|
|
|
return FText::GetEmpty();
|
|
}
|
|
|
|
EVisibility SStateTreeViewRow::GetTransitionsVisibility(const UStateTreeState& State, const EStateTreeTransitionEvent Event) const
|
|
{
|
|
TStaticArray<int32, 5> Counts;
|
|
Algo::ForEach(Counts, [](int32& Count) { Count = 0; });
|
|
|
|
for (const FStateTreeTransition2& Transition : State.Transitions2)
|
|
{
|
|
Counts[static_cast<uint8>(Transition.Event)]++;
|
|
}
|
|
|
|
if (State.Children.Num() == 0
|
|
&& Counts[static_cast<uint8>(Event)] == 0
|
|
&& Event != EStateTreeTransitionEvent::OnCondition)
|
|
{
|
|
// Find the missing transition type, note: Completed = Succeeded|Failed.
|
|
EStateTreeTransitionEvent HandledEvents = EStateTreeTransitionEvent::None;
|
|
HandledEvents |= Counts[static_cast<uint8>(EStateTreeTransitionEvent::OnCompleted)] > 0 ? EStateTreeTransitionEvent::OnCompleted : EStateTreeTransitionEvent::None;
|
|
HandledEvents |= Counts[static_cast<uint8>(EStateTreeTransitionEvent::OnSucceeded)] > 0 ? EStateTreeTransitionEvent::OnSucceeded : EStateTreeTransitionEvent::None;
|
|
HandledEvents |= Counts[static_cast<uint8>(EStateTreeTransitionEvent::OnFailed)] > 0 ? EStateTreeTransitionEvent::OnFailed : EStateTreeTransitionEvent::None;
|
|
const EStateTreeTransitionEvent MissingEvent = HandledEvents ^ EStateTreeTransitionEvent::OnCompleted;
|
|
|
|
return MissingEvent == Event ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
|
|
return Counts[static_cast<uint8>(Event)] > 0 ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
|
|
EVisibility SStateTreeViewRow::GetCompletedTransitionVisibility() const
|
|
{
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
if (IsV2())
|
|
{
|
|
return GetTransitionsVisibility(*State, EStateTreeTransitionEvent::OnCompleted);
|
|
}
|
|
}
|
|
return EVisibility::Visible;
|
|
}
|
|
|
|
FText SStateTreeViewRow::GetCompletedTransitionsDesc() const
|
|
{
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
if (IsV2())
|
|
{
|
|
return GetTransitionsDesc(*State, EStateTreeTransitionEvent::OnCompleted);
|
|
}
|
|
else
|
|
{
|
|
switch (State->StateDoneTransition.Type)
|
|
{
|
|
case EStateTreeTransitionType::NotSet:
|
|
return FText::GetEmpty();
|
|
case EStateTreeTransitionType::Succeeded:
|
|
return LOCTEXT("TransitionSucceeded", "Succeeded");
|
|
case EStateTreeTransitionType::Failed:
|
|
return LOCTEXT("TransitionFailed", "Failed");
|
|
case EStateTreeTransitionType::NextState:
|
|
return LOCTEXT("TransitionNext", "Next");
|
|
case EStateTreeTransitionType::SelectChildState:
|
|
return LOCTEXT("TransitionSelect", "Select");
|
|
case EStateTreeTransitionType::GotoState:
|
|
return FText::FromName(State->StateDoneTransition.Name);
|
|
default:
|
|
ensureMsgf(false, TEXT("Unhandled transition type."));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return LOCTEXT("Invalid", "Invalid");
|
|
}
|
|
|
|
FText SStateTreeViewRow::GetCompletedTransitionsIcon() const
|
|
{
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
if (IsV2())
|
|
{
|
|
return GetTransitionsIcon(*State, EStateTreeTransitionEvent::OnCompleted);
|
|
}
|
|
else
|
|
{
|
|
switch (State->StateDoneTransition.Type)
|
|
{
|
|
case EStateTreeTransitionType::NotSet:
|
|
return FEditorFontGlyphs::Times;
|
|
case EStateTreeTransitionType::Succeeded:
|
|
return FEditorFontGlyphs::Check;
|
|
case EStateTreeTransitionType::Failed:
|
|
return FEditorFontGlyphs::Times;
|
|
case EStateTreeTransitionType::NextState:
|
|
return FEditorFontGlyphs::Long_Arrow_Down;
|
|
case EStateTreeTransitionType::SelectChildState:
|
|
return FEditorFontGlyphs::Level_Down;
|
|
case EStateTreeTransitionType::GotoState:
|
|
return FEditorFontGlyphs::Long_Arrow_Right;
|
|
default:
|
|
ensureMsgf(false, TEXT("Unhandled transition type."));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return FText::GetEmpty();
|
|
}
|
|
|
|
EVisibility SStateTreeViewRow::GetSucceededTransitionVisibility() const
|
|
{
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
if (IsV2())
|
|
{
|
|
return GetTransitionsVisibility(*State, EStateTreeTransitionEvent::OnSucceeded);
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
FText SStateTreeViewRow::GetSucceededTransitionDesc() const
|
|
{
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
if (IsV2())
|
|
{
|
|
return GetTransitionsDesc(*State, EStateTreeTransitionEvent::OnSucceeded);
|
|
}
|
|
}
|
|
return FText::GetEmpty();
|
|
}
|
|
|
|
FText SStateTreeViewRow::GetSucceededTransitionIcon() const
|
|
{
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
if (IsV2())
|
|
{
|
|
return GetTransitionsIcon(*State, EStateTreeTransitionEvent::OnSucceeded);
|
|
}
|
|
}
|
|
return FText::GetEmpty();
|
|
}
|
|
|
|
EVisibility SStateTreeViewRow::GetFailedTransitionVisibility() const
|
|
{
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
if (IsV2())
|
|
{
|
|
return GetTransitionsVisibility(*State, EStateTreeTransitionEvent::OnFailed);
|
|
}
|
|
else
|
|
{
|
|
return State->StateFailedTransition.Type != EStateTreeTransitionType::NotSet ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
FText SStateTreeViewRow::GetFailedTransitionDesc() const
|
|
{
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
if (IsV2())
|
|
{
|
|
return GetTransitionsDesc(*State, EStateTreeTransitionEvent::OnFailed);
|
|
}
|
|
else
|
|
{
|
|
switch (State->StateFailedTransition.Type)
|
|
{
|
|
case EStateTreeTransitionType::NotSet:
|
|
return FText::GetEmpty();
|
|
case EStateTreeTransitionType::Succeeded:
|
|
return LOCTEXT("TransitionSucceeded", "Succeeded");
|
|
case EStateTreeTransitionType::Failed:
|
|
return LOCTEXT("TransitionFailed", "Failed");
|
|
case EStateTreeTransitionType::NextState:
|
|
return LOCTEXT("TransitionNext", "Next");
|
|
case EStateTreeTransitionType::SelectChildState:
|
|
return LOCTEXT("TransitionSelect", "Select");
|
|
case EStateTreeTransitionType::GotoState:
|
|
return FText::FromName(State->StateFailedTransition.Name);
|
|
default:
|
|
ensureMsgf(false, TEXT("Unhandled transition type."));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return LOCTEXT("Invalid", "Invalid");
|
|
}
|
|
|
|
FText SStateTreeViewRow::GetFailedTransitionIcon() const
|
|
{
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
if (IsV2())
|
|
{
|
|
return GetTransitionsIcon(*State, EStateTreeTransitionEvent::OnFailed);
|
|
}
|
|
else
|
|
{
|
|
return FEditorFontGlyphs::Ban;
|
|
}
|
|
}
|
|
return FEditorFontGlyphs::Ban;
|
|
}
|
|
|
|
EVisibility SStateTreeViewRow::GetConditionalTransitionsVisibility() const
|
|
{
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
if (IsV2())
|
|
{
|
|
return GetTransitionsVisibility(*State, EStateTreeTransitionEvent::OnCondition);
|
|
}
|
|
else
|
|
{
|
|
int32 ValidCount = 0;
|
|
for (const FStateTreeTransition& Transition : State->Transitions)
|
|
{
|
|
ValidCount++;
|
|
}
|
|
return ValidCount > 0 ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
FText SStateTreeViewRow::GetConditionalTransitionsDesc() const
|
|
{
|
|
TArray<FText> DescItems;
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
if (IsV2())
|
|
{
|
|
return GetTransitionsDesc(*State, EStateTreeTransitionEvent::OnCondition);
|
|
}
|
|
else
|
|
{
|
|
for (const FStateTreeTransition& Transition : State->Transitions)
|
|
{
|
|
switch (Transition.State.Type)
|
|
{
|
|
case EStateTreeTransitionType::Succeeded:
|
|
DescItems.Add(LOCTEXT("TransitionSucceeded", "Succeeded"));
|
|
break;
|
|
case EStateTreeTransitionType::Failed:
|
|
DescItems.Add(LOCTEXT("TransitionFailed", "Failed"));
|
|
break;
|
|
case EStateTreeTransitionType::SelectChildState:
|
|
DescItems.Add(LOCTEXT("TransitionSelect", "Select"));
|
|
break;
|
|
case EStateTreeTransitionType::GotoState:
|
|
DescItems.Add(FText::FromName(Transition.State.Name));
|
|
break;
|
|
default:
|
|
ensureMsgf(false, TEXT("Unhandled transition type."));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return FText::Join(FText::FromString(TEXT(", ")), DescItems);
|
|
}
|
|
|
|
bool SStateTreeViewRow::IsRoutine() const
|
|
{
|
|
// Routines can be identified by not having parent state.
|
|
const UStateTreeState* State = WeakState.Get();
|
|
return State ? State->Parent == nullptr : false;
|
|
}
|
|
|
|
bool SStateTreeViewRow::IsSelected() const
|
|
{
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
if (StateTreeViewModel)
|
|
{
|
|
return StateTreeViewModel->IsSelected(State);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool SStateTreeViewRow::VerifyNodeTextChanged(const FText& NewLabel, FText& OutErrorMessage)
|
|
{
|
|
return !NewLabel.IsEmptyOrWhitespace();
|
|
}
|
|
|
|
void SStateTreeViewRow::HandleNodeLabelTextCommitted(const FText& NewLabel, ETextCommit::Type CommitType)
|
|
{
|
|
if (StateTreeViewModel)
|
|
{
|
|
if (UStateTreeState* State = WeakState.Get())
|
|
{
|
|
StateTreeViewModel->RenameState(State, FName(*FText::TrimPrecedingAndTrailing(NewLabel).ToString()));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
FReply SStateTreeViewRow::HandleDragDetected(const FGeometry&, const FPointerEvent&)
|
|
{
|
|
return FReply::Handled().BeginDragDrop(FActionTreeViewDragDrop::New(WeakState.Get()));
|
|
}
|
|
|
|
TOptional<EItemDropZone> SStateTreeViewRow::HandleCanAcceptDrop(const FDragDropEvent& DragDropEvent, EItemDropZone DropZone, UStateTreeState* TargetState)
|
|
{
|
|
TSharedPtr<FActionTreeViewDragDrop> DragDropOperation = DragDropEvent.GetOperationAs<FActionTreeViewDragDrop>();
|
|
if (DragDropOperation.IsValid())
|
|
{
|
|
// Cannot drop on selection or child of selection.
|
|
if (StateTreeViewModel && StateTreeViewModel->IsChildOfSelection(TargetState))
|
|
{
|
|
return TOptional<EItemDropZone>();
|
|
}
|
|
|
|
return DropZone;
|
|
}
|
|
|
|
return TOptional<EItemDropZone>();
|
|
}
|
|
|
|
FReply SStateTreeViewRow::HandleAcceptDrop(const FDragDropEvent& DragDropEvent, EItemDropZone DropZone, UStateTreeState* TargetState)
|
|
{
|
|
TSharedPtr<FActionTreeViewDragDrop> DragDropOperation = DragDropEvent.GetOperationAs<FActionTreeViewDragDrop>();
|
|
if (DragDropOperation.IsValid())
|
|
{
|
|
if (StateTreeViewModel)
|
|
{
|
|
if (DropZone == EItemDropZone::AboveItem)
|
|
{
|
|
StateTreeViewModel->MoveSelectedStatesBefore(TargetState);
|
|
}
|
|
else if (DropZone == EItemDropZone::BelowItem)
|
|
{
|
|
StateTreeViewModel->MoveSelectedStatesAfter(TargetState);
|
|
}
|
|
else
|
|
{
|
|
StateTreeViewModel->MoveSelectedStatesInto(TargetState);
|
|
}
|
|
|
|
return FReply::Handled();
|
|
}
|
|
}
|
|
|
|
return FReply::Unhandled();
|
|
}
|
|
|
|
bool SStateTreeViewRow::IsV2() const
|
|
{
|
|
if (const UStateTreeState* State = WeakState.Get())
|
|
{
|
|
return State->IsV2();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|