Files
UnrealEngineUWP/Engine/Source/Developer/LogVisualizer/Private/SVisualLogger.cpp
Marc Audy 3c0e962858 Copying //UE4/Dev-Frame to //UE4/Main
#lockdown Nick.Penwarden

==========================
MAJOR FEATURES + CHANGES
==========================

Change 2775736 on 2015/11/20 by Richard.Hinckley

	Fix for Paper2D issue with repeated imports in one edutor session. Paper2D import process now creates a new importer at the end. This prevents the sprite sheet import process from leaving frame data around, causing subsequent imports (including imports of different sprite sheets) to include this data inappropriately.

	#codereview michael.noland

Change 2776352 on 2015/11/20 by Zak.Middleton

	#ue4 - Avoid useless DetachFromParent() for the same pending AttachParent during registration. Added missing UpdateOverlaps() when detaching from object simulating physics.

	#rb Marc.Audy, Ori.Cohen
	#codereview James.Golding

Change 2776401 on 2015/11/20 by Mieszko.Zielinski

	Implemented a way to do batched points projection to navmesh, where every point can declare a custom projection box #UE4

	The biggest advantage here is that projection box is independent from projected point - no more manual offsetting of projected point to achieve "100uu up and 500uu down"-like functionality

	#jira UE-23705
	#rb Lukasz.Furman

Change 2777450 on 2015/11/23 by Martin.Wilson

	Bake additive data into animations during cooking to avoid doing additive calculations and extra pose extraction and blending at runtime

	#rb Thomas.Sarkanen

Change 2777698 on 2015/11/23 by Mieszko.Zielinski

	Gameplay debugging tools fixes #UE4

	Fixes:
	- made newly added logs respect Log Visualizer's filters
	- added handling of invalid data when trying to draw EGameplayDebuggerShapeElement::Cylinder in AGameplayDebuggingHUDComponent::DrawPerception. This is a patch, root cause to be found.
	- fixed Log Visualizer resetting it's data while trying to serialize invalid objects. This is a patch, root cause to be addressed.

	In addition
	- while at it removed bunch of 'auto' and 'class' keywords from the files I've touched

	#rb Lukasz.Furman

Change 2777762 on 2015/11/23 by Mieszko.Zielinski

	Removed BlackboardComponent's functionality deprecated since 4.7 #UE4

	#rb Lukasz.Furman

Change 2777839 on 2015/11/23 by Zak.Middleton

	#ue4 - Wrap all vector library calls to math functions through our FMath versions, so they benefit from fixes or improvements therein. Added Exp2() function.

	#rb Laurent.Delayen

Change 2777840 on 2015/11/23 by Zak.Middleton

	#ue4 - Fix up uses of library math functions to go through our FMath namespace.

	#rb Laurent.Delayen

Change 2778287 on 2015/11/23 by Stan.Melax

	deprecation of  FCollisionQueryParams(bool)

	See 2774707 description for the whole story

	#OR-9936

	#codereview marc.audy

	Changes to kite will have to be in a separate check-in

	I couldn't submit to all files from the framework branch   addition fixes have their files are shelved in cl 2778299

Change 2778507 on 2015/11/23 by Marc.Audy

	Eliminate spurious cook warnings for known missing packages
	#rb Michael.Noland

Change 2778546 on 2015/11/23 by Aaron.McLeran

	Moving occlusion feature settings from audio component to sound attenuation settings struct.

	- Sound attenuation setting struct is used for all sounds that do 3d spatialization so it make sense for the occlusion feature settings to be there.
	- Kept old low-pass frequency filter setting values on audio component (where HighFrequencyAttenuation used to be)

	#rb Zak.Middleton

Change 2778664 on 2015/11/23 by Zak.Middleton

	#ue4 - Clarify some comparison functions (IsZero, IsNearlyZero, Equals) in FRotator to explain that they compare as orientations, not other interpretations such as rotational speed, winding, etc.

	#rb Aaron.Mcleran
	#codereview Frank.Gigliotti

Change 2779335 on 2015/11/24 by Mieszko.Zielinski

	Another VisualLog patch to avoid crashing due to a core bug that remains to be investigated #UE4

	Again, the core bug here is related visual log trying to serialize invalid objects on a regular basis.

	#rb Lukasz.Furman

Change 2779338 on 2015/11/24 by Benn.Gallagher

	Fixed crash in Persona when focus is taken from a different window
	#jira UE-22516
	#rb Ben.Cosh

Change 2779375 on 2015/11/24 by Benn.Gallagher

	Fix for deadlock in destructibles. Aquiring actor buffer without releasing causes an infinite wait on next aquire.
	#rb Ori.Cohen

Change 2779753 on 2015/11/24 by Zak.Middleton

	#ue4 - FMath::Atan2() no longer calls atan2f() because of some compiler or library bugs causing it to randomly return NaN for valid input. It now uses a high-precision minimax approximation instead, measured to be 2x faster than the stock C version.

	#rb Brian.Karis

Change 2779853 on 2015/11/24 by Marc.Audy
2015-12-02 16:42:06 -05:00

1097 lines
38 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "LogVisualizer.h"
#include "SDockTab.h"
#include "VisualLogger/VisualLogger.h"
#include "VisualLoggerRenderingActor.h"
#include "VisualLoggerCanvasRenderer.h"
#include "DesktopPlatformModule.h"
#include "MainFrame.h"
#include "VisualLoggerCameraController.h"
#include "TimeSliderController.h"
#if WITH_EDITOR
# include "Editor/UnrealEd/Public/EditorComponents.h"
# include "Editor/UnrealEd/Public/EditorReimportHandler.h"
# include "Editor/UnrealEd/Public/TexAlignTools.h"
# include "Editor/UnrealEd/Public/TickableEditorObject.h"
# include "Editor/UnrealEd/Public/Editor.h"
# include "Editor/UnrealEd/Public/EditorViewportClient.h"
#endif
#include "ISettingsModule.h"
#include "VisualLogger/VisualLoggerBinaryFileDevice.h"
#define LOCTEXT_NAMESPACE "SVisualLogger"
DEFINE_LOG_CATEGORY_STATIC(LogVisualLogger, Log, All);
/* Local constants
*****************************************************************************/
static const FName ToolbarTabId("Toolbar");
static const FName FiltersTabId("Filters");
static const FName MainViewTabId("MainView");
static const FName LogsListTabId("LogsList");
static const FName StatusViewTabId("StatusView");
namespace LogVisualizer
{
static const FString LogFileDescription = LOCTEXT("FileTypeDescription", "Visual Log File").ToString();
static const FString LoadFileTypes = FString::Printf(TEXT("%s (*.bvlog;*.%s)|*.bvlog;*.%s"), *LogFileDescription, VISLOG_FILENAME_EXT, VISLOG_FILENAME_EXT);
static const FString SaveFileTypes = FString::Printf(TEXT("%s (*.%s)|*.%s"), *LogFileDescription, VISLOG_FILENAME_EXT, VISLOG_FILENAME_EXT);
}
DECLARE_DELEGATE_TwoParams(FOnWorldChanged, UWorld*, UWorld*);
/* SMessagingDebugger constructors
*****************************************************************************/
namespace
{
static UWorld* GetWorldForGivenObject(const UObject* Object)
{
UWorld* World = Object ? GEngine->GetWorldFromContextObject(Object, false) : nullptr;
#if WITH_EDITOR
UEditorEngine *EEngine = Cast<UEditorEngine>(GEngine);
if (GIsEditor && EEngine != nullptr && World == nullptr)
{
// lets use PlayWorld during PIE/Simulate and regular world from editor otherwise, to draw debug information
World = EEngine->PlayWorld != nullptr ? EEngine->PlayWorld : EEngine->GetEditorWorldContext().World();
}
#endif
if (!GIsEditor && World == nullptr)
{
World = GEngine->GetWorld();
}
return World;
}
}
SVisualLogger::SVisualLogger()
: SCompoundWidget(), CommandList(MakeShareable(new FUICommandList))
{
bPausedLogger = false;
bGotHistogramData = false;
class FVisualLoggerDevice : public FVisualLogDevice
{
public:
FVisualLoggerDevice(SVisualLogger* InVisualLogger, FOnWorldChanged OnWorldChangedDelegate) : VisualLoggerWidget(InVisualLogger), LastUsedWorld(nullptr), OnWorldChanged(OnWorldChangedDelegate) {}
virtual ~FVisualLoggerDevice(){}
virtual void Serialize(const UObject* LogOwner, FName OwnerName, FName OwnerClassName, const FVisualLogEntry& LogEntry) override
{
VisualLoggerWidget->OnNewLogEntry(FVisualLogDevice::FVisualLogEntryItem(OwnerName, OwnerClassName, LogEntry));
UWorld* CurrentWorld = nullptr;
if (FVisualLogger::Get().GetObjectToWorldMap().Contains(LogOwner))
{
CurrentWorld = const_cast<UWorld*>(FVisualLogger::Get().GetObjectToWorldMap()[LogOwner].Get());
}
if (LastUsedWorld != CurrentWorld && CurrentWorld != nullptr)
{
OnWorldChanged.ExecuteIfBound(LastUsedWorld, CurrentWorld);
LastUsedWorld = CurrentWorld;
}
}
SVisualLogger* VisualLoggerWidget;
UWorld* LastUsedWorld;
FOnWorldChanged OnWorldChanged;
};
InternalDevice = MakeShareable(new FVisualLoggerDevice(this, FOnWorldChanged::CreateLambda([this](UWorld* PreviousWorld, UWorld* CurrentWorld){ OnNewWorld(CurrentWorld); })));
FVisualLogger::Get().AddDevice(InternalDevice.Get());
}
SVisualLogger::~SVisualLogger()
{
GEngine->OnWorldAdded().RemoveAll(this);
FVisualLogger::Get().RemoveDevice(InternalDevice.Get());
InternalDevice.Reset();
UWorld* World = NULL;
#if WITH_EDITOR
ULogVisualizerSettings::StaticClass()->GetDefaultObject<ULogVisualizerSettings>()->SavePresistentData();
#endif
if (LastUsedWorld.IsValid())
{
for (TActorIterator<AVisualLoggerRenderingActor> It(LastUsedWorld.Get()); It; ++It)
{
LastUsedWorld->DestroyActor(*It);
}
}
UDebugDrawService::Unregister(DrawOnCanvasDelegateHandle);
VisualLoggerCanvasRenderer.Reset();
FVisualLoggerDatabase::Get().GetEvents().OnRowSelectionChanged.RemoveAll(this);
FVisualLoggerDatabase::Get().GetEvents().OnNewItem.RemoveAll(this);
FVisualLoggerDatabase::Get().GetEvents().OnItemSelectionChanged.RemoveAll(this);
FLogVisualizer::Get().GetEvents().OnFiltersChanged.RemoveAll(this);
FLogVisualizer::Get().GetEvents().OnLogLineSelectionChanged.Unbind();
FLogVisualizer::Get().GetEvents().OnKeyboardEvent.Unbind();
FVisualLoggerDatabase::Get().Reset();
}
/* SMessagingDebugger interface
*****************************************************************************/
void SVisualLogger::Construct(const FArguments& InArgs, const TSharedRef<SDockTab>& ConstructUnderMajorTab, const TSharedPtr<SWindow>& ConstructUnderWindow)
{
bPausedLogger = false;
bGotHistogramData = false;
FLogVisualizer::Get().SetCurrentVisualizer(SharedThis(this));
//////////////////////////////////////////////////////////////////////////
// Visual Logger Events
FLogVisualizer::Get().GetEvents().OnFiltersChanged.AddRaw(this, &SVisualLogger::OnFiltersChanged);
FLogVisualizer::Get().GetEvents().OnLogLineSelectionChanged = FOnLogLineSelectionChanged::CreateRaw(this, &SVisualLogger::OnLogLineSelectionChanged);
FLogVisualizer::Get().GetEvents().OnKeyboardEvent = FOnKeyboardEvent::CreateRaw(this, &SVisualLogger::OnKeyboaedRedirection);
FLogVisualizer::Get().GetTimeSliderController().Get()->GetTimeSliderArgs().OnScrubPositionChanged = FOnScrubPositionChanged::CreateRaw(this, &SVisualLogger::OnScrubPositionChanged);
FVisualLoggerDatabase::Get().GetEvents().OnRowSelectionChanged.AddRaw(this, &SVisualLogger::OnObjectSelectionChanged);
FVisualLoggerDatabase::Get().GetEvents().OnNewItem.AddRaw(this, &SVisualLogger::OnNewItemHandler);
FVisualLoggerDatabase::Get().GetEvents().OnItemSelectionChanged.AddRaw(this, &SVisualLogger::OnItemsSelectionChanged);
GEngine->OnWorldAdded().AddRaw(this, &SVisualLogger::OnNewWorld);
//////////////////////////////////////////////////////////////////////////
// Command Action Lists
const FVisualLoggerCommands& Commands = FVisualLoggerCommands::Get();
FUICommandList& ActionList = *CommandList;
ULogVisualizerSettings* Settings = ULogVisualizerSettings::StaticClass()->GetDefaultObject<ULogVisualizerSettings>();
Settings->LoadPresistentData();
ActionList.MapAction(Commands.StartRecording, FExecuteAction::CreateRaw(this, &SVisualLogger::HandleStartRecordingCommandExecute), FCanExecuteAction::CreateRaw(this, &SVisualLogger::HandleStartRecordingCommandCanExecute), FIsActionChecked(), FIsActionButtonVisible::CreateRaw(this, &SVisualLogger::HandleStartRecordingCommandIsVisible));
ActionList.MapAction(Commands.StopRecording, FExecuteAction::CreateRaw(this, &SVisualLogger::HandleStopRecordingCommandExecute), FCanExecuteAction::CreateRaw(this, &SVisualLogger::HandleStopRecordingCommandCanExecute), FIsActionChecked(), FIsActionButtonVisible::CreateRaw(this, &SVisualLogger::HandleStopRecordingCommandIsVisible));
ActionList.MapAction(Commands.Pause, FExecuteAction::CreateRaw(this, &SVisualLogger::HandlePauseCommandExecute), FCanExecuteAction::CreateRaw(this, &SVisualLogger::HandlePauseCommandCanExecute), FIsActionChecked(), FIsActionButtonVisible::CreateRaw(this, &SVisualLogger::HandlePauseCommandIsVisible));
ActionList.MapAction(Commands.Resume, FExecuteAction::CreateRaw(this, &SVisualLogger::HandleResumeCommandExecute), FCanExecuteAction::CreateRaw(this, &SVisualLogger::HandleResumeCommandCanExecute), FIsActionChecked(), FIsActionButtonVisible::CreateRaw(this, &SVisualLogger::HandleResumeCommandIsVisible));
ActionList.MapAction(Commands.LoadFromVLog, FExecuteAction::CreateRaw(this, &SVisualLogger::HandleLoadCommandExecute), FCanExecuteAction::CreateRaw(this, &SVisualLogger::HandleLoadCommandCanExecute), FIsActionChecked(), FIsActionButtonVisible::CreateRaw(this, &SVisualLogger::HandleLoadCommandCanExecute));
ActionList.MapAction(Commands.SaveToVLog, FExecuteAction::CreateRaw(this, &SVisualLogger::HandleSaveCommandExecute), FCanExecuteAction::CreateRaw(this, &SVisualLogger::HandleSaveCommandCanExecute), FIsActionChecked(), FIsActionButtonVisible::CreateRaw(this, &SVisualLogger::HandleSaveCommandCanExecute));
ActionList.MapAction(Commands.SaveAllToVLog, FExecuteAction::CreateRaw(this, &SVisualLogger::HandleSaveAllCommandExecute), FCanExecuteAction::CreateRaw(this, &SVisualLogger::HandleSaveCommandCanExecute), FIsActionChecked(), FIsActionButtonVisible::CreateRaw(this, &SVisualLogger::HandleSaveCommandCanExecute));
ActionList.MapAction(Commands.FreeCamera,
FExecuteAction::CreateRaw(this, &SVisualLogger::HandleCameraCommandExecute),
FCanExecuteAction::CreateRaw(this, &SVisualLogger::HandleCameraCommandCanExecute),
FIsActionChecked::CreateRaw(this, &SVisualLogger::HandleCameraCommandIsChecked),
FIsActionButtonVisible::CreateRaw(this, &SVisualLogger::HandleCameraCommandCanExecute));
ActionList.MapAction(Commands.ToggleGraphs,
FExecuteAction::CreateLambda([](){bool& bEnableGraphsVisualization = ULogVisualizerSessionSettings::StaticClass()->GetDefaultObject<ULogVisualizerSessionSettings>()->bEnableGraphsVisualization; bEnableGraphsVisualization = !bEnableGraphsVisualization; }),
FCanExecuteAction::CreateLambda([this]()->bool{return FVisualLoggerGraphsDatabase::Get().ContainsHistogramGraphs(); }),
FIsActionChecked::CreateLambda([]()->bool{return ULogVisualizerSessionSettings::StaticClass()->GetDefaultObject<ULogVisualizerSessionSettings>()->bEnableGraphsVisualization; }),
FIsActionButtonVisible::CreateLambda([this]()->bool{return FVisualLoggerGraphsDatabase::Get().ContainsHistogramGraphs(); }));
ActionList.MapAction(Commands.ResetData,
FExecuteAction::CreateRaw(this, &SVisualLogger::ResetData),
FCanExecuteAction::CreateRaw(this, &SVisualLogger::HandleSaveCommandCanExecute),
FIsActionChecked(),
FIsActionButtonVisible::CreateRaw(this, &SVisualLogger::HandleSaveCommandCanExecute));
// Tab Spawners
TabManager = FGlobalTabmanager::Get()->NewTabManager(ConstructUnderMajorTab);
TSharedRef<FWorkspaceItem> AppMenuGroup = TabManager->AddLocalWorkspaceMenuCategory(LOCTEXT("VisualLoggerGroupName", "Visual Logger"));
TabManager->RegisterTabSpawner(ToolbarTabId, FOnSpawnTab::CreateRaw(this, &SVisualLogger::HandleTabManagerSpawnTab, ToolbarTabId))
.SetDisplayName(LOCTEXT("ToolbarTabTitle", "Toolbar"))
.SetGroup(AppMenuGroup)
.SetIcon(FSlateIcon(FLogVisualizerStyle::Get().GetStyleSetName(), "ToolbarTabIcon"));
TabManager->RegisterTabSpawner(FiltersTabId, FOnSpawnTab::CreateRaw(this, &SVisualLogger::HandleTabManagerSpawnTab, FiltersTabId))
.SetDisplayName(LOCTEXT("FiltersTabTitle", "Filters"))
.SetGroup(AppMenuGroup)
.SetIcon(FSlateIcon(FLogVisualizerStyle::Get().GetStyleSetName(), "FiltersTabIcon"));
TabManager->RegisterTabSpawner(MainViewTabId, FOnSpawnTab::CreateRaw(this, &SVisualLogger::HandleTabManagerSpawnTab, MainViewTabId))
.SetDisplayName(LOCTEXT("MainViewTabTitle", "MainView"))
.SetGroup(AppMenuGroup)
.SetIcon(FSlateIcon(FLogVisualizerStyle::Get().GetStyleSetName(), "MainViewTabIcon"));
TabManager->RegisterTabSpawner(LogsListTabId, FOnSpawnTab::CreateRaw(this, &SVisualLogger::HandleTabManagerSpawnTab, LogsListTabId))
.SetDisplayName(LOCTEXT("LogsListTabTitle", "LogsList"))
.SetGroup(AppMenuGroup)
.SetIcon(FSlateIcon(FLogVisualizerStyle::Get().GetStyleSetName(), "LogsListTabIcon"));
TabManager->RegisterTabSpawner(StatusViewTabId, FOnSpawnTab::CreateRaw(this, &SVisualLogger::HandleTabManagerSpawnTab, StatusViewTabId))
.SetDisplayName(LOCTEXT("StatusViewTabTitle", "StatusView"))
.SetGroup(AppMenuGroup)
.SetIcon(FSlateIcon(FLogVisualizerStyle::Get().GetStyleSetName(), "StatusViewTabIcon"));
// Default Layout
const TSharedRef<FTabManager::FLayout> Layout = FTabManager::NewLayout("VisualLoggerLayout_v1.0")
->AddArea
(
FTabManager::NewPrimaryArea()
->SetOrientation(Orient_Vertical)
->Split
(
FTabManager::NewStack()
->AddTab(ToolbarTabId, ETabState::OpenedTab)
->SetHideTabWell(true)
)
->Split
(
FTabManager::NewStack()
->AddTab(FiltersTabId, ETabState::OpenedTab)
->SetHideTabWell(true)
)
->Split
(
FTabManager::NewStack()
->AddTab(MainViewTabId, ETabState::OpenedTab)
->SetHideTabWell(true)
)
->Split
(
FTabManager::NewSplitter()
->SetOrientation(Orient_Horizontal)
->SetSizeCoefficient(0.6f)
->Split
(
FTabManager::NewStack()
->AddTab(StatusViewTabId, ETabState::OpenedTab)
->SetHideTabWell(true)
->SetSizeCoefficient(0.3f)
)
->Split
(
FTabManager::NewStack()
->AddTab(LogsListTabId, ETabState::OpenedTab)
->SetHideTabWell(true)
->SetSizeCoefficient(0.7f)
)
)
);
TabManager->SetOnPersistLayout(FTabManager::FOnPersistLayout::CreateRaw(this, &SVisualLogger::HandleTabManagerPersistLayout));
// Window Menu
FMenuBarBuilder MenuBarBuilder = FMenuBarBuilder(TSharedPtr<FUICommandList>());
MenuBarBuilder.AddPullDownMenu(
LOCTEXT("WindowMenuLabel", "Window"),
FText::GetEmpty(),
FNewMenuDelegate::CreateStatic(&SVisualLogger::FillWindowMenu, TabManager),
"Window"
);
MenuBarBuilder.AddMenuEntry(
LOCTEXT("SettingsMenuLabel", "Settings"),
FText::GetEmpty(),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateLambda(
[this](){
ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings");
if (SettingsModule != nullptr)
{
SettingsModule->ShowViewer("Editor", "General", "VisualLogger");
}
}
)),
"Settings"
);
ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
MenuBarBuilder.MakeWidget()
]
+ SVerticalBox::Slot()
.FillHeight(1.0f)
[
TabManager->RestoreFrom(Layout, ConstructUnderWindow).ToSharedRef()
]
];
VisualLoggerCanvasRenderer = MakeShareable(new FVisualLoggerCanvasRenderer());
DrawOnCanvasDelegateHandle = UDebugDrawService::Register(TEXT("VisLog"), FDebugDrawDelegate::CreateRaw(VisualLoggerCanvasRenderer.Get(), &FVisualLoggerCanvasRenderer::DrawOnCanvas));
Cast<AVisualLoggerRenderingActor>(FVisualLoggerEditorInterface::Get()->GetHelperActor(LastUsedWorld.Get()));
}
void SVisualLogger::OnNewLogEntry(const FVisualLogDevice::FVisualLogEntryItem& Entry)
{
if (bPausedLogger)
{
OnPauseCacheForEntries.Add(Entry);
return;
}
FVisualLoggerDatabase::Get().AddItem(Entry);
if (ULogVisualizerSettings::StaticClass()->GetDefaultObject<ULogVisualizerSettings>()->bStickToRecentData)
{
FLogVisualizer::Get().GetTimeSliderController()->CommitScrubPosition(Entry.Entry.TimeStamp, false);
}
}
void SVisualLogger::HandleMajorTabPersistVisualState()
{
// save any settings here
}
void SVisualLogger::HandleTabManagerPersistLayout(const TSharedRef<FTabManager::FLayout>& LayoutToSave)
{
// save any layout here
}
void SVisualLogger::FillWindowMenu(FMenuBuilder& MenuBuilder, const TSharedPtr<FTabManager> TabManager)
{
if (!TabManager.IsValid())
{
return;
}
TabManager->PopulateLocalTabSpawnerMenu(MenuBuilder);
}
TSharedRef<SDockTab> SVisualLogger::HandleTabManagerSpawnTab(const FSpawnTabArgs& Args, FName TabIdentifier) const
{
TSharedPtr<SWidget> TabWidget = SNullWidget::NullWidget;
bool AutoSizeTab = false;
if (TabIdentifier == ToolbarTabId)
{
TabWidget = SNew(SVisualLoggerToolbar, CommandList);
AutoSizeTab = true;
}
else if (TabIdentifier == FiltersTabId)
{
TabWidget = SAssignNew(VisualLoggerFilters, SVisualLoggerFilters, CommandList);
AutoSizeTab = true;
}
else if (TabIdentifier == MainViewTabId)
{
TabWidget = SAssignNew(MainView, SVisualLoggerView, CommandList).OnFiltersSearchChanged(this, &SVisualLogger::OnFiltersSearchChanged);
AutoSizeTab = false;
}
else if (TabIdentifier == LogsListTabId)
{
TabWidget = SAssignNew(LogsList, SVisualLoggerLogsList, CommandList);
AutoSizeTab = false;
}
else if (TabIdentifier == StatusViewTabId)
{
TabWidget = SAssignNew(StatusView, SVisualLoggerStatusView, CommandList);
AutoSizeTab = false;
}
check(TabWidget.IsValid());
return SNew(SVisualLoggerTab)
.ShouldAutosize(AutoSizeTab)
.TabRole(ETabRole::DocumentTab)
[
TabWidget.ToSharedRef()
];
}
bool SVisualLogger::HandleStartRecordingCommandCanExecute() const
{
return !FVisualLogger::Get().IsRecording();
}
void SVisualLogger::HandleStartRecordingCommandExecute()
{
FVisualLogger::Get().SetIsRecording(true);
}
bool SVisualLogger::HandleStartRecordingCommandIsVisible() const
{
return !FVisualLogger::Get().IsRecording();
}
bool SVisualLogger::HandleStopRecordingCommandCanExecute() const
{
return FVisualLogger::Get().IsRecording();
}
void SVisualLogger::HandleStopRecordingCommandExecute()
{
UWorld* World = FLogVisualizer::Get().GetWorld();
if (FParse::Param(FCommandLine::Get(), TEXT("LogNavOctree")) == true && ULogVisualizerSettings::StaticClass()->GetDefaultObject<ULogVisualizerSettings>()->bLogNavOctreeOnStop)
{
FVisualLogger::NavigationDataDump(World, LogNavigation, ELogVerbosity::Log, INDEX_NONE, FBox());
}
FVisualLogger::Get().SetIsRecording(false);
if (AVisualLoggerCameraController::IsEnabled(World))
{
AVisualLoggerCameraController::DisableCamera(World);
}
if (bPausedLogger)
{
HandleResumeCommandExecute();
}
}
bool SVisualLogger::HandleStopRecordingCommandIsVisible() const
{
return FVisualLogger::Get().IsRecording();
}
bool SVisualLogger::HandlePauseCommandCanExecute() const
{
return !bPausedLogger;
}
void SVisualLogger::HandlePauseCommandExecute()
{
if (ULogVisualizerSettings::StaticClass()->GetDefaultObject<ULogVisualizerSettings>()->bUsePlayersOnlyForPause)
{
const TIndirectArray<FWorldContext>& WorldContexts = GEngine->GetWorldContexts();
for (const FWorldContext& Context : WorldContexts)
{
if (Context.World() != nullptr)
{
Context.World()->bPlayersOnlyPending = true;
}
}
}
bPausedLogger = true;
}
bool SVisualLogger::HandlePauseCommandIsVisible() const
{
return HandlePauseCommandCanExecute();
}
bool SVisualLogger::HandleResumeCommandCanExecute() const
{
return bPausedLogger;
}
void SVisualLogger::HandleResumeCommandExecute()
{
if (ULogVisualizerSettings::StaticClass()->GetDefaultObject<ULogVisualizerSettings>()->bUsePlayersOnlyForPause)
{
const TIndirectArray<FWorldContext>& WorldContexts = GEngine->GetWorldContexts();
for (const FWorldContext& Context : WorldContexts)
{
if (Context.World() != nullptr)
{
Context.World()->bPlayersOnly = false;
Context.World()->bPlayersOnlyPending = false;
}
}
}
bPausedLogger = false;
for (const auto& CurrentEntry : OnPauseCacheForEntries)
{
OnNewLogEntry(CurrentEntry);
}
OnPauseCacheForEntries.Reset();
}
bool SVisualLogger::HandleResumeCommandIsVisible() const
{
return HandleResumeCommandCanExecute();
}
bool SVisualLogger::HandleCameraCommandIsChecked() const
{
UWorld* World = FLogVisualizer::Get().GetWorld();
return World && AVisualLoggerCameraController::IsEnabled(World);
}
bool SVisualLogger::HandleCameraCommandCanExecute() const
{
UWorld* World = FLogVisualizer::Get().GetWorld();
return FVisualLogger::Get().IsRecording() && World && (World->bPlayersOnly || World->bPlayersOnlyPending) && World->IsPlayInEditor() && (GEditor && !GEditor->bIsSimulatingInEditor);
}
void SVisualLogger::HandleCameraCommandExecute()
{
UWorld* World = FLogVisualizer::Get().GetWorld();
if (AVisualLoggerCameraController::IsEnabled(World))
{
AVisualLoggerCameraController::DisableCamera(World);
}
else
{
// switch debug cam on
CameraController = AVisualLoggerCameraController::EnableCamera(World);
}
}
bool SVisualLogger::HandleLoadCommandCanExecute() const
{
return true;
}
void SVisualLogger::HandleLoadCommandExecute()
{
FArchive Ar;
TArray<FVisualLogDevice::FVisualLogEntryItem> RecordedLogs;
TArray<FString> OpenFilenames;
IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
bool bOpened = false;
if (DesktopPlatform)
{
void* ParentWindowWindowHandle = NULL;
IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>(TEXT("MainFrame"));
const TSharedPtr<SWindow>& MainFrameParentWindow = MainFrameModule.GetParentWindow();
if (MainFrameParentWindow.IsValid() && MainFrameParentWindow->GetNativeWindow().IsValid())
{
ParentWindowWindowHandle = MainFrameParentWindow->GetNativeWindow()->GetOSWindowHandle();
}
const FString DefaultBrowsePath = FString::Printf(TEXT("%slogs/"), *FPaths::GameSavedDir());
bOpened = DesktopPlatform->OpenFileDialog(
ParentWindowWindowHandle,
LOCTEXT("OpenProjectBrowseTitle", "Open Project").ToString(),
DefaultBrowsePath,
TEXT(""),
LogVisualizer::LoadFileTypes,
EFileDialogFlags::None,
OpenFilenames
);
}
if (bOpened && OpenFilenames.Num() > 0)
{
OnNewWorld(GetWorldForGivenObject(nullptr));
for (int FilenameIndex = 0; FilenameIndex < OpenFilenames.Num(); ++FilenameIndex)
{
FString CurrentFileName = OpenFilenames[FilenameIndex];
const bool bIsBinaryFile = CurrentFileName.Find(TEXT(".bvlog")) != INDEX_NONE;
if (bIsBinaryFile)
{
FArchive* FileAr = IFileManager::Get().CreateFileReader(*CurrentFileName);
FVisualLoggerHelpers::Serialize(*FileAr, RecordedLogs);
FileAr->Close();
delete FileAr;
FileAr = NULL;
for (FVisualLogDevice::FVisualLogEntryItem& CurrentItem : RecordedLogs)
{
OnNewLogEntry(CurrentItem);
}
}
}
}
}
bool SVisualLogger::HandleSaveCommandCanExecute() const
{
return FVisualLoggerDatabase::Get().NumberOfRows() > 0;
}
void SVisualLogger::HandleSaveAllCommandExecute()
{
HandleSaveCommand(true);
}
void SVisualLogger::HandleSaveCommandExecute()
{
HandleSaveCommand(false);
}
void SVisualLogger::HandleSaveCommand(bool bSaveAllData)
{
TArray<FName> SelectedRows;
if (!bSaveAllData)
{
SelectedRows = FVisualLoggerDatabase::Get().GetSelectedRows();
}
else
{
for (auto Iter(FVisualLoggerDatabase::Get().GetConstRowIterator()); Iter; ++Iter)
{
SelectedRows.Add((*Iter).GetOwnerName());
}
}
if (SelectedRows.Num())
{
// Prompt the user for the filenames
TArray<FString> SaveFilenames;
IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
bool bSaved = false;
if (DesktopPlatform)
{
void* ParentWindowWindowHandle = NULL;
IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>(TEXT("MainFrame"));
const TSharedPtr<SWindow>& MainFrameParentWindow = MainFrameModule.GetParentWindow();
if (MainFrameParentWindow.IsValid() && MainFrameParentWindow->GetNativeWindow().IsValid())
{
ParentWindowWindowHandle = MainFrameParentWindow->GetNativeWindow()->GetOSWindowHandle();
}
const FString DefaultBrowsePath = FString::Printf(TEXT("%slogs/"), *FPaths::GameSavedDir());
bSaved = DesktopPlatform->SaveFileDialog(
ParentWindowWindowHandle,
LOCTEXT("NewProjectBrowseTitle", "Choose a project location").ToString(),
DefaultBrowsePath,
TEXT(""),
LogVisualizer::SaveFileTypes,
EFileDialogFlags::None,
SaveFilenames
);
}
if (bSaved)
{
if (SaveFilenames.Num() > 0)
{
TArray<FVisualLogDevice::FVisualLogEntryItem> FrameCache;
for (auto CurrentName : SelectedRows)
{
FVisualLoggerDBRow& DataRow = FVisualLoggerDatabase::Get().GetRowByName(CurrentName);
FrameCache.Append(DataRow.GetItems());
}
if (FrameCache.Num())
{
FArchive* FileArchive = IFileManager::Get().CreateFileWriter(*SaveFilenames[0]);
FVisualLoggerHelpers::Serialize(*FileArchive, FrameCache);
FileArchive->Close();
delete FileArchive;
FileArchive = NULL;
}
}
}
}
}
void SVisualLogger::ResetData()
{
bGotHistogramData = false;
OnPauseCacheForEntries.Reset();
FLogVisualizer::Get().Reset();
FVisualLoggerDatabase::Get().Reset();
FVisualLoggerFilters::Get().Reset();
if (MainView.IsValid())
{
MainView->ResetData();
}
if (VisualLoggerFilters.IsValid() && ULogVisualizerSettings::StaticClass()->GetDefaultObject<ULogVisualizerSettings>()->bResetDataWithNewSession)
{
VisualLoggerFilters->ResetData();
}
if (LogsList.IsValid())
{
LogsList->ResetData();
}
if (StatusView.IsValid())
{
StatusView->ResetData();
}
if (VisualLoggerCanvasRenderer.IsValid())
{
VisualLoggerCanvasRenderer->ResetData();
}
if (AVisualLoggerRenderingActor* HelperActor = Cast<AVisualLoggerRenderingActor>(FVisualLoggerEditorInterface::Get()->GetHelperActor(LastUsedWorld.Get())))
{
HelperActor->ResetRendering();
}
const TMap<FName, FVisualLogExtensionInterface*>& AllExtensions = FVisualLogger::Get().GetAllExtensions();
for (auto Iterator = AllExtensions.CreateConstIterator(); Iterator; ++Iterator)
{
FVisualLogExtensionInterface* Extension = (*Iterator).Value;
if (Extension != NULL)
{
Extension->ResetData(FVisualLoggerEditorInterface::Get());
}
}
FLogVisualizer::Get().GetEvents().OnLogLineSelectionChanged = FOnLogLineSelectionChanged::CreateRaw(this, &SVisualLogger::OnLogLineSelectionChanged);
FLogVisualizer::Get().GetEvents().OnKeyboardEvent = FOnKeyboardEvent::CreateRaw(this, &SVisualLogger::OnKeyboaedRedirection);
FLogVisualizer::Get().GetTimeSliderController().Get()->GetTimeSliderArgs().OnScrubPositionChanged = FOnScrubPositionChanged::CreateRaw(this, &SVisualLogger::OnScrubPositionChanged);
}
void SVisualLogger::OnNewWorld(UWorld* NewWorld)
{
if (LastUsedWorld.IsValid() && LastUsedWorld != NewWorld)
{
for (TActorIterator<AVisualLoggerRenderingActor> It(LastUsedWorld.Get()); It; ++It)
{
LastUsedWorld->DestroyActor(*It);
}
}
LastUsedWorld = NewWorld;
AVisualLoggerRenderingActor* HelperActor = Cast<AVisualLoggerRenderingActor>(FVisualLoggerEditorInterface::Get()->GetHelperActor(LastUsedWorld.Get()));
if (ensure(HelperActor))
{
if (LastUsedWorld.IsValid() == false || ULogVisualizerSettings::StaticClass()->GetDefaultObject<ULogVisualizerSettings>()->bResetDataWithNewSession)
{
ResetData();
}
// reset data and simulate row/item selection to recreate rendering proxy with correct data
HelperActor->ResetRendering();
const TArray<FName>& SelectedRows = FVisualLoggerDatabase::Get().GetSelectedRows();
HelperActor->ObjectSelectionChanged(SelectedRows);
for (auto& RowName : SelectedRows)
{
FVisualLoggerDBRow& DBRow = FVisualLoggerDatabase::Get().GetRowByName(RowName);
HelperActor->OnItemSelectionChanged(DBRow, DBRow.GetCurrentItemIndex());
}
}
}
void SVisualLogger::OnObjectSelectionChanged(const TArray<FName>& RowNames)
{
const float ScrubTime = FLogVisualizer::Get().GetTimeSliderController().Get()->GetTimeSliderArgs().ScrubPosition.Get();
for (auto RowName : RowNames)
{
FVisualLoggerDBRow& DBRow = FVisualLoggerDatabase::Get().GetRowByName(RowName);
if (DBRow.GetCurrentItemIndex() == INDEX_NONE)
{
DBRow.MoveTo(DBRow.GetClosestItem(ScrubTime, ScrubTime));
}
}
}
void SVisualLogger::OnItemsSelectionChanged(const FVisualLoggerDBRow& ChangedRow, int32 SelectedItemIndex)
{
const TMap<FName, FVisualLogExtensionInterface*>& AllExtensions = FVisualLogger::Get().GetAllExtensions();
for (auto Iterator = AllExtensions.CreateConstIterator(); Iterator; ++Iterator)
{
FVisualLogExtensionInterface* Extension = (*Iterator).Value;
if (Extension != NULL)
{
Extension->OnItemsSelectionChanged(FVisualLoggerEditorInterface::Get());
}
}
}
void SVisualLogger::OnFiltersChanged()
{
const uint32 StartCycles = FPlatformTime::Cycles();
ULogVisualizerSettings* Settings = ULogVisualizerSettings::StaticClass()->GetDefaultObject<ULogVisualizerSettings>();
const FString QuickSearchStrng = FVisualLoggerFilters::Get().GetSearchString();
TArray<TFuture<void> > AllFutures;
for (auto Iterator = FVisualLoggerDatabase::Get().GetRowIterator(); Iterator; ++Iterator)
{
FVisualLoggerDBRow* DBRow = &(*Iterator);
AllFutures.Add(
Async<void>(EAsyncExecution::TaskGraph, [this, DBRow]()
{
const TArray<FVisualLogDevice::FVisualLogEntryItem>& Entries = DBRow->GetItems();
for (int32 Index = 0; Index < Entries.Num(); ++Index)
{
UpdateVisibilityForEntry(*DBRow, Index);
}
}
));
}
bool bAllFuturesReady = false;
do
{
bAllFuturesReady = true;
for (TFuture<void>& CurrentFuture : AllFutures)
{
bAllFuturesReady &= CurrentFuture.IsReady();
if (bAllFuturesReady == false)
{
break;
}
}
if (bAllFuturesReady == false)
{
FPlatformProcess::Sleep(0.01);
}
} while (bAllFuturesReady != true);
for (auto Iterator = FVisualLoggerDatabase::Get().GetRowIterator(); Iterator; ++Iterator)
{
FVisualLoggerDBRow& DBRow = *Iterator;
FVisualLoggerDatabase::Get().SetRowVisibility(DBRow.GetOwnerName(), DBRow.GetNumberOfHiddenItems() != DBRow.GetItems().Num());
}
const uint32 EndCycles = FPlatformTime::Cycles();
const int32 BlockingCycles = int32(FPlatformTime::Cycles() - StartCycles);
{
const TArray<FName>& SelectedRows = FVisualLoggerDatabase::Get().GetSelectedRows();
const float ScrubTime = FLogVisualizer::Get().GetTimeSliderController()->GetTimeSliderArgs().ScrubPosition.Get();
{
for (auto RowName : SelectedRows)
{
auto& DBRow = FVisualLoggerDatabase::Get().GetRowByName(RowName);
const int32 ClosestItem = DBRow.GetClosestItem(ScrubTime, ScrubTime);
const TArray<FVisualLogDevice::FVisualLogEntryItem>& Items = DBRow.GetItems();
if (Items.IsValidIndex(ClosestItem) && Items[ClosestItem].Entry.TimeStamp <= ScrubTime)
{
DBRow.MoveTo(ClosestItem);
}
}
}
}
UE_LOG(LogVisualLogger, Display, TEXT("SVisualLogger::OnFiltersChanged: %5.2fms"), FPlatformTime::ToMilliseconds(BlockingCycles));
}
void SVisualLogger::OnFiltersSearchChanged(const FText& Filter)
{
const uint32 StartCycles = FPlatformTime::Cycles();
FVisualLoggerFilters::Get().SetSearchString(Filter.ToString());
ULogVisualizerSettings* Settings = ULogVisualizerSettings::StaticClass()->GetDefaultObject<ULogVisualizerSettings>();
const FString QuickSearchStrng = FVisualLoggerFilters::Get().GetSearchString();
TArray<TFuture<void> > AllFutures;
for (auto Iterator = FVisualLoggerDatabase::Get().GetRowIterator(); Iterator; ++Iterator)
{
FVisualLoggerDBRow* DBRow = &(*Iterator);
AllFutures.Add(
Async<void>(EAsyncExecution::TaskGraph, [this, DBRow]()
{
const TArray<FVisualLogDevice::FVisualLogEntryItem>& Entries = DBRow->GetItems();
for (int32 Index = 0; Index < Entries.Num(); ++Index)
{
UpdateVisibilityForEntry(*DBRow, Index);
}
}
)
);
}
bool bAllFuturesReady = false;
do
{
bAllFuturesReady = true;
for (TFuture<void>& CurrentFuture : AllFutures)
{
bAllFuturesReady &= CurrentFuture.IsReady();
if (bAllFuturesReady == false)
{
break;
}
}
if (bAllFuturesReady == false)
{
FPlatformProcess::Sleep(0.01);
}
} while (bAllFuturesReady != true);
for (auto Iterator = FVisualLoggerDatabase::Get().GetRowIterator(); Iterator; ++Iterator)
{
FVisualLoggerDBRow& DBRow = *Iterator;
FVisualLoggerDatabase::Get().SetRowVisibility(DBRow.GetOwnerName(), DBRow.GetNumberOfHiddenItems() != DBRow.GetItems().Num());
}
if (LogsList.IsValid())
{
// it depends on rows visibility so it have to be called here, manually after changes to rows visibilities
LogsList->OnFiltersSearchChanged(Filter);
}
if (VisualLoggerCanvasRenderer.IsValid())
{
VisualLoggerCanvasRenderer->DirtyCachedData();
}
const uint32 EndCycles = FPlatformTime::Cycles();
const int32 BlockingCycles = int32(FPlatformTime::Cycles() - StartCycles);
UE_LOG(LogVisualLogger, Display, TEXT("SVisualLogger::OnFiltersSearchChanged: %5.2fms"), FPlatformTime::ToMilliseconds(BlockingCycles));
}
void SVisualLogger::OnNewItemHandler(const FVisualLoggerDBRow& DBRow, int32 ItemIndex)
{
UpdateVisibilityForEntry(DBRow, ItemIndex);
}
void SVisualLogger::UpdateVisibilityForEntry(const FVisualLoggerDBRow& DBRow, int32 ItemIndex)
{
ULogVisualizerSettings* Settings = ULogVisualizerSettings::StaticClass()->GetDefaultObject<ULogVisualizerSettings>();
const FVisualLogDevice::FVisualLogEntryItem& CurrentEntry = DBRow.GetItems()[ItemIndex];
FString SearchString = FVisualLoggerFilters::Get().GetSearchString();
TArray<FVisualLoggerCategoryVerbosityPair> OutCategories;
FVisualLoggerHelpers::GetCategories(CurrentEntry.Entry, OutCategories);
bool bHasValidCategories = false;
for (FVisualLoggerCategoryVerbosityPair& Categoryair : OutCategories)
{
bHasValidCategories = bHasValidCategories || FVisualLoggerFilters::Get().MatchCategoryFilters(Categoryair.CategoryName.ToString(), Categoryair.Verbosity);
}
if (Settings->bSearchInsideLogs && bHasValidCategories && SearchString.Len() > 0)
{
bool bMatchSearchString = false;
for (const FVisualLogLine& CurrentLine : CurrentEntry.Entry.LogLines)
{
if (CurrentLine.Line.Find(SearchString) != INDEX_NONE || CurrentLine.Category.ToString().Find(SearchString) != INDEX_NONE)
{
bMatchSearchString = true;
break;
}
}
if (!bMatchSearchString)
{
for (const FVisualLogEvent& CurrentEvent : CurrentEntry.Entry.Events)
{
if (CurrentEvent.Name.Find(SearchString) != INDEX_NONE)
{
bMatchSearchString = true;
break;
}
}
}
FVisualLoggerDatabase::Get().GetRowByName(DBRow.GetOwnerName()).SetItemVisibility(ItemIndex, bMatchSearchString);
}
else
{
FVisualLoggerDatabase::Get().GetRowByName(DBRow.GetOwnerName()).SetItemVisibility(ItemIndex, bHasValidCategories);
}
}
void SVisualLogger::OnLogLineSelectionChanged(TSharedPtr<struct FLogEntryItem> SelectedItem, int64 UserData, FName TagName)
{
const TMap<FName, FVisualLogExtensionInterface*>& AllExtensions = FVisualLogger::Get().GetAllExtensions();
for (auto Iterator = AllExtensions.CreateConstIterator(); Iterator; ++Iterator)
{
FVisualLogExtensionInterface* Extension = (*Iterator).Value;
if (Extension != NULL)
{
Extension->OnLogLineSelectionChanged(FVisualLoggerEditorInterface::Get(), SelectedItem, UserData);
}
}
}
void SVisualLogger::OnScrubPositionChanged(float NewScrubPosition, bool bScrubbing)
{
const TArray<FName> &SelectedRows = FVisualLoggerDatabase::Get().GetSelectedRows();
const float ScrubTime = FLogVisualizer::Get().GetTimeSliderController()->GetTimeSliderArgs().ScrubPosition.Get();
for (auto RowName : SelectedRows)
{
auto& DBRow = FVisualLoggerDatabase::Get().GetRowByName(RowName);
const int32 ClosestItem = SelectedRows.Num() > 1 ? DBRow.GetClosestItem(NewScrubPosition, ScrubTime) : DBRow.GetClosestItem(NewScrubPosition);
const TArray<FVisualLogDevice::FVisualLogEntryItem>& Items = DBRow.GetItems();
if (Items.IsValidIndex(ClosestItem) && Items[ClosestItem].Entry.TimeStamp <= NewScrubPosition)
{
DBRow.MoveTo(ClosestItem);
}
}
}
FReply SVisualLogger::OnKeyboaedRedirection(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
{
FReply ReturnValue = FReply::Unhandled();
const TArray<FName>& SelectedRows = FVisualLoggerDatabase::Get().GetSelectedRows();
if (SelectedRows.Num() == 0)
{
return ReturnValue;
}
// find time to move by
const FKey Key = InKeyEvent.GetKey();
if (Key == EKeys::Left || Key == EKeys::Right)
{
const float ScrubTime = FLogVisualizer::Get().GetTimeSliderController()->GetTimeSliderArgs().ScrubPosition.Get();
float NewTimeToSet = ScrubTime;
float BestTimeDifference = FLT_MAX;
const int32 MoveDist = InKeyEvent.IsLeftControlDown() ? InKeyEvent.IsLeftShiftDown() ? 20 : 10 : 1;
for (auto RowName : SelectedRows)
{
const FVisualLoggerDBRow& DBRow = FVisualLoggerDatabase::Get().GetRowByName(RowName);
const int32 CurrentItemIndex = DBRow.GetCurrentItemIndex();
if (CurrentItemIndex == INDEX_NONE)
{
continue;
}
if (Key == EKeys::Right)
{
float TimeDifference = DBRow.GetCurrentItem().Entry.TimeStamp - ScrubTime;
if (TimeDifference > 0 && FMath::Abs(TimeDifference) < FMath::Abs(BestTimeDifference))
{
BestTimeDifference = TimeDifference;
NewTimeToSet = DBRow.GetCurrentItem().Entry.TimeStamp;
}
const int32 NextItemIndex = FLogVisualizer::Get().GetNextItem(RowName, MoveDist);
TimeDifference = DBRow.GetItems()[NextItemIndex].Entry.TimeStamp - ScrubTime;
if (TimeDifference > 0 && FMath::Abs(TimeDifference) < FMath::Abs(BestTimeDifference))
{
BestTimeDifference = TimeDifference;
NewTimeToSet = DBRow.GetItems()[NextItemIndex].Entry.TimeStamp;
}
}
else if (Key == EKeys::Left)
{
float TimeDifference = DBRow.GetCurrentItem().Entry.TimeStamp - ScrubTime;
if (TimeDifference < 0 && FMath::Abs(TimeDifference) < FMath::Abs(BestTimeDifference))
{
BestTimeDifference = TimeDifference;
NewTimeToSet = DBRow.GetCurrentItem().Entry.TimeStamp;
}
const int32 PrevItemIndex = FLogVisualizer::Get().GetPreviousItem(RowName, MoveDist);
TimeDifference = DBRow.GetItems()[PrevItemIndex].Entry.TimeStamp - ScrubTime;
if (TimeDifference < 0 && FMath::Abs(TimeDifference) > 0 && FMath::Abs(TimeDifference) < FMath::Abs(BestTimeDifference))
{
BestTimeDifference = TimeDifference;
NewTimeToSet = DBRow.GetItems()[PrevItemIndex].Entry.TimeStamp;
}
}
}
FLogVisualizer::Get().GetTimeSliderController()->CommitScrubPosition(NewTimeToSet, false);
ReturnValue = FReply::Handled();
}
FName OwnerName = SelectedRows[SelectedRows.Num() - 1];
const FVisualLoggerDBRow& DBRow = FVisualLoggerDatabase::Get().GetRowByName(OwnerName);
if (DBRow.GetCurrentItemIndex() != INDEX_NONE)
{
if (Key == EKeys::Home)
{
FLogVisualizer::Get().GotoFirstItem(OwnerName);
ReturnValue = FReply::Handled();
}
else if (Key == EKeys::End)
{
FLogVisualizer::Get().GotoLastItem(OwnerName);
ReturnValue = FReply::Handled();
}
else if (Key == EKeys::Enter)
{
FLogVisualizer::Get().UpdateCameraPosition(OwnerName, DBRow.GetCurrentItemIndex());
ReturnValue = FReply::Handled();
}
}
return ReturnValue;
}
#undef LOCTEXT_NAMESPACE