// Copyright Epic Games, Inc. All Rights Reserved. #include "Tools/UEdMode.h" #include "EditorModeManager.h" #include "EditorModes.h" #include "Toolkits/BaseToolkit.h" #include "Tools/EdModeInteractiveToolsContext.h" #include "Toolkits/ToolkitManager.h" #include "InteractiveToolManager.h" #include "InteractiveToolQueryInterfaces.h" #include "GameFramework/Actor.h" #include "Elements/Framework/TypedElementSelectionSet.h" #include "Elements/Interfaces/TypedElementObjectInterface.h" #include "Settings/LevelEditorViewportSettings.h" #include "EditorViewportClient.h" #include "EditorViewportCommands.h" ////////////////////////////////// // UEdMode UEdMode::UEdMode() : Owner(nullptr) { EditorToolsContext = nullptr; ModeToolsContext = nullptr; ToolCommandList = MakeShareable(new FUICommandList); } void UEdMode::Initialize() { } void UEdMode::SelectNone() { } bool UEdMode::ProcessEditDelete() { return false; } void UEdMode::Enter() { // Update components for selected actors, in case the mode we just exited // was hijacking selection events selection and not updating components. Owner->GetEditorSelectionSet()->ForEachSelectedObject([](AActor* ActorPtr) { ActorPtr->MarkComponentsRenderStateDirty(); return true; }); CreateInteractiveToolsContexts(); ModeToolsContext->ToolManager->OnToolStarted.AddUObject(this, &UEdMode::OnToolStarted); ModeToolsContext->ToolManager->OnToolEnded.AddUObject(this, &UEdMode::OnToolEnded); // Create the settings object so that the toolkit has access to the object we are going to use at creation time if (SettingsClass.IsValid()) { UClass* LoadedSettingsObject = SettingsClass.LoadSynchronous(); SettingsObject = NewObject(this, LoadedSettingsObject); } // Now that the context is ready, make the toolkit CreateToolkit(); if (Toolkit.IsValid()) { Toolkit->Init(Owner->GetToolkitHost(), this); } BindCommands(); if (SettingsObject) { SettingsObject->LoadConfig(); if (Toolkit.IsValid()) { Toolkit->SetModeSettingsObject(SettingsObject); } } Owner->OnEditorModeIDChanged().AddUObject(this, &UEdMode::OnModeActivated); PRAGMA_DISABLE_DEPRECATION_WARNINGS FEditorDelegates::EditorModeIDEnter.Broadcast(GetID()); PRAGMA_ENABLE_DEPRECATION_WARNINGS } void UEdMode::RegisterTool(TSharedPtr UICommand, FString ToolIdentifier, UInteractiveToolBuilder* Builder, EToolsContextScope ToolScope) { if (!Toolkit.IsValid()) { return; } if (ToolScope == EToolsContextScope::Default) { ToolScope = GetDefaultToolScope(); } UEditorInteractiveToolsContext* UseToolsContext = GetInteractiveToolsContext(ToolScope); if (ensure(UseToolsContext != nullptr) == false) { return; } const TSharedRef& CommandList = Toolkit->GetToolkitCommands(); UseToolsContext->ToolManager->RegisterToolType(ToolIdentifier, Builder); CommandList->MapAction(UICommand, FExecuteAction::CreateUObject(UseToolsContext, &UEdModeInteractiveToolsContext::StartTool, ToolIdentifier), FCanExecuteAction::CreateWeakLambda(UseToolsContext, [this, ToolIdentifier, UseToolsContext]() { return ShouldToolStartBeAllowed(ToolIdentifier) && UseToolsContext->ToolManager->CanActivateTool(EToolSide::Mouse, ToolIdentifier); }), FIsActionChecked::CreateUObject(UseToolsContext, &UEdModeInteractiveToolsContext::IsToolActive, EToolSide::Mouse, ToolIdentifier), EUIActionRepeatMode::RepeatDisabled); if (ToolScope == EToolsContextScope::Editor) { RegisteredEditorTools.Emplace(UICommand, ToolIdentifier); } } bool UEdMode::ShouldToolStartBeAllowed(const FString& ToolIdentifier) const { // Disallow starting tools when playing in editor or simulating. return !GEditor->PlayWorld && !GIsPlayInEditorWorld; } void UEdMode::Exit() { if (SettingsObject) { SettingsObject->SaveConfig(); } if (UObjectInitialized()) { Owner->OnEditorModeIDChanged().RemoveAll(this); // Shutdown the Mode-scope ToolsContext, and notify the EditorToolsContext so it can release the reference it holds. // Do this before shutting down the Toolkit as if a Mode-scope Tool is still active it will need to clean up DestroyInteractiveToolsContexts(); } if (Toolkit.IsValid()) { const TSharedRef& CommandList = Toolkit->GetToolkitCommands(); for (auto& RegisteredTool : RegisteredEditorTools) { CommandList->UnmapAction(RegisteredTool.Key); EditorToolsContext->ToolManager->UnregisterToolType(RegisteredTool.Value); } Toolkit.Reset(); } RegisteredEditorTools.SetNum(0); // disconnect from the Mode Manager's shared ToolsContext EditorToolsContext = nullptr; ModeToolsContext = nullptr; PRAGMA_DISABLE_DEPRECATION_WARNINGS FEditorDelegates::EditorModeIDExit.Broadcast(GetID()); PRAGMA_ENABLE_DEPRECATION_WARNINGS } bool UEdMode::UsesToolkits() const { return true; } UWorld* UEdMode::GetWorld() const { return EditorToolsContext.IsValid() ? EditorToolsContext->GetWorld() : nullptr; } FEditorModeTools* UEdMode::GetModeManager() const { return EditorToolsContext.IsValid() ? EditorToolsContext->GetParentEditorModeManager() : nullptr; } AActor* UEdMode::GetFirstSelectedActorInstance() const { if (FEditorModeTools* ModeManager = GetModeManager()) { return ModeManager->GetEditorSelectionSet()->GetTopSelectedObject(); } return nullptr; } UInteractiveToolManager* UEdMode::GetToolManager(EToolsContextScope ToolScope) const { if (ToolScope == EToolsContextScope::Default) { ToolScope = GetDefaultToolScope(); } if (ToolScope == EToolsContextScope::Editor) { return (EditorToolsContext.IsValid()) ? EditorToolsContext->ToolManager : nullptr; } else if (ToolScope == EToolsContextScope::EdMode) { return ModeToolsContext ? ModeToolsContext->ToolManager : nullptr; } ensure(false); return nullptr; } UEditorInteractiveToolsContext* UEdMode::GetInteractiveToolsContext(EToolsContextScope ToolScope) const { if (ToolScope == EToolsContextScope::Default) { ToolScope = GetDefaultToolScope(); } if (ToolScope == EToolsContextScope::Editor) { return EditorToolsContext.IsValid() ? EditorToolsContext.Get() : nullptr; } else if (ToolScope == EToolsContextScope::EdMode) { return ModeToolsContext; } ensure(false); return nullptr; } void UEdMode::CreateToolkit() { if (!UsesToolkits()) { return; } check(!Toolkit.IsValid()) Toolkit = MakeShareable(new FModeToolkit); } void UEdMode::OnModeActivated(const FEditorModeID& InID, bool bIsActive) { if (InID == GetID()) { if (bIsActive) { EditorToolsContext->OnChildEdModeActivated(ModeToolsContext); EditorToolsContext->ToolManager->OnToolStarted.AddUObject(this, &UEdMode::OnToolStarted); EditorToolsContext->ToolManager->OnToolEnded.AddUObject(this, &UEdMode::OnToolEnded); } else { EditorToolsContext->ToolManager->OnToolStarted.RemoveAll(this); EditorToolsContext->ToolManager->OnToolEnded.RemoveAll(this); EditorToolsContext->OnChildEdModeDeactivated(ModeToolsContext); } } } void UEdMode::CreateInteractiveToolsContexts() { // Editor-scope ToolsContext is provided by the Mode Manager EditorToolsContext = Owner->GetInteractiveToolsContext(); check(EditorToolsContext.IsValid()); // Mode-scope ToolsContext is created derived from the Editor-Scope ToolsContext so that the UInputRouter can be shared ModeToolsContext = EditorToolsContext->CreateNewChildEdModeToolsContext(); check(ModeToolsContext); } void UEdMode::DestroyInteractiveToolsContexts() { if (ModeToolsContext) { ModeToolsContext->ShutdownContext(); } } bool UEdMode::IsSnapRotationEnabled() { return GetDefault()->RotGridEnabled; } void UEdMode::BindCommands() { if (Toolkit && GetModeManager()) { const TSharedRef& CommandList = Toolkit->GetToolkitCommands(); const FEditorViewportCommands& ViewportCommands = FEditorViewportCommands::Get(); CommandList->MapAction( ViewportCommands.FocusViewportToSelection, FExecuteAction::CreateLambda([this]() { if (ensure(GetModeManager() && GetModeManager()->GetFocusedViewportClient() && GetModeManager()->GetFocusedViewportClient()->IsLevelEditorClient())) { FEditorViewportClient* ViewportClient = GetModeManager()->GetFocusedViewportClient(); FBox FocusBox = ComputeCustomViewportFocus(); if (FocusBox.IsValid) { // This method has custom logic for smoothly moving linked ortho viewports, so we prefer it for level editor viewports GEditor->MoveViewportCamerasToBox(FocusBox, true); // Note that to support non-level-editor viewports we could do GetModeManager()->GetFocusedViewportClient()->FocusViewportOnBox(FocusBox) // However we instead currently expect that these viewports support the focus api by overriding ComputeBoundingBoxForViewportFocus() } } }), FCanExecuteAction::CreateLambda([this]() { // For level-editor viewports, implement the focus api here to decouple it from the standard level-editor focus logic (which depends on the level editor selection) // Note we do not execute for non-level-editor viewports (e.g., asset editors), which must implement their own focus logic return GetModeManager() && GetModeManager()->GetFocusedViewportClient() && GetModeManager()->GetFocusedViewportClient()->IsLevelEditorClient() && HasCustomViewportFocus(); } ) ); } } FBox UEdMode::GetFocusBoxFromActiveToolFocusAPI() const { UInteractiveToolManager* ToolManager = GetToolManager(); if (ToolManager && ToolManager->HasAnyActiveTool()) { UInteractiveTool* Tool = ToolManager->GetActiveTool(EToolSide::Mouse); IInteractiveToolCameraFocusAPI* FocusAPI = Cast(Tool); if (FocusAPI && FocusAPI->SupportsWorldSpaceFocusBox()) { return FocusAPI->GetWorldSpaceFocusBox(); } } return FBox(); } bool UEdMode::HasCustomViewportFocus() const { // Support ITF Focus API FBox FocusBox = GetFocusBoxFromActiveToolFocusAPI(); return (bool)FocusBox.IsValid; } FBox UEdMode::ComputeCustomViewportFocus() const { // Support ITF Focus API return GetFocusBoxFromActiveToolFocusAPI(); }