void FAbcImportSettingsCustomization::CustomizeDetails(IDetailLayoutBuilder& LayoutBuilder) { TSharedRef<IPropertyHandle> ImportType = LayoutBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UAbcImportSettings, ImportType)); uint8 EnumValue; ImportType->GetValue(EnumValue); IDetailCategoryBuilder& CompressionBuilder = LayoutBuilder.EditCategory("Compression"); CompressionBuilder.SetCategoryVisibility(EnumValue == (uint8)EAlembicImportType::Skeletal); IDetailCategoryBuilder& StaticMeshBuilder = LayoutBuilder.EditCategory("StaticMesh"); StaticMeshBuilder.SetCategoryVisibility(EnumValue == (uint8)EAlembicImportType::StaticMesh); FSimpleDelegate OnImportTypeChangedDelegate = FSimpleDelegate::CreateSP(this, &FAbcImportSettingsCustomization::OnImportTypeChanged, &LayoutBuilder); ImportType->SetOnPropertyValueChanged(OnImportTypeChangedDelegate); if (UAbcImportSettings::Get()->bReimport) { UEnum* ImportTypeEnum = FindObject<UEnum>(ANY_PACKAGE, TEXT("EAlembicImportType")); static FText RestrictReason = FText::FromString("Unable to change type while reimporting"); TSharedPtr<FPropertyRestriction> EnumRestriction = MakeShareable(new FPropertyRestriction(RestrictReason)); for (uint8 EnumIndex = 0; EnumIndex < (ImportTypeEnum->GetMaxEnumValue() + 1); ++EnumIndex) { if (EnumValue != EnumIndex) { const FString RestrictValue = ImportTypeEnum->GetDisplayNameTextByValue(EnumIndex).ToString(); EnumRestriction->AddValue(RestrictValue); } } ImportType->AddRestriction(EnumRestriction.ToSharedRef()); } }
void FPrimitiveComponentDetails::AddAdvancedSubCategory( IDetailLayoutBuilder& DetailBuilder, FName MainCategoryName, FName SubCategoryName) { TArray<TSharedRef<IPropertyHandle> > SubCategoryProperties; IDetailCategoryBuilder& SubCategory = DetailBuilder.EditCategory(SubCategoryName); const bool bSimpleProperties = false; const bool bAdvancedProperties = true; SubCategory.GetDefaultProperties( SubCategoryProperties, bSimpleProperties, bAdvancedProperties ); if( SubCategoryProperties.Num() > 0 ) { IDetailCategoryBuilder& MainCategory = DetailBuilder.EditCategory(MainCategoryName); const bool bForAdvanced = true; IDetailGroup& Group = MainCategory.AddGroup( SubCategoryName, FText::FromName(SubCategoryName), bForAdvanced ); for( int32 PropertyIndex = 0; PropertyIndex < SubCategoryProperties.Num(); ++PropertyIndex ) { TSharedRef<IPropertyHandle>& PropertyHandle = SubCategoryProperties[PropertyIndex]; // Ignore customized properties if( !PropertyHandle->IsCustomized() ) { Group.AddPropertyRow( SubCategoryProperties[PropertyIndex] ); } } } }
/** 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::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) { // Make sure sprite properties are near the top IDetailCategoryBuilder& SpriteCategory = DetailLayout.EditCategory("Sprite", FText::GetEmpty(), ECategoryPriority::Important); BuildSpriteSection(SpriteCategory, DetailLayout); // Build the rendering category IDetailCategoryBuilder& RenderingCategory = DetailLayout.EditCategory("Rendering"); BuildRenderingSection(RenderingCategory, DetailLayout); // Build the collision category IDetailCategoryBuilder& CollisionCategory = DetailLayout.EditCategory("Collision"); BuildCollisionSection(CollisionCategory, DetailLayout); }
void FFMODAmbientSoundDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) { const TArray< TWeakObjectPtr<UObject> >& SelectedObjects = DetailBuilder.GetDetailsView().GetSelectedObjects(); for( int32 ObjectIndex = 0; !AmbientSound.IsValid() && ObjectIndex < SelectedObjects.Num(); ++ObjectIndex ) { const TWeakObjectPtr<UObject>& CurrentObject = SelectedObjects[ObjectIndex]; if ( CurrentObject.IsValid() ) { AmbientSound = Cast<AFMODAmbientSound>(CurrentObject.Get()); } } DetailBuilder.EditCategory(TEXT("Sound")) .AddCustomRow(FText::GetEmpty()) [ SNew(SVerticalBox) + SVerticalBox::Slot() .Padding( 0, 2.0f, 0, 0 ) .FillHeight(1.0f) .VAlign( VAlign_Center ) [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .AutoWidth() .Padding( 2.0f, 0.0f ) .VAlign(VAlign_Center) .HAlign(HAlign_Left) [ SNew(SButton) .VAlign(VAlign_Center) .OnClicked( this, &FFMODAmbientSoundDetails::OnEditSoundClicked ) .Text( LOCTEXT("EditAsset", "Edit") ) .ToolTipText( LOCTEXT("EditAssetToolTip", "Edit this sound cue") ) ] +SHorizontalBox::Slot() .AutoWidth() .Padding( 2.0f, 0.0f ) .VAlign(VAlign_Center) .HAlign(HAlign_Left) [ SNew(SButton) .VAlign(VAlign_Center) .OnClicked( this, &FFMODAmbientSoundDetails::OnPlaySoundClicked ) .Text( LOCTEXT("PlaySoundCue", "Play") ) ] +SHorizontalBox::Slot() .AutoWidth() .Padding( 2.0f, 0.0f ) .VAlign(VAlign_Center) .HAlign(HAlign_Left) [ SNew(SButton) .VAlign(VAlign_Center) .OnClicked( this, &FFMODAmbientSoundDetails::OnStopSoundClicked ) .Text( LOCTEXT("StopSoundCue", "Stop") ) ] ] ]; }
void FDerivedCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) { // So now we're customizing UDerivedClass object // First, try to hide an inherited property { // 'HideMe' property is defined in UBaseClass TSharedPtr<IPropertyHandle> PropertyToHide = DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UDerivedClass, HideMe)); PropertyToHide->MarkHiddenByCustomization(); } { // Now grab any struct property (or property that has customized property value widget) // Note that this is property defined in UDerivedClass! TSharedPtr<IPropertyHandle> TestProperty = DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UDerivedClass, Vector2dProperty)); // Hide this property TestProperty->MarkHiddenByCustomization(); // Get a category so we can add a TestProperty back to the panel IDetailCategoryBuilder& DerivedCategory = DetailLayout.EditCategory("Derived"); DerivedCategory.AddCustomRow(LOCTEXT("DerivedObjectLabel", "Derived")) .NameContent() [ TestProperty->CreatePropertyNameWidget() ] .ValueContent() [ // This will return NullWidget - thats the problem TestProperty->CreatePropertyValueWidget() ]; } }
void FSpriteComponentDetailsCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) { // Create a category so this is displayed early in the properties IDetailCategoryBuilder& SpriteCategory = DetailBuilder.EditCategory("Sprite", FText::GetEmpty(), ECategoryPriority::Important); ObjectsBeingCustomized.Empty(); DetailBuilder.GetObjectsBeingCustomized(/*out*/ ObjectsBeingCustomized); if (ObjectsBeingCustomized.Num() > 1) { // Expose merge buttons FDetailWidgetRow& MergeRow = SpriteCategory.AddCustomRow(LOCTEXT("MergeSearchText", "Merge")) .WholeRowContent() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .AutoWidth() .Padding(2.0f, 0.0f) .VAlign(VAlign_Center) .HAlign(HAlign_Left) [ SNew(SButton) .Text(LOCTEXT("MergeSprites", "Merge Sprites")) .ToolTipText(LOCTEXT("MergeSprites_Tooltip", "Merges all selected sprite components into entries on a single grouped sprite component")) .OnClicked(this, &FSpriteComponentDetailsCustomization::MergeSprites) ] ]; } }
void AddCodeViewCategory(IDetailLayoutBuilder& DetailBuilder, const FGetSelectedActors& GetSelectedActors) { FString SolutionPath; if(FDesktopPlatformModule::Get()->GetSolutionPath(SolutionPath)) { TSharedRef< CodeView::SCodeView > CodeViewWidget = SNew( CodeView::SCodeView ) .GetSelectedActors( GetSelectedActors ); // Only start out expanded if we're already in "ready to populate" mode. This is because we don't want // to immediately start digesting symbols as soon as the widget is visible. Instead, when the user // expands the section, we'll start loading symbols. However, this state is remembered even after // the widget is destroyed. const bool bShouldInitiallyExpand = CodeViewWidget->IsReadyToPopulate(); DetailBuilder.EditCategory( "CodeView", NSLOCTEXT("ActorDetails", "CodeViewSection", "Code View"), ECategoryPriority::Uncommon ) .InitiallyCollapsed( !bShouldInitiallyExpand ) // The expansion state should not be restored .RestoreExpansionState( false ) .OnExpansionChanged( FOnBooleanValueChanged::CreateSP( CodeViewWidget, &CodeView::SCodeView::OnDetailSectionExpansionChanged ) ) .AddCustomRow( NSLOCTEXT("ActorDetails", "CodeViewSection", "Code View") ) [ // @todo editcode1: Width of item is too big for detail view?! CodeViewWidget ]; } }
void FComponentMaterialCategory::Create( IDetailLayoutBuilder& DetailBuilder ) { NotifyHook = DetailBuilder.GetPropertyUtilities()->GetNotifyHook(); FMaterialListDelegates MaterialListDelegates; MaterialListDelegates.OnGetMaterials.BindSP( this, &FComponentMaterialCategory::OnGetMaterialsForView ); MaterialListDelegates.OnMaterialChanged.BindSP( this, &FComponentMaterialCategory::OnMaterialChanged ); TSharedRef<FMaterialList> MaterialList = MakeShareable( new FMaterialList( DetailBuilder, MaterialListDelegates ) ); bool bAnyMaterialsToDisplay = false; for( FMaterialIterator It( SelectedComponents ); It; ++It ) { UActorComponent* CurrentComponent = It.GetComponent(); if( !bAnyMaterialsToDisplay ) { bAnyMaterialsToDisplay = true; break; } } // only show the category if there are materials to display if( bAnyMaterialsToDisplay ) { // Make a category for the materials. IDetailCategoryBuilder& MaterialCategory = DetailBuilder.EditCategory("Materials", FText::GetEmpty(), ECategoryPriority::TypeSpecific ); MaterialCategory.AddCustomBuilder( MaterialList ); } }
void FFormatTextDetails::CustomizeDetails( IDetailLayoutBuilder& DetailLayout ) { const TArray<TWeakObjectPtr<UObject>> Objects = DetailLayout.GetDetailsView().GetSelectedObjects(); check(Objects.Num() > 0); if (Objects.Num() == 1) { TargetNode = CastChecked<UK2Node_FormatText>(Objects[0].Get()); TSharedRef<IPropertyHandle> PropertyHandle = DetailLayout.GetProperty(FName("PinNames"), UK2Node_FormatText::StaticClass()); IDetailCategoryBuilder& InputsCategory = DetailLayout.EditCategory("Arguments", LOCTEXT("FormatTextDetailsArguments", "Arguments")); InputsCategory.AddCustomRow( LOCTEXT("FunctionNewInputArg", "New") ) [ SNew(SBox) .HAlign(HAlign_Right) [ SNew(SButton) .Text(LOCTEXT("FunctionNewInputArg", "New")) .OnClicked(this, &FFormatTextDetails::OnAddNewArgument) .IsEnabled(this, &FFormatTextDetails::CanEditArguments) ] ]; Layout = MakeShareable( new FFormatTextLayout(TargetNode) ); InputsCategory.AddCustomBuilder( Layout.ToSharedRef() ); } UPackage::PackageDirtyStateChangedEvent.AddSP(this, &FFormatTextDetails::OnEditorPackageModified); }
void FAnimMontageSegmentDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) { IDetailCategoryBuilder& SegmentCategory = DetailBuilder.EditCategory("Animation Segment", LOCTEXT("AnimationSegmentCategoryTitle", "Animation Segment") ); SegmentCategory.AddProperty("AnimSegment.AnimReference").DisplayName( LOCTEXT("AnimationReferenceLabel", "Animation Reference") ); SegmentCategory.AddProperty("AnimSegment.AnimStartTime").DisplayName( LOCTEXT("StartTimeLabel", "Start Time") ); SegmentCategory.AddProperty("AnimSegment.AnimEndTime").DisplayName( LOCTEXT("EndTimeLabel", "End Time") ); SegmentCategory.AddProperty("AnimSegment.AnimPlayRate").DisplayName( LOCTEXT("PlayRateLabel", "Play Rate") ); SegmentCategory.AddProperty("AnimSegment.LoopingCount").DisplayName( LOCTEXT("LoopCountLabel", "Loop Count") ); TSharedPtr<IPropertyHandle> InPropertyHandle = DetailBuilder.GetProperty("AnimSegment.AnimReference"); UObject *Object = NULL; InPropertyHandle->GetValue(Object); UAnimSequenceBase *AnimRef = Cast<UAnimSequenceBase>(Object); USkeleton *Skeleton = NULL; if(AnimRef != NULL) { Skeleton = AnimRef->GetSkeleton(); } SegmentCategory.AddCustomRow(FText::GetEmpty(), false) [ SNew(SAnimationSegmentViewport) .Skeleton(Skeleton) .AnimRef(AnimRef) .AnimRefPropertyHandle(DetailBuilder.GetProperty("AnimSegment.AnimReference")) .StartTimePropertyHandle(DetailBuilder.GetProperty("AnimSegment.AnimStartTime")) .EndTimePropertyHandle(DetailBuilder.GetProperty("AnimSegment.AnimEndTime")) ]; }
void FEditorTutorialDetailsCustomization::CustomizeDetails( IDetailLayoutBuilder& DetailLayout ) { struct Local { static FReply OnLaunchClicked(UEditorTutorial* Tutorial) { FLevelEditorModule& LevelEditorModule = FModuleManager::Get().GetModuleChecked<FLevelEditorModule>("LevelEditor"); IIntroTutorials& IntroTutorials = FModuleManager::Get().GetModuleChecked<IIntroTutorials>("IntroTutorials"); IntroTutorials.LaunchTutorial(Tutorial, true, LevelEditorModule.GetLevelEditorTab()->GetParentWindow()); return FReply::Handled(); } }; TArray<TWeakObjectPtr<UObject>> Objects; DetailLayout.GetObjectsBeingCustomized(Objects); check(Objects.Num() > 0); UEditorTutorial* Tutorial = CastChecked<UEditorTutorial>(Objects[0].Get()); IDetailCategoryBuilder& CategoryBuilder = DetailLayout.EditCategory(TEXT("Testing"), LOCTEXT("TestingSection", "Testing"), ECategoryPriority::Important); CategoryBuilder.AddCustomRow(LOCTEXT("LaunchButtonLabel", "Launch")) .WholeRowContent() .VAlign(VAlign_Center) .HAlign(HAlign_Left) [ SNew(SButton) .OnClicked(FOnClicked::CreateStatic(&Local::OnLaunchClicked, Tutorial)) .Text(LOCTEXT("LaunchButtonLabel", "Launch")) .ToolTipText(LOCTEXT("LaunchButtonTooltip", "Test this tutorial.")) ]; }
void FMatineeActorDetails::CustomizeDetails( IDetailLayoutBuilder& DetailLayout ) { const TArray< TWeakObjectPtr<UObject> >& SelectedObjects = DetailLayout.GetDetailsView().GetSelectedObjects(); for( int32 ObjectIndex = 0; ObjectIndex < SelectedObjects.Num(); ++ObjectIndex ) { const TWeakObjectPtr<UObject>& CurrentObject = SelectedObjects[ObjectIndex]; if ( CurrentObject.IsValid() ) { AMatineeActor* CurrentMatineeActor = Cast<AMatineeActor>(CurrentObject.Get()); if (CurrentMatineeActor != NULL) { MatineeActor = CurrentMatineeActor; break; } } } DetailLayout.EditCategory( "MatineeActor", NSLOCTEXT("MatineeActorDetails", "MatineeActor", "Matinee Actor"), ECategoryPriority::Important ) .AddCustomRow( NSLOCTEXT("MatineeActorDetails", "OpenMatinee", "Open Matinee") ) [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .FillWidth(1.f) .Padding(0, 5, 10, 5) [ SNew(SButton) .ContentPadding(3) .VAlign(VAlign_Center) .HAlign(HAlign_Center) .OnClicked( this, &FMatineeActorDetails::OnOpenMatineeForActor ) .Text( NSLOCTEXT("MatineeActorDetails", "OpenMatinee", "Open Matinee") ) ] ]; }
void FRuntimeMeshComponentDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) { IDetailCategoryBuilder& RuntimeMeshCategory = DetailBuilder.EditCategory("RuntimeMesh"); const FText ConvertToStaticMeshText = LOCTEXT("ConvertToStaticMesh", "Create StaticMesh"); // Cache set of selected things SelectedObjectsList = DetailBuilder.GetDetailsView().GetSelectedObjects(); RuntimeMeshCategory.AddCustomRow(ConvertToStaticMeshText, false) .NameContent() [ SNullWidget::NullWidget ] .ValueContent() .VAlign(VAlign_Center) .MaxDesiredWidth(250) [ SNew(SButton) .VAlign(VAlign_Center) .ToolTipText(LOCTEXT("ConvertToStaticMeshTooltip", "Create a new StaticMesh asset using current geometry from this RuntimeMeshComponent. Does not modify instance.")) .OnClicked(this, &FRuntimeMeshComponentDetails::ClickedOnConvertToStaticMesh) .IsEnabled(this, &FRuntimeMeshComponentDetails::ConvertToStaticMeshEnabled) .Content() [ SNew(STextBlock) .Text(ConvertToStaticMeshText) ] ]; }
void AddStructToDetails(FName CategoryName, FName PropertyName, IDetailLayoutBuilder& DetailBuilder, bool bInline = true, bool bAdvanced = false) { IDetailCategoryBuilder& Category = DetailBuilder.EditCategory(CategoryName, TEXT(""), ECategoryPriority::Important); TSharedPtr<IPropertyHandle> Params = DetailBuilder.GetProperty(PropertyName); if (Params.IsValid()) { EPropertyLocation::Type PropertyLocation = bAdvanced ? EPropertyLocation::Advanced : EPropertyLocation::Default; if (bInline) { uint32 NumChildren = 0; Params->GetNumChildren(NumChildren); // add all collision properties for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex) { TSharedPtr<IPropertyHandle> ChildProperty = Params->GetChildHandle(ChildIndex); Category.AddProperty(ChildProperty, PropertyLocation); } } else { Category.AddProperty(Params, PropertyLocation); } } }
void FWorldSettingsDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) { IDetailCategoryBuilder& Category = DetailBuilder.EditCategory("GameMode"); CustomizeGameInfoProperty("DefaultGameMode", DetailBuilder, Category); AddLightmapCustomization(DetailBuilder); }
void FWorldSettingsDetails::AddLightmapCustomization( IDetailLayoutBuilder& DetailBuilder ) { IDetailCategoryBuilder& Category = DetailBuilder.EditCategory("Lightmass"); TSharedRef<FLightmapCustomNodeBuilder> LightMapGroupBuilder = MakeShareable(new FLightmapCustomNodeBuilder(DetailBuilder.GetThumbnailPool())); const bool bForAdvanced = true; Category.AddCustomBuilder(LightMapGroupBuilder, bForAdvanced); }
void FGroupedSpriteComponentDetailsCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) { // Create a category so this is displayed early in the properties IDetailCategoryBuilder& SpriteCategory = DetailBuilder.EditCategory("Sprite", FText::GetEmpty(), ECategoryPriority::Important); ObjectsBeingCustomized.Empty(); DetailBuilder.GetObjectsBeingCustomized(/*out*/ ObjectsBeingCustomized); TSharedRef<SWrapBox> ButtonBox = SNew(SWrapBox).UseAllottedWidth(true); const float MinButtonSize = 100.0f; const FMargin ButtonPadding(0.0f, 2.0f, 2.0f, 0.0f); // Split button ButtonBox->AddSlot() .Padding(ButtonPadding) [ SNew(SBox) .MinDesiredWidth(MinButtonSize) [ SNew(SButton) .VAlign(VAlign_Center) .HAlign(HAlign_Center) .Text(LOCTEXT("SplitSprites", "Split Sprites")) .ToolTipText(LOCTEXT("SplitSprites_Tooltip", "Splits all sprite instances into separate sprite actors or components")) .OnClicked(this, &FGroupedSpriteComponentDetailsCustomization::SplitSprites) ] ]; // Sort button ButtonBox->AddSlot() .Padding(ButtonPadding) [ SNew(SBox) .MinDesiredWidth(MinButtonSize) [ SNew(SButton) .VAlign(VAlign_Center) .HAlign(HAlign_Center) .Text(LOCTEXT("SortSprites", "Sort Sprites")) .ToolTipText(LOCTEXT("SortSprites_Tooltip", "Sorts all sprite instances according to the Translucency Sort Axis in the Rendering project settings")) .OnClicked(this, &FGroupedSpriteComponentDetailsCustomization::SortSprites) ] ]; // Add the action buttons FDetailWidgetRow& GroupActionsRow = SpriteCategory.AddCustomRow(LOCTEXT("GroupActionsSearchText", "Split Sort")) .WholeRowContent() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .FillWidth(1.0f) [ ButtonBox ] ]; }
void FActorComponentDetails::AddExperimentalWarningCategory( IDetailLayoutBuilder& DetailBuilder ) { const TArray< TWeakObjectPtr<UObject> >& SelectedObjects = DetailBuilder.GetDetailsView().GetSelectedObjects(); bool bIsExperimental = false; bool bIsEarlyAccess = false; for (const TWeakObjectPtr<UObject>& SelectedObjectPtr : SelectedObjects) { if (UObject* SelectedObject = SelectedObjectPtr.Get()) { bool bObjectClassIsExperimental, bObjectClassIsEarlyAccess; FObjectEditorUtils::GetClassDevelopmentStatus(SelectedObject->GetClass(), bObjectClassIsExperimental, bObjectClassIsEarlyAccess); bIsExperimental |= bObjectClassIsExperimental; bIsEarlyAccess |= bObjectClassIsEarlyAccess; } } if (bIsExperimental || bIsEarlyAccess) { const FName CategoryName(TEXT("Warning")); const FText CategoryDisplayName = LOCTEXT("WarningCategoryDisplayName", "Warning"); const FText WarningText = bIsExperimental ? LOCTEXT("ExperimentalClassWarning", "Uses experimental class") : LOCTEXT("EarlyAccessClassWarning", "Uses early access class"); const FText SearchString = WarningText; const FText Tooltip = bIsExperimental ? LOCTEXT("ExperimentalClassTooltip", "Here be dragons! Uses one or more unsupported 'experimental' classes") : LOCTEXT("EarlyAccessClassTooltip", "Uses one or more 'early access' classes"); const FString ExcerptName = bIsExperimental ? TEXT("ComponentUsesExperimentalClass") : TEXT("ComponentUsesEarlyAccessClass"); const FSlateBrush* WarningIcon = FEditorStyle::GetBrush(bIsExperimental ? "PropertyEditor.ExperimentalClass" : "PropertyEditor.EarlyAccessClass"); IDetailCategoryBuilder& WarningCategory = DetailBuilder.EditCategory(CategoryName, CategoryDisplayName, ECategoryPriority::Transform); FDetailWidgetRow& WarningRow = WarningCategory.AddCustomRow(SearchString) .WholeRowContent() [ SNew(SHorizontalBox) .ToolTip(IDocumentation::Get()->CreateToolTip(Tooltip, nullptr, TEXT("Shared/LevelEditor"), ExcerptName)) .Visibility(EVisibility::Visible) + SHorizontalBox::Slot() .VAlign(VAlign_Center) .AutoWidth() .Padding(4.0f, 0.0f, 0.0f, 0.0f) [ SNew(SImage) .Image(WarningIcon) ] +SHorizontalBox::Slot() .VAlign(VAlign_Center) .AutoWidth() .Padding(4.0f, 0.0f, 0.0f, 0.0f) [ SNew(STextBlock) .Text(WarningText) .Font(IDetailLayoutBuilder::GetDetailFont()) ] ]; } }
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); } } }
void FParticleSystemComponentDetails::CustomizeDetails(IDetailLayoutBuilder& InDetailLayout) { this->DetailLayout = &InDetailLayout; InDetailLayout.EditCategory("Particles", FText::GetEmpty(), ECategoryPriority::Important); IDetailCategoryBuilder& CustomCategory = InDetailLayout.EditCategory("EmitterActions", NSLOCTEXT("ParticleSystemComponentDetails", "EmitterActionCategoryName", "Emitter Actions"), ECategoryPriority::Important); CustomCategory.AddCustomRow(FText::GetEmpty()) .WholeRowContent() .HAlign(HAlign_Left) [ SNew( SBox ) .MaxDesiredWidth(300.f) [ SNew(SUniformGridPanel) .SlotPadding(2.0f) + SUniformGridPanel::Slot(0, 0) [ SNew(SButton) .OnClicked(this, &FParticleSystemComponentDetails::OnAutoPopulateClicked) .ToolTipText(NSLOCTEXT("ParticleSystemComponentDetails", "AutoPopulateButtonTooltip", "Copies properties from the source particle system into the instanced parameters of this system")) .HAlign(HAlign_Center) [ SNew(STextBlock) .Text(NSLOCTEXT("ParticleSystemComponentDetails", "AutoPopulateButton", "Expose Parameter")) ] ] + SUniformGridPanel::Slot(1, 0) [ SNew(SButton) .OnClicked(this, &FParticleSystemComponentDetails::OnResetEmitter) .ToolTipText(NSLOCTEXT("ParticleSystemComponentDetails", "ResetEmitterButtonTooltip", "Resets the selected particle system.")) .HAlign(HAlign_Center) [ SNew(STextBlock) .Text(NSLOCTEXT("ParticleSystemComponentDetails", "ResetEmitterButton", "Reset Emitter")) ] ] ] ]; }
void FAndroidSDKSettingsCustomization::BuildSDKPathSection(IDetailLayoutBuilder& DetailLayout) { #if PLATFORM_MAC IDetailCategoryBuilder& SDKConfigCategory = DetailLayout.EditCategory(TEXT("SDKConfig")); // hide the property on Mac only TSharedRef<IPropertyHandle> JavaPathProperty = DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UAndroidSDKSettings, JavaPath)); SDKConfigCategory.AddProperty(JavaPathProperty) .Visibility(EVisibility::Hidden); #endif }
void FSpriteDetailsCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) { // Make sure sprite properties are near the top IDetailCategoryBuilder& SpriteCategory = DetailLayout.EditCategory("Sprite", FText::GetEmpty(), ECategoryPriority::Important); BuildSpriteSection(SpriteCategory, DetailLayout); // Build the socket category { IDetailCategoryBuilder& SocketCategory = DetailLayout.EditCategory("Sockets"); SocketCategory.AddProperty(GET_MEMBER_NAME_CHECKED(UPaperSprite, Sockets)); } // Build the collision category IDetailCategoryBuilder& CollisionCategory = DetailLayout.EditCategory("Collision"); BuildCollisionSection(CollisionCategory, DetailLayout); // Build the rendering category IDetailCategoryBuilder& RenderingCategory = DetailLayout.EditCategory("Rendering"); BuildRenderingSection(RenderingCategory, DetailLayout); }
void FIOSTargetSettingsCustomization::BuildIconSection(IDetailLayoutBuilder& DetailLayout) { IDetailCategoryBuilder& RequiredIconCategory = DetailLayout.EditCategory(TEXT("Required Icons")); IDetailCategoryBuilder& OptionalIconCategory = DetailLayout.EditCategory(TEXT("Optional Icons")); // Add the icons for (const FPlatformIconInfo& Info : IconNames) { const FVector2D IconImageMaxSize(Info.IconRequiredSize); IDetailCategoryBuilder& IconCategory = (Info.RequiredState == FPlatformIconInfo::Required) ? RequiredIconCategory : OptionalIconCategory; BuildImageRow(DetailLayout, IconCategory, Info, IconImageMaxSize); } // Add the launch images IDetailCategoryBuilder& LaunchImageCategory = DetailLayout.EditCategory(TEXT("Launch Images")); const FVector2D LaunchImageMaxSize(150.0f, 150.0f); for (const FPlatformIconInfo& Info : LaunchImageNames) { BuildImageRow(DetailLayout, LaunchImageCategory, Info, LaunchImageMaxSize); } }
void FTreeArchitectCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) { IDetailCategoryBuilder& Category = DetailBuilder.EditCategory("Tree"); Category.AddCustomRow(LOCTEXT("TreeArchitectCommand_FilterBuildTree", "build tree")) .ValueContent() [ SNew(SButton) .Text(LOCTEXT("TreeCommand_BuildTree", "Build Tree")) .OnClicked(FOnClicked::CreateStatic(&FTreeArchitectCustomization::BuildTree, &DetailBuilder)) ]; }
void FAndroidTargetSettingsCustomization::BuildIconSection(IDetailLayoutBuilder& DetailLayout) { // Icon category IDetailCategoryBuilder& IconCategory = DetailLayout.EditCategory(TEXT("Icons")); IconCategory.AddCustomRow(TEXT("Icons Hyperlink"), false) .WholeRowWidget [ SNew(SBox) .HAlign(HAlign_Center) [ SNew(SHyperlinkLaunchURL, TEXT("http://developer.android.com/design/style/iconography.html")) .Text(LOCTEXT("AndroidDeveloperIconographyPage", "Android Developer Page on Iconography")) .ToolTipText(LOCTEXT("AndroidDeveloperIconographyPageTooltip", "Opens a page on Android Iconography")) ] ]; for (const FPlatformIconInfo& Info : IconNames) { const FString AutomaticImagePath = EngineAndroidPath / Info.IconPath; const FString TargetImagePath = GameAndroidPath / Info.IconPath; IconCategory.AddCustomRow(Info.IconName.ToString()) .NameContent() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .Padding( FMargin( 0, 1, 0, 1 ) ) .FillWidth(1.0f) [ SNew(STextBlock) .Text(Info.IconName) .Font(DetailLayout.GetDetailFont()) ] ] .ValueContent() .MaxDesiredWidth(400.0f) .MinDesiredWidth(100.0f) [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .FillWidth(1.0f) .VAlign(VAlign_Center) [ SNew(SExternalImageReference, AutomaticImagePath, TargetImagePath) .FileDescription(Info.IconDescription) .RequiredSize(Info.IconRequiredSize) .MaxDisplaySize(FVector2D(Info.IconRequiredSize)) ] ]; } }
void FLightComponentDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) { // Mobility property is on the scene component base class not the light component and that is why we have to use USceneComponent::StaticClass TSharedRef<IPropertyHandle> MobilityHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULightComponent, Mobility), USceneComponent::StaticClass()); // Set a mobility tooltip specific to lights MobilityHandle->SetToolTipText(LOCTEXT("LightMobilityTooltip", "Mobility for lights controls what the light is allowed to do at runtime and therefore what rendering methods are used.\n* A movable light uses fully dynamic lighting and anything can change in game, however it has a large performance cost, typically proportional to the light's influence size.\n* A stationary light will only have its shadowing and bounced lighting from static geometry baked by Lightmass, all other lighting will be dynamic. It can change color and intensity in game. \n* A static light is fully baked into lightmaps and therefore has no performance cost, but also can't change in game.")); IDetailCategoryBuilder& LightCategory = DetailBuilder.EditCategory( "Light", FText::GetEmpty(), ECategoryPriority::TypeSpecific ); LightIntensityProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULightComponent, Intensity), ULightComponentBase::StaticClass()); IESBrightnessTextureProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULightComponent, IESTexture)); IESBrightnessEnabledProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULightComponent, bUseIESBrightness)); IESBrightnessScaleProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULightComponent, IESBrightnessScale)); if( !IESBrightnessEnabledProperty->IsValidHandle() ) { // Brightness and color should be listed first LightCategory.AddProperty( LightIntensityProperty ); LightCategory.AddProperty( DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULightComponent, LightColor), ULightComponentBase::StaticClass() ) ); } else { IDetailCategoryBuilder& LightProfilesCategory = DetailBuilder.EditCategory( "Light Profiles", FText::GetEmpty(), ECategoryPriority::TypeSpecific ); LightCategory.AddProperty( LightIntensityProperty ) .IsEnabled( TAttribute<bool>( this, &FLightComponentDetails::IsLightBrightnessEnabled ) ); LightCategory.AddProperty( DetailBuilder.GetProperty("LightColor", ULightComponentBase::StaticClass() ) ); LightProfilesCategory.AddProperty( IESBrightnessTextureProperty ); LightProfilesCategory.AddProperty( IESBrightnessEnabledProperty ) .IsEnabled( TAttribute<bool>( this, &FLightComponentDetails::IsUseIESBrightnessEnabled ) ); LightProfilesCategory.AddProperty( IESBrightnessScaleProperty) .IsEnabled( TAttribute<bool>( this, &FLightComponentDetails::IsIESBrightnessScaleEnabled ) ); } }
void FPluginMetadataCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) { TArray<TWeakObjectPtr<UObject>> Objects; DetailBuilder.GetObjectsBeingCustomized(Objects); if(Objects.Num() == 1 && Objects[0].IsValid()) { UPluginMetadataObject* PluginMetadata = Cast<UPluginMetadataObject>(Objects[0].Get()); if(PluginMetadata != nullptr && PluginMetadata->TargetIconPath.Len() > 0) { // Get the current icon path FString CurrentIconPath = PluginMetadata->TargetIconPath; if(!FPaths::FileExists(CurrentIconPath)) { CurrentIconPath = IPluginManager::Get().FindPlugin(TEXT("PluginBrowser"))->GetBaseDir() / TEXT("Resources") / TEXT("DefaultIcon128.png"); } // Add the customization to edit the icon row IDetailCategoryBuilder& ImageCategory = DetailBuilder.EditCategory(TEXT("Icon")); const FText IconDesc(NSLOCTEXT("PluginBrowser", "PluginIcon", "Icon")); ImageCategory.AddCustomRow(IconDesc) .NameContent() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .Padding( FMargin( 0, 1, 0, 1 ) ) .FillWidth(1.0f) [ SNew(STextBlock) .Text(IconDesc) .Font(DetailBuilder.GetDetailFont()) ] ] .ValueContent() .MaxDesiredWidth(500.0f) .MinDesiredWidth(100.0f) [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .FillWidth(1.0f) .VAlign(VAlign_Center) [ SNew(SExternalImageReference, CurrentIconPath, PluginMetadata->TargetIconPath) .FileDescription(IconDesc) .RequiredSize(FIntPoint(128, 128)) ] ]; } } }
void FLandscapeSplineDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) { IDetailCategoryBuilder& LandscapeSplineCategory = DetailBuilder.EditCategory("LandscapeSpline", FText::GetEmpty(), ECategoryPriority::Transform); LandscapeSplineCategory.AddCustomRow(FText::GetEmpty()) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .Padding(0, 0, 2, 0) .VAlign(VAlign_Center) .FillWidth(1) [ SNew(STextBlock) .Text(LOCTEXT("SelectAll", "Select all connected:")) ] + SHorizontalBox::Slot() .FillWidth(1) [ SNew(SButton) .Text(LOCTEXT("ControlPoints", "Control Points")) .HAlign(HAlign_Center) .OnClicked(this, &FLandscapeSplineDetails::OnSelectConnectedControlPointsButtonClicked) ] + SHorizontalBox::Slot() .FillWidth(1) [ SNew(SButton) .Text(LOCTEXT("Segments", "Segments")) .HAlign(HAlign_Center) .OnClicked(this, &FLandscapeSplineDetails::OnSelectConnectedSegmentsButtonClicked) ] ]; LandscapeSplineCategory.AddCustomRow(FText::GetEmpty()) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .Padding(0, 0, 2, 0) .VAlign(VAlign_Center) .FillWidth(1) [ SNew(SButton) .Text(LOCTEXT("Move Selected ControlPnts+Segs to Current level", "Move to current level")) .HAlign(HAlign_Center) .OnClicked(this, &FLandscapeSplineDetails::OnMoveToCurrentLevelButtonClicked) .IsEnabled(this, &FLandscapeSplineDetails::IsMoveToCurrentLevelButtonEnabled) ] ]; }
void FMoviePlayerSettingsDetails::CustomizeDetails( IDetailLayoutBuilder& DetailLayout ) { IDetailCategoryBuilder& MoviesCategory = DetailLayout.EditCategory("Movies"); StartupMoviesPropertyHandle = DetailLayout.GetProperty("StartupMovies"); TSharedRef<FDetailArrayBuilder> StartupMoviesBuilder = MakeShareable( new FDetailArrayBuilder( StartupMoviesPropertyHandle.ToSharedRef() ) ); StartupMoviesBuilder->OnGenerateArrayElementWidget( FOnGenerateArrayElementWidget::CreateSP(this, &FMoviePlayerSettingsDetails::GenerateArrayElementWidget) ); MoviesCategory.AddProperty( "bWaitForMoviesToComplete" ); MoviesCategory.AddProperty( "bMoviesAreSkippable" ); const bool bForAdvanced = false; MoviesCategory.AddCustomBuilder( StartupMoviesBuilder, bForAdvanced ); }