Files
UnrealEngineUWP/Engine/Source/Developer/LogVisualizer/Private/SVisualLoggerView.cpp
sebastian kowalczyk 49aba11ae5 Optimizations to Visual Logger edytor tool:
Much faster filters due to Async framework, it's paraller now,
Optimizations to items rendered on sequencer,
Optimization to graphs rendered on canvas,
Changed as much as possible to event driven flot, to update data only when needed.

[CL 2670708 by sebastian kowalczyk in Main branch]
2015-08-27 05:21:28 -04:00

453 lines
14 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "LogVisualizer.h"
#include "SVisualLoggerView.h"
#include "TimeSliderController.h"
#include "ITimeSlider.h"
#include "STimeSlider.h"
#include "SSearchBox.h"
#include "SSequencerSectionOverlay.h"
#include "STimelinesContainer.h"
#define LOCTEXT_NAMESPACE "SVisualLoggerFilters"
class SInputCatcherOverlay : public SOverlay
{
public:
void Construct(const FArguments& InArgs, TSharedRef<class FVisualLoggerTimeSliderController> InTimeSliderController)
{
SOverlay::Construct(InArgs);
TimeSliderController = InTimeSliderController;
}
/** Controller for manipulating time */
TSharedPtr<class FVisualLoggerTimeSliderController> TimeSliderController;
private:
/** SWidget Interface */
virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
virtual FReply OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
private:
};
FReply SInputCatcherOverlay::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
if (MouseEvent.GetEffectingButton() != EKeys::LeftMouseButton)
{
return TimeSliderController->OnMouseButtonDown(SharedThis(this), MyGeometry, MouseEvent);
}
return FReply::Unhandled();
}
FReply SInputCatcherOverlay::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
if (MouseEvent.GetEffectingButton() != EKeys::LeftMouseButton)
{
return TimeSliderController->OnMouseButtonUp(SharedThis(this), MyGeometry, MouseEvent);
}
return FReply::Unhandled();
}
FReply SInputCatcherOverlay::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
return TimeSliderController->OnMouseMove(SharedThis(this), MyGeometry, MouseEvent);
}
FReply SInputCatcherOverlay::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
if (MouseEvent.IsLeftShiftDown() || MouseEvent.IsLeftControlDown())
{
return TimeSliderController->OnMouseWheel(SharedThis(this), MyGeometry, MouseEvent);
}
return FReply::Unhandled();
}
void SVisualLoggerView::Construct(const FArguments& InArgs, const TSharedRef<FUICommandList>& InCommandList)
{
AnimationOutlinerFillPercentage = .25f;
TSharedRef<SScrollBar> ZoomScrollBar =
SNew(SScrollBar)
.Orientation(EOrientation::Orient_Horizontal)
.Thickness(FVector2D(2.0f, 2.0f));
ZoomScrollBar->SetState(0.0f, 1.0f);
FLogVisualizer::Get().GetTimeSliderController()->SetExternalScrollbar(ZoomScrollBar);
// Create the top and bottom sliders
const bool bMirrorLabels = true;
TSharedRef<ITimeSlider> TopTimeSlider = SNew(STimeSlider, FLogVisualizer::Get().GetTimeSliderController().ToSharedRef()).MirrorLabels(bMirrorLabels);
TSharedRef<ITimeSlider> BottomTimeSlider = SNew(STimeSlider, FLogVisualizer::Get().GetTimeSliderController().ToSharedRef()).MirrorLabels(bMirrorLabels);
TSharedRef<SScrollBar> ScrollBar =
SNew(SScrollBar)
.Thickness(FVector2D(2.0f, 2.0f));
ULogVisualizerSettings* Settings = ULogVisualizerSettings::StaticClass()->GetDefaultObject<ULogVisualizerSettings>();
ChildSlot
[
SNew(SBorder)
.Padding(2)
.BorderImage(FLogVisualizerStyle::Get().GetBrush("ToolPanel.GroupBorder"))
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SAssignNew(SearchSplitter, SSplitter)
.Orientation(Orient_Horizontal)
.OnSplitterFinishedResizing(this, &SVisualLoggerView::OnSearchSplitterResized)
+ SSplitter::Slot()
.Value(0.25)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.Padding(FMargin(0))
.AutoWidth()
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
SNew(SImage)
.Visibility_Lambda([]()->EVisibility{ return FVisualLoggerFilters::Get().GetSelectedObjects().Num() > 0 ? EVisibility::Visible : EVisibility::Collapsed; })
.Image(FLogVisualizerStyle::Get().GetBrush("Filters.FilterIcon"))
]
+ SHorizontalBox::Slot()
.Padding(FMargin(0))
.HAlign(HAlign_Right)
.AutoWidth()
[
SAssignNew(ClassesComboButton, SComboButton)
.Visibility_Lambda([this]()->EVisibility{ return TimelinesContainer.IsValid() && (TimelinesContainer->GetAllNodes().Num() > 1 || FVisualLoggerFilters::Get().GetSelectedObjects().Num() > 0) ? EVisibility::Visible : EVisibility::Collapsed; })
.ComboButtonStyle(FLogVisualizerStyle::Get(), "Filters.Style")
.ForegroundColor(FLinearColor::White)
.ContentPadding(0)
.OnGetMenuContent(this, &SVisualLoggerView::MakeClassesFilterMenu)
.ToolTipText(LOCTEXT("SetFilterByClasses", "Select classes to show"))
.HasDownArrow(true)
.ContentPadding(FMargin(1, 0))
.ButtonContent()
[
SNew(STextBlock)
.TextStyle(FLogVisualizerStyle::Get(), "Filters.Text")
.Text(LOCTEXT("FilterClasses", "Classes"))
]
]
+ SHorizontalBox::Slot()
.Padding(FMargin(0))
.HAlign(HAlign_Fill)
.FillWidth(1)
[
SNew(SBox)
.Padding(FMargin(0, 0, 4, 0))
[
// Search box for searching through the outliner
SNew(SSearchBox)
.OnTextChanged(this, &SVisualLoggerView::OnSearchChanged)
]
]
]
+ SSplitter::Slot()
.Value(0.75)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
[
#if 0 //top time slider disabled to test idea with filter's search box
SNew(SBorder)
.Padding(FMargin(0.0f, 2.0f, 0.0f, 0.0f))
.BorderImage(FLogVisualizerStyle::Get().GetBrush("ToolPanel.GroupBorder"))
.BorderBackgroundColor(FLinearColor(.50f, .50f, .50f, 1.0f))
[
TopTimeSlider
]
#else
SNew(SBox)
.Padding(FMargin(0, 0, 4, 0))
[
SAssignNew(SearchBox, SSearchBox)
.OnTextChanged(InArgs._OnFiltersSearchChanged)
.HintText_Lambda([Settings]()->FText{return Settings->bSearchInsideLogs ? LOCTEXT("FiltersSearchHint", "Log Data Search") : LOCTEXT("FiltersSearchHint", "Log Category Search"); })
]
#endif
]
]
]
+ SVerticalBox::Slot()
.FillHeight(1.0)
[
SNew(SInputCatcherOverlay, FLogVisualizer::Get().GetTimeSliderController().ToSharedRef())
+ SOverlay::Slot()
[
MakeSectionOverlay(FLogVisualizer::Get().GetTimeSliderController().ToSharedRef(), InArgs._ViewRange, InArgs._ScrubPosition, false)
]
+ SOverlay::Slot()
[
SAssignNew(ScrollBox, SScrollBox)
.ExternalScrollbar(ScrollBar)
+ SScrollBox::Slot()
[
SAssignNew(TimelinesContainer, STimelinesContainer, SharedThis(this), FLogVisualizer::Get().GetTimeSliderController().ToSharedRef())
]
]
+ SOverlay::Slot()
[
MakeSectionOverlay(FLogVisualizer::Get().GetTimeSliderController().ToSharedRef(), InArgs._ViewRange, InArgs._ScrubPosition, true)
]
+ SOverlay::Slot()
.VAlign(VAlign_Bottom)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(TAttribute<float>(this, &SVisualLoggerView::GetAnimationOutlinerFillPercentage))
[
// Take up space but display nothing. This is required so that all areas dependent on time align correctly
SNullWidget::NullWidget
]
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
ZoomScrollBar
]
]
]
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(TAttribute<float>(this, &SVisualLoggerView::GetAnimationOutlinerFillPercentage))
[
SNew(SSpacer)
]
+ SHorizontalBox::Slot()
.Padding(FMargin(0.0f, 0.0f, 0.0f, 0.0f))
.FillWidth(1.0f)
[
SNew(SBorder)
.Padding(FMargin(0.0f, 0.0f, 0.0f, 2.0f))
.BorderImage(FLogVisualizerStyle::Get().GetBrush("ToolPanel.GroupBorder"))
.BorderBackgroundColor(FLinearColor(.50f, .50f, .50f, 1.0f))
[
BottomTimeSlider
]
]
]
]
+ SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.AutoWidth()
[
ScrollBar
]
]
];
SearchBox->SetText(FText::FromString(FVisualLoggerFilters::Get().GetSearchString()));
FLogVisualizer::Get().GetEvents().GetAnimationOutlinerFillPercentageFunc.BindLambda(
[this]()->float{
SSplitter::FSlot const& LeftSplitterSlot = SearchSplitter->SlotAt(0);
SSplitter::FSlot const& RightSplitterSlot = SearchSplitter->SlotAt(1);
return LeftSplitterSlot.SizeValue.Get() / RightSplitterSlot.SizeValue.Get();
});
OnSearchSplitterResized();
}
SVisualLoggerView::~SVisualLoggerView()
{
FLogVisualizer::Get().GetEvents().GetAnimationOutlinerFillPercentageFunc.Unbind();
}
void SVisualLoggerView::SetAnimationOutlinerFillPercentage(float FillPercentage)
{
AnimationOutlinerFillPercentage = FillPercentage;
}
void SVisualLoggerView::SetSearchString(FText SearchString)
{
if (SearchBox.IsValid())
{
SearchBox->SetText(SearchString);
}
}
void SVisualLoggerView::OnSearchSplitterResized()
{
SSplitter::FSlot const& LeftSplitterSlot = SearchSplitter->SlotAt(0);
SSplitter::FSlot const& RightSplitterSlot = SearchSplitter->SlotAt(1);
const float AnimationOutlinerFillPercentage = LeftSplitterSlot.SizeValue.Get() / RightSplitterSlot.SizeValue.Get();
SetAnimationOutlinerFillPercentage(AnimationOutlinerFillPercentage);
FLogVisualizer::Get().SetAnimationOutlinerFillPercentage(AnimationOutlinerFillPercentage);
}
void SVisualLoggerView::OnSearchChanged(const FText& Filter)
{
TimelinesContainer->OnSearchChanged(Filter);
}
TSharedRef<SWidget> SVisualLoggerView::MakeSectionOverlay(TSharedRef<FVisualLoggerTimeSliderController> TimeSliderController, const TAttribute< TRange<float> >& ViewRange, const TAttribute<float>& ScrubPosition, bool bTopOverlay)
{
return
SNew(SHorizontalBox)
.Visibility(EVisibility::HitTestInvisible)
+ SHorizontalBox::Slot()
.FillWidth(TAttribute<float>(this, &SVisualLoggerView::GetAnimationOutlinerFillPercentage))
[
// Take up space but display nothing. This is required so that all areas dependent on time align correctly
SNullWidget::NullWidget
]
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
[
SNew(SVisualLoggerSectionOverlay, TimeSliderController)
.DisplayScrubPosition(bTopOverlay)
.DisplayTickLines(!bTopOverlay)
];
}
void SVisualLoggerView::ResetData()
{
TimelinesContainer->ResetData();
}
void SVisualLoggerView::OnFiltersChanged()
{
TimelinesContainer->OnFiltersChanged();
}
void SVisualLoggerView::OnFiltersSearchChanged(const FText& Filter)
{
TimelinesContainer->OnFiltersSearchChanged(Filter);
}
FCursorReply SVisualLoggerView::OnCursorQuery(const FGeometry& MyGeometry, const FPointerEvent& CursorEvent) const
{
if (FLogVisualizer::Get().GetTimeSliderController()->IsPanning())
{
return FCursorReply::Cursor(EMouseCursor::GrabHand);
}
return FCursorReply::Cursor(EMouseCursor::Default);
}
TSharedRef<SWidget> SVisualLoggerView::MakeClassesFilterMenu()
{
const TArray< TSharedPtr<class STimeline> >& AllTimelines = TimelinesContainer->GetAllNodes();
FMenuBuilder MenuBuilder(true, NULL);
TArray<FString> UniqueClasses;
MenuBuilder.BeginSection(TEXT("Graphs"));
for (TSharedPtr<class STimeline> CurrentTimeline : AllTimelines)
{
FString OwnerClassName = CurrentTimeline->GetOwnerClassName().ToString();
if (UniqueClasses.Find(OwnerClassName) == INDEX_NONE)
{
FText LabelText = FText::FromString(OwnerClassName);
MenuBuilder.AddMenuEntry(
LabelText,
FText::Format(LOCTEXT("FilterByClassPrefix", "Toggle {0} class"), LabelText),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateLambda([this, OwnerClassName]()
{
if (FVisualLoggerFilters::Get().MatchObjectName(OwnerClassName) && FVisualLoggerFilters::Get().GetSelectedObjects().Num() != 0)
{
FVisualLoggerFilters::Get().RemoveObjectFromSelection(OwnerClassName);
}
else
{
FVisualLoggerFilters::Get().SelectObject(OwnerClassName);
}
OnChangedClassesFilter();
}),
FCanExecuteAction(),
FIsActionChecked::CreateLambda([OwnerClassName]()->bool
{
return FVisualLoggerFilters::Get().GetSelectedObjects().Find(OwnerClassName) != INDEX_NONE;
}),
FIsActionButtonVisible()),
NAME_None,
EUserInterfaceActionType::ToggleButton
);
UniqueClasses.AddUnique(OwnerClassName);
}
}
//show any classes from persistent data
for (const FString& SelectedObj : FVisualLoggerFilters::Get().GetSelectedObjects())
{
if (UniqueClasses.Find(SelectedObj) == INDEX_NONE)
{
FText LabelText = FText::FromString(SelectedObj);
MenuBuilder.AddMenuEntry(
LabelText,
FText::Format(LOCTEXT("FilterByClassPrefix", "Toggle {0} class"), LabelText),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateLambda([this, SelectedObj]()
{
if (FVisualLoggerFilters::Get().MatchObjectName(SelectedObj) && FVisualLoggerFilters::Get().GetSelectedObjects().Num() != 0)
{
FVisualLoggerFilters::Get().RemoveObjectFromSelection(SelectedObj);
}
else
{
FVisualLoggerFilters::Get().SelectObject(SelectedObj);
}
OnChangedClassesFilter();
}),
FCanExecuteAction(),
FIsActionChecked::CreateLambda([SelectedObj]()->bool
{
return FVisualLoggerFilters::Get().GetSelectedObjects().Find(SelectedObj) != INDEX_NONE;
}),
FIsActionButtonVisible()),
NAME_None,
EUserInterfaceActionType::ToggleButton
);
UniqueClasses.AddUnique(SelectedObj);
}
}
MenuBuilder.EndSection();
FDisplayMetrics DisplayMetrics;
FSlateApplication::Get().GetDisplayMetrics(DisplayMetrics);
const FVector2D DisplaySize(
DisplayMetrics.PrimaryDisplayWorkAreaRect.Right - DisplayMetrics.PrimaryDisplayWorkAreaRect.Left,
DisplayMetrics.PrimaryDisplayWorkAreaRect.Bottom - DisplayMetrics.PrimaryDisplayWorkAreaRect.Top);
return
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.MaxHeight(DisplaySize.Y * 0.5)
[
MenuBuilder.MakeWidget()
];
}
void SVisualLoggerView::OnChangedClassesFilter()
{
ULogVisualizerSettings::StaticClass()->GetDefaultObject<ULogVisualizerSettings>()->SaveConfig();
for (auto CurrentItem : TimelinesContainer->GetAllNodes())
{
CurrentItem->UpdateVisibility();
}
}
#undef LOCTEXT_NAMESPACE