// Copyright Epic Games, Inc. All Rights Reserved. #include "RewindDebuggerModule.h" #include "AnimInstanceHelpers.h" #include "BlueprintDoubleClickHandler.h" #include "Engine/Selection.h" #include "Features/IModularFeatures.h" #include "Framework/Docking/LayoutExtender.h" #include "IAnimationBlueprintEditorModule.h" #include "Kismet2/DebuggerCommands.h" #include "LevelEditor.h" #include "Modules/ModuleManager.h" #include "PropertyTraceMenu.h" #include "RewindDebugger.h" #include "RewindDebuggerCommands.h" #include "RewindDebuggerStyle.h" #include "SRewindDebugger.h" #include "ToolMenus.h" #include "Widgets/Docking/SDockTab.h" #include "WorkspaceMenuStructure.h" #include "WorkspaceMenuStructureModule.h" #define LOCTEXT_NAMESPACE "RewindDebuggerModule" const FName FRewindDebuggerModule::MainTabName("RewindDebugger2"); const FName FRewindDebuggerModule::DetailsTabName("RewindDebuggerDetails2"); const FName FRewindDebuggerModule::MainMenuName("RewindDebugger.MainMenu"); const FName FRewindDebuggerModule::TrackContextMenuName("RewindDebugger.TrackContextMenu"); TSharedRef FRewindDebuggerModule::SpawnRewindDebuggerDetailsTab(const FSpawnTabArgs& SpawnTabArgs) { if (FRewindDebugger::Instance() == nullptr) { FRewindDebugger::Initialize(); } FRewindDebugger* RewindDebugger = FRewindDebugger::Instance(); RewindDebugger->SetIsDetailsPanelOpen(true); TSharedRef MajorTab = SNew(SDockTab) .TabRole(ETabRole::PanelTab); MajorTab->SetOnTabClosed(SDockTab::FOnTabClosedCallback::CreateLambda( [](TSharedRef) { FRewindDebugger::Instance()->SetIsDetailsPanelOpen(false); })); RewindDebugger->UpdateDetailsPanel(MajorTab); return MajorTab; } TSharedRef FRewindDebuggerModule::SpawnRewindDebuggerTab(const FSpawnTabArgs& SpawnTabArgs) { if (FRewindDebugger::Instance() == nullptr) { FRewindDebugger::Initialize(); } const TSharedRef MajorTab = SNew(SDockTab) .TabRole(ETabRole::PanelTab) .OnTabClosed_Lambda([this](TSharedRef) { // clear reference to widget so it will be destroyed RewindDebuggerWidget = nullptr; }); TSharedPtr CommandList = MakeShared(); const FRewindDebuggerCommands& Commands = FRewindDebuggerCommands::Get(); FRewindDebugger* DebuggerInstance = FRewindDebugger::Instance(); CommandList->MapAction(Commands.Play, FExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::Play), FCanExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::CanPlay), FIsActionChecked(), FIsActionButtonVisible()); CommandList->MapAction(Commands.Pause, FExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::Pause), FCanExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::CanPause), FIsActionChecked(), FIsActionButtonVisible()); CommandList->MapAction(Commands.PauseOrPlay, FExecuteAction::CreateLambda([DebuggerInstance]() { if (DebuggerInstance->CanPause()) { DebuggerInstance->Pause(); } else if (DebuggerInstance->CanPlay()) { DebuggerInstance->Play(); } }), FCanExecuteAction::CreateLambda([DebuggerInstance]() { return DebuggerInstance->CanPause() || DebuggerInstance->CanPlay(); }), FIsActionChecked(), FIsActionButtonVisible()); CommandList->MapAction(Commands.ReversePlay, FExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::PlayReverse), FCanExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::CanPlayReverse), FIsActionChecked(), FIsActionButtonVisible()); CommandList->MapAction(Commands.PreviousFrame, FExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::StepBackward), FCanExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::CanScrub), FIsActionChecked(), FIsActionButtonVisible()); CommandList->MapAction(Commands.FirstFrame, FExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::ScrubToStart), FCanExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::CanScrub), FIsActionChecked(), FIsActionButtonVisible()); CommandList->MapAction(Commands.LastFrame, FExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::ScrubToEnd), FCanExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::CanScrub), FIsActionChecked(), FIsActionButtonVisible()); CommandList->MapAction(Commands.NextFrame, FExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::StepForward), FCanExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::CanScrub), FIsActionChecked(), FIsActionButtonVisible()); CommandList->MapAction(Commands.StartRecording, FExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::StartRecording), FCanExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::CanStartRecording), FIsActionChecked(), FIsActionButtonVisible::CreateLambda([]() { return !FRewindDebugger::Instance()->IsRecording();})); CommandList->MapAction(Commands.StopRecording, FExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::StopRecording), FCanExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::CanStopRecording), FIsActionChecked(), FIsActionButtonVisible::CreateRaw(DebuggerInstance, &FRewindDebugger::CanStopRecording)); CommandList->MapAction(Commands.AutoEject, FExecuteAction::CreateLambda([DebuggerInstance]() { DebuggerInstance->SetShouldAutoEject(!DebuggerInstance->ShouldAutoEject()); }), FCanExecuteAction(), FCanExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::ShouldAutoEject), FIsActionButtonVisible()); CommandList->MapAction(Commands.AutoRecord, FExecuteAction::CreateLambda([DebuggerInstance]() { DebuggerInstance->SetShouldAutoRecordOnPIE(!DebuggerInstance->ShouldAutoRecordOnPIE()); }), FCanExecuteAction(), FCanExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::ShouldAutoRecordOnPIE), FIsActionButtonVisible()); CommandList->MapAction(Commands.OpenTrace, FExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::OpenTrace), FCanExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::CanOpenTrace), FIsActionChecked(), FIsActionButtonVisible()); CommandList->MapAction(Commands.AttachToSession, FExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::AttachToSession), FCanExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::CanOpenTrace), FIsActionChecked(), FIsActionButtonVisible()); CommandList->MapAction(Commands.SaveTrace, FExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::SaveTrace), FCanExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::CanSaveTrace), FIsActionChecked(), FIsActionButtonVisible()); CommandList->MapAction(Commands.ClearTrace, FExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::ClearTrace), FCanExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::CanClearTrace), FIsActionChecked(), FIsActionButtonVisible()); // Register PIE Rewind Debugger Commands if (GEditor != nullptr) { check(FPlayWorldCommands::GlobalPlayWorldActions.IsValid()); FPlayWorldCommands::GlobalPlayWorldActions->MapAction(Commands.StartRecording, FExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::StartRecording), FCanExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::CanStartRecording), FIsActionChecked(), FIsActionButtonVisible::CreateLambda([]() { return !FRewindDebugger::Instance()->IsRecording();})); FPlayWorldCommands::GlobalPlayWorldActions->MapAction(Commands.StopRecording, FExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::StopRecording), FCanExecuteAction::CreateRaw(DebuggerInstance, &FRewindDebugger::CanStopRecording), FIsActionChecked(), FIsActionButtonVisible::CreateRaw(DebuggerInstance, &FRewindDebugger::CanStopRecording)); } RewindDebuggerWidget = SNew(SRewindDebugger, CommandList.ToSharedRef(), MajorTab, SpawnTabArgs.GetOwnerWindow()) .DebuggedObjectName({ DebuggerInstance->GetDebugTargetActorProperty(), URewindDebuggerSettings::Get().DebugTargetActor}) .RecordingDuration(DebuggerInstance->GetRecordingDurationProperty()) .Tracks(&DebuggerInstance->GetTracks()) .TraceTime(DebuggerInstance->GetTraceTimeProperty()) .OnScrubPositionChanged_Raw(DebuggerInstance,&FRewindDebugger::ScrubToTime) .OnViewRangeChanged_Raw(DebuggerInstance,&FRewindDebugger::SetCurrentViewRange) .OnTrackDoubleClicked_Raw(DebuggerInstance, &FRewindDebugger::TrackDoubleClicked) .OnTrackSelectionChanged_Raw(DebuggerInstance, &FRewindDebugger::TrackSelectionChanged) .BuildTrackContextMenu_Raw(DebuggerInstance, &FRewindDebugger::BuildTrackContextMenu) .TrackTypes_Lambda([]() { return FRewindDebugger::Instance()->GetTrackTypes(); }) .ScrubTime_Lambda([]() { return FRewindDebugger::Instance()->GetScrubTime(); }); DebuggerInstance->SetTrackCursorDelegate(FRewindDebugger::FOnTrackCursor::CreateSP(RewindDebuggerWidget.Get(), &SRewindDebugger::TrackCursor)); DebuggerInstance->SetTrackListChangedDelegate(FRewindDebugger::FOnTrackListChanged::CreateSP(RewindDebuggerWidget.Get(), &SRewindDebugger::RefreshTracks)); MajorTab->SetContent(RewindDebuggerWidget.ToSharedRef()); return MajorTab; } static FAnimInstanceDoubleClickHandler AnimInstanceDoubleClickHandler; static FBlueprintDoubleClickHandler BlueprintDoubleClickHandler; void FRewindDebuggerModule::StartupModule() { UToolMenus::Get()->RegisterMenu(FRewindDebuggerModule::MainMenuName); UToolMenus::Get()->RegisterMenu(FRewindDebuggerModule::TrackContextMenuName); FRewindDebuggerStyle::Initialize(); FRewindDebuggerCommands::Register(); FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); /* LevelEditorTabManagerChangedHandle = */ LevelEditorModule.OnTabManagerChanged().AddLambda([this]() { FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); TSharedPtr LevelEditorTabManager = LevelEditorModule.GetLevelEditorTabManager(); LevelEditorTabManager->RegisterTabSpawner( MainTabName, FOnSpawnTab::CreateRaw(this, &FRewindDebuggerModule::SpawnRewindDebuggerTab)) .SetDisplayName(LOCTEXT("TabTitle", "Rewind Debugger")) .SetIcon(FSlateIcon("RewindDebuggerStyle", "RewindDebugger.RewindIcon")) .SetTooltipText(LOCTEXT("TooltipText", "Opens Rewind Debugger.")) .SetGroup(WorkspaceMenu::GetMenuStructure().GetDeveloperToolsDebugCategory()); LevelEditorTabManager->RegisterTabSpawner( DetailsTabName, FOnSpawnTab::CreateStatic(&FRewindDebuggerModule::SpawnRewindDebuggerDetailsTab)) .SetDisplayName(LOCTEXT("DetailsTabTitle", "Rewind Debugger Details")) .SetIcon(FSlateIcon("RewindDebuggerStyle", "RewindDebugger.RewindDetailsIcon")) .SetTooltipText(LOCTEXT("DetailsWindowTooltipText", "Opens Rewind Debugger Details Window.")) .SetGroup(WorkspaceMenu::GetMenuStructure().GetDeveloperToolsDebugCategory()); }); /*LevelEditorLayoutExtensionHandle = */ LevelEditorModule.OnRegisterLayoutExtensions().AddLambda( [this](FLayoutExtender& Extender) { Extender.ExtendLayout(FName("LevelEditorSelectionDetails"), ELayoutExtensionPosition::After, FTabManager::FTab(DetailsTabName, ETabState::ClosedTab)); Extender.ExtendLayout(FName("Sequencer"), ELayoutExtensionPosition::After, FTabManager::FTab(MainTabName, ETabState::ClosedTab)); } ); RewindDebuggerCameraExtension.Initialize(); RewindDebuggerAnimationExtension.Initialize(); IModularFeatures::Get().RegisterModularFeature(IRewindDebuggerExtension::ModularFeatureName, &RewindDebuggerCameraExtension); IModularFeatures::Get().RegisterModularFeature(IRewindDebuggerExtension::ModularFeatureName, &RewindDebuggerAnimationExtension); IModularFeatures::Get().RegisterModularFeature(IRewindDebuggerDoubleClickHandler::ModularFeatureName, &AnimInstanceDoubleClickHandler); IModularFeatures::Get().RegisterModularFeature(IRewindDebuggerDoubleClickHandler::ModularFeatureName, &BlueprintDoubleClickHandler); FPropertyTraceMenu::Register(); FAnimInstanceMenu::Register(); FRewindDebugger::RegisterTrackContextMenu(); FRewindDebugger::RegisterToolBar(); } void FRewindDebuggerModule::ShutdownModule() { RewindDebuggerAnimationExtension.Shutdown(); IModularFeatures::Get().UnregisterModularFeature(IRewindDebuggerExtension::ModularFeatureName, &RewindDebuggerCameraExtension); IModularFeatures::Get().UnregisterModularFeature(IRewindDebuggerExtension::ModularFeatureName, &RewindDebuggerAnimationExtension); IModularFeatures::Get().UnregisterModularFeature(IRewindDebuggerDoubleClickHandler::ModularFeatureName, &AnimInstanceDoubleClickHandler); IModularFeatures::Get().UnregisterModularFeature(IRewindDebuggerDoubleClickHandler::ModularFeatureName, &BlueprintDoubleClickHandler); FRewindDebuggerCommands::Unregister(); FRewindDebuggerStyle::Shutdown(); FRewindDebugger::Shutdown(); } IMPLEMENT_MODULE(FRewindDebuggerModule, RewindDebugger); #undef LOCTEXT_NAMESPACE