// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "PerPlatformPropertyCustomization.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Engine/GameViewportClient.h" #include "Widgets/SBoxPanel.h" #include "Widgets/Layout/SSpacer.h" #include "Widgets/Text/STextBlock.h" #include "Widgets/Layout/SBox.h" #include "Widgets/Input/SComboBox.h" #include "Widgets/Images/SImage.h" #include "DetailWidgetRow.h" #include "Editor.h" #include "PropertyHandle.h" #include "DetailLayoutBuilder.h" #include "SPerPlatformPropertiesWidget.h" #include "ScopedTransaction.h" #include "IPropertyUtilities.h" #include "UObject/MetaData.h" #define LOCTEXT_NAMESPACE "PerPlatformPropertyCustomization" template void FPerPlatformPropertyCustomization::CustomizeHeader(TSharedRef StructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) { PropertyUtilities = StructCustomizationUtils.GetPropertyUtilities(); int32 PlatformNumber = PlatformInfo::GetAllPlatformGroupNames().Num(); HeaderRow.NameContent() [ StructPropertyHandle->CreatePropertyNameWidget() ] .ValueContent() .MinDesiredWidth(CalcDesiredWidth(StructPropertyHandle)) .MaxDesiredWidth((float)(PlatformNumber + 1)*125.0f) [ SNew(SPerPlatformPropertiesWidget) .OnGenerateWidget(this, &FPerPlatformPropertyCustomization::GetWidget, StructPropertyHandle) .OnAddPlatform(this, &FPerPlatformPropertyCustomization::AddPlatformOverride, StructPropertyHandle) .OnRemovePlatform(this, &FPerPlatformPropertyCustomization::RemovePlatformOverride, StructPropertyHandle) .PlatformOverrideNames(this, &FPerPlatformPropertyCustomization::GetPlatformOverrideNames, StructPropertyHandle) ]; } template TSharedRef FPerPlatformPropertyCustomization::GetWidget(FName PlatformGroupName, TSharedRef StructPropertyHandle) const { TSharedPtr EditProperty; if (PlatformGroupName == NAME_None) { EditProperty = StructPropertyHandle->GetChildHandle(FName("Default")); } else { TSharedPtr MapProperty = StructPropertyHandle->GetChildHandle(FName("PerPlatform")); if (MapProperty.IsValid()) { uint32 NumChildren = 0; MapProperty->GetNumChildren(NumChildren); for (uint32 ChildIdx = 0; ChildIdx < NumChildren; ChildIdx++) { TSharedPtr ChildProperty = MapProperty->GetChildHandle(ChildIdx); if (ChildProperty.IsValid()) { TSharedPtr KeyProperty = ChildProperty->GetKeyHandle(); if (KeyProperty.IsValid()) { FName KeyName; if(KeyProperty->GetValue(KeyName) == FPropertyAccess::Success && KeyName == PlatformGroupName) { EditProperty = ChildProperty; break; } } } } } } // Push down struct metadata to per-platform properties { // First get the source map const TMap* SourceMap = UMetaData::GetMapForObject(StructPropertyHandle->GetMetaDataProperty()); if (SourceMap) { // Iterate through source map, setting each key/value pair in the destination for (const auto& It : *SourceMap) { EditProperty->SetInstanceMetaData(*It.Key.ToString(), *It.Value); } } // Copy instance metadata as well const TMap* InstanceSourceMap = StructPropertyHandle->GetInstanceMetaDataMap(); for (const auto& It : *InstanceSourceMap) { EditProperty->SetInstanceMetaData(*It.Key.ToString(), *It.Value); } } if (EditProperty.IsValid()) { return EditProperty->CreatePropertyValueWidget(false); } else { return SNew(STextBlock) .Text(NSLOCTEXT("FPerPlatformPropertyCustomization", "GetWidget", "Could not find valid property")) .ColorAndOpacity(FLinearColor::Red); } } template float FPerPlatformPropertyCustomization::CalcDesiredWidth(TSharedRef StructPropertyHandle) { int32 NumOverrides = 0; TSharedPtr MapProperty = StructPropertyHandle->GetChildHandle(FName("PerPlatform")); if (MapProperty.IsValid()) { TArray RawData; MapProperty->AccessRawData(RawData); for (const void* Data : RawData) { const TMap* PerPlatformMap = (const TMap*)(Data); NumOverrides = FMath::Max(PerPlatformMap->Num(), NumOverrides); } } return (float)(1 + NumOverrides) * 125.f; } template bool FPerPlatformPropertyCustomization::AddPlatformOverride(FName PlatformGroupName, TSharedRef StructPropertyHandle) { FScopedTransaction Transaction(LOCTEXT("AddPlatformOverride", "Add Platform Override")); TSharedPtr PerPlatformProperty = StructPropertyHandle->GetChildHandle(FName("PerPlatform")); TSharedPtr DefaultProperty = StructPropertyHandle->GetChildHandle(FName("Default")); if (PerPlatformProperty.IsValid() && DefaultProperty.IsValid()) { TSharedPtr MapProperty = PerPlatformProperty->AsMap(); if (MapProperty.IsValid()) { MapProperty->AddItem(); uint32 NumChildren = 0; PerPlatformProperty->GetNumChildren(NumChildren); for (uint32 ChildIdx = 0; ChildIdx < NumChildren; ChildIdx++) { TSharedPtr ChildProperty = PerPlatformProperty->GetChildHandle(ChildIdx); if (ChildProperty.IsValid()) { TSharedPtr KeyProperty = ChildProperty->GetKeyHandle(); if (KeyProperty.IsValid()) { FName KeyName; if (KeyProperty->GetValue(KeyName) == FPropertyAccess::Success && KeyName == NAME_None) { // Set Key KeyProperty->SetValue(PlatformGroupName); // Set Value typename PerPlatformType::ValueType DefaultValue; DefaultProperty->GetValue(DefaultValue); ChildProperty->SetValue(DefaultValue); if(PropertyUtilities.IsValid()) { PropertyUtilities.Pin()->ForceRefresh(); } return true; } } } } } } return false; } template bool FPerPlatformPropertyCustomization::RemovePlatformOverride(FName PlatformGroupName, TSharedRef StructPropertyHandle) { FScopedTransaction Transaction(LOCTEXT("RemovePlatformOverride", "Remove Platform Override")); TSharedPtr MapProperty = StructPropertyHandle->GetChildHandle(FName("PerPlatform")); if (MapProperty.IsValid()) { TArray RawData; MapProperty->AccessRawData(RawData); for (const void* Data : RawData) { TMap* PerPlatformMap = (TMap*)(Data); check(PerPlatformMap); TArray KeyArray; PerPlatformMap->GenerateKeyArray(KeyArray); for (FName PlatformName : KeyArray) { if (PlatformName == PlatformGroupName) { PerPlatformMap->Remove(PlatformName); if (PropertyUtilities.IsValid()) { PropertyUtilities.Pin()->ForceRefresh(); } return true; } } } } return false; } template TArray FPerPlatformPropertyCustomization::GetPlatformOverrideNames(TSharedRef StructPropertyHandle) const { TArray PlatformOverrideNames; TSharedPtr MapProperty = StructPropertyHandle->GetChildHandle(FName("PerPlatform")); if (MapProperty.IsValid()) { TArray RawData; MapProperty->AccessRawData(RawData); for (const void* Data : RawData) { const TMap* PerPlatformMap = (const TMap*)(Data); check(PerPlatformMap); TArray KeyArray; PerPlatformMap->GenerateKeyArray(KeyArray); for (FName PlatformName : KeyArray) { PlatformOverrideNames.AddUnique(PlatformName); } } } return PlatformOverrideNames; } template TSharedRef FPerPlatformPropertyCustomization::MakeInstance() { return MakeShareable(new FPerPlatformPropertyCustomization); } /* Only explicitly instantiate the types which are supported *****************************************************************************/ template class FPerPlatformPropertyCustomization; template class FPerPlatformPropertyCustomization; template class FPerPlatformPropertyCustomization; #undef LOCTEXT_NAMESPACE