// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "LevelEditorContextMenu.h" #include "Misc/Attribute.h" #include "Styling/SlateColor.h" #include "Input/Reply.h" #include "Widgets/SWidget.h" #include "Misc/Paths.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/SBoxPanel.h" #include "Textures/SlateIcon.h" #include "Framework/Commands/UIAction.h" #include "Framework/Commands/UICommandList.h" #include "Framework/MultiBox/MultiBoxExtender.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "Components/ActorComponent.h" #include "GameFramework/Actor.h" #include "Kismet2/ComponentEditorUtils.h" #include "Engine/Selection.h" #include "HAL/FileManager.h" #include "Modules/ModuleManager.h" #include "SLevelEditor.h" #include "Layout/WidgetPath.h" #include "Framework/Application/MenuStack.h" #include "Framework/Application/SlateApplication.h" #include "Widgets/Images/SImage.h" #include "Widgets/Input/SButton.h" #include "EditorStyleSet.h" #include "Editor/GroupActor.h" #include "LevelEditorViewport.h" #include "EditorModes.h" #include "EditorModeInterpolation.h" #include "LevelEditor.h" #include "Matinee/MatineeActor.h" #include "Kismet2/KismetEditorUtilities.h" #include "AssetSelection.h" #include "LevelEditorActions.h" #include "SceneOutlinerPublicTypes.h" #include "SceneOutlinerModule.h" #include "Kismet2/DebuggerCommands.h" #include "Styling/SlateIconFinder.h" #include "EditorViewportCommands.h" #include "Toolkits/GlobalEditorCommonCommands.h" #include "LevelEditorCreateActorMenu.h" #include "SourceCodeNavigation.h" #include "EditorClassUtils.h" #include "Framework/Commands/GenericCommands.h" #include "LevelViewportActions.h" #include "ActorGroupingUtils.h" #define LOCTEXT_NAMESPACE "LevelViewportContextMenu" DEFINE_LOG_CATEGORY_STATIC(LogViewportMenu, Log, All); class FLevelEditorContextMenuImpl { public: static FSelectedActorInfo SelectionInfo; public: /** * Fills in menu options for the select actor menu * * @param MenuBuilder The menu to add items to */ static void FillSelectActorMenu( class FMenuBuilder& MenuBuilder ); /** * Fills in menu options for the actor visibility menu * * @param MenuBuilder The menu to add items to */ static void FillActorVisibilityMenu( class FMenuBuilder& MenuBuilder ); /** * Fills in menu options for the actor level menu * * @param SharedLevel The level shared between all selected actors. If any actors are in a different level, this is NULL * @param bAllInCurrentLevel true if all selected actors are in the current level * @param MenuBuilder The menu to add items to */ static void FillActorLevelMenu( class FMenuBuilder& MenuBuilder ); /** * Fills in menu options for the transform menu * * @param MenuBuilder The menu to add items to */ static void FillTransformMenu( class FMenuBuilder& MenuBuilder ); /** * Fills in menu options for the Fill Actor menu * * @param MenuBuilder The menu to add items to */ static void FillActorMenu( class FMenuBuilder& MenuBuilder ); /** * Fills in menu options for the snap menu * * @param MenuBuilder The menu to add items to */ static void FillSnapAlignMenu( class FMenuBuilder& MenuBuilder ); /** * Fills in menu options for the pivot menu * * @param MenuBuilder The menu to add items to */ static void FillPivotMenu( class FMenuBuilder& MenuBuilder ); /** * Fills in menu options for the group menu * * @param MenuBuilder The menu to add items to */ static void FillGroupMenu( class FMenuBuilder& MenuBuilder ); /** * Fills in menu options for the edit menu * * @param MenuBuilder The menu to add items to * @param ContextType The context for this editor menu */ static void FillEditMenu( class FMenuBuilder& MenuBuilder, LevelEditorMenuContext ContextType ); private: /** * Fills in menu options for the matinee selection menu * * @param MenuBuilder The menu to add items to */ static void FillMatineeSelectActorMenu( class FMenuBuilder& MenuBuilder ); }; FSelectedActorInfo FLevelEditorContextMenuImpl::SelectionInfo; struct FLevelScriptEventMenuHelper { /** * Fills in menu options for events that can be associated with that actors's blueprint in the level script blueprint * * @param MenuBuilder The menu to add items to */ static void FillLevelBlueprintEventsMenu(class FMenuBuilder& MenuBuilder, const TArray& SelectedActors); }; // NOTE: We intentionally receive a WEAK pointer here because we want to be callable by a delegate whose // payload contains a weak reference to a level editor instance TSharedPtr< SWidget > FLevelEditorContextMenu::BuildMenuWidget( TWeakPtr< SLevelEditor > LevelEditor, LevelEditorMenuContext ContextType, TSharedPtr Extender ) { // Build up the menu const bool bShouldCloseWindowAfterMenuSelection = true; FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, TSharedPtr()); FillMenu(MenuBuilder, LevelEditor, ContextType, Extender); return MenuBuilder.MakeWidget(); } void FLevelEditorContextMenu::FillMenu( FMenuBuilder& MenuBuilder, TWeakPtr LevelEditor, LevelEditorMenuContext ContextType, TSharedPtr Extender ) { auto LevelEditorActionsList = LevelEditor.Pin()->GetLevelEditorActions().ToSharedRef(); MenuBuilder.PushCommandList(LevelEditorActionsList); if (GEditor->GetSelectedComponentCount() > 0) { TArray SelectedComponents; for (FSelectedEditableComponentIterator It(GEditor->GetSelectedEditableComponentIterator()); It; ++It) { SelectedComponents.Add(CastChecked(*It)); } MenuBuilder.BeginSection("ComponentControl", LOCTEXT("ComponentControlHeading", "Component")); { AActor* OwnerActor = GEditor->GetSelectedActors()->GetTop(); if(OwnerActor) { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectComponentOwnerActor, NAME_None, FText::Format(LOCTEXT("SelectComponentOwner", "Select Owner [{0}]"), FText::FromString(OwnerActor->GetHumanReadableName())), TAttribute(), FSlateIconFinder::FindIconForClass(OwnerActor->GetClass()) ); } MenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().FocusViewportToSelection); const FVector* ClickLocation = &GEditor->ClickLocation; FUIAction GoHereAction; GoHereAction.ExecuteAction = FExecuteAction::CreateStatic(&FLevelEditorActionCallbacks::GoHere_Clicked, ClickLocation); MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().GoHere); MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().SnapCameraToObject); MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().SnapObjectToCamera); } MenuBuilder.EndSection(); FComponentEditorUtils::FillComponentContextMenuOptions(MenuBuilder, SelectedComponents); } else if (GEditor->GetSelectedActorCount() > 0) { // Generate information about our selection TArray SelectedActors; GEditor->GetSelectedActors()->GetSelectedObjects(SelectedActors); FSelectedActorInfo& SelectionInfo = FLevelEditorContextMenuImpl::SelectionInfo; SelectionInfo = AssetSelectionUtils::BuildSelectedActorInfo(SelectedActors); // Get all menu extenders for this context menu from the level editor module FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked(TEXT("LevelEditor")); TArray MenuExtenderDelegates = LevelEditorModule.GetAllLevelViewportContextMenuExtenders(); TArray> Extenders; if (Extender.IsValid()) { Extenders.Add(Extender); } for (int32 i = 0; i < MenuExtenderDelegates.Num(); ++i) { if (MenuExtenderDelegates[i].IsBound()) { Extenders.Add(MenuExtenderDelegates[i].Execute(LevelEditorActionsList, SelectedActors)); } } MenuBuilder.PushExtender(FExtender::Combine(Extenders).ToSharedRef()); // Check if current selection has any assets that can be browsed to TArray< UObject* > ReferencedAssets; GEditor->GetReferencedAssetsForEditorSelection(ReferencedAssets); const bool bCanSyncToContentBrowser = GEditor->CanSyncToContentBrowser(); if (bCanSyncToContentBrowser || ReferencedAssets.Num() > 0) { MenuBuilder.BeginSection("ActorAsset", LOCTEXT("AssetHeading", "Asset")); { if (bCanSyncToContentBrowser) { MenuBuilder.AddMenuEntry(FGlobalEditorCommonCommands::Get().FindInContentBrowser); } if (ReferencedAssets.Num() == 1) { auto Asset = ReferencedAssets[0]; MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().EditAsset, NAME_None, FText::Format(LOCTEXT("EditAssociatedAsset", "Edit {0}"), FText::FromString(Asset->GetName())), TAttribute(), FSlateIconFinder::FindIconForClass(Asset->GetClass()) ); } else if (ReferencedAssets.Num() > 1) { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().EditAssetNoConfirmMultiple, NAME_None, LOCTEXT("EditAssociatedAssetsMultiple", "Edit Multiple Assets"), TAttribute(), FSlateIcon(FEditorStyle::GetStyleSetName(), "ClassIcon.Default") ); } } MenuBuilder.EndSection(); } MenuBuilder.BeginSection("ActorControl", LOCTEXT("ActorHeading", "Actor")); { MenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().FocusViewportToSelection); const FVector* ClickLocation = &GEditor->ClickLocation; FUIAction GoHereAction; GoHereAction.ExecuteAction = FExecuteAction::CreateStatic(&FLevelEditorActionCallbacks::GoHere_Clicked, ClickLocation); MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().GoHere); MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().SnapCameraToObject); MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().SnapObjectToCamera); if (SelectedActors.Num() == 1) { const FLevelViewportCommands& Actions = FLevelViewportCommands::Get(); auto Viewport = LevelEditor.Pin()->GetActiveViewport(); if (Viewport.IsValid()) { auto& ViewportClient = Viewport->GetLevelViewportClient(); if (ViewportClient.IsPerspective() && !ViewportClient.IsLockedToMatinee()) { if (Viewport->IsSelectedActorLocked()) { MenuBuilder.AddMenuEntry( Actions.EjectActorPilot, NAME_None, FText::Format(LOCTEXT("PilotActor_Stop", "Stop piloting '{0}'"), FText::FromString(SelectedActors[0]->GetActorLabel())) ); } else { MenuBuilder.AddMenuEntry( Actions.PilotSelectedActor, NAME_None, FText::Format(LOCTEXT("PilotActor", "Pilot '{0}'"), FText::FromString(SelectedActors[0]->GetActorLabel())) ); } } } } } MenuBuilder.EndSection(); // Go to C++ Code if (SelectionInfo.SelectionClass != NULL) { if (FSourceCodeNavigation::IsCompilerAvailable()) { FString ClassHeaderPath; if (FSourceCodeNavigation::FindClassHeaderPath(SelectionInfo.SelectionClass, ClassHeaderPath) && IFileManager::Get().FileSize(*ClassHeaderPath) != INDEX_NONE) { const FString CodeFileName = FPaths::GetCleanFilename(*ClassHeaderPath); MenuBuilder.BeginSection("ActorCode", LOCTEXT("ActorCodeHeading", "C++")); { MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().GoToCodeForActor, NAME_None, FText::Format(LOCTEXT("GoToCodeForActor", "Open {0}"), FText::FromString(CodeFileName)), FText::Format(LOCTEXT("GoToCodeForActor_ToolTip", "Opens the header file for this actor ({0}) in a code editing program"), FText::FromString(CodeFileName))); } MenuBuilder.EndSection(); } } const FString DocumentationLink = FEditorClassUtils::GetDocumentationLink(SelectionInfo.SelectionClass); if (!DocumentationLink.IsEmpty()) { MenuBuilder.BeginSection("ActorDocumentation", LOCTEXT("ActorDocsHeading", "Documentation")); { MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().GoToDocsForActor, NAME_None, LOCTEXT("GoToDocsForActor", "View Documentation"), LOCTEXT("GoToDocsForActor_ToolTip", "Click to open documentation for this actor"), FSlateIcon(FEditorStyle::GetStyleSetName(), "HelpIcon.Hovered")); } MenuBuilder.EndSection(); } } MenuBuilder.BeginSection("ActorSelectVisibilityLevels"); { // Add a sub-menu for "Select" MenuBuilder.AddSubMenu( LOCTEXT("SelectSubMenu", "Select"), LOCTEXT("SelectSubMenu_ToolTip", "Opens the actor selection menu"), FNewMenuDelegate::CreateStatic(&FLevelEditorContextMenuImpl::FillSelectActorMenu)); MenuBuilder.AddSubMenu( LOCTEXT("EditSubMenu", "Edit"), FText::GetEmpty(), FNewMenuDelegate::CreateStatic(&FLevelEditorContextMenuImpl::FillEditMenu, ContextType)); MenuBuilder.AddSubMenu( LOCTEXT("VisibilitySubMenu", "Visibility"), LOCTEXT("VisibilitySubMenu_ToolTip", "Selected actor visibility options"), FNewMenuDelegate::CreateStatic(&FLevelEditorContextMenuImpl::FillActorVisibilityMenu)); // Build the menu for grouping actors BuildGroupMenu(MenuBuilder, SelectionInfo); MenuBuilder.AddSubMenu( LOCTEXT("LevelSubMenu", "Level"), LOCTEXT("LevelSubMenu_ToolTip", "Options for interacting with this actor's level"), FNewMenuDelegate::CreateStatic(&FLevelEditorContextMenuImpl::FillActorLevelMenu)); } MenuBuilder.EndSection(); if (ContextType == LevelEditorMenuContext::Viewport) { LevelEditorCreateActorMenu::FillAddReplaceViewportContextMenuSections(MenuBuilder); MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().OpenMergeActor, NAME_None, LOCTEXT("OpenMergeActor", "Merge Actors"), LOCTEXT("OpenMergeActor_ToolTip", "Click to open the Merge Actor panel")); } if (GEditor->PlayWorld != NULL) { if (SelectionInfo.NumSelected > 0) { MenuBuilder.BeginSection("Simulation", NSLOCTEXT("LevelViewportContextMenu", "SimulationHeading", "Simulation")); { MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().KeepSimulationChanges); } MenuBuilder.EndSection(); } } MenuBuilder.BeginSection("LevelViewportAttach"); { // Only display the attach menu if we have actors selected if (GEditor->GetSelectedActorCount()) { if (SelectionInfo.bHaveAttachedActor) { MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().DetachFromParent); } MenuBuilder.AddSubMenu( LOCTEXT("ActorAttachToSubMenu", "Attach To"), LOCTEXT("ActorAttachToSubMenu_ToolTip", "Attach Actor as child"), FNewMenuDelegate::CreateStatic(&FLevelEditorContextMenuImpl::FillActorMenu)); } // Add a heading for "Movement" if an actor is selected if (GEditor->GetSelectedActorIterator()) { // Add a sub-menu for "Transform" MenuBuilder.AddSubMenu( LOCTEXT("TransformSubMenu", "Transform"), LOCTEXT("TransformSubMenu_ToolTip", "Actor transform utils"), FNewMenuDelegate::CreateStatic(&FLevelEditorContextMenuImpl::FillTransformMenu)); } // Add a sub-menu for "Pivot" MenuBuilder.AddSubMenu( LOCTEXT("PivotSubMenu", "Pivot"), LOCTEXT("PivotSubMenu_ToolTip", "Actor pivoting utils"), FNewMenuDelegate::CreateStatic(&FLevelEditorContextMenuImpl::FillPivotMenu)); } MenuBuilder.EndSection(); FLevelScriptEventMenuHelper::FillLevelBlueprintEventsMenu(MenuBuilder, SelectedActors); MenuBuilder.PopExtender(); } else if (ContextType == LevelEditorMenuContext::SceneOutliner) { TWeakPtr SceneOutlinerPtr = LevelEditor.Pin()->GetSceneOutliner(); if (SceneOutlinerPtr.IsValid()) { MenuBuilder.BeginSection("SelectVisibilityLevels"); { MenuBuilder.AddSubMenu( LOCTEXT("EditSubMenu", "Edit"), FText::GetEmpty(), FNewMenuDelegate::CreateStatic(&FLevelEditorContextMenuImpl::FillEditMenu, ContextType)); } } } MenuBuilder.PopCommandList(); } namespace EViewOptionType { enum Type { Top, Bottom, Left, Right, Front, Back, Perspective }; } TSharedPtr MakeViewOptionWidget(const TSharedRef< SLevelEditor >& LevelEditor, bool bShouldCloseWindowAfterMenuSelection, EViewOptionType::Type ViewOptionType) { FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, LevelEditor->GetActiveViewport()->GetCommandList()); if (ViewOptionType == EViewOptionType::Top) { MenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().Top); } else if (ViewOptionType == EViewOptionType::Bottom) { MenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().Bottom); } else if (ViewOptionType == EViewOptionType::Left) { MenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().Left); } else if (ViewOptionType == EViewOptionType::Right) { MenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().Right); } else if (ViewOptionType == EViewOptionType::Front) { MenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().Front); } else if (ViewOptionType == EViewOptionType::Back) { MenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().Back); } else if (ViewOptionType == EViewOptionType::Perspective) { MenuBuilder.AddMenuEntry(FEditorViewportCommands::Get().Perspective); } else { return nullptr; } return MenuBuilder.MakeWidget(); } void BuildViewOptionMenu(const TSharedRef< SLevelEditor >& LevelEditor, TSharedPtr InWidget, const FVector2D WidgetPosition) { if (InWidget.IsValid()) { FSlateApplication::Get().PushMenu( LevelEditor->GetActiveViewport().ToSharedRef(), FWidgetPath(), InWidget.ToSharedRef(), WidgetPosition, FPopupTransitionEffect(FPopupTransitionEffect::ContextMenu)); } } void FLevelEditorContextMenu::SummonViewOptionMenu( const TSharedRef< SLevelEditor >& LevelEditor, const ELevelViewportType ViewOption ) { const FVector2D MouseCursorLocation = FSlateApplication::Get().GetCursorPos(); bool bShouldCloseWindowAfterMenuSelection = true; EViewOptionType::Type ViewOptionType = EViewOptionType::Perspective; switch (ViewOption) { case LVT_OrthoNegativeXY: ViewOptionType = EViewOptionType::Bottom; break; case LVT_OrthoNegativeXZ: ViewOptionType = EViewOptionType::Back; break; case LVT_OrthoNegativeYZ: ViewOptionType = EViewOptionType::Right; break; case LVT_OrthoXY: ViewOptionType = EViewOptionType::Top; break; case LVT_OrthoXZ: ViewOptionType = EViewOptionType::Front; break; case LVT_OrthoYZ: ViewOptionType = EViewOptionType::Left; break; case LVT_Perspective: ViewOptionType = EViewOptionType::Perspective; break; }; // Build up menu BuildViewOptionMenu(LevelEditor, MakeViewOptionWidget(LevelEditor, bShouldCloseWindowAfterMenuSelection, ViewOptionType), MouseCursorLocation); } void FLevelEditorContextMenu::SummonMenu( const TSharedRef< SLevelEditor >& LevelEditor, LevelEditorMenuContext ContextType ) { struct Local { static void ExtendMenu( FMenuBuilder& MenuBuilder ) { // one extra entry when summoning the menu this way MenuBuilder.BeginSection("ActorPreview", LOCTEXT("PreviewHeading", "Preview") ); { // Note: not using a command for play from here since it requires a mouse click FUIAction PlayFromHereAction( FExecuteAction::CreateStatic( &FPlayWorldCommandCallbacks::StartPlayFromHere ) ); const FText PlayFromHereLabel = GEditor->OnlyLoadEditorVisibleLevelsInPIE() ? LOCTEXT("PlayFromHereVisible", "Play From Here (visible levels)") : LOCTEXT("PlayFromHere", "Play From Here"); MenuBuilder.AddMenuEntry( PlayFromHereLabel, LOCTEXT("PlayFromHere_ToolTip", "Starts a game preview from the clicked location"),FSlateIcon(), PlayFromHereAction ); } MenuBuilder.EndSection(); } }; TSharedRef Extender = MakeShareable(new FExtender); Extender->AddMenuExtension("LevelViewportAttach", EExtensionHook::After, TSharedPtr< FUICommandList >(), FMenuExtensionDelegate::CreateStatic(&Local::ExtendMenu)); // Create the context menu! TSharedPtr MenuWidget = BuildMenuWidget( LevelEditor, ContextType, Extender ); if ( MenuWidget.IsValid() ) { // @todo: Should actually use the location from a click event instead! const FVector2D MouseCursorLocation = FSlateApplication::Get().GetCursorPos(); FSlateApplication::Get().PushMenu( LevelEditor->GetActiveViewport().ToSharedRef(), FWidgetPath(), MenuWidget.ToSharedRef(), MouseCursorLocation, FPopupTransitionEffect( FPopupTransitionEffect::ContextMenu ) ); } } FSlateColor InvertOnHover( const TWeakPtr< SWidget > WidgetPtr ) { TSharedPtr< SWidget > Widget = WidgetPtr.Pin(); if ( Widget.IsValid() && Widget->IsHovered() ) { static const FName InvertedForegroundName("InvertedForeground"); return FEditorStyle::GetSlateColor(InvertedForegroundName); } return FSlateColor::UseForeground(); } void FLevelEditorContextMenu::BuildGroupMenu( FMenuBuilder& MenuBuilder, const FSelectedActorInfo& SelectedActorInfo ) { if( UActorGroupingUtils::IsGroupingActive() ) { // Whether or not we added a grouping sub-menu bool bNeedGroupSubMenu = SelectedActorInfo.bHaveSelectedLockedGroup || SelectedActorInfo.bHaveSelectedUnlockedGroup; // Grouping based on selection (must have selected at least two actors) if( SelectedActorInfo.NumSelected > 1 ) { if( !SelectedActorInfo.bHaveSelectedLockedGroup && !SelectedActorInfo.bHaveSelectedUnlockedGroup ) { // Only one menu entry needed so dont use a sub-menu MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().RegroupActors, NAME_None, FLevelEditorCommands::Get().GroupActors->GetLabel(), FLevelEditorCommands::Get().GroupActors->GetDescription() ); } else { // Put everything into a sub-menu bNeedGroupSubMenu = true; } } if( bNeedGroupSubMenu ) { MenuBuilder.AddSubMenu( LOCTEXT("GroupMenu", "Groups"), LOCTEXT("GroupMenu_ToolTip", "Opens the actor grouping menu"), FNewMenuDelegate::CreateStatic( &FLevelEditorContextMenuImpl::FillGroupMenu ) ); } } } void FLevelEditorContextMenuImpl::FillSelectActorMenu( FMenuBuilder& MenuBuilder ) { FText SelectAllActorStr = FText::Format( LOCTEXT("SelectActorsOfSameClass", "Select All {0}(s)"), FText::FromString( SelectionInfo.SelectionStr ) ); int32 NumSelectedSurfaces = AssetSelectionUtils::GetNumSelectedSurfaces( SelectionInfo.SharedWorld ); MenuBuilder.BeginSection("SelectActorGeneral", LOCTEXT("SelectAnyHeading", "General") ); { MenuBuilder.AddMenuEntry( FGenericCommands::Get().SelectAll, NAME_None, TAttribute(), LOCTEXT("SelectAll_ToolTip", "Selects all actors") ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectNone ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().InvertSelection ); } MenuBuilder.EndSection(); if( !SelectionInfo.bHaveBrush && SelectionInfo.bAllSelectedActorsOfSameType && SelectionInfo.SelectionStr.Len() != 0 ) { // These menu options appear if only if all the actors are the same type and we aren't selecting brush MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectAllActorsOfSameClass, NAME_None, SelectAllActorStr ); } MenuBuilder.BeginSection("SelectActorHierarchy", LOCTEXT("SelectHierarchyHeading", "Hierarchy") ); { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectImmediateChildren ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectAllDescendants ); } MenuBuilder.EndSection(); // Add brush commands when we have a brush or any surfaces selected MenuBuilder.BeginSection("SelectBSP", LOCTEXT("SelectBSPHeading", "BSP") ); { if( SelectionInfo.bHaveBrush || NumSelectedSurfaces > 0 ) { if( SelectionInfo.bAllSelectedAreBrushes ) { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectAllActorsOfSameClass, NAME_None, SelectAllActorStr ); } } MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectAllAddditiveBrushes ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectAllSubtractiveBrushes ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectAllSurfaces ); } MenuBuilder.EndSection(); if( SelectionInfo.NumSelected > 0 || NumSelectedSurfaces > 0 ) { // If any actors are selected add lights selection options MenuBuilder.BeginSection("SelectLights", LOCTEXT("SelectLightHeading", "Lights") ); { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectRelevantLights ); if ( SelectionInfo.bHaveLight ) { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectAllLights ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectStationaryLightsExceedingOverlap ); } } MenuBuilder.EndSection(); if( SelectionInfo.bHaveStaticMesh ) { // if any static meshes are selected allow selecting actors using the same mesh MenuBuilder.BeginSection("SelectMeshes", LOCTEXT("SelectStaticMeshHeading", "Static Meshes") ); { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectStaticMeshesOfSameClass, NAME_None, LOCTEXT("SelectStaticMeshesOfSameClass_Menu", "Select Matching (Selected Classes)") ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectStaticMeshesAllClasses, NAME_None, LOCTEXT("SelectStaticMeshesAllClasses_Menu", "Select Matching (All Classes)") ); } MenuBuilder.EndSection(); if (SelectionInfo.NumSelected == 1) { MenuBuilder.BeginSection("SelectHLODCluster", LOCTEXT("SelectHLODClusterHeading", "Hierachical LODs")); { MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().SelectOwningHierarchicalLODCluster, NAME_None, LOCTEXT("SelectOwningHierarchicalLODCluster_Menu", "Select Owning HierarchicalLODCluster")); } MenuBuilder.EndSection(); } } if( SelectionInfo.bHavePawn || SelectionInfo.bHaveSkeletalMesh ) { // if any skeletal meshes are selected allow selecting actors using the same mesh MenuBuilder.BeginSection("SelectSkeletalMeshes", LOCTEXT("SelectSkeletalMeshHeading", "Skeletal Meshes") ); { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectSkeletalMeshesOfSameClass ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectSkeletalMeshesAllClasses ); } MenuBuilder.EndSection(); } if( SelectionInfo.bHaveEmitter ) { MenuBuilder.BeginSection("SelectEmitters", LOCTEXT("SelectEmitterHeading", "Emitters") ); { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectMatchingEmitter ); } MenuBuilder.EndSection(); } } if( SelectionInfo.bHaveBrush || SelectionInfo.NumSelected > 0 ) { MenuBuilder.BeginSection("SelectMaterial", LOCTEXT("SelectMaterialHeading", "Materials") ); { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectAllWithSameMaterial ); } MenuBuilder.EndSection(); } // Add geometry collection commands if (FModuleManager::Get().IsModuleLoaded("GeometryCollectionEditor")) { MenuBuilder.BeginSection("SelectBones", LOCTEXT("GeometryCollectionHeading", "Geometry Collection")); { MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().GeometryCollectionSelectAllGeometry); MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().GeometryCollectionSelectNone); MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().GeometryCollectionSelectInverseGeometry); } MenuBuilder.EndSection(); } // build matinee related selection menu FillMatineeSelectActorMenu( MenuBuilder ); } void FLevelEditorContextMenuImpl::FillMatineeSelectActorMenu( FMenuBuilder& MenuBuilder ) { MenuBuilder.BeginSection("SelectMatinee", LOCTEXT("SelectMatineeHeading", "Matinee") ); { // show list of Matinee Actors that controls this actor // this is ugly but we don't have good way of knowing which Matinee actor controls me // in the future this can be cached to TMap somewhere and use that list // for now we show only when 1 actor is selected if ( SelectionInfo.SharedLevel && SelectionInfo.NumSelected == 1 ) { TArray MatineeActors; // first collect all matinee actors for ( AActor* Actor : SelectionInfo.SharedLevel->Actors ) { AMatineeActor * CurActor = Cast(Actor); if ( CurActor ) { MatineeActors.Add(CurActor); } } if ( MatineeActors.Num() > 0 ) { FSelectionIterator ActorIter( GEditor->GetSelectedActorIterator() ); AActor* SelectedActor = Cast(*ActorIter); // now delete the matinee actors that don't control currently selected actor for (int32 MatineeActorIter=0; MatineeActorIter CutMatineeControlledActors; CurMatineeActor->GetControlledActors(CutMatineeControlledActors); bool bIsMatineeControlled=false; for ( AActor* ControlledActor : CutMatineeControlledActors ) { if (ControlledActor == SelectedActor) { bIsMatineeControlled = true; } } // if not, remove it if (!bIsMatineeControlled) { MatineeActors.RemoveAt(MatineeActorIter); --MatineeActorIter; } } // if some matinee controls this, add to menu for direct selection if ( MatineeActors.Num() > 0 ) { for (int32 MatineeActorIter=0; MatineeActorIterGetName() ) ); FUIAction CurMatineeActorAction( FExecuteAction::CreateStatic( &FLevelEditorActionCallbacks::OnSelectMatineeActor, CurMatineeActor ) ); MenuBuilder.AddMenuEntry( Text, Text, FSlateIcon(), CurMatineeActorAction ); // if matinee is opened, and if that is CurMatineeActor, show option to go to group if( GLevelEditorModeTools().IsModeActive( FBuiltinEditorModes::EM_InterpEdit ) ) { const FEdModeInterpEdit* InterpEditMode = (const FEdModeInterpEdit*)GLevelEditorModeTools().GetActiveMode( FBuiltinEditorModes::EM_InterpEdit ); if ( InterpEditMode && InterpEditMode->MatineeActor == CurMatineeActor ) { FUIAction SelectedActorAction( FExecuteAction::CreateStatic( &FLevelEditorActionCallbacks::OnSelectMatineeGroup, SelectedActor ) ); MenuBuilder.AddMenuEntry( LOCTEXT("SelectMatineeGroupForActorMenuTitle", "Select Matinee Group For This Actor"), LOCTEXT("SelectMatineeGroupForActorMenuTooltip", "Selects matinee group controlling this actor"), FSlateIcon(), SelectedActorAction ); } } } } } } // if this class is Matinee Actor, add option to allow select all controlled actors if ( SelectionInfo.bHaveMatinee ) { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SelectAllActorsControlledByMatinee ); } } MenuBuilder.EndSection(); } void FLevelEditorContextMenuImpl::FillActorVisibilityMenu( FMenuBuilder& MenuBuilder ) { MenuBuilder.BeginSection("VisibilitySelected"); { // Show 'Show Selected' only if the selection has any hidden actors if ( SelectionInfo.bHaveHidden ) { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().ShowSelected ); } MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().HideSelected ); } MenuBuilder.EndSection(); MenuBuilder.BeginSection("VisibilityAll"); { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().ShowSelectedOnly ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().ShowAll ); } MenuBuilder.EndSection(); MenuBuilder.BeginSection("VisibilityStartup"); { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().ShowAllStartup ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().ShowSelectedStartup ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().HideSelectedStartup ); } } void FLevelEditorContextMenuImpl::FillActorLevelMenu( FMenuBuilder& MenuBuilder ) { MenuBuilder.BeginSection("ActorLevel", LOCTEXT("ActorLevel", "Actor Level")); { if( SelectionInfo.SharedLevel && SelectionInfo.SharedWorld && SelectionInfo.SharedWorld->GetCurrentLevel() != SelectionInfo.SharedLevel ) { // All actors are in the same level and that level is not the current level // so add a menu entry to make the shared level current FText MakeCurrentLevelText = FText::Format( LOCTEXT("MakeCurrentLevelMenu", "Make Current Level: {0}"), FText::FromString( SelectionInfo.SharedLevel->GetOutermost()->GetName() ) ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().MakeActorLevelCurrent, NAME_None, MakeCurrentLevelText ); } if( !SelectionInfo.bAllSelectedActorsBelongToCurrentLevel ) { // Only show this menu entry if any actors are not in the current level MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().MoveSelectedToCurrentLevel ); } MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().FindActorLevelInContentBrowser); } MenuBuilder.EndSection(); MenuBuilder.BeginSection("LevelBlueprint", LOCTEXT("LevelBlueprint", "Level Blueprint")); { MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().FindActorInLevelScript); } MenuBuilder.EndSection(); MenuBuilder.BeginSection("LevelBrowser", LOCTEXT("LevelBrowser", "Level Browser")); { MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().FindLevelsInLevelBrowser); MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().AddLevelsToSelection); MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().RemoveLevelsFromSelection); } MenuBuilder.EndSection(); } void FLevelEditorContextMenuImpl::FillTransformMenu( FMenuBuilder& MenuBuilder ) { if ( FLevelEditorActionCallbacks::ActorSelected_CanExecute() ) { MenuBuilder.BeginSection("TransformSnapAlign"); { MenuBuilder.AddSubMenu( LOCTEXT("SnapAlignSubMenu", "Snap/Align"), LOCTEXT("SnapAlignSubMenu_ToolTip", "Actor snap/align utils"), FNewMenuDelegate::CreateStatic( &FLevelEditorContextMenuImpl::FillSnapAlignMenu ) ); } MenuBuilder.EndSection(); MenuBuilder.BeginSection("DeltaTransformToActors"); { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().DeltaTransformToActors ); } MenuBuilder.EndSection(); } MenuBuilder.BeginSection("MirrorLock"); { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().MirrorActorX ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().MirrorActorY ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().MirrorActorZ ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LockActorMovement ); } } void FLevelEditorContextMenuImpl::FillActorMenu( FMenuBuilder& MenuBuilder ) { struct Local { static FReply OnInteractiveActorPickerClicked() { FSlateApplication::Get().DismissAllMenus(); FLevelEditorActionCallbacks::AttachActorIteractive(); return FReply::Handled(); } }; SceneOutliner::FInitializationOptions InitOptions; { InitOptions.Mode = ESceneOutlinerMode::ActorPicker; InitOptions.bShowHeaderRow = false; InitOptions.bFocusSearchBoxWhenOpened = true; // Only display Actors that we can attach too InitOptions.Filters->AddFilterPredicate( SceneOutliner::FActorFilterPredicate::CreateStatic( &FLevelEditorActionCallbacks::IsAttachableActor) ); } if(SelectionInfo.bHaveAttachedActor) { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().DetachFromParent, NAME_None, LOCTEXT( "None", "None" ) ); } // Actor selector to allow the user to choose a parent actor FSceneOutlinerModule& SceneOutlinerModule = FModuleManager::LoadModuleChecked( "SceneOutliner" ); TSharedRef< SWidget > MenuWidget = SNew(SHorizontalBox) +SHorizontalBox::Slot() .AutoWidth() [ SNew(SVerticalBox) +SVerticalBox::Slot() .MaxHeight(400.0f) [ SceneOutlinerModule.CreateSceneOutliner( InitOptions, FOnActorPicked::CreateStatic( &FLevelEditorActionCallbacks::AttachToActor ) ) ] ] +SHorizontalBox::Slot() .VAlign(VAlign_Top) .AutoWidth() [ SNew(SVerticalBox) +SVerticalBox::Slot() .AutoHeight() .Padding(4.0f, 0.0f, 0.0f, 0.0f) [ SNew(SButton) .ToolTipText( LOCTEXT( "PickButtonLabel", "Pick a parent actor to attach to") ) .ButtonStyle(FEditorStyle::Get(), "HoverHintOnly") .OnClicked(FOnClicked::CreateStatic(&Local::OnInteractiveActorPickerClicked)) .ContentPadding(4.0f) .ForegroundColor(FSlateColor::UseForeground()) .IsFocusable(false) [ SNew(SImage) .Image(FEditorStyle::GetBrush("PropertyWindow.Button_PickActorInteractive")) .ColorAndOpacity(FSlateColor::UseForeground()) ] ] ]; MenuBuilder.AddWidget(MenuWidget, FText::GetEmpty(), false); } void FLevelEditorContextMenuImpl::FillSnapAlignMenu( FMenuBuilder& MenuBuilder ) { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SnapOriginToGrid ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SnapOriginToGridPerActor ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().AlignOriginToGrid ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SnapTo2DLayer ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SnapToFloor ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().AlignToFloor ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SnapPivotToFloor ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().AlignPivotToFloor ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SnapBottomCenterBoundsToFloor ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().AlignBottomCenterBoundsToFloor ); /* MenuBuilder.AddMenuSeparator(); AActor* Actor = GEditor->GetSelectedActors()->GetBottom(); if( Actor && FLevelEditorActionCallbacks::ActorsSelected_CanExecute()) { const FString Label = Actor->GetActorLabel(); // Update the options to show the actors label TSharedPtr< FUICommandInfo > SnapOriginToActor = FLevelEditorCommands::Get().SnapOriginToActor; TSharedPtr< FUICommandInfo > AlignOriginToActor = FLevelEditorCommands::Get().AlignOriginToActor; TSharedPtr< FUICommandInfo > SnapToActor = FLevelEditorCommands::Get().SnapToActor; TSharedPtr< FUICommandInfo > AlignToActor = FLevelEditorCommands::Get().AlignToActor; TSharedPtr< FUICommandInfo > SnapPivotToActor = FLevelEditorCommands::Get().SnapPivotToActor; TSharedPtr< FUICommandInfo > AlignPivotToActor = FLevelEditorCommands::Get().AlignPivotToActor; TSharedPtr< FUICommandInfo > SnapBottomCenterBoundsToActor = FLevelEditorCommands::Get().SnapBottomCenterBoundsToActor; TSharedPtr< FUICommandInfo > AlignBottomCenterBoundsToActor = FLevelEditorCommands::Get().AlignBottomCenterBoundsToActor; SnapOriginToActor->Label = FString::Printf( *LOCTEXT("Snap Origin To", "Snap Origin to %s"), *Label); AlignOriginToActor->Label = FString::Printf( *LOCTEXT("Align Origin To", "Align Origin to %s"), *Label); SnapToActor->Label = FString::Printf( *LOCTEXT("Snap To", "Snap to %s"), *Label); AlignToActor->Label = FString::Printf( *LOCTEXT("Align To", "Align to %s"), *Label); SnapPivotToActor->Label = FString::Printf( *LOCTEXT("Snap Pivot To", "Snap Pivot to %s"), *Label); AlignPivotToActor->Label = FString::Printf( *LOCTEXT("Align Pivot To", "Align Pivot to %s"), *Label); SnapBottomCenterBoundsToActor->Label = FString::Printf( *LOCTEXT("Snap Bottom Center Bounds To", "Snap Bottom Center Bounds to %s"), *Label); AlignBottomCenterBoundsToActor->Label = FString::Printf( *LOCTEXT("Align Bottom Center Bounds To", "Align Bottom Center Bounds to %s"), *Label); MenuBuilder.AddMenuEntry( SnapOriginToActor ); MenuBuilder.AddMenuEntry( AlignOriginToActor ); MenuBuilder.AddMenuEntry( SnapToActor ); MenuBuilder.AddMenuEntry( AlignToActor ); MenuBuilder.AddMenuEntry( SnapPivotToActor ); MenuBuilder.AddMenuEntry( AlignPivotToActor ); MenuBuilder.AddMenuEntry( SnapBottomCenterBoundsToActor ); MenuBuilder.AddMenuEntry( AlignBottomCenterBoundsToActor ); } else { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SnapOriginToActor ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().AlignOriginToActor ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SnapToActor ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().AlignToActor ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SnapPivotToActor ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().AlignPivotToActor ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SnapBottomCenterBoundsToActor ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().AlignBottomCenterBoundsToActor ); } */ } void FLevelEditorContextMenuImpl::FillPivotMenu( FMenuBuilder& MenuBuilder ) { MenuBuilder.BeginSection("SaveResetPivot"); { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SavePivotToPrePivot ); MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().ResetPrePivot ); MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().MovePivotHere); MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().MovePivotHereSnapped); } MenuBuilder.EndSection(); MenuBuilder.BeginSection("MovePivot"); { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().MovePivotToCenter ); } MenuBuilder.EndSection(); } void FLevelEditorContextMenuImpl::FillGroupMenu( class FMenuBuilder& MenuBuilder ) { if( SelectionInfo.NumSelectedUngroupedActors > 1 ) { // Only show this menu item if we have more than one actor. MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().GroupActors ); } if( SelectionInfo.bHaveSelectedLockedGroup || SelectionInfo.bHaveSelectedUnlockedGroup ) { const int32 NumActiveGroups = AGroupActor::NumActiveGroups(true); // Regroup will clear any existing groups and create a new one from the selection // Only allow regrouping if multiple groups are selected, or a group and ungrouped actors are selected if( NumActiveGroups > 1 || (NumActiveGroups && SelectionInfo.NumSelectedUngroupedActors) ) { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().RegroupActors ); } MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().UngroupActors ); if( SelectionInfo.bHaveSelectedUnlockedGroup ) { // Only allow removal of loose actors or locked subgroups if( !SelectionInfo.bHaveSelectedLockedGroup || ( SelectionInfo.bHaveSelectedLockedGroup && SelectionInfo.bHaveSelectedSubGroup ) ) { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().RemoveActorsFromGroup ); } MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LockGroup ); } if( SelectionInfo.bHaveSelectedLockedGroup ) { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().UnlockGroup ); } // Only allow group adds if a single group is selected in addition to ungrouped actors if( AGroupActor::NumActiveGroups(true, false) == 1 && SelectionInfo.NumSelectedUngroupedActors ) { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().AddActorsToGroup ); } } } void FLevelEditorContextMenuImpl::FillEditMenu( class FMenuBuilder& MenuBuilder, LevelEditorMenuContext ContextType ) { MenuBuilder.AddMenuEntry( FGenericCommands::Get().Cut ); MenuBuilder.AddMenuEntry( FGenericCommands::Get().Copy ); MenuBuilder.AddMenuEntry( FGenericCommands::Get().Paste ); if (ContextType == LevelEditorMenuContext::Viewport) { MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().PasteHere ); } MenuBuilder.AddMenuEntry( FGenericCommands::Get().Duplicate ); MenuBuilder.AddMenuEntry( FGenericCommands::Get().Delete ); MenuBuilder.AddMenuEntry( FGenericCommands::Get().Rename ); } void FLevelScriptEventMenuHelper::FillLevelBlueprintEventsMenu(class FMenuBuilder& MenuBuilder, const TArray& SelectedActors) { AActor* SelectedActor = (1 == SelectedActors.Num()) ? SelectedActors[0] : NULL; if (FKismetEditorUtilities::IsActorValidForLevelScript(SelectedActor)) { const bool bAnyEventExists = FKismetEditorUtilities::AnyBoundLevelScriptEventForActor(SelectedActor, false); const bool bAnyEventCanBeAdded = FKismetEditorUtilities::AnyBoundLevelScriptEventForActor(SelectedActor, true); if (bAnyEventExists || bAnyEventCanBeAdded) { TWeakObjectPtr ActorPtr(SelectedActor); MenuBuilder.BeginSection("LevelBlueprintEvents", LOCTEXT("LevelBlueprintEvents", "Level Blueprint Events")); if (bAnyEventExists) { MenuBuilder.AddSubMenu( LOCTEXT("JumpEventSubMenu", "Jump to Event"), FText::GetEmpty(), FNewMenuDelegate::CreateStatic(&FKismetEditorUtilities::AddLevelScriptEventOptionsForActor , ActorPtr , true , false , true)); } if (bAnyEventCanBeAdded) { MenuBuilder.AddSubMenu( LOCTEXT("AddEventSubMenu", "Add Event"), FText::GetEmpty(), FNewMenuDelegate::CreateStatic(&FKismetEditorUtilities::AddLevelScriptEventOptionsForActor , ActorPtr , false , true , true)); } MenuBuilder.EndSection(); } } } #undef LOCTEXT_NAMESPACE