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 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 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 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 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 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] ); } } } }
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 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 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 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 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 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 FFoliageTypePaintingCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailLayoutBuilder) { // Hide categories we are not going to customize FFoliageTypeCustomizationHelpers::HideFoliageCategory(DetailLayoutBuilder, "Procedural"); FFoliageTypeCustomizationHelpers::HideFoliageCategory(DetailLayoutBuilder, "Reapply"); // Show all the properties with a reapply condition or that depend on another variable to be relevant TMap<const FName, IDetailPropertyRow*> PropertyRowsByName; ShowFoliagePropertiesForCategory(DetailLayoutBuilder, "Painting", PropertyRowsByName); ShowFoliagePropertiesForCategory(DetailLayoutBuilder, "Placement", PropertyRowsByName); ShowFoliagePropertiesForCategory(DetailLayoutBuilder, "InstanceSettings", PropertyRowsByName); // Density adjustment factor should only be visible when reapplying FFoliageTypeCustomizationHelpers::ModifyFoliagePropertyRow(*PropertyRowsByName.Find(GET_MEMBER_NAME_CHECKED(UFoliageType, DensityAdjustmentFactor)), TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FFoliageTypePaintingCustomization::GetReapplyModeVisibility)), TAttribute<bool>()); // Set the scale visibility attribute for each axis Scaling = DetailLayoutBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFoliageType, Scaling)); ReapplyScaling = DetailLayoutBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFoliageType, ReapplyScaling)); FFoliageTypeCustomizationHelpers::ModifyFoliagePropertyRow(*PropertyRowsByName.Find(GET_MEMBER_NAME_CHECKED(UFoliageType, ScaleX)), TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FFoliageTypePaintingCustomization::GetScaleVisibility, EAxis::X)), TAttribute<bool>()); FFoliageTypeCustomizationHelpers::ModifyFoliagePropertyRow(*PropertyRowsByName.Find(GET_MEMBER_NAME_CHECKED(UFoliageType, ScaleY)), TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FFoliageTypePaintingCustomization::GetScaleVisibility, EAxis::Y)), TAttribute<bool>()); FFoliageTypeCustomizationHelpers::ModifyFoliagePropertyRow(*PropertyRowsByName.Find(GET_MEMBER_NAME_CHECKED(UFoliageType, ScaleZ)), TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FFoliageTypePaintingCustomization::GetScaleVisibility, EAxis::Z)), TAttribute<bool>()); }
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 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 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 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 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 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 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 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 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 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 ); }
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 FSpriteDetailsCustomization::BuildRenderingSection(IDetailCategoryBuilder& RenderingCategory, IDetailLayoutBuilder& DetailLayout) { // Add the rendering geometry mode into the parent container (renamed) const FString RenderGeometryTypePropertyPath = FString::Printf(TEXT("%s.%s"), GET_MEMBER_NAME_STRING_CHECKED(UPaperSprite, RenderGeometry), GET_MEMBER_NAME_STRING_CHECKED(FSpritePolygonCollection, GeometryType)); RenderingCategory.AddProperty(DetailLayout.GetProperty(*RenderGeometryTypePropertyPath)) .DisplayName(LOCTEXT("RenderGeometryType", "Render Geometry Type")); // Show the rendering geometry settings TSharedRef<IPropertyHandle> RenderGeometry = DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UPaperSprite, RenderGeometry)); IDetailPropertyRow& RenderGeometryProperty = RenderingCategory.AddProperty(RenderGeometry); // Add the render polygons into advanced (renamed) const FString RenderGeometryPolygonsPropertyPath = FString::Printf(TEXT("%s.%s"), GET_MEMBER_NAME_STRING_CHECKED(UPaperSprite, RenderGeometry), GET_MEMBER_NAME_STRING_CHECKED(FSpritePolygonCollection, Polygons)); RenderingCategory.AddProperty(DetailLayout.GetProperty(*RenderGeometryPolygonsPropertyPath), EPropertyLocation::Advanced) .DisplayName(LOCTEXT("RenderPolygons", "Render Polygons")); }
void FIOSTargetSettingsCustomization::BuildImageRow(IDetailLayoutBuilder& DetailLayout, IDetailCategoryBuilder& Category, const FPlatformIconInfo& Info, const FVector2D& MaxDisplaySize) { const FString AutomaticImagePath = EngineGraphicsPath / Info.IconPath; const FString TargetImagePath = GameGraphicsPath / Info.IconPath; Category.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(MaxDisplaySize) ] ]; }
void FWorldSettingsDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) { IDetailCategoryBuilder& Category = DetailBuilder.EditCategory("GameMode"); CustomizeGameInfoProperty("DefaultGameMode", DetailBuilder, Category); AddLightmapCustomization(DetailBuilder); }
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION void FBehaviorDecoratorDetails::CustomizeDetails( IDetailLayoutBuilder& DetailLayout ) { FText AbortModeDesc = LOCTEXT("ObserverTitle","Observer aborts"); PropUtils = &(DetailLayout.GetPropertyUtilities().Get()); TArray<TWeakObjectPtr<UObject> > EditedObjects; DetailLayout.GetObjectsBeingCustomized(EditedObjects); for (int32 i = 0; i < EditedObjects.Num(); i++) { UBTDecorator* MyDecorator = Cast<UBTDecorator>(EditedObjects[i].Get()); if (MyDecorator) { MyNode = MyDecorator; break; } } UpdateAllowedAbortModes(); ModeProperty = DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UBTDecorator, FlowAbortMode)); // dynamic FlowAbortMode combo IDetailCategoryBuilder& FlowCategory = DetailLayout.EditCategory( "FlowControl" ); IDetailPropertyRow& AbortModeRow = FlowCategory.AddProperty(ModeProperty); AbortModeRow.IsEnabled(TAttribute<bool>(this, &FBehaviorDecoratorDetails::GetAbortModeEnabled)); AbortModeRow.Visibility(TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FBehaviorDecoratorDetails::GetModeVisibility))); AbortModeRow.CustomWidget() .NameContent() [ ModeProperty->CreatePropertyNameWidget(AbortModeDesc) ] .ValueContent() [ SNew(SComboButton) .OnGetMenuContent(this, &FBehaviorDecoratorDetails::OnGetAbortModeContent) .ContentPadding(FMargin( 2.0f, 2.0f )) .ButtonContent() [ SNew(STextBlock) .Text(this, &FBehaviorDecoratorDetails::GetCurrentAbortModeDesc) .Font(IDetailLayoutBuilder::GetDetailFont()) ] ]; InitPropertyValues(); }
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 FProceduralFoliageComponentDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) { const FName ProceduralFoliageCategoryName("ProceduralFoliage"); IDetailCategoryBuilder& ProceduralFoliageCategory = DetailBuilder.EditCategory(ProceduralFoliageCategoryName); const FText ResimulateText = NSLOCTEXT("ProceduralFoliageComponentDetails","ResimulateButtonText", "Resimulate" ); TArray< TWeakObjectPtr<UObject> > ObjectsBeingCustomized; DetailBuilder.GetObjectsBeingCustomized( ObjectsBeingCustomized ); for( TWeakObjectPtr<UObject>& Object : ObjectsBeingCustomized ) { UProceduralFoliageComponent* Component = Cast<UProceduralFoliageComponent>( Object.Get() ); if( ensure( Component ) ) { SelectedComponents.Add( Component ); } } TArray<TSharedRef<IPropertyHandle>> AllProperties; bool bSimpleProperties = true; bool bAdvancedProperties = false; // Add all properties in the category in order ProceduralFoliageCategory.GetDefaultProperties(AllProperties, bSimpleProperties, bAdvancedProperties); for( auto& Property : AllProperties ) { ProceduralFoliageCategory.AddProperty(Property); } FDetailWidgetRow& NewRow = ProceduralFoliageCategory.AddCustomRow( ResimulateText ); NewRow.ValueContent() .MaxDesiredWidth(120.f) [ SNew(SButton) .OnClicked( this, &FProceduralFoliageComponentDetails::OnResimulateClicked ) .ToolTipText( NSLOCTEXT("ProceduralFoliageComponentDetails","ResimulateButton_Tooltip", "Resimulates the ProceduralFoliage asset and replaces previously spawned instances" ) ) .IsEnabled( this, &FProceduralFoliageComponentDetails::IsResimulateEnabled ) [ SNew( STextBlock ) .Font( IDetailLayoutBuilder::GetDetailFont() ) .Text( ResimulateText ) ] ]; }