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

166 lines
6.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "InstancedStaticMeshComponentDetails.h"
#include "Components/InstancedStaticMeshComponent.h"
#include "Containers/Array.h"
#include "DetailCategoryBuilder.h"
#include "DetailLayoutBuilder.h"
#include "DetailWidgetRow.h"
#include "Fonts/SlateFontInfo.h"
#include "HAL/IConsoleManager.h"
#include "HAL/PlatformCrt.h"
#include "Input/Reply.h"
#include "Internationalization/Internationalization.h"
#include "Misc/AssertionMacros.h"
#include "Misc/Attribute.h"
#include "PropertyHandle.h"
#include "SlotBase.h"
#include "Styling/CoreStyle.h"
#include "Styling/ISlateStyle.h"
#include "Templates/Casts.h"
#include "Types/SlateEnums.h"
#include "UObject/NameTypes.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/Images/SImage.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/Text/STextBlock.h"
class UObject;
#define LOCTEXT_NAMESPACE "InstancedStaticMeshComponentDetails"
TAutoConsoleVariable<int32> CVarMaxNumInstancesDetails(
TEXT("r.Editor.MaxNumInstancesDetails"),
512,
TEXT("Maximum number of instances shown in the details panel. Above this value, instances are hidden by default. \n")
TEXT("< 0 : No maximum\n"),
ECVF_RenderThreadSafe);
TSharedRef<IDetailCustomization> FInstancedStaticMeshComponentDetails::MakeInstance()
{
return MakeShareable(new FInstancedStaticMeshComponentDetails);
}
void FInstancedStaticMeshComponentDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
TArray<TWeakObjectPtr<UObject>> ObjectsBeingCustomized;
DetailBuilder.GetObjectsBeingCustomized(ObjectsBeingCustomized);
TSharedPtr<IPropertyHandle> PerInstanceSMDataProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UInstancedStaticMeshComponent, PerInstanceSMData));
TSharedPtr<IPropertyHandle> PerInstanceSMCustomDataProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UInstancedStaticMeshComponent, PerInstanceSMCustomData));
// Doesn't support multi-selection :
if (ObjectsBeingCustomized.Num() != 1)
{
PerInstanceSMDataProperty->MarkHiddenByCustomization();
PerInstanceSMCustomDataProperty->MarkHiddenByCustomization();
}
else
{
ComponentBeingCustomized = Cast<UInstancedStaticMeshComponent>(ObjectsBeingCustomized[0]);
if (ensure(ComponentBeingCustomized.IsValid()))
{
const FName InstancesCategoryName("Instances");
IDetailCategoryBuilder& InstancesCategory = DetailBuilder.EditCategory(InstancesCategoryName);
NumInstances = ComponentBeingCustomized->PerInstanceSMData.Num();
bForceShowAllInstances = ComponentBeingCustomized->bForceShowAllInstancesDetails;
int32 MaxNumInstances = CVarMaxNumInstancesDetails.GetValueOnGameThread();
bool bTooManyInstances = (MaxNumInstances >= 0) && (NumInstances > MaxNumInstances);
// If there are too many instances to display, hide the property as it will slow down the UI :
if (bTooManyInstances && !bForceShowAllInstances)
{
PerInstanceSMDataProperty->MarkHiddenByCustomization();
PerInstanceSMCustomDataProperty->MarkHiddenByCustomization();
}
// If there are too many instances to display, display an additional button to show/hide instances :
if (bTooManyInstances)
{
const FText ShowHideAllInstancesText = LOCTEXT("ShowHideAllInstancesText", "Show/Hide All Instances");
const FText SlatePerformanceWarningText = LOCTEXT("SlatePerformanceWarningText", "Slate Performance Warning");
const FText TooltipText = LOCTEXT("ShowHideAllInstancesTooltip", "Displays per-instance data in the details. Can be very slow when there are lots of instances.");
FDetailWidgetRow& CustomRow = InstancesCategory.AddCustomRow(ShowHideAllInstancesText)
.NameContent()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.VAlign(VAlign_Center)
.HAlign(HAlign_Left)
.AutoWidth()
.Padding(0.0f, 0.0f, 4.0f, 0.0f)
[
SNew(SImage)
.Image(FCoreStyle::Get().GetBrush("Icons.Warning"))
.ToolTipText(TooltipText)
]
+ SHorizontalBox::Slot()
.VAlign(VAlign_Center)
.HAlign(HAlign_Left)
.AutoWidth()
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFontItalic())
.Text(SlatePerformanceWarningText)
.ToolTipText(TooltipText)
]
]
.ValueContent()
.MaxDesiredWidth(120.f)
[
SNew(SButton)
.OnClicked(this, &FInstancedStaticMeshComponentDetails::OnShowHideAllInstancesClicked)
.ToolTipText(TooltipText)
.IsEnabled(this, &FInstancedStaticMeshComponentDetails::IsShowHideAllInstancesEnabled)
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFontItalic())
.Text(this, &FInstancedStaticMeshComponentDetails::GetShowHideAllInstancesText)
]
];
}
}
}
}
void FInstancedStaticMeshComponentDetails::CustomizeDetails(const TSharedPtr<IDetailLayoutBuilder>& DetailBuilder)
{
CachedDetailBuilder = DetailBuilder;
CustomizeDetails(*DetailBuilder);
}
FReply FInstancedStaticMeshComponentDetails::OnShowHideAllInstancesClicked()
{
bForceShowAllInstances = !bForceShowAllInstances;
// pass the new value the component, as the following refresh will instanciate a new FInstancedStaticMeshComponentDetails :
if (ComponentBeingCustomized.IsValid())
{
ComponentBeingCustomized->bForceShowAllInstancesDetails = bForceShowAllInstances;
}
// Here we can only take the ptr as ForceRefreshDetails() checks that the reference is unique.
if (IDetailLayoutBuilder* DetailBuilder = CachedDetailBuilder.Pin().Get())
{
DetailBuilder->ForceRefreshDetails();
}
return FReply::Handled();
}
bool FInstancedStaticMeshComponentDetails::IsShowHideAllInstancesEnabled() const
{
return (NumInstances > 0);
}
FText FInstancedStaticMeshComponentDetails::GetShowHideAllInstancesText() const
{
return bForceShowAllInstances ?
FText::Format(LOCTEXT("HideAllInstances", "Hide All {0} Instances"), NumInstances)
: FText::Format(LOCTEXT("ShowAllInstances", "Show All {0} Instances"), NumInstances);
}
#undef LOCTEXT_NAMESPACE