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 )
{
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-03-14 13:35:46 -04:00
BindToDebuggerDelegates ( ) ;
}
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 ( ) ;
}
}
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 ) ;
}
2022-04-21 08:02:21 -04:00
TArray < UStateTreeState * > * FStateTreeViewModel : : GetSubTrees ( ) const
2021-09-28 13:33:17 -04:00
{
2022-09-30 11:31:57 -04:00
UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) ;
2022-07-22 15:06:06 -04:00
return TreeData ! = nullptr ? & ToRawPtrTArrayUnsafe ( 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 ;
}
void FStateTreeViewModel : : GetSelectedStates ( TArray < UStateTreeState * > & OutSelectedStates )
{
OutSelectedStates . Reset ( ) ;
2022-09-30 11:31:57 -04:00
for ( 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 ) ;
}
}
}
2022-09-30 11:31:57 -04:00
void FStateTreeViewModel : : GetSelectedStates ( TArray < TWeakObjectPtr < UStateTreeState > > & OutSelectedStates )
{
OutSelectedStates . Reset ( ) ;
for ( TWeakObjectPtr < UStateTreeState > & WeakState : SelectedStates )
{
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 ( ) ;
}
2022-04-21 08:02:21 -04:00
TArray < UStateTreeState * > & ParentArray = ParentState ? ParentState - > Children : TreeData - > SubTrees ;
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
TArray < UStateTreeState * > & ArrayToRemoveFrom = ParentState ? ParentState - > Children : TreeData - > SubTrees ;
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 )
{
PasteStatesAsChildrenFromClipboard ( AfterState - > Parent , Index + 1 ) ;
}
}
}
void FStateTreeViewModel : : PasteStatesAsChildrenFromClipboard ( UStateTreeState * ParentState , const int32 Index )
{
UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) ;
if ( TreeData = = nullptr )
{
return ;
}
FString TextToImport ;
FPlatformApplicationMisc : : ClipboardPaste ( TextToImport ) ;
UObject * Outer = ParentState ? static_cast < UObject * > ( ParentState ) : static_cast < UObject * > ( TreeData ) ;
Outer - > Modify ( ) ;
UE : : StateTree : : Editor : : FStateTreeStateTextFactory Factory ;
Factory . ProcessBuffer ( Outer , RF_Transactional , TextToImport ) ;
TArray < UStateTreeState * > & ParentArray = ParentState ? ParentState - > Children : TreeData - > SubTrees ;
const int32 TargetIndex = ( Index = = INDEX_NONE ) ? ParentArray . Num ( ) : Index ;
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 ) ;
}
}
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
}
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
2022-01-28 03:38:15 -05:00
TArray < 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
{
TArray < UStateTreeState * > & ArrayToMoveTo = TargetParent ? TargetParent - > Children : TreeData - > SubTrees ;
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
Debugger - > OnActiveStatesChanged . BindLambda ( [ this ] ( const TConstArrayView < FStateTreeStateHandle > NewActiveStates )
{
if ( const UStateTree * OuterStateTree = GetStateTree ( ) )
{
ActiveStates . Reset ( NewActiveStates . Num ( ) ) ;
for ( const FStateTreeStateHandle Handle : NewActiveStates )
{
ActiveStates . Add ( OuterStateTree - > GetStateIdFromHandle ( Handle ) ) ;
}
}
} ) ;
Debugger - > OnBreakpointsChanged . BindLambda ( [ this ] ( TConstArrayView < FStateTreeStateHandle > NewActiveBreakpoints )
{
StatesWithBreakpoints . Reset ( NewActiveBreakpoints . Num ( ) ) ;
const UStateTreeEditorData * TreeData = TreeDataWeak . Get ( ) ;
if ( const UStateTree * OuterStateTree = TreeData ? TreeData - > GetTypedOuter < UStateTree > ( ) : nullptr )
{
TreeData - > VisitHierarchy ( [ this , & NewActiveBreakpoints , & OuterStateTree ] ( const UStateTreeState & State , UStateTreeState * /*ParentState*/ )
{
const FStateTreeStateHandle Handle = OuterStateTree - > GetStateHandleFromId ( State . ID ) ;
if ( NewActiveBreakpoints . Contains ( Handle ) )
{
2023-03-15 11:59:44 -04:00
StatesWithBreakpoints . Emplace ( State . ID ) ;
2023-03-14 13:35:46 -04:00
}
return EStateTreeVisitor : : Continue ;
} ) ;
}
} ) ;
# endif // WITH_STATETREE_DEBUGGER
}
bool FStateTreeViewModel : : DoesStateHaveBreakpoint ( const UStateTreeState & State ) const
{
# if WITH_STATETREE_DEBUGGER
2023-03-15 11:59:44 -04:00
return StatesWithBreakpoints . Contains ( State . ID ) ;
2023-03-14 13:35:46 -04:00
# else
return false ;
# 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