(UE4 4.26 ) UE4 定制UObject/UStruct对象的细节面板(DetailsView)

前言

UE4的细节面板(DetailViews)指的是展示UObject对象属性的面板,如下所示:

UE4提供了一套针对UObject的细节面板生成机制,能将一个UObject的各种属性轻松的生成对应的细节面板

细节面板(DetailsView)

细节面板(DetailsView)的构成

上面是StaticMeshComponent的细节面板,其自身就是SDetailsView, 为SCompountWidget组件。

SDetailsView自身是由UObject的属性生成,类别名其实就是UObject的UPEROPRTY(Category = "xxx").

而根据UObject的变量类别的不同,最终生成的控件就不同。

具体可以在PropertyEditorHelpers.cpp看到所有的UObject属性反射生成的SWidget类型

细节面板(DetailsView)的创建

用IDetailsView来创建细节面板,IDetailsView本身也是SCompoundWidget

        TSharedPtr<IDetailsView> DetailsView;	
        FPropertyEditorModule& PropertyEditorModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
	FDetailsViewArgs DetailsViewArgs(false, false, false, FDetailsViewArgs::HideNameArea);
	DetailsView = PropertyEditorModule.CreateDetailView(DetailsViewArgs);
	FTestDetailViewEdMode* TestEdMode = GetEditorMode();

	if (nullptr != TestEdMode)
	{
		DetailsView->SetObject(TestEdMode->MyObject);
	}

这里的MyObject对象为

UCLASS()
class TESTDETAILVIEW_API UMyObject : public UObject
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere, Category = "Test")
		float a;

	UPROPERTY(EditAnywhere, Category = "Test")
		UStaticMesh* Mesh;

	UPROPERTY(EditAnywhere, Category = "Test")
		int c;
};

细节面板(DetailsView)的定制

由UObject生成的DetailsView是各种可调节值的控件的组合, 也就是SPropertyEditorXXX类。但有时候,我们为了功能需求想进一步定制DetailsView,比如在XXX类别下加个SButton之类。想要进一步定制DetailsView,得继承IDetailCustomization,并在CustomizeDetails中增加控件。

继承IDetailCustomization

class  MyObjectDetailCustom : public IDetailCustomization
{
public:
	static TSharedRef<IDetailCustomization> MakeInstance();

	virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
};

CustomizeDetails,EditCategory,AddCustomRow

在void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)函数中增加需要的控件,IDetailLayoutBuilder接口提供了编辑类别EditCategory可以返回IDetailCategoryBuilder, 而IDetailCategoryBuilder的AddCustomRow可以给类别增加自定义的一行内容

TSharedRef<IDetailCustomization> MyObjectDetailCustom::MakeInstance()
{
	return MakeShareable(new MyObjectDetailCustom);
}

void MyObjectDetailCustom::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{

	IDetailCategoryBuilder& aaa = DetailBuilder.EditCategory(FName("aaa"));
	aaa.AddCustomRow(FText::GetEmpty())
	.NameContent()
	[
		SNew(STextBlock)
		.Text(FText::FromString("sss"))
	]
	.ValueContent()
	[
		SNew(SButton)
		.Text(FText::FromString("abc"))
	];

}

当然要让CustomizeDetails起效果,还得在开始模块进行CustomizeDetails的注册

	FPropertyEditorModule& PropertyEditorModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
	PropertyEditorModule.RegisterCustomClassLayout(FName("MyObject"), FOnGetDetailCustomizationInstance::CreateStatic(&MyObjectDetailCustom::MakeInstance));

退出模块记得注销CustomizeDetails

	FPropertyEditorModule& PropertyEditorModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
	PropertyEditorModule.UnregisterCustomClassLayout(FName("MyObject"));

效果如下:

其实CustomizeDetails是SDetailsView的SetObject调用的。

细节面板(DetailsView)的刷新

SDetailsView的接口SetObject不仅仅用于生成UObject对应的细节面板,当SetObject(XXXUObject, true)指定true的时候可以重新刷新界面

细节面板(DetailsView)控制UObject的某些属性是否显示

有时候细节面板存在太多的属性,导致界面过于复杂,我们希望某些属性在某些情况下不显示或者显示,SDetailsView提供了

void SDetailsViewBase::SetIsPropertyVisibleDelegate(FIsPropertyVisible InIsPropertyVisible)  接口

/** Delegate called to see if a property should be visible */
DECLARE_DELEGATE_RetVal_OneParam( bool, FIsPropertyVisible, const FPropertyAndParent& );

例子:

​
UCLASS()
class TESTDETAILVIEW_API UMyObject : public UObject
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere, Category = "Test", meta = (TestShow = "111"))
		float a;

	UPROPERTY(EditAnywhere, Category = "Test")
		UStaticMesh* Mesh;

	UPROPERTY(EditAnywhere, Category = "Test", meta = (TestShow = "222"))
		int c;
	
};

​

这里利用属性的Meta数据作为属性显示和不显示的判断依据(实际上编辑器细节面板开发控制属性显示不显示经常用到UProperty的MetaData)

DetailsView->SetIsPropertyVisibleDelegate(FIsPropertyVisible::CreateSP(this, &SMyCompoundWidget::IsPropertyVisible));


bool SMyCompoundWidget::IsPropertyVisible(const FPropertyAndParent& PropertyAndParent)
{
	const UProperty& Property = PropertyAndParent.Property;

	static const FName MetaName = "TestShow";
	if (Property.HasMetaData(MetaName))
	{
		TArray<FString> MetaStrs;
		Property.GetMetaData(MetaName).ParseIntoArray(MetaStrs, TEXT(","), true);
		if(MetaStrs.Contains("222"))
			return true;
	}

	return false;
}

显示结果:

定制细节面板(CustomizeDetails)控制类别下添加的行(FDetailWidgetRow)是否显示

可以直接用FDetailWidgetRow的Visibility

aaa.AddCustomRow(FText::GetEmpty())
	.Visibility(xxxxxxxx)
	.ValueContent()
	[
		SNew(SButton)
		.Text(FText::FromString("abc"))
	];

UStruct对象的细节面板

前面说到的是针对UObject对象的细节面板定制, 实际上UStruct对象也是可以定制细节面板。一个非常经典的例子就是FColor(颜色)或者FCurve(曲线),他们在细节面板暴露的UI就比较特别,就是通过UStruct DetailPanel来定制的。

拿下面自定的UStuct为例子:

USTRUCT()
struct FTestData
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere, Category = "Asset")
	int32 A;

	UPROPERTY(EditAnywhere, Category = "Asset")
	UStaticMesh* Mesh;
};

UStruct Customization定制类声明和定义

class FTestDataCustomization : public IPropertyTypeCustomization
{
public:
	static TSharedRef<IPropertyTypeCustomization> MakeInstance()
	{
		return MakeShareable(new FTestDataCustomization());
	}

	/**
	 * Destructor
	 */
	virtual ~FTestDataCustomization()
	{
		
	}

public:
	// IDetailCustomization interface
	virtual void CustomizeHeader(TSharedRef<IPropertyHandle> InStructPropertyHandle, FDetailWidgetRow& InHeaderRow, IPropertyTypeCustomizationUtils& InStructCustomizationUtils) override;
	virtual void CustomizeChildren(TSharedRef<IPropertyHandle> InStructPropertyHandle, IDetailChildrenBuilder& InChildBuilder, IPropertyTypeCustomizationUtils& InStructCustomizationUtils) override;

private:
	TSharedPtr<IPropertyHandle> APropertyHandle;
	TSharedPtr<IPropertyHandle> MeshPropertyHandle;
void FTestDataCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> InStructPropertyHandle, FDetailWidgetRow& InHeaderRow, IPropertyTypeCustomizationUtils& InStructCustomizationUtils)
{
	// 获取本UStruct对象所在的UObject对象
	const TArray<TWeakObjectPtr<UObject>>& SelectedObjects = InStructCustomizationUtils.GetPropertyUtilities()->GetSelectedObjects();
	if(SelectedObjects.Num() > 0 && SelectedObjects[0].IsValid())
	{
		
	}

	// 获取本Struct对象
	void* ValuePtr;
	InStructPropertyHandle->GetValueData(ValuePtr);
	FTestData* Test = reinterpret_cast<FTestData*>(ValuePtr);
	if(Test)
	{
		
	}
	
	APropertyHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTestData, A));
	MeshPropertyHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTestData, Mesh));

	InHeaderRow.WholeRowContent()
	.HAlign(HAlign_Center)
	[
		SNew(SVerticalBox)
		+SVerticalBox::Slot()
		[
			SNew(SHorizontalBox)
			+SHorizontalBox::Slot()
			[
				APropertyHandle->CreatePropertyNameWidget()
			]
			+SHorizontalBox::Slot()
			[
				APropertyHandle->CreatePropertyValueWidget()
			]
		]
		+SVerticalBox::Slot()
		[
			SNew(SHorizontalBox)
			+SHorizontalBox::Slot()
			[
				MeshPropertyHandle->CreatePropertyNameWidget()
			]
			+SHorizontalBox::Slot()
			[
				MeshPropertyHandle->CreatePropertyValueWidget()
			]
		]
		+SVerticalBox::Slot()
		[
			SNew(SButton)
			.Text(FText::FromString("Hello World"))
		]
	];
	
}

void FTestDataCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> InStructPropertyHandle, IDetailChildrenBuilder& InChildBuilder, IPropertyTypeCustomizationUtils& InStructCustomizationUtils)
{
	
}

步骤和UObjectDetail极其类似。

UStruct Customization定制类注册(在模块启动)

	FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
	PropertyModule.RegisterCustomPropertyTypeLayout( "TestData", FOnGetPropertyTypeCustomizationInstance::CreateStatic( &FTestDataCustomization::MakeInstance ) );

退出模块记得注销

	FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
	PropertyModule.UnregisterCustomPropertyTypeLayout( "TestData");

效果演示

参考资料

[1]. UE4的地形编辑器源码 Engine\Source\Editor\LandscapeEditor文件夹下的SLandscapeEditor.cpp,SLandscapeEditor.h,

LandscapeEditorObject.h

[2].https://docs.unrealengine.com/zh-CN/Programming/Slate/DetailsCustomization/index.html

[3] FBlackboardSelectorDetails

  • 8
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值