Files
UnrealEngineUWP/Engine/Source/Developer/SlateReflector/Private/VisualTreeCapture.cpp
nick darnell fe1945b758 Slate - Introducing a metadata you can attach to widgets to make it be ignored by the picker in Widget Reflector for paint hit testing. The debug paint canvas can ruin the experience when you're trying to pick against painted widgets, so now it gets ignored.
Matt.Kuhlenschmidt

#ROBOMERGE-OWNER: ryan.vance
#ROBOMERGE-AUTHOR: nick.darnell
#ROBOMERGE-SOURCE: CL 5830878 via CL 5830881 via CL 5835228 via CL 5836028
#ROBOMERGE-BOT: DEVVR (Main -> Dev-VR)

[CL 5876935 by nick darnell in Dev-VR branch]
2019-04-12 21:14:18 -04:00

206 lines
5.9 KiB
C++

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#include "VisualTreeCapture.h"
#include "Debugging/SlateDebugging.h"
#include "Rendering/SlateRenderTransform.h"
#include "Rendering/DrawElements.h"
#include "Widgets/SWidget.h"
#include "Framework/Application/SlateApplication.h"
#include "Types/InvisibleToWidgetReflectorMetaData.h"
static float VectorSign(const FVector2D& Vec, const FVector2D& A, const FVector2D& B)
{
return FMath::Sign((B.X - A.X) * (Vec.Y - A.Y) - (B.Y - A.Y) * (Vec.X - A.X));
}
// Returns true when the point is inside the triangle
// Should not return true when the point is on one of the edges
static bool IsPointInTriangle(const FVector2D& TestPoint, const FVector2D& A, const FVector2D& B, const FVector2D& C)
{
float BA = VectorSign(B, A, TestPoint);
float CB = VectorSign(C, B, TestPoint);
float AC = VectorSign(A, C, TestPoint);
// point is in the same direction of all 3 tri edge lines
// must be inside, regardless of tri winding
return BA == CB && CB == AC;
}
FVisualEntry::FVisualEntry(int32 InElementIndex)
: ElementIndex(InElementIndex)
{
}
void FVisualEntry::Resolve(const FSlateWindowElementList& ElementList)
{
const FSlateDrawElement& Element = ElementList.GetDrawElements()[ElementIndex];
const FSlateRenderTransform& Transform = Element.GetRenderTransform();
const FVector2D& LocalSize = Element.GetLocalSize();
TopLeft = Transform.TransformPoint(FVector2D(0, 0));
TopRight = Transform.TransformPoint(FVector2D(LocalSize.X, 0));
BottomLeft = Transform.TransformPoint(FVector2D(0, LocalSize.Y));
BottomRight = Transform.TransformPoint(LocalSize);
LayerId = Element.GetLayer();
ClippingIndex = Element.GetClippingIndex();
}
bool FVisualEntry::IsPointInside(const FVector2D& Point) const
{
if (IsPointInTriangle(Point, TopLeft, TopRight, BottomLeft) || IsPointInTriangle(Point, BottomLeft, TopRight, BottomRight))
{
return true;
}
return false;
}
TSharedPtr<const SWidget> FVisualTreeSnapshot::Pick(FVector2D Point)
{
for (int Index = Entries.Num() - 1; Index >= 0; Index--)
{
const FVisualEntry& Entry = Entries[Index];
if (Entry.ClippingIndex != -1 && !ClippingStates[Entry.ClippingIndex].IsPointInside(Point))
{
continue;
}
if (!Entry.IsPointInside(Point))
{
continue;
}
TSharedPtr<const SWidget> Widget = Entry.Widget.Pin();
//if (Widget->GetVisibility() == EVisibility::HitTestInvisible)
//{
// continue;
//}
// Build List.
return Widget;
}
return TSharedPtr<const SWidget>();
}
FVisualTreeCapture::FVisualTreeCapture()
{
}
FVisualTreeCapture::~FVisualTreeCapture()
{
Disable();
}
void FVisualTreeCapture::Enable()
{
#if WITH_SLATE_DEBUGGING
FSlateApplication::Get().OnWindowBeingDestroyed().AddRaw(this, &FVisualTreeCapture::OnWindowBeingDestroyed);
FSlateDebugging::BeginWindow.AddRaw(this, &FVisualTreeCapture::BeginWindow);
FSlateDebugging::EndWindow.AddRaw(this, &FVisualTreeCapture::EndWindow);
FSlateDebugging::BeginWidgetPaint.AddRaw(this, &FVisualTreeCapture::BeginWidgetPaint);
FSlateDebugging::EndWidgetPaint.AddRaw(this, &FVisualTreeCapture::EndWidgetPaint);
FSlateDebugging::ElementAdded.AddRaw(this, &FVisualTreeCapture::ElementAdded);
#endif
}
void FVisualTreeCapture::Disable()
{
#if WITH_SLATE_DEBUGGING
if (FSlateApplication::IsInitialized())
{
FSlateApplication::Get().OnWindowBeingDestroyed().RemoveAll(this);
}
FSlateDebugging::BeginWindow.RemoveAll(this);
FSlateDebugging::EndWindow.RemoveAll(this);
FSlateDebugging::BeginWidgetPaint.RemoveAll(this);
FSlateDebugging::EndWidgetPaint.RemoveAll(this);
FSlateDebugging::ElementAdded.RemoveAll(this);
#endif
}
void FVisualTreeCapture::Reset()
{
VisualTrees.Reset();
}
TSharedPtr<FVisualTreeSnapshot> FVisualTreeCapture::GetVisualTreeForWindow(SWindow* InWindow)
{
return VisualTrees.FindRef(InWindow);
}
void FVisualTreeCapture::BeginWindow(const FSlateWindowElementList& ElementList)
{
TSharedPtr<FVisualTreeSnapshot> Tree = VisualTrees.FindRef(ElementList.GetPaintWindow());
if (!Tree.IsValid())
{
Tree = MakeShared<FVisualTreeSnapshot>();
VisualTrees.Add(ElementList.GetPaintWindow(), Tree);
}
Tree->Entries.Reset();
Tree->ClippingStates.Reset();
}
void FVisualTreeCapture::EndWindow(const FSlateWindowElementList& ElementList)
{
TSharedPtr<FVisualTreeSnapshot> Tree = VisualTrees.FindRef(ElementList.GetPaintWindow());
if (Tree.IsValid())
{
for (FVisualEntry& Entry : Tree->Entries)
{
Entry.Resolve(ElementList);
}
Tree->ClippingStates = ElementList.GetClippingManager().GetClippingStates();
Tree->Entries.Sort([](const FVisualEntry& A, const FVisualEntry& B) {
return A.LayerId < B.LayerId;
});
}
}
void FVisualTreeCapture::BeginWidgetPaint(const SWidget* Widget, const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, const FSlateWindowElementList& ElementList, int32 LayerId)
{
TSharedPtr<FVisualTreeSnapshot> Tree = VisualTrees.FindRef(ElementList.GetPaintWindow());
if (Tree.IsValid())
{
Tree->WidgetStack.Push(Widget->AsShared());
}
}
void FVisualTreeCapture::EndWidgetPaint(const SWidget* Widget, const FSlateWindowElementList& ElementList, int32 LayerId)
{
TSharedPtr<FVisualTreeSnapshot> Tree = VisualTrees.FindRef(ElementList.GetPaintWindow());
if (Tree.IsValid())
{
Tree->WidgetStack.Pop();
}
}
void FVisualTreeCapture::ElementAdded(const FSlateWindowElementList& ElementList, int32 InElementIndex)
{
TSharedPtr<FVisualTreeSnapshot> Tree = VisualTrees.FindRef(ElementList.GetPaintWindow());
if (Tree.IsValid())
{
if (Tree->WidgetStack.Num() > 0)
{
// Ignore any element added from a widget that's invisible to the widget reflector.
if (Tree->WidgetStack.Top().Pin()->GetMetaData<FInvisibleToWidgetReflectorMetaData>())
{
return;
}
FVisualEntry Entry(InElementIndex);
Entry.Widget = Tree->WidgetStack.Top();
Tree->Entries.Add(Entry);
}
}
}
void FVisualTreeCapture::OnWindowBeingDestroyed(const SWindow& WindowBeingDestoyed)
{
VisualTrees.Remove(&WindowBeingDestoyed);
}