Files
UnrealEngineUWP/Engine/Source/Editor/DetailCustomizations/Private/DataTableCustomization.cpp
aurel cordonnier 8eebe8841f Merge UE5/RET @ 16305968 to UE5/Main
This represents UE4/Main @ 16261013 and Dev-PerfTest @ 16259937

[CL 16306996 by aurel cordonnier in ue5-main branch]
2021-05-12 18:10:03 -04:00

216 lines
6.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "DataTableCustomization.h"
#include "DataTableEditorUtils.h"
#include "Editor.h"
#include "Widgets/SWidget.h"
#include "Widgets/Text/STextBlock.h"
#include "PropertyCustomizationHelpers.h"
#include "Framework/Application/SlateApplication.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#define LOCTEXT_NAMESPACE "FDataTableCustomizationLayout"
void FDataTableCustomizationLayout::CustomizeHeader(TSharedRef<class IPropertyHandle> InStructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
this->StructPropertyHandle = InStructPropertyHandle;
if (StructPropertyHandle->HasMetaData(TEXT("RowType")))
{
const FString& RowType = StructPropertyHandle->GetMetaData(TEXT("RowType"));
RowTypeFilter = FName(*RowType);
RowFilterStruct = FindObject<UScriptStruct>(ANY_PACKAGE, *RowTypeFilter.ToString());
}
FSimpleDelegate OnDataTableChangedDelegate = FSimpleDelegate::CreateSP(this, &FDataTableCustomizationLayout::OnDataTableChanged);
StructPropertyHandle->SetOnPropertyValueChanged(OnDataTableChangedDelegate);
HeaderRow
.NameContent()
[
InStructPropertyHandle->CreatePropertyNameWidget(FText::GetEmpty(), FText::GetEmpty(), false)
];
FDataTableEditorUtils::AddSearchForReferencesContextMenu(HeaderRow, FExecuteAction::CreateSP(this, &FDataTableCustomizationLayout::OnSearchForReferences));
}
void FDataTableCustomizationLayout::CustomizeChildren(TSharedRef<class IPropertyHandle> InStructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
/** Get all the existing property handles */
DataTablePropertyHandle = InStructPropertyHandle->GetChildHandle("DataTable");
RowNamePropertyHandle = InStructPropertyHandle->GetChildHandle("RowName");
if (DataTablePropertyHandle->IsValidHandle() && RowNamePropertyHandle->IsValidHandle())
{
/** Setup Change callback */
FSimpleDelegate OnDataTableChangedDelegate = FSimpleDelegate::CreateSP(this, &FDataTableCustomizationLayout::OnDataTableChanged);
DataTablePropertyHandle->SetOnPropertyValueChanged(OnDataTableChangedDelegate);
/** Construct a asset picker widget with a custom filter */
StructBuilder.AddCustomRow(LOCTEXT("DataTable_TableName", "Data Table"))
.NameContent()
[
SNew(STextBlock)
.Text(LOCTEXT("DataTable_TableName", "Data Table"))
.Font(StructCustomizationUtils.GetRegularFont())
]
.ValueContent()
.MaxDesiredWidth(0.0f) // don't constrain the combo button width
[
SNew(SObjectPropertyEntryBox)
.PropertyHandle(DataTablePropertyHandle)
.AllowedClass(UDataTable::StaticClass())
.OnShouldFilterAsset(this, &FDataTableCustomizationLayout::ShouldFilterAsset)
];
FPropertyComboBoxArgs ComboArgs(RowNamePropertyHandle,
FOnGetPropertyComboBoxStrings::CreateSP(this, &FDataTableCustomizationLayout::OnGetRowStrings),
FOnGetPropertyComboBoxValue::CreateSP(this, &FDataTableCustomizationLayout::OnGetRowValueString));
ComboArgs.ShowSearchForItemCount = 1;
/** Construct a combo box widget to select from a list of valid options */
StructBuilder.AddCustomRow(LOCTEXT("DataTable_RowName", "Row Name"))
.NameContent()
[
SNew(STextBlock)
.Text(LOCTEXT("DataTable_RowName", "Row Name"))
.Font(StructCustomizationUtils.GetRegularFont())
]
.ValueContent()
.MaxDesiredWidth(0.0f) // don't constrain the combo button width
[
PropertyCustomizationHelpers::MakePropertyComboBox(ComboArgs)
];
}
}
bool FDataTableCustomizationLayout::GetCurrentValue(UDataTable*& OutDataTable, FName& OutName) const
{
if (RowNamePropertyHandle.IsValid() && RowNamePropertyHandle->IsValidHandle() && DataTablePropertyHandle.IsValid() && DataTablePropertyHandle->IsValidHandle())
{
// If either handle is multiple value or failure, fail
UObject* SourceDataTable = nullptr;
if (DataTablePropertyHandle->GetValue(SourceDataTable) == FPropertyAccess::Success)
{
OutDataTable = Cast<UDataTable>(SourceDataTable);
if (RowNamePropertyHandle->GetValue(OutName) == FPropertyAccess::Success)
{
return true;
}
}
}
return false;
}
void FDataTableCustomizationLayout::OnSearchForReferences()
{
UDataTable* DataTable;
FName RowName;
if (GetCurrentValue(DataTable, RowName) && DataTable)
{
TArray<FAssetIdentifier> AssetIdentifiers;
AssetIdentifiers.Add(FAssetIdentifier(DataTable, RowName));
FEditorDelegates::OnOpenReferenceViewer.Broadcast(AssetIdentifiers, FReferenceViewerParams());
}
}
FString FDataTableCustomizationLayout::OnGetRowValueString() const
{
if (!RowNamePropertyHandle.IsValid() || !RowNamePropertyHandle->IsValidHandle())
{
return FString();
}
FName RowNameValue;
const FPropertyAccess::Result RowResult = RowNamePropertyHandle->GetValue(RowNameValue);
if (RowResult == FPropertyAccess::Success)
{
if (RowNameValue.IsNone())
{
return LOCTEXT("DataTable_None", "None").ToString();
}
return RowNameValue.ToString();
}
else if (RowResult == FPropertyAccess::Fail)
{
return LOCTEXT("DataTable_None", "None").ToString();
}
else
{
return LOCTEXT("MultipleValues", "Multiple Values").ToString();
}
}
void FDataTableCustomizationLayout::OnGetRowStrings(TArray< TSharedPtr<FString> >& OutStrings, TArray<TSharedPtr<SToolTip>>& OutToolTips, TArray<bool>& OutRestrictedItems) const
{
UDataTable* DataTable = nullptr;
FName IgnoredRowName;
// Ignore return value as we will show rows if table is the same but row names are multiple values
GetCurrentValue(DataTable, IgnoredRowName);
TArray<FName> AllRowNames;
if (DataTable != nullptr)
{
for (TMap<FName, uint8*>::TConstIterator Iterator(DataTable->GetRowMap()); Iterator; ++Iterator)
{
AllRowNames.Add(Iterator.Key());
}
// Sort the names alphabetically.
AllRowNames.Sort(FNameLexicalLess());
}
for (const FName& RowName : AllRowNames)
{
OutStrings.Add(MakeShared<FString>(RowName.ToString()));
OutRestrictedItems.Add(false);
}
}
void FDataTableCustomizationLayout::OnDataTableChanged()
{
UDataTable* CurrentTable;
FName OldName;
// Clear name on table change if no longer valid
if (GetCurrentValue(CurrentTable, OldName))
{
if (!CurrentTable || !CurrentTable->FindRowUnchecked(OldName))
{
RowNamePropertyHandle->SetValue(FName());
}
}
}
bool FDataTableCustomizationLayout::ShouldFilterAsset(const struct FAssetData& AssetData)
{
if (!RowTypeFilter.IsNone())
{
static const FName RowStructureTagName("RowStructure");
FName RowStructure;
if (AssetData.GetTagValue<FName>(RowStructureTagName, RowStructure))
{
if (RowStructure == RowTypeFilter)
{
return false;
}
// This is slow, but at the moment we don't have an alternative to the short struct name search
UScriptStruct* RowStruct = FindObject<UScriptStruct>(ANY_PACKAGE, *RowStructure.ToString());
if (RowStruct && RowFilterStruct && RowStruct->IsChildOf(RowFilterStruct))
{
return false;
}
}
return true;
}
return false;
}
#undef LOCTEXT_NAMESPACE