void FDestructibleMeshDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
		//we always hide bodysetup as it's not useful in this editor
		TSharedPtr<IPropertyHandle> BodySetupHandler = DetailBuilder.GetProperty("BodySetup");
		if (BodySetupHandler.IsValid())
		{
			DetailBuilder.HideProperty(BodySetupHandler);
		}
		
		//rest of customization is just moving stuff out of DefaultDestructibleParameters so it's nicer to view
		TSharedPtr<IPropertyHandle> DefaultParams = DetailBuilder.GetProperty("DefaultDestructibleParameters");
		if (DefaultParams.IsValid() == false)
		{
			return;
		}

		AddStructToDetails("Damage", "DefaultDestructibleParameters.DamageParameters", DetailBuilder);
		AddStructToDetails("Damage", "DefaultDestructibleParameters.AdvancedParameters", DetailBuilder, true, true);
		AddStructToDetails("Debris", "DefaultDestructibleParameters.DebrisParameters", DetailBuilder);
		AddStructToDetails("Flags", "DefaultDestructibleParameters.Flags", DetailBuilder);
		AddStructToDetails("HierarchyDepth", "DefaultDestructibleParameters.SpecialHierarchyDepths", DetailBuilder);
		AddStructToDetails("HierarchyDepth", "DefaultDestructibleParameters.DepthParameters", DetailBuilder, false, true);

		

		//hide the default params as we've taken everything out of it
		DetailBuilder.HideProperty(DefaultParams);
}
void FBlackboardDataDetails::CustomizeDetails( IDetailLayoutBuilder& DetailLayout )
{
	// First hide all keys
	DetailLayout.HideProperty(TEXT("Keys"));
	DetailLayout.HideProperty(TEXT("ParentKeys"));

	// Now show only the currently selected key
	bool bIsInherited = false;
	int32 CurrentSelection = INDEX_NONE;
	if(OnGetSelectedBlackboardItemIndex.IsBound())
	{
		CurrentSelection = OnGetSelectedBlackboardItemIndex.Execute(bIsInherited);
	}

	if(CurrentSelection >= 0)
	{
		TSharedPtr<IPropertyHandle> KeysHandle = bIsInherited ? DetailLayout.GetProperty(TEXT("ParentKeys")) : DetailLayout.GetProperty(TEXT("Keys"));
		check(KeysHandle.IsValid());
		uint32 NumChildKeys = 0;
		KeysHandle->GetNumChildren(NumChildKeys);
		if((uint32)CurrentSelection < NumChildKeys)
		{
			TSharedPtr<IPropertyHandle> KeyHandle = KeysHandle->GetChildHandle((uint32)CurrentSelection);

			IDetailCategoryBuilder& DetailCategoryBuilder = DetailLayout.EditCategory("Key");
			TSharedPtr<IPropertyHandle> EntryNameProperty = KeyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FBlackboardEntry, EntryName));
			DetailCategoryBuilder.AddCustomRow(LOCTEXT("EntryNameLabel", "Entry Name"))
			.NameContent()
			[
				EntryNameProperty->CreatePropertyNameWidget()
			]
			.ValueContent()
			[
				SNew(SHorizontalBox)
				.IsEnabled(true)
				+SHorizontalBox::Slot()
				[
					EntryNameProperty->CreatePropertyValueWidget()
				]
			];

#if WITH_EDITORONLY_DATA
// 			TSharedPtr<IPropertyHandle> EntryDescriptionHandle = ElementProperty->GetChildHandle("EntryDescription");
			TSharedPtr<IPropertyHandle> EntryDescriptionHandle = KeyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FBlackboardEntry, EntryDescription));

			DetailCategoryBuilder.AddProperty(EntryDescriptionHandle);
#endif

			TSharedPtr<IPropertyHandle> KeyTypeProperty = KeyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FBlackboardEntry, KeyType));
			DetailCategoryBuilder.AddProperty(KeyTypeProperty);

			TSharedPtr<IPropertyHandle> bInstanceSyncedProperty = KeyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FBlackboardEntry, bInstanceSynced));
			DetailCategoryBuilder.AddProperty(bInstanceSyncedProperty);
		}	
	}
}
/** IDetailCustomization interface */
void FSpeedTreeImportDataDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout)
{
	CachedDetailBuilder = &DetailLayout;
	TArray<TWeakObjectPtr<UObject>> EditingObjects;
	DetailLayout.GetObjectsBeingCustomized(EditingObjects);
	check(EditingObjects.Num() == 1);
	SpeedTreeImportData = Cast<USpeedTreeImportData>(EditingObjects[0].Get());
	if (SpeedTreeImportData == nullptr)
	{
		return;
	}

	//We have to hide FilePath category
	DetailLayout.HideCategory(FName(TEXT("File Path")));
	
	//Mesh category Must be the first category (Important)
	DetailLayout.EditCategory(FName(TEXT("Mesh")), FText::GetEmpty(), ECategoryPriority::Important);

	//Get the Materials category
	IDetailCategoryBuilder& MaterialsCategoryBuilder = DetailLayout.EditCategory(FName(TEXT("Materials")));
	TArray<TSharedRef<IPropertyHandle>> MaterialCategoryDefaultProperties;
	MaterialsCategoryBuilder.GetDefaultProperties(MaterialCategoryDefaultProperties);
	
	//We have to make the logic for vertex processing
	TSharedRef<IPropertyHandle> MakeMaterialsCheckProp = DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(USpeedTreeImportData, MakeMaterialsCheck));
	MakeMaterialsCheckProp->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FSpeedTreeImportDataDetails::OnForceRefresh));

	TSharedRef<IPropertyHandle> IncludeVertexProcessingCheckProp = DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(USpeedTreeImportData, IncludeVertexProcessingCheck));
	IncludeVertexProcessingCheckProp->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FSpeedTreeImportDataDetails::OnForceRefresh));

	//Hide all properties, we will show them in the correct order with the correct grouping
	for (TSharedRef<IPropertyHandle> Handle : MaterialCategoryDefaultProperties)
	{
		DetailLayout.HideProperty(Handle);
	}

	MaterialsCategoryBuilder.AddProperty(MakeMaterialsCheckProp);
	if (SpeedTreeImportData->MakeMaterialsCheck)
	{
		for (TSharedRef<IPropertyHandle> Handle : MaterialCategoryDefaultProperties)
		{
			const FString& MetaData = Handle->GetMetaData(TEXT("EditCondition"));
			if (MetaData.Compare(TEXT("MakeMaterialsCheck")) == 0 && IncludeVertexProcessingCheckProp->GetProperty() != Handle->GetProperty())
			{
				MaterialsCategoryBuilder.AddProperty(Handle);
			}
		}
		IDetailGroup& VertexProcessingGroup = MaterialsCategoryBuilder.AddGroup(FName(TEXT("VertexProcessingGroup")), LOCTEXT("VertexProcessingGroup_DisplayName", "Vertex Processing"), false, true);
		VertexProcessingGroup.AddPropertyRow(IncludeVertexProcessingCheckProp);
		for (TSharedRef<IPropertyHandle> Handle : MaterialCategoryDefaultProperties)
		{
			const FString& MetaData = Handle->GetMetaData(TEXT("EditCondition"));
			if (MetaData.Compare(TEXT("IncludeVertexProcessingCheck")) == 0)
			{
				VertexProcessingGroup.AddPropertyRow(Handle);
			}
		}
	}
}
void FSpriteDetailsCustomization::BuildTextureSection(IDetailCategoryBuilder& SpriteCategory, IDetailLayoutBuilder& DetailLayout)
{
	// Grab information about the material
	TSharedPtr<IPropertyHandle> DefaultMaterialProperty = DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UPaperSprite, DefaultMaterial));

	FText SourceTextureOverrideLabel;
	if (DefaultMaterialProperty.IsValid())
	{
		UObject* DefaultMaterialAsObject;
		if (DefaultMaterialProperty->GetValue(/*out*/ DefaultMaterialAsObject) == FPropertyAccess::Success)
		{
			if (UMaterialInterface* DefaultMaterialInterface = Cast<UMaterialInterface>(DefaultMaterialAsObject))
			{
				if (UMaterial* DefaultMaterial = DefaultMaterialInterface->GetMaterial())
				{
					// Get a list of sprite samplers
					TArray<const UMaterialExpressionSpriteTextureSampler*> SpriteSamplerExpressions;
					DefaultMaterial->GetAllExpressionsOfType(/*inout*/ SpriteSamplerExpressions);

					// Turn that into a set of labels
					for (const UMaterialExpressionSpriteTextureSampler* Sampler : SpriteSamplerExpressions)
					{
						if (!Sampler->SlotDisplayName.IsEmpty())
						{
							if (Sampler->bSampleAdditionalTextures)
							{
								AdditionalTextureLabels.FindOrAdd(Sampler->AdditionalSlotIndex) = Sampler->SlotDisplayName;
							}
							else
							{
								SourceTextureOverrideLabel = Sampler->SlotDisplayName;
							}
						}
					}
				}
			}
		}
	}

	// Create the base texture widget
	TSharedPtr<IPropertyHandle> SourceTextureProperty = DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UPaperSprite, SourceTexture));
	DetailLayout.HideProperty(SourceTextureProperty);
	SpriteCategory.AddCustomRow(SourceTextureProperty->GetPropertyDisplayName())
		.NameContent()
		[
			CreateTextureNameWidget(SourceTextureProperty, SourceTextureOverrideLabel)
		]
		.ValueContent()
		.MaxDesiredWidth(TOptional<float>())
		[
			SourceTextureProperty->CreatePropertyValueWidget()
		];

	// Create the additional textures widget
	TSharedPtr<IPropertyHandle> AdditionalSourceTexturesProperty = DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UPaperSprite, AdditionalSourceTextures));
	TSharedRef<FDetailArrayBuilder> AdditionalSourceTexturesBuilder = MakeShareable(new FDetailArrayBuilder(AdditionalSourceTexturesProperty.ToSharedRef()));
	AdditionalSourceTexturesBuilder->OnGenerateArrayElementWidget(FOnGenerateArrayElementWidget::CreateSP(this, &FSpriteDetailsCustomization::GenerateAdditionalTextureWidget));
	SpriteCategory.AddCustomBuilder(AdditionalSourceTexturesBuilder);
}
void FBodySetupDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder )
{
	// Customize collision section
	{
		if ( DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UBodySetup, DefaultInstance))->IsValidHandle() )
		{
			DetailBuilder.GetObjectsBeingCustomized(ObjectsCustomized);
			TSharedPtr<IPropertyHandle> BodyInstanceHandler = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UBodySetup, DefaultInstance));

			const bool bInPhat = ObjectsCustomized.Num() && (Cast<USkeletalBodySetup>(ObjectsCustomized[0].Get()) != nullptr);
			if (bInPhat)
			{
				TSharedRef<IPropertyHandle> AsyncEnabled = BodyInstanceHandler->GetChildHandle(GET_MEMBER_NAME_CHECKED(FBodyInstance, bUseAsyncScene)).ToSharedRef();
				AsyncEnabled->MarkHiddenByCustomization();
			}

			BodyInstanceCustomizationHelper = MakeShareable(new FBodyInstanceCustomizationHelper(ObjectsCustomized));
			BodyInstanceCustomizationHelper->CustomizeDetails(DetailBuilder, DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UBodySetup, DefaultInstance)));

			IDetailCategoryBuilder& CollisionCategory = DetailBuilder.EditCategory("Collision");
			DetailBuilder.HideProperty(BodyInstanceHandler);

			TSharedPtr<IPropertyHandle> CollisionTraceHandler = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UBodySetup, CollisionTraceFlag));
			DetailBuilder.HideProperty(CollisionTraceHandler);

			// add physics properties to physics category
			uint32 NumChildren = 0;
			BodyInstanceHandler->GetNumChildren(NumChildren);

			static const FName CollisionCategoryName(TEXT("Collision"));

			// add all properties of this now - after adding 
			for (uint32 ChildIndex=0; ChildIndex < NumChildren; ++ChildIndex)
			{
				TSharedPtr<IPropertyHandle> ChildProperty = BodyInstanceHandler->GetChildHandle(ChildIndex);
				FName CategoryName = FObjectEditorUtils::GetCategoryFName(ChildProperty->GetProperty());
				if (CategoryName == CollisionCategoryName)
				{
					CollisionCategory.AddProperty(ChildProperty);
				}
			}
		}
	}
}
void FConfigPropertyHelperDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
	TSharedPtr<IPropertyHandle> PropertyHandle = DetailBuilder.GetProperty("EditProperty");
	DetailBuilder.HideProperty(PropertyHandle);

	UObject* PropValue;
	PropertyHandle->GetValue(PropValue);
	OriginalProperty = CastChecked<UProperty>(PropValue);

	// Create a runtime UClass with the provided property as the only member. We will use this in the details view for the config hierarchy.
	ConfigEditorPropertyViewClass = NewObject<UClass>(GetTransientPackage(), TEXT("TempConfigEditorUClass"), RF_Public|RF_Standalone);

	// Keep a record of the UProperty we are looking to update
	ConfigEditorCopyOfEditProperty = DuplicateObject<UProperty>(OriginalProperty, ConfigEditorPropertyViewClass, PropValue->GetFName());
	ConfigEditorPropertyViewClass->ClassConfigName = OriginalProperty->GetOwnerClass()->ClassConfigName;
	ConfigEditorPropertyViewClass->SetSuperStruct(UObject::StaticClass());
	ConfigEditorPropertyViewClass->ClassFlags |= (CLASS_DefaultConfig | CLASS_Config);
	ConfigEditorPropertyViewClass->AddCppProperty(ConfigEditorCopyOfEditProperty);
	ConfigEditorPropertyViewClass->Bind();
	ConfigEditorPropertyViewClass->StaticLink(true);
	ConfigEditorPropertyViewClass->AssembleReferenceTokenStream();
	ConfigEditorPropertyViewClass->AddToRoot();
	
	// Cache the CDO for the object
	ConfigEditorPropertyViewCDO = ConfigEditorPropertyViewClass->GetDefaultObject(true);
	ConfigEditorPropertyViewCDO->AddToRoot();

	// Get access to all of the config files where this property is configurable.
	ConfigFilesHandle = DetailBuilder.GetProperty("ConfigFilePropertyObjects");
	DetailBuilder.HideProperty(ConfigFilesHandle);

	// Add the properties to a property table so we can edit these.
	IDetailCategoryBuilder& ConfigHierarchyCategory = DetailBuilder.EditCategory("ConfigHierarchy");
	ConfigHierarchyCategory.AddCustomRow(LOCTEXT("ConfigHierarchy", "ConfigHierarchy"))
	[
		// Create a property table with the values.
		ConstructPropertyTable(DetailBuilder)
	];

	// Listen for changes to the properties, we handle these by updating the ini file associated.
	FCoreUObjectDelegates::OnObjectPropertyChanged.AddSP(this, &FConfigPropertyHelperDetails::OnPropertyValueChanged);
}
Ejemplo n.º 7
0
void FDeviceProfileDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
	// Hide all the properties apart from the Console Variables.
	IDetailCategoryBuilder& Category = DetailBuilder.EditCategory("DeviceSettings");

	TSharedPtr<IPropertyHandle> DeviceTypeHandle = DetailBuilder.GetProperty("DeviceType");
	DetailBuilder.HideProperty(DeviceTypeHandle);

	TSharedPtr<IPropertyHandle> MeshLODSettingsHandle = DetailBuilder.GetProperty("MeshLODSettings");
	DetailBuilder.HideProperty(MeshLODSettingsHandle);

	TSharedPtr<IPropertyHandle> TextureLODSettingsHandle = DetailBuilder.GetProperty("TextureLODSettings");
	DetailBuilder.HideProperty(TextureLODSettingsHandle);

	// Setup the parent profile panel
	ParentProfileDetails = MakeShareable(new FDeviceProfileParentPropertyDetails(&DetailBuilder));
	ParentProfileDetails->CreateParentPropertyView();

	// Setup the console variable editor
	ConsoleVariablesDetails = MakeShareable(new FDeviceProfileConsoleVariablesPropertyDetails(&DetailBuilder));
	ConsoleVariablesDetails->CreateConsoleVariablesPropertyView();
}
void FMacGraphicsSwitchingSettingsDetails::CustomizeDetails( IDetailLayoutBuilder& DetailLayout )
{
	TSharedRef<IPropertyHandle> PreferredRendererPropertyHandle = DetailLayout.GetProperty("RendererID");
	DetailLayout.HideProperty("RendererID");

	bool bAllowMultiGPUs = IMacGraphicsSwitchingModule::Get().AllowMultipleGPUs();
	bool bAllowAutomaticGraphicsSwitching = IMacGraphicsSwitchingModule::Get().AllowAutomaticGraphicsSwitching();
	
	TSharedRef<IPropertyHandle> MultiGPUPropertyHandle = DetailLayout.GetProperty("bUseMultipleRenderers");
	if (!bAllowMultiGPUs)
	{
		MultiGPUPropertyHandle->SetValue(false);
		DetailLayout.HideProperty("bUseMultipleRenderers");
	}
	
	TSharedRef<IPropertyHandle> SwitchingPropertyHandle = DetailLayout.GetProperty("bAllowAutomaticGraphicsSwitching");
	if (!bAllowAutomaticGraphicsSwitching)
	{
		SwitchingPropertyHandle->SetValue(false);
		DetailLayout.HideProperty("bAllowAutomaticGraphicsSwitching");
	}
	
	IDetailCategoryBuilder& AccessorCategory = DetailLayout.EditCategory( "OpenGL" );
	AccessorCategory.AddCustomRow( LOCTEXT("PreferredRenderer", "Preferred Renderer").ToString() )
	.NameContent()
	[
		PreferredRendererPropertyHandle->CreatePropertyNameWidget()
	]
	.ValueContent()
	.MinDesiredWidth(113)
	.MaxDesiredWidth(113)
	[
		SNew(SMacGraphicsSwitchingWidget)
		.bLiveSwitching(false)
		.PreferredRendererPropertyHandle(PreferredRendererPropertyHandle)
	];
}
void FSourceCodeAccessSettingsDetails::CustomizeDetails( IDetailLayoutBuilder& DetailLayout )
{
	TSharedRef<IPropertyHandle> PreferredProviderPropertyHandle = DetailLayout.GetProperty("PreferredAccessor");
	DetailLayout.HideProperty("PreferredAccessor");

	// regenerate accessors list
	Accessors.Empty();

	const int32 FeatureCount = IModularFeatures::Get().GetModularFeatureImplementationCount("SourceCodeAccessor");
	for(int32 FeatureIndex = 0; FeatureIndex < FeatureCount; FeatureIndex++)
	{
		IModularFeature* Feature = IModularFeatures::Get().GetModularFeatureImplementation("SourceCodeAccessor", FeatureIndex);
		check(Feature);

		ISourceCodeAccessor& Accessor = *static_cast<ISourceCodeAccessor*>(Feature);
		if(Accessor.GetFName() != FName("None"))
		{
			Accessors.Add(MakeShareable(new FAccessorItem(Accessor.GetNameText(), Accessor.GetFName())));
		}
	}

	IDetailCategoryBuilder& AccessorCategory = DetailLayout.EditCategory( "Accessor" );
	AccessorCategory.AddCustomRow( LOCTEXT("PreferredAccessor", "Preferred Accessor").ToString() )
	.NameContent()
	[
		PreferredProviderPropertyHandle->CreatePropertyNameWidget()
	]
	.ValueContent()
	.MinDesiredWidth(113)
	.MaxDesiredWidth(113)
	[
		SNew(SComboBox< TSharedPtr<FAccessorItem>>)
		.ToolTipText(LOCTEXT("PreferredAccessorToolTip", "Choose the way to access source code."))
		.OptionsSource(&Accessors)
		.OnSelectionChanged(this, &FSourceCodeAccessSettingsDetails::OnSelectionChanged, PreferredProviderPropertyHandle)
		.ContentPadding(2)
		.OnGenerateWidget(this, &FSourceCodeAccessSettingsDetails::OnGenerateWidget)
		.Content()
		[
			SNew(STextBlock)
			.Text(this, &FSourceCodeAccessSettingsDetails::GetAccessorText)
			.Font( IDetailLayoutBuilder::GetDetailFont() )
		]
	];
}
void FTransitionPoseEvaluatorNodeDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder )
{	
	const TArray< TWeakObjectPtr<UObject> >& SelectedObjects = DetailBuilder.GetDetailsView().GetSelectedObjects();
	for (int32 ObjectIndex = 0; (EvaluatorNode == NULL) && (ObjectIndex < SelectedObjects.Num()); ++ObjectIndex)
	{
		const TWeakObjectPtr<UObject>& CurrentObject = SelectedObjects[ObjectIndex];
		if (CurrentObject.IsValid())
		{
			EvaluatorNode = Cast<UAnimGraphNode_TransitionPoseEvaluator>(CurrentObject.Get());
		}
	}

	IDetailCategoryBuilder& PoseCategory = DetailBuilder.EditCategory("Pose", LOCTEXT("PoseCategoryName", "Pose") );
	TSharedPtr<IPropertyHandle> FramesToCachePosePropety = DetailBuilder.GetProperty(TEXT("Node.FramesToCachePose"));

	//@TODO: CONDUIT: try both
	DetailBuilder.HideProperty(FramesToCachePosePropety);
	PoseCategory.AddProperty( FramesToCachePosePropety ).Visibility( TAttribute<EVisibility>( this, &FTransitionPoseEvaluatorNodeDetails::GetFramesToCachePoseVisibility ) );
}
void FTODAssetDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout)
{
	const IDetailsView& DetailView = DetailLayout.GetDetailsView();

	TWeakObjectPtr<UObject> InspectedObject;

	for (TWeakObjectPtr<UObject> inspObj : DetailView.GetSelectedObjects())
	{
		InspectedObject = inspObj;
		break;
	}

	UTODAsset* TODAsset = Cast<UTODAsset>(InspectedObject.Get());

	if (TODAsset)
	{
		for (TFieldIterator<UProperty> PropIt(TODAsset->GetClass()); PropIt; ++PropIt)
		{
			UProperty* prop = *PropIt;
			DetailLayout.HideProperty(prop->GetFName());
		}
	}

	FName CurrentPropertyName = TEXT("SunIntensityCurve");// NAME_None;
	//if (OnGetCurrentProperty.IsBound())
	//{
	//	CurrentPropertyName = OnGetCurrentProperty.Execute();
	//}
	if (CurrentPropertyName != NAME_None)
	{
		TSharedPtr<IPropertyHandle> PropHandle = DetailLayout.GetProperty(CurrentPropertyName);
		check(PropHandle.IsValid());


		IDetailCategoryBuilder& DetailCategoryBuilder = DetailLayout.EditCategory("Property Detail");
		DetailCategoryBuilder.AddProperty(PropHandle);
	}
}
void FTileSetDetailsCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailLayout)
{
	MyDetailLayout = &DetailLayout;
	
	for (const TWeakObjectPtr<UObject> SelectedObject : DetailLayout.GetSelectedObjects())
	{
		if (UPaperTileSet* TileSet = Cast<UPaperTileSet>(SelectedObject.Get()))
		{
			TileSetPtr = TileSet;
			break;
		}
	}

 	IDetailCategoryBuilder& TileSetCategory = DetailLayout.EditCategory("TileSet", FText::GetEmpty());

	// Add the width and height in cells of this tile set to the header
	TileSetCategory.HeaderContent
	(
		SNew(SBox)
		.HAlign(HAlign_Right)
		[
			SNew(SHorizontalBox)
			+SHorizontalBox::Slot()
			.Padding(FMargin(5.0f, 0.0f))
			.AutoWidth()
			[
				SNew(STextBlock)
				.Font(FEditorStyle::GetFontStyle("TinyText"))
				.Text(this, &FTileSetDetailsCustomization::GetCellDimensionHeaderText)
				.ColorAndOpacity(this, &FTileSetDetailsCustomization::GetCellDimensionHeaderColor)
				.ToolTipText(LOCTEXT("NumCellsTooltip", "Number of tile cells in this tile set"))
			]
		]
	);


	if (bIsEmbeddedInTileSetEditor)
	{
		// Hide the array to start with
		const FName MetadataArrayName = UPaperTileSet::GetPerTilePropertyName();
		TSharedPtr<IPropertyHandle> PerTileArrayProperty = DetailLayout.GetProperty(MetadataArrayName);
		DetailLayout.HideProperty(PerTileArrayProperty);
		// this array is potentially huge and has a costly validation overhead.  We only ever show one element in the array so there is no need to validate every element.
		PerTileArrayProperty->SetIgnoreValidation(true);

		if (SelectedSingleTileIndex != INDEX_NONE)
		{
			// Customize for the single tile being edited
			IDetailCategoryBuilder& SingleTileCategory = DetailLayout.EditCategory("SingleTileEditor", FText::GetEmpty());
			
			uint32 NumChildren;
			if ((PerTileArrayProperty->GetNumChildren(/*out*/ NumChildren) == FPropertyAccess::Success) && ((uint32)SelectedSingleTileIndex < NumChildren))
			{
				TSharedPtr<IPropertyHandle> OneTileEntry = PerTileArrayProperty->GetChildHandle(SelectedSingleTileIndex);
				SingleTileCategory.AddProperty(OneTileEntry)
					.ShouldAutoExpand(true);
			}

			// Add a display of the tile index being edited to the header
			const FText TileIndexHeaderText = FText::Format(LOCTEXT("SingleTileSectionHeader", "Editing Tile #{0}"), FText::AsNumber(SelectedSingleTileIndex));
			SingleTileCategory.HeaderContent
			(
				SNew(SBox)
				.HAlign(HAlign_Right)
				[
					SNew(SHorizontalBox)
					+SHorizontalBox::Slot()
					.Padding(FMargin(5.0f, 0.0f))
					.AutoWidth()
					[
						SNew(STextBlock)
						.Font(FEditorStyle::GetFontStyle("TinyText"))
						.Text(TileIndexHeaderText)
					]
				]
			);
		}
	}
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void FLandscapeEditorDetailCustomization_CopyPaste::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
	if (!IsToolActive("ToolSet_CopyPaste"))
	{
		return;
	}

	IDetailCategoryBuilder& ToolsCategory = DetailBuilder.EditCategory("Tool Settings");

	ToolsCategory.AddCustomRow("Copy Data to Gizmo")
	[
		SNew(SButton)
		.ToolTipText(LOCTEXT("CopyToGizmo.Tooltip", "Copies the data within the gizmo bounds to the gizmo taking into account any masking from selected regions."))
		.Text(LOCTEXT("CopyToGizmo", "Copy Data to Gizmo"))
		.HAlign(HAlign_Center)
		.OnClicked_Static(&FLandscapeEditorDetailCustomization_CopyPaste::OnCopyToGizmoButtonClicked)
	];

	ToolsCategory.AddCustomRow("Fit Gizmo to Selected Regions")
	[
		SNew(SButton)
		.ToolTipText(LOCTEXT("FitGizmoToSelection.Tooltip", "Positions and resizes the gizmo so that it completely encompasses all region selections."))
		.Text(LOCTEXT("FitGizmoToSelection", "Fit Gizmo to Selected Regions"))
		.HAlign(HAlign_Center)
		.OnClicked_Static(&FLandscapeEditorDetailCustomization_CopyPaste::OnFitGizmoToSelectionButtonClicked)
	];

	ToolsCategory.AddCustomRow("Fit Height Values to Gizmo Size")
	[
		SNew(SButton)
		.ToolTipText(LOCTEXT("FitHeightsToGizmo.Tooltip", "Scales the data in the gizmo to fit the gizmo's Z size"))
		.Text(LOCTEXT("FitHeightsToGizmo", "Fit Height Values to Gizmo Size"))
		.HAlign(HAlign_Center)
		.OnClicked_Static(&FLandscapeEditorDetailCustomization_CopyPaste::OnFitHeightsToGizmoButtonClicked)
	];

	ToolsCategory.AddCustomRow("Clear Gizmo Data")
	[
		SNew(SButton)
		.ToolTipText(LOCTEXT("ClearGizmoData.Tooltip", "Clears the gizmo of any copied data."))
		.Text(LOCTEXT("ClearGizmoData", "Clear Gizmo Data"))
		.HAlign(HAlign_Center)
		.OnClicked_Static(&FLandscapeEditorDetailCustomization_CopyPaste::OnClearGizmoDataButtonClicked)
	];


	IDetailGroup& GizmoImportExportGroup = ToolsCategory.AddGroup("Gizmo Import / Export", LOCTEXT("ImportExportTitle", "Gizmo Import / Export").ToString(), true);

	TSharedRef<IPropertyHandle> PropertyHandle_Heightmap = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULandscapeEditorObject, GizmoHeightmapFilenameString));
	DetailBuilder.HideProperty(PropertyHandle_Heightmap);
	GizmoImportExportGroup.AddPropertyRow(PropertyHandle_Heightmap)
	.CustomWidget()
	.NameContent()
	[
		PropertyHandle_Heightmap->CreatePropertyNameWidget()
	]
	.ValueContent()
	.MinDesiredWidth(250.0f)
	.MaxDesiredWidth(0)
	[
		SNew(SHorizontalBox)
		+ SHorizontalBox::Slot()
		[
			PropertyHandle_Heightmap->CreatePropertyValueWidget()
		]
		+ SHorizontalBox::Slot()
		.AutoWidth()
		//.Padding(0,0,12,0) // Line up with the other properties due to having no reset to default button
		[
			SNew(SButton)
			.ContentPadding(FMargin(4, 0))
			.Text(NSLOCTEXT("UnrealEd", "GenericOpenDialog", "..."))
			.OnClicked_Static(&FLandscapeEditorDetailCustomization_CopyPaste::OnGizmoHeightmapFilenameButtonClicked, PropertyHandle_Heightmap)
		]
	];

	TSharedRef<IPropertyHandle> PropertyHandle_ImportSize = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULandscapeEditorObject, GizmoImportSize));
	TSharedRef<IPropertyHandle> PropertyHandle_ImportSize_X = PropertyHandle_ImportSize->GetChildHandle("X").ToSharedRef();
	TSharedRef<IPropertyHandle> PropertyHandle_ImportSize_Y = PropertyHandle_ImportSize->GetChildHandle("Y").ToSharedRef();
	DetailBuilder.HideProperty(PropertyHandle_ImportSize);
	GizmoImportExportGroup.AddPropertyRow(PropertyHandle_ImportSize)
	.CustomWidget()
	.NameContent()
	[
		PropertyHandle_ImportSize->CreatePropertyNameWidget()
	]
	.ValueContent()
	[
		SNew(SHorizontalBox)
		+ SHorizontalBox::Slot()
		.FillWidth(1)
		[
			SNew(SNumericEntryBox<int32>)
			.LabelVAlign(VAlign_Center)
			.Font(DetailBuilder.GetDetailFont())
			.MinValue(1)
			.MaxValue(8192)
			.MinSliderValue(1)
			.MaxSliderValue(8192)
			.AllowSpin(true)
			.UndeterminedString(NSLOCTEXT("PropertyEditor", "MultipleValues", "Multiple Values"))
			.Value_Static(&FLandscapeEditorDetailCustomization_Base::OnGetValue, PropertyHandle_ImportSize_X)
			.OnValueChanged_Static(&FLandscapeEditorDetailCustomization_Base::OnValueChanged, PropertyHandle_ImportSize_X)
			.OnValueCommitted_Static(&FLandscapeEditorDetailCustomization_Base::OnValueCommitted, PropertyHandle_ImportSize_X)
		]
		+ SHorizontalBox::Slot()
		.AutoWidth()
		.Padding(2, 0)
		.VAlign(VAlign_Center)
		[
			SNew(STextBlock)
			.Font(DetailBuilder.GetDetailFont())
			.Text(FString().AppendChar(0xD7)) // Multiply sign
		]
		+ SHorizontalBox::Slot()
		.FillWidth(1)
		[
			SNew(SNumericEntryBox<int32>)
			.LabelVAlign(VAlign_Center)
			.Font(DetailBuilder.GetDetailFont())
			.MinValue(1)
			.MaxValue(8192)
			.MinSliderValue(1)
			.MaxSliderValue(8192)
			.AllowSpin(true)
			.UndeterminedString(NSLOCTEXT("PropertyEditor", "MultipleValues", "Multiple Values"))
			.Value_Static(&FLandscapeEditorDetailCustomization_Base::OnGetValue, PropertyHandle_ImportSize_Y)
			.OnValueChanged_Static(&FLandscapeEditorDetailCustomization_Base::OnValueChanged, PropertyHandle_ImportSize_Y)
			.OnValueCommitted_Static(&FLandscapeEditorDetailCustomization_Base::OnValueCommitted, PropertyHandle_ImportSize_Y)
		]
	];

	TSharedRef<IPropertyHandle> PropertyHandle_ImportLayers = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULandscapeEditorObject, GizmoImportLayers));
	DetailBuilder.HideProperty(PropertyHandle_ImportLayers);
	GizmoImportExportGroup.AddPropertyRow(PropertyHandle_ImportLayers);

	GizmoImportExportGroup.AddWidgetRow()
	.FilterString("")
	[
		SNew(SHorizontalBox)
		+ SHorizontalBox::Slot()
		.AutoWidth()
		[
			SNew(SButton)
			.Text(LOCTEXT("GizmoImport", "Import"))
			.IsEnabled(this, &FLandscapeEditorDetailCustomization_CopyPaste::GetGizmoImportButtonIsEnabled)
			.OnClicked(this, &FLandscapeEditorDetailCustomization_CopyPaste::OnGizmoImportButtonClicked)
		]
		+ SHorizontalBox::Slot()
		.AutoWidth()
		[
			SNew(SButton)
			.Text(LOCTEXT("GizmoExport", "Export"))
			.OnClicked(this, &FLandscapeEditorDetailCustomization_CopyPaste::OnGizmoExportButtonClicked)
		]
	];

	//GuessGizmoImportSize();
}
Ejemplo n.º 14
0
void FActorDetails::CustomizeDetails( IDetailLayoutBuilder& DetailLayout )
{
	// These details only apply when adding an instance of the actor in a level
	if( !DetailLayout.GetDetailsView().HasClassDefaultObject() && DetailLayout.GetDetailsView().GetSelectedActorInfo().NumSelected > 0 )
	{
		// Build up a list of unique blueprints in the selection set (recording the first actor in the set for each one)
		TMap<UBlueprint*, UObject*> UniqueBlueprints;

		// Per level Actor Counts
		TMap<ULevel*, int32> ActorsPerLevelCount;

		bool bHasBillboardComponent = false;
		const TArray< TWeakObjectPtr<UObject> >& SelectedObjects = DetailLayout.GetDetailsView().GetSelectedObjects();
		for (int32 ObjectIndex = 0; ObjectIndex < SelectedObjects.Num(); ++ObjectIndex)
		{
			AActor* Actor = Cast<AActor>( SelectedObjects[ObjectIndex].Get() );

			if (Actor != NULL)
			{
				// Store the selected actors for use later. Its fine to do this when CustomizeDetails is called because if the selected actors changes, CustomizeDetails will be called again on a new instance
				// and our current resource would be destroyed.
				SelectedActors.Add( Actor );

				// Record the level that contains this actor and increment it's actor count
				ULevel* Level = Actor->GetTypedOuter<ULevel>();
				if (Level != NULL)
				{
					int32& ActorCountForThisLevel = ActorsPerLevelCount.FindOrAdd(Level);
					++ActorCountForThisLevel;
				}

				// Add to the unique blueprint map if the actor is generated from a blueprint
				if (UBlueprint* Blueprint = Cast<UBlueprint>(Actor->GetClass()->ClassGeneratedBy))
				{
					if (!UniqueBlueprints.Find(Blueprint))
					{
						UniqueBlueprints.Add(Blueprint, Actor);
					}
				}

				if (!bHasBillboardComponent)
				{
					bHasBillboardComponent = Actor->FindComponentByClass<UBillboardComponent>() != NULL;
				}
			}
		}

		if (!bHasBillboardComponent)
		{
			// Actor billboard scale is not relevant if the actor doesn't have a billboard component
			DetailLayout.HideProperty( GET_MEMBER_NAME_CHECKED(AActor, SpriteScale) );
		}

		AddTransformCategory( DetailLayout );

		AddMaterialCategory( DetailLayout );

		AddActorCategory( DetailLayout, ActorsPerLevelCount );

		// Get the list of hidden categories
		TArray<FString> HideCategories;
		DetailLayout.GetDetailsView().GetBaseClass()->GetHideCategories(HideCategories);

		// Add Blueprint category, if not being hidden
		if (!HideCategories.Contains(TEXT("Blueprint")))
		{
			AddBlueprintCategory(DetailLayout, UniqueBlueprints);
		}

		if( GetDefault<UEditorExperimentalSettings>()->bCodeView )
		{
			AddCodeViewCategory( DetailLayout );
		}

		if (!HideCategories.Contains(TEXT("Layers")))
		{
			AddLayersCategory(DetailLayout);
		}

		//AddComponentsCategory( DetailLayout );
	}
}
Ejemplo n.º 15
0
void FSpriteDetailsCustomization::BuildCollisionSection(IDetailCategoryBuilder& CollisionCategory, IDetailLayoutBuilder& DetailLayout)
{
	TSharedPtr<IPropertyHandle> SpriteCollisionDomainProperty = DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UPaperSprite, SpriteCollisionDomain));
	TAttribute<EVisibility> ParticipatesInPhysics = TAttribute<EVisibility>::Create( TAttribute<EVisibility>::FGetter::CreateSP( this, &FSpriteDetailsCustomization::AnyPhysicsMode, SpriteCollisionDomainProperty) ) ;
	TAttribute<EVisibility> ParticipatesInPhysics3D = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FSpriteDetailsCustomization::PhysicsModeMatches, SpriteCollisionDomainProperty, ESpriteCollisionMode::Use3DPhysics));
	TAttribute<EVisibility> ParticipatesInPhysics2D = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FSpriteDetailsCustomization::PhysicsModeMatches, SpriteCollisionDomainProperty, ESpriteCollisionMode::Use2DPhysics));

	CollisionCategory.AddProperty(SpriteCollisionDomainProperty);

	// Add a warning bar about 2D collision being experimental
	FText WarningFor2D = LOCTEXT("Experimental2DPhysicsWarning", "2D collision support is *experimental*");
	FText TooltipFor2D = LOCTEXT("Experimental2DPhysicsWarningTooltip", "2D collision support is *experimental* and should not be relied on yet.\n\nRigid body collision detection and response works, but there are only precompiled libraries for Windows currently.\n\nRaycasts are partially supported (and need to be enabled in project settings), but queries, sweeps, or overlap tests are not implemented yet.");
	GenerateWarningRow(CollisionCategory, /*bExperimental=*/ true, WarningFor2D, TooltipFor2D, TEXT("Shared/Editors/SpriteEditor"), TEXT("CollisionDomain2DWarning"))
		.Visibility(ParticipatesInPhysics2D);

	// Add a warning bar if 2D collision queries aren't enabled
	TAttribute<EVisibility> WarnAbout2DQueriesBeingDisabledVisibility = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FSpriteDetailsCustomization::Get2DPhysicsNotEnabledWarningVisibility, SpriteCollisionDomainProperty));
	FText QueryWarningFor2D = LOCTEXT("Query2DPhysicsWarning", "2D collision queries are disabled");
	FText QueryTooltipFor2D = LOCTEXT("Query2DPhysicsWarningTooltip", "You can enable 2D queries in Project Settings..Physics by setting bEnable2DPhysics to true, otherwise only collision detection and response will work.\n\nNote: Only raycasts are partially supported; other queries, sweeps, and overlap tests are not implemented yet.");
	GenerateWarningRow(CollisionCategory, /*bExperimental=*/ false, QueryWarningFor2D, QueryTooltipFor2D, TEXT("Shared/Editors/SpriteEditor"), TEXT("Disabled2DCollisionQueriesWarning"))
		.Visibility(WarnAbout2DQueriesBeingDisabledVisibility);

	// Add the collision geometry mode into the parent container (renamed)
	{
		// Restrict the diced value
		TSharedPtr<FPropertyRestriction> PreventDicedRestriction = MakeShareable(new FPropertyRestriction(LOCTEXT("CollisionGeometryDoesNotSupportDiced", "Collision geometry can not be set to Diced")));
		PreventDicedRestriction->AddValue(TEXT("Diced"));

		// Find and add the property
		const FString CollisionGeometryTypePropertyPath = FString::Printf(TEXT("%s.%s"), GET_MEMBER_NAME_STRING_CHECKED(UPaperSprite, CollisionGeometry), GET_MEMBER_NAME_STRING_CHECKED(FSpritePolygonCollection, GeometryType));
		TSharedPtr<IPropertyHandle> CollisionGeometryTypeProperty = DetailLayout.GetProperty(*CollisionGeometryTypePropertyPath);

		CollisionGeometryTypeProperty->AddRestriction(PreventDicedRestriction.ToSharedRef());

		CollisionCategory.AddProperty(CollisionGeometryTypeProperty)
			.DisplayName(LOCTEXT("CollisionGeometryType", "Collision Geometry Type"))
			.Visibility(ParticipatesInPhysics);
	}

	// Show the collision geometry when not None
	CollisionCategory.AddProperty( DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UPaperSprite, CollisionGeometry)) )
		.Visibility(ParticipatesInPhysics);

	// Show the collision thickness only in 3D mode
	CollisionCategory.AddProperty( DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UPaperSprite, CollisionThickness)) )
		.Visibility(ParticipatesInPhysics3D);

	// Add the collision polygons into advanced (renamed)
	const FString CollisionGeometryPolygonsPropertyPath = FString::Printf(TEXT("%s.%s"), GET_MEMBER_NAME_STRING_CHECKED(UPaperSprite, CollisionGeometry), GET_MEMBER_NAME_STRING_CHECKED(FSpritePolygonCollection, Polygons));
	CollisionCategory.AddProperty(DetailLayout.GetProperty(*CollisionGeometryPolygonsPropertyPath), EPropertyLocation::Advanced)
		.DisplayName(LOCTEXT("CollisionPolygons", "Collision Polygons"))
		.Visibility(ParticipatesInPhysics);

	// Show the default body instance (and only it) from the body setup (if it exists)
	DetailLayout.HideProperty("BodySetup");
	IDetailPropertyRow& BodySetupDefaultInstance = CollisionCategory.AddProperty("BodySetup.DefaultInstance");
	
	TArray<TWeakObjectPtr<UObject>> SpritesBeingEdited;
	DetailLayout.GetObjectsBeingCustomized(/*out*/ SpritesBeingEdited);

	TArray<UObject*> BodySetupList;
	for (auto WeakSpritePtr : SpritesBeingEdited)
	{
		if (UPaperSprite* Sprite = Cast<UPaperSprite>(WeakSpritePtr.Get()))
		{
			if (UBodySetup* BodySetup = Sprite->BodySetup)
			{
				BodySetupList.Add(BodySetup);
			}
		}
	}
	
	if (BodySetupList.Num() > 0)
	{
		IDetailPropertyRow* DefaultInstanceRow = CollisionCategory.AddExternalProperty(BodySetupList, GET_MEMBER_NAME_CHECKED(UBodySetup, DefaultInstance));
		if (DefaultInstanceRow != nullptr)
		{
			DefaultInstanceRow->Visibility(ParticipatesInPhysics);
		}
	}
}
void FSpriteDetailsCustomization::BuildCollisionSection(IDetailCategoryBuilder& CollisionCategory, IDetailLayoutBuilder& DetailLayout)
{
	TSharedPtr<IPropertyHandle> SpriteCollisionDomainProperty = DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UPaperSprite, SpriteCollisionDomain));

	CollisionCategory.HeaderContent
	(
		SNew(SBox)
		.HAlign(HAlign_Right)
		[
			SNew(SHorizontalBox)
			+SHorizontalBox::Slot()
			.Padding(FMargin(5.0f, 0.0f))
			.AutoWidth()
			[
				SNew(STextBlock)
				.Font(FEditorStyle::GetFontStyle("TinyText"))
				.Text(this, &FSpriteDetailsCustomization::GetCollisionHeaderContentText, SpriteCollisionDomainProperty)
			]
		]
	);

	TAttribute<EVisibility> ParticipatesInPhysics = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP( this, &FSpriteDetailsCustomization::AnyPhysicsMode, SpriteCollisionDomainProperty));
	TAttribute<EVisibility> ParticipatesInPhysics3D = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FSpriteDetailsCustomization::PhysicsModeMatches, SpriteCollisionDomainProperty, ESpriteCollisionMode::Use3DPhysics));
	TAttribute<EVisibility> HideWhenInRenderingMode = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FSpriteDetailsCustomization::EditorModeIsNot, ESpriteEditorMode::EditRenderingGeomMode));
	TAttribute<EVisibility> ShowWhenInRenderingMode = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FSpriteDetailsCustomization::EditorModeMatches, ESpriteEditorMode::EditRenderingGeomMode));

	static const FText EditCollisionInCollisionMode = LOCTEXT("CollisionPropertiesHiddenInRenderingMode", "Switch to 'Edit Collsion' mode\nto edit Collision settings");
	CollisionCategory.AddCustomRow(EditCollisionInCollisionMode)
		.Visibility(ShowWhenInRenderingMode)
		.WholeRowContent()
		.HAlign(HAlign_Center)
		[
			SNew(STextBlock)
			.Font(DetailLayout.GetDetailFontItalic())
			.Justification(ETextJustify::Center)
			.Text(EditCollisionInCollisionMode)
		];

	CollisionCategory.AddProperty(SpriteCollisionDomainProperty).Visibility(HideWhenInRenderingMode);

	// Add the collision geometry mode into the parent container (renamed)
	{
		// Restrict the diced value
		TSharedPtr<FPropertyRestriction> PreventDicedRestriction = MakeShareable(new FPropertyRestriction(LOCTEXT("CollisionGeometryDoesNotSupportDiced", "Collision geometry can not be set to Diced")));
		const UEnum* const SpritePolygonModeEnum = FindObject<UEnum>(ANY_PACKAGE, TEXT("ESpritePolygonMode"));		
		PreventDicedRestriction->AddDisabledValue(SpritePolygonModeEnum->GetNameStringByValue((uint8)ESpritePolygonMode::Diced));

		// Find and add the property
		const FString CollisionGeometryTypePropertyPath = FString::Printf(TEXT("%s.%s"), GET_MEMBER_NAME_STRING_CHECKED(UPaperSprite, CollisionGeometry), GET_MEMBER_NAME_STRING_CHECKED(FSpriteGeometryCollection, GeometryType));
		TSharedPtr<IPropertyHandle> CollisionGeometryTypeProperty = DetailLayout.GetProperty(*CollisionGeometryTypePropertyPath);

		CollisionGeometryTypeProperty->AddRestriction(PreventDicedRestriction.ToSharedRef());

		CollisionCategory.AddProperty(CollisionGeometryTypeProperty)
			.DisplayName(LOCTEXT("CollisionGeometryType", "Collision Geometry Type"))
			.Visibility(ParticipatesInPhysics);
	}

	// Show the collision thickness only in 3D mode
	CollisionCategory.AddProperty( DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UPaperSprite, CollisionThickness)) )
		.Visibility(ParticipatesInPhysics3D);

	// Show the default body instance (and only it) from the body setup (if it exists)
	DetailLayout.HideProperty("BodySetup");
	IDetailPropertyRow& BodySetupDefaultInstance = CollisionCategory.AddProperty("BodySetup.DefaultInstance");
	
	TArray<TWeakObjectPtr<UObject>> SpritesBeingEdited;
	DetailLayout.GetObjectsBeingCustomized(/*out*/ SpritesBeingEdited);

	TArray<UObject*> BodySetupList;
	for (auto WeakSpritePtr : SpritesBeingEdited)
	{
		if (UPaperSprite* Sprite = Cast<UPaperSprite>(WeakSpritePtr.Get()))
		{
			if (UBodySetup* BodySetup = Sprite->BodySetup)
			{
				BodySetupList.Add(BodySetup);
			}
		}
	}
	
	if (BodySetupList.Num() > 0)
	{
		IDetailPropertyRow* DefaultInstanceRow = CollisionCategory.AddExternalObjectProperty(BodySetupList, GET_MEMBER_NAME_CHECKED(UBodySetup, DefaultInstance));
		if (DefaultInstanceRow != nullptr)
		{
			DefaultInstanceRow->Visibility(ParticipatesInPhysics);
		}
	}

	// Show the collision geometry when not None
	CollisionCategory.AddProperty(DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UPaperSprite, CollisionGeometry)))
		.Visibility(ParticipatesInPhysics);

	// Add the collision polygons into advanced (renamed)
	const FString CollisionGeometryPolygonsPropertyPath = FString::Printf(TEXT("%s.%s"), GET_MEMBER_NAME_STRING_CHECKED(UPaperSprite, CollisionGeometry), GET_MEMBER_NAME_STRING_CHECKED(FSpriteGeometryCollection, Shapes));
	CollisionCategory.AddProperty(DetailLayout.GetProperty(*CollisionGeometryPolygonsPropertyPath), EPropertyLocation::Advanced)
		.DisplayName(LOCTEXT("CollisionShapes", "Collision Shapes"))
		.Visibility(ParticipatesInPhysics);
}
Ejemplo n.º 17
0
void FBodySetupDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder )
{
	// Customize collision section
	{
		if ( DetailBuilder.GetProperty("DefaultInstance")->IsValidHandle() )
		{
			IDetailCategoryBuilder& PhysicsCategory = DetailBuilder.EditCategory("Physics");
			IDetailCategoryBuilder& CollisionCategory = DetailBuilder.EditCategory("Collision");

			TSharedPtr<IPropertyHandle> BodyInstanceHandler = DetailBuilder.GetProperty("DefaultInstance");
			DetailBuilder.HideProperty(BodyInstanceHandler);

			TSharedPtr<IPropertyHandle> CollisionTraceHandler = DetailBuilder.GetProperty("CollisionTraceFlag");
			DetailBuilder.HideProperty(CollisionTraceHandler);

			// add physics properties to physics category
			uint32 NumChildren = 0;
			BodyInstanceHandler->GetNumChildren(NumChildren);

			// Get the objects being customized so we can enable/disable editing of 'Simulate Physics'
			DetailBuilder.GetObjectsBeingCustomized(ObjectsCustomized);

			// add all properties of this now - after adding 
			for (uint32 ChildIndex=0; ChildIndex < NumChildren; ++ChildIndex)
			{
				TSharedPtr<IPropertyHandle> ChildProperty = BodyInstanceHandler->GetChildHandle(ChildIndex);
				FString Category = FObjectEditorUtils::GetCategory(ChildProperty->GetProperty());
				if (ChildProperty->GetProperty()->GetName() == TEXT("bSimulatePhysics") || ChildProperty->GetProperty()->GetName() == TEXT("bAutoWeld"))
				{
					// skip bSimulatePhysics
					// this is because we don't want bSimulatePhysics to show up 
					// phat editor 
					// staitc mesh already hides everything else not interested in
					// so phat editor just should not show this option
					//also hide bAutoWeld for phat
					continue;
				}
				else if (ChildProperty->GetProperty()->GetName() == TEXT("MassInKg"))
				{
					PhysicsCategory.AddCustomRow(TEXT("Mass"), false)
						.IsEnabled(TAttribute<bool>(this, &FBodySetupDetails::IsBodyMassEnabled))
						.NameContent()
						[
							ChildProperty->CreatePropertyNameWidget()
						]
					.ValueContent()
						[
							SNew(SVerticalBox)
							+ SVerticalBox::Slot()
							.AutoHeight()
							[
								SNew(SEditableTextBox)
								.Text(this, &FBodySetupDetails::OnGetBodyMass)
								.IsReadOnly(this, &FBodySetupDetails::IsBodyMassReadOnly)
								.Font(IDetailLayoutBuilder::GetDetailFont())
								.Visibility(this, &FBodySetupDetails::IsMassVisible, false)
							]

							+ SVerticalBox::Slot()
								.AutoHeight()
								[
									SNew(SVerticalBox)
									.Visibility(this, &FBodySetupDetails::IsMassVisible, true)
									+ SVerticalBox::Slot()
									.AutoHeight()
									[
										ChildProperty->CreatePropertyValueWidget()
									]
								]

						];

					continue;
				}
				if (Category == TEXT("Physics"))
				{
					PhysicsCategory.AddProperty(ChildProperty);
				}
				else if (Category == TEXT("Collision"))
				{
					CollisionCategory.AddProperty(ChildProperty);
				}
			}
		}
	}
}
void FTODAssetPropertyDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout)
{
	const IDetailsView& DetailView = DetailLayout.GetDetailsView();
	//first find asset we are going to edit.
	TWeakObjectPtr<UObject> InspectedObject;
	for (TWeakObjectPtr<UObject> inspObj : DetailView.GetSelectedObjects())
	{
		InspectedObject = inspObj;
		break;
	}

	UTODAsset* TODAsset = Cast<UTODAsset>(InspectedObject.Get());
	CurrentTODAsset = Cast<UTODAsset>(InspectedObject.Get());
	if (TODAsset)
	{
		for (TFieldIterator<UProperty> PropIt(TODAsset->GetClass()); PropIt; ++PropIt)
		{
			UProperty* prop = *PropIt;
			DetailLayout.HideProperty(prop->GetFName());
			//PropertyHandles.Add(DetailLayout.GetProperty(prop->GetFName()));
			UStructProperty* structProp = Cast<UStructProperty>(prop);
			if (structProp)
			{
				FRuntimeFloatCurve* floatCurve = structProp->ContainerPtrToValuePtr<FRuntimeFloatCurve>(TODAsset);
				if (floatCurve)
				{
					TSharedPtr<FTODFloatCurveProperty> tempFloatProp = MakeShareable(new FTODFloatCurveProperty());
					tempFloatProp->PropertyHandle = DetailLayout.GetProperty(prop->GetFName());
					tempFloatProp->TODAsset = TODAsset;
					tempFloatProp->CategoryName = tempFloatProp->PropertyHandle->GetMetaData(TEXT("Category"));
					FloatCurves.Add(tempFloatProp);
				}
			}

		}
	}
	IDetailCategoryBuilder& DetailCategoryBuilder = DetailLayout.EditCategory("Property Detail");
	FDetailWidgetRow& DetailRow = DetailCategoryBuilder.AddCustomRow(FString("Custom Row"));
	////now customize each property
	//FRuntimeFloatCurve* floatCurve;

	TSharedPtr<IPropertyHandle> hour = DetailLayout.GetProperty(TEXT("Hour"));
	DetailCategoryBuilder.AddProperty(hour);


	IDetailCategoryBuilder& SunCategoryBuilder = DetailLayout.EditCategory("Sun");
	IDetailCategoryBuilder& AFCategoryBuilder = DetailLayout.EditCategory("Atmospheric Fog");
	IDetailCategoryBuilder& HFCategoryBuilder = DetailLayout.EditCategory("Height Fog");
	IDetailCategoryBuilder& PPCategoryBuilder = DetailLayout.EditCategory("Post Process");
	IDetailCategoryBuilder& SkyLightCategoryBuilder = DetailLayout.EditCategory("SkyLight");
	IDetailCategoryBuilder& MoonCategoryBuilder = DetailLayout.EditCategory("Moon");
	for (TSharedPtr<FTODFloatCurveProperty> floatCurves : FloatCurves)
	{
		if (floatCurves->CategoryName == FString("Sun"))
			floatCurves->ConstructWidget(SunCategoryBuilder);

		if (floatCurves->CategoryName == FString("Atmospheric Fog"))
			floatCurves->ConstructWidget(AFCategoryBuilder);

		if (floatCurves->CategoryName == FString("Height Fog"))
			floatCurves->ConstructWidget(HFCategoryBuilder);

		if (floatCurves->CategoryName == FString("Post Process"))
			floatCurves->ConstructWidget(PPCategoryBuilder);

		if (floatCurves->CategoryName == FString("SkyLight"))
			floatCurves->ConstructWidget(SkyLightCategoryBuilder);

		if (floatCurves->CategoryName == FString("Moon"))
			floatCurves->ConstructWidget(MoonCategoryBuilder);
	}
}
void FSpriteDetailsCustomization::BuildCollisionSection(IDetailCategoryBuilder& CollisionCategory, IDetailLayoutBuilder& DetailLayout)
{
	TSharedPtr<IPropertyHandle> SpriteCollisionDomainProperty = DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UPaperSprite, SpriteCollisionDomain));

	CollisionCategory.HeaderContent
	(
		SNew(SBox)
		.HAlign(HAlign_Right)
		[
			SNew(SHorizontalBox)
			+SHorizontalBox::Slot()
			.Padding(FMargin(5.0f, 0.0f))
			.AutoWidth()
			[
				SNew(STextBlock)
				.Font(FEditorStyle::GetFontStyle("TinyText"))
				.Text(this, &FSpriteDetailsCustomization::GetCollisionHeaderContentText, SpriteCollisionDomainProperty)
			]
		]
	);

	TAttribute<EVisibility> ParticipatesInPhysics = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP( this, &FSpriteDetailsCustomization::AnyPhysicsMode, SpriteCollisionDomainProperty));
	TAttribute<EVisibility> ParticipatesInPhysics3D = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FSpriteDetailsCustomization::PhysicsModeMatches, SpriteCollisionDomainProperty, ESpriteCollisionMode::Use3DPhysics));
	TAttribute<EVisibility> ParticipatesInPhysics2D = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FSpriteDetailsCustomization::PhysicsModeMatches, SpriteCollisionDomainProperty, ESpriteCollisionMode::Use2DPhysics));
	TAttribute<EVisibility> HideWhenInRenderingMode = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FSpriteDetailsCustomization::EditorModeIsNot, ESpriteEditorMode::EditRenderingGeomMode));
	TAttribute<EVisibility> ShowWhenInRenderingMode = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FSpriteDetailsCustomization::EditorModeMatches, ESpriteEditorMode::EditRenderingGeomMode));

	static const FText EditCollisionInCollisionMode = LOCTEXT("CollisionPropertiesHiddenInRenderingMode", "Switch to 'Edit Collsion' mode\nto edit Collision settings");
	CollisionCategory.AddCustomRow(EditCollisionInCollisionMode)
		.Visibility(ShowWhenInRenderingMode)
		.WholeRowContent()
		.HAlign(HAlign_Center)
		[
			SNew(STextBlock)
			.Font(DetailLayout.GetDetailFontItalic())
			.Justification(ETextJustify::Center)
			.Text(EditCollisionInCollisionMode)
		];

	CollisionCategory.AddProperty(SpriteCollisionDomainProperty).Visibility(HideWhenInRenderingMode);

	// Add a warning bar about 2D collision being experimental
	FText WarningFor2D = LOCTEXT("Experimental2DPhysicsWarning", "2D collision support is *experimental*");
	FText TooltipFor2D = LOCTEXT("Experimental2DPhysicsWarningTooltip", "2D collision support is *experimental* and should not be relied on yet.\n\nRigid body collision detection and response works, but there are only precompiled libraries for Windows currently.\n\nRaycasts are partially supported (and need to be enabled in project settings), but queries, sweeps, or overlap tests are not implemented yet.");
	GenerateWarningRow(CollisionCategory, /*bExperimental=*/ true, WarningFor2D, TooltipFor2D, TEXT("Shared/Editors/SpriteEditor"), TEXT("CollisionDomain2DWarning"))
		.Visibility(ParticipatesInPhysics2D);

	// Add a warning bar if 2D collision queries aren't enabled
	TAttribute<EVisibility> WarnAbout2DQueriesBeingDisabledVisibility = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FSpriteDetailsCustomization::Get2DPhysicsNotEnabledWarningVisibility, SpriteCollisionDomainProperty));
	FText QueryWarningFor2D = LOCTEXT("Query2DPhysicsWarning", "2D collision queries are disabled");
	FText QueryTooltipFor2D = LOCTEXT("Query2DPhysicsWarningTooltip", "You can enable 2D queries in Project Settings..Physics by setting bEnable2DPhysics to true, otherwise only collision detection and response will work.\n\nNote: Only raycasts are partially supported; other queries, sweeps, and overlap tests are not implemented yet.");
	GenerateWarningRow(CollisionCategory, /*bExperimental=*/ false, QueryWarningFor2D, QueryTooltipFor2D, TEXT("Shared/Editors/SpriteEditor"), TEXT("Disabled2DCollisionQueriesWarning"))
		.Visibility(WarnAbout2DQueriesBeingDisabledVisibility);

	// Add the collision geometry mode into the parent container (renamed)
	{
		// Restrict the diced value
		TSharedPtr<FPropertyRestriction> PreventDicedRestriction = MakeShareable(new FPropertyRestriction(LOCTEXT("CollisionGeometryDoesNotSupportDiced", "Collision geometry can not be set to Diced")));
		const UEnum* const SpritePolygonModeEnum = FindObject<UEnum>(ANY_PACKAGE, TEXT("ESpritePolygonMode"));		
		PreventDicedRestriction->AddDisabledValue(SpritePolygonModeEnum->GetEnumNameStringByValue((uint8)ESpritePolygonMode::Diced));

		// Find and add the property
		const FString CollisionGeometryTypePropertyPath = FString::Printf(TEXT("%s.%s"), GET_MEMBER_NAME_STRING_CHECKED(UPaperSprite, CollisionGeometry), GET_MEMBER_NAME_STRING_CHECKED(FSpriteGeometryCollection, GeometryType));
		TSharedPtr<IPropertyHandle> CollisionGeometryTypeProperty = DetailLayout.GetProperty(*CollisionGeometryTypePropertyPath);

		CollisionGeometryTypeProperty->AddRestriction(PreventDicedRestriction.ToSharedRef());

		CollisionCategory.AddProperty(CollisionGeometryTypeProperty)
			.DisplayName(LOCTEXT("CollisionGeometryType", "Collision Geometry Type"))
			.Visibility(ParticipatesInPhysics);
	}

	// Show the collision thickness only in 3D mode
	CollisionCategory.AddProperty( DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UPaperSprite, CollisionThickness)) )
		.Visibility(ParticipatesInPhysics3D);

	// Show the default body instance (and only it) from the body setup (if it exists)
	DetailLayout.HideProperty("BodySetup");
	IDetailPropertyRow& BodySetupDefaultInstance = CollisionCategory.AddProperty("BodySetup.DefaultInstance");
	
	TArray<TWeakObjectPtr<UObject>> SpritesBeingEdited;
	DetailLayout.GetObjectsBeingCustomized(/*out*/ SpritesBeingEdited);

	TArray<UObject*> BodySetupList;
	for (auto WeakSpritePtr : SpritesBeingEdited)
	{
		if (UPaperSprite* Sprite = Cast<UPaperSprite>(WeakSpritePtr.Get()))
		{
			if (UBodySetup* BodySetup = Sprite->BodySetup)
			{
				BodySetupList.Add(BodySetup);
			}
		}
	}
	
	if (BodySetupList.Num() > 0)
	{
		IDetailPropertyRow* DefaultInstanceRow = CollisionCategory.AddExternalProperty(BodySetupList, GET_MEMBER_NAME_CHECKED(UBodySetup, DefaultInstance));
		if (DefaultInstanceRow != nullptr)
		{
			DefaultInstanceRow->Visibility(ParticipatesInPhysics);
		}
	}

	// Show the collision geometry when not None
	CollisionCategory.AddProperty(DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UPaperSprite, CollisionGeometry)))
		.Visibility(ParticipatesInPhysics);

	// Add the collision polygons into advanced (renamed)
	const FString CollisionGeometryPolygonsPropertyPath = FString::Printf(TEXT("%s.%s"), GET_MEMBER_NAME_STRING_CHECKED(UPaperSprite, CollisionGeometry), GET_MEMBER_NAME_STRING_CHECKED(FSpriteGeometryCollection, Shapes));
	CollisionCategory.AddProperty(DetailLayout.GetProperty(*CollisionGeometryPolygonsPropertyPath), EPropertyLocation::Advanced)
		.DisplayName(LOCTEXT("CollisionShapes", "Collision Shapes"))
		.Visibility(ParticipatesInPhysics);
}
Ejemplo n.º 20
0
void FBodySetupDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder )
{
	// Customize collision section
	{
		if ( DetailBuilder.GetProperty("DefaultInstance")->IsValidHandle() )
		{
			IDetailCategoryBuilder& PhysicsCategory = DetailBuilder.EditCategory("Physics");
			IDetailCategoryBuilder& CollisionCategory = DetailBuilder.EditCategory("Collision");

			TSharedPtr<IPropertyHandle> BodyInstanceHandler = DetailBuilder.GetProperty("DefaultInstance");
			DetailBuilder.HideProperty(BodyInstanceHandler);

			TSharedPtr<IPropertyHandle> CollisionTraceHandler = DetailBuilder.GetProperty("CollisionTraceFlag");
			DetailBuilder.HideProperty(CollisionTraceHandler);

			// add physics properties to physics category
			uint32 NumChildren = 0;
			BodyInstanceHandler->GetNumChildren(NumChildren);

			// Get the objects being customized so we can enable/disable editing of 'Simulate Physics'
			DetailBuilder.GetObjectsBeingCustomized(ObjectsCustomized);

			PhysicsCategory.AddCustomRow(TEXT("Mass"), false)
				.NameContent()
				[
					SNew (STextBlock)
					.Text(NSLOCTEXT("MassInKG", "MassInKG_Name", "Mass in KG"))
					.ToolTipText(NSLOCTEXT("MassInKG", "MassInKG_ToolTip", "Mass of the body in KG"))
					.Font( IDetailLayoutBuilder::GetDetailFont() )
				]
			.ValueContent()
				[
					SNew(SEditableTextBox)
					.Text(this, &FBodySetupDetails::OnGetBodyMass)
					.IsReadOnly(this, &FBodySetupDetails::IsBodyMassReadOnly)
					.Font(IDetailLayoutBuilder::GetDetailFont())
				];

			// add all properties of this now - after adding 
			for (uint32 ChildIndex=0; ChildIndex < NumChildren; ++ChildIndex)
			{
				TSharedPtr<IPropertyHandle> ChildProperty = BodyInstanceHandler->GetChildHandle(ChildIndex);
				FString Category = FObjectEditorUtils::GetCategory(ChildProperty->GetProperty());
				if (ChildProperty->GetProperty()->GetName() == TEXT("bSimulatePhysics"))
				{
					// skip bSimulatePhysics
					// this is because we don't want bSimulatePhysics to show up 
					// phat editor 
					// staitc mesh already hides everything else not interested in
					// so phat editor just should not show this option
					continue;
				}
				if (Category == TEXT("Physics"))
				{
					PhysicsCategory.AddProperty(ChildProperty);
				}
				else if (Category == TEXT("Collision"))
				{
					CollisionCategory.AddProperty(ChildProperty);
				}
			}
		}
	}
}
Ejemplo n.º 21
0
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void FBrushDetails::CustomizeDetails( IDetailLayoutBuilder& DetailLayout )
{
	// Get level editor commands for our menus
	FLevelEditorModule& LevelEditor = FModuleManager::GetModuleChecked<FLevelEditorModule>( TEXT("LevelEditor") );
	TSharedRef<const FUICommandList> CommandBindings = LevelEditor.GetGlobalLevelEditorActions();
	const FLevelEditorCommands& Commands = LevelEditor.GetLevelEditorCommands();

	// See if we have a volume. If we do - we hide the BSP stuff (solidity, order)
	bool bHaveAVolume = false;
	TArray< TWeakObjectPtr<UObject> > SelectedObjects = DetailLayout.GetDetailsView().GetSelectedObjects();
	for (int32 ObjIdx = 0; ObjIdx < SelectedObjects.Num(); ObjIdx++)
	{
		if (ABrush* Brush = Cast<ABrush>(SelectedObjects[ObjIdx].Get()))
		{
			if (AVolume* Volume = Cast<AVolume>(Brush))
			{
				bHaveAVolume = true;
			}

			if (!FActorEditorUtils::IsABuilderBrush(Brush))
			{
				// Store the selected actors for use later. Its fine to do this when CustomizeDetails is called because if the selected actors changes, CustomizeDetails will be called again on a new instance
				// and our current resource would be destroyed.
				SelectedBrushes.Add(Brush);
			}
		}
	}

	FMenuBuilder PolygonsMenuBuilder( true, CommandBindings );
	{
		PolygonsMenuBuilder.BeginSection("BrushDetailsPolygons");
		{
			PolygonsMenuBuilder.AddMenuEntry( Commands.MergePolys );
			PolygonsMenuBuilder.AddMenuEntry( Commands.SeparatePolys );
		}
		PolygonsMenuBuilder.EndSection();
	}

	FMenuBuilder SolidityMenuBuilder( true, CommandBindings );
	{
		SolidityMenuBuilder.AddMenuEntry( Commands.MakeSolid );
		SolidityMenuBuilder.AddMenuEntry( Commands.MakeSemiSolid );
		SolidityMenuBuilder.AddMenuEntry( Commands.MakeNonSolid );
	}

	FMenuBuilder OrderMenuBuilder( true, CommandBindings );
	{
		OrderMenuBuilder.AddMenuEntry( Commands.OrderFirst );
		OrderMenuBuilder.AddMenuEntry( Commands.OrderLast );
	}

	struct Local
	{
		static FReply ExecuteExecCommand(FString InCommand)
		{
			GUnrealEd->Exec( GWorld, *InCommand );
			return FReply::Handled();
		}

		static TSharedRef<SWidget> GenerateBuildMenuContent(TSharedRef<IPropertyHandle> BrushBuilderHandle, IDetailLayoutBuilder* InDetailLayout)
		{
			class FBrushFilter : public IClassViewerFilter
			{
			public:
				virtual bool IsClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const UClass* InClass, TSharedRef< class FClassViewerFilterFuncs > InFilterFuncs )
				{
					return !InClass->HasAnyClassFlags(CLASS_NotPlaceable) && !InClass->HasAnyClassFlags(CLASS_Abstract) && InClass->IsChildOf(UBrushBuilder::StaticClass());
				}

				virtual bool IsUnloadedClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const TSharedRef< const class IUnloadedBlueprintData > InUnloadedClassData, TSharedRef< class FClassViewerFilterFuncs > InFilterFuncs)
				{
					return false;
				}
			};

			FClassViewerInitializationOptions Options;
			Options.ClassFilter = MakeShareable(new FBrushFilter);
			Options.Mode = EClassViewerMode::ClassPicker;
			Options.DisplayMode = EClassViewerDisplayMode::ListView;
			return FModuleManager::LoadModuleChecked<FClassViewerModule>("ClassViewer").CreateClassViewer(Options, FOnClassPicked::CreateStatic(&Local::OnClassPicked, BrushBuilderHandle, InDetailLayout));
		}

		static void OnClassPicked(UClass* InChosenClass, TSharedRef<IPropertyHandle> BrushBuilderHandle, IDetailLayoutBuilder* InDetailLayout)
		{
			FSlateApplication::Get().DismissAllMenus();

			TArray<UObject*> OuterObjects;
			BrushBuilderHandle->GetOuterObjects(OuterObjects);

			struct FNewBrushBuilder
			{
				UBrushBuilder* Builder;
				ABrush* Brush;
			};

			TArray<FNewBrushBuilder> NewBuilders;
			TArray<FString> NewObjectPaths;

			{
				const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "BrushSet", "Brush Set"));
				for (UObject* OuterObject : OuterObjects)
				{
					UBrushBuilder* NewUObject = NewObject<UBrushBuilder>(OuterObject, InChosenClass, NAME_None, RF_Transactional);

					FNewBrushBuilder NewBuilder;
					NewBuilder.Builder = NewUObject;
					NewBuilder.Brush = CastChecked<ABrush>(OuterObject);

					NewBuilders.Add(NewBuilder);
					NewObjectPaths.Add(NewUObject->GetPathName());
				}

				BrushBuilderHandle->SetPerObjectValues(NewObjectPaths);

				// make sure the brushes are rebuilt
				for (FNewBrushBuilder& NewObject : NewBuilders)
				{
					NewObject.Builder->Build(NewObject.Brush->GetWorld(), NewObject.Brush);
				}

				GEditor->RebuildAlteredBSP();
			}



			InDetailLayout->ForceRefreshDetails();
		}

		static FText GetBuilderText(TSharedRef<IPropertyHandle> BrushBuilderHandle)
		{
			UObject* Object = nullptr;
			BrushBuilderHandle->GetValue(Object);
			if(Object != nullptr)
			{
				UBrushBuilder* BrushBuilder = CastChecked<UBrushBuilder>(Object);
				const FText NameText = BrushBuilder->GetClass()->GetDisplayNameText();
				if(!NameText.IsEmpty())
				{
					return NameText;
				}
				else
				{
					return FText::FromString(FName::NameToDisplayString(BrushBuilder->GetClass()->GetName(), false));
				}
			}

			return LOCTEXT("None", "None");
		}
	};

	// Hide the brush builder if it is NULL
	TSharedRef<IPropertyHandle> BrushBuilderPropertyHandle = DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(ABrush, BrushBuilder));
	UObject* BrushBuilderObject = nullptr;
	BrushBuilderPropertyHandle->GetValue(BrushBuilderObject);
	if(BrushBuilderObject == nullptr)
	{
		DetailLayout.HideProperty("BrushBuilder");
	}
	else
	{
		BrushBuilderObject->SetFlags( RF_Transactional );
	}

	IDetailCategoryBuilder& BrushBuilderCategory = DetailLayout.EditCategory( "BrushSettings", FText::GetEmpty(), ECategoryPriority::Important );

	BrushBuilderCategory.AddProperty( GET_MEMBER_NAME_CHECKED(ABrush, BrushType) );
	BrushBuilderCategory.AddCustomRow( LOCTEXT("BrushShape", "Brush Shape") )
	.NameContent()
	[
		SNew( STextBlock )
		.Text( LOCTEXT("BrushShape", "Brush Shape"))
		.Font( IDetailLayoutBuilder::GetDetailFont() )
	]
	.ValueContent()
	.MinDesiredWidth(113)
	.MaxDesiredWidth(113)
	[
		SNew(SComboButton)
		.ToolTipText(LOCTEXT("BspModeBuildTooltip", "Rebuild this brush from a parametric builder."))
		.OnGetMenuContent_Static(&Local::GenerateBuildMenuContent, BrushBuilderPropertyHandle, &DetailLayout)
		.ContentPadding(2)
		.ButtonContent()
		[
			SNew(STextBlock)
			.Text_Static(&Local::GetBuilderText, BrushBuilderPropertyHandle)
			.Font( IDetailLayoutBuilder::GetDetailFont() )
		]
	];

	BrushBuilderCategory.AddCustomRow( FText::GetEmpty(), true )
	[
		SNew(SHorizontalBox)
		+ SHorizontalBox::Slot()
		.FillWidth(1)
		.Padding(1.0f)
		[
			SNew(SComboButton)
			.ContentPadding(2)
			.ButtonContent()
			[
				SNew(STextBlock)
				.Text(NSLOCTEXT("BrushDetails", "PolygonsMenu", "Polygons"))
				.ToolTipText(NSLOCTEXT("BrushDetails", "PolygonsMenu_ToolTip", "Polygon options"))
				.Font(IDetailLayoutBuilder::GetDetailFont())
			]
			.MenuContent()
			[
				PolygonsMenuBuilder.MakeWidget()
			]
		]
		+ SHorizontalBox::Slot()
		.FillWidth(1)
		.Padding(1.0f)
		[
			SNew(SComboButton)
			.ContentPadding(2)
			.Visibility(bHaveAVolume ? EVisibility::Collapsed : EVisibility::Visible)
			.ButtonContent()
			[
				SNew(STextBlock)
				.Text(NSLOCTEXT("BrushDetails", "SolidityMenu", "Solidity"))
				.ToolTipText(NSLOCTEXT("BrushDetails", "SolidityMenu_ToolTip", "Solidity options"))
				.Font(IDetailLayoutBuilder::GetDetailFont())
			]
			.MenuContent()
			[
				SolidityMenuBuilder.MakeWidget()
			]
		]
		+ SHorizontalBox::Slot()
		.FillWidth(1)
		.Padding(1.0f)
		[
			SNew(SComboButton)
			.ContentPadding(2)
			.Visibility(bHaveAVolume ? EVisibility::Collapsed : EVisibility::Visible)
			.ButtonContent()
			[
				SNew(STextBlock)
				.Text(NSLOCTEXT("BrushDetails", "OrderMenu", "Order"))
				.ToolTipText(NSLOCTEXT("BrushDetails", "OrderMenu_ToolTip", "Order options"))
				.Font(IDetailLayoutBuilder::GetDetailFont())
			]
			.MenuContent()
			[
				OrderMenuBuilder.MakeWidget()
			]
		]
	];

	TSharedPtr< SHorizontalBox > BrushHorizontalBox;

	BrushBuilderCategory.AddCustomRow( FText::GetEmpty(), true)
	[
		SAssignNew(BrushHorizontalBox, SHorizontalBox)
		+SHorizontalBox::Slot()
		[
			SNew( SButton )
			.ToolTipText( LOCTEXT("AlignBrushVerts_Tooltip", "Aligns each vertex of the brush to the grid.") )
			.OnClicked( FOnClicked::CreateStatic( &Local::ExecuteExecCommand, FString( TEXT("ACTOR ALIGN VERTS") ) ) )
			.HAlign( HAlign_Center )
			[
				SNew( STextBlock )
				.Text( LOCTEXT("AlignBrushVerts", "Align Brush Vertices") )
				.Font( IDetailLayoutBuilder::GetDetailFont() )
			]
		]
	];

	if (SelectedBrushes.Num() > 0)
	{
		BrushHorizontalBox->AddSlot()
		[
			SNew( SButton )
			.ToolTipText( LOCTEXT("CreateStaticMeshActor_Tooltip", "Creates a static mesh from selected brushes or volumes and replaces them in the scene with the new static mesh") )
			.OnClicked( this, &FBrushDetails::OnCreateStaticMesh )
			.HAlign( HAlign_Center )
			[
				SNew( STextBlock )
				.Text( LOCTEXT("CreateStaticMeshActor", "Create Static Mesh") )
				.Font( IDetailLayoutBuilder::GetDetailFont() )
			]
		];
	}
}
Ejemplo n.º 22
0
void FFbxImportUIDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder )
{
	CachedDetailBuilder = &DetailBuilder;
	TArray<TWeakObjectPtr<UObject>> EditingObjects;
	DetailBuilder.GetObjectsBeingCustomized(EditingObjects);
	check(EditingObjects.Num() == 1);

	ImportUI = Cast<UFbxImportUI>(EditingObjects[0].Get());

	// Handle mesh category
	IDetailCategoryBuilder& MeshCategory = DetailBuilder.EditCategory("Mesh", FText::GetEmpty(), ECategoryPriority::Important);
	IDetailCategoryBuilder& TransformCategory = DetailBuilder.EditCategory("Transform");
	TArray<TSharedRef<IPropertyHandle>> CategoryDefaultProperties;
	TArray<TSharedPtr<IPropertyHandle>> ExtraProperties;

	// Grab and hide per-type import options
	TSharedRef<IPropertyHandle> StaticMeshDataProp = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFbxImportUI, StaticMeshImportData));
	TSharedRef<IPropertyHandle> SkeletalMeshDataProp = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFbxImportUI, SkeletalMeshImportData));
	TSharedRef<IPropertyHandle> AnimSequenceDataProp = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFbxImportUI, AnimSequenceImportData));
	DetailBuilder.HideProperty(StaticMeshDataProp);
	DetailBuilder.HideProperty(SkeletalMeshDataProp);
	DetailBuilder.HideProperty(AnimSequenceDataProp);

	MeshCategory.GetDefaultProperties(CategoryDefaultProperties);

	switch(ImportUI->MeshTypeToImport)
	{
		case FBXIT_StaticMesh:
			CollectChildPropertiesRecursive(StaticMeshDataProp, ExtraProperties);
			break;
		case FBXIT_SkeletalMesh:
			if(ImportUI->bImportMesh)
			{
				CollectChildPropertiesRecursive(SkeletalMeshDataProp, ExtraProperties);
			}
			else
			{
				ImportUI->MeshTypeToImport = FBXIT_Animation;
			}
			break;
		default:
			break;
	}
	EFBXImportType ImportType = ImportUI->MeshTypeToImport;

	if(ImportUI->OriginalImportType == FBXIT_SkeletalMesh)
	{
		TSharedRef<IPropertyHandle> ImportMeshProp = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFbxImportUI, bImportMesh));
		ImportMeshProp->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FFbxImportUIDetails::ImportMeshToggleChanged));
		MeshCategory.AddProperty(ImportMeshProp);
	}

	if(ImportType != FBXIT_Animation)
	{
		TSharedRef<IPropertyHandle> ImportSkeletalProp = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFbxImportUI, bImportAsSkeletal));
		ImportSkeletalProp->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FFbxImportUIDetails::MeshImportModeChanged));
		MeshCategory.AddProperty(ImportSkeletalProp);
	}

	for(TSharedRef<IPropertyHandle> Handle : CategoryDefaultProperties)
	{
		FString MetaData = Handle->GetMetaData(TEXT("ImportType"));
		if(!IsImportTypeMetaDataValid(ImportType, MetaData))
		{
			DetailBuilder.HideProperty(Handle);
		}
	}

	for(TSharedPtr<IPropertyHandle> Handle : ExtraProperties)
	{
		FString ImportTypeMetaData = Handle->GetMetaData(TEXT("ImportType"));
		FString CategoryMetaData = Handle->GetMetaData(TEXT("ImportCategory"));
		if(IsImportTypeMetaDataValid(ImportType, ImportTypeMetaData))
		{
			// Decide on category
			if(!CategoryMetaData.IsEmpty())
			{
				// Populate custom categories.
				IDetailCategoryBuilder& CustomCategory = DetailBuilder.EditCategory(*CategoryMetaData);
				CustomCategory.AddProperty(Handle);
			}
			else
			{
				// No override, add to default mesh category
				IDetailPropertyRow& PropertyRow = MeshCategory.AddProperty(Handle);

				UProperty* Property = Handle->GetProperty();
				if (Property != nullptr)
				{
					if (Property->GetFName() == GET_MEMBER_NAME_CHECKED(UFbxStaticMeshImportData, StaticMeshLODGroup))
					{
						SetStaticMeshLODGroupWidget(PropertyRow, Handle);
					}

					if (Property->GetFName() == GET_MEMBER_NAME_CHECKED(UFbxStaticMeshImportData, VertexOverrideColor))
					{
						// Cache the VertexColorImportOption property
						VertexColorImportOptionHandle = StaticMeshDataProp->GetChildHandle(GET_MEMBER_NAME_CHECKED(UFbxStaticMeshImportData, VertexColorImportOption));

						PropertyRow.IsEnabled(TAttribute<bool>(this, &FFbxImportUIDetails::GetVertexOverrideColorEnabledState));
					}
				}
			}
		}
	}

	// Animation Category
	IDetailCategoryBuilder& AnimCategory = DetailBuilder.EditCategory("Animation", FText::GetEmpty(), ECategoryPriority::Important);

	CategoryDefaultProperties.Empty();
	AnimCategory.GetDefaultProperties(CategoryDefaultProperties);
	for(TSharedRef<IPropertyHandle> Handle : CategoryDefaultProperties)
	{
		FString MetaData = Handle->GetMetaData(TEXT("ImportType"));
		if(!IsImportTypeMetaDataValid(ImportType, MetaData))
		{
			DetailBuilder.HideProperty(Handle);
		}
	}

	if(ImportType == FBXIT_Animation || ImportType == FBXIT_SkeletalMesh)
	{
		ExtraProperties.Empty();
		CollectChildPropertiesRecursive(AnimSequenceDataProp, ExtraProperties);

		// Before we add the import data properties we need to re-add any properties we want to appear above them in the UI
		TSharedRef<IPropertyHandle> ImportAnimProp = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFbxImportUI, bImportAnimations));
		// If we're importing an animation file we really don't need to ask this
		DetailBuilder.HideProperty(ImportAnimProp);
		if(ImportType == FBXIT_Animation)
		{
			ImportUI->bImportAnimations = true;
		}
		else
		{
			AnimCategory.AddProperty(ImportAnimProp);
		}

		for(TSharedPtr<IPropertyHandle> Handle : ExtraProperties)
		{
			FString CategoryMetaData = Handle->GetMetaData(TEXT("ImportCategory"));
			if(Handle->GetProperty()->GetOuter() == UFbxAnimSequenceImportData::StaticClass()
			   && CategoryMetaData.IsEmpty())
			{
				// Add to default anim category if no override specified
				IDetailPropertyRow& PropertyRow = AnimCategory.AddProperty(Handle);
			}
			else if(ImportType == FBXIT_Animation && !CategoryMetaData.IsEmpty())
			{
				// Override category is available
				IDetailCategoryBuilder& CustomCategory = DetailBuilder.EditCategory(*CategoryMetaData);
				CustomCategory.AddProperty(Handle);
			}
		}
	}
	else
	{
		// Hide animation options
		CategoryDefaultProperties.Empty();
		AnimCategory.GetDefaultProperties(CategoryDefaultProperties);

		for(TSharedRef<IPropertyHandle> Handle : CategoryDefaultProperties)
		{
			DetailBuilder.HideProperty(Handle);
		}
	}

	// Material Category
	IDetailCategoryBuilder& MaterialCategory = DetailBuilder.EditCategory("Material");
	if(ImportType == FBXIT_Animation)
	{
		// In animation-only mode, hide the material display
		CategoryDefaultProperties.Empty();
		MaterialCategory.GetDefaultProperties(CategoryDefaultProperties);

		for(TSharedRef<IPropertyHandle> Handle : CategoryDefaultProperties)
		{
			DetailBuilder.HideProperty(Handle);
		}
	}
	else
	{
		TSharedRef<IPropertyHandle> TextureDataProp = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFbxImportUI, TextureImportData));
		DetailBuilder.HideProperty(TextureDataProp);

		ExtraProperties.Empty();
		CollectChildPropertiesRecursive(TextureDataProp, ExtraProperties);

		for(TSharedPtr<IPropertyHandle> Handle : ExtraProperties)
		{
			// We ignore base import data for this window.
			if(Handle->GetProperty()->GetOuter() == UFbxTextureImportData::StaticClass())
			{
				MaterialCategory.AddProperty(Handle);
			}
		}
	}
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void FAnimTransitionNodeDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder )
{
	// Get a handle to the node we're viewing
	const TArray< TWeakObjectPtr<UObject> >& SelectedObjects = DetailBuilder.GetDetailsView().GetSelectedObjects();
	for (int32 ObjectIndex = 0; !TransitionNode.IsValid() && (ObjectIndex < SelectedObjects.Num()); ++ObjectIndex)
	{
		const TWeakObjectPtr<UObject>& CurrentObject = SelectedObjects[ObjectIndex];
		if (CurrentObject.IsValid())
		{
			TransitionNode = Cast<UAnimStateTransitionNode>(CurrentObject.Get());
		}
	}

	bool bTransitionToConduit = false;
	if (UAnimStateTransitionNode* TransitionNodePtr = TransitionNode.Get())
	{
		UAnimStateNodeBase* NextState = TransitionNodePtr->GetNextState();
		bTransitionToConduit = (NextState != NULL) && (NextState->IsA<UAnimStateConduitNode>());
	}

	//////////////////////////////////////////////////////////////////////////

	IDetailCategoryBuilder& TransitionCategory = DetailBuilder.EditCategory("Transition", LOCTEXT("TransitionCategoryTitle", "Transition") );

	if (bTransitionToConduit)
	{
		// Transitions to conduits are just shorthand for some other real transition;
		// All of the blend related settings are ignored, so hide them.
		DetailBuilder.HideProperty(GET_MEMBER_NAME_CHECKED(UAnimStateTransitionNode, Bidirectional));
		DetailBuilder.HideProperty(GET_MEMBER_NAME_CHECKED(UAnimStateTransitionNode, CrossfadeDuration));
		DetailBuilder.HideProperty(GET_MEMBER_NAME_CHECKED(UAnimStateTransitionNode, BlendMode));
		DetailBuilder.HideProperty(GET_MEMBER_NAME_CHECKED(UAnimStateTransitionNode, LogicType));
		DetailBuilder.HideProperty(GET_MEMBER_NAME_CHECKED(UAnimStateTransitionNode, PriorityOrder));
	}
	else
	{
		TransitionCategory.AddCustomRow( LOCTEXT("TransitionEventPropertiesCategoryLabel", "Transition") )
		[
			SNew( STextBlock )
			.Text( LOCTEXT("TransitionEventPropertiesCategoryLabel", "Transition") )
			.Font( IDetailLayoutBuilder::GetDetailFontBold() )
		];

		TransitionCategory.AddProperty(GET_MEMBER_NAME_CHECKED(UAnimStateTransitionNode, PriorityOrder)).DisplayName(LOCTEXT("PriorityOrderLabel", "Priority Order"));
		TransitionCategory.AddProperty(GET_MEMBER_NAME_CHECKED(UAnimStateTransitionNode, Bidirectional)).DisplayName(LOCTEXT("BidirectionalLabel", "Bidirectional"));
		TransitionCategory.AddProperty(GET_MEMBER_NAME_CHECKED(UAnimStateTransitionNode, LogicType)).DisplayName(LOCTEXT("BlendLogicLabel", "Blend Logic") );

		UAnimStateTransitionNode* TransNode = TransitionNode.Get();
		if (TransitionNode != NULL)
		{
			// The sharing option for the rule
			TransitionCategory.AddCustomRow( LOCTEXT("TransitionRuleSharingLabel", "Transition Rule Sharing") )
			[
				GetWidgetForInlineShareMenu(TEXT("Transition Rule Sharing"), TransNode->SharedRulesName, TransNode->bSharedRules,
					FOnClicked::CreateSP(this, &FAnimTransitionNodeDetails::OnPromoteToSharedClick, true),
					FOnClicked::CreateSP(this, &FAnimTransitionNodeDetails::OnUnshareClick, true), 
					FOnGetContent::CreateSP(this, &FAnimTransitionNodeDetails::OnGetShareableNodesMenu, true))
			];


// 			TransitionCategory.AddRow()
// 				[
// 					SNew( STextBlock )
// 					.Text( TEXT("Crossfade Settings") )
// 					.Font( IDetailLayoutBuilder::GetDetailFontBold() )
// 				];


			// Show the rule itself
			UEdGraphPin* CanExecPin = NULL;
			if (UAnimationTransitionGraph* TransGraph = Cast<UAnimationTransitionGraph>(TransNode->BoundGraph))
			{
				if (UAnimGraphNode_TransitionResult* ResultNode = TransGraph->GetResultNode())
				{
					CanExecPin = ResultNode->FindPin(TEXT("bCanEnterTransition"));
				}
			}

			// indicate if a native transition rule applies to this
			UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(TransitionNode.Get());
			if(Blueprint && Blueprint->ParentClass)
			{
				UAnimInstance* AnimInstance = CastChecked<UAnimInstance>(Blueprint->ParentClass->GetDefaultObject());
				if(AnimInstance)
				{
					UEdGraph* ParentGraph = TransitionNode->GetGraph();
					UAnimStateNodeBase* PrevState = TransitionNode->GetPreviousState();
					UAnimStateNodeBase* NextState = TransitionNode->GetNextState();
					if(PrevState != nullptr && NextState != nullptr && ParentGraph != nullptr)
					{
						FName FunctionName;
						if(AnimInstance->HasNativeTransitionBinding(ParentGraph->GetFName(), FName(*PrevState->GetStateName()), FName(*NextState->GetStateName()), FunctionName))
						{
							TransitionCategory.AddCustomRow( LOCTEXT("NativeBindingPresent_Filter", "Transition has native binding") )
							[
								SNew(STextBlock)
								.Text(FText::Format(LOCTEXT("NativeBindingPresent", "Transition has native binding to {0}()"), FText::FromName(FunctionName)))
								.Font( IDetailLayoutBuilder::GetDetailFontBold() )
							];
						}
					}
				}
			}

			TransitionCategory.AddCustomRow( CanExecPin ? CanExecPin->PinFriendlyName : FText::GetEmpty() )
			[
				SNew(SKismetLinearExpression, CanExecPin)
			];
		}

		//////////////////////////////////////////////////////////////////////////

		IDetailCategoryBuilder& CrossfadeCategory = DetailBuilder.EditCategory("BlendSettings", LOCTEXT("BlendSettingsCategoryTitle", "BlendSettings") );
		if (TransitionNode != NULL)
		{
			// The sharing option for the crossfade settings
			CrossfadeCategory.AddCustomRow( LOCTEXT("TransitionCrossfadeSharingLabel", "Transition Crossfade Sharing") )
			[
				GetWidgetForInlineShareMenu(TEXT("Transition Crossfade Sharing"), TransNode->SharedCrossfadeName, TransNode->bSharedCrossfade,
					FOnClicked::CreateSP(this, &FAnimTransitionNodeDetails::OnPromoteToSharedClick, false), 
					FOnClicked::CreateSP(this, &FAnimTransitionNodeDetails::OnUnshareClick, false), 
					FOnGetContent::CreateSP(this, &FAnimTransitionNodeDetails::OnGetShareableNodesMenu, false))
			];
		}

		//@TODO: Gate editing these on shared non-authorative ones
		CrossfadeCategory.AddProperty(GET_MEMBER_NAME_CHECKED(UAnimStateTransitionNode, CrossfadeDuration)).DisplayName( LOCTEXT("DurationLabel", "Duration") );
		CrossfadeCategory.AddProperty(GET_MEMBER_NAME_CHECKED(UAnimStateTransitionNode, BlendMode)).DisplayName( LOCTEXT("ModeLabel", "Mode") );
		CrossfadeCategory.AddProperty(GET_MEMBER_NAME_CHECKED(UAnimStateTransitionNode, CustomBlendCurve)).DisplayName(LOCTEXT("CurveLabel", "Custom Blend Curve"));

		USkeleton* TargetSkeleton = TransitionNode->GetAnimBlueprint()->TargetSkeleton;

		if(TargetSkeleton)
		{
			TSharedPtr<IPropertyHandle> BlendProfileHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UAnimStateTransitionNode, BlendProfile));
			UObject* BlendProfilePropertyValue = nullptr;
			BlendProfileHandle->GetValue(BlendProfilePropertyValue);
			UBlendProfile* CurrentProfile = Cast<UBlendProfile>(BlendProfilePropertyValue);

			ISkeletonEditorModule& SkeletonEditorModule = FModuleManager::LoadModuleChecked<ISkeletonEditorModule>("SkeletonEditor");

			FBlendProfilePickerArgs Args;
			Args.InitialProfile = CurrentProfile;
			Args.OnBlendProfileSelected = FOnBlendProfileSelected::CreateSP(this, &FAnimTransitionNodeDetails::OnBlendProfileChanged, BlendProfileHandle);
			Args.bAllowNew = false;

			CrossfadeCategory.AddProperty(BlendProfileHandle).CustomWidget(true)
				.NameContent()
				[
					BlendProfileHandle->CreatePropertyNameWidget()
				]
				.ValueContent()
				[
					SkeletonEditorModule.CreateBlendProfilePicker(TargetSkeleton, Args)
				];
		}

		// Add a button that is only visible when blend logic type is custom
		CrossfadeCategory.AddCustomRow( LOCTEXT("EditBlendGraph", "Edit Blend Graph") )
		[
			SNew( SHorizontalBox )
			+SHorizontalBox::Slot()
			.HAlign(HAlign_Right)
			.FillWidth(1)
			.Padding(0,0,10.0f,0)
			[
				SNew(SButton)
				.HAlign(HAlign_Right)
				.OnClicked(this, &FAnimTransitionNodeDetails::OnClickEditBlendGraph)
				.Visibility( this, &FAnimTransitionNodeDetails::GetBlendGraphButtonVisibility )
				.Text(LOCTEXT("EditBlendGraph", "Edit Blend Graph"))
			]
		];

		//////////////////////////////////////////////////////////////////////////

		IDetailCategoryBuilder& NotificationCategory = DetailBuilder.EditCategory("Notifications", LOCTEXT("NotificationsCategoryTitle", "Notifications") );

		NotificationCategory.AddCustomRow( LOCTEXT("StartTransitionEventPropertiesCategoryLabel", "Start Transition Event") )
		[
			SNew( STextBlock )
			.Text( LOCTEXT("StartTransitionEventPropertiesCategoryLabel", "Start Transition Event") )
			.Font( IDetailLayoutBuilder::GetDetailFontBold() )
		];
		CreateTransitionEventPropertyWidgets(NotificationCategory, TEXT("TransitionStart"));


		NotificationCategory.AddCustomRow( LOCTEXT("EndTransitionEventPropertiesCategoryLabel", "End Transition Event" ) ) 
		[
			SNew( STextBlock )
			.Text( LOCTEXT("EndTransitionEventPropertiesCategoryLabel", "End Transition Event" ) )
			.Font( IDetailLayoutBuilder::GetDetailFontBold() )
		];
		CreateTransitionEventPropertyWidgets(NotificationCategory, TEXT("TransitionEnd"));

		NotificationCategory.AddCustomRow( LOCTEXT("InterruptTransitionEventPropertiesCategoryLabel", "Interrupt Transition Event") )
		[
			SNew( STextBlock )
			.Text( LOCTEXT("InterruptTransitionEventPropertiesCategoryLabel", "Interrupt Transition Event") )
			.Font( IDetailLayoutBuilder::GetDetailFontBold() )
		];
		CreateTransitionEventPropertyWidgets(NotificationCategory, TEXT("TransitionInterrupt"));
	}

	DetailBuilder.HideProperty(GET_MEMBER_NAME_CHECKED(UAnimStateTransitionNode, TransitionStart));
	DetailBuilder.HideProperty(GET_MEMBER_NAME_CHECKED(UAnimStateTransitionNode, TransitionEnd));
}
void FEditorUtilityInstanceDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayoutBuilder)
{
    SelectedObjectsList = DetailLayoutBuilder.GetDetailsView().GetSelectedObjects();

    // Hide some useless categories
    //@TODO: How to hide Actors, Layers, etc...?

    // Build a list of unique selected blutilities
    TArray<UClass*> UniqueBlutilityClasses;
    bool bFoundAnyCDOs = false;

    for (auto SelectedObjectIt = SelectedObjectsList.CreateConstIterator(); SelectedObjectIt; ++SelectedObjectIt)
    {
        UObject* Object = (*SelectedObjectIt).Get();

        if (!Object->HasAnyFlags(RF_ClassDefaultObject))
        {
            UClass* ObjectClass = Object->GetClass();

            if (UEditorUtilityBlueprint* Blutility = Cast<UEditorUtilityBlueprint>(ObjectClass->ClassGeneratedBy))
            {
                UniqueBlutilityClasses.Add(ObjectClass);
            }
        }
        else
        {
            bFoundAnyCDOs = true;
        }
    }

    // Run thru each one
    UniqueBlutilityClasses.Sort(FCompareClassNames());
    for (auto ClassIt = UniqueBlutilityClasses.CreateIterator(); ClassIt; ++ClassIt)
    {
        UClass* Class = *ClassIt;

        FString CategoryName = FString::Printf(TEXT("%sActions"), *Class->ClassGeneratedBy->GetName());
        IDetailCategoryBuilder& ActionsCategory = DetailLayoutBuilder.EditCategory(*CategoryName);

        const APlacedEditorUtilityBase* PlacedActorCDO = Cast<const APlacedEditorUtilityBase>(Class->GetDefaultObject());
        if (PlacedActorCDO)
        {
            ActionsCategory.AddCustomRow( PlacedActorCDO->HelpText )
            [
                SNew(STextBlock)
                .Text(PlacedActorCDO->HelpText)
            ];
        }

        const UGlobalEditorUtilityBase* GlobalBlutilityCDO = Cast<const UGlobalEditorUtilityBase>(Class->GetDefaultObject());
        if (GlobalBlutilityCDO)
        {
            ActionsCategory.AddCustomRow( GlobalBlutilityCDO->HelpText )
            [
                SNew(STextBlock)
                .Text(GlobalBlutilityCDO->HelpText)
            ];
        }

        TSharedRef<SWrapBox> WrapBox = SNew(SWrapBox).UseAllottedWidth(true);
        int32 NumButtons = 0;

        for (TFieldIterator<UFunction> FuncIt(Class, EFieldIteratorFlags::IncludeSuper); FuncIt; ++FuncIt)
        {
            UFunction* Function = *FuncIt;

            const bool bCanExecute = (Function->NumParms == 0) && Function->HasAllFunctionFlags(FUNC_Exec);

            if (bCanExecute)
            {
                ++NumButtons;

                const FString ButtonCaption = FName::NameToDisplayString(*Function->GetName(), false);

                //@TODO: Expose the code in UK2Node_CallFunction::GetUserFacingFunctionName / etc...
                FString Tooltip = Function->GetToolTipText().ToString();
                if (Tooltip.IsEmpty())
                {
                    Tooltip = Function->GetName();
                }

                TWeakObjectPtr<UFunction> WeakFunctionPtr(Function);

                WrapBox->AddSlot()
                [
                    SNew(SButton)
                    .Text(ButtonCaption)
                    .OnClicked(	FOnClicked::CreateSP(this, &FEditorUtilityInstanceDetails::OnExecuteAction, WeakFunctionPtr) )
                    .ToolTipText(Tooltip)
                ];

            }
        }

        if (NumButtons > 0)
        {
            ActionsCategory.AddCustomRow(TEXT(""))
            [
                WrapBox
            ];
        }
    }

    // Hide the hint property
    if (!bFoundAnyCDOs)
    {
        DetailLayoutBuilder.HideProperty(TEXT("HelpText"));
    }
}