Files
UnrealEngineUWP/Engine/Source/Editor/VREditor/VREditorModule.cpp
zach brockway b14715aa8b VR Editor: Workaround for tool menu differences triggering ensure in another stream.
#jira UE-164382
#preflight skip

[CL 22088663 by zach brockway in ue5-main branch]
2022-09-20 00:50:01 -04:00

314 lines
9.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "VREditorModule.h"
#include "Modules/ModuleManager.h"
#include "Stats/Stats.h"
#include "HAL/IConsoleManager.h"
#include "IHeadMountedDisplay.h"
#include "ISettingsModule.h"
#include "IVREditorModule.h"
#include "IXRTrackingSystem.h"
#include "LevelEditorActions.h"
#include "ToolMenus.h"
#include "VREditorModeManager.h"
#include "VREditorStyle.h"
#include "VREditorMode.h"
#include "VRModeSettings.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "Framework/MultiBox/MultiBoxExtender.h"
#include "HeadMountedDisplayTypes.h"
#include "UI/VREditorFloatingUI.h"
#define LOCTEXT_NAMESPACE "VREditorModule"
DEFINE_LOG_CATEGORY(LogVREditor);
class FVREditorModule : public IVREditorModule
{
public:
FVREditorModule()
{
}
// FModuleInterface overrides
virtual void StartupModule() override;
virtual void ShutdownModule() override;
virtual void PostLoadCallback() override;
virtual bool SupportsDynamicReloading() override
{
return true;
}
// IVREditorModule overrides
virtual bool IsVREditorEnabled() const override;
virtual bool IsVREditorAvailable() const override;
virtual bool IsVREditorButtonActive() const override;
virtual void EnableVREditor( const bool bEnable, const bool bForceWithoutHMD ) override;
virtual bool IsVREditorModeActive() override;
virtual UVREditorMode* GetVRMode() override;
virtual void UpdateActorPreview(TSharedRef<SWidget> InWidget, int32 Index, AActor *Actor, bool bIsPanelDetached = false) override;
virtual void UpdateExternalUMGUI(const FVREditorFloatingUICreationContext& CreationContext) override;
virtual void UpdateExternalSlateUI(TSharedRef<SWidget> InSlateWidget, FName Name, FVector2D InSize) override;
virtual TSharedPtr<FExtender> GetRadialMenuExtender() override
{
return RadialMenuExtender;
}
static void ToggleForceVRMode();
/** Return a multicast delegate which is executed when VR mode starts. */
virtual FOnVREditingModeEnter& OnVREditingModeEnter() override { return ModeManager.OnVREditingModeEnter(); }
/** Return a multicast delegate which is executed when VR mode stops. */
virtual FOnVREditingModeExit& OnVREditingModeExit() override { return ModeManager.OnVREditingModeExit(); }
private:
void ExtendToolbarMenu();
private:
TSharedPtr<class FExtender> RadialMenuExtender;
/** Handles turning VR Editor mode on and off */
FVREditorModeManager ModeManager;
FDelegateHandle DeferredInitDelegate;
};
namespace VREd
{
static FAutoConsoleCommand ForceVRMode( TEXT( "VREd.ForceVRMode" ), TEXT( "Toggles VREditorMode, even if not in immersive VR" ), FConsoleCommandDelegate::CreateStatic( &FVREditorModule::ToggleForceVRMode ) );
}
void FVREditorModule::StartupModule()
{
LLM_SCOPE_BYNAME(TEXT("VREditor"));
RadialMenuExtender = MakeShareable(new FExtender());
// UToolMenus::RegisterStartupCallback is still too early.
DeferredInitDelegate = FCoreDelegates::OnBeginFrame.AddLambda(
[this]()
{
ExtendToolbarMenu();
// Must happen last; this implicitly deallocates this lambda's captures.
FCoreDelegates::OnBeginFrame.Remove(DeferredInitDelegate);
}
);
}
void FVREditorModule::ShutdownModule()
{
UToolMenus::UnregisterOwner(this);
if (GIsEditor)
{
FVREditorStyle::Shutdown();
}
}
void FVREditorModule::PostLoadCallback()
{
}
bool FVREditorModule::IsVREditorEnabled() const
{
return ModeManager.IsVREditorActive();
}
bool FVREditorModule::IsVREditorAvailable() const
{
return ModeManager.IsVREditorAvailable();
}
bool FVREditorModule::IsVREditorButtonActive() const
{
return ModeManager.IsVREditorButtonActive();
}
void FVREditorModule::EnableVREditor( const bool bEnable, const bool bForceWithoutHMD )
{
ModeManager.EnableVREditor( bEnable, bForceWithoutHMD );
}
bool FVREditorModule::IsVREditorModeActive()
{
return ModeManager.IsVREditorActive();
}
UVREditorMode* FVREditorModule::GetVRMode()
{
return ModeManager.GetCurrentVREditorMode();
}
void FVREditorModule::UpdateActorPreview(TSharedRef<SWidget> InWidget, int32 Index, AActor* Actor, bool bIsPanelDetached)
{
GetVRMode()->RefreshActorPreviewWidget(InWidget, Index, Actor, bIsPanelDetached);
}
void FVREditorModule::UpdateExternalUMGUI(const FVREditorFloatingUICreationContext& CreationContext)
{
GetVRMode()->UpdateExternalUMGUI(CreationContext);
}
void FVREditorModule::UpdateExternalSlateUI(TSharedRef<SWidget> InSlateWidget, FName Name, FVector2D InSize)
{
GetVRMode()->UpdateExternalSlateUI(InSlateWidget, Name, InSize);
}
void FVREditorModule::ToggleForceVRMode()
{
const bool bForceWithoutHMD = true;
FVREditorModule& Self = FModuleManager::GetModuleChecked< FVREditorModule >( TEXT( "VREditor" ) );
Self.EnableVREditor( !Self.IsVREditorEnabled(), bForceWithoutHMD );
}
void FVREditorModule::ExtendToolbarMenu()
{
FToolMenuOwnerScoped OwnerScoped(this);
UToolMenu* EditorToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar.AssetsToolBar");
if (!ensure(EditorToolbarMenu))
{
return;
}
FToolMenuSection* EditorToolbarSection = EditorToolbarMenu->FindSection("Content");
if (!EditorToolbarSection)
{
return;
}
// The primary toggle button.
EditorToolbarSection->AddEntry(FToolMenuEntry::InitToolBarButton(
FLevelEditorCommands::Get().ToggleVR,
LOCTEXT("ToggleVR", "VR Mode")
));
// The "..." split, which summons the tool menu.
static const FName OptionsToolMenuName = "VrEditor.ToggleVrOptions";
EditorToolbarSection->AddEntry(FToolMenuEntry::InitComboButton(
"ToggleVrOptionsMenu",
FUIAction(),
FOnGetContent::CreateLambda([]() { return UToolMenus::Get()->GenerateWidget(OptionsToolMenuName, FToolMenuContext()); }),
TAttribute<FText>(),
LOCTEXT("ToggleVrOptions_ComboSplit_Tooltip", "Configure VR Editor Mode"),
TAttribute<FSlateIcon>(),
true
));
// Register the split options tool menu.
UToolMenu* OptionsMenu = UToolMenus::Get()->RegisterMenu(OptionsToolMenuName);
// The "Status" section indicates whether an HMD is available.
FToolMenuSection& StatusSection = OptionsMenu->AddSection("Status");
StatusSection.AddDynamicEntry("StatusEntry", FNewToolMenuSectionDelegate::CreateLambda(
[](FToolMenuSection& InSection)
{
const bool bHasHMDDevice = GEngine->XRSystem
&& GEngine->XRSystem->GetHMDDevice()
&& GEngine->XRSystem->GetHMDDevice()->IsHMDEnabled();
const FText HmdAvailableText = LOCTEXT("ToggleVrOptions_Status_HmdAvailable", "Headset available ({XrVersionString})");
const FText HmdNotAvailableText = LOCTEXT("ToggleVrOptions_Status_HmdNotAvailable", "No headset available");
const FText StatusText = bHasHMDDevice
? FText::FormatNamed(HmdAvailableText, TEXT("XrVersionString"), FText::FromString(GEngine->XRSystem->GetVersionString()))
: HmdNotAvailableText;
InSection.AddEntry(FToolMenuEntry::InitWidget(
"HeadsetStatus",
SNew(SBox)
.Padding(FMargin(16.0f, 3.0f))
[
SNew(STextBlock)
.ColorAndOpacity(FSlateColor::UseSubduedForeground())
.Text(StatusText)
],
FText::GetEmpty()
));
}));
// The "Modes" section enumerates the UVREditorMode-derived classes for radio selection.
OptionsMenu->AddDynamicSection("DynamicModesSection", FNewToolMenuDelegate::CreateLambda(
[this](UToolMenu* InMenu)
{
FToolMenuSection& Section = InMenu->AddSection("Modes", LOCTEXT("ToggleVrOptions_ModesSection_Label", "Modes"));
TSoftClassPtr<UVREditorMode> CurrentModeClassSoft = GetDefault<UVRModeSettings>()->ModeClass;
UClass* CurrentModeClass = CurrentModeClassSoft.LoadSynchronous();
TArray<UClass*> ModeClasses;
ModeManager.GetConcreteModeClasses(ModeClasses);
Algo::Sort(ModeClasses,
[](const UClass* Lhs, const UClass* Rhs)
{
return Lhs->GetDisplayNameText().CompareTo(Rhs->GetDisplayNameText()) < 0;
});
if (!ModeClasses.Contains(CurrentModeClass))
{
FToolUIAction UnavailableModeAction;
UnavailableModeAction.CanExecuteAction = FToolMenuCanExecuteAction::CreateLambda([](const FToolMenuContext&) { return false; });
UnavailableModeAction.GetActionCheckState = FToolMenuGetActionCheckState::CreateLambda([](const FToolMenuContext&) { return ECheckBoxState::Checked; });
Section.AddMenuEntry("UnavailableMode",
CurrentModeClassSoft.IsNull() ? LOCTEXT("NullModeClass", "(Unset)") : FText::AsCultureInvariant(CurrentModeClassSoft.ToString()),
TAttribute<FText>(),
TAttribute<FSlateIcon>(),
UnavailableModeAction,
EUserInterfaceActionType::RadioButton);
}
for (UClass* Mode : ModeClasses)
{
FToolUIAction SelectModeAction;
SelectModeAction.ExecuteAction = FToolMenuExecuteAction::CreateLambda(
[Mode](const FToolMenuContext&)
{
GetMutableDefault<UVRModeSettings>()->ModeClass = Mode;
});
SelectModeAction.CanExecuteAction = FToolMenuCanExecuteAction::CreateLambda(
[](const FToolMenuContext&)
{
return true;
});
SelectModeAction.GetActionCheckState = FToolMenuGetActionCheckState::CreateLambda(
[Mode](const FToolMenuContext&)
{
return Mode == GetDefault<UVRModeSettings>()->ModeClass.Get() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
});
Section.AddMenuEntry(Mode->GetFName(),
Mode->GetDisplayNameText(),
FText::AsCultureInvariant(Mode->GetClassPathName().ToString()),
TAttribute<FSlateIcon>(),
SelectModeAction,
EUserInterfaceActionType::RadioButton);
}
}));
FToolMenuSection& SettingsSection = OptionsMenu->AddSection("Settings");
SettingsSection.AddSeparator("SettingsStartSeparator");
SettingsSection.AddMenuEntry(
"EditorSettings",
LOCTEXT("ToggleVrOptions_EditorSettings_Label", "Settings..."),
LOCTEXT("ToggleVrOptions_EditorSettings_Tooltip", "Jump to the VR Editor Mode section in the Editor Preferences"),
FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.GameSettings"),
FUIAction(FExecuteAction::CreateLambda([]() { FModuleManager::LoadModuleChecked<ISettingsModule>("Settings").ShowViewer("Editor", "General", "VR Mode"); }))
);
}
IMPLEMENT_MODULE( FVREditorModule, VREditor )
#undef LOCTEXT_NAMESPACE