Files
UnrealEngineUWP/Engine/Source/Editor/DetailCustomizations/Private/DialogueWaveWidgets.cpp
robert manuszewski d1443992e1 Deprecating ANY_PACKAGE.
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]
2022-06-01 03:46:59 -04:00

1039 lines
28 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "DialogueWaveWidgets.h"
#include "Widgets/Text/STextBlock.h"
#include "Editor.h"
#include "Modules/ModuleManager.h"
#include "SlateOptMacros.h"
#include "Widgets/Layout/SWrapBox.h"
#include "Widgets/Images/SImage.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Notifications/SErrorHint.h"
#include "Widgets/Input/SComboButton.h"
#include "Sound/DialogueVoice.h"
#include "DetailLayoutBuilder.h"
#include "SAssetDropTarget.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "Engine/Selection.h"
#define LOCTEXT_NAMESPACE "DialogueWaveDetails"
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SDialogueVoicePropertyEditor::Construct( const FArguments& InArgs, const TSharedRef<IPropertyHandle>& InPropertyHandle, const TSharedRef<FAssetThumbnailPool>& InAssetThumbnailPool )
{
DialogueVoicePropertyHandle = InPropertyHandle;
AssetThumbnailPool = InAssetThumbnailPool;
IsEditable = InArgs._IsEditable;
OnShouldFilterAsset = InArgs._OnShouldFilterAsset;
if( DialogueVoicePropertyHandle->IsValidHandle() )
{
const UDialogueVoice* DialogueVoice = NULL;
if( DialogueVoicePropertyHandle->IsValidHandle() )
{
UObject* Object = NULL;
DialogueVoicePropertyHandle->GetValue(Object);
DialogueVoice = Cast<UDialogueVoice>(Object);
}
const float ThumbnailSizeX = 64.0f;
const float ThumbnailSizeY = 64.0f;
AssetThumbnail = MakeShareable( new FAssetThumbnail( DialogueVoice, ThumbnailSizeX, ThumbnailSizeY, AssetThumbnailPool ) );
TSharedRef<SWidget> AssetWidget =
SNew( SAssetDropTarget )
.ToolTipText( this, &SDialogueVoicePropertyEditor::OnGetToolTip )
.OnAreAssetsAcceptableForDrop( this, &SDialogueVoicePropertyEditor::OnIsAssetAcceptableForDrop )
.OnAssetsDropped( this, &SDialogueVoicePropertyEditor::OnAssetDropped )
[
SNew( SBox )
.WidthOverride( ThumbnailSizeX )
.HeightOverride( ThumbnailSizeY )
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.Padding(0.0f)
[
AssetThumbnail->MakeThumbnailWidget()
]
];
if(IsEditable)
{
const TSharedRef<SWidget> UseButton = PropertyCustomizationHelpers::MakeUseSelectedButton( FSimpleDelegate::CreateSP( this, &SDialogueVoicePropertyEditor::OnUseSelectedDialogueVoice ) );
UseButton->SetEnabled( TAttribute<bool>::Create( TAttribute<bool>::FGetter::CreateSP( this, &SDialogueVoicePropertyEditor::CanUseSelectedAsset) ) );
const TSharedRef<SWidget> BrowseButton = PropertyCustomizationHelpers::MakeBrowseButton( FSimpleDelegate::CreateSP( this, &SDialogueVoicePropertyEditor::OnBrowseToDialogueVoice ) );
BrowseButton->SetEnabled( TAttribute<bool>::Create( TAttribute<bool>::FGetter::CreateSP( this, &SDialogueVoicePropertyEditor::CanBrowseToAsset) ) );
TSharedRef<SWidget> ButtonsColumnWidget =
SNew( SVerticalBox )
+ SVerticalBox::Slot()
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
.Padding(1.0f)
.AutoHeight()
[
UseButton
]
+ SVerticalBox::Slot()
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
.Padding(1.0f)
.AutoHeight()
[
BrowseButton
];
struct Local
{
static FVector2D GetDesiredSize(TSharedRef<SWidget> Widget)
{
return Widget->GetDesiredSize();
}
};
TSharedRef<SHorizontalBox> HorizontalBox = SNew( SHorizontalBox );
if(InArgs._ShouldCenterThumbnail)
{
HorizontalBox->AddSlot()
.HAlign(HAlign_Right)
.VAlign(VAlign_Fill)
.FillWidth(1.0f)
[
SNew( SSpacer )
.Size_Static( &Local::GetDesiredSize, ButtonsColumnWidget )
];
}
// Thumbnail
HorizontalBox->AddSlot()
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.AutoWidth()
[
SAssignNew( ComboButton, SComboButton )
.ToolTipText( this, &SDialogueVoicePropertyEditor::OnGetToolTip )
.ButtonStyle( FAppStyle::Get(), "PropertyEditor.AssetComboStyle" )
.ForegroundColor(FAppStyle::GetColor("PropertyEditor.AssetName.ColorAndOpacity"))
.OnGetMenuContent( this, &SDialogueVoicePropertyEditor::OnGetMenuContent )
.ContentPadding(2.0f)
.ButtonContent()
[
AssetWidget
]
];
// Path Property Buttons
if(InArgs._ShouldCenterThumbnail)
{
HorizontalBox->AddSlot()
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
.FillWidth(1.0f)
[
// Redundant Horizontal Box Slot exists to AutoWidth the contents here - avoids squishing nested button images.
SNew( SHorizontalBox )
+SHorizontalBox::Slot()
.Padding(0.0f) // Don't influence positioning - we're only here to correct sizing.
.AutoWidth()
[
ButtonsColumnWidget
]
];
}
else
{
HorizontalBox->AddSlot()
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
.AutoWidth()
[
ButtonsColumnWidget
];
}
ChildSlot
.VAlign(VAlign_Fill)
[
HorizontalBox
];
}
else
{
ChildSlot
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
AssetWidget
];
}
}
}
void SDialogueVoicePropertyEditor::Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime )
{
UDialogueVoice* CurrentDialogueVoice = NULL;
if( DialogueVoicePropertyHandle->IsValidHandle() )
{
UObject* Object = NULL;
DialogueVoicePropertyHandle->GetValue(Object);
CurrentDialogueVoice = Cast<UDialogueVoice>(Object);
}
if( AssetThumbnail->GetAsset() != CurrentDialogueVoice )
{
AssetThumbnail->SetAsset( CurrentDialogueVoice );
}
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
FText SDialogueVoicePropertyEditor::OnGetToolTip() const
{
FText ToolTipText;
const FAssetData& AssetData = AssetThumbnail->GetAssetData();
if( AssetData.IsValid() )
{
ToolTipText = FText::FromName(AssetData.PackageName);
}
return ToolTipText;
}
TSharedRef<SWidget> SDialogueVoicePropertyEditor::OnGetMenuContent()
{
TArray<const UClass*> AllowedClasses;
AllowedClasses.Add(UDialogueVoice::StaticClass());
UDialogueVoice* DialogueVoice = NULL;
{
UObject* Object = NULL;
DialogueVoicePropertyHandle->GetValue(Object);
DialogueVoice = Cast<UDialogueVoice>(Object);
}
return PropertyCustomizationHelpers::MakeAssetPickerWithMenu(
DialogueVoice,
false,
AllowedClasses,
PropertyCustomizationHelpers::GetNewAssetFactoriesForClasses(AllowedClasses),
OnShouldFilterAsset,
FOnAssetSelected::CreateSP(this, &SDialogueVoicePropertyEditor::OnAssetSelectedFromPicker),
FSimpleDelegate::CreateSP(this, &SDialogueVoicePropertyEditor::CloseMenu));
}
void SDialogueVoicePropertyEditor::CloseMenu()
{
ComboButton->SetIsOpen(false);
}
bool SDialogueVoicePropertyEditor::OnIsAssetAcceptableForDrop( TArrayView<FAssetData> InAssets ) const
{
// Only dialogue voice can be dropped
return Cast<UDialogueVoice>( InAssets[0].GetAsset() ) != nullptr;
}
void SDialogueVoicePropertyEditor::OnAssetDropped( const FDragDropEvent&, TArrayView<FAssetData> InAssets )
{
ReplaceDialogueVoice( CastChecked<UDialogueVoice>(InAssets[0].GetAsset()) );
}
FText SDialogueVoicePropertyEditor::GetDialogueVoiceDescription() const
{
UDialogueVoice* DialogueVoice = NULL;
{
UObject* Object = NULL;
DialogueVoicePropertyHandle->GetValue(Object);
DialogueVoice = Cast<UDialogueVoice>(Object);
}
return DialogueVoice ? FText::FromString(DialogueVoice->GetDesc()) : LOCTEXT("None", "None");
}
bool SDialogueVoicePropertyEditor::CanUseSelectedAsset()
{
bool Result = false;
// Load selected assets
FEditorDelegates::LoadSelectedAssetsIfNeeded.Broadcast();
// Get the first dialogue voice selected
USelection* DialogueVoiceSelection = GEditor->GetSelectedObjects();
if (DialogueVoiceSelection && DialogueVoiceSelection->Num() == 1)
{
UDialogueVoice* DialogueVoiceToAssign = DialogueVoiceSelection->GetTop<UDialogueVoice>();
if( DialogueVoiceToAssign && ( !OnShouldFilterAsset.IsBound() || !OnShouldFilterAsset.Execute(DialogueVoiceToAssign) ) )
{
Result = true;
}
}
return Result;
}
void SDialogueVoicePropertyEditor::OnUseSelectedDialogueVoice()
{
// Load selected assets
FEditorDelegates::LoadSelectedAssetsIfNeeded.Broadcast();
// Get the first dialogue voice selected
USelection* DialogueVoiceSelection = GEditor->GetSelectedObjects();
if (DialogueVoiceSelection && DialogueVoiceSelection->Num() == 1)
{
UDialogueVoice* DialogueVoiceToAssign = DialogueVoiceSelection->GetTop<UDialogueVoice>();
if( DialogueVoiceToAssign )
{
ReplaceDialogueVoice( DialogueVoiceToAssign );
}
}
}
void SDialogueVoicePropertyEditor::ReplaceDialogueVoice( const UDialogueVoice* const NewDialogueVoice )
{
if( !OnShouldFilterAsset.IsBound() || !OnShouldFilterAsset.Execute(NewDialogueVoice) )
{
const UDialogueVoice* PrevDialogueVoice = NULL;
const UDialogueVoice* DialogueVoice = NULL;
{
UObject* Object = NULL;
DialogueVoicePropertyHandle->GetValue(Object);
DialogueVoice = Cast<UDialogueVoice>(Object);
}
if( DialogueVoice )
{
PrevDialogueVoice = DialogueVoice;
}
if( NewDialogueVoice != PrevDialogueVoice )
{
// Replace the dialogue voice
DialogueVoicePropertyHandle->SetValue( NewDialogueVoice );
}
}
}
bool SDialogueVoicePropertyEditor::CanBrowseToAsset()
{
const UDialogueVoice* DialogueVoice = NULL;
{
UObject* Object = NULL;
DialogueVoicePropertyHandle->GetValue(Object);
DialogueVoice = Cast<UDialogueVoice>(Object);
}
return DialogueVoice != NULL;
}
void SDialogueVoicePropertyEditor::OnBrowseToDialogueVoice()
{
UDialogueVoice* DialogueVoice = NULL;
{
UObject* Object = NULL;
DialogueVoicePropertyHandle->GetValue(Object);
DialogueVoice = Cast<UDialogueVoice>(Object);
}
if( DialogueVoice )
{
// Find the item in the content browser
GoToAssetInContentBrowser(MakeWeakObjectPtr(DialogueVoice));
}
}
void SDialogueVoicePropertyEditor::OnAssetSelectedFromPicker( const FAssetData& InAssetData )
{
UDialogueVoice* NewDialogueVoice = Cast<UDialogueVoice>(InAssetData.GetAsset());
if( NewDialogueVoice )
{
ReplaceDialogueVoice( NewDialogueVoice );
}
}
/**
* Called to get the dialogue voice path that should be displayed
*/
FText SDialogueVoicePropertyEditor::GetDialogueVoicePath() const
{
const UDialogueVoice* EdittingDialogueVoice = NULL;
{
UObject* Object = NULL;
DialogueVoicePropertyHandle->GetValue(Object);
EdittingDialogueVoice = Cast<UDialogueVoice>(Object);
}
if( EdittingDialogueVoice )
{
return FText::FromString( EdittingDialogueVoice->GetOuter()->GetPathName() );
}
else
{
return FText::GetEmpty();
}
}
/**
* Called when the dialogue voice path is changed by a user
*/
void SDialogueVoicePropertyEditor::OnDialogueVoicePathChanged( const FText& NewText, ETextCommit::Type TextCommitType )
{
const FString& NewString = NewText.ToString();
if( !NewText.EqualTo( GetDialogueVoicePath() ) && NewString.Len() < NAME_SIZE )
{
const UDialogueVoice* PrevDialogueVoice = NULL;
const UDialogueVoice* DialogueVoice = NULL;
{
UObject* Object = NULL;
DialogueVoicePropertyHandle->GetValue(Object);
DialogueVoice = Cast<UDialogueVoice>(Object);
}
if( DialogueVoice )
{
PrevDialogueVoice = DialogueVoice;
}
const UDialogueVoice* DialogueVoiceToAssign = NULL;
if( !NewString.IsEmpty() )
{
if( NewString.Contains( TEXT(".") ) )
{
// Formatted text string, use the exact path instead of any package
DialogueVoiceToAssign = FindObject<UDialogueVoice>(nullptr, *NewString);
}
else
{
DialogueVoiceToAssign = FindFirstObject<UDialogueVoice>(*NewString, EFindFirstObjectOptions::NativeFirst | EFindFirstObjectOptions::EnsureIfAmbiguous);
}
if( !DialogueVoiceToAssign )
{
DialogueVoiceToAssign = Cast<UDialogueVoice>( StaticLoadObject( UDialogueVoice::StaticClass(), NULL, *NewString ) );
}
if( !DialogueVoiceToAssign )
{
// If we still don't have the dialogue voice attempt to find it via the asset registry
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>( "AssetRegistry" );
// Collect a full list of assets with the specified class
TArray<FAssetData> AssetData;
AssetRegistryModule.Get().GetAssetsByPackageName( FName(*NewString), AssetData );
if( AssetData.Num() > 0 )
{
// There should really only be one dialogue voice found
DialogueVoiceToAssign = Cast<UDialogueVoice>( AssetData[0].GetAsset() );
}
}
}
if( NewString.IsEmpty() || DialogueVoiceToAssign )
{
ReplaceDialogueVoice( DialogueVoiceToAssign );
}
}
}
void SDialogueVoicePropertyEditor::GoToAssetInContentBrowser( TWeakObjectPtr<UObject> Object )
{
if( Object.IsValid() )
{
TArray< UObject* > Objects;
Objects.Add( Object.Get() );
GEditor->SyncBrowserToObjects( Objects );
}
}
void STargetsSummaryWidget::Construct( const FArguments& InArgs, const TSharedRef<IPropertyHandle>& InPropertyHandle, const TSharedRef<FAssetThumbnailPool>& InAssetThumbnailPool )
{
TargetsPropertyHandle = InPropertyHandle;
AssetThumbnailPool = InAssetThumbnailPool;
IsEditable = InArgs._IsEditable;
WrapWidth = InArgs._WrapWidth;
AllottedWidth = 0.0f;
GenerateContent();
}
float STargetsSummaryWidget::GetPreferredWidthForWrapping() const
{
return WrapWidth.IsBound() ? WrapWidth.Get() : AllottedWidth;
}
void STargetsSummaryWidget::Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime )
{
AllottedWidth = AllottedGeometry.Size.X;
if( TargetsPropertyHandle->IsValidHandle() )
{
uint32 TargetCount;
TargetsPropertyHandle->GetNumChildren(TargetCount);
if( TargetCount != DisplayedTargets.Num() )
{
// The array sizes differ so we need to refresh the list
GenerateContent();
}
}
}
FText STargetsSummaryWidget::GetDialogueVoiceDescription() const
{
FText Result = LOCTEXT( "NoTargets", "No One" );
if( TargetsPropertyHandle->IsValidHandle() )
{
uint32 TargetCount;
TargetsPropertyHandle->GetNumChildren(TargetCount);
if( TargetCount > 1 )
{
Result = LOCTEXT("Multiple", "Multiple");
}
else if( TargetCount == 1 )
{
const TSharedPtr<IPropertyHandle> SingleTargetPropertyHandle = TargetsPropertyHandle->GetChildHandle(0);
UDialogueVoice* DialogueVoice = NULL;
if( SingleTargetPropertyHandle->IsValidHandle() )
{
UObject* Object = NULL;
SingleTargetPropertyHandle->GetValue(Object);
DialogueVoice = Cast<UDialogueVoice>(Object);
}
Result = DialogueVoice ? FText::FromString(DialogueVoice->GetDesc()) : LOCTEXT("None", "None");
}
}
return Result;
}
bool STargetsSummaryWidget::FilterTargets( const struct FAssetData& InAssetData )
{
bool ShouldAssetBeFilteredOut = false;
if( TargetsPropertyHandle->IsValidHandle() )
{
uint32 TargetCount;
TargetsPropertyHandle->GetNumChildren(TargetCount);
// Show tiles only.
for(uint32 i = 0; i < TargetCount; ++i)
{
const TSharedPtr<IPropertyHandle> TargetPropertyHandle = TargetsPropertyHandle->GetChildHandle(i);
const UDialogueVoice* DialogueVoice = NULL;
if( TargetPropertyHandle->IsValidHandle() )
{
UObject* Object = NULL;
TargetPropertyHandle->GetValue(Object);
DialogueVoice = Cast<UDialogueVoice>(Object);
}
if( DialogueVoice == InAssetData.GetAsset() )
{
ShouldAssetBeFilteredOut = true;
break;
}
}
}
return ShouldAssetBeFilteredOut;
}
class SRemovableDialogueVoicePropertyEditor : public SDialogueVoicePropertyEditor
{
public:
SRemovableDialogueVoicePropertyEditor()
: bIsPressed(false)
{
}
protected:
virtual FReply OnMouseButtonDown( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) override;
virtual FReply OnMouseButtonDoubleClick( const FGeometry& InMyGeometry, const FPointerEvent& InMouseEvent ) override;
virtual FReply OnMouseButtonUp( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) override;
private:
void DoRemove();
private:
bool bIsPressed;
};
FReply SRemovableDialogueVoicePropertyEditor::OnMouseButtonDown( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
FReply Reply = FReply::Unhandled();
if ( MouseEvent.GetEffectingButton() == EKeys::MiddleMouseButton )
{
bIsPressed = true;
//we need to capture the mouse for MouseUp events
Reply = FReply::Handled().CaptureMouse(AsShared()).SetUserFocus(AsShared(), EFocusCause::Mouse);
}
//return the constructed reply
return Reply;
}
FReply SRemovableDialogueVoicePropertyEditor::OnMouseButtonDoubleClick( const FGeometry& InMyGeometry, const FPointerEvent& InMouseEvent )
{
return OnMouseButtonDown( InMyGeometry, InMouseEvent );
}
FReply SRemovableDialogueVoicePropertyEditor::OnMouseButtonUp( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
FReply Reply = FReply::Unhandled();
if ( MouseEvent.GetEffectingButton() == EKeys::MiddleMouseButton )
{
bIsPressed = false;
const bool bIsUnderMouse = MyGeometry.IsUnderLocation(MouseEvent.GetScreenSpacePosition());
if( bIsUnderMouse )
{
// If we were asked to allow the button to be clicked on mouse up, regardless of whether the user
// pressed the button down first, then we'll allow the click to proceed without an active capture
if( HasMouseCapture() )
{
DoRemove();
Reply = FReply::Handled();
}
}
//If the user of the button didn't handle this click, then the button's
//default behavior handles it.
if(Reply.IsEventHandled() == false)
{
Reply = FReply::Handled();
}
//If the user hasn't requested a new mouse captor, then the default
//behavior of the button is to release mouse capture.
if(Reply.GetMouseCaptor().IsValid() == false)
{
Reply.ReleaseMouseCapture();
}
}
return Reply;
}
void SRemovableDialogueVoicePropertyEditor::DoRemove()
{
if( IsEditable )
{
const TSharedPtr<IPropertyHandle> ParentPropertyHandle = DialogueVoicePropertyHandle->GetParentHandle();
const TSharedPtr<IPropertyHandleArray> ParentPropertyArrayHandle = ParentPropertyHandle->AsArray();
ParentPropertyArrayHandle->DeleteItem( DialogueVoicePropertyHandle->GetIndexInArray() );
}
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void STargetsSummaryWidget::GenerateContent()
{
if( TargetsPropertyHandle->IsValidHandle() )
{
DisplayedTargets.Empty();
uint32 TargetCount;
TargetsPropertyHandle->GetNumChildren(TargetCount);
FSlateFontInfo Font = IDetailLayoutBuilder::GetDetailFont();
if( TargetCount > 1 )
{
const TSharedRef<SWrapBox> WrapBox =
SNew( SWrapBox )
.PreferredSize(this, &STargetsSummaryWidget::GetPreferredWidthForWrapping);
// Show tiles only.
for(uint32 i = 0; i < TargetCount; ++i)
{
const TSharedPtr<IPropertyHandle> TargetPropertyHandle = TargetsPropertyHandle->GetChildHandle(i);
const UDialogueVoice* DialogueVoice = NULL;
if( TargetPropertyHandle->IsValidHandle() )
{
UObject* Object = NULL;
TargetPropertyHandle->GetValue(Object);
DialogueVoice = Cast<UDialogueVoice>(Object);
}
DisplayedTargets.Add(DialogueVoice);
WrapBox->AddSlot()
.Padding(2.0f)
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
[
SNew( SRemovableDialogueVoicePropertyEditor, TargetPropertyHandle.ToSharedRef(), AssetThumbnailPool.ToSharedRef() )
.IsEditable( IsEditable )
.OnShouldFilterAsset( this, &STargetsSummaryWidget::FilterTargets)
];
}
ChildSlot
.HAlign(HAlign_Center)
[
WrapBox
];
}
else if( TargetCount == 1 )
{
const TSharedPtr<IPropertyHandle> SingleTargetPropertyHandle = TargetsPropertyHandle->GetChildHandle(0);
const UDialogueVoice* DialogueVoice = NULL;
if( SingleTargetPropertyHandle->IsValidHandle() )
{
UObject* Object = NULL;
SingleTargetPropertyHandle->GetValue(Object);
DialogueVoice = Cast<UDialogueVoice>(Object);
}
DisplayedTargets.Add(DialogueVoice);
TSharedRef<SWidget> TargetPropertyEditor =
SNew( SRemovableDialogueVoicePropertyEditor, SingleTargetPropertyHandle.ToSharedRef(), AssetThumbnailPool.ToSharedRef() )
.IsEditable( IsEditable )
.OnShouldFilterAsset( this, &STargetsSummaryWidget::FilterTargets)
.ShouldCenterThumbnail(true);
ChildSlot
.HAlign(HAlign_Center)
[
SNew( SBox )
.Padding(2.0f)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
TargetPropertyEditor
]
];
}
else
{
const float ThumbnailSizeX = 64.0f;
const float ThumbnailSizeY = 64.0f;
ChildSlot
.HAlign(HAlign_Center)
[
SNew( SBox )
.Padding(2.0f)
.WidthOverride( ThumbnailSizeX )
.HeightOverride( ThumbnailSizeY )
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
];
}
}
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SDialogueContextHeaderWidget::Construct( const FArguments& InArgs, const TSharedRef<IPropertyHandle>& InPropertyHandle, const TSharedRef<FAssetThumbnailPool>& InAssetThumbnailPool )
{
ContextPropertyHandle = InPropertyHandle;
if( ContextPropertyHandle->IsValidHandle() )
{
FSlateFontInfo Font = IDetailLayoutBuilder::GetDetailFont();
const TSharedPtr<IPropertyHandle> SpeakerPropertyHandle = ContextPropertyHandle->GetChildHandle("Speaker");
const TSharedPtr<IPropertyHandle> TargetsPropertyHandle = ContextPropertyHandle->GetChildHandle("Targets");
TSharedRef<SWidget> AddButton = PropertyCustomizationHelpers::MakeAddButton( FSimpleDelegate::CreateSP( this, &SDialogueContextHeaderWidget::AddTargetButton_OnClick ) );
TSharedRef<SWidget> RemoveButton = PropertyCustomizationHelpers::MakeRemoveButton( FSimpleDelegate::CreateSP( this, &SDialogueContextHeaderWidget::RemoveTargetButton_OnClick ) );
TSharedRef<SWidget> EmptyButton = PropertyCustomizationHelpers::MakeEmptyButton( FSimpleDelegate::CreateSP( this, &SDialogueContextHeaderWidget::EmptyTargetsButton_OnClick ) );
TSharedRef<SDialogueVoicePropertyEditor> SpeakerPropertyEditor =
SNew( SDialogueVoicePropertyEditor, SpeakerPropertyHandle.ToSharedRef(), InAssetThumbnailPool )
.IsEditable(true)
.ShouldCenterThumbnail(true);
TSharedRef<STargetsSummaryWidget> TargetsSummaryWidget =
SNew( STargetsSummaryWidget, TargetsPropertyHandle.ToSharedRef(), InAssetThumbnailPool );
ChildSlot
[
SNew( SHorizontalBox )
+SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew( SVerticalBox )
+SVerticalBox::Slot()
.AutoHeight()
[
SNew( SHorizontalBox )
+SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.VAlign(VAlign_Center)
.FillWidth(1.0f)
[
SAssignNew( SpeakerErrorHint, SErrorHint )
]
+SHorizontalBox::Slot()
.AutoWidth()
[
SpeakerPropertyHandle->CreatePropertyNameWidget()
]
+SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNullWidget::NullWidget
]
]
+SVerticalBox::Slot()
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.FillHeight(1.0f)
[
SNew( SVerticalBox )
+SVerticalBox::Slot()
.Padding(2.0f)
.HAlign(HAlign_Fill)
.AutoHeight()
[
SpeakerPropertyEditor
]
+SVerticalBox::Slot()
.Padding( 2.0f )
.HAlign(HAlign_Center)
.AutoHeight()
[
// Voice Description
SNew( STextBlock )
.Font( Font )
.Text( SpeakerPropertyEditor, &SDialogueVoicePropertyEditor::GetDialogueVoiceDescription )
]
]
]
+SHorizontalBox::Slot()
.VAlign(VAlign_Center)
.AutoWidth()
[
SNew( SImage )
.Image( FAppStyle::GetBrush("DialogueWaveDetails.SpeakerToTarget") )
.ColorAndOpacity( FSlateColor::UseForeground() )
]
+SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew( SVerticalBox )
+SVerticalBox::Slot()
.AutoHeight()
[
SNew( SHorizontalBox )
+SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.VAlign(VAlign_Center)
.FillWidth(1.0f)
[
SAssignNew( TargetsErrorHint, SErrorHint )
]
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew( SHorizontalBox )
+SHorizontalBox::Slot()
.AutoWidth()
[
TargetsPropertyHandle->CreatePropertyNameWidget( LOCTEXT("DirectedAt", "Directed At") )
]
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew( SHorizontalBox )
+SHorizontalBox::Slot()
.Padding(1.0f)
.VAlign(VAlign_Center)
.AutoWidth()
[
AddButton
]
+SHorizontalBox::Slot()
.Padding(1.0f)
.VAlign(VAlign_Center)
.AutoWidth()
[
RemoveButton
]
+SHorizontalBox::Slot()
.Padding(1.0f)
.VAlign(VAlign_Center)
.AutoWidth()
[
EmptyButton
]
]
]
+SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNullWidget::NullWidget
]
]
+SVerticalBox::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Center)
.FillHeight(1.0f)
[
SNew( SVerticalBox )
+SVerticalBox::Slot()
.Padding(2.0f)
.HAlign(HAlign_Fill)
.AutoHeight()
[
TargetsSummaryWidget
]
+SVerticalBox::Slot()
.Padding( 2.0f )
.HAlign(HAlign_Center)
.AutoHeight()
[
// Voice Description
SNew( STextBlock )
.Font( Font )
.Text( TargetsSummaryWidget, &STargetsSummaryWidget::GetDialogueVoiceDescription )
]
]
]
];
}
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SDialogueContextHeaderWidget::Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime )
{
if( !IsSpeakerValid() )
{
if( SpeakerErrorHint.IsValid() ) { SpeakerErrorHint->SetError( LOCTEXT("NullSpeakerError", "Speaker can not be \"None\".") ); }
}
else
{
if( SpeakerErrorHint.IsValid() ) { SpeakerErrorHint->SetError( FText::GetEmpty() ); }
}
if( !IsTargetSetValid() )
{
if( TargetsErrorHint.IsValid() ) { TargetsErrorHint->SetError( LOCTEXT("NullTargetError", "Target set can not contain \"None\".") ); }
}
else
{
if( TargetsErrorHint.IsValid() ) { TargetsErrorHint->SetError( FText::GetEmpty() ); }
}
}
bool SDialogueContextHeaderWidget::IsSpeakerValid() const
{
bool Result = false;
if( ContextPropertyHandle.IsValid() && ContextPropertyHandle->IsValidHandle() )
{
const TSharedPtr<IPropertyHandle> SpeakerPropertyHandle = ContextPropertyHandle->GetChildHandle("Speaker");
const UDialogueVoice* Speaker = nullptr;
if( SpeakerPropertyHandle.IsValid() && SpeakerPropertyHandle->IsValidHandle() )
{
UObject* Object = nullptr;
SpeakerPropertyHandle->GetValue(Object);
Speaker = Cast<UDialogueVoice>(Object);
}
Result = ( Speaker != nullptr );
}
return Result;
}
bool SDialogueContextHeaderWidget::IsTargetSetValid() const
{
bool Result = false;
if( ContextPropertyHandle.IsValid() && ContextPropertyHandle->IsValidHandle() )
{
Result = true;
const TSharedPtr<IPropertyHandle> TargetsPropertyHandle = ContextPropertyHandle->GetChildHandle("Targets");
const TSharedPtr<IPropertyHandleArray> TargetsArrayPropertyHandle = TargetsPropertyHandle->AsArray();
uint32 TargetCount;
TargetsArrayPropertyHandle->GetNumElements(TargetCount);
for(uint32 i = 0; i < TargetCount; ++i)
{
TSharedPtr<IPropertyHandle> TargetPropertyHandle = TargetsArrayPropertyHandle->GetElement(i);
const UDialogueVoice* Target = nullptr;
if( TargetPropertyHandle.IsValid() && TargetPropertyHandle->IsValidHandle() )
{
UObject* Object = nullptr;
TargetPropertyHandle->GetValue(Object);
Target = Cast<UDialogueVoice>(Object);
}
if ( Target == nullptr )
{
Result = false;
break;
}
}
}
return Result;
}
void SDialogueContextHeaderWidget::AddTargetButton_OnClick()
{
if( ContextPropertyHandle->IsValidHandle() )
{
const TSharedPtr<IPropertyHandle> TargetsPropertyHandle = ContextPropertyHandle->GetChildHandle("Targets");
const TSharedPtr<IPropertyHandleArray> TargetsArrayPropertyHandle = TargetsPropertyHandle->AsArray();
TargetsArrayPropertyHandle->AddItem();
}
}
void SDialogueContextHeaderWidget::RemoveTargetButton_OnClick()
{
if( ContextPropertyHandle->IsValidHandle() )
{
const TSharedPtr<IPropertyHandle> TargetsPropertyHandle = ContextPropertyHandle->GetChildHandle("Targets");
const TSharedPtr<IPropertyHandleArray> TargetsArrayPropertyHandle = TargetsPropertyHandle->AsArray();
uint32 TargetCount;
TargetsArrayPropertyHandle->GetNumElements(TargetCount);
if( TargetCount > 0 )
{
TargetsArrayPropertyHandle->DeleteItem( TargetCount - 1 );
}
}
}
void SDialogueContextHeaderWidget::EmptyTargetsButton_OnClick()
{
if( ContextPropertyHandle->IsValidHandle() )
{
const TSharedPtr<IPropertyHandle> TargetsPropertyHandle = ContextPropertyHandle->GetChildHandle("Targets");
const TSharedPtr<IPropertyHandleArray> TargetsArrayPropertyHandle = TargetsPropertyHandle->AsArray();
TargetsArrayPropertyHandle->EmptyArray();
}
}
#undef LOCTEXT_NAMESPACE