2021-09-28 13:33:17 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "StateTreeViewModel.h"
# include "StateTree.h"
# include "StateTreeEditorData.h"
# include "StateTreeDelegates.h"
2023-03-14 13:35:46 -04:00
# include "Debugger/StateTreeDebugger.h"
2021-09-28 13:33:17 -04:00
# include "Editor.h"
# include "ScopedTransaction.h"
2023-03-03 11:22:29 -05:00
# include "HAL/PlatformApplicationMisc.h"
# include "UnrealExporter.h"
# include "Exporters/Exporter.h"
# include "Factories.h"
2021-09-28 13:33:17 -04:00
# define LOCTEXT_NAMESPACE "StateTreeEditor"
2022-04-21 08:02:21 -04:00
namespace UE : : StateTree : : Editor
2021-09-28 13:33:17 -04:00
{
2023-03-03 11:22:29 -05:00
class FStateTreeStateTextFactory : public FCustomizableTextObjectFactory
{
public :
FStateTreeStateTextFactory ( )
: FCustomizableTextObjectFactory ( GWarn )
{ }
virtual bool CanCreateClass ( UClass * InObjectClass , bool & bOmitSubObjs ) const override
{
UE_LOG ( LogTemp , Error , TEXT ( " *** CanCreateClass: %s " ) , * GetNameSafe ( InObjectClass ) ) ;
return InObjectClass - > IsChildOf ( UStateTreeState : : StaticClass ( ) )
| | InObjectClass - > IsChildOf ( UStateTreeClipboardBindings : : StaticClass ( ) ) ;
}
virtual void ProcessConstructedObject ( UObject * NewObject ) override
{
if ( UStateTreeState * State = Cast < UStateTreeState > ( NewObject ) )
{
States . Add ( State ) ;
}
else if ( UStateTreeClipboardBindings * Bindings = Cast < UStateTreeClipboardBindings > ( NewObject ) )
{
ClipboardBindings = Bindings ;
}
}
public :
TArray < UStateTreeState * > States ;
UStateTreeClipboardBindings * ClipboardBindings = nullptr ;
} ;
void CollectBindingsRecursive ( UStateTreeEditorData * TreeData , UStateTreeState * State , TArray < FStateTreePropertyPathBinding > & AllBindings )
{
if ( ! State )
{
return ;
}
TreeData - > VisitStateNodes ( * State , [ TreeData , & AllBindings ] ( const UStateTreeState * State , const FStateTreeBindableStructDesc & Desc , const FStateTreeDataView Value )
{
TArray < FStateTreePropertyPathBinding > NodeBindings ;
TreeData - > GetPropertyEditorBindings ( ) - > GetPropertyBindingsFor ( Desc . ID , NodeBindings ) ;
AllBindings . Append ( NodeBindings ) ;
return EStateTreeVisitor : : Continue ;
} ) ;
for ( UStateTreeState * ChildState : State - > Children )
{
CollectBindingsRecursive ( TreeData , ChildState , AllBindings ) ;
}
}
FString ExportStatesToText ( UStateTreeEditorData * TreeData , const TArrayView < UStateTreeState * > States )
{
if ( States . IsEmpty ( ) )
{
return FString ( ) ;
}
// Clear the mark state for saving.
UnMarkAllObjects ( EObjectMark ( OBJECTMARK_TagExp | OBJECTMARK_TagImp ) ) ;
FStringOutputDevice Archive ;
const FExportObjectInnerContext Context ;
UStateTreeClipboardBindings * ClipboardBindings = NewObject < UStateTreeClipboardBindings > ( ) ;
check ( ClipboardBindings ) ;
for ( UStateTreeState * State : States )
{
UObject * ThisOuter = State - > GetOuter ( ) ;
UExporter : : ExportToOutputDevice ( & Context , State , nullptr , Archive , TEXT ( " copy " ) , 0 , PPF_ExportsNotFullyQualified | PPF_Copy | PPF_Delimited , false , ThisOuter ) ;
CollectBindingsRecursive ( TreeData , State , ClipboardBindings - > Bindings ) ;
}
UExporter : : ExportToOutputDevice ( & Context , ClipboardBindings , nullptr , Archive , TEXT ( " copy " ) , 0 , PPF_ExportsNotFullyQualified | PPF_Copy | PPF_Delimited , false ) ;
return * Archive ;
}
void CollectStateLinks ( const UStruct * Struct , void * Memory , TArray < FStateTreeStateLink * > & Links )
{
for ( TPropertyValueIterator < FStructProperty > It ( Struct , Memory ) ; It ; + + It )
{
if ( It - > Key - > Struct = = TBaseStructure < FStateTreeStateLink > : : Get ( ) )
{
FStateTreeStateLink * StateLink = static_cast < FStateTreeStateLink * > ( const_cast < void * > ( It - > Value ) ) ;
Links . Add ( StateLink ) ;
}
}
}
void FixNodesAfterDuplication ( TArrayView < FStateTreeEditorNode > Nodes , TMap < FGuid , FGuid > & IDsMap , TArray < FStateTreeStateLink * > & Links )
{
for ( FStateTreeEditorNode & Node : Nodes )
{
const FGuid NewNodeID = FGuid : : NewGuid ( ) ;
IDsMap . Emplace ( Node . ID , NewNodeID ) ;
Node . ID = NewNodeID ;
if ( Node . Node . IsValid ( ) )
{
CollectStateLinks ( Node . Node . GetScriptStruct ( ) , Node . Node . GetMutableMemory ( ) , Links ) ;
}
if ( Node . Instance . IsValid ( ) )
{
CollectStateLinks ( Node . Instance . GetScriptStruct ( ) , Node . Instance . GetMutableMemory ( ) , Links ) ;
}
if ( Node . InstanceObject )
{
CollectStateLinks ( Node . InstanceObject - > GetClass ( ) , Node . InstanceObject , Links ) ;
}
}
}
void FixStateAfterDuplication ( UStateTreeState * State , UStateTreeState * NewParentState , TMap < FGuid , FGuid > & IDsMap , TArray < FStateTreeStateLink * > & Links , TArray < UStateTreeState * > & NewStates )
{
State - > Modify ( ) ;
const FGuid NewStateID = FGuid : : NewGuid ( ) ;
IDsMap . Emplace ( State - > ID , NewStateID ) ;
State - > ID = NewStateID ;
const FGuid NewParametersID = FGuid : : NewGuid ( ) ;
IDsMap . Emplace ( State - > Parameters . ID , NewParametersID ) ;
State - > Parameters . ID = NewParametersID ;
State - > Parent = NewParentState ;
NewStates . Add ( State ) ;
if ( State - > Type = = EStateTreeStateType : : Linked )
{
Links . Emplace ( & State - > LinkedSubtree ) ;
}
FixNodesAfterDuplication ( TArrayView < FStateTreeEditorNode > ( & State - > SingleTask , 1 ) , IDsMap , Links ) ;
FixNodesAfterDuplication ( State - > Tasks , IDsMap , Links ) ;
FixNodesAfterDuplication ( State - > EnterConditions , IDsMap , Links ) ;
for ( FStateTreeTransition & Transition : State - > Transitions )
{
2023-06-20 13:49:25 -04:00
// Transition Ids are not used by nodes so no need to add to 'IDsMap'
Transition . ID = FGuid : : NewGuid ( ) ;
2023-03-03 11:22:29 -05:00
FixNodesAfterDuplication ( Transition . Conditions , IDsMap , Links ) ;
Links . Emplace ( & Transition . State ) ;
}
for ( UStateTreeState * Child : State - > Children )
{
FixStateAfterDuplication ( Child , State , IDsMap , Links , NewStates ) ;
}
}
2021-09-28 13:33:17 -04:00
// Removes states from the array which are children of any other state.
void RemoveContainedChildren ( TArray < UStateTreeState * > & States )
{
TSet < UStateTreeState * > UniqueStates ;
for ( UStateTreeState * State : States )
{
UniqueStates . Add ( State ) ;
}
for ( int32 i = 0 ; i < States . Num ( ) ; )
{
UStateTreeState * State = States [ i ] ;
// Walk up the parent state sand if the current state
// exists in any of them, remove it.
UStateTreeState * StateParent = State - > Parent ;
bool bShouldRemove = false ;
while ( StateParent )
{
if ( UniqueStates . Contains ( StateParent ) )
{
bShouldRemove = true ;
break ;
}
StateParent = StateParent - > Parent ;
}
if ( bShouldRemove )
{
States . RemoveAt ( i ) ;
}
else
{
i + + ;
}
}
}
// Returns true if the state is child of parent state.
bool IsChildOf ( const UStateTreeState * ParentState , const UStateTreeState * State )
{
for ( const UStateTreeState * Child : ParentState - > Children )
{
if ( Child = = State )
{
return true ;
}
if ( IsChildOf ( Child , State ) )
{
return true ;
}
}
return false ;
}
} ;
FStateTreeViewModel : : FStateTreeViewModel ( )
2022-09-30 11:31:57 -04:00
: TreeDataWeak ( nullptr )
2023-03-14 13:35:46 -04:00
# if WITH_STATETREE_DEBUGGER
, Debugger ( MakeShareable ( new FStateTreeDebugger ) )
# endif // WITH_STATETREE_DEBUGGER
2021-09-28 13:33:17 -04:00
{
}
FStateTreeViewModel : : ~ FStateTreeViewModel ( )
{
GEditor - > UnregisterForUndo ( this ) ;
UE : : StateTree : : Delegates : : OnIdentifierChanged . RemoveAll ( this ) ;
}
void FStateTreeViewModel : : Init ( UStateTreeEditorData * InTreeData )
{
2022-09-30 11:31:57 -04:00
TreeDataWeak = InTreeData ;
2021-09-28 13:33:17 -04:00
GEditor - > RegisterForUndo ( this ) ;
UE : : StateTree : : Delegates : : OnIdentifierChanged . AddSP ( this , & FStateTreeViewModel : : HandleIdentifierChanged ) ;
2023-06-15 11:38:28 -04:00
2023-03-17 09:41:13 -04:00
# if WITH_STATETREE_DEBUGGER
2023-06-15 11:38:28 -04:00
UE : : StateTree : : Delegates : : OnBreakpointsChanged . AddSP ( this , & FStateTreeViewModel : : HandleBreakpointsChanged ) ;
UE : : StateTree : : Delegates : : OnPostCompile . AddSP ( this , & FStateTreeViewModel : : HandlePostCompile ) ;
2023-03-17 09:41:13 -04:00
Debugger - > SetAsset ( GetStateTree ( ) ) ;
2023-03-14 13:35:46 -04:00
BindToDebuggerDelegates ( ) ;
2023-06-15 11:38:28 -04:00
RefreshDebuggerBreakpoints ( ) ;
2023-03-17 09:41:13 -04:00
# endif // WITH_STATETREE_DEBUGGER
2023-03-14 13:35:46 -04:00
}
const UStateTree * FStateTreeViewModel : : GetStateTree ( ) const
{
if ( const UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) )
{
return TreeData - > GetTypedOuter < UStateTree > ( ) ;
}
return nullptr ;
2021-09-28 13:33:17 -04:00
}
2022-04-21 08:02:21 -04:00
void FStateTreeViewModel : : HandleIdentifierChanged ( const UStateTree & StateTree ) const
2021-09-28 13:33:17 -04:00
{
2023-03-14 13:35:46 -04:00
if ( GetStateTree ( ) = = & StateTree )
2021-09-28 13:33:17 -04:00
{
OnAssetChanged . Broadcast ( ) ;
}
}
2023-06-15 11:38:28 -04:00
# if WITH_STATETREE_DEBUGGER
2024-04-19 13:14:25 -04:00
bool FStateTreeViewModel : : CanAddStateBreakpoint ( const EStateTreeBreakpointType Type ) const
{
const UStateTreeEditorData * EditorData = TreeDataWeak . Get ( ) ;
if ( ! ensure ( EditorData ! = nullptr ) )
{
return false ;
}
for ( const TWeakObjectPtr < UStateTreeState > & WeakState : SelectedStates )
{
if ( const UStateTreeState * State = WeakState . Get ( ) )
{
if ( EditorData - > HasBreakpoint ( State - > ID , Type ) = = false )
{
return true ;
}
}
}
return false ;
}
bool FStateTreeViewModel : : CanRemoveStateBreakpoint ( const EStateTreeBreakpointType Type ) const
{
const UStateTreeEditorData * EditorData = TreeDataWeak . Get ( ) ;
if ( ! ensure ( EditorData ! = nullptr ) )
{
return false ;
}
for ( const TWeakObjectPtr < UStateTreeState > & WeakState : SelectedStates )
{
if ( const UStateTreeState * State = WeakState . Get ( ) )
{
if ( EditorData - > HasBreakpoint ( State - > ID , Type ) )
{
return true ;
}
}
}
return false ;
}
ECheckBoxState FStateTreeViewModel : : GetStateBreakpointCheckState ( const EStateTreeBreakpointType Type ) const
{
const bool bCanAdd = CanAddStateBreakpoint ( Type ) ;
const bool bCanRemove = CanRemoveStateBreakpoint ( Type ) ;
if ( bCanAdd & & bCanRemove )
{
return ECheckBoxState : : Undetermined ;
}
if ( bCanRemove )
{
return ECheckBoxState : : Checked ;
}
if ( bCanAdd )
{
return ECheckBoxState : : Unchecked ;
}
// Should not happen since action is not visible in this case
return ECheckBoxState : : Undetermined ;
}
void FStateTreeViewModel : : HandleEnableStateBreakpoint ( EStateTreeBreakpointType Type )
{
TArray < UStateTreeState * > ValidatedSelectedStates ;
GetSelectedStates ( ValidatedSelectedStates ) ;
if ( ValidatedSelectedStates . IsEmpty ( ) )
{
return ;
}
UStateTreeEditorData * EditorData = TreeDataWeak . Get ( ) ;
if ( ! ensure ( EditorData ! = nullptr ) )
{
return ;
}
TBitArray < > HasBreakpoint ;
HasBreakpoint . Reserve ( ValidatedSelectedStates . Num ( ) ) ;
for ( const UStateTreeState * SelectedState : ValidatedSelectedStates )
{
HasBreakpoint . Add ( SelectedState ! = nullptr & & EditorData - > HasBreakpoint ( SelectedState - > ID , Type ) ) ;
}
check ( HasBreakpoint . Num ( ) = = ValidatedSelectedStates . Num ( ) ) ;
// Process CanAdd first so in case of undetermined state (mixed selection) we add by default.
if ( CanAddStateBreakpoint ( Type ) )
{
const FScopedTransaction Transaction ( LOCTEXT ( " AddStateBreakpoint " , " Add State Breakpoint(s) " )) ;
EditorData - > Modify ( ) ;
for ( int Index = 0 ; Index < ValidatedSelectedStates . Num ( ) ; + + Index )
{
const UStateTreeState * SelectedState = ValidatedSelectedStates [ Index ] ;
if ( HasBreakpoint [ Index ] = = false & & SelectedState ! = nullptr )
{
EditorData - > AddBreakpoint ( SelectedState - > ID , Type ) ;
}
}
}
else if ( CanRemoveStateBreakpoint ( Type ) )
{
const FScopedTransaction Transaction ( LOCTEXT ( " RemoveStateBreakpoint " , " Remove State Breakpoint(s) " )) ;
EditorData - > Modify ( ) ;
for ( int Index = 0 ; Index < ValidatedSelectedStates . Num ( ) ; + + Index )
{
const UStateTreeState * SelectedState = ValidatedSelectedStates [ Index ] ;
if ( HasBreakpoint [ Index ] & & SelectedState ! = nullptr )
{
EditorData - > RemoveBreakpoint ( SelectedState - > ID , Type ) ;
}
}
}
}
UStateTreeState * FStateTreeViewModel : : FindStateAssociatedToBreakpoint ( FStateTreeDebuggerBreakpoint Breakpoint ) const
{
UStateTreeEditorData * EditorData = TreeDataWeak . Get ( ) ;
if ( EditorData = = nullptr )
{
return nullptr ;
}
const UStateTree * StateTree = GetStateTree ( ) ;
if ( StateTree = = nullptr )
{
return nullptr ;
}
UStateTreeState * StateTreeState = nullptr ;
if ( const FStateTreeStateHandle * StateHandle = Breakpoint . ElementIdentifier . TryGet < FStateTreeStateHandle > ( ) )
{
const FGuid StateId = StateTree - > GetStateIdFromHandle ( * StateHandle ) ;
StateTreeState = EditorData - > GetMutableStateByID ( StateId ) ;
}
else if ( const FStateTreeDebuggerBreakpoint : : FStateTreeTaskIndex * TaskIndex = Breakpoint . ElementIdentifier . TryGet < FStateTreeDebuggerBreakpoint : : FStateTreeTaskIndex > ( ) )
{
const FGuid TaskId = StateTree - > GetNodeIdFromIndex ( TaskIndex - > Index ) ;
EditorData - > VisitHierarchy ( [ & TaskId , & StateTreeState ] ( UStateTreeState & State , UStateTreeState * /*ParentState*/ )
{
for ( const FStateTreeEditorNode & EditorNode : State . Tasks )
{
if ( EditorNode . ID = = TaskId )
{
StateTreeState = & State ;
return EStateTreeVisitor : : Break ;
}
}
return EStateTreeVisitor : : Continue ;
} ) ;
}
else if ( const FStateTreeDebuggerBreakpoint : : FStateTreeTransitionIndex * TransitionIndex = Breakpoint . ElementIdentifier . TryGet < FStateTreeDebuggerBreakpoint : : FStateTreeTransitionIndex > ( ) )
{
const FGuid TransitionId = StateTree - > GetTransitionIdFromIndex ( TransitionIndex - > Index ) ;
EditorData - > VisitHierarchy ( [ & TransitionId , & StateTreeState ] ( UStateTreeState & State , UStateTreeState * /*ParentState*/ )
{
for ( const FStateTreeTransition & StateTransition : State . Transitions )
{
if ( StateTransition . ID = = TransitionId )
{
StateTreeState = & State ;
return EStateTreeVisitor : : Break ;
}
}
return EStateTreeVisitor : : Continue ;
} ) ;
}
return StateTreeState ;
}
2023-06-15 11:38:28 -04:00
void FStateTreeViewModel : : HandleBreakpointsChanged ( const UStateTree & StateTree )
{
if ( GetStateTree ( ) = = & StateTree )
{
RefreshDebuggerBreakpoints ( ) ;
}
}
void FStateTreeViewModel : : HandlePostCompile ( const UStateTree & StateTree )
{
if ( GetStateTree ( ) = = & StateTree )
{
RefreshDebuggerBreakpoints ( ) ;
}
}
void FStateTreeViewModel : : RefreshDebuggerBreakpoints ( )
{
const UStateTree * StateTree = GetStateTree ( ) ;
const UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) ;
if ( StateTree ! = nullptr & & TreeData ! = nullptr )
{
Debugger - > ClearAllBreakpoints ( ) ;
for ( const FStateTreeEditorBreakpoint & Breakpoint : TreeData - > Breakpoints )
{
2023-06-20 13:49:25 -04:00
// Test if the ID is associated to a task
2023-06-15 11:38:28 -04:00
const FStateTreeIndex16 Index = StateTree - > GetNodeIndexFromId ( Breakpoint . ID ) ;
if ( Index . IsValid ( ) )
{
2023-06-20 13:49:25 -04:00
Debugger - > SetTaskBreakpoint ( Index , Breakpoint . BreakpointType ) ;
2023-06-15 11:38:28 -04:00
}
else
{
2023-06-20 13:49:25 -04:00
// Then test if the ID is associated to a State
2023-06-15 11:38:28 -04:00
FStateTreeStateHandle StateHandle = StateTree - > GetStateHandleFromId ( Breakpoint . ID ) ;
if ( StateHandle . IsValid ( ) )
{
2023-06-20 13:49:25 -04:00
Debugger - > SetStateBreakpoint ( StateHandle , Breakpoint . BreakpointType ) ;
}
else
{
// Then test if the ID is associated to a transition
const FStateTreeIndex16 TransitionIndex = StateTree - > GetTransitionIndexFromId ( Breakpoint . ID ) ;
if ( TransitionIndex . IsValid ( ) )
{
Debugger - > SetTransitionBreakpoint ( TransitionIndex , Breakpoint . BreakpointType ) ;
}
2023-06-15 11:38:28 -04:00
}
}
}
}
}
# endif // WITH_STATETREE_DEBUGGER
2022-04-21 08:02:21 -04:00
void FStateTreeViewModel : : NotifyAssetChangedExternally ( ) const
2021-09-28 13:33:17 -04:00
{
OnAssetChanged . Broadcast ( ) ;
}
2022-04-21 08:02:21 -04:00
void FStateTreeViewModel : : NotifyStatesChangedExternally ( const TSet < UStateTreeState * > & ChangedStates , const FPropertyChangedEvent & PropertyChangedEvent ) const
2021-09-28 13:33:17 -04:00
{
OnStatesChanged . Broadcast ( ChangedStates , PropertyChangedEvent ) ;
}
2023-04-20 13:00:27 -04:00
TArray < TObjectPtr < UStateTreeState > > * FStateTreeViewModel : : GetSubTrees ( ) const
2021-09-28 13:33:17 -04:00
{
2022-09-30 11:31:57 -04:00
UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) ;
2023-04-20 13:00:27 -04:00
return TreeData ! = nullptr ? & TreeData - > SubTrees : nullptr ;
2022-01-28 03:38:15 -05:00
}
int32 FStateTreeViewModel : : GetSubTreeCount ( ) const
{
2022-09-30 11:31:57 -04:00
UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) ;
2022-01-28 03:38:15 -05:00
return TreeData ! = nullptr ? TreeData - > SubTrees . Num ( ) : 0 ;
2021-09-28 13:33:17 -04:00
}
2022-09-30 11:31:57 -04:00
void FStateTreeViewModel : : GetSubTrees ( TArray < TWeakObjectPtr < UStateTreeState > > & OutSubtrees ) const
{
OutSubtrees . Reset ( ) ;
if ( UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) )
{
for ( UStateTreeState * Subtree : TreeData - > SubTrees )
{
OutSubtrees . Add ( Subtree ) ;
}
}
}
2021-09-28 13:33:17 -04:00
void FStateTreeViewModel : : PostUndo ( bool bSuccess )
{
// TODO: see if we can narrow this down.
OnAssetChanged . Broadcast ( ) ;
}
void FStateTreeViewModel : : PostRedo ( bool bSuccess )
{
OnAssetChanged . Broadcast ( ) ;
}
void FStateTreeViewModel : : ClearSelection ( )
{
SelectedStates . Reset ( ) ;
2022-09-30 11:31:57 -04:00
const TArray < TWeakObjectPtr < UStateTreeState > > SelectedStatesArr ;
2021-09-28 13:33:17 -04:00
OnSelectionChanged . Broadcast ( SelectedStatesArr ) ;
}
2022-04-21 08:02:21 -04:00
void FStateTreeViewModel : : SetSelection ( UStateTreeState * Selected )
2021-09-28 13:33:17 -04:00
{
SelectedStates . Reset ( ) ;
2022-04-21 08:02:21 -04:00
SelectedStates . Add ( Selected ) ;
2021-09-28 13:33:17 -04:00
2022-09-30 11:31:57 -04:00
TArray < TWeakObjectPtr < UStateTreeState > > SelectedStatesArr ;
2022-04-21 08:02:21 -04:00
SelectedStatesArr . Add ( Selected ) ;
2021-09-28 13:33:17 -04:00
OnSelectionChanged . Broadcast ( SelectedStatesArr ) ;
}
2022-09-30 11:31:57 -04:00
void FStateTreeViewModel : : SetSelection ( const TArray < TWeakObjectPtr < UStateTreeState > > & InSelectedStates )
2021-09-28 13:33:17 -04:00
{
SelectedStates . Reset ( ) ;
2022-09-30 11:31:57 -04:00
for ( const TWeakObjectPtr < UStateTreeState > & State : InSelectedStates )
2021-09-28 13:33:17 -04:00
{
2022-09-30 11:31:57 -04:00
if ( State . Get ( ) )
2021-09-28 13:33:17 -04:00
{
SelectedStates . Add ( State ) ;
}
}
TArray < FGuid > SelectedTaskIDArr ;
OnSelectionChanged . Broadcast ( InSelectedStates ) ;
}
bool FStateTreeViewModel : : IsSelected ( const UStateTreeState * State ) const
{
2022-09-30 11:31:57 -04:00
const TWeakObjectPtr < UStateTreeState > WeakState = const_cast < UStateTreeState * > ( State ) ;
return SelectedStates . Contains ( WeakState ) ;
2021-09-28 13:33:17 -04:00
}
bool FStateTreeViewModel : : IsChildOfSelection ( const UStateTreeState * State ) const
{
2022-09-30 11:31:57 -04:00
for ( const TWeakObjectPtr < UStateTreeState > & WeakSelectedState : SelectedStates )
2021-09-28 13:33:17 -04:00
{
if ( const UStateTreeState * SelectedState = Cast < UStateTreeState > ( WeakSelectedState . Get ( ) ) )
{
if ( SelectedState = = State )
{
return true ;
}
2022-04-21 08:02:21 -04:00
if ( UE : : StateTree : : Editor : : IsChildOf ( SelectedState , State ) )
2021-09-28 13:33:17 -04:00
{
2022-04-21 08:02:21 -04:00
return true ;
2021-09-28 13:33:17 -04:00
}
}
}
return false ;
}
2023-06-05 13:12:19 -04:00
void FStateTreeViewModel : : GetSelectedStates ( TArray < UStateTreeState * > & OutSelectedStates ) const
2021-09-28 13:33:17 -04:00
{
OutSelectedStates . Reset ( ) ;
2023-06-05 13:12:19 -04:00
for ( const TWeakObjectPtr < UStateTreeState > & WeakState : SelectedStates )
2021-09-28 13:33:17 -04:00
{
2022-09-30 11:31:57 -04:00
if ( UStateTreeState * State = WeakState . Get ( ) )
2021-09-28 13:33:17 -04:00
{
OutSelectedStates . Add ( State ) ;
}
}
}
2023-06-05 13:12:19 -04:00
void FStateTreeViewModel : : GetSelectedStates ( TArray < TWeakObjectPtr < UStateTreeState > > & OutSelectedStates ) const
2022-09-30 11:31:57 -04:00
{
OutSelectedStates . Reset ( ) ;
2023-06-05 13:12:19 -04:00
for ( const TWeakObjectPtr < UStateTreeState > & WeakState : SelectedStates )
2022-09-30 11:31:57 -04:00
{
if ( WeakState . Get ( ) )
{
OutSelectedStates . Add ( WeakState ) ;
}
}
}
2022-01-28 03:38:15 -05:00
bool FStateTreeViewModel : : HasSelection ( ) const
{
return SelectedStates . Num ( ) > 0 ;
}
2022-09-30 11:31:57 -04:00
void FStateTreeViewModel : : GetPersistentExpandedStates ( TSet < TWeakObjectPtr < UStateTreeState > > & OutExpandedStates )
2021-09-28 13:33:17 -04:00
{
OutExpandedStates . Reset ( ) ;
2022-09-30 11:31:57 -04:00
if ( UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) )
2021-09-28 13:33:17 -04:00
{
2022-09-30 11:31:57 -04:00
for ( UStateTreeState * SubTree : TreeData - > SubTrees )
{
GetExpandedStatesRecursive ( SubTree , OutExpandedStates ) ;
}
2021-09-28 13:33:17 -04:00
}
}
2022-09-30 11:31:57 -04:00
void FStateTreeViewModel : : GetExpandedStatesRecursive ( UStateTreeState * State , TSet < TWeakObjectPtr < UStateTreeState > > & OutExpandedStates )
2021-09-28 13:33:17 -04:00
{
if ( State - > bExpanded )
{
OutExpandedStates . Add ( State ) ;
}
for ( UStateTreeState * Child : State - > Children )
{
GetExpandedStatesRecursive ( Child , OutExpandedStates ) ;
}
}
2022-09-30 11:31:57 -04:00
void FStateTreeViewModel : : SetPersistentExpandedStates ( TSet < TWeakObjectPtr < UStateTreeState > > & InExpandedStates )
2021-09-28 13:33:17 -04:00
{
2022-09-30 11:31:57 -04:00
UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) ;
2022-04-21 08:02:21 -04:00
if ( TreeData = = nullptr )
2021-09-28 13:33:17 -04:00
{
return ;
}
TreeData - > Modify ( ) ;
2022-09-30 11:31:57 -04:00
for ( TWeakObjectPtr < UStateTreeState > & WeakState : InExpandedStates )
2021-09-28 13:33:17 -04:00
{
2022-09-30 11:31:57 -04:00
if ( UStateTreeState * State = WeakState . Get ( ) )
2021-09-28 13:33:17 -04:00
{
State - > bExpanded = true ;
}
}
}
void FStateTreeViewModel : : AddState ( UStateTreeState * AfterState )
{
2022-09-30 11:31:57 -04:00
UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) ;
2022-04-21 08:02:21 -04:00
if ( TreeData = = nullptr )
2021-09-28 13:33:17 -04:00
{
return ;
}
const FScopedTransaction Transaction ( LOCTEXT ( " AddStateTransaction " , " Add State " ) ) ;
UStateTreeState * NewState = NewObject < UStateTreeState > ( TreeData , FName ( ) , RF_Transactional ) ;
UStateTreeState * ParentState = nullptr ;
2022-09-30 11:31:57 -04:00
if ( AfterState = = nullptr )
{
// If no subtrees, add a subtree, or add to the root state.
if ( TreeData - > SubTrees . IsEmpty ( ) )
{
TreeData - > Modify ( ) ;
TreeData - > SubTrees . Add ( NewState ) ;
}
else
{
UStateTreeState * RootState = TreeData - > SubTrees [ 0 ] ;
if ( ensureMsgf ( RootState , TEXT ( " %s: Root state is empty. " ) , * GetNameSafe ( TreeData - > GetOuter ( ) ) ) )
{
RootState - > Modify ( ) ;
RootState - > Children . Add ( NewState ) ;
NewState - > Parent = RootState ;
ParentState = RootState ;
}
}
}
else
2021-09-28 13:33:17 -04:00
{
ParentState = AfterState - > Parent ;
2022-04-21 08:02:21 -04:00
if ( ParentState ! = nullptr )
2021-09-28 13:33:17 -04:00
{
ParentState - > Modify ( ) ;
}
else
{
TreeData - > Modify ( ) ;
}
2023-04-18 22:39:29 -04:00
TArray < TObjectPtr < UStateTreeState > > & ParentArray = ParentState ? ParentState - > Children : TreeData - > SubTrees ;
2022-04-21 08:02:21 -04:00
const int32 TargetIndex = ParentArray . Find ( AfterState ) ;
if ( TargetIndex ! = INDEX_NONE )
{
// Insert After
ParentArray . Insert ( NewState , TargetIndex + 1 ) ;
NewState - > Parent = ParentState ;
}
else
{
// Fallback, should never happen.
ensureMsgf ( false , TEXT ( " %s: Failed to find specified target state %s on state %s while adding new state. " ) , * GetNameSafe ( TreeData - > GetOuter ( ) ) , * GetNameSafe ( AfterState ) , * GetNameSafe ( ParentState ) ) ;
ParentArray . Add ( NewState ) ;
NewState - > Parent = ParentState ;
}
2021-09-28 13:33:17 -04:00
}
OnStateAdded . Broadcast ( ParentState , NewState ) ;
}
void FStateTreeViewModel : : AddChildState ( UStateTreeState * ParentState )
{
2022-09-30 11:31:57 -04:00
UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) ;
2022-04-21 08:02:21 -04:00
if ( TreeData = = nullptr | | ParentState = = nullptr )
2021-09-28 13:33:17 -04:00
{
return ;
}
const FScopedTransaction Transaction ( LOCTEXT ( " AddChildStateTransaction " , " Add Child State " ) ) ;
2022-04-21 08:02:21 -04:00
UStateTreeState * NewState = NewObject < UStateTreeState > ( ParentState , FName ( ) , RF_Transactional ) ;
2021-09-28 13:33:17 -04:00
2022-04-21 08:02:21 -04:00
ParentState - > Modify ( ) ;
2021-09-28 13:33:17 -04:00
ParentState - > Children . Add ( NewState ) ;
NewState - > Parent = ParentState ;
OnStateAdded . Broadcast ( ParentState , NewState ) ;
}
void FStateTreeViewModel : : RenameState ( UStateTreeState * State , FName NewName )
{
2022-04-21 08:02:21 -04:00
if ( State = = nullptr )
2021-09-28 13:33:17 -04:00
{
return ;
}
const FScopedTransaction Transaction ( LOCTEXT ( " RenameTransaction " , " Rename " ) ) ;
State - > Modify ( ) ;
State - > Name = NewName ;
TSet < UStateTreeState * > AffectedStates ;
AffectedStates . Add ( State ) ;
FProperty * NameProperty = FindFProperty < FProperty > ( UStateTreeState : : StaticClass ( ) , GET_MEMBER_NAME_CHECKED ( UStateTreeState , Name ) ) ;
FPropertyChangedEvent PropertyChangedEvent ( NameProperty , EPropertyChangeType : : ValueSet ) ;
OnStatesChanged . Broadcast ( AffectedStates , PropertyChangedEvent ) ;
}
void FStateTreeViewModel : : RemoveSelectedStates ( )
{
2022-09-30 11:31:57 -04:00
UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) ;
2022-04-21 08:02:21 -04:00
if ( TreeData = = nullptr )
2021-09-28 13:33:17 -04:00
{
return ;
}
TArray < UStateTreeState * > States ;
GetSelectedStates ( States ) ;
// Remove items whose parent also exists in the selection.
2022-04-21 08:02:21 -04:00
UE : : StateTree : : Editor : : RemoveContainedChildren ( States ) ;
2021-09-28 13:33:17 -04:00
if ( States . Num ( ) > 0 )
{
const FScopedTransaction Transaction ( LOCTEXT ( " DeleteStateTransaction " , " Delete State " ) ) ;
TSet < UStateTreeState * > AffectedParents ;
2022-04-21 08:02:21 -04:00
for ( UStateTreeState * StateToRemove : States )
2021-09-28 13:33:17 -04:00
{
2022-04-21 08:02:21 -04:00
if ( StateToRemove )
2021-09-28 13:33:17 -04:00
{
2022-04-21 08:02:21 -04:00
StateToRemove - > Modify ( ) ;
UStateTreeState * ParentState = StateToRemove - > Parent ;
if ( ParentState ! = nullptr )
2021-09-28 13:33:17 -04:00
{
AffectedParents . Add ( ParentState ) ;
ParentState - > Modify ( ) ;
}
else
{
AffectedParents . Add ( nullptr ) ;
TreeData - > Modify ( ) ;
}
2022-04-21 08:02:21 -04:00
2023-04-18 22:39:29 -04:00
TArray < TObjectPtr < UStateTreeState > > & ArrayToRemoveFrom = ParentState ? ParentState - > Children : TreeData - > SubTrees ;
2022-04-21 08:02:21 -04:00
const int32 ItemIndex = ArrayToRemoveFrom . Find ( StateToRemove ) ;
if ( ItemIndex ! = INDEX_NONE )
{
ArrayToRemoveFrom . RemoveAt ( ItemIndex ) ;
StateToRemove - > Parent = nullptr ;
}
2021-09-28 13:33:17 -04:00
}
}
OnStatesRemoved . Broadcast ( AffectedParents ) ;
ClearSelection ( ) ;
}
}
2023-03-03 11:22:29 -05:00
void FStateTreeViewModel : : CopySelectedStates ( )
{
UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) ;
if ( TreeData = = nullptr )
{
return ;
}
TArray < UStateTreeState * > States ;
GetSelectedStates ( States ) ;
UE : : StateTree : : Editor : : RemoveContainedChildren ( States ) ;
FString ExportedText = UE : : StateTree : : Editor : : ExportStatesToText ( TreeData , States ) ;
FPlatformApplicationMisc : : ClipboardCopy ( * ExportedText ) ;
}
bool FStateTreeViewModel : : CanPasteStatesFromClipboard ( ) const
{
FString TextToImport ;
FPlatformApplicationMisc : : ClipboardPaste ( TextToImport ) ;
UE : : StateTree : : Editor : : FStateTreeStateTextFactory Factory ;
return Factory . CanCreateObjectsFromText ( TextToImport ) ;
}
void FStateTreeViewModel : : PasteStatesFromClipboard ( UStateTreeState * AfterState )
{
UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) ;
if ( TreeData = = nullptr )
{
return ;
}
if ( AfterState )
{
const int32 Index = AfterState - > Parent ? AfterState - > Parent - > Children . Find ( AfterState ) : TreeData - > SubTrees . Find ( AfterState ) ;
if ( Index ! = INDEX_NONE )
{
2023-06-14 06:17:45 -04:00
FString TextToImport ;
FPlatformApplicationMisc : : ClipboardPaste ( TextToImport ) ;
const FScopedTransaction Transaction ( LOCTEXT ( " PasteStatesTransaction " , " Paste State(s) " )) ;
PasteStatesAsChildrenFromText ( TextToImport , AfterState - > Parent , Index + 1 ) ;
2023-03-03 11:22:29 -05:00
}
}
}
2023-06-14 06:17:45 -04:00
void FStateTreeViewModel : : PasteStatesAsChildrenFromClipboard ( UStateTreeState * ParentState )
2023-03-03 11:22:29 -05:00
{
UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) ;
if ( TreeData = = nullptr )
{
return ;
}
2023-06-14 06:17:45 -04:00
2023-03-03 11:22:29 -05:00
FString TextToImport ;
FPlatformApplicationMisc : : ClipboardPaste ( TextToImport ) ;
2023-06-14 06:17:45 -04:00
const FScopedTransaction Transaction ( LOCTEXT ( " PasteStatesTransaction " , " Paste State(s) " )) ;
PasteStatesAsChildrenFromText ( TextToImport , ParentState , INDEX_NONE ) ;
}
void FStateTreeViewModel : : PasteStatesAsChildrenFromText ( const FString & TextToImport , UStateTreeState * ParentState , const int32 IndexToInsertAt )
{
UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) ;
if ( TreeData = = nullptr )
{
return ;
}
2023-03-03 11:22:29 -05:00
UObject * Outer = ParentState ? static_cast < UObject * > ( ParentState ) : static_cast < UObject * > ( TreeData ) ;
Outer - > Modify ( ) ;
UE : : StateTree : : Editor : : FStateTreeStateTextFactory Factory ;
Factory . ProcessBuffer ( Outer , RF_Transactional , TextToImport ) ;
2023-04-18 22:39:29 -04:00
TArray < TObjectPtr < UStateTreeState > > & ParentArray = ParentState ? ParentState - > Children : TreeData - > SubTrees ;
2023-06-14 06:17:45 -04:00
const int32 TargetIndex = ( IndexToInsertAt = = INDEX_NONE ) ? ParentArray . Num ( ) : IndexToInsertAt ;
2023-03-03 11:22:29 -05:00
ParentArray . Insert ( Factory . States , TargetIndex ) ;
TArray < FStateTreeStateLink * > Links ;
TMap < FGuid , FGuid > IDsMap ;
TArray < UStateTreeState * > NewStates ;
for ( UStateTreeState * State : Factory . States )
{
UE : : StateTree : : Editor : : FixStateAfterDuplication ( State , ParentState , IDsMap , Links , NewStates ) ;
}
// Copy property bindings for the duplicated states.
if ( Factory . ClipboardBindings )
{
for ( const TPair < FGuid , FGuid > & Entry : IDsMap )
{
const FGuid OldTargetID = Entry . Key ;
const FGuid NewTargetID = Entry . Value ;
for ( const FStateTreePropertyPathBinding & Binding : Factory . ClipboardBindings - > Bindings )
{
if ( Binding . GetTargetPath ( ) . GetStructID ( ) = = OldTargetID )
{
FStateTreePropertyPath TargetPath ( Binding . GetTargetPath ( ) ) ;
TargetPath . SetStructID ( NewTargetID ) ;
FStateTreePropertyPath SourcePath ( Binding . GetSourcePath ( ) ) ;
if ( const FGuid * NewSourceID = IDsMap . Find ( Binding . GetSourcePath ( ) . GetStructID ( ) ) )
{
SourcePath . SetStructID ( * NewSourceID ) ;
}
TreeData - > GetPropertyEditorBindings ( ) - > AddPropertyBinding ( SourcePath , TargetPath ) ;
}
}
}
}
// Patch IDs in state links.
for ( FStateTreeStateLink * Link : Links )
{
if ( FGuid * NewID = IDsMap . Find ( Link - > ID ) )
{
Link - > ID = * NewID ;
}
}
for ( UStateTreeState * State : NewStates )
{
OnStateAdded . Broadcast ( State - > Parent , State ) ;
}
}
2023-06-14 06:17:45 -04:00
void FStateTreeViewModel : : DuplicateSelectedStates ( )
{
UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) ;
if ( TreeData = = nullptr )
{
return ;
}
TArray < UStateTreeState * > States ;
GetSelectedStates ( States ) ;
UE : : StateTree : : Editor : : RemoveContainedChildren ( States ) ;
if ( States . IsEmpty ( ) )
{
return ;
}
FString ExportedText = UE : : StateTree : : Editor : : ExportStatesToText ( TreeData , States ) ;
// Place duplicates after first selected state.
UStateTreeState * AfterState = States [ 0 ] ;
const int32 Index = AfterState - > Parent ? AfterState - > Parent - > Children . Find ( AfterState ) : TreeData - > SubTrees . Find ( AfterState ) ;
if ( Index ! = INDEX_NONE )
{
const FScopedTransaction Transaction ( LOCTEXT ( " DuplicateStatesTransaction " , " Duplicate State(s) " )) ;
2023-06-21 10:32:41 -04:00
PasteStatesAsChildrenFromText ( ExportedText , AfterState - > Parent , Index + 1 ) ;
2023-06-14 06:17:45 -04:00
}
}
2021-09-28 13:33:17 -04:00
void FStateTreeViewModel : : MoveSelectedStatesBefore ( UStateTreeState * TargetState )
{
2022-04-21 08:02:21 -04:00
MoveSelectedStates ( TargetState , FStateTreeViewModelInsert : : Before ) ;
2021-09-28 13:33:17 -04:00
}
void FStateTreeViewModel : : MoveSelectedStatesAfter ( UStateTreeState * TargetState )
{
2022-04-21 08:02:21 -04:00
MoveSelectedStates ( TargetState , FStateTreeViewModelInsert : : After ) ;
2021-09-28 13:33:17 -04:00
}
void FStateTreeViewModel : : MoveSelectedStatesInto ( UStateTreeState * TargetState )
{
2022-04-21 08:02:21 -04:00
MoveSelectedStates ( TargetState , FStateTreeViewModelInsert : : Into ) ;
2021-09-28 13:33:17 -04:00
}
2023-06-05 13:12:19 -04:00
bool FStateTreeViewModel : : CanEnableStates ( ) const
{
TArray < UStateTreeState * > States ;
GetSelectedStates ( States ) ;
for ( const UStateTreeState * State : States )
{
// Stop if at least one state can be enabled
if ( State - > bEnabled = = false )
{
return true ;
}
}
return false ;
}
bool FStateTreeViewModel : : CanDisableStates ( ) const
{
TArray < UStateTreeState * > States ;
GetSelectedStates ( States ) ;
for ( const UStateTreeState * State : States )
{
// Stop if at least one state can be disabled
if ( State - > bEnabled )
{
return true ;
}
}
return false ;
}
void FStateTreeViewModel : : SetSelectedStatesEnabled ( const bool bEnable )
{
TArray < UStateTreeState * > States ;
GetSelectedStates ( States ) ;
if ( States . Num ( ) > 0 )
{
const FScopedTransaction Transaction ( LOCTEXT ( " SetStatesEnabledTransaction " , " Set State Enabled " ) ) ;
for ( UStateTreeState * State : States )
{
State - > Modify ( ) ;
State - > bEnabled = bEnable ;
}
2023-08-16 15:18:31 -04:00
OnAssetChanged . Broadcast ( ) ;
2023-06-05 13:12:19 -04:00
}
}
2022-04-21 08:02:21 -04:00
void FStateTreeViewModel : : MoveSelectedStates ( UStateTreeState * TargetState , const FStateTreeViewModelInsert RelativeLocation )
2021-09-28 13:33:17 -04:00
{
2022-09-30 11:31:57 -04:00
UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) ;
2022-04-21 08:02:21 -04:00
if ( TreeData = = nullptr | | TargetState = = nullptr )
2021-09-28 13:33:17 -04:00
{
return ;
}
TArray < UStateTreeState * > States ;
GetSelectedStates ( States ) ;
// Remove child items whose parent also exists in the selection.
2022-04-21 08:02:21 -04:00
UE : : StateTree : : Editor : : RemoveContainedChildren ( States ) ;
2021-09-28 13:33:17 -04:00
2022-04-21 08:02:21 -04:00
// Remove states which contain target state as child.
States . RemoveAll ( [ TargetState ] ( const UStateTreeState * State )
{
return UE : : StateTree : : Editor : : IsChildOf ( State , TargetState ) ;
} ) ;
if ( States . Num ( ) > 0 & & TargetState ! = nullptr )
2021-09-28 13:33:17 -04:00
{
const FScopedTransaction Transaction ( LOCTEXT ( " MoveTransaction " , " Move " ) ) ;
TSet < UStateTreeState * > AffectedParents ;
TSet < UStateTreeState * > AffectedStates ;
2022-04-21 08:02:21 -04:00
UStateTreeState * TargetParent = TargetState - > Parent ;
if ( RelativeLocation = = FStateTreeViewModelInsert : : Into )
{
AffectedParents . Add ( TargetState ) ;
}
else
{
AffectedParents . Add ( TargetParent ) ;
}
2021-09-28 13:33:17 -04:00
for ( int32 i = States . Num ( ) - 1 ; i > = 0 ; i - - )
{
if ( UStateTreeState * State = States [ i ] )
{
2022-04-21 08:02:21 -04:00
State - > Modify ( ) ;
if ( State - > Parent )
{
AffectedParents . Add ( State - > Parent ) ;
}
2021-09-28 13:33:17 -04:00
}
}
2022-04-22 03:10:20 -04:00
if ( RelativeLocation = = FStateTreeViewModelInsert : : Into )
2022-04-21 14:39:34 -04:00
{
// Move into
TargetState - > Modify ( ) ;
}
2021-09-28 13:33:17 -04:00
for ( UStateTreeState * Parent : AffectedParents )
{
if ( Parent )
{
Parent - > Modify ( ) ;
}
else
{
TreeData - > Modify ( ) ;
}
}
// Add in reverse order to keep the original order.
for ( int32 i = States . Num ( ) - 1 ; i > = 0 ; i - - )
{
if ( UStateTreeState * SelectedState = States [ i ] )
{
AffectedStates . Add ( SelectedState ) ;
2022-04-21 08:02:21 -04:00
UStateTreeState * SelectedParent = SelectedState - > Parent ;
// Remove from current parent
2023-04-18 22:39:29 -04:00
TArray < TObjectPtr < UStateTreeState > > & ArrayToRemoveFrom = SelectedParent ? SelectedParent - > Children : TreeData - > SubTrees ;
2022-04-21 08:02:21 -04:00
const int32 ItemIndex = ArrayToRemoveFrom . Find ( SelectedState ) ;
if ( ItemIndex ! = INDEX_NONE )
{
ArrayToRemoveFrom . RemoveAt ( ItemIndex ) ;
SelectedState - > Parent = nullptr ;
}
// Insert to new parent
if ( RelativeLocation = = FStateTreeViewModelInsert : : Into )
{
// Into
TargetState - > Children . Insert ( SelectedState , /*Index*/ 0 ) ;
SelectedState - > Parent = TargetState ;
}
else
{
2023-04-18 22:39:29 -04:00
TArray < TObjectPtr < UStateTreeState > > & ArrayToMoveTo = TargetParent ? TargetParent - > Children : TreeData - > SubTrees ;
2022-04-21 08:02:21 -04:00
const int32 TargetIndex = ArrayToMoveTo . Find ( TargetState ) ;
if ( TargetIndex ! = INDEX_NONE )
{
if ( RelativeLocation = = FStateTreeViewModelInsert : : Before )
{
// Before
ArrayToMoveTo . Insert ( SelectedState , TargetIndex ) ;
SelectedState - > Parent = TargetParent ;
}
else if ( RelativeLocation = = FStateTreeViewModelInsert : : After )
{
// After
ArrayToMoveTo . Insert ( SelectedState , TargetIndex + 1 ) ;
SelectedState - > Parent = TargetParent ;
}
}
else
{
// Fallback, should never happen.
ensureMsgf ( false , TEXT ( " %s: Failed to find specified target state %s on state %s while moving a state. " ) , * GetNameSafe ( TreeData - > GetOuter ( ) ) , * GetNameSafe ( TargetState ) , * GetNameSafe ( SelectedParent ) ) ;
ArrayToMoveTo . Add ( SelectedState ) ;
SelectedState - > Parent = TargetParent ;
}
}
2021-09-28 13:33:17 -04:00
}
}
OnStatesMoved . Broadcast ( AffectedParents , AffectedStates ) ;
2022-09-30 11:31:57 -04:00
TArray < TWeakObjectPtr < UStateTreeState > > WeakStates ;
for ( UStateTreeState * State : States )
{
WeakStates . Add ( State ) ;
}
SetSelection ( WeakStates ) ;
2021-09-28 13:33:17 -04:00
}
}
2023-03-14 13:35:46 -04:00
void FStateTreeViewModel : : BindToDebuggerDelegates ( )
{
# if WITH_STATETREE_DEBUGGER
2023-12-13 10:21:13 -05:00
Debugger - > OnActiveStatesChanged . BindSPLambda ( this , [ this ] ( const FStateTreeTraceActiveStates & NewActiveStates )
2023-03-14 13:35:46 -04:00
{
if ( const UStateTree * OuterStateTree = GetStateTree ( ) )
{
2023-12-13 10:21:13 -05:00
for ( const FStateTreeTraceActiveStates : : FAssetActiveStates & AssetActiveStates : NewActiveStates . PerAssetStates )
2023-03-14 13:35:46 -04:00
{
2023-12-13 10:21:13 -05:00
// Only track states owned by the StateTree associated to the view model (skip linked assets)
if ( AssetActiveStates . WeakStateTree = = OuterStateTree )
{
ActiveStates . Reset ( AssetActiveStates . ActiveStates . Num ( ) ) ;
for ( const FStateTreeStateHandle Handle : AssetActiveStates . ActiveStates )
{
ActiveStates . Add ( OuterStateTree - > GetStateIdFromHandle ( Handle ) ) ;
}
}
2023-03-14 13:35:46 -04:00
}
}
} ) ;
# endif // WITH_STATETREE_DEBUGGER
}
bool FStateTreeViewModel : : IsStateActiveInDebugger ( const UStateTreeState & State ) const
{
# if WITH_STATETREE_DEBUGGER
return ActiveStates . Contains ( State . ID ) ;
# else
return false ;
# endif // WITH_STATETREE_DEBUGGER
}
2021-09-28 13:33:17 -04:00
# undef LOCTEXT_NAMESPACE