概述
本文主要討論自定義細節面板的製作,包含顯示分類及屬性信息。
版本:420
模塊:PropertyEditor
定製類型
-
默認佈局
在C++中,只要你的屬性以 UPROPERTY宏修飾且爲常用類型,Editor系統將會爲此屬性創建一個默認佈局。 -
自定義屬性
當你創建一個自定義結構時,若想編輯器爲其創建佈局,必須以UStruct修飾;但若是你想爲細節面板添加按鈕,或其他定製化控件,以實現某些功能時,本文可以爲你提供一個參考。
在IPropertyTypeCustomization的實現中,接口CustomizeHeader和CustomizeChildren提供了屬性的擴展功能。我們需要創建IPropertyTypeCustomization的子類,如下:
//MyStructCustomization.h
#include "Editor/DetailCustomizations/Private/DetailCustomizationsPrivatePCH.h"
#pragma once
class FMyStructCustomization : public IPropertyTypeCustomization
{
public:
static TSharedRef<IPropertyTypeCustomization> MakeInstance();
/** IPropertyTypeCustomization interface */
virtual void CustomizeHeader(TSharedRef<class IPropertyHandle> StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
virtual void CustomizeChildren(TSharedRef<class IPropertyHandle> StructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
private:
TSharedPtr<IPropertyHandle> SomeUPropertyHandle;
};
////MyStructCustomization.cpp
#include "DetailCustomizations/MyStructCustomization.h"
#define LOCTEXT_NAMESPACE "MyStructCustomization"
TSharedRef<IPropertyTypeCustomization> FMyStructCustomization::MakeInstance()
{
return MakeShareable(new FMyStructCustomization());
}
void FMyStructCustomization::CustomizeHeader(TSharedRef<class IPropertyHandle> StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
uint32 NumChildren;
StructPropertyHandle->GetNumChildren(NumChildren);
for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex)
{
const TSharedRef< IPropertyHandle > ChildHandle = StructPropertyHandle->GetChildHandle(ChildIndex).ToSharedRef();
if (ChildHandle->GetProperty()->GetName() == TEXT("SomeUProperty"))
{
SomeUPropertyHandle = ChildHandle;
}
}
check(SomeUPropertyHandle.IsValid());
HeaderRow.NameContent()
[
StructPropertyHandle->CreatePropertyNameWidget(TEXT("New property header name"), false)
]
.ValueContent()
.MinDesiredWidth(500)
[
SNew(STextBlock)
.Text(LOCTEXT("Extra info", "Some new representation"))
.Font(IDetailLayoutBuilder::GetDetailFont())
];
}
void FMyStructCustomization::CustomizeChildren(TSharedRef<class IPropertyHandle> StructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
//Create further customization here
}
#undef LOCTEXT_NAMESPACE
其中,CustomizeHeader 從自定義結構體MyStruct 中提取SomeUProperty屬性初始化屬性SomeUPropertyHandle,然偶以Slate代碼覆寫細節面板。(關於Slate的學習可查閱官方文檔)
- 自定義Object(細節面板)
如果你想自定義結構展示,也想控制整個detail面板的渲染,如自定義分類和其具體細節等等,實現IDetailCustomization接口即可;如下:
//MyClassDetails.h
#include "Editor/DetailCustomizations/Private/DetailCustomizationsPrivatePCH.h"
#pragma once
class FMyClassDetails : public IDetailCustomization
{
public:
/** Makes a new instance of this detail layout class for a specific detail view requesting it */
static TSharedRef<IDetailCustomization> MakeInstance();
/** IDetailCustomization interface */
virtual void CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) override;
};
////MyClassDetails.cpp
#include "MyProjectPCH.h"
#include "DetailCustomizations/MyClassDetails.h"
#define LOCTEXT_NAMESPACE "MyClassDetails"
TSharedRef<IDetailCustomization> FMyClassDetails::MakeInstance()
{
return MakeShareable(new FMyClassDetails);
}
void FMyClassDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
// Create a category so this is displayed early in the properties
IDetailCategoryBuilder& MyCategory = DetailBuilder.EditCategory("CategoryName", TEXT("Extra info"), ECategoryPriority::Important);
//You can get properties using the detailbuilder
//MyProperty= DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(MyClass, MyClassPropertyName));
MyCategory.AddCustomRow(LOCTEXT("Extra info", "Row header name").ToString())
.NameContent()
[
SNew(STextBlock)
.Text(LOCTEXT("Extra info", "Custom row header name"))
.Font(IDetailLayoutBuilder::GetDetailFont())
]
.ValueContent().MinDesiredWidth(500)
[
SNew(STextBlock)
.Text(LOCTEXT("Extra info", "Custom row content"))
.Font(IDetailLayoutBuilder::GetDetailFont())
];
}
#undef LOCTEXT_NAMESPACE
其中CustomizeDetails 函數創建了分類"CategoryName",然後在分類"CategoryName"下新建名稱爲"Custom row header name",內容爲"Custom row content"的屬性。
面板註冊
以上已經成功創建出了我們所需的細節面板,但爲了讓UE4編輯器知道該定製的存在,還需要註冊正確的模板。本步驟可在解決方案的任何位置進行,但爲了避免不可預期的錯誤,推薦僅在一些代碼僅運行一次的地方進行。如:主遊戲模塊或者game mode中。
在build.cs中包含PropertyEditor,如下:
PrivateDependencyModuleNames.AddRange(
new string[] {
"Core",
"CoreUObject",
"Engine",
"UnrealEd",
"AssetTools",
"Projects",
"Slate",
"EditorStyle",
"SlateCore",
"PropertyEditor"
}
在合適的位置進行註冊,自定義屬性使用RegisterCustomPropertyTypeLayout,如下:
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
//Custom properties
PropertyModule.RegisterCustomPropertyTypeLayout("MyStruct", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FMyStructCustomization::MakeInstance));
自定義object使用RegisterCustomClassLayout,如下:
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
//Custom detail views
PropertyModule.RegisterCustomClassLayout("MyClass", FOnGetDetailCustomizationInstance::CreateStatic(&FMyClassDetails::MakeInstance));
注:輸入的字符串爲自定義類型去除修飾符,如UMyClass,則應輸入MyClass。本例子寫在插件中,因此註冊寫在了插件的StartupModule函數中
官方資源
引擎源碼
Source/Editor/DetailCustomizations/Private/DetailCustomizations.cpp
可供讀者參考
自定義分類的使用可參考:
Source/Editor/DetailCustomizations/Private/StaticMeshComponentDetails.h
Source/Editor/DetailCustomizations/Private/StaticMeshComponentDetails.cpp
自定義屬性可參考:
Source/Editor/DetailCustomizations/Private/SlateColorCustomization.h
Source/Editor/DetailCustomizations/Private/SlateColorCustomization.cpp