You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
This change consists of multiple changes: Core: - Deprecation of ANY_PACKAGE macro. Added ANY_PACKAGE_DEPRECATED macro which can still be used for backwards compatibility purposes (only used in CoreUObject) - Deprecation of StaticFindObjectFast* functions that take bAnyPackage parameter - Added UStruct::GetStructPathName function that returns FTopLevelAssetPath representing the path name (package + object FName, super quick compared to UObject::GetPathName) + wrapper UClass::GetClassPathName to make it look better when used with UClasses - Added (Static)FindFirstObject* functions that find a first object given its Name (no Outer). These functions are used in places I consider valid to do global UObject (UClass) lookups like parsing command line parameters / checking for unique object names - Added static UClass::TryFindType function which serves a similar purpose as FindFirstObject however it's going to throw a warning (with a callstack / maybe ensure in the future?) if short class name is provided. This function is used in places that used to use short class names but now should have been converted to use path names to catch any potential regressions and or edge cases I missed. - Added static UClass::TryConvertShortNameToPathName utility function - Added static UClass::TryFixShortClassNameExportPath utility function - Object text export paths will now also include class path (Texture2D'/Game/Textures/Grass.Grass' -> /Script/Engine.Texture2D'/Game/Textures/Grass.Grass') - All places that manually generated object export paths for objects will now use FObjectPropertyBase::GetExportPath - Added a new startup test that checks for short type names in UClass/FProperty MetaData values AssetRegistry: - Deprecated any member variables (FAssetData / FARFilter) or functions that use FNames to represent class names and replaced them with FTopLevelAssetPath - Added new member variables and new function overloads that use FTopLevelAssetPath to represent class names - This also applies to a few other modules' APIs to match AssetRegistry changes Everything else: - Updated code that used ANY_PACKAGE (depending on the use case) to use FindObject(nullptr, PathToObject), UClass::TryFindType (used when path name is expected, warns if it's a short name) or FindFirstObject (usually for finding types based on user input but there's been a few legitimate use cases not related to user input) - Updated code that used AssetRegistry API to use FTopLevelAssetPaths and USomeClass::StaticClass()->GetClassPathName() instead of GetFName() - Updated meta data and hardcoded FindObject(ANY_PACKAGE, "EEnumNameOrClassName") calls to use path names #jira UE-99463 #rb many.people [FYI] Marcus.Wassmer #preflight 629248ec2256738f75de9b32 #codereviewnumbers 20320742, 20320791, 20320799, 20320756, 20320809, 20320830, 20320840, 20320846, 20320851, 20320863, 20320780, 20320765, 20320876, 20320786 #ROBOMERGE-OWNER: robert.manuszewski #ROBOMERGE-AUTHOR: robert.manuszewski #ROBOMERGE-SOURCE: CL 20430220 via CL 20433854 via CL 20435474 via CL 20435484 #ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v949-20362246) [CL 20448496 by robert manuszewski in ue5-main branch]
1051 lines
35 KiB
C++
1051 lines
35 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
#include "SSkeletonSlotNames.h"
|
|
#include "Framework/Notifications/NotificationManager.h"
|
|
#include "Widgets/Notifications/SNotificationList.h"
|
|
#include "Misc/ScopedSlowTask.h"
|
|
#include "Framework/MultiBox/MultiBoxDefs.h"
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
#include "Animation/Skeleton.h"
|
|
#include "Styling/AppStyle.h"
|
|
#include "Layout/WidgetPath.h"
|
|
#include "Framework/Application/MenuStack.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "Animation/AnimMontage.h"
|
|
#include "FileHelpers.h"
|
|
#include "SSlotNameReferenceWindow.h"
|
|
#include "AssetRegistry/AssetRegistryModule.h"
|
|
#include "ScopedTransaction.h"
|
|
#include "Widgets/Input/SSearchBox.h"
|
|
#include "Widgets/Text/SInlineEditableTextBlock.h"
|
|
#include "Widgets/Input/STextEntryPopup.h"
|
|
#include "Animation/AnimBlueprint.h"
|
|
#include "AnimGraphNode_Slot.h"
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
#include "Interfaces/IMainFrameModule.h"
|
|
#include "IEditableSkeleton.h"
|
|
#include "TabSpawners.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "SkeletonSlotNames"
|
|
|
|
static const FName ColumnId_SlotNameLabel( "SlotName" );
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// SMorphTargetListRow
|
|
|
|
typedef TSharedPtr< FDisplayedSlotNameInfo > FDisplayedSlotNameInfoPtr;
|
|
|
|
class SSlotNameListRow
|
|
: public SMultiColumnTableRow< FDisplayedSlotNameInfoPtr >
|
|
{
|
|
public:
|
|
|
|
SLATE_BEGIN_ARGS( SSlotNameListRow ) {}
|
|
|
|
/** The item for this row **/
|
|
SLATE_ARGUMENT( FDisplayedSlotNameInfoPtr, Item )
|
|
|
|
/* Widget used to display the list of morph targets */
|
|
SLATE_ARGUMENT( TSharedPtr<SSkeletonSlotNames>, SlotNameListView )
|
|
|
|
SLATE_END_ARGS()
|
|
|
|
void Construct( const FArguments& InArgs, const TSharedRef<STableViewBase>& OwnerTableView );
|
|
|
|
/** Overridden from SMultiColumnTableRow. Generates a widget for this column of the tree row. */
|
|
virtual TSharedRef<SWidget> GenerateWidgetForColumn( const FName& ColumnName ) override;
|
|
|
|
private:
|
|
|
|
/** Widget used to display the list of slot name */
|
|
TSharedPtr<SSkeletonSlotNames> SlotNameListView;
|
|
|
|
/** The notify being displayed by this row */
|
|
FDisplayedSlotNameInfoPtr Item;
|
|
};
|
|
|
|
void SSlotNameListRow::Construct( const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView )
|
|
{
|
|
Item = InArgs._Item;
|
|
SlotNameListView = InArgs._SlotNameListView;
|
|
|
|
check( Item.IsValid() );
|
|
|
|
SMultiColumnTableRow< FDisplayedSlotNameInfoPtr >::Construct( FSuperRowType::FArguments(), InOwnerTableView );
|
|
}
|
|
|
|
TSharedRef< SWidget > SSlotNameListRow::GenerateWidgetForColumn( const FName& ColumnName )
|
|
{
|
|
check( ColumnName == ColumnId_SlotNameLabel );
|
|
|
|
// Items can be either Slots or Groups.
|
|
FText ItemText = Item->bIsGroupItem ? FText::Format(LOCTEXT("AnimSlotManagerGroupItem", "(Group) {0}"), FText::FromName(Item->Name))
|
|
: FText::Format(LOCTEXT("AnimSlotManagerSlotItem", "(Slot) {0}"), FText::FromName(Item->Name));
|
|
|
|
return
|
|
SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
SNew(SExpanderArrow, SharedThis(this))
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(ItemText)
|
|
];
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
// FSkeletonSlotNamesSummoner
|
|
|
|
FSkeletonSlotNamesSummoner::FSkeletonSlotNamesSummoner(TSharedPtr<class FAssetEditorToolkit> InHostingApp, const TSharedRef<IEditableSkeleton>& InEditableSkeleton, FOnObjectSelected InOnObjectSelected)
|
|
: FWorkflowTabFactory(FPersonaTabs::SkeletonSlotNamesID, InHostingApp)
|
|
, EditableSkeleton(InEditableSkeleton)
|
|
, OnObjectSelected(InOnObjectSelected)
|
|
{
|
|
TabLabel = LOCTEXT("AnimSlotManagerTabTitle", "Anim Slot Manager");
|
|
TabIcon = FSlateIcon(FAppStyle::GetAppStyleSetName(), "Persona.Tabs.AnimSlotManager");
|
|
|
|
EnableTabPadding();
|
|
bIsSingleton = true;
|
|
|
|
ViewMenuDescription = LOCTEXT("SkeletonSlotNamesMenu", "Anim Slots");
|
|
ViewMenuTooltip = LOCTEXT("SkeletonSlotNames_ToolTip", "Manage Skeleton's Slots and Groups.");
|
|
}
|
|
|
|
TSharedRef<SWidget> FSkeletonSlotNamesSummoner::CreateTabBody(const FWorkflowTabSpawnInfo& Info) const
|
|
{
|
|
return SNew(SSkeletonSlotNames, EditableSkeleton.Pin().ToSharedRef())
|
|
.OnObjectSelected(OnObjectSelected);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
// SSkeletonSlotNames
|
|
|
|
void SSkeletonSlotNames::Construct(const FArguments& InArgs, const TSharedRef<IEditableSkeleton>& InEditableSkeleton)
|
|
{
|
|
EditableSkeleton = InEditableSkeleton;
|
|
OnObjectSelected = InArgs._OnObjectSelected;
|
|
|
|
InEditableSkeleton->RegisterOnSlotsChanged(FSimpleMulticastDelegate::FDelegate::CreateSP(this, &SSkeletonSlotNames::RefreshSlotNameListWithFilter));
|
|
|
|
// Toolbar
|
|
FToolBarBuilder ToolbarBuilder(TSharedPtr< FUICommandList >(), FMultiBoxCustomization::None);
|
|
|
|
// Save USkeleton
|
|
ToolbarBuilder.AddToolBarButton(
|
|
FUIAction(FExecuteAction::CreateSP(this, &SSkeletonSlotNames::OnSaveSkeleton))
|
|
, NAME_None
|
|
, LOCTEXT("AnimSlotManagerToolbarSaveLabel", "Save")
|
|
, LOCTEXT("AnimSlotManagerToolbarSaveTooltip", "Saves changes into Skeleton asset")
|
|
, FSlateIcon(FAppStyle::GetAppStyleSetName(), "AnimSlotManager.SaveSkeleton")
|
|
);
|
|
|
|
ToolbarBuilder.AddSeparator();
|
|
|
|
// Add Slot
|
|
ToolbarBuilder.AddToolBarButton(
|
|
FUIAction(FExecuteAction::CreateSP(this, &SSkeletonSlotNames::OnAddSlot))
|
|
, NAME_None
|
|
, LOCTEXT("AnimSlotManagerToolbarAddSlotLabel", "Add Slot")
|
|
, LOCTEXT("AnimSlotManagerToolbarAddSlotTooltip", "Create a new unique Slot name")
|
|
, FSlateIcon(FAppStyle::GetAppStyleSetName(), "AnimSlotManager.AddSlot")
|
|
);
|
|
|
|
// Add Group
|
|
ToolbarBuilder.AddToolBarButton(
|
|
FUIAction(FExecuteAction::CreateSP(this, &SSkeletonSlotNames::OnAddGroup))
|
|
, NAME_None
|
|
, LOCTEXT("AnimSlotManagerToolbarAddGroupLabel", "Add Group")
|
|
, LOCTEXT("AnimSlotManagerToolbarAddGroupTooltip", "Create a new unique Group name")
|
|
, FSlateIcon(FAppStyle::GetAppStyleSetName(), "AnimSlotManager.AddGroup")
|
|
);
|
|
|
|
this->ChildSlot
|
|
[
|
|
SNew( SVerticalBox )
|
|
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
ToolbarBuilder.MakeWidget()
|
|
]
|
|
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding( FMargin( 0.0f, 0.0f, 0.0f, 4.0f ) )
|
|
[
|
|
SAssignNew( NameFilterBox, SSearchBox )
|
|
.SelectAllTextWhenFocused( true )
|
|
.OnTextChanged( this, &SSkeletonSlotNames::OnFilterTextChanged )
|
|
.OnTextCommitted( this, &SSkeletonSlotNames::OnFilterTextCommitted )
|
|
.HintText( LOCTEXT( "AnimSlotManagerSlotNameSearchBoxHint", "Slot name filter...") )
|
|
]
|
|
|
|
+ SVerticalBox::Slot()
|
|
.FillHeight( 1.0f ) // This is required to make the scrollbar work, as content overflows Slate containers by default
|
|
[
|
|
SAssignNew( SlotNameListView, SSlotNameListType )
|
|
.TreeItemsSource(&NotifyList)
|
|
.OnGenerateRow( this, &SSkeletonSlotNames::GenerateNotifyRow )
|
|
.OnGetChildren(this, &SSkeletonSlotNames::GetChildrenForInfo)
|
|
.OnContextMenuOpening( this, &SSkeletonSlotNames::OnGetContextMenuContent )
|
|
.SelectionMode(ESelectionMode::Single)
|
|
.OnSelectionChanged( this, &SSkeletonSlotNames::OnNotifySelectionChanged )
|
|
.ItemHeight( 22.0f )
|
|
.HeaderRow
|
|
(
|
|
SNew( SHeaderRow )
|
|
+ SHeaderRow::Column( ColumnId_SlotNameLabel )
|
|
.DefaultLabel( LOCTEXT( "SlotNameNameLabel", "Slot Name" ) )
|
|
)
|
|
]
|
|
];
|
|
|
|
CreateSlotNameList();
|
|
}
|
|
|
|
void SSkeletonSlotNames::OnFilterTextChanged( const FText& SearchText )
|
|
{
|
|
FilterText = SearchText;
|
|
|
|
RefreshSlotNameListWithFilter();
|
|
}
|
|
|
|
void SSkeletonSlotNames::OnFilterTextCommitted( const FText& SearchText, ETextCommit::Type CommitInfo )
|
|
{
|
|
// Just do the same as if the user typed in the box
|
|
OnFilterTextChanged( SearchText );
|
|
}
|
|
|
|
TSharedRef<ITableRow> SSkeletonSlotNames::GenerateNotifyRow(TSharedPtr<FDisplayedSlotNameInfo> InInfo, const TSharedRef<STableViewBase>& OwnerTable)
|
|
{
|
|
check( InInfo.IsValid() );
|
|
|
|
return
|
|
SNew( SSlotNameListRow, OwnerTable )
|
|
.Item( InInfo )
|
|
.SlotNameListView( SharedThis( this ) );
|
|
}
|
|
|
|
void SSkeletonSlotNames::GetChildrenForInfo(TSharedPtr<FDisplayedSlotNameInfo> InInfo, TArray< TSharedPtr<FDisplayedSlotNameInfo> >& OutChildren)
|
|
{
|
|
check(InInfo.IsValid());
|
|
OutChildren = InInfo->Children;
|
|
}
|
|
|
|
TSharedPtr<SWidget> SSkeletonSlotNames::OnGetContextMenuContent() const
|
|
{
|
|
const bool bShouldCloseWindowAfterMenuSelection = true;
|
|
FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, NULL);
|
|
|
|
TArray< TSharedPtr< FDisplayedSlotNameInfo > > SelectedItems = SlotNameListView.Get()->GetSelectedItems();
|
|
|
|
bool bHasSelectedItem = (SelectedItems.Num() > 0);
|
|
bool bShowGroupItem = bHasSelectedItem && SelectedItems[0].Get()->bIsGroupItem;
|
|
bool bShowSlotItem = bHasSelectedItem && !SelectedItems[0].Get()->bIsGroupItem;
|
|
|
|
if (bShowGroupItem)
|
|
{
|
|
TSharedPtr<FDisplayedSlotNameInfo> SelectedItemPtr = SelectedItems[0];
|
|
|
|
MenuBuilder.BeginSection("SlotManagerSlotGroupActions", LOCTEXT("SlotManagerSlotGroupActions", "Slot Group Actions"));
|
|
// Delete Slot Group
|
|
{
|
|
FDisplayedSlotNameInfo* SlotInfo = SelectedItemPtr.Get();
|
|
|
|
FUIAction Action = FUIAction(FExecuteAction::CreateSP(const_cast<SSkeletonSlotNames*>(this), &SSkeletonSlotNames::OnDeleteSlotGroup, SlotInfo->Name));
|
|
Action.CanExecuteAction.BindSP(const_cast<SSkeletonSlotNames*>(this), &SSkeletonSlotNames::CanDeleteSlotGroup, SlotInfo->Name);
|
|
const FText Label = LOCTEXT("AnimSlotManagerContextMenuDeleteSlotGroupLabel", "Delete Slot Group");
|
|
const FText ToolTipText = LOCTEXT("AnimSlotManagerContextMenuDeleteSlotGroupTooltip", "Delete this slot group.");
|
|
MenuBuilder.AddMenuEntry(Label, ToolTipText, FSlateIcon(), Action);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
}
|
|
else if (bShowSlotItem)
|
|
{
|
|
TSharedPtr<FDisplayedSlotNameInfo> SelectedItemPtr = SelectedItems[0];
|
|
|
|
MenuBuilder.BeginSection("SlotManagerSlotActions", LOCTEXT("SlotManagerSlotActions", "Slot Actions"));
|
|
// Set Slot Group
|
|
{
|
|
MenuBuilder.AddSubMenu(
|
|
FText::Format(LOCTEXT("ContextMenuSetSlotGroupLabel", "Set Slot {0} Group to"), FText::FromName(SelectedItems[0].Get()->Name)),
|
|
FText::Format(LOCTEXT("ContextMenuSetSlotGroupToolTip", "Set Slot {0} Group"), FText::FromName(SelectedItems[0].Get()->Name)),
|
|
FNewMenuDelegate::CreateRaw(const_cast<SSkeletonSlotNames*>(this), &SSkeletonSlotNames::FillSetSlotGroupSubMenu));
|
|
}
|
|
// Rename Slot
|
|
{
|
|
FDisplayedSlotNameInfo* SlotInfo = SelectedItemPtr.Get();
|
|
|
|
FUIAction Action = FUIAction(FExecuteAction::CreateSP(const_cast<SSkeletonSlotNames*>(this), &SSkeletonSlotNames::OnRenameSlot, SlotInfo->Name));
|
|
const FText Label = LOCTEXT("AnimSlotManagerContextMenuRenameSlotLabel", "Rename Slot");
|
|
const FText ToolTipText = LOCTEXT("AnimSlotManagerContextMenuRenameSlotTooltip", "Rename this slot");
|
|
MenuBuilder.AddMenuEntry(Label, ToolTipText, FSlateIcon(), Action);
|
|
}
|
|
// Delete Slot
|
|
{
|
|
FDisplayedSlotNameInfo* SlotInfo = SelectedItemPtr.Get();
|
|
|
|
FUIAction Action = FUIAction(FExecuteAction::CreateSP(const_cast<SSkeletonSlotNames*>(this), &SSkeletonSlotNames::OnDeleteSlot, SlotInfo->Name));
|
|
const FText Label = LOCTEXT("AnimSlotManagerContextMenuDeleteSlotLabel", "Delete Slot");
|
|
const FText ToolTipText = LOCTEXT("AnimSlotManagerContextMenuDeleteSlotTooltip", "Delete this slot.");
|
|
MenuBuilder.AddMenuEntry(Label, ToolTipText, FSlateIcon(), Action);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
}
|
|
|
|
MenuBuilder.BeginSection("SlotManagerGeneralActions", LOCTEXT("SlotManagerGeneralActions", "Slot Manager Actions"));
|
|
// Add Slot
|
|
{
|
|
FUIAction Action = FUIAction(FExecuteAction::CreateSP(const_cast<SSkeletonSlotNames*>(this), &SSkeletonSlotNames::OnAddSlot));
|
|
const FText Label = LOCTEXT("AnimSlotManagerContextMenuAddSlotLabel", "Add Slot");
|
|
const FText ToolTipText = LOCTEXT("AnimSlotManagerContextMenuAddSlotTooltip", "Adds a new Slot");
|
|
MenuBuilder.AddMenuEntry(Label, ToolTipText, FSlateIcon(), Action);
|
|
}
|
|
// Add Group
|
|
{
|
|
FUIAction Action = FUIAction(FExecuteAction::CreateSP(const_cast<SSkeletonSlotNames*>(this), &SSkeletonSlotNames::OnAddGroup));
|
|
const FText Label = LOCTEXT("AnimSlotManagerContextMenuAddGroupLabel", "Add Group");
|
|
const FText ToolTipText = LOCTEXT("AnimSlotManagerContextMenuAddGroupTooltip", "Adds a new Group");
|
|
MenuBuilder.AddMenuEntry(Label, ToolTipText, FSlateIcon(), Action);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
void SSkeletonSlotNames::FillSetSlotGroupSubMenu(FMenuBuilder& MenuBuilder)
|
|
{
|
|
const TArray<FAnimSlotGroup>& SlotGroups = EditableSkeleton->GetSkeleton().GetSlotGroups();
|
|
for (auto SlotGroup : SlotGroups)
|
|
{
|
|
const FName& GroupName = SlotGroup.GroupName;
|
|
|
|
const FText ToolTipText = FText::Format(LOCTEXT("ContextMenuSetSlotSubMenuToolTip", "Changes slot's group to {0}"), FText::FromName(GroupName));
|
|
/* FString Label = Class->GetDisplayNameText().ToString();*/
|
|
const FText Label = FText::FromName(GroupName);
|
|
|
|
FUIAction UIAction;
|
|
UIAction.ExecuteAction.BindRaw(this, &SSkeletonSlotNames::ContextMenuOnSetSlot, GroupName);
|
|
MenuBuilder.AddMenuEntry(Label, ToolTipText, FSlateIcon(), UIAction);
|
|
}
|
|
}
|
|
|
|
void SSkeletonSlotNames::ContextMenuOnSetSlot(FName InNewGroupName)
|
|
{
|
|
TArray< TSharedPtr< FDisplayedSlotNameInfo > > SelectedItems = SlotNameListView.Get()->GetSelectedItems();
|
|
|
|
bool bHasSelectedItem = (SelectedItems.Num() > 0);
|
|
bool bShowSlotItem = bHasSelectedItem && !SelectedItems[0].Get()->bIsGroupItem;
|
|
|
|
if (bShowSlotItem)
|
|
{
|
|
const FName SlotName = SelectedItems[0].Get()->Name;
|
|
if (EditableSkeleton->GetSkeleton().ContainsSlotName(SlotName))
|
|
{
|
|
EditableSkeleton->SetSlotGroupName(SlotName, InNewGroupName);
|
|
|
|
RefreshSlotNameListWithFilter();
|
|
}
|
|
|
|
// Highlight newly created item.
|
|
TSharedPtr< FDisplayedSlotNameInfo > Item = FindItemNamed(SlotName);
|
|
if (Item.IsValid())
|
|
{
|
|
SlotNameListView->SetSelection(Item);
|
|
}
|
|
|
|
FSlateApplication::Get().DismissAllMenus();
|
|
}
|
|
}
|
|
|
|
void SSkeletonSlotNames::OnNotifySelectionChanged(TSharedPtr<FDisplayedSlotNameInfo> Selection, ESelectInfo::Type SelectInfo)
|
|
{
|
|
if(Selection.IsValid())
|
|
{
|
|
ShowNotifyInDetailsView(Selection->Name);
|
|
}
|
|
}
|
|
|
|
void SSkeletonSlotNames::OnSaveSkeleton()
|
|
{
|
|
TArray< UPackage* > PackagesToSave;
|
|
PackagesToSave.Add(EditableSkeleton->GetSkeleton().GetOutermost());
|
|
|
|
FEditorFileUtils::PromptForCheckoutAndSave(PackagesToSave, /*bCheckDirty=*/ false, /*bPromptToSave=*/ false);
|
|
}
|
|
|
|
void SSkeletonSlotNames::OnAddSlot()
|
|
{
|
|
TSharedRef<STextEntryPopup> TextEntry =
|
|
SNew(STextEntryPopup)
|
|
.Label(LOCTEXT("NewSlotName_AskSlotName", "New Slot Name"))
|
|
.OnTextCommitted(this, &SSkeletonSlotNames::AddSlotPopUpOnCommit);
|
|
|
|
// Show dialog to enter new track name
|
|
FSlateApplication::Get().PushMenu(
|
|
SharedThis(this),
|
|
FWidgetPath(),
|
|
TextEntry,
|
|
FSlateApplication::Get().GetCursorPos(),
|
|
FPopupTransitionEffect(FPopupTransitionEffect::TypeInPopup)
|
|
);
|
|
}
|
|
|
|
void SSkeletonSlotNames::OnAddGroup()
|
|
{
|
|
TSharedRef<STextEntryPopup> TextEntry =
|
|
SNew(STextEntryPopup)
|
|
.Label(LOCTEXT("NewGroupName_AskGroupName", "New Group Name"))
|
|
.OnTextCommitted(this, &SSkeletonSlotNames::AddGroupPopUpOnCommit);
|
|
|
|
// Show dialog to enter new track name
|
|
FSlateApplication::Get().PushMenu(
|
|
SharedThis(this),
|
|
FWidgetPath(),
|
|
TextEntry,
|
|
FSlateApplication::Get().GetCursorPos(),
|
|
FPopupTransitionEffect(FPopupTransitionEffect::TypeInPopup)
|
|
);
|
|
}
|
|
|
|
void SSkeletonSlotNames::AddSlotPopUpOnCommit(const FText & InNewSlotText, ETextCommit::Type CommitInfo)
|
|
{
|
|
if (!InNewSlotText.IsEmpty())
|
|
{
|
|
const FScopedTransaction Transaction(LOCTEXT("NewSlotName_AddSlotName", "Add New Slot Node Name"));
|
|
|
|
FName NewSlotName = FName(*InNewSlotText.ToString());
|
|
const USkeleton& Skeleton = EditableSkeleton->GetSkeleton();
|
|
// Keep slot and group names unique
|
|
if (!Skeleton.ContainsSlotName(NewSlotName) && (Skeleton.FindAnimSlotGroup(NewSlotName) == nullptr))
|
|
{
|
|
TArray< TSharedPtr< FDisplayedSlotNameInfo > > SelectedItems = SlotNameListView->GetSelectedItems();
|
|
bool bHasSelectedItem = (SelectedItems.Num() > 0);
|
|
bool bShowGroupItem = bHasSelectedItem && SelectedItems[0].Get()->bIsGroupItem;
|
|
|
|
EditableSkeleton->SetSlotGroupName(NewSlotName, bShowGroupItem ? SelectedItems[0].Get()->Name : FAnimSlotGroup::DefaultGroupName);
|
|
|
|
RefreshSlotNameListWithFilter();
|
|
}
|
|
|
|
// Highlight newly created item.
|
|
TSharedPtr< FDisplayedSlotNameInfo > Item = FindItemNamed(NewSlotName);
|
|
if (Item.IsValid())
|
|
{
|
|
SlotNameListView->SetSelection(Item);
|
|
}
|
|
|
|
FSlateApplication::Get().DismissAllMenus();
|
|
}
|
|
}
|
|
|
|
void SSkeletonSlotNames::AddGroupPopUpOnCommit(const FText & InNewGroupText, ETextCommit::Type CommitInfo)
|
|
{
|
|
if (!InNewGroupText.IsEmpty())
|
|
{
|
|
FName NewGroupName = FName(*InNewGroupText.ToString());
|
|
const USkeleton& Skeleton = EditableSkeleton->GetSkeleton();
|
|
// Keep slot and group names unique
|
|
if (!Skeleton.ContainsSlotName(NewGroupName) && EditableSkeleton->AddSlotGroupName(NewGroupName))
|
|
{
|
|
RefreshSlotNameListWithFilter();
|
|
}
|
|
|
|
// Highlight newly created item.
|
|
TSharedPtr< FDisplayedSlotNameInfo > Item = FindItemNamed(NewGroupName);
|
|
if (Item.IsValid())
|
|
{
|
|
SlotNameListView->SetSelection(Item);
|
|
}
|
|
|
|
FSlateApplication::Get().DismissAllMenus();
|
|
}
|
|
}
|
|
|
|
void SSkeletonSlotNames::GetCompatibleAnimBlueprints( TArray<FAssetData>& OutAssets )
|
|
{
|
|
//Get the skeleton tag to search for
|
|
const USkeleton& Skeleton = EditableSkeleton->GetSkeleton();
|
|
FString SkeletonExportName = FAssetData(&Skeleton).GetExportTextName();
|
|
|
|
// Load the asset registry module
|
|
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
|
|
|
|
TArray<FAssetData> AssetDataList;
|
|
AssetRegistryModule.Get().GetAssetsByClass(UAnimBlueprint::StaticClass()->GetClassPathName(), AssetDataList, true);
|
|
|
|
OutAssets.Empty(AssetDataList.Num());
|
|
|
|
for(FAssetData& Data : AssetDataList)
|
|
{
|
|
const FString AssetSkeleton = Data.GetTagValueRef<FString>("TargetSkeleton");
|
|
if(AssetSkeleton == SkeletonExportName)
|
|
{
|
|
OutAssets.Add(Data);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SSkeletonSlotNames::RefreshSlotNameListWithFilter()
|
|
{
|
|
CreateSlotNameList( NameFilterBox->GetText().ToString() );
|
|
}
|
|
|
|
void SSkeletonSlotNames::CreateSlotNameList(const FString& SearchText)
|
|
{
|
|
NotifyList.Empty();
|
|
|
|
const USkeleton& Skeleton = EditableSkeleton->GetSkeleton();
|
|
const TArray<FAnimSlotGroup>& SlotGroups = Skeleton.GetSlotGroups();
|
|
for (auto SlotGroup : SlotGroups)
|
|
{
|
|
const FName& GroupName = SlotGroup.GroupName;
|
|
|
|
TSharedRef<FDisplayedSlotNameInfo> GroupItem = FDisplayedSlotNameInfo::Make(GroupName, true);
|
|
SlotNameListView->SetItemExpansion(GroupItem, true);
|
|
NotifyList.Add(GroupItem);
|
|
|
|
for (auto SlotName : SlotGroup.SlotNames)
|
|
{
|
|
if (SearchText.IsEmpty() || GroupName.ToString().Contains(SearchText) || SlotName.ToString().Contains(SearchText))
|
|
{
|
|
TSharedRef<FDisplayedSlotNameInfo> SlotItem = FDisplayedSlotNameInfo::Make(SlotName, false);
|
|
SlotNameListView->SetItemExpansion(SlotItem, true);
|
|
NotifyList[NotifyList.Num() - 1]->Children.Add(SlotItem);
|
|
}
|
|
}
|
|
}
|
|
|
|
SlotNameListView->RequestTreeRefresh();
|
|
}
|
|
|
|
TSharedPtr< FDisplayedSlotNameInfo > SSkeletonSlotNames::FindItemNamed(FName ItemName) const
|
|
{
|
|
for (auto SlotGroupItem : NotifyList)
|
|
{
|
|
if (SlotGroupItem->Name == ItemName)
|
|
{
|
|
return SlotGroupItem;
|
|
}
|
|
for (auto SlotItem : SlotGroupItem->Children)
|
|
{
|
|
if (SlotItem->Name == ItemName)
|
|
{
|
|
return SlotItem;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void SSkeletonSlotNames::ShowNotifyInDetailsView(FName NotifyName)
|
|
{
|
|
// @todo nothing to show now, but in the future
|
|
// we can show the list of montage that are used by this slot node?
|
|
}
|
|
|
|
void SSkeletonSlotNames::GetCompatibleAnimMontages(TArray<struct FAssetData>& OutAssets)
|
|
{
|
|
//Get the skeleton tag to search for
|
|
const USkeleton& Skeleton = EditableSkeleton->GetSkeleton();
|
|
FString SkeletonExportName = FAssetData(&Skeleton).GetExportTextName();
|
|
|
|
// Load the asset registry module
|
|
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
|
|
|
|
TArray<FAssetData> AssetDataList;
|
|
AssetRegistryModule.Get().GetAssetsByClass(UAnimMontage::StaticClass()->GetClassPathName(), AssetDataList, true);
|
|
|
|
OutAssets.Empty(AssetDataList.Num());
|
|
|
|
for( int32 AssetIndex = 0; AssetIndex < AssetDataList.Num(); ++AssetIndex )
|
|
{
|
|
const FAssetData& PossibleAnimMontage = AssetDataList[AssetIndex];
|
|
if( SkeletonExportName == PossibleAnimMontage.GetTagValueRef<FString>("Skeleton") )
|
|
{
|
|
OutAssets.Add( PossibleAnimMontage );
|
|
}
|
|
}
|
|
}
|
|
|
|
UObject* SSkeletonSlotNames::ShowInDetailsView( UClass* EdClass )
|
|
{
|
|
UObject *Obj = EditorObjectTracker.GetEditorObjectForClass(EdClass);
|
|
|
|
if(Obj != NULL)
|
|
{
|
|
OnObjectSelected.ExecuteIfBound(Obj);
|
|
}
|
|
return Obj;
|
|
}
|
|
|
|
void SSkeletonSlotNames::ClearDetailsView()
|
|
{
|
|
OnObjectSelected.ExecuteIfBound(nullptr);
|
|
}
|
|
|
|
void SSkeletonSlotNames::PostUndoRedo()
|
|
{
|
|
RefreshSlotNameListWithFilter();
|
|
}
|
|
|
|
void SSkeletonSlotNames::AddReferencedObjects( FReferenceCollector& Collector )
|
|
{
|
|
EditorObjectTracker.AddReferencedObjects(Collector);
|
|
}
|
|
|
|
void SSkeletonSlotNames::NotifyUser( FNotificationInfo& NotificationInfo )
|
|
{
|
|
TSharedPtr<SNotificationItem> Notification = FSlateNotificationManager::Get().AddNotification( NotificationInfo );
|
|
if ( Notification.IsValid() )
|
|
{
|
|
Notification->SetCompletionState( SNotificationItem::CS_Fail );
|
|
}
|
|
}
|
|
|
|
void SSkeletonSlotNames::OnDeleteSlot(FName SlotName)
|
|
{
|
|
TArray<FAssetData> CompatibleMontages;
|
|
TMultiMap<UAnimBlueprint*, UAnimGraphNode_Slot*> CompatibleSlotNodes;
|
|
GetMontagesAndNodesUsingSlot(SlotName, CompatibleMontages, CompatibleSlotNodes);
|
|
|
|
if(CompatibleMontages.Num() > 0 || CompatibleSlotNodes.Num() > 0)
|
|
{
|
|
// We can't delete here - still have references. Give the user a chance to fix.
|
|
if(!ReferenceWindow.IsValid())
|
|
{
|
|
// No existing window
|
|
SAssignNew(ReferenceWindow, SWindow)
|
|
.AutoCenter(EAutoCenter::PreferredWorkArea)
|
|
.SizingRule(ESizingRule::Autosized)
|
|
.Title(LOCTEXT("ReferenceWindowTitle", "Slot References"));
|
|
|
|
ReferenceWindow->SetContent
|
|
(
|
|
SNew(SBorder)
|
|
.Padding(FMargin(3))
|
|
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
|
|
[
|
|
SAssignNew(ReferenceWidget, SSlotNameReferenceWindow)
|
|
.ReferencingMontages(&CompatibleMontages)
|
|
.ReferencingNodes(&CompatibleSlotNodes)
|
|
.SlotName(SlotName.ToString())
|
|
.OperationText(LOCTEXT("DeleteOperation", "Delete"))
|
|
.WidgetWindow(ReferenceWindow)
|
|
.OnRetry(FSimpleDelegate::CreateSP(this, &SSkeletonSlotNames::RetryDeleteSlot, SlotName))
|
|
]
|
|
);
|
|
|
|
TSharedPtr<SWindow> ParentWindow;
|
|
IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>(TEXT("MainFrame"));
|
|
ParentWindow = MainFrameModule.GetParentWindow();
|
|
|
|
FSlateApplication::Get().AddWindowAsNativeChild(ReferenceWindow.ToSharedRef(), ParentWindow.ToSharedRef());
|
|
ReferenceWindow->SetOnWindowClosed(FOnWindowClosed::CreateSP(this, &SSkeletonSlotNames::ReferenceWindowClosed));
|
|
}
|
|
else
|
|
{
|
|
TSharedPtr<SSlotNameReferenceWindow> RefWidgetPinned = ReferenceWidget.Pin();
|
|
if(RefWidgetPinned.IsValid())
|
|
{
|
|
FReferenceWindowInfo WindowInfo;
|
|
WindowInfo.ReferencingMontages = &CompatibleMontages;
|
|
WindowInfo.ReferencingNodes = &CompatibleSlotNodes;
|
|
WindowInfo.ItemText = FText::FromName(SlotName);
|
|
WindowInfo.OperationText = LOCTEXT("DeleteOperation", "Delete");
|
|
WindowInfo.RetryDelegate = FSimpleDelegate::CreateSP(this, &SSkeletonSlotNames::RetryDeleteSlot, SlotName);
|
|
|
|
RefWidgetPinned->UpdateInfo(WindowInfo);
|
|
ReferenceWindow->BringToFront();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DeleteSlot(SlotName);
|
|
}
|
|
}
|
|
|
|
void SSkeletonSlotNames::DeleteSlot(const FName& SlotName)
|
|
{
|
|
const USkeleton& Skeleton = EditableSkeleton->GetSkeleton();
|
|
if(Skeleton.ContainsSlotName(SlotName))
|
|
{
|
|
EditableSkeleton->DeleteSlotName(SlotName);
|
|
RefreshSlotNameListWithFilter();
|
|
}
|
|
}
|
|
|
|
void SSkeletonSlotNames::GetAnimMontagesUsingSlot(FName SlotName, TArray<FAssetData>& OutMontages)
|
|
{
|
|
TArray<FAssetData> SkeletonCompatibleMontages;
|
|
GetCompatibleAnimMontages(SkeletonCompatibleMontages);
|
|
|
|
for(FAssetData& MontageData : SkeletonCompatibleMontages)
|
|
{
|
|
UAnimMontage* Montage = Cast<UAnimMontage>(MontageData.GetAsset());
|
|
|
|
check(Montage);
|
|
|
|
for(FSlotAnimationTrack& SlotTrack : Montage->SlotAnimTracks)
|
|
{
|
|
if(SlotTrack.SlotName == SlotName)
|
|
{
|
|
OutMontages.Add(MontageData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SSkeletonSlotNames::GetAnimMontagesUsingSlotGroup(FName SlotGroupName, TArray<FAssetData>& OutMontages)
|
|
{
|
|
if(const FAnimSlotGroup* Group = EditableSkeleton->GetSkeleton().FindAnimSlotGroup(SlotGroupName))
|
|
{
|
|
for(const FName& SlotName : Group->SlotNames)
|
|
{
|
|
GetAnimMontagesUsingSlot(SlotName, OutMontages);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SSkeletonSlotNames::ReferenceWindowClosed(const TSharedRef<SWindow>& Window)
|
|
{
|
|
ReferenceWindow = nullptr;
|
|
}
|
|
|
|
void SSkeletonSlotNames::RetryDeleteSlot(FName SlotName)
|
|
{
|
|
TArray<FAssetData> CompatibleMontages;
|
|
TMultiMap<UAnimBlueprint*, UAnimGraphNode_Slot*> CompatibleSlotNodes;
|
|
GetMontagesAndNodesUsingSlot(SlotName, CompatibleMontages, CompatibleSlotNodes);
|
|
|
|
if(CompatibleMontages.Num() > 0 || CompatibleSlotNodes.Num() > 0)
|
|
{
|
|
// Still can't delete
|
|
TSharedPtr<SSlotNameReferenceWindow> PinnedWidget = ReferenceWidget.Pin();
|
|
if(PinnedWidget.IsValid())
|
|
{
|
|
FReferenceWindowInfo WindowInfo;
|
|
WindowInfo.ReferencingMontages = &CompatibleMontages;
|
|
WindowInfo.ReferencingNodes = &CompatibleSlotNodes;
|
|
WindowInfo.ItemText = FText::FromName(SlotName);
|
|
WindowInfo.OperationText = LOCTEXT("DeleteOperation", "Delete");
|
|
WindowInfo.RetryDelegate = FSimpleDelegate::CreateSP(this, &SSkeletonSlotNames::RetryDeleteSlot, SlotName);
|
|
|
|
PinnedWidget->UpdateInfo(WindowInfo);
|
|
ReferenceWindow->BringToFront();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ReferenceWindow->RequestDestroyWindow();
|
|
DeleteSlot(SlotName);
|
|
}
|
|
}
|
|
|
|
void SSkeletonSlotNames::GetMontagesAndNodesUsingSlot(const FName& SlotName, TArray<FAssetData>& CompatibleMontages, TMultiMap<UAnimBlueprint*, UAnimGraphNode_Slot*> &CompatibleSlotNodes)
|
|
{
|
|
FScopedSlowTask SlowTask(3, LOCTEXT("AssetReferenceSlowTaskMessage", "Checking for slot references."));
|
|
SlowTask.MakeDialog();
|
|
|
|
TArray<FAssetData> CompatibleBlueprints;
|
|
SlowTask.EnterProgressFrame(1, LOCTEXT("AssetReferenceTask_Blueprints", "Searching Blueprints"));
|
|
GetCompatibleAnimBlueprints(CompatibleBlueprints);
|
|
|
|
SlowTask.EnterProgressFrame(1, LOCTEXT("AssetReferenceTask_Montages", "Searching Montages"));
|
|
GetAnimMontagesUsingSlot(SlotName, CompatibleMontages);
|
|
|
|
SlowTask.EnterProgressFrame(1, LOCTEXT("AssetReferenceTask_Nodes", "Searching Nodes"));
|
|
for(FAssetData& Data : CompatibleBlueprints)
|
|
{
|
|
TArray<UEdGraph*> BPGraphs;
|
|
UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(Data.GetAsset());
|
|
|
|
AnimBP->GetAllGraphs(BPGraphs);
|
|
for(UEdGraph* Graph : BPGraphs)
|
|
{
|
|
TArray<UAnimGraphNode_Slot*> SlotNodes;
|
|
Graph->GetNodesOfClass(SlotNodes);
|
|
|
|
for(UAnimGraphNode_Slot* SlotNode : SlotNodes)
|
|
{
|
|
if(SlotNode->Node.SlotName == SlotName)
|
|
{
|
|
CompatibleSlotNodes.Add(AnimBP, SlotNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we end up loading in any previously unsaved assets they can add names to the list so refresh
|
|
RefreshSlotNameListWithFilter();
|
|
}
|
|
|
|
void SSkeletonSlotNames::GetMontagesAndNodesUsingSlotGroup(const FName& SlotGroupName, TArray<FAssetData>& OutMontages, TMultiMap<UAnimBlueprint*, UAnimGraphNode_Slot*> &OutBlueprintSlotMap)
|
|
{
|
|
if(const FAnimSlotGroup* SlotGroup = EditableSkeleton->GetSkeleton().FindAnimSlotGroup(SlotGroupName))
|
|
{
|
|
for(const auto& SlotName : SlotGroup->SlotNames)
|
|
{
|
|
GetMontagesAndNodesUsingSlot(SlotName, OutMontages, OutBlueprintSlotMap);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SSkeletonSlotNames::OnRenameSlotPopupCommitted(const FText & InNewSlotText, ETextCommit::Type CommitInfo, FName OldName)
|
|
{
|
|
if(CommitInfo == ETextCommit::OnEnter)
|
|
{
|
|
FName NewName(*InNewSlotText.ToString());
|
|
|
|
// Need to dismiss menus early or the slow task in GetMontagesAndNodesUsingSlot will fail to show onscreen
|
|
FSlateApplication::Get().DismissAllMenus();
|
|
|
|
// Make sure the name doesn't already exist
|
|
if(EditableSkeleton->GetSkeleton().ContainsSlotName(NewName))
|
|
{
|
|
FNotificationInfo Notification(FText::Format(LOCTEXT("ToastRenameFailDesc", "Rename Failed! Slot name {0} already exists in the target skeleton."), FText::FromName(NewName)));
|
|
Notification.ExpireDuration = 3.0f;
|
|
Notification.bFireAndForget = true;
|
|
|
|
NotifyUser(Notification);
|
|
|
|
return;
|
|
}
|
|
|
|
// Validate references
|
|
TArray<FAssetData> CompatibleMontages;
|
|
TMultiMap<UAnimBlueprint*, UAnimGraphNode_Slot*> CompatibleSlotNodes;
|
|
GetMontagesAndNodesUsingSlot(OldName, CompatibleMontages, CompatibleSlotNodes);
|
|
|
|
if(CompatibleMontages.Num() > 0 || CompatibleSlotNodes.Num() > 0)
|
|
{
|
|
// We can't rename here - still have references. Give the user a chance to fix.
|
|
if(!ReferenceWindow.IsValid())
|
|
{
|
|
// No existing window
|
|
SAssignNew(ReferenceWindow, SWindow)
|
|
.AutoCenter(EAutoCenter::PreferredWorkArea)
|
|
.SizingRule(ESizingRule::Autosized)
|
|
.Title(LOCTEXT("ReferenceWindowTitle", "Slot References"));
|
|
|
|
ReferenceWindow->SetContent
|
|
(
|
|
SNew(SBorder)
|
|
.Padding(FMargin(3))
|
|
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
|
|
[
|
|
SAssignNew(ReferenceWidget, SSlotNameReferenceWindow)
|
|
.ReferencingMontages(&CompatibleMontages)
|
|
.ReferencingNodes(&CompatibleSlotNodes)
|
|
.SlotName(OldName.ToString())
|
|
.OperationText(LOCTEXT("RenameOperation", "Rename"))
|
|
.WidgetWindow(ReferenceWindow)
|
|
.OnRetry(FSimpleDelegate::CreateSP(this, &SSkeletonSlotNames::RetryRenameSlot, OldName, NewName))
|
|
]
|
|
);
|
|
|
|
TSharedPtr<SWindow> ParentWindow;
|
|
IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>(TEXT("MainFrame"));
|
|
ParentWindow = MainFrameModule.GetParentWindow();
|
|
|
|
FSlateApplication::Get().AddWindowAsNativeChild(ReferenceWindow.ToSharedRef(), ParentWindow.ToSharedRef());
|
|
ReferenceWindow->SetOnWindowClosed(FOnWindowClosed::CreateSP(this, &SSkeletonSlotNames::ReferenceWindowClosed));
|
|
}
|
|
else
|
|
{
|
|
TSharedPtr<SSlotNameReferenceWindow> RefWidgetPinned = ReferenceWidget.Pin();
|
|
if(RefWidgetPinned.IsValid())
|
|
{
|
|
FReferenceWindowInfo WindowInfo;
|
|
WindowInfo.ReferencingMontages = &CompatibleMontages;
|
|
WindowInfo.ReferencingNodes = &CompatibleSlotNodes;
|
|
WindowInfo.ItemText = FText::FromName(OldName);
|
|
WindowInfo.OperationText = LOCTEXT("RenameOperation", "Rename");
|
|
WindowInfo.RetryDelegate = FSimpleDelegate::CreateSP(this, &SSkeletonSlotNames::RetryRenameSlot, OldName, NewName);
|
|
|
|
RefWidgetPinned->UpdateInfo(WindowInfo);
|
|
ReferenceWindow->BringToFront();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RenameSlot(OldName, NewName);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SSkeletonSlotNames::OnRenameSlot(FName CurrentName)
|
|
{
|
|
TSharedRef<STextEntryPopup> TextEntry =
|
|
SNew(STextEntryPopup)
|
|
.Label(LOCTEXT("RenameSlotName_AskSlotName", "New Slot Name"))
|
|
.OnTextCommitted(this, &SSkeletonSlotNames::OnRenameSlotPopupCommitted, CurrentName);
|
|
|
|
// Show dialog to enter new track name
|
|
FSlateApplication::Get().PushMenu(
|
|
SharedThis(this),
|
|
FWidgetPath(),
|
|
TextEntry,
|
|
FSlateApplication::Get().GetCursorPos(),
|
|
FPopupTransitionEffect(FPopupTransitionEffect::TypeInPopup)
|
|
);
|
|
}
|
|
|
|
void SSkeletonSlotNames::RenameSlot(FName CurrentName, FName NewName)
|
|
{
|
|
if(EditableSkeleton->GetSkeleton().ContainsSlotName(CurrentName))
|
|
{
|
|
EditableSkeleton->RenameSlotName(CurrentName, NewName);
|
|
RefreshSlotNameListWithFilter();
|
|
}
|
|
}
|
|
|
|
void SSkeletonSlotNames::RetryRenameSlot(FName CurrentName, FName NewName)
|
|
{
|
|
TArray<FAssetData> CompatibleMontages;
|
|
TMultiMap<UAnimBlueprint*, UAnimGraphNode_Slot*> CompatibleSlotNodes;
|
|
GetMontagesAndNodesUsingSlot(CurrentName, CompatibleMontages, CompatibleSlotNodes);
|
|
|
|
if(CompatibleMontages.Num() > 0 || CompatibleSlotNodes.Num() > 0)
|
|
{
|
|
// Still can't rename
|
|
TSharedPtr<SSlotNameReferenceWindow> PinnedWidget = ReferenceWidget.Pin();
|
|
if(PinnedWidget.IsValid())
|
|
{
|
|
FReferenceWindowInfo WindowInfo;
|
|
WindowInfo.ReferencingMontages = &CompatibleMontages;
|
|
WindowInfo.ReferencingNodes = &CompatibleSlotNodes;
|
|
WindowInfo.ItemText = FText::FromName(CurrentName);
|
|
WindowInfo.OperationText = LOCTEXT("DeleteOperation", "Delete");
|
|
WindowInfo.RetryDelegate = FSimpleDelegate::CreateSP(this, &SSkeletonSlotNames::RetryRenameSlot, CurrentName, NewName);
|
|
|
|
PinnedWidget->UpdateInfo(WindowInfo);
|
|
ReferenceWindow->BringToFront();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ReferenceWindow->RequestDestroyWindow();
|
|
RenameSlot(CurrentName, NewName);
|
|
}
|
|
}
|
|
|
|
void SSkeletonSlotNames::OnDeleteSlotGroup(FName GroupName)
|
|
{
|
|
TArray<FAssetData> CompatibleMontages;
|
|
TMultiMap<UAnimBlueprint*, UAnimGraphNode_Slot*> CompatibleSlotNodes;
|
|
GetMontagesAndNodesUsingSlotGroup(GroupName, CompatibleMontages, CompatibleSlotNodes);
|
|
|
|
if(CompatibleMontages.Num() > 0 || CompatibleSlotNodes.Num() > 0)
|
|
{
|
|
// Can't delete, still referenced
|
|
// We can't rename here - still have references. Give the user a chance to fix.
|
|
if(!ReferenceWindow.IsValid())
|
|
{
|
|
// No existing window
|
|
SAssignNew(ReferenceWindow, SWindow)
|
|
.AutoCenter(EAutoCenter::PreferredWorkArea)
|
|
.SizingRule(ESizingRule::Autosized)
|
|
.Title(LOCTEXT("ReferenceWindowTitle", "Slot References"));
|
|
|
|
ReferenceWindow->SetContent
|
|
(
|
|
SNew(SBorder)
|
|
.Padding(FMargin(3))
|
|
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
|
|
[
|
|
SAssignNew(ReferenceWidget, SSlotNameReferenceWindow)
|
|
.ReferencingMontages(&CompatibleMontages)
|
|
.ReferencingNodes(&CompatibleSlotNodes)
|
|
.SlotName(GroupName.ToString())
|
|
.OperationText(LOCTEXT("DeleteGroupOperation", "Delete Group"))
|
|
.WidgetWindow(ReferenceWindow)
|
|
.OnRetry(FSimpleDelegate::CreateSP(this, &SSkeletonSlotNames::RetryDeleteSlotGroup, GroupName))
|
|
]
|
|
);
|
|
|
|
TSharedPtr<SWindow> ParentWindow;
|
|
IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>(TEXT("MainFrame"));
|
|
ParentWindow = MainFrameModule.GetParentWindow();
|
|
|
|
FSlateApplication::Get().AddWindowAsNativeChild(ReferenceWindow.ToSharedRef(), ParentWindow.ToSharedRef());
|
|
ReferenceWindow->SetOnWindowClosed(FOnWindowClosed::CreateSP(this, &SSkeletonSlotNames::ReferenceWindowClosed));
|
|
}
|
|
else
|
|
{
|
|
TSharedPtr<SSlotNameReferenceWindow> RefWidgetPinned = ReferenceWidget.Pin();
|
|
if(RefWidgetPinned.IsValid())
|
|
{
|
|
FReferenceWindowInfo WindowInfo;
|
|
WindowInfo.ReferencingMontages = &CompatibleMontages;
|
|
WindowInfo.ReferencingNodes = &CompatibleSlotNodes;
|
|
WindowInfo.ItemText = FText::FromName(GroupName);
|
|
WindowInfo.OperationText = LOCTEXT("DeleteGroupOperation", "Delete Group");
|
|
WindowInfo.RetryDelegate = FSimpleDelegate::CreateSP(this, &SSkeletonSlotNames::RetryDeleteSlotGroup, GroupName);
|
|
|
|
RefWidgetPinned->UpdateInfo(WindowInfo);
|
|
ReferenceWindow->BringToFront();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DeleteSlotGroup(GroupName);
|
|
}
|
|
}
|
|
|
|
void SSkeletonSlotNames::RetryDeleteSlotGroup(FName GroupName)
|
|
{
|
|
TArray<FAssetData> CompatibleMontages;
|
|
TMultiMap<UAnimBlueprint*, UAnimGraphNode_Slot*> CompatibleSlotNodes;
|
|
GetMontagesAndNodesUsingSlotGroup(GroupName, CompatibleMontages, CompatibleSlotNodes);
|
|
|
|
if(CompatibleMontages.Num() > 0 || CompatibleSlotNodes.Num() > 0)
|
|
{
|
|
// Still can't rename
|
|
TSharedPtr<SSlotNameReferenceWindow> PinnedWidget = ReferenceWidget.Pin();
|
|
if(PinnedWidget.IsValid())
|
|
{
|
|
FReferenceWindowInfo WindowInfo;
|
|
WindowInfo.ReferencingMontages = &CompatibleMontages;
|
|
WindowInfo.ReferencingNodes = &CompatibleSlotNodes;
|
|
WindowInfo.ItemText = FText::FromName(GroupName);
|
|
WindowInfo.OperationText = LOCTEXT("DeleteGroupOperation", "Delete Group");
|
|
WindowInfo.RetryDelegate = FSimpleDelegate::CreateSP(this, &SSkeletonSlotNames::RetryDeleteSlotGroup, GroupName);
|
|
|
|
PinnedWidget->UpdateInfo(WindowInfo);
|
|
ReferenceWindow->BringToFront();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ReferenceWindow->RequestDestroyWindow();
|
|
DeleteSlotGroup(GroupName);
|
|
}
|
|
}
|
|
|
|
bool SSkeletonSlotNames::CanDeleteSlotGroup(FName GroupName)
|
|
{
|
|
static const FName DefaultGroupName("DefaultGroup");
|
|
return GroupName != DefaultGroupName;
|
|
}
|
|
|
|
void SSkeletonSlotNames::DeleteSlotGroup(const FName& GroupName)
|
|
{
|
|
if(EditableSkeleton->GetSkeleton().FindAnimSlotGroup(GroupName) != nullptr)
|
|
{
|
|
EditableSkeleton->DeleteSlotGroup(GroupName);
|
|
RefreshSlotNameListWithFilter();
|
|
}
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|