Files
UnrealEngineUWP/Engine/Source/Editor/LevelEditor/Private/LevelEditorContextMenu.cpp
Chris Gagnon 1dd3e0189f Merging //UE4/Dev-Main to Dev-Editor (//UE4/Dev-Editor)
#rb none

[CL 4730305 by Chris Gagnon in Dev-Editor branch]
2019-01-15 18:47:22 -05:00

1208 lines
44 KiB
C++

// 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<AActor*>& 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<FExtender> Extender )
{
// Build up the menu
const bool bShouldCloseWindowAfterMenuSelection = true;
FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, TSharedPtr<const FUICommandList>());
FillMenu(MenuBuilder, LevelEditor, ContextType, Extender);
return MenuBuilder.MakeWidget();
}
void FLevelEditorContextMenu::FillMenu( FMenuBuilder& MenuBuilder, TWeakPtr<SLevelEditor> LevelEditor, LevelEditorMenuContext ContextType, TSharedPtr<FExtender> Extender )
{
auto LevelEditorActionsList = LevelEditor.Pin()->GetLevelEditorActions().ToSharedRef();
MenuBuilder.PushCommandList(LevelEditorActionsList);
if (GEditor->GetSelectedComponentCount() > 0)
{
TArray<UActorComponent*> SelectedComponents;
for (FSelectedEditableComponentIterator It(GEditor->GetSelectedEditableComponentIterator()); It; ++It)
{
SelectedComponents.Add(CastChecked<UActorComponent>(*It));
}
MenuBuilder.BeginSection("ComponentControl", LOCTEXT("ComponentControlHeading", "Component"));
{
AActor* OwnerActor = GEditor->GetSelectedActors()->GetTop<AActor>();
if(OwnerActor)
{
MenuBuilder.AddMenuEntry(
FLevelEditorCommands::Get().SelectComponentOwnerActor,
NAME_None,
FText::Format(LOCTEXT("SelectComponentOwner", "Select Owner [{0}]"), FText::FromString(OwnerActor->GetHumanReadableName())),
TAttribute<FText>(),
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<AActor*> SelectedActors;
GEditor->GetSelectedActors()->GetSelectedObjects<AActor>(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<FLevelEditorModule>(TEXT("LevelEditor"));
TArray<FLevelEditorModule::FLevelViewportMenuExtender_SelectedActors> MenuExtenderDelegates = LevelEditorModule.GetAllLevelViewportContextMenuExtenders();
TArray<TSharedPtr<FExtender>> 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<FText>(),
FSlateIconFinder::FindIconForClass(Asset->GetClass())
);
}
else if (ReferencedAssets.Num() > 1)
{
MenuBuilder.AddMenuEntry(
FLevelEditorCommands::Get().EditAssetNoConfirmMultiple,
NAME_None,
LOCTEXT("EditAssociatedAssetsMultiple", "Edit Multiple Assets"),
TAttribute<FText>(),
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<ISceneOutliner> 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<SWidget> 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<SWidget> 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<FExtender> Extender = MakeShareable(new FExtender);
Extender->AddMenuExtension("LevelViewportAttach", EExtensionHook::After, TSharedPtr< FUICommandList >(), FMenuExtensionDelegate::CreateStatic(&Local::ExtendMenu));
// Create the context menu!
TSharedPtr<SWidget> 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<FText>(), 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<AMatineeActor*> MatineeActors;
// first collect all matinee actors
for ( AActor* Actor : SelectionInfo.SharedLevel->Actors )
{
AMatineeActor * CurActor = Cast<AMatineeActor>(Actor);
if ( CurActor )
{
MatineeActors.Add(CurActor);
}
}
if ( MatineeActors.Num() > 0 )
{
FSelectionIterator ActorIter( GEditor->GetSelectedActorIterator() );
AActor* SelectedActor = Cast<AActor>(*ActorIter);
// now delete the matinee actors that don't control currently selected actor
for (int32 MatineeActorIter=0; MatineeActorIter<MatineeActors.Num(); ++MatineeActorIter)
{
AMatineeActor * CurMatineeActor = MatineeActors[MatineeActorIter];
TArray<AActor *> 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; MatineeActorIter<MatineeActors.Num(); ++MatineeActorIter)
{
AMatineeActor * CurMatineeActor = MatineeActors[MatineeActorIter];
const FText Text = FText::Format( LOCTEXT("SelectMatineeActor", "Select {0}"), FText::FromString( CurMatineeActor->GetName() ) );
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<FSceneOutlinerModule>( "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<AActor>();
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<AActor*>& 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<AActor> 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