Files
UnrealEngineUWP/Engine/Source/Developer/LogVisualizer/Private/SLogVisualizer.cpp
Ben Zeigler 3f7149bdb0 Merging using UE4-Fortnite-To-UE4 Up to Changelist #2080066
[CL 2082174 by Ben Zeigler in Main branch]
2014-05-22 14:14:52 -04:00

1562 lines
44 KiB
C++

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#include "LogVisualizerPCH.h"
#include "SLogBar.h"
#include "Debug/LogVisualizerCameraController.h"
#include "MainFrame.h"
#include "DesktopPlatformModule.h"
#include "Json.h"
#include "Editor/UnrealEd/Classes/Editor/EditorEngine.h"
#include "SFilterList.h"
#define LOCTEXT_NAMESPACE "SLogVisualizer"
const FName SLogVisualizer::NAME_LogName = TEXT("LogName");
const FName SLogVisualizer::NAME_StartTime = TEXT("StartTime");
const FName SLogVisualizer::NAME_EndTime = TEXT("EndTime");
const FName SLogVisualizer::NAME_LogTimeSpan = TEXT("LogTimeSpan");
namespace LogVisualizer
{
static const FString LogFileExtensionPure = TEXT("vlog");
static const FString LogFileDescription = LOCTEXT("FileTypeDescription", "Visual Log File").ToString();
static const FString LogFileExtension = FString::Printf(TEXT("*.%s"), *LogFileExtensionPure);
static const FString FileTypes = FString::Printf( TEXT("%s (%s)|%s"), *LogFileDescription, *LogFileExtension, *LogFileExtension );
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SLogVisualizer::Construct(const FArguments& InArgs, FLogVisualizer* InLogVisualizer)
{
LogVisualizer = InLogVisualizer;
SortBy = ELogsSortMode::ByName;
LogEntryIndex = INDEX_NONE;
SelectedLogIndex = INDEX_NONE;
LogsStartTime = FLT_MAX;
LogsEndTime = -FLT_MAX;
ScrollbarOffset = 0.f;
ZoomSliderValue = 0.f;
LastBarsOffset = 0.f;
MinZoom = 1.0f;
MaxZoom = 20.0f;
CurrentViewedTime = 0.f;
bDrawLogEntriesPath = true;
bIgnoreTrivialLogs = true;
UsedCategories.Empty();
ChildSlot
[
SNew(SBorder)
.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
[
SNew(SVerticalBox)
// Toolbar
+SVerticalBox::Slot()
.AutoHeight()
[
SNew( SOverlay )
+ SOverlay::Slot()
[
SNew(SHorizontalBox)
// Record button
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(1)
.AspectRatio()
[
SNew(SButton)
.OnClicked(this, &SLogVisualizer::OnRecordButtonClicked)
.Content()
[
SNew(SImage)
.Image(this, &SLogVisualizer::GetRecordButtonBrush)
]
]
// 'Pause' toggle button
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(1)
.AspectRatio()
[
SNew(SCheckBox)
.Style(FEditorStyle::Get(), "ToggleButtonCheckbox")
.OnCheckStateChanged(this, &SLogVisualizer::OnPauseChanged)
.IsChecked(this, &SLogVisualizer::GetPauseState)
.Content()
[
SNew(SImage)
.Image(FEditorStyle::GetBrush("LogVisualizer.Pause"))
]
]
// 'Camera' toggle button
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(1)
.AspectRatio()
[
SNew(SCheckBox)
.Style(FEditorStyle::Get(), "ToggleButtonCheckbox")
.OnCheckStateChanged(this, &SLogVisualizer::OnToggleCamera)
.IsChecked(this, &SLogVisualizer::GetToggleCameraState)
.Content()
[
SNew(SImage)
.Image(FEditorStyle::GetBrush("LogVisualizer.Camera"))
]
]
+SHorizontalBox::Slot()
.MaxWidth(3)
.Padding(1)
.AspectRatio()
[
SNew(SSeparator)
.Orientation(Orient_Vertical)
]
// 'Save' function
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(1)
.AspectRatio()
[
SNew(SButton)
.OnClicked(this, &SLogVisualizer::OnSave)
.Content()
[
SNew(SImage)
.Image(FEditorStyle::GetBrush("LogVisualizer.Save"))
]
]
// 'Load' function
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(1)
.AspectRatio()
[
SNew(SButton)
.OnClicked(this, &SLogVisualizer::OnLoad)
.Content()
[
SNew(SImage)
.Image(FEditorStyle::GetBrush("LogVisualizer.Load"))
]
]
// 'Remove' function
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(1)
.AspectRatio()
[
SNew(SButton)
.OnClicked(this, &SLogVisualizer::OnRemove)
.Content()
[
SNew(SImage)
.Image(FEditorStyle::GetBrush("LogVisualizer.Remove"))
]
]
+SHorizontalBox::Slot()
.MaxWidth(3)
.Padding(1)
.AspectRatio()
[
SNew(SSeparator)
.Orientation(Orient_Vertical)
]
]
+SOverlay::Slot()
.HAlign(HAlign_Right)
.Padding(4)
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(SCheckBox)
.OnCheckStateChanged(this, &SLogVisualizer::OnDrawLogEntriesPathChanged)
.IsChecked(this, &SLogVisualizer::GetDrawLogEntriesPathState)
.Content()
[
SNew(STextBlock)
.Text(LOCTEXT("VisLogDrawLogsPath", "Draw Log\'s path"))
.ToolTipText(LOCTEXT("VisLogDrawLogsPathTooltip", "Toggle whether path of composed of log entries\' locations"))
]
]
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(SCheckBox)
.OnCheckStateChanged(this, &SLogVisualizer::OnIgnoreTrivialLogs)
.IsChecked(this, &SLogVisualizer::GetIgnoreTrivialLogs)
.Content()
[
SNew(STextBlock)
.Text(LOCTEXT("VisLogIgnoreTrivialLogs", "Ignore trivial logs"))
.ToolTipText(LOCTEXT("VisLogIgnoreTrivialLogsTooltip", "Whether to show trivial logs, i.e. the ones with only one entry."))
]
]
]
]
// Filters
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(STutorialWrapper, TEXT("CategoryFilters"))
[
SAssignNew(FilterListPtr, SFilterList)
.OnFilterChanged(this, &SLogVisualizer::OnLogCategoryFiltersChanged)
/*.OnGetContextMenu(this, &SLogVisualizer::GetFilterContextMenu)*/
/*.FrontendFilters(FrontendFilters)*/
]
]
+SVerticalBox::Slot()
.FillHeight(5)
[
SNew(SSplitter)
.Orientation(Orient_Vertical)
+SSplitter::Slot()
[
SNew(SBorder)
.BorderImage(FEditorStyle::GetBrush("Menu.Background"))
.Padding(1.0)
[
SAssignNew(LogsListWidget, SListView< TSharedPtr<FLogsListItem> >)
.ItemHeight(20)
.ListItemsSource(&LogsList)
.SelectionMode(ESelectionMode::Multi)
.OnGenerateRow(this, &SLogVisualizer::LogsListGenerateRow)
.OnSelectionChanged(this, &SLogVisualizer::LogsListSelectionChanged)
.HeaderRow(
SNew(SHeaderRow)
// ID
+SHeaderRow::Column(NAME_LogName)
.SortMode(this, &SLogVisualizer::GetLogsSortMode)
.OnSort(this, &SLogVisualizer::OnSortByChanged)
.HAlignCell(HAlign_Left)
.FillWidth(0.25f)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.HAlign(HAlign_Left)
.Padding(0.0, 2.0)
[
SNew(STextBlock)
.Text(LOCTEXT("VisLogName", "Log Subject"))
]
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(5,0)
[
SAssignNew(LogNameFilterBox, SEditableTextBox)
.SelectAllTextWhenFocused(true)
.OnTextCommitted(this, &SLogVisualizer::FilterTextCommitted)
.MinDesiredWidth(170.f)
.RevertTextOnEscape(true)
]
]
+SHeaderRow::Column(NAME_LogTimeSpan)
/*.OnSort(this, &SLogVisualizer::OnSortByChanged)*/
.VAlignCell(VAlign_Center)
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
[
SNew(STextBlock)
.Text(LOCTEXT("VisLogTimeSpan", "Overview"))
.ToolTipText(LOCTEXT("VisLogTimeSpanTooltip", "Mouse-over to see timestamp, click to show log entry"))
]
]
)
]
]
+SSplitter::Slot()
[
SNew(SBorder)
.BorderImage(FEditorStyle::GetBrush("Menu.Background"))
.Padding(1.0)
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
.AutoHeight()
.MaxHeight(60)
[
SAssignNew( Timeline, STimeline )
.MinValue(0.0f)
.MaxValue(100.0f)
.FixedLabelSpacing(100.f)
]
+SVerticalBox::Slot()
.AutoHeight()
.Padding( 2 )
.VAlign( VAlign_Fill )
[
SAssignNew( ScrollBar, SScrollBar )
.Orientation( Orient_Horizontal )
.OnUserScrolled( this, &SLogVisualizer::OnZoomScrolled )
]
+SVerticalBox::Slot()
.AutoHeight()
.Padding( 2 )
[
SAssignNew(ZoomSlider, SSlider)
.Value( this, &SLogVisualizer::GetZoomValue )
.OnValueChanged( this, &SLogVisualizer::OnSetZoomValue )
]
+SVerticalBox::Slot()
.Padding(2)
.FillHeight(3)
//.VAlign(VAlign_Fill)
[
SNew(SSplitter)
+SSplitter::Slot()
.Value(1)
[
SNew( SBorder )
.Padding(1)
.BorderImage( FEditorStyle::GetBrush( "ToolBar.Background" ) )
[
SAssignNew(StatusItemsView, STreeView<TSharedPtr<FLogStatusItem> >)
.ItemHeight(40.0f)
.TreeItemsSource(&StatusItems)
.OnGenerateRow(this, &SLogVisualizer::HandleGenerateLogStatus)
.OnGetChildren(this, &SLogVisualizer::OnLogStatusGetChildren)
.SelectionMode(ESelectionMode::None)
]
]
+SSplitter::Slot()
.Value(3)
[
SNew( SBorder )
.Padding(1)
.BorderImage( FEditorStyle::GetBrush( "ToolBar.Background" ) )
[
SAssignNew(LogsLinesWidget, SListView<TSharedPtr<FLogEntryItem> >)
.ItemHeight(20)
.ListItemsSource(&LogEntryLines)
.SelectionMode(ESelectionMode::Multi)
.OnGenerateRow(this, &SLogVisualizer::LogEntryLinesGenerateRow)
//.OnSelectionChanged(this, &SLogVisualizer::LogEntryLineSelectionChanged)*/
]
]
]
]
]
]
+SVerticalBox::Slot()
.AutoHeight()
[
// Status area
SNew(STextBlock)
.Text(this, &SLogVisualizer::GetStatusText)
]
]
];
LogVisualizer->OnLogAdded().AddSP(this, &SLogVisualizer::OnLogAdded);
TArray<TSharedPtr<FActorsVisLog> >& Logs = LogVisualizer->Logs;
TSharedPtr<FActorsVisLog>* SharedLog = Logs.GetTypedData();
for (int32 LogIndex = 0; LogIndex < Logs.Num(); ++LogIndex, ++SharedLog)
{
if (SharedLog->IsValid())
{
AddLog(LogIndex, SharedLog->Get());
}
}
if (LogsList.Num() == 0)
{
Timeline->SetVisibility(EVisibility::Hidden);
ScrollBar->SetVisibility(EVisibility::Hidden);
ZoomSlider->SetVisibility(EVisibility::Hidden);
}
DoFullUpdate();
LastBrowsePath = FPaths::GameLogDir();
DrawingOnCanvasDelegate = FDebugDrawDelegate::CreateSP(this, &SLogVisualizer::DrawOnCanvas);
UDebugDrawService::Register(TEXT("VisLog"), DrawingOnCanvasDelegate);
UGameplayDebuggingComponent::OnDebuggingTargetChangedDelegate.AddSP(this, &SLogVisualizer::SelectionChanged);
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
SLogVisualizer::~SLogVisualizer()
{
UGameplayDebuggingComponent::OnDebuggingTargetChangedDelegate.RemoveAll(this);
LogVisualizer->OnLogAdded().RemoveAll(this);
UDebugDrawService::Unregister(DrawingOnCanvasDelegate);
}
int32 SLogVisualizer::GetCurrentVisibleLogEntryIndex(const TArray<TSharedPtr<FVisLogEntry> >& InVisibleEntries)
{
if (LogVisualizer->Logs.IsValidIndex(SelectedLogIndex))
{
TSharedPtr<FActorsVisLog> Log = LogVisualizer->Logs[SelectedLogIndex];
if (Log.IsValid() && Log->Entries.IsValidIndex(LogEntryIndex))
{
for (int32 Index = 0; Index < InVisibleEntries.Num(); ++Index)
{
if (InVisibleEntries[Index] == Log->Entries[LogEntryIndex])
{
return Index;
}
}
}
}
return INDEX_NONE;
}
void SLogVisualizer::GetVisibleEntries(const TSharedPtr<FActorsVisLog>& Log, TArray<TSharedPtr<FVisLogEntry> >& OutEntries)
{
OutEntries.Empty();
if (FilterListPtr.IsValid())
{
for (int32 i = 0; i < Log->Entries.Num(); ++i)
{
// if any log line is visible - add this entry
for (int32 j = 0; j < Log->Entries[i]->LogLines.Num(); ++j)
{
if (FilterListPtr->IsFilterEnabled(Log->Entries[i]->LogLines[j].Category.ToString(), Log->Entries[i]->LogLines[j].Verbosity))
{
OutEntries.AddUnique(Log->Entries[i]);
break;
}
}
}
return;
}
// if there is no LogFilter widget - show all
OutEntries = Log->Entries;
}
void SLogVisualizer::OnLogCategoryFiltersChanged()
{
RebuildFilteredList();
if (LogVisualizer->Logs.IsValidIndex(SelectedLogIndex))
{
TSharedPtr<FActorsVisLog> Log = LogVisualizer->Logs[SelectedLogIndex];
ShowEntry(Log->Entries[LogEntryIndex].Get());
}
}
UWorld* SLogVisualizer::GetWorld() const
{
// TODO: This needs to be an internalized reference
UEditorEngine *EEngine = Cast<UEditorEngine>(GEngine);
if (GIsEditor && EEngine != NULL)
{
// lets use PlayWorld during PIE/Simulate and regular world from editor otherwise, to draw debug information
return EEngine->PlayWorld != NULL ? EEngine->PlayWorld : EEngine->GetEditorWorldContext().World();
}
else if (!GIsEditor)
{
return LogVisualizer->GetWorld();
}
return NULL;
}
void SLogVisualizer::Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime )
{
SCompoundWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
TimeTillNextUpdate -= InDeltaTime;
UWorld* World = GetWorld();
if (World && !World->bPlayersOnly && TimeTillNextUpdate < 0 && LogVisualizer->IsRecording())
{
DoFullUpdate();
}
}
FReply SLogVisualizer::OnMouseWheel( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
if (MouseEvent.IsLeftControlDown())
{
OnSetZoomValue(FMath::Clamp(ZoomSliderValue + MouseEvent.GetWheelDelta() * 0.05f, 0.f, 1.f));
return FReply::Handled();
}
return SCompoundWidget::OnMouseWheel(MyGeometry, MouseEvent);
}
FReply SLogVisualizer::OnKeyDown( const FGeometry& MyGeometry, const FKeyboardEvent& InKeyboardEvent )
{
const FKey Key = InKeyboardEvent.GetKey();
if (Key == EKeys::Left || Key == EKeys::Right)
{
int32 MoveBy = Key == EKeys::Left ? -1 : 1;
if (InKeyboardEvent.IsLeftControlDown())
{
MoveBy *= 10;
}
IncrementCurrentLogIndex(MoveBy);
return FReply::Handled();
}
return SCompoundWidget::OnKeyDown(MyGeometry, InKeyboardEvent);
}
TSharedRef< SWidget > SLogVisualizer::MakeMainMenu()
{
FMenuBarBuilder MenuBuilder( NULL );
{
// File
MenuBuilder.AddPullDownMenu(
NSLOCTEXT("LogVisualizer", "FileMenu", "File"),
NSLOCTEXT("LogVisualizer", "FileMenu_ToolTip", "Open the file menu"),
FNewMenuDelegate::CreateSP( this, &SLogVisualizer::OpenSavedSession ) );
// Help
MenuBuilder.AddPullDownMenu(
NSLOCTEXT("LogVisualizer", "HelpMenu", "Help"),
NSLOCTEXT("LogVisualizer", "HelpMenu_ToolTip", "Open the help menu"),
FNewMenuDelegate::CreateSP( this, &SLogVisualizer::FillHelpMenu ) );
}
// Create the menu bar
TSharedRef< SWidget > MenuBarWidget = MenuBuilder.MakeWidget();
return MenuBarWidget;
}
void SLogVisualizer::FillHelpMenu(FMenuBuilder& MenuBuilder)
{
}
void SLogVisualizer::OpenSavedSession(FMenuBuilder& MenuBuilder)
{
}
//----------------------------------------------------------------------//
// non-slate
//----------------------------------------------------------------------//
void SLogVisualizer::SelectionChanged(AActor* DebuggedActor, bool bIsBeingDebuggedNow)
{
if (DebuggedActor != NULL && bIsBeingDebuggedNow)
{
SelectActor(DebuggedActor);
}
}
void SLogVisualizer::IncrementCurrentLogIndex(int32 IncrementBy)
{
TSharedPtr<FActorsVisLog> Log = LogVisualizer->Logs[SelectedLogIndex];
check(Log.IsValid());
int32 NewEntryIndex = FMath::Clamp(LogEntryIndex + IncrementBy, 0, Log->Entries.Num() - 1);
if (FilterListPtr.IsValid())
{
while (NewEntryIndex >= 0 && NewEntryIndex < Log->Entries.Num())
{
bool bShouldShow = true;
for (int32 LineIndex = 0; LineIndex < Log->Entries[NewEntryIndex]->LogLines.Num(); ++LineIndex)
{
if (FilterListPtr->IsFilterEnabled(Log->Entries[NewEntryIndex]->LogLines[LineIndex].Category.ToString(), Log->Entries[NewEntryIndex]->LogLines[LineIndex].Verbosity))
{
bShouldShow = false;
break;
}
}
if (!bShouldShow)
{
break;
}
NewEntryIndex += (IncrementBy > 0 ? 1 : -1);
}
}
if (NewEntryIndex != LogEntryIndex && Log->Entries.IsValidIndex(NewEntryIndex))
{
LogEntryIndex = NewEntryIndex;
ShowEntry(Log->Entries[NewEntryIndex].Get());
}
}
void SLogVisualizer::AddLog(int32 Index, const FActorsVisLog* Log)
{
if (Log->Entries.Num() == 0)
{
return;
}
if (LogsList.Num() == 0)
{
Timeline->SetVisibility(EVisibility::Visible);
ScrollBar->SetVisibility(EVisibility::Visible);
ZoomSlider->SetVisibility(EVisibility::Visible);
}
const float StartTimestamp = Log->Entries[0]->TimeStamp;
const float EndTimestamp = Log->Entries[Log->Entries.Num() - 1]->TimeStamp;
for (int32 EntryIndex = 0; EntryIndex < Log->Entries.Num(); ++EntryIndex)
{
for (auto Iter(Log->Entries[EntryIndex]->LogLines.CreateConstIterator()); Iter; Iter++)
{
int32 Index = UsedCategories.Find(Iter->Category.ToString());
if (Index == INDEX_NONE)
{
Index = UsedCategories.Add(Iter->Category.ToString());
FilterListPtr->AddFilter(Iter->Category.ToString(), GetColorForUsedCategory(Index));
}
}
}
LogsList.Add(MakeShareable(new FLogsListItem(Log->Name.ToString()
, StartTimestamp, EndTimestamp, Index)));
}
void SLogVisualizer::DoFullUpdate()
{
TSharedPtr<FLogsListItem>* LogListItem = LogsList.GetTypedData();
for (int32 ItemIndex = 0; ItemIndex < LogsList.Num(); ++ItemIndex, ++LogListItem)
{
if (LogListItem->IsValid() && LogVisualizer->Logs.IsValidIndex((*LogListItem)->LogIndex))
{
TSharedPtr<FActorsVisLog>& Log = LogVisualizer->Logs[(*LogListItem)->LogIndex];
LogsStartTime = FMath::Min(Log->Entries[0]->TimeStamp, LogsStartTime);
LogsEndTime = FMath::Max(Log->Entries[Log->Entries.Num()-1]->TimeStamp, LogsEndTime);
}
}
Timeline->SetMinMaxValues(LogsStartTime, LogsEndTime);
// set zoom max so that single even on SBarLogs has desired size on maximum zoom
const float WidthPx = Timeline->GetDrawingGeometry().Size.X;
if (WidthPx > 0)
{
const float OldMaxZoom = MaxZoom;
const float PxPerTimeUnit = WidthPx * SLogBar::TimeUnit / (LogsEndTime - LogsStartTime);
MaxZoom = SLogBar::MaxUnitSizePx / PxPerTimeUnit;
if (MaxZoom < MinZoom)
{
MaxZoom = MinZoom;
}
ZoomSliderValue = MaxZoom * ZoomSliderValue / OldMaxZoom;
// update
}
RebuildFilteredList();
TimeTillNextUpdate = 1.f/FullUpdateFrequency;
}
void SLogVisualizer::OnLogAdded()
{
// take last log
const int32 NewLogIndex = LogVisualizer->Logs.Num()-1;
TSharedPtr<FLogsListItem> Item;
for (int32 Index = 0; Index < LogsList.Num(); ++Index)
{
Item = LogsList[Index];
TArray<TSharedPtr<FActorsVisLog> >& Logs = LogVisualizer->Logs;
if (Item->Name == Logs[NewLogIndex]->Name.ToString())
{
break;
}
}
if (!Item.IsValid())
{
AddLog(NewLogIndex, LogVisualizer->Logs[NewLogIndex].Get());
}
RequestFullUpdate();
}
TSharedRef<ITableRow> SLogVisualizer::LogsListGenerateRow(TSharedPtr<FLogsListItem> InItem, const TSharedRef<STableViewBase>& OwnerTable)
{
return SNew(SLogsTableRow, OwnerTable)
.Item(InItem)
.OwnerVisualizerWidget(SharedThis(this));
}
void SLogVisualizer::LogsListSelectionChanged(TSharedPtr<FLogsListItem> SelectedItem, ESelectInfo::Type SelectInfo)
{
//@todo find log entry closest to current time selection
//LogEntryIndex = INDEX_NONE;
const int32 NewLogIndex = SelectedItem.IsValid() ? SelectedItem->LogIndex : INDEX_NONE;
if (NewLogIndex != SelectedLogIndex && NewLogIndex != INDEX_NONE)
{
SelectedLogIndex = NewLogIndex;
TSharedPtr<FActorsVisLog> Log = LogVisualizer->Logs[NewLogIndex];
LogEntryIndex = Log->Entries.Num() - 1;
}
//SetCurrentViewedTime(CurrentViewedTime, /*bForce=*/true);
LogsLinesWidget->RequestListRefresh();
}
TSharedRef<ITableRow> SLogVisualizer::LogEntryLinesGenerateRow(TSharedPtr<FLogEntryItem> Item, const TSharedRef<STableViewBase>& OwnerTable)
{
return SNew( STableRow< TSharedPtr<FString> >, OwnerTable )
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(FMargin(5.0f, 0.0f))
[
SNew(STextBlock)
.ColorAndOpacity(FSlateColor(Item->CategoryColor))
.Text(Item->Category)
]
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(FMargin(5.0f, 0.0f))
[
SNew(STextBlock)
.ColorAndOpacity(FSlateColor(FLinearColor::Gray))
.Text(FString(TEXT("(")) + FString(FOutputDevice::VerbosityToString(Item->Verbosity)) + FString(TEXT(")")))
]
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(FMargin(5.0f, 0.0f))
[
SNew(STextBlock)
.Text(Item->Line)
]
];
}
/*
void SLogVisualizer::LogEntryLineSelectionChanged(TSharedPtr<FLogsListItem> SelectedItem, ESelectInfo::Type SelectInfo)
{
}
*/
bool SLogVisualizer::ShouldListLog(const FActorsVisLog& Log)
{
//// Check log name filter
if ((LogNameFilterString.Len() > 0 && !Log.Name.ToString().Contains(LogNameFilterString) )
|| (bIgnoreTrivialLogs == true && Log.Entries.Num() < 2)
)
{
return false;
}
return true;
}
void SLogVisualizer::UpdateFilterInfo()
{
// get filters
LogNameFilterString = LogNameFilterBox->GetText().ToString();
}
void SLogVisualizer::SetCurrentViewedTime(float NewTime, const bool bForce)
{
if (CurrentViewedTime == NewTime && bForce == false)
{
return;
}
CurrentViewedTime = NewTime;
}
void SLogVisualizer::RequestShowLogEntry(TSharedPtr<FLogsListItem> Item, TSharedPtr<FVisLogEntry> LogEntry)
{
ShowLogEntry(Item, LogEntry);
}
void SLogVisualizer::ShowLogEntry(TSharedPtr<FLogsListItem> Item, TSharedPtr<FVisLogEntry> LogEntry)
{
if(LogsListWidget->GetSelectedItems().Find(Item) == INDEX_NONE)
{
LogsListWidget->ClearSelection();
LogsListWidget->SetItemSelection(Item, true);
}
if (LogVisualizer->Logs.IsValidIndex(SelectedLogIndex))
{
TSharedPtr<FActorsVisLog> Log = LogVisualizer->Logs[SelectedLogIndex];
LogEntryIndex = Log->Entries.Find(LogEntry);
}
else
{
LogEntryIndex = INDEX_NONE;
}
ShowEntry(LogEntry.Get());
}
FLinearColor SLogVisualizer::GetColorForUsedCategory(int32 Index) const
{
switch (Index)
{
case 0: return FLinearColor(FColorList::Aquamarine);
case 1: return FLinearColor(FColorList::Cyan);
case 2: return FLinearColor(FColorList::Brown);
case 3: return FLinearColor(FColorList::Green);
case 4: return FLinearColor(FColorList::Orange);
case 5: return FLinearColor(FColorList::Magenta);
case 6: return FLinearColor(FColorList::BrightGold);
case 7: return FLinearColor(FColorList::NeonBlue);
default:
return FLinearColor::White;
}
}
TSharedRef<ITableRow> SLogVisualizer::HandleGenerateLogStatus(TSharedPtr<FLogStatusItem> InItem, const TSharedRef<STableViewBase>& OwnerTable)
{
if (InItem->Children.Num() > 0)
{
return SNew(STableRow<TSharedPtr<FLogStatusItem> >, OwnerTable)
[
SNew(STextBlock).Text(InItem->ItemText)
];
}
FString TooltipText = FString::Printf(TEXT("%s: %s"), *InItem->ItemText, *InItem->ValueText);
return SNew(STableRow<TSharedPtr<FLogStatusItem> >, OwnerTable)
[
SNew(SBorder)
.BorderImage( FEditorStyle::GetBrush("NoBorder") )
.ToolTipText(TooltipText)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew(STextBlock).Text(InItem->ItemText).ColorAndOpacity(FColorList::Aquamarine)
]
+SHorizontalBox::Slot()
.Padding(4.0f, 0, 0, 0)
.AutoWidth()
[
SNew(STextBlock).Text(InItem->ValueText)
]
]
];
}
void SLogVisualizer::OnLogStatusGetChildren(TSharedPtr<FLogStatusItem> InItem, TArray< TSharedPtr<FLogStatusItem> >& OutItems)
{
OutItems = InItem->Children;
}
void SLogVisualizer::UpdateStatusItems(const FVisLogEntry* LogEntry)
{
TArray<FString> ExpandedCategories;
for (int32 i = 0; i < StatusItems.Num(); i++)
{
const bool bIsExpanded = StatusItemsView->IsItemExpanded(StatusItems[i]);
if (bIsExpanded)
{
ExpandedCategories.Add(StatusItems[i]->ItemText);
}
}
StatusItems.Empty();
if (LogEntry)
{
FString TimestampDesc = FString::Printf(TEXT("%.2fs"), LogEntry->TimeStamp);
StatusItems.Add(MakeShareable(new FLogStatusItem(LOCTEXT("VisLogTimestamp","Time").ToString(), TimestampDesc)));
for (int32 iCategory = 0; iCategory < LogEntry->Status.Num(); iCategory++)
{
if (LogEntry->Status[iCategory].Data.Num() <= 0)
{
continue;
}
TSharedRef<FLogStatusItem> StatusItem = MakeShareable(new FLogStatusItem(LogEntry->Status[iCategory].Category));
for (int32 iLine = 0; iLine < LogEntry->Status[iCategory].Data.Num(); iLine++)
{
FString KeyDesc, ValueDesc;
const bool bHasValue = LogEntry->Status[iCategory].GetDesc(iLine, KeyDesc, ValueDesc);
if (bHasValue)
{
StatusItem->Children.Add(MakeShareable(new FLogStatusItem(KeyDesc, ValueDesc)));
}
}
StatusItems.Add(StatusItem);
}
}
StatusItemsView->RequestTreeRefresh();
for (int32 iItem = 0; iItem < StatusItems.Num(); iItem++)
{
for (int32 i = 0; i < ExpandedCategories.Num(); i++)
{
if (StatusItems[iItem]->ItemText == ExpandedCategories[i])
{
StatusItemsView->SetItemExpansion(StatusItems[iItem], true);
break;
}
}
}
}
void SLogVisualizer::ShowEntry(const FVisLogEntry* LogEntry)
{
UpdateStatusItems(LogEntry);
LogEntryLines.Reset();
const FVisLogEntry::FLogLine* LogLine = LogEntry->LogLines.GetTypedData();
for (int LineIndex = 0; LineIndex < LogEntry->LogLines.Num(); ++LineIndex, ++LogLine)
{
bool bShowLine = true;
if (FilterListPtr.IsValid())
{
bShowLine = FilterListPtr->IsFilterEnabled(LogLine->Category.ToString(), LogLine->Verbosity);
}
if (bShowLine)
{
FLogEntryItem EntryItem;
EntryItem.Category = LogLine->Category.ToString();
int32 Index = UsedCategories.Find(EntryItem.Category);
if (Index == INDEX_NONE)
{
Index = UsedCategories.Add(EntryItem.Category);
}
EntryItem.CategoryColor = GetColorForUsedCategory(Index);
EntryItem.Verbosity = LogLine->Verbosity;
EntryItem.Line = LogLine->Line;
LogEntryLines.Add(MakeShareable(new FLogEntryItem(EntryItem)));
}
}
SetCurrentViewedTime(LogEntry->TimeStamp);
LogsLinesWidget->RequestListRefresh();
}
int32 SLogVisualizer::FindIndexInLogsList(const int32 LogIndex) const
{
for (int32 Index = 0; Index < LogsList.Num(); ++Index)
{
if (LogsList[Index]->LogIndex == LogIndex)
{
return Index;
}
}
return INDEX_NONE;
}
void SLogVisualizer::RebuildFilteredList()
{
// store current selection
TArray< TSharedPtr<FLogsListItem> > ItemsToSelect = LogsListWidget->GetSelectedItems();
LogsList.Reset();
for (int32 LogIndex = 0; LogIndex < LogVisualizer->Logs.Num(); ++LogIndex)
{
const FActorsVisLog& Log = *(LogVisualizer->Logs[LogIndex]);
//const int32 IndexInList = FindIndexInLogsList(LogIndex);
if (ShouldListLog(Log))
{
// Passed filter so add to filtered results (defer sorting until end)
AddLog(LogIndex, &Log);
}
}
// When underlying array changes, refresh list
LogsListWidget->RequestListRefresh();
// redo selection
if (ItemsToSelect.Num() > 0)
{
TSharedPtr<FLogsListItem>* Item = ItemsToSelect.GetTypedData();
for (int32 ItemIndex = 0; ItemIndex < ItemsToSelect.Num(); ++ItemIndex, ++Item)
{
const int32 IndexInList = FindIndexInLogsList((*Item)->LogIndex);
if (IndexInList != INDEX_NONE)
{
LogsListWidget->SetItemSelection(LogsList[IndexInList], true);
}
}
}
}
float SLogVisualizer::GetZoomValue() const
{
return ZoomSliderValue;
}
void SLogVisualizer::OnSetZoomValue( float NewValue )
{
const float PrevZoom = GetZoom();
const float PrevVisibleRange = 1.0f / PrevZoom;
ZoomSliderValue = NewValue;
const float Zoom = GetZoom();
const float MaxOffset = GetMaxScrollOffsetFraction();
const float MaxGraphOffset = GetMaxGraphOffset();
const float ViewedTimeSpan = (LogsEndTime - LogsStartTime) / Zoom;
const float ScrollOffsetFraction = FMath::Clamp((CurrentViewedTime - LogsStartTime - ViewedTimeSpan/2) / (LogsEndTime - LogsStartTime), 0.0f, MaxOffset);
const float WidthPx = Timeline->GetDrawingGeometry().Size.X;
const float GraphOffset = MaxOffset > 0 ? (ScrollOffsetFraction / MaxOffset) * MaxGraphOffset : 0.f;
ZoomChangedNotify.Broadcast(Zoom, -GraphOffset);
ScrollBar->SetState( ScrollOffsetFraction, 1.0f / Zoom );
Timeline->SetZoom( Zoom );
Timeline->SetOffset( -GraphOffset );
ScrollbarOffset = -GraphOffset;
}
void SLogVisualizer::OnZoomScrolled(float InScrollOffsetFraction)
{
if( ZoomSliderValue > 0.0f )
{
const float MaxOffset = GetMaxScrollOffsetFraction();
const float MaxGraphOffset = GetMaxGraphOffset();
InScrollOffsetFraction = FMath::Clamp( InScrollOffsetFraction, 0.0f, MaxOffset );
float GraphOffset = -( InScrollOffsetFraction / MaxOffset ) * MaxGraphOffset;
ScrollBar->SetState( InScrollOffsetFraction, 1.0f / GetZoom() );
ZoomChangedNotify.Broadcast(GetZoom(), GraphOffset);
Timeline->SetOffset( GraphOffset );
ScrollbarOffset = GraphOffset;
}
}
void SLogVisualizer::OnDrawLogEntriesPathChanged(ESlateCheckBoxState::Type NewState)
{
bDrawLogEntriesPath = (NewState == ESlateCheckBoxState::Checked);
}
ESlateCheckBoxState::Type SLogVisualizer::GetDrawLogEntriesPathState() const
{
return bDrawLogEntriesPath ? ESlateCheckBoxState::Checked : ESlateCheckBoxState::Unchecked;
}
void SLogVisualizer::OnIgnoreTrivialLogs(ESlateCheckBoxState::Type NewState)
{
bIgnoreTrivialLogs = (NewState == ESlateCheckBoxState::Checked);
DoFullUpdate();
}
ESlateCheckBoxState::Type SLogVisualizer::GetIgnoreTrivialLogs() const
{
return bIgnoreTrivialLogs ? ESlateCheckBoxState::Checked : ESlateCheckBoxState::Unchecked;
}
void SLogVisualizer::OnToggleCamera(ESlateCheckBoxState::Type NewState)
{
UWorld* World = GetWorld();
if (ALogVisualizerCameraController::IsEnabled(World))
{
ALogVisualizerCameraController::DisableCamera(World);
}
else
{
ALogVisualizerCameraController::EnableCamera(World);
}
}
ESlateCheckBoxState::Type SLogVisualizer::GetToggleCameraState() const
{
return ALogVisualizerCameraController::IsEnabled(GetWorld())
? ESlateCheckBoxState::Checked : ESlateCheckBoxState::Unchecked;
}
//----------------------------------------------------------------------//
// Drawing
//----------------------------------------------------------------------//
void SLogVisualizer::DrawOnCanvas(UCanvas* Canvas, APlayerController*)
{
UWorld* World = GetWorld();
if (World != NULL && LogVisualizer->Logs.IsValidIndex(SelectedLogIndex))
{
TSharedPtr<FActorsVisLog> Log = LogVisualizer->Logs[SelectedLogIndex];
const TArray<TSharedPtr<FVisLogEntry> >& Entries = Log->Entries;
if (bDrawLogEntriesPath)
{
const TSharedPtr<FVisLogEntry>* Entry = Entries.GetTypedData();
FVector Location = (*Entry)->Location;
++Entry;
for (int32 Index = 1; Index < Entries.Num(); ++Index, ++Entry)
{
const FVector CurrentLocation = (*Entry)->Location;
DrawDebugLine(World, Location, CurrentLocation, FColor(160, 160, 240));
Location = CurrentLocation;
}
}
if (Entries.IsValidIndex(LogEntryIndex))
{
// draw all additional data stored in current entry
const TSharedPtr<FVisLogEntry>& Entry = Entries[LogEntryIndex];
// mark current location
DrawDebugCone(World, Entry->Location, /*Direction*/FVector(0, 0, 1), /*Length*/200.f
, PI/64, PI/64, /*NumSides*/16, FColor::Red);
UFont* Font = GEngine->GetSmallFont();
FCanvasTextItem TextItem( FVector2D::ZeroVector, FText::GetEmpty(), Font, FLinearColor::White );
const FString TimeStampString = FString::Printf(TEXT("%.2f"), Entry->TimeStamp);
const FVector EntryScreenLoc = Canvas->Project(Entry->Location);
Canvas->SetDrawColor(FColor::Black);
Canvas->DrawText(Font, TimeStampString,EntryScreenLoc.X+1, EntryScreenLoc.Y+1);
Canvas->SetDrawColor(FColor::White);
Canvas->DrawText(Font, TimeStampString, EntryScreenLoc.X, EntryScreenLoc.Y);
const FVisLogEntry::FElementToDraw* ElementToDraw = Entry->ElementsToDraw.GetTypedData();
const int32 ElementsCount = Entry->ElementsToDraw.Num();
for (int32 ElementIndex = 0; ElementIndex < ElementsCount; ++ElementIndex, ++ElementToDraw)
{
const FColor Color = ElementToDraw->GetFColor();
Canvas->SetDrawColor(Color);
switch(ElementToDraw->GetType())
{
case FVisLogEntry::FElementToDraw::SinglePoint:
{
const float Radius = float(ElementToDraw->Radius);
const bool bDrawLabel = ElementToDraw->Description.IsEmpty() == false;
const FVector* Location = ElementToDraw->Points.GetTypedData();
for (int32 Index = 0; Index < ElementToDraw->Points.Num(); ++Index, ++Location)
{
DrawDebugSphere(World, *Location, Radius, 16, Color);
if (bDrawLabel)
{
const FVector ScreenLoc = Canvas->Project(*Location);
Canvas->DrawText(Font, FString::Printf(TEXT("%s_%d"), *ElementToDraw->Description, Index), ScreenLoc.X, ScreenLoc.Y);
}
}
}
break;
case FVisLogEntry::FElementToDraw::Segment:
{
const float Thickness = float(ElementToDraw->Thicknes);
const bool bDrawLabel = ElementToDraw->Description.IsEmpty() == false && ElementToDraw->Points.Num() > 2;
const FVector* Location = ElementToDraw->Points.GetTypedData();
for (int32 Index = 0; Index + 1 < ElementToDraw->Points.Num(); Index += 2, Location += 2)
{
DrawDebugLine(World, *Location, *(Location + 1), Color
, /*bPersistentLines*/false, /*LifeTime*/-1
, /*DepthPriority*/0, Thickness);
if (bDrawLabel)
{
const FString PrintString = FString::Printf(TEXT("%s_%d"), *ElementToDraw->Description, Index);
float TextXL, TextYL;
Canvas->StrLen(Font, PrintString, TextXL, TextYL);
const FVector ScreenLoc = Canvas->Project(*Location + (*(Location+1)-*Location)/2);
Canvas->DrawText(Font, *PrintString, ScreenLoc.X - TextXL/2.0f, ScreenLoc.Y - TextYL/2.0f);
}
}
if (ElementToDraw->Description.IsEmpty() == false)
{
float TextXL, TextYL;
Canvas->StrLen(Font, ElementToDraw->Description, TextXL, TextYL);
const FVector ScreenLoc = Canvas->Project(ElementToDraw->Points[0]
+ (ElementToDraw->Points[1] - ElementToDraw->Points[0])/2);
Canvas->DrawText(Font, *ElementToDraw->Description, ScreenLoc.X - TextXL/2.0f, ScreenLoc.Y - TextYL/2.0f);
}
}
break;
case FVisLogEntry::FElementToDraw::Path:
{
const float Thickness = float(ElementToDraw->Thicknes);
FVector Location = ElementToDraw->Points[0];
for (int32 Index = 1; Index < ElementToDraw->Points.Num(); ++Index)
{
const FVector CurrentLocation = ElementToDraw->Points[Index];
DrawDebugLine(World, Location, CurrentLocation, Color
, /*bPersistentLines*/false, /*LifeTime*/-1
, /*DepthPriority*/0, Thickness);
Location = CurrentLocation;
}
}
break;
case FVisLogEntry::FElementToDraw::Box:
{
const float Thickness = float(ElementToDraw->Thicknes);
const bool bDrawLabel = ElementToDraw->Description.IsEmpty() == false && ElementToDraw->Points.Num() > 2;
const FVector* BoxExtent = ElementToDraw->Points.GetTypedData();
for (int32 Index = 0; Index + 1 < ElementToDraw->Points.Num(); Index += 2, BoxExtent += 2)
{
FBox Box(*BoxExtent, *(BoxExtent + 1));
DrawDebugBox(World, Box.GetCenter(), Box.GetExtent(), Color
, /*bPersistentLines*/false, /*LifeTime*/-1
, /*DepthPriority*/0/*, Thickness*/);
if (bDrawLabel)
{
const FString PrintString = FString::Printf(TEXT("%s_%d"), *ElementToDraw->Description, Index);
float TextXL, TextYL;
Canvas->StrLen(Font, PrintString, TextXL, TextYL);
const FVector ScreenLoc = Canvas->Project(Box.GetCenter());
Canvas->DrawText(Font, *PrintString, ScreenLoc.X - TextXL/2.0f, ScreenLoc.Y - TextYL/2.0f);
}
}
if (ElementToDraw->Description.IsEmpty() == false)
{
float TextXL, TextYL;
Canvas->StrLen(Font, ElementToDraw->Description, TextXL, TextYL);
const FVector ScreenLoc = Canvas->Project(ElementToDraw->Points[0]
+ (ElementToDraw->Points[1] - ElementToDraw->Points[0])/2);
Canvas->DrawText(Font, *ElementToDraw->Description, ScreenLoc.X - TextXL/2.0f, ScreenLoc.Y - TextYL/2.0f);
}
}
break;
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////
const FSlateBrush* SLogVisualizer::GetRecordButtonBrush() const
{
if(LogVisualizer->IsRecording())
{
// If recording, show stop button
return FEditorStyle::GetBrush("LogVisualizer.Stop");
}
else
{
// If stopped, show record button
return FEditorStyle::GetBrush("LogVisualizer.Record");
}
}
FString SLogVisualizer::GetStatusText() const
{
return TEXT("");
}
ESlateCheckBoxState::Type SLogVisualizer::GetPauseState() const
{
UWorld* World = GetWorld();
return (World != NULL && (World->bPlayersOnly || World->bPlayersOnlyPending)) ? ESlateCheckBoxState::Checked : ESlateCheckBoxState::Unchecked;
}
FReply SLogVisualizer::OnRecordButtonClicked()
{
// Toggle recording state
LogVisualizer->SetIsRecording(!LogVisualizer->IsRecording());
return FReply::Handled();
}
FReply SLogVisualizer::OnLoad()
{
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();
}
bOpened = DesktopPlatform->OpenFileDialog(
ParentWindowWindowHandle,
LOCTEXT("OpenProjectBrowseTitle", "Open Project").ToString(),
LastBrowsePath,
TEXT(""),
LogVisualizer::FileTypes,
EFileDialogFlags::None,
OpenFilenames
);
}
if ( bOpened )
{
if ( OpenFilenames.Num() > 0 )
{
LastBrowsePath = OpenFilenames[0];
LoadFiles(OpenFilenames);
}
}
DoFullUpdate();
return FReply::Handled();
}
FReply SLogVisualizer::OnSave()
{
// 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();
}
bSaved = DesktopPlatform->SaveFileDialog(
ParentWindowWindowHandle,
LOCTEXT("NewProjectBrowseTitle", "Choose a project location").ToString(),
LastBrowsePath,
TEXT(""),
LogVisualizer::FileTypes,
EFileDialogFlags::None,
SaveFilenames
);
}
if ( bSaved )
{
if ( SaveFilenames.Num() > 0 )
{
LastBrowsePath = SaveFilenames[0];
SaveSelectedLogs(SaveFilenames[0]);
/*CurrentProjectFilePath = FPaths::GetPath(FPaths::GetPath(SaveFilenames[0]));
CurrentProjectFileName = FPaths::GetBaseFilename(SaveFilenames[0]);*/
}
}
return FReply::Handled();
}
FReply SLogVisualizer::OnRemove()
{
TArray< TSharedPtr<FLogsListItem> > ItemsToRemove = LogsListWidget->GetSelectedItems();
if (ItemsToRemove.Num() > 0)
{
TArray<int32> IndicesToRemove;
IndicesToRemove.AddUninitialized(ItemsToRemove.Num());
for (int32 ListItemIndex = 0; ListItemIndex < ItemsToRemove.Num(); ++ListItemIndex)
{
IndicesToRemove[ListItemIndex] = ItemsToRemove[ListItemIndex]->LogIndex;
}
IndicesToRemove.Sort();
for (int32 LogToRemove = IndicesToRemove.Num() - 1; LogToRemove >= 0; --LogToRemove)
{
LogVisualizer->Logs.RemoveAtSwap(IndicesToRemove[LogToRemove], 1, false);
const int32 IndexInList = FindIndexInLogsList(IndicesToRemove[LogToRemove]);
if (IndexInList != INDEX_NONE)
{
LogsList.RemoveAtSwap(IndexInList);
}
}
LogsListWidget->ClearSelection();
RebuildFilteredList();
}
return FReply::Handled();
}
void SLogVisualizer::OnPauseChanged(ESlateCheckBoxState::Type NewState)
{
UWorld* World = GetWorld();
if (World != NULL)
{
if (NewState != ESlateCheckBoxState::Checked)
{
World->bPlayersOnly = false;
World->bPlayersOnlyPending = false;
ALogVisualizerCameraController::DisableCamera(World);
}
else
{
World->bPlayersOnlyPending = true;
// switch debug cam on
CameraController = ALogVisualizerCameraController::EnableCamera(World);
if (CameraController.IsValid())
{
CameraController->OnActorSelected = ALogVisualizerCameraController::FActorSelectedDelegate::CreateSP(
this, &SLogVisualizer::CameraActorSelected
);
CameraController->OnIterateLogEntries = ALogVisualizerCameraController::FLogEntryIterationDelegate::CreateSP(
this, &SLogVisualizer::IncrementCurrentLogIndex
);
}
}
}
}
void SLogVisualizer::CameraActorSelected(AActor* SelectedActor)
{
// find log corresponding to this Actor
if (SelectedActor == NULL || LogVisualizer == NULL)
{
return;
}
SelectActor(SelectedActor);
}
void SLogVisualizer::SelectActor(AActor* SelectedActor)
{
const AActor* LogOwner = SelectedActor->GetVisualLogRedirection();
const int32 LogIndex = LogVisualizer->GetLogIndexForActor(LogOwner);
if (LogVisualizer->Logs.IsValidIndex(LogIndex))
{
SelectedLogIndex = LogIndex;
// find item pointing to given log index
for (int32 ItemIndex = 0; ItemIndex < LogsList.Num(); ++ItemIndex)
{
if (LogsList[ItemIndex]->LogIndex == LogIndex)
{
TSharedPtr<FActorsVisLog> Log = LogVisualizer->Logs[SelectedLogIndex];
ShowLogEntry(LogsList[ItemIndex], Log->Entries[Log->Entries.Num()-1]);
break;
}
}
}
}
void SLogVisualizer::FilterTextCommitted(const FText& CommentText, ETextCommit::Type CommitInfo)
{
UpdateFilterInfo();
DoFullUpdate();
}
FString SLogVisualizer::GetLogEntryStatusText() const
{
return TEXT("Pause game with Pause button\nand select log entry to start viewing\nlog's content");
}
void SLogVisualizer::OnSortByChanged(const FName& ColumnName, EColumnSortMode::Type NewSortMode)
{
SortBy = ELogsSortMode::ByName;
if (ColumnName == NAME_StartTime)
{
SortBy = ELogsSortMode::ByStartTime;
}
else if (ColumnName == NAME_EndTime)
{
SortBy = ELogsSortMode::ByEndTime;
}
RebuildFilteredList();
}
EColumnSortMode::Type SLogVisualizer::GetLogsSortMode() const
{
return (SortBy == ELogsSortMode::ByName) ? EColumnSortMode::Ascending : EColumnSortMode::None;
}
void SLogVisualizer::LoadFiles(TArray<FString>& OpenFilenames)
{
for (int FilenameIndex = 0; FilenameIndex < OpenFilenames.Num(); ++FilenameIndex)
{
FArchive* FileAr = IFileManager::Get().CreateFileReader(*(OpenFilenames[FilenameIndex]));
if (FileAr != NULL)
{
TSharedPtr<FJsonObject> Object;
TSharedRef<TJsonReader<UCS2CHAR> > Reader = TJsonReader<UCS2CHAR>::Create(FileAr);
if (FJsonSerializer::Deserialize(Reader, Object))
{
TArray< TSharedPtr<FJsonValue> > JsonLogs = Object->GetArrayField(LogVisualizerJson::TAG_LOGS);
for (int32 LogIndex = 0; LogIndex < JsonLogs.Num(); ++LogIndex)
{
TSharedPtr<FActorsVisLog> NewLog = MakeShareable(new FActorsVisLog(JsonLogs[LogIndex]));
LogVisualizer->AddLoadedLog(NewLog);
}
}
FileAr->Close();
}
}
if (OpenFilenames.Num() > 0)
{
RebuildFilteredList();
}
}
void SLogVisualizer::SaveSelectedLogs(FString& Filename)
{
TSharedPtr<FJsonObject> Object = MakeShareable(new FJsonObject);
TArray< TSharedPtr<FJsonValue> > EntriesArray;
TArray< TSharedPtr<FLogsListItem> > ItemsToSave = LogsListWidget->GetSelectedItems();
if (ItemsToSave.Num() == 0)
{
// store all
ItemsToSave = LogsList;
}
EntriesArray.Reserve(ItemsToSave.Num());
TSharedPtr<FLogsListItem>* LogListItem = ItemsToSave.GetTypedData();
for (int32 ItemIndex = 0; ItemIndex < ItemsToSave.Num(); ++ItemIndex, ++LogListItem)
{
if (LogListItem->IsValid() && LogVisualizer->Logs.IsValidIndex((*LogListItem)->LogIndex))
{
TSharedPtr<FActorsVisLog> Log = LogVisualizer->Logs[(*LogListItem)->LogIndex];
EntriesArray.Add(Log->ToJson());
}
}
if (EntriesArray.Num() > 0)
{
Object->SetArrayField(LogVisualizerJson::TAG_LOGS, EntriesArray);
FArchive* FileAr = IFileManager::Get().CreateFileWriter(*Filename);
if (FileAr != NULL)
{
TSharedRef<TJsonWriter<UCS2CHAR> > Writer = TJsonWriter<UCS2CHAR>::Create(FileAr);
FJsonSerializer::Serialize( Object.ToSharedRef(), Writer );
FileAr->Close();
}
}
}
#undef LOCTEXT_NAMESPACE