2021-09-28 13:33:17 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "StateTreeViewModel.h"
# include "Templates/SharedPointer.h"
# include "StateTree.h"
# include "StateTreeEditorData.h"
# include "StateTreeState.h"
# include "StateTreeTaskBase.h"
# include "StateTreeDelegates.h"
# include "Editor.h"
# include "EditorSupportDelegates.h"
# include "ScopedTransaction.h"
# include "Modules/ModuleManager.h"
# include "StateTreeEditorModule.h"
# include "Framework/MultiBox/MultiBoxBuilder.h"
# define LOCTEXT_NAMESPACE "StateTreeEditor"
2022-04-21 08:02:21 -04:00
namespace UE : : StateTree : : Editor
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 ( )
: TreeData ( nullptr )
{
}
FStateTreeViewModel : : ~ FStateTreeViewModel ( )
{
GEditor - > UnregisterForUndo ( this ) ;
UE : : StateTree : : Delegates : : OnIdentifierChanged . RemoveAll ( this ) ;
}
void FStateTreeViewModel : : Init ( UStateTreeEditorData * InTreeData )
{
TreeData = InTreeData ;
GEditor - > RegisterForUndo ( this ) ;
UE : : StateTree : : Delegates : : OnIdentifierChanged . AddSP ( this , & FStateTreeViewModel : : HandleIdentifierChanged ) ;
}
2022-04-21 08:02:21 -04:00
void FStateTreeViewModel : : HandleIdentifierChanged ( const UStateTree & StateTree ) const
2021-09-28 13:33:17 -04:00
{
const UStateTree * OuterStateTree = TreeData ? Cast < UStateTree > ( TreeData - > GetOuter ( ) ) : nullptr ;
if ( OuterStateTree = = & StateTree )
{
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-01-28 03:38:15 -05:00
return TreeData ! = nullptr ? & TreeData - > SubTrees : nullptr ;
}
int32 FStateTreeViewModel : : GetSubTreeCount ( ) const
{
return TreeData ! = nullptr ? TreeData - > SubTrees . Num ( ) : 0 ;
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-04-21 08:02:21 -04:00
const TArray < 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
TArray < UStateTreeState * > SelectedStatesArr ;
2022-04-21 08:02:21 -04:00
SelectedStatesArr . Add ( Selected ) ;
2021-09-28 13:33:17 -04:00
OnSelectionChanged . Broadcast ( SelectedStatesArr ) ;
}
void FStateTreeViewModel : : SetSelection ( const TArray < UStateTreeState * > & InSelectedStates )
{
SelectedStates . Reset ( ) ;
for ( UStateTreeState * State : InSelectedStates )
{
if ( State )
{
SelectedStates . Add ( State ) ;
}
}
TArray < FGuid > SelectedTaskIDArr ;
OnSelectionChanged . Broadcast ( InSelectedStates ) ;
}
bool FStateTreeViewModel : : IsSelected ( const UStateTreeState * State ) const
{
return SelectedStates . Contains ( State ) ;
}
bool FStateTreeViewModel : : IsChildOfSelection ( const UStateTreeState * State ) const
{
for ( const FWeakObjectPtr & WeakSelectedState : SelectedStates )
{
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 ( ) ;
for ( FWeakObjectPtr & WeakState : SelectedStates )
{
if ( UStateTreeState * State = Cast < UStateTreeState > ( WeakState . Get ( ) ) )
{
OutSelectedStates . Add ( State ) ;
}
}
}
2022-01-28 03:38:15 -05:00
bool FStateTreeViewModel : : HasSelection ( ) const
{
return SelectedStates . Num ( ) > 0 ;
}
2021-09-28 13:33:17 -04:00
void FStateTreeViewModel : : GetPersistentExpandedStates ( TSet < UStateTreeState * > & OutExpandedStates )
{
OutExpandedStates . Reset ( ) ;
2022-04-21 08:02:21 -04:00
if ( TreeData = = nullptr )
2021-09-28 13:33:17 -04:00
{
return ;
}
2022-01-28 03:38:15 -05:00
for ( UStateTreeState * SubTree : TreeData - > SubTrees )
2021-09-28 13:33:17 -04:00
{
2022-01-28 03:38:15 -05:00
GetExpandedStatesRecursive ( SubTree , OutExpandedStates ) ;
2021-09-28 13:33:17 -04:00
}
}
void FStateTreeViewModel : : GetExpandedStatesRecursive ( UStateTreeState * State , TSet < UStateTreeState * > & OutExpandedStates )
{
if ( State - > bExpanded )
{
OutExpandedStates . Add ( State ) ;
}
for ( UStateTreeState * Child : State - > Children )
{
GetExpandedStatesRecursive ( Child , OutExpandedStates ) ;
}
}
void FStateTreeViewModel : : SetPersistentExpandedStates ( TSet < UStateTreeState * > & InExpandedStates )
{
2022-04-21 08:02:21 -04:00
if ( TreeData = = nullptr )
2021-09-28 13:33:17 -04:00
{
return ;
}
TreeData - > Modify ( ) ;
for ( UStateTreeState * State : InExpandedStates )
{
2022-04-21 08:02:21 -04:00
if ( State ! = nullptr )
2021-09-28 13:33:17 -04:00
{
State - > bExpanded = true ;
}
}
}
void FStateTreeViewModel : : AddState ( UStateTreeState * AfterState )
{
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-04-21 08:02:21 -04:00
if ( AfterState ! = nullptr )
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-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-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 ( ) ;
}
}
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-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-21 14:49:31 -04:00
if ( RelativeLocation = = FStateTreeViewModelInsert : : Before )
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 ) ;
SetSelection ( States ) ;
}
}
# undef LOCTEXT_NAMESPACE