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 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 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 FActorComponentDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) { AddExperimentalWarningCategory(DetailBuilder); TSharedPtr<IPropertyHandle> PrimaryTickProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UActorComponent, PrimaryComponentTick)); // Defaults only show tick properties if (DetailBuilder.GetDetailsView().HasClassDefaultObject()) { IDetailCategoryBuilder& TickCategory = DetailBuilder.EditCategory("Tick"); TickCategory.AddProperty(PrimaryTickProperty->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTickFunction, bStartWithTickEnabled))); TickCategory.AddProperty(PrimaryTickProperty->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTickFunction, bTickEvenWhenPaused)), EPropertyLocation::Advanced); TickCategory.AddProperty(PrimaryTickProperty->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTickFunction, bAllowTickOnDedicatedServer)), EPropertyLocation::Advanced); } PrimaryTickProperty->MarkHiddenByCustomization(); }
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 FSkeletonNotifyDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) { IDetailCategoryBuilder& Category = DetailBuilder.EditCategory("Skeleton Notify", TEXT("Skeleton Notify") ); const FSlateFontInfo DetailFontInfo = IDetailLayoutBuilder::GetDetailFont(); Category.AddProperty("Name").DisplayName( TEXT("Notify Name") ); TSharedPtr<IPropertyHandle> InPropertyHandle = DetailBuilder.GetProperty("AnimationNames"); TArray< TWeakObjectPtr<UObject> > SelectedObjects = DetailBuilder.GetDetailsView().GetSelectedObjects(); UEditorSkeletonNotifyObj* EdObj = NULL; for(int i = 0; i < SelectedObjects.Num(); ++i) { UObject* Obj = SelectedObjects[0].Get(); EdObj = Cast<UEditorSkeletonNotifyObj>(Obj); if(EdObj) { break; } } if(EdObj) { Category.AddCustomRow(TEXT("Animations")) .NameContent() [ SNew(STextBlock) .ToolTipText(LOCTEXT("Animations_Tooltip", "List of animations that reference this notify")) .Text( LOCTEXT("AnimationsLabel","Animations") ) .Font( DetailFontInfo ) ] .ValueContent() [ SNew(SListView<TSharedPtr<FString>>) .ListItemsSource(&EdObj->AnimationNames) .OnGenerateRow(this, &FSkeletonNotifyDetails::MakeAnimationRow) ]; } }
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 FPrimitiveComponentDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) { // Get the objects being customized so we can enable/disable editing of 'Simulate Physics' DetailBuilder.GetObjectsBeingCustomized(ObjectsCustomized); // See if we are hiding Physics category TArray<FString> HideCategories; FEditorCategoryUtils::GetClassHideCategories(DetailBuilder.GetDetailsView().GetBaseClass(), HideCategories); if(!HideCategories.Contains("Materials")) { AddMaterialCategory(DetailBuilder); } TSharedRef<IPropertyHandle> MobilityHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UPrimitiveComponent, Mobility), USceneComponent::StaticClass()); MobilityHandle->SetToolTipText(LOCTEXT("PrimitiveMobilityTooltip", "Mobility for primitive components controls how they can be modified in game and therefore how they interact with lighting and physics.\n● A movable primitive component can be changed in game, but requires dynamic lighting and shadowing from lights which have a large performance cost.\n● A static primitive component can't be changed in game, but can have its lighting baked, which allows rendering to be very efficient.")); if(!HideCategories.Contains("Physics")) { AddPhysicsCategory(DetailBuilder); } if (!HideCategories.Contains("Collision")) { AddCollisionCategory(DetailBuilder); } if(!HideCategories.Contains("Lighting")) { AddLightingCategory(DetailBuilder); } AddAdvancedSubCategory( DetailBuilder, "Rendering", "TextureStreaming" ); AddAdvancedSubCategory( DetailBuilder, "Rendering", "LOD"); }
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() ) ] ]; } }
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")); } }
void FAmbientSoundDetails::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<AAmbientSound>(CurrentObject.Get()); } } DetailBuilder.EditCategory( "Sound", FText::GetEmpty(), ECategoryPriority::Important ) .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, &FAmbientSoundDetails::OnEditSoundCueClicked ) .IsEnabled( this, &FAmbientSoundDetails::IsEditSoundCueEnabled ) .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) [ // Add a menu for displaying all textures SNew( SComboButton ) .OnGetMenuContent( this, &FAmbientSoundDetails::OnGetSoundCueTemplates ) .VAlign( VAlign_Center ) .ContentPadding(2) .ButtonContent() [ SNew( STextBlock ) .ToolTipText( LOCTEXT("NewSoundCueToolTip", "Create a new sound cue with the desired template") ) .Text( LOCTEXT("NewSoundCue", "New") ) ] ] +SHorizontalBox::Slot() .AutoWidth() .Padding( 2.0f, 0.0f ) .VAlign(VAlign_Center) .HAlign(HAlign_Left) [ SNew(SButton) .VAlign(VAlign_Center) .OnClicked( this, &FAmbientSoundDetails::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, &FAmbientSoundDetails::OnStopSoundClicked ) .Text( LOCTEXT("StopSoundCue", "Stop") ) ] ] ]; DetailBuilder.EditCategory("Attenuation", FText::GetEmpty(), ECategoryPriority::TypeSpecific); DetailBuilder.EditCategory("Modulation", FText::GetEmpty(), ECategoryPriority::TypeSpecific); }
void FPaperTileMapDetailsCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) { const TArray< TWeakObjectPtr<UObject> >& SelectedObjects = DetailLayout.GetDetailsView().GetSelectedObjects(); MyDetailLayout = &DetailLayout; FNotifyHook* NotifyHook = DetailLayout.GetPropertyUtilities()->GetNotifyHook(); bool bEditingActor = false; UPaperTileMap* TileMap = nullptr; UPaperTileMapComponent* TileComponent = nullptr; for (int32 ObjectIndex = 0; ObjectIndex < SelectedObjects.Num(); ++ObjectIndex) { UObject* TestObject = SelectedObjects[ObjectIndex].Get(); if (AActor* CurrentActor = Cast<AActor>(TestObject)) { if (UPaperTileMapComponent* CurrentComponent = CurrentActor->FindComponentByClass<UPaperTileMapComponent>()) { bEditingActor = true; TileComponent = CurrentComponent; TileMap = CurrentComponent->TileMap; break; } } else if (UPaperTileMapComponent* TestComponent = Cast<UPaperTileMapComponent>(TestObject)) { TileComponent = TestComponent; TileMap = TestComponent->TileMap; break; } else if (UPaperTileMap* TestTileMap = Cast<UPaperTileMap>(TestObject)) { TileMap = TestTileMap; break; } } TileMapPtr = TileMap; TileMapComponentPtr = TileComponent; IDetailCategoryBuilder& TileMapCategory = DetailLayout.EditCategory("Tile Map"); TAttribute<EVisibility> InternalInstanceVis = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FPaperTileMapDetailsCustomization::GetVisibilityForInstancedOnlyProperties)); if (TileComponent != nullptr) { TileMapCategory .AddCustomRow(LOCTEXT( "TileMapInstancingControlsSearchText", "Edit New Promote Asset")) [ SNew(SVerticalBox) + SVerticalBox::Slot() .Padding(0.0f, 2.0f, 0.0f, 0.0f) .FillHeight(1.0f) .VAlign(VAlign_Center) [ SNew(SHorizontalBox) // Edit button +SHorizontalBox::Slot() .AutoWidth() .Padding( 2.0f, 0.0f ) .VAlign(VAlign_Center) .HAlign(HAlign_Left) [ SNew(SButton) .VAlign(VAlign_Center) .OnClicked(this, &FPaperTileMapDetailsCustomization::EnterTileMapEditingMode) .Visibility(this, &FPaperTileMapDetailsCustomization::GetNonEditModeVisibility) .Text( LOCTEXT("EditAsset", "Edit") ) .ToolTipText( LOCTEXT("EditAssetToolTip", "Edit this tile map") ) ] // Create new tile map button +SHorizontalBox::Slot() .AutoWidth() .Padding( 2.0f, 0.0f ) .VAlign(VAlign_Center) .HAlign(HAlign_Left) [ SNew(SButton) .VAlign(VAlign_Center) .OnClicked(this, &FPaperTileMapDetailsCustomization::OnNewButtonClicked) .Visibility(this, &FPaperTileMapDetailsCustomization::GetNewButtonVisiblity) .Text(LOCTEXT("CreateNewInstancedMap", "New")) .ToolTipText( LOCTEXT("CreateNewInstancedMapToolTip", "Create a new tile map") ) ] // Promote to asset button +SHorizontalBox::Slot() .AutoWidth() .Padding( 2.0f, 0.0f ) .VAlign(VAlign_Center) .HAlign(HAlign_Left) [ SNew(SButton) .VAlign(VAlign_Center) .OnClicked(this, &FPaperTileMapDetailsCustomization::OnPromoteButtonClicked) .Visibility(this, &FPaperTileMapDetailsCustomization::GetVisibilityForInstancedOnlyProperties) .Text(LOCTEXT("PromoteToAsset", "Promote to asset")) .ToolTipText(LOCTEXT("PromoteToAssetToolTip", "Save this tile map as a reusable asset")) ] ] ]; TileMapCategory.AddProperty(GET_MEMBER_NAME_CHECKED(UPaperTileMapComponent, TileMap)); } // Add the layer browser if (TileMap != nullptr) { TAttribute<EVisibility> LayerBrowserVis; LayerBrowserVis.Set(EVisibility::Visible); if (TileComponent != nullptr) { LayerBrowserVis = InternalInstanceVis; } FText TileLayerListText = LOCTEXT("TileLayerList", "Tile layer list"); TileMapCategory.AddCustomRow(TileLayerListText) .Visibility(LayerBrowserVis) [ SNew(SVerticalBox) +SVerticalBox::Slot() .AutoHeight() [ SNew(STextBlock) .Font(DetailLayout.GetDetailFont()) .Text(TileLayerListText) ] +SVerticalBox::Slot() [ SNew(STileLayerList, TileMap, NotifyHook) ] ]; } // Add all of the properties from the inline tilemap if ((TileComponent != nullptr) && (TileComponent->OwnsTileMap())) { TArray<UObject*> ListOfTileMaps; ListOfTileMaps.Add(TileMap); for (TFieldIterator<UProperty> PropIt(UPaperTileMap::StaticClass()); PropIt; ++PropIt) { UProperty* TestProperty = *PropIt; if (TestProperty->HasAnyPropertyFlags(CPF_Edit)) { FName CategoryName(*TestProperty->GetMetaData(TEXT("Category"))); IDetailCategoryBuilder& Category = DetailLayout.EditCategory(CategoryName); if (IDetailPropertyRow* ExternalRow = Category.AddExternalProperty(ListOfTileMaps, TestProperty->GetFName())) { ExternalRow->Visibility(InternalInstanceVis); } } } } // Make sure the setup category is near the top DetailLayout.EditCategory("Setup"); }
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 FPaperTileMapDetailsCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) { const TArray< TWeakObjectPtr<UObject> >& SelectedObjects = DetailLayout.GetDetailsView().GetSelectedObjects(); UPaperTileMap* TileMap = NULL; for (int32 ObjectIndex = 0; ObjectIndex < SelectedObjects.Num(); ++ObjectIndex) { if (AActor* CurrentActor = Cast<AActor>(SelectedObjects[ObjectIndex].Get())) { if (UPaperTileMapRenderComponent* CurrentTileMap = CurrentActor->FindComponentByClass<UPaperTileMapRenderComponent>()) { TileMap = CurrentTileMap->TileMap; break; } } } TileMapPtr = TileMap; IDetailCategoryBuilder& TileMapCategory = DetailLayout.EditCategory("TileMap"); TileMapCategory .AddCustomRow(TEXT("EnterEditMode")) [ SNew(SVerticalBox) +SVerticalBox::Slot() [ // SNew(SHorizontalBox) // +SHorizontalBox::Slot() // .AutoWidth() // .Padding(10,5) // [ SNew(SButton) .ContentPadding(3) .VAlign(VAlign_Center) .HAlign(HAlign_Center) .OnClicked(this, &FPaperTileMapDetailsCustomization::EnterTileMapEditingMode) .Visibility(this, &FPaperTileMapDetailsCustomization::GetNonEditModeVisibility) .Text( LOCTEXT( "EnterTileMapEditMode", "Enter Edit Mode" ) ) // ] ] ]; //@TODO: Handle showing layers when multiple tile maps are selected if (TileMap != NULL) { IDetailCategoryBuilder& LayersCategory = DetailLayout.EditCategory("Tile Layers"); LayersCategory.AddCustomRow(TEXT("Tile layer list")) [ SNew(STileLayerList, TileMap) ]; LayersCategory.AddCustomRow(TEXT("Add Layer, Add Collision Layer")) [ SNew(SVerticalBox) +SVerticalBox::Slot() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .AutoWidth() .Padding(10,5) [ SNew(SButton) .ContentPadding(3) .VAlign(VAlign_Center) .HAlign(HAlign_Center) .OnClicked(this, &FPaperTileMapDetailsCustomization::AddLayerClicked) .Text( LOCTEXT( "AddLayer", "Add Layer" ) ) ] +SHorizontalBox::Slot() .AutoWidth() .Padding(10,5) [ SNew(SButton) .ContentPadding(3) .VAlign(VAlign_Center) .HAlign(HAlign_Center) .OnClicked(this, &FPaperTileMapDetailsCustomization::AddCollisionLayerClicked) .Text( LOCTEXT( "AddCollisionLayer", "Add Collision Layer" ) ) ] ] ]; } // Make sure the setup category is near the top DetailLayout.EditCategory("Setup"); }
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 ); } }
void FPrimitiveComponentDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) { TSharedRef<IPropertyHandle> MobilityHandle = DetailBuilder.GetProperty("Mobility", USceneComponent::StaticClass()); MobilityHandle->SetToolTipText(LOCTEXT("PrimitiveMobilityTooltip", "Mobility for primitive components controls how they can be modified in game and therefore how they interact with lighting and physics.\n● A movable primitive component can be changed in game, but requires dynamic lighting and shadowing from lights which have a large performance cost.\n● A static primitive component can't be changed in game, but can have its lighting baked, which allows rendering to be very efficient.").ToString()); if ( DetailBuilder.GetProperty("BodyInstance")->IsValidHandle() ) { TSharedPtr<IPropertyHandle> BodyInstanceHandler = DetailBuilder.GetProperty("BodyInstance"); uint32 NumChildren = 0; BodyInstanceHandler->GetNumChildren(NumChildren); // See if we are hiding Physics category TArray<FString> HideCategories; FEditorCategoryUtils::GetClassHideCategories(DetailBuilder.GetDetailsView().GetBaseClass(), HideCategories); if (!HideCategories.Contains(TEXT("Physics"))) { IDetailCategoryBuilder& PhysicsCategory = DetailBuilder.EditCategory("Physics"); // Get the objects being customized so we can enable/disable editing of 'Simulate Physics' DetailBuilder.GetObjectsBeingCustomized(ObjectsCustomized); bool bDisplayMass = true; bool bDisplayMassOverride = true; for (int32 i = 0; i < ObjectsCustomized.Num(); ++i) { if (ObjectsCustomized[i].IsValid() && ObjectsCustomized[i]->IsA(UDestructibleComponent::StaticClass())) { bDisplayMass = false; bDisplayMassOverride = false; } if (ObjectsCustomized[i].IsValid() && ObjectsCustomized[i]->IsA(USkeletalMeshComponent::StaticClass())) { bDisplayMassOverride = false; } } // add all physics properties now - after adding mass for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex) { TSharedPtr<IPropertyHandle> ChildProperty = BodyInstanceHandler->GetChildHandle(ChildIndex); FString Category = FObjectEditorUtils::GetCategory(ChildProperty->GetProperty()); FString PropName = ChildProperty->GetProperty()->GetName(); if (Category == TEXT("Physics")) { // Only permit modifying bSimulatePhysics when the body has some geometry. if (PropName == TEXT("bSimulatePhysics")) { PhysicsCategory.AddProperty(ChildProperty).EditCondition(TAttribute<bool>(this, &FPrimitiveComponentDetails::IsSimulatePhysicsEditable), NULL); } else if (PropName == TEXT("bUseAsyncScene")) { //we only enable bUseAsyncScene if the project uses an AsyncScene PhysicsCategory.AddProperty(ChildProperty).EditCondition(TAttribute<bool>(this, &FPrimitiveComponentDetails::IsUseAsyncEditable), NULL); } else if (PropName == TEXT("LockedAxisMode")) { LockedAxisProperty = ChildProperty; PhysicsCategory.AddProperty(ChildProperty); } else if (PropName == TEXT("CustomLockedAxis")) { //we only enable bUseAsyncScene if the project uses an AsyncScene PhysicsCategory.AddProperty(ChildProperty).Visibility(TAttribute<EVisibility>(this, &FPrimitiveComponentDetails::IsCustomLockedAxisSelected)); } else if (PropName == TEXT("bAutoWeld")) { PhysicsCategory.AddProperty(ChildProperty).Visibility(TAttribute<EVisibility>(this, &FPrimitiveComponentDetails::IsAutoWeldVisible)) .EditCondition(TAttribute<bool>(this, &FPrimitiveComponentDetails::IsAutoWeldEditable), NULL); } else if (PropName == TEXT("bOverrideMass")) { if (bDisplayMassOverride) { PhysicsCategory.AddProperty(ChildProperty); } } else if (PropName == TEXT("MassInKg")) { if (bDisplayMass) { PhysicsCategory.AddCustomRow(TEXT("Mass"), false) .IsEnabled(TAttribute<bool>(this, &FPrimitiveComponentDetails::IsBodyMassEnabled)) .NameContent() [ ChildProperty->CreatePropertyNameWidget() ] .ValueContent() [ SNew(SVerticalBox) + SVerticalBox::Slot() .AutoHeight() [ SNew(SEditableTextBox) .Text(this, &FPrimitiveComponentDetails::OnGetBodyMass) .IsReadOnly(this, &FPrimitiveComponentDetails::IsBodyMassReadOnly) .Font(IDetailLayoutBuilder::GetDetailFont()) .Visibility(this, &FPrimitiveComponentDetails::IsMassVisible, false) ] + SVerticalBox::Slot() .AutoHeight() [ SNew(SVerticalBox) .Visibility(this, &FPrimitiveComponentDetails::IsMassVisible, true) + SVerticalBox::Slot() .AutoHeight() [ ChildProperty->CreatePropertyValueWidget() ] ] ]; } } else { PhysicsCategory.AddProperty(ChildProperty); } } } } // Collision { IDetailCategoryBuilder& CollisionCategory = DetailBuilder.EditCategory("Collision"); // add all collision properties for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex) { TSharedPtr<IPropertyHandle> ChildProperty = BodyInstanceHandler->GetChildHandle(ChildIndex); FString Category = FObjectEditorUtils::GetCategory(ChildProperty->GetProperty()); if (Category == TEXT("Collision")) { CollisionCategory.AddProperty(ChildProperty); } } } } AddAdvancedSubCategory( DetailBuilder, "Rendering", "TextureStreaming" ); AddAdvancedSubCategory( DetailBuilder, "Rendering", "LOD"); }
void FSceneCaptureDetails::CustomizeDetails( IDetailLayoutBuilder& DetailLayout ) { // Add all the properties that are there by default // (These would get added by default anyway, but we want to add them first so what we add next comes later in the list) TArray<TSharedRef<IPropertyHandle>> SceneCaptureCategoryDefaultProperties; IDetailCategoryBuilder& SceneCaptureCategoryBuilder = DetailLayout.EditCategory("SceneCapture"); SceneCaptureCategoryBuilder.GetDefaultProperties(SceneCaptureCategoryDefaultProperties); for (TSharedRef<IPropertyHandle> Handle : SceneCaptureCategoryDefaultProperties) { SceneCaptureCategoryBuilder.AddProperty(Handle); } 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() ) { ASceneCapture2D* CurrentCaptureActor2D = Cast<ASceneCapture2D>(CurrentObject.Get()); if (CurrentCaptureActor2D != nullptr) { SceneCaptureComponent = Cast<USceneCaptureComponent>(CurrentCaptureActor2D->GetCaptureComponent2D()); break; } ASceneCaptureCube* CurrentCaptureActorCube = Cast<ASceneCaptureCube>(CurrentObject.Get()); if (CurrentCaptureActorCube != nullptr) { SceneCaptureComponent = Cast<USceneCaptureComponent>(CurrentCaptureActorCube->GetCaptureComponentCube()); break; } } } // Show flags that should be exposed for Scene Captures TArray<FEngineShowFlags::EShowFlag> ShowFlagsToAllowForCaptures; ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_AtmosphericFog); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_BSP); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_Decals); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_Fog); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_Landscape); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_Particles); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_SkeletalMeshes); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_StaticMeshes); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_Translucency); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_DeferredLighting); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_InstancedStaticMeshes); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_InstancedFoliage); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_InstancedGrass); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_Paper2DSprites); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_TextRender); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_AmbientOcclusion); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_DynamicShadows); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_SkyLighting); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_AmbientCubemap); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_DistanceFieldAO); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_LightFunctions); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_LightShafts); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_ReflectionEnvironment); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_ScreenSpaceReflections); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_TexturedLightProfiles); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_AntiAliasing); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_TemporalAA); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_MotionBlur); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_Bloom); ShowFlagsToAllowForCaptures.Add(FEngineShowFlags::EShowFlag::SF_EyeAdaptation); // Create array of flag name strings for each group TArray< TArray<FString> > ShowFlagsByGroup; for (int32 GroupIndex = 0; GroupIndex < SFG_Max; ++GroupIndex) { ShowFlagsByGroup.Add(TArray<FString>()); } // Add the show flags we want to expose to their group's array for (FEngineShowFlags::EShowFlag AllowedFlag : ShowFlagsToAllowForCaptures) { FString FlagName; FlagName = FEngineShowFlags::FindNameByIndex(AllowedFlag); if (!FlagName.IsEmpty()) { EShowFlagGroup Group = FEngineShowFlags::FindShowFlagGroup(*FlagName); ShowFlagsByGroup[Group].Add(FlagName); } } // Sort the flags in their respective group alphabetically for (TArray<FString>& ShowFlagGroup : ShowFlagsByGroup) { ShowFlagGroup.Sort(SortAlphabeticallyByLocalizedText); } // Add each group for (int32 GroupIndex = 0; GroupIndex < SFG_Max; ++GroupIndex) { // Don't add a group if there are no flags allowed for it if (ShowFlagsByGroup[GroupIndex].Num() >= 1) { FText GroupName; FText GroupTooltip; switch (GroupIndex) { case SFG_Normal: GroupName = LOCTEXT("CommonShowFlagHeader", "General Show Flags"); break; case SFG_Advanced: GroupName = LOCTEXT("AdvancedShowFlagsMenu", "Advanced Show Flags"); break; case SFG_PostProcess: GroupName = LOCTEXT("PostProcessShowFlagsMenu", "Post Processing Show Flags"); break; case SFG_Developer: GroupName = LOCTEXT("DeveloperShowFlagsMenu", "Developer Show Flags"); break; case SFG_Visualize: GroupName = LOCTEXT("VisualizeShowFlagsMenu", "Visualize Show Flags"); break; case SFG_LightTypes: GroupName = LOCTEXT("LightTypesShowFlagsMenu", "Light Types Show Flags"); break; case SFG_LightingComponents: GroupName = LOCTEXT("LightingComponentsShowFlagsMenu", "Lighting Components Show Flags"); break; case SFG_LightingFeatures: GroupName = LOCTEXT("LightingFeaturesShowFlagsMenu", "Lighting Features Show Flags"); break; case SFG_CollisionModes: GroupName = LOCTEXT("CollisionModesShowFlagsMenu", "Collision Modes Show Flags"); break; case SFG_Hidden: GroupName = LOCTEXT("HiddenShowFlagsMenu", "Hidden Show Flags"); break; default: // Should not get here unless a new group is added without being updated here GroupName = LOCTEXT("MiscFlagsMenu", "Misc Show Flags"); break; } FName GroupFName = FName(*(GroupName.ToString())); IDetailGroup& Group = SceneCaptureCategoryBuilder.AddGroup(GroupFName, GroupName, true); // Add each show flag for this group for (FString& FlagName : ShowFlagsByGroup[GroupIndex]) { bool bFlagHidden = false; FText LocalizedText; FEngineShowFlags::FindShowFlagDisplayName(FlagName, LocalizedText); Group.AddWidgetRow() .IsEnabled(true) .NameContent() [ SNew(STextBlock) .Text(LocalizedText) ] .ValueContent() [ SNew(SCheckBox) .OnCheckStateChanged(this, &FSceneCaptureDetails::OnShowFlagCheckStateChanged, FlagName) .IsChecked(this, &FSceneCaptureDetails::OnGetDisplayCheckState, FlagName) ] .FilterString(LocalizedText); } } } }
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)); }