Files
UnrealEngine/Engine/Source/Editor/UnrealEd/Private/SSocketManager.cpp
2025-05-18 13:04:45 +08:00

1030 lines
28 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SSocketManager.h"
#include "Widgets/Layout/SSplitter.h"
#include "UObject/UnrealType.h"
#include "Engine/StaticMesh.h"
#include "Modules/ModuleManager.h"
#include "UObject/UObjectHash.h"
#include "UObject/UObjectIterator.h"
#include "Widgets/Layout/SSeparator.h"
#include "Developer/ToolWidgets/Public/SPositiveActionButton.h"
#include "Widgets/Views/SListView.h"
#include "Styling/AppStyle.h"
#include "Components/StaticMeshComponent.h"
#include "Editor/UnrealEdEngine.h"
#include "Engine/StaticMeshSocket.h"
#include "UnrealEdGlobals.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "IStaticMeshEditor.h"
#include "PropertyEditorModule.h"
#include "ScopedTransaction.h"
#include "Interfaces/IAnalyticsProvider.h"
#include "EngineAnalytics.h"
#include "Widgets/Text/SInlineEditableTextBlock.h"
#include "Framework/Commands/GenericCommands.h"
#define LOCTEXT_NAMESPACE "SSCSSocketManagerEditor"
struct SocketListItem
{
public:
SocketListItem(UStaticMeshSocket* InSocket)
: Socket(InSocket)
{
}
/** The static mesh socket this represents */
UStaticMeshSocket* Socket;
/** Delegate for when the context menu requests a rename */
DECLARE_DELEGATE(FOnRenameRequested);
FOnRenameRequested OnRenameRequested;
};
class SSocketDisplayItem : public SMultiColumnTableRow< TSharedPtr<SocketListItem> >
{
public:
SLATE_BEGIN_ARGS( SSocketDisplayItem ):
_ReadOnly(false)
{}
/** The socket this item displays. */
SLATE_ARGUMENT( TWeakPtr< SocketListItem >, SocketItem )
/** Pointer back to the socket manager */
SLATE_ARGUMENT( TWeakPtr< SSocketManager >, SocketManagerPtr )
/** Whether the widget should be editable or not */
SLATE_ARGUMENT( bool, ReadOnly)
SLATE_END_ARGS()
/**
* Construct the widget
*
* @param InArgs A declaration from which to construct the widget
*/
void Construct( const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView )
{
SocketItem = InArgs._SocketItem;
SocketManagerPtr = InArgs._SocketManagerPtr;
bReadOnly = InArgs._ReadOnly;
ImportedSocketBrush = FAppStyle::Get().GetBrush("Icons.Import");
NotImportedSocketBrush = FAppStyle::Get().GetBrush("NoBrush");
auto Args = FSuperRowType::FArguments();
// .Style(&FAppStyle::Get().GetWidgetStyle<FTableRowStyle>("SceneOutliner.TableViewRow"));
SMultiColumnTableRow< TSharedPtr<SocketListItem> >::Construct(Args, InOwnerTableView);
}
virtual TSharedRef<SWidget> GenerateWidgetForColumn( const FName& ColumnName ) override
{
// todo make these static id names
if( ColumnName == TEXT("Socket"))
{
TSharedPtr< SInlineEditableTextBlock > InlineWidget;
SAssignNew( InlineWidget, SInlineEditableTextBlock )
.IsReadOnly(bReadOnly)
.Text( this, &SSocketDisplayItem::GetSocketName )
.OnVerifyTextChanged( this, &SSocketDisplayItem::OnVerifySocketNameChanged )
.OnTextCommitted( this, &SSocketDisplayItem::OnCommitSocketName )
.IsSelected( this, &SMultiColumnTableRow< TSharedPtr<SocketListItem> >::IsSelectedExclusively );
TSharedPtr<SocketListItem> SocketItemPinned = SocketItem.Pin();
if (SocketItemPinned.IsValid())
{
SocketItemPinned->OnRenameRequested.BindSP(InlineWidget.Get(), &SInlineEditableTextBlock::EnterEditingMode);
}
return SNew(SBox)
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
.Padding(FMargin(20.f, 4.f))
[
InlineWidget.ToSharedRef()
];
}
else
{
return SNew(SBox)
.HAlign(HAlign_Right)
.VAlign(VAlign_Center)
[
SNew(SImage)
.ToolTipText(this, &SSocketDisplayItem::GetImportToolTip)
.ColorAndOpacity(FSlateColor::UseForeground())
.Image(this, &SSocketDisplayItem::GetImportBrush)
];
}
}
private:
/** Returns the socket name */
FText GetSocketName() const
{
TSharedPtr<SocketListItem> SocketItemPinned = SocketItem.Pin();
return SocketItemPinned.IsValid() ? FText::FromName(SocketItemPinned->Socket->SocketName) : FText();
}
const FSlateBrush* GetImportBrush() const
{
TSharedPtr<SocketListItem> SocketItemPinned = SocketItem.Pin();
return SocketItemPinned.IsValid() && SocketItemPinned->Socket->bSocketCreatedAtImport ? ImportedSocketBrush : NotImportedSocketBrush;
}
FText GetImportToolTip() const
{
TSharedPtr<SocketListItem> SocketItemPinned = SocketItem.Pin();
if (SocketItemPinned.IsValid() && SocketItemPinned->Socket->bSocketCreatedAtImport)
{
return LOCTEXT("ImportedSockedTooltip", "Socket was imported from the source mesh.");
}
return FText::GetEmpty();
}
bool OnVerifySocketNameChanged( const FText& InNewText, FText& OutErrorMessage )
{
bool bVerifyName = true;
FText NewText = FText::TrimPrecedingAndTrailing(InNewText);
if(NewText.IsEmpty())
{
OutErrorMessage = LOCTEXT( "EmptySocketName_Error", "Sockets must have a name!");
bVerifyName = false;
}
else
{
TSharedPtr<SocketListItem> SocketItemPinned = SocketItem.Pin();
TSharedPtr<SSocketManager> SocketManagerPinned = SocketManagerPtr.Pin();
if (SocketItemPinned.IsValid() && SocketItemPinned->Socket != nullptr && SocketItemPinned->Socket->SocketName.ToString() != NewText.ToString() &&
SocketManagerPinned.IsValid() && SocketManagerPinned->CheckForDuplicateSocket(NewText.ToString()))
{
OutErrorMessage = LOCTEXT("DuplicateSocket_Error", "Socket name in use!");
bVerifyName = false;
}
}
return bVerifyName;
}
void OnCommitSocketName( const FText& InText, ETextCommit::Type CommitInfo )
{
FText NewText = FText::TrimPrecedingAndTrailing(InText);
TSharedPtr<SocketListItem> PinnedSocketItem = SocketItem.Pin();
if (PinnedSocketItem.IsValid())
{
UStaticMeshSocket* SelectedSocket = PinnedSocketItem->Socket;
if (SelectedSocket != NULL)
{
FScopedTransaction Transaction( LOCTEXT("SetSocketName", "Set Socket Name") );
FProperty* ChangedProperty = FindFProperty<FProperty>( UStaticMeshSocket::StaticClass(), "SocketName" );
// Pre edit, calls modify on the object
SelectedSocket->PreEditChange(ChangedProperty);
// Edit the property itself
SelectedSocket->SocketName = FName(*NewText.ToString());
// Post edit
FPropertyChangedEvent PropertyChangedEvent( ChangedProperty );
SelectedSocket->PostEditChangeProperty(PropertyChangedEvent);
}
}
}
private:
/** The Socket to display. */
TWeakPtr< SocketListItem > SocketItem;
/** Pointer back to the socket manager */
TWeakPtr< SSocketManager > SocketManagerPtr;
const FSlateBrush* ImportedSocketBrush;
const FSlateBrush* NotImportedSocketBrush;
bool bReadOnly;
};
TSharedPtr<ISocketManager> ISocketManager::CreateSocketManager(TSharedPtr<class IStaticMeshEditor> InStaticMeshEditor, FSimpleDelegate InOnSocketSelectionChanged )
{
TSharedPtr<SSocketManager> SocketManager;
SAssignNew(SocketManager, SSocketManager)
.StaticMeshEditorPtr(InStaticMeshEditor)
.OnSocketSelectionChanged( InOnSocketSelectionChanged )
.ReadOnly(InStaticMeshEditor->GetOpenMethod() == EAssetOpenMethod::View);
TSharedPtr<ISocketManager> ISocket;
ISocket = StaticCastSharedPtr<ISocketManager>(SocketManager);
return ISocket;
}
void SSocketManager::Construct(const FArguments& InArgs)
{
StaticMeshEditorPtr = InArgs._StaticMeshEditorPtr;
OnSocketSelectionChanged = InArgs._OnSocketSelectionChanged;
bReadOnly = InArgs._ReadOnly;
TSharedPtr<IStaticMeshEditor> StaticMeshEditorPinned = StaticMeshEditorPtr.Pin();
if (!StaticMeshEditorPinned.IsValid())
{
return;
}
// Register a post undo function which keeps the socket manager list view consistent with the static mesh
StaticMeshEditorPinned->RegisterOnPostUndo(IStaticMeshEditor::FOnPostUndo::CreateSP(this, &SSocketManager::PostUndo));
StaticMesh = StaticMeshEditorPinned->GetStaticMesh();
FDetailsViewArgs Args;
Args.bHideSelectionTip = true;
Args.bLockable = false;
Args.bAllowSearch = false;
Args.bShowOptions = false;
Args.NotifyHook = this;
Args.NameAreaSettings = FDetailsViewArgs::HideNameArea;
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
SocketDetailsView = PropertyModule.CreateDetailView(Args);
SocketDetailsView->SetIsPropertyEditingEnabledDelegate(FIsPropertyEditingEnabled::CreateLambda([this]
{
if(TSharedPtr<IStaticMeshEditor> StaticMeshEditorPinned = StaticMeshEditorPtr.Pin())
{
return StaticMeshEditorPinned->GetOpenMethod() == EAssetOpenMethod::Edit;
}
return true;
}));
WorldSpaceRotation = FVector::ZeroVector;
this->ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.FillHeight(1.0f)
[
SNew(SSplitter)
.Orientation(Orient_Vertical)
+ SSplitter::Slot()
.Value(.3f)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
[
SNew(SBorder)
.BorderImage(FAppStyle::Get().GetBrush("Brushes.Panel"))
.HAlign(HAlign_Left)
.Padding(FMargin(12.f, 6.f))
[
SNew(STextBlock)
.TextStyle(FAppStyle::Get(), "ButtonText")
.Text(LOCTEXT("Sockets", "Sockets"))
.TransformPolicy(ETextTransformPolicy::ToUpper)
]
]
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
[
SNew(SPositiveActionButton)
.ToolTipText(LOCTEXT("CreateSocket", "Create Socket"))
.Icon(FAppStyle::Get().GetBrush("Icons.Plus"))
.OnClicked(this, &SSocketManager::CreateSocket_Execute)
.Visibility(this, &SSocketManager::CreateSocket_IsVisible )
]
]
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SSeparator)
]
+ SVerticalBox::Slot()
.FillHeight(1.0f)
[
SAssignNew(SocketListView, SListView<TSharedPtr< SocketListItem > >)
.SelectionMode(ESelectionMode::Multi)
.ListItemsSource(&SocketList)
// Generates the actual widget for a tree item
.OnGenerateRow(this, &SSocketManager::MakeWidgetFromOption)
// Find out when the user selects something in the tree
.OnSelectionChanged(this, &SSocketManager::SocketSelectionChanged_Execute)
.OnContextMenuOpening(this, &SSocketManager::OnContextMenuOpening)
.OnItemScrolledIntoView(this, &SSocketManager::OnItemScrolledIntoView)
.HeaderRow
(
SNew(SHeaderRow)
.Visibility(EVisibility::Collapsed)
+ SHeaderRow::Column(TEXT("Socket"))
.HAlignCell(HAlign_Fill)
.VAlignCell(VAlign_Center)
+ SHeaderRow::Column(TEXT("Imported"))
.HAlignCell(HAlign_Right)
.VAlignCell(VAlign_Center)
)
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(FMargin(12.f, 6.f))
[
SNew(STextBlock)
.Text(this, &SSocketManager::GetSocketHeaderText)
]
]
]
+ SSplitter::Slot()
.Value(.7f)
[
SNew(SOverlay)
+ SOverlay::Slot()
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.Visibility(this, &SSocketManager::GetSelectSocketMessageVisibility)
[
SNew(STextBlock)
.Text(LOCTEXT("NoSocketSelected", "Select a Socket"))
]
]
+ SOverlay::Slot()
[
SocketDetailsView.ToSharedRef()
]
]
]
];
RefreshSocketList();
AddPropertyChangeListenerToSockets();
}
SSocketManager::~SSocketManager()
{
TSharedPtr<IStaticMeshEditor> StaticMeshEditorPinned = StaticMeshEditorPtr.Pin();
if (StaticMeshEditorPinned.IsValid())
{
StaticMeshEditorPinned->UnregisterOnPostUndo(this);
}
RemovePropertyChangeListenerFromSockets();
}
UStaticMeshSocket* SSocketManager::GetSelectedSocket() const
{
if( SocketListView->GetSelectedItems().Num())
{
return SocketListView->GetSelectedItems()[0]->Socket;
}
return nullptr;
}
TArray<UStaticMeshSocket*> SSocketManager::GetSelectedSockets() const
{
TArray<UStaticMeshSocket*> Sockets;
Sockets.Reserve(SocketListView->GetNumItemsSelected());
for (TSharedPtr<SocketListItem>& Socket : SocketListView->GetSelectedItems())
{
Sockets.Add(Socket->Socket);
}
return Sockets;
}
bool SSocketManager::HasSelectedSockets() const
{
return SocketListView->GetNumItemsSelected() > 0;
}
EVisibility SSocketManager::GetSelectSocketMessageVisibility() const
{
return SocketListView->GetSelectedItems().Num() > 0 ? EVisibility::Hidden : EVisibility::Visible;
}
void SSocketManager::SetSelectedSocket(UStaticMeshSocket* InSelectedSocket)
{
if (InSelectedSocket)
{
for( int32 i=0; i < SocketList.Num(); i++)
{
if(SocketList[i]->Socket == InSelectedSocket)
{
SocketListView->SetSelection(SocketList[i]);
SocketListView->RequestListRefresh();
SocketSelectionChanged(InSelectedSocket);
break;
}
}
}
else
{
SocketListView->ClearSelection();
SocketListView->RequestListRefresh();
SocketSelectionChanged(NULL);
}
}
void SSocketManager::AddSelectedSocket(UStaticMeshSocket* InSelectedSocket)
{
TArray<UStaticMeshSocket*> SelectedSockets = GetSelectedSockets();
SelectedSockets.AddUnique(InSelectedSocket);
SetSelectedSockets(SelectedSockets);
}
void SSocketManager::RemoveSelectedSocket(const UStaticMeshSocket* InSelectedSocket)
{
TSharedPtr<SocketListItem> SocketItem = nullptr;
for (int32 i = 0; i < SocketListView->GetItems().Num(); ++i)
{
if (SocketListView->GetItems()[i]->Socket == InSelectedSocket)
{
SocketItem = SocketListView->GetItems()[i];
break;
}
}
if (SocketItem == nullptr)
{
return;
}
SocketListView->SetItemSelection(SocketItem, false);
SocketListView->RequestListRefresh();
SocketSelectionChanged(GetSelectedSockets());
}
TSharedRef< ITableRow > SSocketManager::MakeWidgetFromOption( TSharedPtr<SocketListItem> InItem, const TSharedRef< STableViewBase >& OwnerTable )
{
return SNew( SSocketDisplayItem, OwnerTable )
.SocketItem(InItem)
.SocketManagerPtr(SharedThis(this))
.ReadOnly(bReadOnly);
}
void SSocketManager::CreateSocket()
{
TSharedPtr<IStaticMeshEditor> StaticMeshEditorPinned = StaticMeshEditorPtr.Pin();
if (StaticMeshEditorPinned.IsValid())
{
UStaticMesh* CurrentStaticMesh = StaticMeshEditorPinned->GetStaticMesh();
const FScopedTransaction Transaction( LOCTEXT( "CreateSocket", "Create Socket" ) );
UStaticMeshSocket* NewSocket = NewObject<UStaticMeshSocket>(CurrentStaticMesh);
check(NewSocket);
if (FEngineAnalytics::IsAvailable())
{
FEngineAnalytics::GetProvider().RecordEvent(TEXT("Editor.Usage.StaticMesh.CreateSocket"));
}
FString SocketNameString = TEXT("Socket");
FName SocketName = FName(*SocketNameString);
// Make sure the new name is valid
int32 Index = 0;
while (CheckForDuplicateSocket(SocketName.ToString()))
{
SocketName = FName(*FString::Printf(TEXT("%s%i"), *SocketNameString, Index));
++Index;
}
NewSocket->SocketName = SocketName;
NewSocket->SetFlags( RF_Transactional );
NewSocket->OnPropertyChanged().AddSP( this, &SSocketManager::OnSocketPropertyChanged );
CurrentStaticMesh->PreEditChange(NULL);
CurrentStaticMesh->AddSocket(NewSocket);
CurrentStaticMesh->PostEditChange();
CurrentStaticMesh->MarkPackageDirty();
TSharedPtr< SocketListItem > SocketItem = MakeShareable( new SocketListItem(NewSocket) );
SocketList.Add( SocketItem );
SocketListView->RequestListRefresh();
SocketListView->SetSelection(SocketItem);
RequestRenameSelectedSocket();
}
}
void SSocketManager::DuplicateSelectedSocket()
{
TSharedPtr<IStaticMeshEditor> StaticMeshEditorPinned = StaticMeshEditorPtr.Pin();
UStaticMeshSocket* SelectedSocket = GetSelectedSocket();
if(StaticMeshEditorPinned.IsValid() && SelectedSocket)
{
const FScopedTransaction Transaction( LOCTEXT( "SocketManager_DuplicateSocket", "Duplicate Socket" ) );
UStaticMesh* CurrentStaticMesh = StaticMeshEditorPinned->GetStaticMesh();
UStaticMeshSocket* NewSocket = DuplicateObject(SelectedSocket, CurrentStaticMesh);
// Create a unique name for this socket
NewSocket->SocketName = MakeUniqueObjectName(CurrentStaticMesh, UStaticMeshSocket::StaticClass(), NewSocket->SocketName);
// Add the new socket to the static mesh
CurrentStaticMesh->PreEditChange(NULL);
CurrentStaticMesh->AddSocket(NewSocket);
CurrentStaticMesh->PostEditChange();
CurrentStaticMesh->MarkPackageDirty();
RefreshSocketList();
// Select the duplicated socket
SetSelectedSocket(NewSocket);
}
}
void SSocketManager::DuplicateSelectedSockets()
{
TSharedPtr<IStaticMeshEditor> StaticMeshEditorPinned = StaticMeshEditorPtr.Pin();
TArray<UStaticMeshSocket*> SelectedSockets = GetSelectedSockets();
if (!StaticMeshEditorPinned.IsValid() || SelectedSockets.Num() == 0)
{
return;
}
const FScopedTransaction Transaction(LOCTEXT("SocketManager_DuplicateSocket", "Duplicate Socket"));
UStaticMesh* CurrentStaticMesh = StaticMeshEditorPinned->GetStaticMesh();
CurrentStaticMesh->PreEditChange(NULL);
TArray<UStaticMeshSocket*> NewSockets;
NewSockets.Reserve(SelectedSockets.Num());
for (UStaticMeshSocket* SelectedSocket : SelectedSockets)
{
UStaticMeshSocket* NewSocket = DuplicateObject(SelectedSocket, CurrentStaticMesh);
NewSockets.Add(NewSocket);
// Create a unique name for this socket
NewSocket->SocketName = MakeUniqueObjectName(CurrentStaticMesh, UStaticMeshSocket::StaticClass(), NewSocket->SocketName);
// Add the new socket to the static mesh
CurrentStaticMesh->AddSocket(NewSocket);
}
CurrentStaticMesh->PostEditChange();
CurrentStaticMesh->MarkPackageDirty();
RefreshSocketList();
// Select the duplicated sockets
SetSelectedSockets(NewSockets);
}
void SSocketManager::UpdateStaticMesh()
{
RefreshSocketList();
}
void SSocketManager::SetSelectedSockets(TArray<UStaticMeshSocket*>& InSelectedSockets)
{
if (InSelectedSockets.Num())
{
TArray<TSharedPtr<SocketListItem>> SocketItems;
for (const UStaticMeshSocket* Socket : InSelectedSockets)
{
for (int32 i = 0; i < SocketListView->GetItems().Num(); ++i)
{
if (SocketListView->GetItems()[i]->Socket == Socket)
{
SocketItems.Add(SocketListView->GetItems()[i]);
}
}
}
SocketListView->ClearSelection();
SocketListView->SetItemSelection(SocketItems, true);
SocketListView->RequestListRefresh();
SocketSelectionChanged(InSelectedSockets);
}
else
{
SocketListView->ClearSelection();
SocketListView->RequestListRefresh();
SocketSelectionChanged(NULL);
}
}
void SSocketManager::RequestRenameSelectedSocket()
{
if(SocketListView->GetSelectedItems().Num() == 1)
{
TSharedPtr< SocketListItem > SocketItem = SocketListView->GetSelectedItems()[0];
SocketListView->RequestScrollIntoView(SocketItem);
DeferredRenameRequest = SocketItem;
}
}
void SSocketManager::DeleteSelectedSocket()
{
if(SocketListView->GetSelectedItems().Num())
{
const FScopedTransaction Transaction( LOCTEXT( "DeleteSocket", "Delete Socket" ) );
TSharedPtr<IStaticMeshEditor> StaticMeshEditorPinned = StaticMeshEditorPtr.Pin();
if (StaticMeshEditorPinned.IsValid())
{
UStaticMesh* CurrentStaticMesh = StaticMeshEditorPinned->GetStaticMesh();
CurrentStaticMesh->PreEditChange(NULL);
UStaticMeshSocket* SelectedSocket = SocketListView->GetSelectedItems()[ 0 ]->Socket;
SelectedSocket->OnPropertyChanged().RemoveAll( this );
CurrentStaticMesh->Sockets.Remove(SelectedSocket);
CurrentStaticMesh->PostEditChange();
RefreshSocketList();
}
}
}
void SSocketManager::DeleteSelectedSockets()
{
if(SocketListView->GetSelectedItems().Num())
{
const FScopedTransaction Transaction(LOCTEXT("DeleteSockets", "Delete Sockets"));
TSharedPtr<IStaticMeshEditor> StaticMeshEditorPinned = StaticMeshEditorPtr.Pin();
if (StaticMeshEditorPinned.IsValid())
{
UStaticMesh* CurrentStaticMesh = StaticMeshEditorPinned->GetStaticMesh();
CurrentStaticMesh->PreEditChange(NULL);
for (int32 i = 0; i < SocketListView->GetSelectedItems().Num(); ++i)
{
UStaticMeshSocket* SelectedSocket = SocketListView->GetSelectedItems()[i]->Socket;
SelectedSocket->OnPropertyChanged().RemoveAll(this);
CurrentStaticMesh->Sockets.Remove(SelectedSocket);
}
CurrentStaticMesh->PostEditChange();
RefreshSocketList();
}
}
}
void SSocketManager::RefreshSocketList()
{
// The static mesh might not be the same one we built the SocketListView with
// check it here and update it if necessary.
TSharedPtr<IStaticMeshEditor> StaticMeshEditorPinned = StaticMeshEditorPtr.Pin();
if (StaticMeshEditorPinned.IsValid())
{
bool bIsSameStaticMesh = true;
UStaticMesh* CurrentStaticMesh = StaticMeshEditorPinned->GetStaticMesh();
if (!StaticMesh.IsValid() || CurrentStaticMesh != StaticMesh.Get())
{
StaticMesh = CurrentStaticMesh;
bIsSameStaticMesh = false;
}
// Only rebuild the socket list if it differs from the static meshes socket list
// This is done so that an undo on a socket property doesn't cause the selected
// socket to be de-selected, thus hiding the socket properties on the detail view.
// NB: Also force a rebuild if the underlying StaticMesh has been changed.
bool bRefreshList = StaticMesh->Sockets.Num() != SocketList.Num() || !bIsSameStaticMesh;
if (!bRefreshList)
{
//Make sure list is in sync (UObject are the sames)
for (int32 SocketIndex = 0; SocketIndex < StaticMesh->Sockets.Num(); ++SocketIndex)
{
if (StaticMesh->Sockets[SocketIndex].Get() != SocketList[SocketIndex].Get()->Socket)
{
bRefreshList = true;
break;
}
}
}
if (bRefreshList)
{
SocketListView->ClearSelection();
SocketList.Empty();
for (int32 i = 0; i < StaticMesh->Sockets.Num(); i++)
{
UStaticMeshSocket* Socket = StaticMesh->Sockets[i];
SocketList.Add(MakeShareable(new SocketListItem(Socket)));
}
SocketListView->RequestListRefresh();
}
// Set the socket on the detail view to keep it in sync with the sockets properties
if (SocketListView->GetSelectedItems().Num())
{
TArray< UObject* > ObjectList;
ObjectList.Add(SocketListView->GetSelectedItems()[0]->Socket);
SocketDetailsView->SetObjects(ObjectList, true);
}
StaticMeshEditorPinned->RefreshViewport();
}
else
{
SocketList.Empty();
SocketListView->ClearSelection();
SocketListView->RequestListRefresh();
}
}
bool SSocketManager::CheckForDuplicateSocket(const FString& InSocketName)
{
for( int32 i=0; i < SocketList.Num(); i++)
{
if(SocketList[i]->Socket->SocketName.ToString() == InSocketName)
{
return true;
}
}
return false;
}
void SSocketManager::SocketSelectionChanged(UStaticMeshSocket* InSocket)
{
TArray<UStaticMeshSocket*> SelectedSockets = GetSelectedSockets();
TArray<UObject*> SelectedObjects;
SelectedObjects.Reserve(SelectedSockets.Num());
for (UStaticMeshSocket* Socket : SelectedSockets)
{
SelectedObjects.Add(Socket);
}
SocketDetailsView->SetObjects(SelectedObjects);
// Notify listeners
OnSocketSelectionChanged.ExecuteIfBound();
}
void SSocketManager::SocketSelectionChanged(TArray<UStaticMeshSocket*> InSockets)
{
TArray<UObject*> SelectedObjects;
SelectedObjects.Reserve(InSockets.Num());
for (UStaticMeshSocket* Socket : InSockets)
{
if (Socket)
{
SelectedObjects.Add(Socket);
}
}
SocketDetailsView->SetObjects(SelectedObjects);
// Notify listeners
OnSocketSelectionChanged.ExecuteIfBound();
}
void SSocketManager::SocketSelectionChanged_Execute(TSharedPtr<SocketListItem> InItem, ESelectInfo::Type /*SelectInfo*/)
{
if(InItem.IsValid())
{
SocketSelectionChanged(InItem->Socket);
}
else
{
SocketSelectionChanged(NULL);
}
}
FReply SSocketManager::CreateSocket_Execute()
{
CreateSocket();
return FReply::Handled();
}
EVisibility SSocketManager::CreateSocket_IsVisible() const
{
return bReadOnly ? EVisibility::Collapsed : EVisibility::Visible;
}
FText SSocketManager::GetSocketHeaderText() const
{
UStaticMesh* CurrentStaticMesh = nullptr;
TSharedPtr<IStaticMeshEditor> StaticMeshEditorPinned = StaticMeshEditorPtr.Pin();
if (StaticMeshEditorPinned.IsValid())
{
CurrentStaticMesh = StaticMeshEditorPinned->GetStaticMesh();
}
return FText::Format(LOCTEXT("SocketHeader_TotalFmt", "{0} sockets"), FText::AsNumber((CurrentStaticMesh != nullptr) ? CurrentStaticMesh->Sockets.Num() : 0));
}
void SSocketManager::SocketName_TextChanged(const FText& InText)
{
CheckForDuplicateSocket(InText.ToString());
}
TSharedPtr<SWidget> SSocketManager::OnContextMenuOpening()
{
const bool bShouldCloseWindowAfterMenuSelection = true;
TSharedPtr<IStaticMeshEditor> StaticMeshEditorPinned = StaticMeshEditorPtr.Pin();
if (!StaticMeshEditorPinned.IsValid())
{
return TSharedPtr<SWidget>();
}
FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, StaticMeshEditorPinned->GetToolkitCommands());
{
MenuBuilder.BeginSection("BasicOperations");
{
MenuBuilder.AddMenuEntry(FGenericCommands::Get().Delete);
MenuBuilder.AddMenuEntry(FGenericCommands::Get().Duplicate);
MenuBuilder.AddMenuEntry(FGenericCommands::Get().Rename);
}
MenuBuilder.EndSection();
}
return MenuBuilder.MakeWidget();
}
void SSocketManager::NotifyPostChange( const FPropertyChangedEvent& PropertyChangedEvent, FProperty* PropertyThatChanged )
{
TArray< TSharedPtr< SocketListItem > > SelectedList = SocketListView->GetSelectedItems();
if(SelectedList.Num())
{
if(PropertyThatChanged->GetName() == TEXT("Pitch") || PropertyThatChanged->GetName() == TEXT("Yaw") || PropertyThatChanged->GetName() == TEXT("Roll"))
{
const UStaticMeshSocket* Socket = SelectedList[0]->Socket;
WorldSpaceRotation.Set( Socket->RelativeRotation.Pitch, Socket->RelativeRotation.Yaw, Socket->RelativeRotation.Roll );
}
}
}
void SSocketManager::AddPropertyChangeListenerToSockets()
{
TSharedPtr<IStaticMeshEditor> StaticMeshEditorPinned = StaticMeshEditorPtr.Pin();
if (StaticMeshEditorPinned.IsValid())
{
UStaticMesh* CurrentStaticMesh = StaticMeshEditorPinned->GetStaticMesh();
for (int32 i = 0; i < CurrentStaticMesh->Sockets.Num(); ++i)
{
CurrentStaticMesh->Sockets[i]->OnPropertyChanged().AddSP(this, &SSocketManager::OnSocketPropertyChanged);
}
}
}
void SSocketManager::RemovePropertyChangeListenerFromSockets()
{
TSharedPtr<IStaticMeshEditor> StaticMeshEditorPinned = StaticMeshEditorPtr.Pin();
if (StaticMeshEditorPinned.IsValid())
{
UStaticMesh* CurrentStaticMesh = StaticMeshEditorPinned->GetStaticMesh();
if (CurrentStaticMesh)
{
for (int32 i = 0; i < CurrentStaticMesh->Sockets.Num(); ++i)
{
CurrentStaticMesh->Sockets[i]->OnPropertyChanged().RemoveAll(this);
}
}
}
}
void SSocketManager::OnSocketPropertyChanged( const UStaticMeshSocket* Socket, const FProperty* ChangedProperty )
{
static FName RelativeRotationName(TEXT("RelativeRotation"));
static FName RelativeLocationName(TEXT("RelativeLocation"));
check(Socket != nullptr);
FName ChangedPropertyName = ChangedProperty->GetFName();
if ( ChangedPropertyName == RelativeRotationName )
{
TArray<UStaticMeshSocket*> SelectedSockets = GetSelectedSockets();
for (const UStaticMeshSocket* SelectedSocket : SelectedSockets)
{
if (Socket == SelectedSocket)
{
WorldSpaceRotation.Set(Socket->RelativeRotation.Pitch, Socket->RelativeRotation.Yaw, Socket->RelativeRotation.Roll);
}
}
}
TSharedPtr<IStaticMeshEditor> StaticMeshEditorPinned = StaticMeshEditorPtr.Pin();
if (!StaticMeshEditorPinned.IsValid())
{
return;
}
if (ChangedPropertyName == RelativeRotationName || ChangedPropertyName == RelativeLocationName)
{
// If socket location or rotation is changed, update the position of any actors attached to it in instances of this mesh
UStaticMesh* CurrentStaticMesh = StaticMeshEditorPinned->GetStaticMesh();
if (CurrentStaticMesh != nullptr)
{
bool bUpdatedChild = false;
for (TObjectIterator<UStaticMeshComponent> It; It; ++It)
{
if (It->GetStaticMesh() == CurrentStaticMesh)
{
const AActor* Actor = It->GetOwner();
if (Actor != nullptr)
{
const USceneComponent* Root = Actor->GetRootComponent();
if (Root != nullptr)
{
for (USceneComponent* Child : Root->GetAttachChildren())
{
if (Child != nullptr && Child->GetAttachSocketName() == Socket->SocketName)
{
Child->UpdateComponentToWorld();
bUpdatedChild = true;
}
}
}
}
}
}
if (bUpdatedChild)
{
GUnrealEd->RedrawLevelEditingViewports();
}
}
}
}
void SSocketManager::PostUndo()
{
RefreshSocketList();
}
void SSocketManager::OnItemScrolledIntoView( TSharedPtr<SocketListItem> InItem, const TSharedPtr<ITableRow>& InWidget)
{
TSharedPtr<SocketListItem> DeferredRenameRequestPinned = DeferredRenameRequest.Pin();
if( DeferredRenameRequestPinned.IsValid() )
{
DeferredRenameRequestPinned->OnRenameRequested.ExecuteIfBound();
DeferredRenameRequest.Reset();
}
}
#undef LOCTEXT_NAMESPACE