vault backup: 2024-10-12 17:19:45
@@ -0,0 +1,15 @@
|
||||
# AdvancedClassDisplay
|
||||
|
||||
- **功能描述:** 指定该类型的变量在高级显示里显示
|
||||
|
||||
- **使用位置:** UCLASS
|
||||
|
||||
- **引擎模块:** DetailsPanel
|
||||
|
||||
- **元数据类型:** bool
|
||||
|
||||
- **关联项:**
|
||||
|
||||
UCLASS:[AdvancedClassDisplay](../../Specifier/UCLASS/Category/AdvancedClassDisplay/AdvancedClassDisplay.md)
|
||||
|
||||
- **常用程度:** ★★★
|
@@ -0,0 +1,174 @@
|
||||
# AllowEditInlineCustomization
|
||||
|
||||
- **功能描述:** 允许EditInline的对象属性可以自定义属性细节面板来编辑该对象内的数据。
|
||||
- **使用位置:** UPROPERTY
|
||||
- **元数据类型:** string="abc"
|
||||
- **关联项:** [EditInline](../EditInline/EditInline.md)
|
||||
- **常用程度:** ★
|
||||
|
||||
允许EditInline的对象属性可以自定义属性细节面板来编辑该对象内的数据。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyCommonObject :public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
int32 MyInt = 123;
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
FString MyString;
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyCustomAsset :public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (EditInline))
|
||||
UMyCommonObject* MyCommonObject;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (EditInline,AllowEditInlineCustomization))
|
||||
UMyCommonObject* MyCommonObject_Customization;
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## 效果:
|
||||
|
||||

|
||||
|
||||
要做到自定义EditInline的效果,采用自定义的IPropertyTypeCustomization和RegisterCustomPropertyTypeLayout也能做到。区别是,正如上面代码里的UMyCustomAsset里面有两个同类型的UMyCommonObject*对象,假如用IPropertyTypeCustomization的方式,就会导致两个变量都变成自定义的UI模式。而用AllowEditInlineCustomization就可以使得其中你想要的那个变成自定义方式,而其他的不做改变。
|
||||
|
||||
在用法上,AllowEditInlineCustomization必须配合自定义的FAssetEditorToolkit来自己定义一个DetailView(而不是只自定义某个类型在引擎统一的DetailView的显示),然后再自定义IDetailCustomization来提供具体的Widget,最后用RegisterInstancedCustomPropertyLayout来关联起来。
|
||||
|
||||
```cpp
|
||||
DetailsView->RegisterInstancedCustomPropertyLayout(UMyCommonObject::StaticClass(),FOnGetDetailCustomizationInstance::CreateStatic(&FMyCommonObjectDetailsCustomization::MakeInstance));
|
||||
```
|
||||
|
||||
(这部分代码可参考MyCustomAsset的相关实现)
|
||||
|
||||
## 源码:
|
||||
|
||||
```cpp
|
||||
FDetailPropertyRow::FDetailPropertyRow(TSharedPtr<FPropertyNode> InPropertyNode, TSharedRef<FDetailCategoryImpl> InParentCategory, TSharedPtr<FComplexPropertyNode> InExternalRootNode)
|
||||
{
|
||||
static FName InlineCustomizationKeyMeta("AllowEditInlineCustomization");
|
||||
if (PropertyNode->AsComplexNode() && ExternalRootNode.IsValid()) // AsComplexNode works both for objects and structs
|
||||
{
|
||||
// We are showing an entirely different object inline. Generate a layout for it now.
|
||||
if (IDetailsViewPrivate* DetailsView = InParentCategory->GetDetailsView())
|
||||
{
|
||||
ExternalObjectLayout = MakeShared<FDetailLayoutData>();
|
||||
DetailsView->UpdateSinglePropertyMap(InExternalRootNode, *ExternalObjectLayout, true);
|
||||
}
|
||||
}
|
||||
else if (PropertyNode->HasNodeFlags(EPropertyNodeFlags::EditInlineNew) && PropertyNode->GetProperty()->HasMetaData(InlineCustomizationKeyMeta))
|
||||
{
|
||||
// Allow customization of 'edit inline new' objects if the metadata key has been specified.
|
||||
// The child of this node, if set, will be an object node that we will want to treat as an 'external object layout'
|
||||
TSharedPtr<FPropertyNode> ChildNode = PropertyNode->GetNumChildNodes() > 0 ? PropertyNode->GetChildNode(0) : nullptr;
|
||||
TSharedPtr<FComplexPropertyNode> ComplexChildNode = StaticCastSharedPtr<FComplexPropertyNode>(ChildNode);
|
||||
if (ComplexChildNode.IsValid())
|
||||
{
|
||||
// We are showing an entirely different object inline. Generate a layout for it now.
|
||||
if (IDetailsViewPrivate* DetailsView = InParentCategory->GetDetailsView())
|
||||
{
|
||||
ExternalObjectLayout = MakeShared<FDetailLayoutData>();
|
||||
DetailsView->UpdateSinglePropertyMap(ComplexChildNode, *ExternalObjectLayout, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
作用的原理是在创建FDetailPropertyRow的时候,即一个属性的在细节面板里的一行,如果有AllowEditInlineCustomization,就会创建ExternalObjectLayout ,之后在FDetailPropertyRow的创建孩子的时候,就会判断是否有ExternalObjectLayout,如果有就可以应用上我们之前的Customization,如果没有就会应用默认的设置。如下是使用ExternalObjectLayout的代码:
|
||||
|
||||
```cpp
|
||||
void FDetailPropertyRow::GenerateChildrenForPropertyNode( TSharedPtr<FPropertyNode>& RootPropertyNode, FDetailNodeList& OutChildren )
|
||||
{
|
||||
// Children should be disabled if we are disabled
|
||||
TAttribute<bool> ParentEnabledState = TAttribute<bool>::CreateSP(this, &FDetailPropertyRow::GetEnabledState);
|
||||
|
||||
if( PropertyTypeLayoutBuilder.IsValid() && bShowCustomPropertyChildren )
|
||||
{
|
||||
const TArray< FDetailLayoutCustomization >& ChildRows = PropertyTypeLayoutBuilder->GetChildCustomizations();
|
||||
|
||||
for( int32 ChildIndex = 0; ChildIndex < ChildRows.Num(); ++ChildIndex )
|
||||
{
|
||||
TSharedRef<FDetailItemNode> ChildNodeItem = MakeShared<FDetailItemNode>(ChildRows[ChildIndex], ParentCategory.Pin().ToSharedRef(), ParentEnabledState);
|
||||
ChildNodeItem->Initialize();
|
||||
OutChildren.Add( ChildNodeItem );
|
||||
}
|
||||
}
|
||||
else if (ExternalObjectLayout.IsValid() && ExternalObjectLayout->DetailLayout->HasDetails())
|
||||
{
|
||||
OutChildren.Append(ExternalObjectLayout->DetailLayout->GetAllRootTreeNodes());
|
||||
//自定义的面板
|
||||
}
|
||||
else if ((bShowCustomPropertyChildren || !CustomPropertyWidget.IsValid()) && RootPropertyNode->GetNumChildNodes() > 0)
|
||||
{
|
||||
//正常的默认创建孩子
|
||||
}
|
||||
```
|
||||
|
||||
源码里使用的一个例子是LevelSequence上Bind Actor上的Binding Property的细节面板。
|
||||
|
||||
假如我们采用一些代码去掉
|
||||
|
||||
```cpp
|
||||
USTRUCT()
|
||||
struct FMovieSceneBindingPropertyInfo
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Locator for the entry
|
||||
UPROPERTY(EditAnywhere, Category = "Default", meta=(AllowedLocators="Actor", DisplayName="Actor"))
|
||||
FUniversalObjectLocator Locator;
|
||||
|
||||
// Flags for how to resolve the locator
|
||||
UPROPERTY()
|
||||
ELocatorResolveFlags ResolveFlags = ELocatorResolveFlags::None;
|
||||
|
||||
UPROPERTY(Instanced, VisibleAnywhere, Category = "Default", meta=(EditInline, AllowEditInlineCustomization, DisplayName="Custom Binding Type"))
|
||||
UMovieSceneCustomBinding* CustomBinding = nullptr;
|
||||
};
|
||||
|
||||
//自己Hack 代码
|
||||
UObject* obj = UInsiderLibrary::FindObjectWithNameSmart(TEXT("MovieSceneBindingPropertyInfo"));
|
||||
UScriptStruct* ss = Cast<UScriptStruct>(obj);
|
||||
FProperty* prop = ss->FindPropertyByName(TEXT("CustomBinding"));
|
||||
prop->RemoveMetaData(TEXT("AllowEditInlineCustomization"));
|
||||
```
|
||||
|
||||
效果就会从左变到右边:
|
||||
|
||||

|
||||
|
||||
注册的方式也不同:
|
||||
|
||||
```cpp
|
||||
void ULevelSequenceEditorSubsystem::AddBindingDetailCustomizations(TSharedRef<IDetailsView> DetailsView, TSharedPtr<ISequencer> ActiveSequencer, FGuid BindingGuid)
|
||||
{
|
||||
// TODO: Do we want to create a generalized way for folks to add instanced property layouts for other custom binding types so they can have access to sequencer context?
|
||||
if (ActiveSequencer.IsValid())
|
||||
{
|
||||
UMovieSceneSequence* Sequence = ActiveSequencer->GetFocusedMovieSceneSequence();
|
||||
UMovieScene* MovieScene = Sequence ? Sequence->GetMovieScene() : nullptr;
|
||||
if (MovieScene)
|
||||
{
|
||||
FPropertyEditorModule& PropertyEditor = FModuleManager::Get().LoadModuleChecked<FPropertyEditorModule>(TEXT("PropertyEditor"));
|
||||
DetailsView->RegisterInstancedCustomPropertyTypeLayout(FMovieSceneBindingPropertyInfo::StaticStruct()->GetFName(), FOnGetPropertyTypeCustomizationInstance::CreateLambda([](TWeakPtr<ISequencer> InSequencer, UMovieScene* InMovieScene, FGuid InBindingGuid, ULevelSequenceEditorSubsystem* LevelSequenceEditorSubsystem)
|
||||
{
|
||||
return MakeShared<FMovieSceneBindingPropertyInfoDetailCustomization>(InSequencer, InMovieScene, InBindingGuid, LevelSequenceEditorSubsystem);
|
||||
}, ActiveSequencer.ToWeakPtr(), MovieScene, BindingGuid, this));
|
||||
|
||||
DetailsView->RegisterInstancedCustomPropertyLayout(UMovieSceneSpawnableActorBinding::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FMovieSceneSpawnableActorBindingBaseCustomization::MakeInstance, ActiveSequencer.ToWeakPtr(), MovieScene, BindingGuid));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 253 KiB |
After Width: | Height: | Size: 20 KiB |
@@ -0,0 +1,15 @@
|
||||
# AutoCollapseCategories
|
||||
|
||||
- **功能描述:** 指定类内部的属性目录自动折叠起来
|
||||
|
||||
- **使用位置:** UCLASS
|
||||
|
||||
- **引擎模块:** DetailsPanel
|
||||
|
||||
- **元数据类型:** strings="a,b,c"
|
||||
|
||||
- **关联项:**
|
||||
|
||||
UCLASS:[AutoCollapseCategories](../../Specifier/UCLASS/Category/AutoCollapseCategories/AutoCollapseCategories.md), [DontAutoCollapseCategories](../../Specifier/UCLASS/Category/DontAutoCollapseCategories.md), [AutoExpandCategories](../../Specifier/UCLASS/Category/AutoExpandCategories/AutoExpandCategories.md)
|
||||
|
||||
- **常用程度:** ★★★
|
@@ -0,0 +1,15 @@
|
||||
# AutoExpandCategories
|
||||
|
||||
- **功能描述:** 指定类内部的属性目录自动展开起来
|
||||
|
||||
- **使用位置:** UCLASS
|
||||
|
||||
- **引擎模块:** DetailsPanel
|
||||
|
||||
- **元数据类型:** strings="a,b,c"
|
||||
|
||||
- **关联项:**
|
||||
|
||||
UCLASS:[AutoExpandCategories](../../Specifier/UCLASS/Category/AutoExpandCategories/AutoExpandCategories.md), [AutoCollapseCategories](../../Specifier/UCLASS/Category/AutoCollapseCategories/AutoCollapseCategories.md)
|
||||
|
||||
- **常用程度:** ★★★
|
@@ -0,0 +1,16 @@
|
||||
# Category
|
||||
|
||||
- **功能描述:** 指定属性在细节面板中的分类
|
||||
|
||||
- **使用位置:** UFUNCTION, UPROPERTY
|
||||
|
||||
- **引擎模块:** DetailsPanel
|
||||
|
||||
- **元数据类型:** string="A | B | C"
|
||||
|
||||
- **关联项:**
|
||||
|
||||
UFUNCTION:[Category](../../Specifier/UFUNCTION/Category/Category.md)
|
||||
UPROPERTY:[Category](../../Specifier/UPROPERTY/DetaisPanel/Category/Category.md)
|
||||
|
||||
- **常用程度:** ★★★★★
|
@@ -0,0 +1,17 @@
|
||||
# ClassGroupNames
|
||||
|
||||
- **功能描述:** 指定ClassGroup的名字
|
||||
|
||||
- **使用位置:** UCLASS
|
||||
|
||||
- **引擎模块:** DetailsPanel
|
||||
|
||||
- **元数据类型:** strings="a,b,c"
|
||||
|
||||
- **限制类型:** TArray<FString>
|
||||
|
||||
- **关联项:**
|
||||
|
||||
UCLASS:[ClassGroup](../../Specifier/UCLASS/Category/ClassGroup/ClassGroup.md)
|
||||
|
||||
- **常用程度:** ★★★
|
@@ -0,0 +1,61 @@
|
||||
# DeprecatedNode
|
||||
|
||||
- **功能描述:** 用于BehaviorTreeNode或EnvQueryNode,说明该类已废弃,在编辑器中红色错误展示并有错误ToolTip提示
|
||||
- **使用位置:** UCLASS
|
||||
- **引擎模块:** DetailsPanel
|
||||
- **元数据类型:** bool
|
||||
- **限制类型:** BehaviorTreeNode,EnvQueryNode
|
||||
- **常用程度:** ★★
|
||||
|
||||
在AI行为树或EQS的节点上设置,标记该节点已经弃用。
|
||||
|
||||
## 源码中的例子:
|
||||
|
||||
```cpp
|
||||
UCLASS(meta = (DeprecatedNode, DeprecationMessage = "Please use IsAtLocation decorator instead."), MinimalAPI)
|
||||
class UBTDecorator_ReachedMoveGoal : public UBTDecorator
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
};
|
||||
|
||||
UCLASS(MinimalAPI, meta=(DeprecatedNode, DeprecationMessage = "This class is now deprecated, please use RunMode supporting random results instead."))
|
||||
class UEnvQueryTest_Random : public UEnvQueryTest
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
};
|
||||
```
|
||||
|
||||
## C++测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(meta = (DeprecatedNode, DeprecationMessage = "This BT node is deprecated. Don't use this anymore."), MinimalAPI)
|
||||
class UBTTask_MyDeprecatedNode : public UBTTaskNode
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
};
|
||||
```
|
||||
|
||||
行为树里的结果,如果加上DeprecatedNode,就会红色显示,并提示错误信息。
|
||||
|
||||

|
||||
|
||||
## 源码里测试的代码:
|
||||
|
||||
```cpp
|
||||
FString FGraphNodeClassHelper::GetDeprecationMessage(const UClass* Class)
|
||||
{
|
||||
static FName MetaDeprecated = TEXT("DeprecatedNode");
|
||||
static FName MetaDeprecatedMessage = TEXT("DeprecationMessage");
|
||||
FString DefDeprecatedMessage("Please remove it!");
|
||||
FString DeprecatedPrefix("DEPRECATED");
|
||||
FString DeprecatedMessage;
|
||||
|
||||
if (Class && Class->HasAnyClassFlags(CLASS_Native) && Class->HasMetaData(MetaDeprecated))
|
||||
{
|
||||
DeprecatedMessage = DeprecatedPrefix + TEXT(": ");
|
||||
DeprecatedMessage += Class->HasMetaData(MetaDeprecatedMessage) ? Class->GetMetaData(MetaDeprecatedMessage) : DefDeprecatedMessage;
|
||||
}
|
||||
|
||||
return DeprecatedMessage;
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 130 KiB |
@@ -0,0 +1,63 @@
|
||||
# DisplayAfter
|
||||
|
||||
- **功能描述:** 使本属性在指定的属性之后显示。
|
||||
- **使用位置:** UPROPERTY
|
||||
- **引擎模块:** DetailsPanel
|
||||
- **元数据类型:** string="abc"
|
||||
- **常用程度:** ★★★
|
||||
|
||||
使本属性在指定的属性之后显示。
|
||||
|
||||
- 默认情况下,属性在细节面板中的顺序是依照头文件中的定义顺序。但如果我们想自己调节这个顺序,就可以用该标记。
|
||||
- 限制条件是这两个属性必须得是在同一个Category下。这也很好理解,Category组织的优先级肯定更大。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(BlueprintType)
|
||||
class INSIDER_API UMyProperty_Priority :public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AfterTest)
|
||||
int32 MyInt = 123;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AfterTest)
|
||||
FString MyString;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AfterTest, meta = (DisplayAfter = "MyInt"))
|
||||
int32 MyInt_After = 123;
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AfterTest2, meta = (DisplayAfter = "MyInt"))
|
||||
int32 MyInt_After2 = 123;
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
## 测试效果:
|
||||
|
||||
可见MyInt_After直接在Int后显示。
|
||||
|
||||
而MyInt_After2 因为在不同的Category下,因此就保留原样。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
检查该属性如果有DisplayAfter,就把它插入在指定的属性之后。
|
||||
|
||||
```cpp
|
||||
void PropertyEditorHelpers::OrderPropertiesFromMetadata(TArray<FProperty*>& Properties)
|
||||
{
|
||||
const FString& DisplayAfterPropertyName = Prop->GetMetaData(NAME_DisplayAfter);
|
||||
if (DisplayAfterPropertyName.IsEmpty())
|
||||
{
|
||||
InsertProperty(OrderedProperties);
|
||||
}
|
||||
else
|
||||
{
|
||||
TArray<TPair<FProperty*, int32>>& DisplayAfterProperties = DisplayAfterPropertyMap.FindOrAdd(FName(*DisplayAfterPropertyName));
|
||||
InsertProperty(DisplayAfterProperties);
|
||||
}
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,42 @@
|
||||
# DisplayPriority
|
||||
|
||||
- **功能描述:** 指定本属性在细节面板的显示顺序优先级,越小的优先级越高。
|
||||
- **使用位置:** UPROPERTY
|
||||
- **引擎模块:** DetailsPanel
|
||||
- **元数据类型:** int32
|
||||
- **常用程度:** ★★★
|
||||
|
||||
指定本属性在细节面板的显示顺序优先级,越小的优先级越高。
|
||||
|
||||
- 如果有DisplayAfter的设置,则DisplayAfter的优先级更高。
|
||||
- 同样的限制得是在同Category里。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PriorityTest, meta = (DisplayPriority = 3))
|
||||
int32 MyInt_P3 = 123;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PriorityTest, meta = (DisplayPriority = 1))
|
||||
int32 MyInt_P1 = 123;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PriorityTest, meta = (DisplayPriority = 2))
|
||||
int32 MyInt_P2 = 123;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PriorityTest, meta = (DisplayPriority = 4,DisplayAfter="MyInt_P1"))
|
||||
int32 MyInt_P4 = 123;
|
||||
```
|
||||
|
||||
## 测试结果:
|
||||
|
||||
P4即使优先级比较低,但因为DisplayAfter也仍然排在了P1之后。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
排序的逻辑在这个函数内,自行查看就好。一个简单的插入排序算法。
|
||||
|
||||
```cpp
|
||||
void PropertyEditorHelpers::OrderPropertiesFromMetadata(TArray<FProperty*>& Properties)
|
||||
{}
|
||||
```
|
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 150 KiB |
@@ -0,0 +1,77 @@
|
||||
# EditCondition
|
||||
|
||||
- **功能描述:** 给一个属性指定另外一个属性或者表达式来作为是否可编辑的条件。
|
||||
- **使用位置:** UPROPERTY
|
||||
- **引擎模块:** DetailsPanel
|
||||
- **元数据类型:** string="abc"
|
||||
- **关联项:** [EditConditionHides](../EditConditionHides/EditConditionHides.md), [InlineEditConditionToggle](../InlineEditConditionToggle/InlineEditConditionToggle.md), [HideEditConditionToggle](../HideEditConditionToggle/HideEditConditionToggle.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
给一个属性指定另外一个属性或者表达式来作为是否可编辑的条件。
|
||||
|
||||
- 表达式里引用的属性必须得是同一个类或结构范围内的。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(BlueprintType)
|
||||
class INSIDER_API UMyProperty_EditCondition_Test :public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Property)
|
||||
bool MyBool;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Property)
|
||||
int32 MyInt = 123;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Property, meta = (EditCondition = "MyBool"))
|
||||
int32 MyInt_EditCondition = 123;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Property, meta = (EditCondition = "!MyBool"))
|
||||
int32 MyInt_EditCondition_Not = 123;
|
||||
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PropertyExpression)
|
||||
int32 MyFirstInt = 123;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PropertyExpression)
|
||||
int32 MySecondInt = 123;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PropertyExpression, meta = (EditCondition = "(MyFirstInt+MySecondInt)==500"))
|
||||
int32 MyInt_EditConditionExpression = 123;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PropertyExpression, meta = (EditCondition = "!((MyFirstInt+MySecondInt)==500)"))
|
||||
int32 MyInt_EditConditionExpression_Not = 123;
|
||||
};
|
||||
```
|
||||
|
||||
## 测试结果:
|
||||
|
||||
- 可以通过bool单个属性来控制其他属性是否可以编辑
|
||||
- 也可以通过一个表达式引入更复杂的计算机制来决定是否来编辑。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
在细节面板的属性初始化的时候,会判断该属性EditCondition设置,如果有值,会创建FEditConditionParser来解析表达式然后求值。
|
||||
|
||||
```cpp
|
||||
void FPropertyNode::InitNode(const FPropertyNodeInitParams& InitParams)
|
||||
{
|
||||
const FString& EditConditionString = MyProperty->GetMetaData(TEXT("EditCondition"));
|
||||
|
||||
// see if the property supports some kind of edit condition and this isn't the "parent" property of a static array
|
||||
const bool bIsStaticArrayParent = MyProperty->ArrayDim > 1 && GetArrayIndex() != -1;
|
||||
if (!EditConditionString.IsEmpty() && !bIsStaticArrayParent)
|
||||
{
|
||||
EditConditionExpression = EditConditionParser.Parse(EditConditionString);
|
||||
if (EditConditionExpression.IsValid())
|
||||
{
|
||||
EditConditionContext = MakeShareable(new FEditConditionContext(*this));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 182 KiB |
@@ -0,0 +1,58 @@
|
||||
# EditConditionHides
|
||||
|
||||
- **功能描述:** 在已经有EditCondition的情况下,指定该属性在EditCondition不满足的情况下隐藏起来。
|
||||
- **使用位置:** UPROPERTY
|
||||
- **元数据类型:** bool
|
||||
- **关联项:** [EditCondition](../EditCondition/EditCondition.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
在已经有EditCondition的情况下,指定该属性在EditCondition不满足的情况下隐藏起来。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(BlueprintType)
|
||||
class INSIDER_API UMyProperty_EditCondition_Test :public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Property)
|
||||
bool MyBool;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Property, meta = (EditConditionHides, EditCondition = "MyBool"))
|
||||
int32 MyInt_EditCondition_Hides = 123;
|
||||
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PropertyExpression)
|
||||
int32 MyFirstInt = 123;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PropertyExpression)
|
||||
int32 MySecondInt = 123;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = PropertyExpression, meta = (EditConditionHides, EditCondition = "(MyFirstInt+MySecondInt)==500"))
|
||||
int32 MyInt_EditConditionExpression_Hides = 123;
|
||||
};
|
||||
```
|
||||
|
||||
## 测试效果:
|
||||
|
||||
下面的图中可以明显见到两个属性随着条件的满足显示了出来。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
其实就是加了个是否显示的判断。
|
||||
|
||||
```cpp
|
||||
bool FPropertyNode::IsOnlyVisibleWhenEditConditionMet() const
|
||||
{
|
||||
static const FName Name_EditConditionHides("EditConditionHides");
|
||||
if (Property.IsValid() && Property->HasMetaData(Name_EditConditionHides))
|
||||
{
|
||||
return HasEditCondition();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
```
|
@@ -0,0 +1,100 @@
|
||||
# EditInline
|
||||
|
||||
- **功能描述:** 为对象属性创建一个实例,并作为子对象。
|
||||
|
||||
- **使用位置:** UPROPERTY
|
||||
|
||||
- **引擎模块:** DetailsPanel
|
||||
|
||||
- **元数据类型:** bool
|
||||
|
||||
- **关联项:** [NoEditInline](../NoEditInline.md), [AllowEditInlineCustomization](../AllowEditInlineCustomization/AllowEditInlineCustomization.md), [ForceInlineRow](../ForceInlineRow/ForceInlineRow.md)
|
||||
|
||||
UPROPERTY:[Instanced](../../../Specifier/UPROPERTY/Instance/Instanced/Instanced.md)
|
||||
|
||||
- **常用程度:** ★★★
|
||||
|
||||
为对象属性创建一个实例,并作为子对象。
|
||||
|
||||
也可以手动设置。如果UClass上有EditInlineNew,但是属性上没有Instanced,这个时候可以手动的设置EditInline然后通过自己手动赋值对象引用属性来使得这个对象可以直接编辑。
|
||||
|
||||
和ShowInnerProperties是否等价?EditInline在对象容器(Array,Map,Set)的情况下,也可以使用。但ShowInnerProperties只能在单个对象属性上生效。
|
||||
|
||||
可以设置在Struct上?看源码里也有该设置。但其实并没有效果。
|
||||
|
||||
## 示例代码:
|
||||
|
||||
```cpp
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
UMyProperty_EditInline_Sub* MyObject;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (EditInline))
|
||||
UMyProperty_EditInline_Sub* MyObject_EditInline;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (NoEditInline))
|
||||
UMyProperty_EditInline_Sub* MyObject_NoEditInline;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
TArray<UMyProperty_EditInline_Sub*> MyObjectArray;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (EditInline))
|
||||
TArray<UMyProperty_EditInline_Sub*> MyObjectArray_EditInline;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (NoEditInline))
|
||||
TArray<UMyProperty_EditInline_Sub*> MyObjectArray_NoEditInline;
|
||||
```
|
||||
|
||||
## 蓝图效果:
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
会相应的设置EPropertyNodeFlags::EditInlineNew。
|
||||
|
||||
```cpp
|
||||
void FPropertyNode::InitNode(const FPropertyNodeInitParams& InitParams)
|
||||
{
|
||||
// we are EditInlineNew if this property has the flag, or if inside a container that has the flag.
|
||||
bIsEditInlineNew = GotReadAddresses && bIsObjectOrInterface && !MyProperty->HasMetaData(Name_NoEditInline) &&
|
||||
(MyProperty->HasMetaData(Name_EditInline) || (bIsInsideContainer && OwnerProperty->HasMetaData(Name_EditInline)));
|
||||
bShowInnerObjectProperties = bIsObjectOrInterface && MyProperty->HasMetaData(Name_ShowInnerProperties);
|
||||
|
||||
if (bIsEditInlineNew)
|
||||
{
|
||||
SetNodeFlags(EPropertyNodeFlags::EditInlineNew, true);
|
||||
}
|
||||
else if (bShowInnerObjectProperties)
|
||||
{
|
||||
SetNodeFlags(EPropertyNodeFlags::ShowInnerObjectProperties, true);
|
||||
}
|
||||
}
|
||||
|
||||
bool SPropertyEditorEditInline::Supports( const FPropertyNode* InTreeNode, int32 InArrayIdx )
|
||||
{
|
||||
return InTreeNode
|
||||
&& InTreeNode->HasNodeFlags(EPropertyNodeFlags::EditInlineNew)
|
||||
&& InTreeNode->FindObjectItemParent()
|
||||
&& !InTreeNode->IsPropertyConst();
|
||||
}
|
||||
|
||||
void FItemPropertyNode::InitExpansionFlags(void)
|
||||
{
|
||||
FProperty* MyProperty = GetProperty();
|
||||
|
||||
if (TSharedPtr<FPropertyNode>& ValueNode = GetOrCreateOptionalValueNode())
|
||||
{
|
||||
// This is a set optional, so check its SetValue instead.
|
||||
MyProperty = ValueNode->GetProperty();
|
||||
}
|
||||
|
||||
bool bExpandableType = CastField<FStructProperty>(MyProperty)
|
||||
|| (CastField<FArrayProperty>(MyProperty) || CastField<FSetProperty>(MyProperty) || CastField<FMapProperty>(MyProperty));
|
||||
|
||||
if (bExpandableType
|
||||
|| HasNodeFlags(EPropertyNodeFlags::EditInlineNew)
|
||||
|| HasNodeFlags(EPropertyNodeFlags::ShowInnerObjectProperties)
|
||||
|| (MyProperty->ArrayDim > 1 && ArrayIndex == -1))
|
||||
{
|
||||
SetNodeFlags(EPropertyNodeFlags::CanBeExpanded, true);
|
||||
}
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 102 KiB |
@@ -0,0 +1,154 @@
|
||||
# ForceInlineRow
|
||||
|
||||
- **功能描述:** 强制TMap属性里的结构key和其他Value合并到同一行来显示
|
||||
- **使用位置:** UPROPERTY
|
||||
- **元数据类型:** bool
|
||||
- **关联项:** [EditInline](../EditInline/EditInline.md)
|
||||
- **常用程度:** ★
|
||||
|
||||
强制TMap属性里的结构key和其他Value合并到同一行来显示。这里要注意的点是:
|
||||
|
||||
- 本属性是TMap属性,这样才有Key。TArray或TSet是没有用的。
|
||||
- FStruct作为Key,这样源码里的机制才能生效,因为判断的就是Key Property
|
||||
- 该FStruct有注册相关的IPropertyTypeCustomization,这样才能自定义该结构的显示UI
|
||||
- 该IPropertyTypeCustomization的ShouldInlineKey返回false(默认就是),否则true的话则不管有没有标ForceInlineRow,都会合并成一行
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
TMap<FMyCommonStruct, int32> MyStructMap;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ForceInlineRow))
|
||||
TMap<FMyCommonStruct, int32> MyStructMap_ForceInlineRow;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
TMap<int32, FMyCommonStruct> MyStructMap2;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ForceInlineRow))
|
||||
TMap<int32, FMyCommonStruct> MyStructMap_ForceInlineRow2;
|
||||
|
||||
|
||||
|
||||
void FMyCommonStructCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils)
|
||||
{
|
||||
HeaderRow.NameContent()[SNew(STextBlock).Text(INVTEXT("This is MyCommonStruct"))];
|
||||
|
||||
TSharedPtr<IPropertyHandle> IntPropertyHandle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FMyCommonStruct, MyInt));
|
||||
TSharedPtr<IPropertyHandle> StringPropertyHandle = PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FMyCommonStruct, MyString));
|
||||
|
||||
HeaderRow.ValueContent()
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
+ SHorizontalBox::Slot()
|
||||
.Padding(5.0f, 0.0f).AutoWidth()
|
||||
[
|
||||
IntPropertyHandle->CreatePropertyNameWidget()
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.Padding(5.0f, 0.0f).AutoWidth()
|
||||
[
|
||||
IntPropertyHandle->CreatePropertyValueWidget()
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.Padding(5.0f, 0.0f).AutoWidth()
|
||||
[
|
||||
StringPropertyHandle->CreatePropertyNameWidget()
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.Padding(5.0f, 0.0f).AutoWidth()
|
||||
[
|
||||
StringPropertyHandle->CreatePropertyValueWidget()
|
||||
]
|
||||
];
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## 测试效果:
|
||||
|
||||
可以见到MyStructMap的数据项展示就分为了两行。而带有ForceInlineRow之后,数据项UI就合并为一行,显得更加的简洁。
|
||||
|
||||
在下面也特别观察到如果把FStruct作为Value,则是没有这个区别的。
|
||||
|
||||

|
||||
|
||||
假如不注册FMyCommonStruct相应的IPropertyTypeCustomization的话,则结构的属性UI采用默认方式显示,则都是分为两行。
|
||||
|
||||

|
||||
|
||||
而假如FMyCommonStruct的IPropertyTypeCustomization的ShouldInlineKey返回true,则会导致即使没有ForceInlineRow也会把该拥有该结构作为Key的属性给都合并为一行显示,这个时候就失去ForceInlineRow的作用和区别了。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
该部分逻辑也同样处于在FDetailPropertyRow的构造函数创建过程中,判断是否有GetPropertyKeyNode,则其实是在要求TMap属性。
|
||||
|
||||
接着作为Key的类型,如果是UObject*,则因为NeedsKeyNode一直返回false,则无论如何都会进入MakePropertyEditor的分支。
|
||||
|
||||
因此此项测试的类型其实是Struct,这样就必须依赖bInlineRow 和FoundPropertyCustomisation 的配合。这个时候就必须有IPropertyTypeCustomization才会进入分支,而且如果IPropertyTypeCustomization::ShouldInlineKey()返回true,则就不管属性上的ForceInlineRow如何,都会进入分支。否则就靠属性上的ForceInlineRow,这个时候才是这个Meta发挥作用的时候。
|
||||
|
||||
```cpp
|
||||
FDetailPropertyRow::FDetailPropertyRow(TSharedPtr<FPropertyNode> InPropertyNode, TSharedRef<FDetailCategoryImpl> InParentCategory, TSharedPtr<FComplexPropertyNode> InExternalRootNode)
|
||||
{
|
||||
if (PropertyNode->GetPropertyKeyNode().IsValid())
|
||||
{
|
||||
TSharedPtr<IPropertyTypeCustomization> FoundPropertyCustomisation = GetPropertyCustomization(PropertyNode->GetPropertyKeyNode().ToSharedRef(), ParentCategory.Pin().ToSharedRef());
|
||||
|
||||
bool bInlineRow = FoundPropertyCustomisation != nullptr ? FoundPropertyCustomisation->ShouldInlineKey() : false;
|
||||
|
||||
static FName InlineKeyMeta("ForceInlineRow");
|
||||
bInlineRow |= InPropertyNode->GetParentNode()->GetProperty()->HasMetaData(InlineKeyMeta);
|
||||
|
||||
// Only create the property editor if it's not a struct or if it requires to be inlined (and has customization)
|
||||
if (!NeedsKeyNode(PropertyNodeRef, InParentCategory) || (bInlineRow && FoundPropertyCustomisation != nullptr))
|
||||
{
|
||||
CachedKeyCustomTypeInterface = FoundPropertyCustomisation;
|
||||
|
||||
MakePropertyEditor(PropertyNode->GetPropertyKeyNode().ToSharedRef(), Utilities, PropertyKeyEditor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FDetailPropertyRow::NeedsKeyNode(TSharedRef<FPropertyNode> InPropertyNode, TSharedRef<FDetailCategoryImpl> InParentCategory)
|
||||
{
|
||||
FStructProperty* KeyStructProp = CastField<FStructProperty>(InPropertyNode->GetPropertyKeyNode()->GetProperty());
|
||||
return KeyStructProp != nullptr;
|
||||
}
|
||||
```
|
||||
|
||||
源码里使用的例子:
|
||||
|
||||
在源码里搜索发现到该例子,但实际上其实这里HLODSetups上的ForceInlineRow并不能起作用。
|
||||
|
||||
```cpp
|
||||
USTRUCT()
|
||||
struct FRuntimePartitionDesc
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
/** Partition class */
|
||||
UPROPERTY(EditAnywhere, Category = RuntimeSettings)
|
||||
TSubclassOf<URuntimePartition> Class;
|
||||
|
||||
/** Name for this partition, used to map actors to it through the Actor.RuntimeGrid property */
|
||||
UPROPERTY(EditAnywhere, Category = RuntimeSettings, Meta = (EditCondition = "Class != nullptr", HideEditConditionToggle))
|
||||
FName Name;
|
||||
|
||||
/** Main partition object */
|
||||
UPROPERTY(VisibleAnywhere, Category = RuntimeSettings, Instanced, Meta = (EditCondition = "Class != nullptr", HideEditConditionToggle, NoResetToDefault, TitleProperty = "Name"))
|
||||
TObjectPtr<URuntimePartition> MainLayer;
|
||||
|
||||
/** HLOD setups used by this partition, one for each layers in the hierarchy */
|
||||
UPROPERTY(EditAnywhere, Category = RuntimeSettings, Meta = (EditCondition = "Class != nullptr", HideEditConditionToggle, ForceInlineRow))
|
||||
TArray<FRuntimePartitionHLODSetup> HLODSetups;
|
||||
#endif
|
||||
|
||||
#if WITH_EDITOR
|
||||
void UpdateHLODPartitionLayers();
|
||||
#endif
|
||||
};
|
||||
```
|
After Width: | Height: | Size: 103 KiB |
After Width: | Height: | Size: 74 KiB |
After Width: | Height: | Size: 103 KiB |
@@ -0,0 +1,15 @@
|
||||
# HideBehind
|
||||
|
||||
- **功能描述:** 只在指定的属性为true或不为空的时候本属性才显示
|
||||
- **使用位置:** UPROPERTY
|
||||
- **引擎模块:** DetailsPanel
|
||||
- **元数据类型:** string="abc"
|
||||
- **限制类型:** Foliage模块中
|
||||
- **常用程度:** ★
|
||||
|
||||
```cpp
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Placement, meta=(UIMin = 0, ClampMin = 0, UIMax = 359, ClampMax = 359, HideBehind="AlignToNormal"))
|
||||
float AlignMaxAngle;
|
||||
```
|
||||
|
||||
只在Foliage里用到,其实用EditCondition就可以达到同样的效果了。
|
@@ -0,0 +1,9 @@
|
||||
# HideCategories
|
||||
|
||||
- **功能描述:** 隐藏的类别
|
||||
- **使用位置:** UCLASS
|
||||
- **引擎模块:** DetailsPanel
|
||||
- **元数据类型:** strings="a,b,c"
|
||||
Related To UCLASS: ShowCategories (../../Specifier/UCLASS/ShowCategories.md)
|
||||
- **关联项:** ShowCategories (ShowCategories.md)
|
||||
- **常用程度:** ★★★
|
After Width: | Height: | Size: 75 KiB |
@@ -0,0 +1,84 @@
|
||||
# HideEditConditionToggle
|
||||
|
||||
- **功能描述:** 用在使用EditCondition的属性上,表示该属性不想要其EditCondition用到的属性被隐藏起来。
|
||||
- **使用位置:** UPROPERTY
|
||||
- **引擎模块:** DetailsPanel
|
||||
- **元数据类型:** bool
|
||||
- **限制类型:** bool
|
||||
- **关联项:** [EditCondition](../EditCondition/EditCondition.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
用在使用EditCondition的属性上,表示该属性不想要其EditCondition用到的属性被隐藏起来。和InlineEditConditionToggle是有相反的作用。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = InlineEditConditionToggle, meta = (InlineEditConditionToggle))
|
||||
bool MyBool_Inline;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = InlineEditConditionToggle, meta = (EditCondition = "MyBool_Inline"))
|
||||
int32 MyInt_EditCondition_UseInline = 123;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = InlineEditConditionToggle, meta = (HideEditConditionToggle,EditCondition = "MyBool_Inline"))
|
||||
int32 MyInt_EditCondition_UseInline_Hide = 123;
|
||||
};
|
||||
```
|
||||
|
||||
## 测试效果:
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
判断如果有HideEditConditionToggle,就支持不支持当前行有单选框的按钮。
|
||||
|
||||
```cpp
|
||||
|
||||
bool FPropertyNode::SupportsEditConditionToggle() const
|
||||
{
|
||||
if (!Property.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FProperty* MyProperty = Property.Get();
|
||||
|
||||
static const FName Name_HideEditConditionToggle("HideEditConditionToggle");
|
||||
if (EditConditionExpression.IsValid() && !Property->HasMetaData(Name_HideEditConditionToggle))
|
||||
{
|
||||
const FBoolProperty* ConditionalProperty = EditConditionContext->GetSingleBoolProperty(EditConditionExpression);
|
||||
if (ConditionalProperty != nullptr)
|
||||
{
|
||||
// There are 2 valid states for inline edit conditions:
|
||||
// 1. The property is marked as editable and has InlineEditConditionToggle set.
|
||||
// 2. The property is not marked as editable and does not have InlineEditConditionToggle set.
|
||||
// In both cases, the original property will be hidden and only show up as a toggle.
|
||||
|
||||
static const FName Name_InlineEditConditionToggle("InlineEditConditionToggle");
|
||||
const bool bIsInlineEditCondition = ConditionalProperty->HasMetaData(Name_InlineEditConditionToggle);
|
||||
const bool bIsEditable = ConditionalProperty->HasAllPropertyFlags(CPF_Edit);
|
||||
|
||||
if (bIsInlineEditCondition == bIsEditable)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (bIsInlineEditCondition && !bIsEditable)
|
||||
{
|
||||
UE_LOG(LogPropertyNode, Warning, TEXT("Property being used as inline edit condition is not editable, but has redundant InlineEditConditionToggle flag. Field \"%s\" in class \"%s\"."), *ConditionalProperty->GetNameCPP(), *Property->GetOwnerStruct()->GetName());
|
||||
return true;
|
||||
}
|
||||
|
||||
// The property is already shown, and not marked as inline edit condition.
|
||||
if (!bIsInlineEditCondition && bIsEditable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
```
|
@@ -0,0 +1,69 @@
|
||||
# HideInDetailPanel
|
||||
|
||||
- **功能描述:** 在Actor的事件面板里隐藏该动态多播委托属性。
|
||||
- **使用位置:** UPROPERTY
|
||||
- **引擎模块:** DetailsPanel
|
||||
- **元数据类型:** bool
|
||||
- **限制类型:** Actor里的动态多播委托
|
||||
- **常用程度:** ★★
|
||||
|
||||
在Actor的事件面板里隐藏该动态多播委托属性。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(BlueprintType,Blueprintable)
|
||||
class INSIDER_API AMyProperty_HideInDetailPanel :public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnMyHideTestEvent);
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Event")
|
||||
FOnMyHideTestEvent MyEvent;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Event", meta = (HideInDetailPanel))
|
||||
FOnMyHideTestEvent MyEvent_HideInDetailPanel;
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## 测试效果:
|
||||
|
||||
测试步骤是在蓝图里创建AMyProperty_HideInDetailPanel 的子类,然后观察Event的显示情况。
|
||||
|
||||
可见MyEvent会显示在Class Defautls里的Events,而MyEvent_HideInDetailPanel则没有显示。
|
||||
|
||||
不过MyEvent_HideInDetailPanel依然是可以在蓝图里进行绑定,只不过默认没显示在UI上而已。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
先判断没有这个标记,然后创建相应的UI控件。
|
||||
|
||||
```cpp
|
||||
void FActorDetails::AddEventsCategory(IDetailLayoutBuilder& DetailBuilder)
|
||||
{
|
||||
IDetailCategoryBuilder& EventsCategory = DetailBuilder.EditCategory("Events", FText::GetEmpty(), ECategoryPriority::Uncommon);
|
||||
static const FName HideInDetailPanelName("HideInDetailPanel");
|
||||
|
||||
// Find all the Multicast delegate properties and give a binding button for them
|
||||
for (TFieldIterator<FMulticastDelegateProperty> PropertyIt(Actor->GetClass(), EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt)
|
||||
{
|
||||
FMulticastDelegateProperty* Property = *PropertyIt;
|
||||
|
||||
// Only show BP assiangable, non-hidden delegates
|
||||
if (!Property->HasAnyPropertyFlags(CPF_Parm) && Property->HasAllPropertyFlags(CPF_BlueprintAssignable) && !Property->HasMetaData(HideInDetailPanelName))
|
||||
{}
|
||||
}
|
||||
}
|
||||
|
||||
void FBlueprintDetails::AddEventsCategory(IDetailLayoutBuilder& DetailBuilder, FName PropertyName, UClass* PropertyClass)
|
||||
{
|
||||
static const FName HideInDetailPanelName("HideInDetailPanel");
|
||||
// Check for multicast delegates that we can safely assign
|
||||
if ( !Property->HasAnyPropertyFlags(CPF_Parm) && Property->HasAllPropertyFlags(CPF_BlueprintAssignable) &&
|
||||
!Property->HasMetaData(HideInDetailPanelName) )
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 190 KiB |
@@ -0,0 +1,10 @@
|
||||
# IgnoreCategoryKeywordsInSubclasses
|
||||
|
||||
- **功能描述:** 用于让一个类的首个子类忽略所有继承的 ShowCategories 和 HideCategories 说明符。
|
||||
- **使用位置:** UCLASS
|
||||
- **引擎模块:** DetailsPanel
|
||||
- **元数据类型:** bool
|
||||
Related To UCLASS: ComponentWrapperClass (../../Specifier/UCLASS/ComponentWrapperClass.md)
|
||||
- **常用程度:** ★
|
||||
|
||||
和ComponentWrapperClass相互关联
|
After Width: | Height: | Size: 90 KiB |
@@ -0,0 +1,88 @@
|
||||
# InlineEditConditionToggle
|
||||
|
||||
- **功能描述:** 使这个bool属性在被用作EditCondition的时候内联到对方的属性行里成为一个单选框,而不是自己成为一个编辑行。
|
||||
- **使用位置:** UPROPERTY
|
||||
- **元数据类型:** bool
|
||||
- **限制类型:** bool
|
||||
- **关联项:** [EditCondition](../EditCondition/EditCondition.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
使这个bool属性在被用作EditCondition的时候内联到对方的属性行里成为一个单选框,而不是自己成为一个编辑行。
|
||||
|
||||
虽然EditCondition支持别的类型属性或者是表达式,但是这个InlineEditConditionToggle只支持bool属性。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = InlineEditConditionToggle, meta = (InlineEditConditionToggle))
|
||||
bool MyBool_Inline;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = InlineEditConditionToggle, meta = (EditCondition = "MyBool_Inline"))
|
||||
int32 MyInt_EditCondition_UseInline = 123;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = InlineEditConditionToggle)
|
||||
int32 MyThirdInt_Inline = 123;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = InlineEditConditionToggle, meta = (EditCondition = "MyThirdInt_Inline>200"))
|
||||
int32 MyInt_EditConditionExpression_UseInline = 123;
|
||||
```
|
||||
|
||||
## 测试效果:
|
||||
|
||||
可见MyBool_Inline变成了单选框。而MyThirdInt_Inline就没有被隐藏掉。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
可以看到用这个判断是否支持出现单选框。
|
||||
|
||||
```cpp
|
||||
|
||||
bool FPropertyNode::SupportsEditConditionToggle() const
|
||||
{
|
||||
if (!Property.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FProperty* MyProperty = Property.Get();
|
||||
|
||||
static const FName Name_HideEditConditionToggle("HideEditConditionToggle");
|
||||
if (EditConditionExpression.IsValid() && !Property->HasMetaData(Name_HideEditConditionToggle))
|
||||
{
|
||||
const FBoolProperty* ConditionalProperty = EditConditionContext->GetSingleBoolProperty(EditConditionExpression);
|
||||
if (ConditionalProperty != nullptr)
|
||||
{
|
||||
// There are 2 valid states for inline edit conditions:
|
||||
// 1. The property is marked as editable and has InlineEditConditionToggle set.
|
||||
// 2. The property is not marked as editable and does not have InlineEditConditionToggle set.
|
||||
// In both cases, the original property will be hidden and only show up as a toggle.
|
||||
|
||||
static const FName Name_InlineEditConditionToggle("InlineEditConditionToggle");
|
||||
const bool bIsInlineEditCondition = ConditionalProperty->HasMetaData(Name_InlineEditConditionToggle);
|
||||
const bool bIsEditable = ConditionalProperty->HasAllPropertyFlags(CPF_Edit);
|
||||
|
||||
if (bIsInlineEditCondition == bIsEditable)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (bIsInlineEditCondition && !bIsEditable)
|
||||
{
|
||||
UE_LOG(LogPropertyNode, Warning, TEXT("Property being used as inline edit condition is not editable, but has redundant InlineEditConditionToggle flag. Field \"%s\" in class \"%s\"."), *ConditionalProperty->GetNameCPP(), *Property->GetOwnerStruct()->GetName());
|
||||
return true;
|
||||
}
|
||||
|
||||
// The property is already shown, and not marked as inline edit condition.
|
||||
if (!bIsInlineEditCondition && bIsEditable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
```
|
@@ -0,0 +1,130 @@
|
||||
# MaxPropertyDepth
|
||||
|
||||
- **功能描述:** 指定对象或结构在细节面板里展开的层数。
|
||||
- **使用位置:** UPROPERTY
|
||||
- **引擎模块:** DetailsPanel
|
||||
- **元数据类型:** int32
|
||||
- **限制类型:** 对象或结构属性
|
||||
- **常用程度:** ★
|
||||
|
||||
指定对象或结构在细节面板里展开的层数。
|
||||
|
||||
- 默认是没有限制的,可以一直递归展开到最深层次字段。
|
||||
- 如果对象的子对象再有子对象,这样递归很多层级,可能我们会想要限制不想展开太深,因此我们可以指定一个层级限制。
|
||||
- 取值-1表示没有限制,0表示完全不展开,>0表示限制的层数。
|
||||
- 源码里没有找到例子,但却是可以工作的。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
USTRUCT(BlueprintType)
|
||||
struct INSIDER_API FMyStructDepth1
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
int32 MyInt1 = 123;
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
FString MyString1;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct INSIDER_API FMyStructDepth2
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
FMyStructDepth1 MyStruct1;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct INSIDER_API FMyStructDepth3
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
FMyStructDepth2 MyStruct2;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct INSIDER_API FMyStructDepth4
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
FMyStructDepth3 MyStruct3;
|
||||
};
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class INSIDER_API UMyProperty_MaxPropertyDepth :public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
FMyStructDepth4 MyStruct;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite,meta=(MaxPropertyDepth=2))
|
||||
FMyStructDepth4 MyStruct_Depth;
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## 测试效果:
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
在每个FPropertyNode构建子节点的时候,检查一下当前的MaxChildDepthAllowed ,超过了就不继续往下构建。
|
||||
|
||||
```cpp
|
||||
/** Safety Value representing Depth in the property tree used to stop diabolical topology cases
|
||||
* -1 = No limit on children
|
||||
* 0 = No more children are allowed. Do not process child nodes
|
||||
* >0 = A limit has been set by the property and will tick down for successive children
|
||||
*/
|
||||
int32 MaxChildDepthAllowed;
|
||||
|
||||
void FPropertyNode::InitNode(const FPropertyNodeInitParams& InitParams)
|
||||
{
|
||||
|
||||
//Get the property max child depth
|
||||
static const FName Name_MaxPropertyDepth("MaxPropertyDepth");
|
||||
if (Property->HasMetaData(Name_MaxPropertyDepth))
|
||||
{
|
||||
int32 NewMaxChildDepthAllowed = Property->GetIntMetaData(Name_MaxPropertyDepth);
|
||||
//Ensure new depth is valid. Otherwise just let the parent specified value stand
|
||||
if (NewMaxChildDepthAllowed > 0)
|
||||
{
|
||||
//if there is already a limit on the depth allowed, take the minimum of the allowable depths
|
||||
if (MaxChildDepthAllowed >= 0)
|
||||
{
|
||||
MaxChildDepthAllowed = FMath::Min(MaxChildDepthAllowed, NewMaxChildDepthAllowed);
|
||||
}
|
||||
else
|
||||
{
|
||||
//no current limit, go ahead and take the new limit
|
||||
MaxChildDepthAllowed = NewMaxChildDepthAllowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FPropertyNode::RebuildChildren()
|
||||
{
|
||||
if (MaxChildDepthAllowed != 0)
|
||||
{
|
||||
//the case where we don't want init child nodes is when an Item has children that we don't want to display
|
||||
//the other option would be to make each node "Read only" under that item.
|
||||
//The example is a material assigned to a static mesh.
|
||||
if (HasNodeFlags(EPropertyNodeFlags::CanBeExpanded) && (ChildNodes.Num() == 0))
|
||||
{
|
||||
InitChildNodes();
|
||||
if (ExpandedPropertyItemSet.Size() > 0)
|
||||
{
|
||||
FPropertyNodeUtils::SetExpandedItems(ThisAsSharedRef, ExpandedPropertyItemSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 41 KiB |
@@ -0,0 +1,18 @@
|
||||
# NoEditInline
|
||||
|
||||
- **功能描述:** Object properties pointing to an UObject instance whos class is marked editinline will not show their properties inline in property windows. Useful for getting actor components to appear in the component tree but not inline in the root actor details panel.
|
||||
- **使用位置:** UPROPERTY
|
||||
- **元数据类型:** bool
|
||||
- **限制类型:** UObject*
|
||||
- **关联项:** EditInline (EditInline.md)
|
||||
|
||||
对象引用默认就不能EditInline,因此也不需要额外再加上这个。除非Instanced之后?
|
||||
|
||||
结构属性默认就可以EditInline,加上这个后也没有作用,因此也不需要加上这个。
|
||||
|
||||
在源码中只找到:
|
||||
|
||||
```cpp
|
||||
UPROPERTY(VisibleAnywhere, Category = "Connection Point", meta = (NoEditInline))
|
||||
FLinearColor Color = FLinearColor::Black;
|
||||
```
|
@@ -0,0 +1,46 @@
|
||||
# NoResetToDefault
|
||||
|
||||
- **功能描述:** 禁用和隐藏属性在细节面板上的“重置”功能。
|
||||
- **使用位置:** UPROPERTY
|
||||
- **引擎模块:** DetailsPanel
|
||||
- **元数据类型:** bool
|
||||
- **常用程度:** ★★★
|
||||
|
||||
禁用和隐藏属性在细节面板上的“重置”功能。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite,Category=ResetToDefaultTest)
|
||||
int32 MyInt_Default = 123;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite,Category=ResetToDefaultTest, meta = (NoResetToDefault))
|
||||
int32 MyInt_NoResetToDefault = 123;
|
||||
```
|
||||
|
||||
## 测试效果:
|
||||
|
||||
可以发现默认的属性在改变值后,右侧会出现一个重置按钮,以便让属性重置回默认值。NoResetToDefault的作用就是去除这个功能。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
编辑器里会判断这个meta,如果没有则创建SResetToDefaultPropertyEditor。
|
||||
|
||||
```cpp
|
||||
bool SSingleProperty::GeneratePropertyCustomization()
|
||||
{
|
||||
if (!PropertyEditor->GetPropertyHandle()->HasMetaData(TEXT("NoResetToDefault")) && !bShouldHideResetToDefault)
|
||||
{
|
||||
HorizontalBox->AddSlot()
|
||||
.Padding( 2.0f )
|
||||
.AutoWidth()
|
||||
.VAlign( VAlign_Center )
|
||||
[
|
||||
SNew( SResetToDefaultPropertyEditor, PropertyEditor->GetPropertyHandle() )
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 83 KiB |
@@ -0,0 +1,15 @@
|
||||
# PrioritizeCategories
|
||||
|
||||
- **功能描述:** 把指定的属性目录优先显示在前面
|
||||
|
||||
- **使用位置:** UCLASS
|
||||
|
||||
- **引擎模块:** DetailsPanel
|
||||
|
||||
- **元数据类型:** strings="a,b,c"
|
||||
|
||||
- **关联项:**
|
||||
|
||||
UCLASS:[PrioritizeCategories](../../Specifier/UCLASS/Category/PrioritizeCategories/PrioritizeCategories.md)
|
||||
|
||||
- **常用程度:** ★★★
|
@@ -0,0 +1,16 @@
|
||||
# ReapplyCondition
|
||||
|
||||
- **功能描述:** // Properties that have a ReapplyCondition should be disabled behind the specified property when in reapply mode
|
||||
- **使用位置:** UPROPERTY
|
||||
- **引擎模块:** DetailsPanel
|
||||
- **元数据类型:** string="abc"
|
||||
- **常用程度:** ★
|
||||
|
||||
## 代码:
|
||||
|
||||
```cpp
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Placement, meta=(UIMin = 0, ClampMin = 0, UIMax = 359, ClampMax = 359, ReapplyCondition="ReapplyRandomPitchAngle"))
|
||||
float RandomPitchAngle;
|
||||
```
|
||||
|
||||
也只在Foliage中用到。
|
@@ -0,0 +1,24 @@
|
||||
# ShowCategories
|
||||
|
||||
- **功能描述:** 显示类别
|
||||
- **使用位置:** UCLASS
|
||||
- **元数据类型:** strings="a,b,c"
|
||||
- **关联项:** [HideCategories](HideCategories.md)
|
||||
|
||||
在类上面标记的ShowCategories,并不会保存到meta中去,只是用来抹除基类HideCategories的设置。因此meta里的ShowCategories是没有用到的。
|
||||
|
||||
```cpp
|
||||
//(BlueprintType = true, IncludePath = Class/Display/MyClass_HideCategories.h, IsBlueprintBase = true, ModuleRelativePath = Class/Display/MyClass_HideCategories.h)
|
||||
UCLASS(Blueprintable, ShowCategories = MyGroup1)
|
||||
class INSIDER_API UMyClass_ShowCategories :public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int Property_NotInGroup;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = MyGroup1)
|
||||
int Property_Group1;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyGroup2 | MyGroup22")
|
||||
int Property_Group222;
|
||||
};
|
||||
```
|
@@ -0,0 +1,7 @@
|
||||
# UsesHierarchy
|
||||
|
||||
- **功能描述:** 说明类使用层级数据。用于实例化“细节”面板中的层级编辑功能。
|
||||
- **使用位置:** UCLASS
|
||||
- **引擎模块:** DetailsPanel
|
||||
- **元数据类型:** bool
|
||||
- **常用程度:** 0
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,93 @@
|
||||
# bShowOnlyWhenTrue
|
||||
|
||||
- **功能描述:** 根据编辑器config配置文件里字段值来决定当前属性是否显示。
|
||||
- **使用位置:** UPROPERTY
|
||||
- **引擎模块:** DetailsPanel
|
||||
- **元数据类型:** string="abc"
|
||||
- **常用程度:** ★
|
||||
|
||||
根据编辑器config配置文件里字段值来决定当前属性是否显示。
|
||||
|
||||
- 这个编辑器config配置文件,指的是GEditorPerProjectIni,因此一般是Config\DefaultEditorPerProjectUserSettings.ini
|
||||
- 其中Section的名字是“UnrealEd.PropertyFilters”
|
||||
- 然后Key的值就可以定了。
|
||||
|
||||
在源码里没有找到使用的例子,但这依然是可以工作的。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
D:\github\GitWorkspace\Hello\Config\DefaultEditorPerProjectUserSettings.ini
|
||||
[UnrealEd.PropertyFilters]
|
||||
ShowMyInt=true
|
||||
ShowMyString=false
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class INSIDER_API UMyProperty_Show :public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 MyInt = 123;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (bShowOnlyWhenTrue = "ShowMyInt"))
|
||||
int32 MyInt_WithShowOnly = 123;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (bShowOnlyWhenTrue = "ShowMyString"))
|
||||
FString MyString_WithShowOnly;
|
||||
};
|
||||
```
|
||||
|
||||
## 测试结果:
|
||||
|
||||
可见MyString_WithShowOnly就没有显示出来,因为我们在DefaultEditorPerProjectUserSettings中配置了ShowMyString=false。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
就是取得config中的值用来决定属性框是否显示。
|
||||
|
||||
```cpp
|
||||
void FObjectPropertyNode::GetCategoryProperties(const TSet<UClass*>& ClassesToConsider, const FProperty* CurrentProperty, bool bShouldShowDisableEditOnInstance, bool bShouldShowHiddenProperties,
|
||||
const TSet<FName>& CategoriesFromBlueprints, TSet<FName>& CategoriesFromProperties, TArray<FName>& SortedCategories)
|
||||
{
|
||||
bool bMetaDataAllowVisible = true;
|
||||
const FString& ShowOnlyWhenTrueString = CurrentProperty->GetMetaData(Name_bShowOnlyWhenTrue);
|
||||
if (ShowOnlyWhenTrueString.Len())
|
||||
{
|
||||
//ensure that the metadata visibility string is actually set to true in order to show this property
|
||||
GConfig->GetBool(TEXT("UnrealEd.PropertyFilters"), *ShowOnlyWhenTrueString, bMetaDataAllowVisible, GEditorPerProjectIni);
|
||||
}
|
||||
|
||||
if (bMetaDataAllowVisible)
|
||||
{
|
||||
if (PropertyEditorHelpers::ShouldBeVisible(*this, CurrentProperty) && !HiddenCategories.Contains(CategoryName))
|
||||
{
|
||||
if (!CategoriesFromBlueprints.Contains(CategoryName) && !CategoriesFromProperties.Contains(CategoryName))
|
||||
{
|
||||
SortedCategories.AddUnique(CategoryName);
|
||||
}
|
||||
CategoriesFromProperties.Add(CategoryName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FCategoryPropertyNode::InitChildNodes()
|
||||
{
|
||||
bool bMetaDataAllowVisible = true;
|
||||
if (!bShowHiddenProperties)
|
||||
{
|
||||
static const FName Name_bShowOnlyWhenTrue("bShowOnlyWhenTrue");
|
||||
const FString& MetaDataVisibilityCheckString = It->GetMetaData(Name_bShowOnlyWhenTrue);
|
||||
if (MetaDataVisibilityCheckString.Len())
|
||||
{
|
||||
//ensure that the metadata visibility string is actually set to true in order to show this property
|
||||
// @todo Remove this
|
||||
GConfig->GetBool(TEXT("UnrealEd.PropertyFilters"), *MetaDataVisibilityCheckString, bMetaDataAllowVisible, GEditorPerProjectIni);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|