6.4 KiB
CustomizeProperty
- 功能描述: 使用在FAnimNode的成员属性上,告诉编辑器不要为它生成默认Details面板控件,后续会在DetailsCustomization里自定义创建相应的编辑控件。
- 使用位置: UPROPERTY
- 引擎模块: AnimationGraph
- 元数据类型: bool
- 限制类型: FAnimNode里的属性
- 常用程度: ★
使用在FAnimNode的成员属性上,告诉编辑器不要为它生成默认Details面板控件,后续会在DetailsCustomization里自定义创建相应的编辑控件。
和AllowEditInlineCustomization的作用有点像,都只是做个标记提示编辑器会在别的地方进行自定义,不用为它生成默认Details面板控件。
源码中例子:
在源码里能见到挺多例子,常见的就是在AnimBP中的节点上的属性,其在细节面板需要专门的定制化编辑。最常见的例子是Slot这个节点,其SlotName只是个FString类型,但是在细节面板里显示的却是个ComboString。这是因为它标上了CustomizeProperty,告知默认的动画节点细节面板生成器*(FAnimGraphNodeDetails)先不要为这个属性创建编辑控件,之后会在自己的定制化(FAnimGraphNodeSlotDetails)里为SlotName再创建自定义UI。
struct FAnimNode_Slot : public FAnimNode_Base
{
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Settings, meta=(CustomizeProperty))
FName SlotName;
}
void FPersonaModule::CustomizeBlueprintEditorDetails(const TSharedRef<class IDetailsView>& InDetailsView, FOnInvokeTab InOnInvokeTab)
{
InDetailsView->RegisterInstancedCustomPropertyLayout(UAnimGraphNode_Slot::StaticClass(),
FOnGetDetailCustomizationInstance::CreateStatic(&FAnimGraphNodeSlotDetails::MakeInstance, InOnInvokeTab));
InDetailsView->SetExtensionHandler(MakeShared<FAnimGraphNodeBindingExtension>());
}
测试代码:
USTRUCT(BlueprintInternalUseOnly)
struct INSIDER_API FAnimNode_MyCustomProperty : public FAnimNode_Base
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CustomProperty)
FString MyString_Default;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CustomProperty, meta = (CustomizeProperty))
FString MyString_CustomizeProperty;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CustomProperty, meta = (CustomizeProperty))
FString MyString_CustomizeProperty_Other;
};
UCLASS()
class INSIDEREDITOR_API UAnimGraphNode_MyCustomProperty : public UAnimGraphNode_Base
{
GENERATED_UCLASS_BODY()
UPROPERTY(EditAnywhere, Category = Settings)
FAnimNode_MyCustomProperty Node;
};
//再创建一个定制化,生成自定义UI
void FMyAnimNode_MyCustomPropertyCustomization::CustomizeDetails(class IDetailLayoutBuilder& DetailBuilder)
{
TSharedPtr<IPropertyHandle> PropertyHandle = DetailBuilder.GetProperty(TEXT("Node.MyString_CustomProperty"));
//Just for test
ComboListItems.Empty();
ComboListItems.Add(MakeShareable(new FString(TEXT("First"))));
ComboListItems.Add(MakeShareable(new FString(TEXT("Second"))));
ComboListItems.Add(MakeShareable(new FString(TEXT("Third"))));
const TSharedPtr<FString> ComboBoxSelectedItem = ComboListItems[0];
IDetailCategoryBuilder& Group = DetailBuilder.EditCategory(TEXT("CustomProperty"));
Group.AddCustomRow(INVTEXT("CustomProperty"))
.NameContent()
[
PropertyHandle->CreatePropertyNameWidget()
]
.ValueContent()
[
SNew(STextComboBox)
.OptionsSource(&ComboListItems)
.InitiallySelectedItem(ComboBoxSelectedItem)
.ContentPadding(2.f)
.ToolTipText(FText::FromString(*ComboBoxSelectedItem))
];
}
//注册定制化
PropertyModule.RegisterCustomClassLayout(TEXT("AnimGraphNode_MyCustomProperty"), FOnGetDetailCustomizationInstance::CreateStatic(&FMyAnimNode_MyCustomPropertyCustomization::MakeInstance));
测试效果:
SlotName的效果如右侧所示。
我们自己模仿的例子可见MyString_Default依然只是个默认String,而MyString_CustomizeProperty则为它创建了自定义UI。
作为对比,MyString_CustomizeProperty_Other我们标上了CustomizeProperty但是没有为它创建UI,则没有显示出来,说明引擎默认的机制因此就把它的UI默认创建流程给跳过了。
原理:
CustomizeProperty其实会会改变Pin的bPropertyIsCustomized 属性(GetRecordDefaults中体现),然后在创建流程的过程中不创建默认的widget,这个可见CustomizeDetails中的bPropertyIsCustomized判断得知。
void FAnimBlueprintNodeOptionalPinManager::GetRecordDefaults(FProperty* TestProperty, FOptionalPinFromProperty& Record) const
{
const UAnimationGraphSchema* Schema = GetDefault<UAnimationGraphSchema>();
// Determine if this is a pose or array of poses
FArrayProperty* ArrayProp = CastField<FArrayProperty>(TestProperty);
FStructProperty* StructProp = CastField<FStructProperty>(ArrayProp ? ArrayProp->Inner : TestProperty);
const bool bIsPoseInput = (StructProp && StructProp->Struct->IsChildOf(FPoseLinkBase::StaticStruct()));
//@TODO: Error if they specified two or more of these flags
const bool bAlwaysShow = TestProperty->HasMetaData(Schema->NAME_AlwaysAsPin) || bIsPoseInput;
const bool bOptional_ShowByDefault = TestProperty->HasMetaData(Schema->NAME_PinShownByDefault);
const bool bOptional_HideByDefault = TestProperty->HasMetaData(Schema->NAME_PinHiddenByDefault);
const bool bNeverShow = TestProperty->HasMetaData(Schema->NAME_NeverAsPin);
const bool bPropertyIsCustomized = TestProperty->HasMetaData(Schema->NAME_CustomizeProperty);
const bool bCanTreatPropertyAsOptional = CanTreatPropertyAsOptional(TestProperty);
Record.bCanToggleVisibility = bCanTreatPropertyAsOptional && (bOptional_ShowByDefault || bOptional_HideByDefault);
Record.bShowPin = bAlwaysShow || bOptional_ShowByDefault;
Record.bPropertyIsCustomized = bPropertyIsCustomized;
}
//这个是在AnimBP中选中一个节点然后在右侧细节面板中的属性
void FAnimGraphNodeDetails::CustomizeDetails(class IDetailLayoutBuilder& DetailBuilder)
{
// sometimes because of order of customization
// this gets called first for the node you'd like to customize
// then the above statement won't work
// so you can mark certain property to have meta data "CustomizeProperty"
// which will trigger below statement
if (OptionalPin.bPropertyIsCustomized)
{
continue;
}
TSharedRef<SWidget> InternalCustomWidget = CreatePropertyWidget(TargetProperty, TargetPropertyHandle.ToSharedRef(), AnimGraphNode->GetClass());
}