IDetailCustomization
특정 UClass의 디테일 패널 레이아웃을 완전히 재구성하여 직관적인 에디터 경험을 만듭니다
IDetailCustomization 인터페이스
클래스 단위 디테일 패널 커스터마이징의 핵심
IDetailCustomization은 특정 UClass(액터, 컴포넌트, UObject)의 디테일 패널 전체 레이아웃을 커스터마이징합니다. 명시적으로 변경하지 않은 프로퍼티는 기본 UI 그대로 표시됩니다.
UCLASS()
class AMyNPC : public AActor
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, Category = "Identity")
FString NPCName;
UPROPERTY(EditAnywhere, Category = "Identity")
int32 NPCLevel;
UPROPERTY(EditAnywhere, Category = "Combat")
float AttackPower;
UPROPERTY(EditAnywhere, Category = "Combat")
float DefensePower;
UPROPERTY(EditAnywhere, Category = "AI")
bool bIsAggressive;
UPROPERTY(EditAnywhere, Category = "AI",
meta = (EditCondition = "bIsAggressive"))
float AggroRange;
};
CustomizeDetails 구현
IDetailLayoutBuilder를 활용한 레이아웃 재구성
#pragma once
#include "IDetailCustomization.h"
class FMyNPCDetailCustomization
: public IDetailCustomization
{
public:
static TSharedRef<IDetailCustomization> MakeInstance();
virtual void CustomizeDetails(
IDetailLayoutBuilder& DetailBuilder) override;
private:
FReply OnResetToDefaults();
TWeakObjectPtr<AMyNPC> EditedNPC;
};
#include "FMyNPCDetailCustomization.h"
#include "DetailLayoutBuilder.h"
#include "DetailCategoryBuilder.h"
#include "DetailWidgetRow.h"
TSharedRef<IDetailCustomization>
FMyNPCDetailCustomization::MakeInstance()
{
return MakeShareable(new FMyNPCDetailCustomization);
}
void FMyNPCDetailCustomization::CustomizeDetails(
IDetailLayoutBuilder& DetailBuilder)
{
// 편집 중인 객체 가져오기
TArray<TWeakObjectPtr<UObject>> Objects;
DetailBuilder.GetObjectsBeingCustomized(Objects);
if (Objects.Num() > 0)
{
EditedNPC = Cast<AMyNPC>(Objects[0].Get());
}
// === 카테고리 순서 재배치 ===
// Identity를 가장 위로
IDetailCategoryBuilder& IdentityCat =
DetailBuilder.EditCategory("Identity",
LOCTEXT("Identity", "NPC Identity"),
ECategoryPriority::Important);
// Combat 카테고리
IDetailCategoryBuilder& CombatCat =
DetailBuilder.EditCategory("Combat");
// AI 카테고리
IDetailCategoryBuilder& AICat =
DetailBuilder.EditCategory("AI");
// === 커스텀 위젯 행 추가 ===
IdentityCat.AddCustomRow(
LOCTEXT("InfoSearch", "NPC Info"))
.NameContent()
[
SNew(STextBlock)
.Text(LOCTEXT("Summary", "Quick Summary"))
.Font(IDetailLayoutBuilder::GetDetailFontBold())
]
.ValueContent()
[
SNew(STextBlock)
.Text(this, &FMyNPCDetailCustomization::GetSummaryText)
.ColorAndOpacity(FSlateColor(
FLinearColor(0.0f, 0.8f, 1.0f)))
];
// === 커스텀 버튼 추가 ===
CombatCat.AddCustomRow(
LOCTEXT("ActionsSearch", "Actions"))
.WholeRowContent()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(4.0f)
[
SNew(SButton)
.Text(LOCTEXT("Reset", "Reset to Defaults"))
.OnClicked(this,
&FMyNPCDetailCustomization::OnResetToDefaults)
]
];
// === 프로퍼티 숨기기 ===
// 특정 프로퍼티를 기본 표시에서 숨기기
// DetailBuilder.HideProperty(
// DetailBuilder.GetProperty(
// GET_MEMBER_NAME_CHECKED(AMyNPC, InternalData)));
}
IDetailLayoutBuilder API
디테일 레이아웃 빌더의 핵심 메서드
| 메서드 | 기능 | 사용 예 |
|---|---|---|
EditCategory() |
카테고리 접근/생성 | 카테고리 순서 변경, 새 카테고리 추가 |
GetProperty() |
프로퍼티 핸들 가져오기 | 값 읽기/쓰기, 숨기기 |
HideProperty() |
프로퍼티 숨기기 | 내부 변수, 불필요한 프로퍼티 |
HideCategory() |
카테고리 전체 숨기기 | 부모 클래스 카테고리 제거 |
GetObjectsBeingCustomized() |
편집 중인 UObject 배열 | 현재 선택된 객체 참조 |
ForceRefreshDetails() |
디테일 패널 새로고침 | 동적 레이아웃 변경 후 |
GetDetailFontBold() |
디테일 패널 볼드 폰트 | 커스텀 행의 스타일링 |
IDetailCategoryBuilder 메서드
| 메서드 | 기능 |
|---|---|
AddCustomRow() |
Slate 위젯으로 커스텀 행 추가 |
AddProperty() |
특정 프로퍼티를 이 카테고리에 표시 |
SetCategoryVisibility() |
카테고리 가시성 설정 |
SetSortOrder() |
카테고리 정렬 순서 설정 |
카테고리의 표시 우선순위: Variable(기본) < Transform < Important < Uncommon(접힌 상태). EditCategory의 세 번째 인자로 지정합니다.
등록 및 고급 패턴
커스터마이징 등록과 동적 레이아웃 패턴
void FMyGameEditorModule::StartupModule()
{
FPropertyEditorModule& PropertyModule =
FModuleManager::LoadModuleChecked<FPropertyEditorModule>(
"PropertyEditor");
// 클래스 이름으로 등록 (접두사 제외)
PropertyModule.RegisterCustomClassLayout(
AMyNPC::StaticClass()->GetFName(),
FOnGetDetailCustomizationInstance::CreateStatic(
&FMyNPCDetailCustomization::MakeInstance));
PropertyModule.NotifyCustomizationModuleChanged();
}
void FMyGameEditorModule::ShutdownModule()
{
if (FModuleManager::Get().IsModuleLoaded("PropertyEditor"))
{
FPropertyEditorModule& PropertyModule =
FModuleManager::GetModuleChecked<FPropertyEditorModule>(
"PropertyEditor");
PropertyModule.UnregisterCustomClassLayout(
AMyNPC::StaticClass()->GetFName());
}
}
RegisterCustomClassLayout에 전달하는 이름은 UClass의 GetFName() 결과입니다. AMyNPC 클래스의 경우 "MyNPC"가 아니라 AMyNPC::StaticClass()->GetFName()을 사용하세요.
핵심 요약
- IDetailCustomization은 특정 UClass의 디테일 패널 전체 레이아웃을 재구성합니다. 명시적으로 변경하지 않은 프로퍼티는 기본 UI로 유지됩니다
- IDetailLayoutBuilder로 카테고리 순서 변경, 프로퍼티 숨기기, 커스텀 위젯 행 추가가 가능합니다
- IDetailCategoryBuilder::AddCustomRow로 버튼, 요약 텍스트 등 임의의 Slate 위젯을 디테일 패널에 삽입합니다
RegisterCustomClassLayout으로 등록하며,StaticClass()->GetFName()으로 클래스 이름을 전달합니다- ECategoryPriority로 카테고리 표시 우선순위를 제어합니다. Important는 항상 위에, Uncommon은 접힌 상태로 표시됩니다
도전 과제
배운 내용을 직접 실습해보세요
커스텀 AActor 클래스의 디테일 패널을 IDetailCustomization으로 재구성하세요. 프로퍼티를 커스텀 카테고리로 그룹핑하고, 특정 프로퍼티 값에 따라 다른 프로퍼티를 숨기거나 표시하는 조건부 로직을 구현하세요.
IDetailCustomization::CustomizeDetails에서 AddCustomRow()로 디테일 패널 내에 '데이터 초기화', '값 복사', '검증' 버튼을 추가하세요. 버튼 클릭 시 선택된 Actor의 프로퍼티를 조작하는 기능을 구현하세요.
여러 Actor를 동시 선택했을 때 공통 프로퍼티와 혼합 상태를 올바르게 표시하는 IDetailCustomization을 구현하세요. IPropertyHandle::GetNumPerObjectValues()로 다중 값을 처리하고, 일괄 변경 기능을 지원하세요.