Files
UnrealEngine/Engine/Source/Developer/SlateReflector/Private/Widgets/NavigationSimulationOverlay.cpp
2025-05-18 13:04:45 +08:00

314 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Widgets/NavigationSimulationOverlay.h"
#include "Framework/Application/SlateApplication.h"
#include "Models/WidgetReflectorNode.h"
#include "Rendering/DrawElements.h"
#include "Styling/CoreStyle.h"
#include "Styling/WidgetReflectorStyle.h"
#define LOCTEXT_NAMESPACE "SlateNavigationSimulationOverlay"
//***********************************************************
//SSlateNavigationSimulationOverlay
namespace NavigationSimulationOverlay
{
static const FName FocusRectangleBrush = TEXT("FocusRectangle");
static const FName DebugBorderBrush = TEXT("Debug.Border");
static const FName SymbolsDownArrow = TEXT("Symbols.DownArrow");
static const FName SymbolsUpArrow = TEXT("Symbols.UpArrow");
static const FName SymbolsRightArrow = TEXT("Symbols.RightArrow");
static const FName SymbolsLeftArrow = TEXT("Symbols.LeftArrow");
struct FOverlayBrushes
{
FOverlayBrushes()
{
const ISlateStyle& CoreStyle = FCoreStyle::Get();
FocusBrush = CoreStyle.GetBrush(FocusRectangleBrush);
DebugBrush = CoreStyle.GetBrush(DebugBorderBrush);
const ISlateStyle& ReflectorStyle = FWidgetReflectorStyle::Get();
LeftArrowBrush = ReflectorStyle.GetBrush(NavigationSimulationOverlay::SymbolsLeftArrow);
RightArrowBrush = ReflectorStyle.GetBrush(NavigationSimulationOverlay::SymbolsRightArrow);
UpArrowBrush = ReflectorStyle.GetBrush(NavigationSimulationOverlay::SymbolsUpArrow);
DownArrowBrush = ReflectorStyle.GetBrush(NavigationSimulationOverlay::SymbolsDownArrow);
ArrowSize = UpArrowBrush->ImageSize;
}
const FSlateBrush* FocusBrush;
const FSlateBrush* DebugBrush;
const FSlateBrush* LeftArrowBrush;
const FSlateBrush* RightArrowBrush;
const FSlateBrush* UpArrowBrush;
const FSlateBrush* DownArrowBrush;
FVector2D ArrowSize;
};
struct FPaintNode
{
TArray<FVector2D> SourcePoints;
FVector2D SourceCenterPoint;
FLinearColor Color;
FVector2D Direction;
const FSlateBrush* ArrowBrush = nullptr;
static FPaintNode MakePaintNode(const FNavigationSimulationWidgetNodeItem& NodeItem, const FPaintGeometry& PaintGeometry, const FOverlayBrushes& Brushes)
{
FPaintNode PaintNode;
const FVector2D TopLeft = FVector2D::ZeroVector;
const FVector2D BottomRight = PaintGeometry.GetLocalSize();
const FVector2D TopRight = FVector2D(BottomRight.X, TopLeft.Y);
const FVector2D BottomLeft = FVector2D(TopLeft.X, BottomRight.Y);
switch (NodeItem.NavigationType)
{
case EUINavigation::Up:
{
PaintNode.Direction = {0.f, 1.f};
PaintNode.SourcePoints.Add(TopLeft);
PaintNode.SourcePoints.Add(TopRight);
PaintNode.SourceCenterPoint = FVector2D(TopRight.X / 2.f, TopRight.Y);
PaintNode.Color = FLinearColor::Red;
PaintNode.ArrowBrush = Brushes.UpArrowBrush;
break;
}
case EUINavigation::Down:
{
PaintNode.Direction = {0.f, -1.f};
PaintNode.SourcePoints.Add(BottomLeft);
PaintNode.SourcePoints.Add(BottomRight);
PaintNode.SourceCenterPoint = FVector2D(BottomRight.X / 2.f, BottomRight.Y);
PaintNode.Color = FLinearColor::Blue;
PaintNode.ArrowBrush = Brushes.DownArrowBrush;
break;
}
case EUINavigation::Left:
{
PaintNode.Direction = {1.f, 0.f};
PaintNode.SourcePoints.Add(TopLeft);
PaintNode.SourcePoints.Add(BottomLeft);
PaintNode.SourceCenterPoint = FVector2D(BottomLeft.X, BottomRight.Y / 2.f);
PaintNode.Color = FColor::Cyan;
PaintNode.ArrowBrush = Brushes.LeftArrowBrush;
break;
}
case EUINavigation::Right:
{
PaintNode.Direction = {-1.f, 0.f};
PaintNode.SourcePoints.Add(TopRight);
PaintNode.SourcePoints.Add(BottomRight);
PaintNode.SourceCenterPoint = FVector2D(BottomRight.X, BottomRight.Y / 2.f);
PaintNode.Color = FLinearColor::Yellow;
PaintNode.ArrowBrush = Brushes.RightArrowBrush;
}
break;
}
return PaintNode;
}
};
struct FWidgetGeometryMap
{
TMap<FNavigationSimulationWidgetInfo::TPointerAsInt, FPaintGeometry> Map;
const FPaintGeometry* GetGeometry(const TSharedRef<const SWidget>& Widget)
{
const FNavigationSimulationWidgetInfo::TPointerAsInt WidgetPtr = FWidgetReflectorNodeUtils::GetWidgetAddress(Widget);
if (const FPaintGeometry* FoundGeometry = Map.Find(WidgetPtr))
{
return FoundGeometry;
}
TSharedPtr<SWindow> WidgetWindow = FSlateApplication::Get().FindWidgetWindow(Widget);
if (!WidgetWindow.IsValid())
{
return nullptr;
}
while (WidgetWindow->GetParentWidget().IsValid())
{
TSharedRef<SWidget> CurrentWidget = WidgetWindow->GetParentWidget().ToSharedRef();
TSharedPtr<SWindow> ParentWidgetWindow = FSlateApplication::Get().FindWidgetWindow(CurrentWidget);
if (!ParentWidgetWindow.IsValid())
{
break;
}
WidgetWindow = ParentWidgetWindow;
}
TSharedRef<SWindow> CurrentWindowRef = WidgetWindow.ToSharedRef();
FWidgetPath WidgetPath;
if (FSlateApplication::Get().GeneratePathToWidgetUnchecked(Widget, WidgetPath))
{
FArrangedWidget ArrangedWidget = WidgetPath.FindArrangedWidget(Widget).Get(FArrangedWidget::GetNullWidget());
ArrangedWidget.Geometry.AppendTransform(FSlateLayoutTransform(Inverse(CurrentWindowRef->GetPositionInScreen())));
const FVector2D InflateAmount = FVector2D(1, 1) / FVector2D(ArrangedWidget.Geometry.GetAccumulatedRenderTransform().GetMatrix().GetScale().GetVector());
FPaintGeometry PaintGeometry = ArrangedWidget.Geometry.ToInflatedPaintGeometry(InflateAmount);
Map.Add(WidgetPtr, PaintGeometry);
return Map.Find(WidgetPtr);
}
return nullptr;
}
const FPaintGeometry* GetGeometry(const FNavigationSimulationWidgetInfo& WidgetInfo, const FGeometry& AllottedGeometry, const FVector2D& RootDrawOffset)
{
if (const FPaintGeometry* FoundGeometry = Map.Find(WidgetInfo.WidgetPtr))
{
return FoundGeometry;
}
if (WidgetInfo.bHasGeometry)
{
const FVector2D InflateAmount = FVector2D(1, 1) / FVector2D(WidgetInfo.WidgetGeometry.GetAccumulatedRenderTransform().GetMatrix().GetScale().GetVector());
const FVector2D NewLocalOffset = RootDrawOffset + WidgetInfo.WidgetGeometry.GetAccumulatedLayoutTransform().GetTranslation();
const FVector2D NewLocalSize = TransformPoint(WidgetInfo.WidgetGeometry.GetAccumulatedLayoutTransform().GetScale(), WidgetInfo.WidgetGeometry.GetLocalSize());
Map.Add(WidgetInfo.WidgetPtr, AllottedGeometry.ToPaintGeometry(NewLocalSize, FSlateLayoutTransform(NewLocalOffset)));
return Map.Find(WidgetInfo.WidgetPtr);
}
return nullptr;
}
};
void Paint(
const NavigationSimulationOverlay::FOverlayBrushes& Brushes
, const FNavigationSimulationWidgetNodePtr& NodePtr
, TFunctionRef<TOptional<FPaintGeometry>(const FNavigationSimulationWidgetInfo&)> GetGeometry
, FSlateWindowElementList& OutDrawElements
, int32 LayerId)
{
const TOptional<FPaintGeometry> SourcePaintGeometry = GetGeometry(NodePtr->NavigationSource);
if (!SourcePaintGeometry.IsSet())
{
return;
}
const FScale2D SourceScale = SourcePaintGeometry->GetAccumulatedRenderTransform().GetMatrix().GetScale();
for (const FNavigationSimulationWidgetNodeItem& NodeItem : NodePtr->Simulations)
{
const bool bCanPaint = NodeItem.NavigationType == EUINavigation::Down
|| NodeItem.NavigationType == EUINavigation::Up
|| NodeItem.NavigationType == EUINavigation::Left
|| NodeItem.NavigationType == EUINavigation::Right;
if (!NodeItem.Destination.IsWidgetExplicitlyNull() && bCanPaint)
{
const FPaintNode PaintNode = FPaintNode::MakePaintNode(NodeItem, SourcePaintGeometry.GetValue(), Brushes);
FSlateDrawElement::MakeLines(
OutDrawElements,
LayerId,
SourcePaintGeometry.GetValue(),
PaintNode.SourcePoints,
ESlateDrawEffect::None,
PaintNode.Color,
false,
1.f
);
const TOptional<FPaintGeometry> DestinationPaintGeometry = GetGeometry(NodeItem.Destination);
if (DestinationPaintGeometry.IsSet())
{
const FVector2D DestinationSize = TransformPoint(DestinationPaintGeometry->GetAccumulatedRenderTransform().GetMatrix().GetScale(), DestinationPaintGeometry->GetLocalSize());
FVector2D DestinationCenter = DestinationPaintGeometry->GetAccumulatedRenderTransform().GetTranslation() - SourcePaintGeometry->GetAccumulatedRenderTransform().GetTranslation();
DestinationCenter += DestinationSize / 2.f;
FVector2D ScaledDestinationCenter = SourceScale.Inverse().TransformPoint(DestinationCenter);
TArray<FVector2D> DestinationPoints;
DestinationPoints.Add(PaintNode.SourceCenterPoint);
DestinationPoints.Add(ScaledDestinationCenter);
FSlateDrawElement::MakeLines(
OutDrawElements,
LayerId,
SourcePaintGeometry.GetValue(),
DestinationPoints,
ESlateDrawEffect::None,
PaintNode.Color,
false,
1.f
);
const FVector2D IconPaintSize = TransformPoint(DestinationPaintGeometry->GetAccumulatedRenderTransform().GetMatrix().GetScale(), PaintNode.ArrowBrush->ImageSize / 2.f);
const FVector2D IconPaintOffset = TransformPoint(DestinationPaintGeometry->GetAccumulatedRenderTransform().GetMatrix().GetScale(), PaintNode.ArrowBrush->ImageSize * PaintNode.Direction / 6.f);
const FVector2D IconPaintRootLocation = DestinationPaintGeometry->GetAccumulatedRenderTransform().GetTranslation();
const FVector2D IconPaintLocation = IconPaintRootLocation - (IconPaintSize/2.f) + (DestinationSize / 2.f) + (IconPaintOffset);
FGeometry ArrowGeometry = FGeometry::MakeRoot(IconPaintSize, FSlateLayoutTransform(1.f, IconPaintLocation));
FSlateDrawElement::MakeBox(
OutDrawElements,
LayerId,
ArrowGeometry.ToPaintGeometry(),
PaintNode.ArrowBrush,
ESlateDrawEffect::None,
PaintNode.Color
);
}
}
}
}
}
int32 FNavigationSimulationOverlay::PaintSnapshotNode(const TArray<FNavigationSimulationWidgetNodePtr>& NodeToPaint, const FGeometry& AllottedGeometry, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FVector2D& RootDrawOffset)
{
++LayerId;
NavigationSimulationOverlay::FWidgetGeometryMap GeometryMap;
const NavigationSimulationOverlay::FOverlayBrushes OverlayBrushes;
for (const FNavigationSimulationWidgetNodePtr& NodePtr : NodeToPaint)
{
auto GetGeometry = [&GeometryMap, &AllottedGeometry, RootDrawOffset](const FNavigationSimulationWidgetInfo& Node) -> TOptional<FPaintGeometry>
{
if (Node.bHasGeometry)
{
return TOptional<FPaintGeometry>(*GeometryMap.GetGeometry(Node, AllottedGeometry, RootDrawOffset));
}
return TOptional<FPaintGeometry>();
};
NavigationSimulationOverlay::Paint(OverlayBrushes, NodePtr, GetGeometry, OutDrawElements, LayerId);
}
return LayerId;
}
int32 FNavigationSimulationOverlay::PaintLiveNode(const TArray<FNavigationSimulationWidgetNodePtr>& NodeToPaint, const FGeometry& AllottedGeometry, FSlateWindowElementList& OutDrawElements, int32 LayerId)
{
++LayerId;
NavigationSimulationOverlay::FWidgetGeometryMap GeometryMap;
const NavigationSimulationOverlay::FOverlayBrushes OverlayBrushes;
for (const FNavigationSimulationWidgetNodePtr& NodePtr : NodeToPaint)
{
auto GetGeometry = [&GeometryMap](const FNavigationSimulationWidgetInfo& Info) -> TOptional<FPaintGeometry>
{
if (TSharedPtr<const SWidget> WidgetLive = Info.WidgetLive.Pin())
{
return TOptional<FPaintGeometry>(*GeometryMap.GetGeometry(WidgetLive.ToSharedRef()));
}
return TOptional<FPaintGeometry>();
};
NavigationSimulationOverlay::Paint(OverlayBrushes, NodePtr, GetGeometry, OutDrawElements, LayerId);
}
return LayerId;
}
#undef LOCTEXT_NAMESPACE