Files
UnrealEngine/Engine/Source/Editor/UMGEditor/Private/Designer/SDesignSurface.cpp
2025-05-18 13:04:45 +08:00

661 lines
23 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Designer/SDesignSurface.h"
#include "Rendering/DrawElements.h"
#include "Framework/Application/SlateApplication.h"
#if WITH_EDITOR
#include "Settings/EditorStyleSettings.h"
#include "Settings/LevelEditorViewportSettings.h"
#endif // WITH_EDITOR
#define LOCTEXT_NAMESPACE "UMG"
struct FZoomLevelEntry
{
public:
FZoomLevelEntry(float InZoomAmount, const FText& InDisplayText, EGraphRenderingLOD::Type InLOD)
: DisplayText(FText::Format(LOCTEXT("Zoom", "Zoom {0}"), InDisplayText))
, ZoomAmount(InZoomAmount)
, LOD(InLOD)
{
}
public:
FText DisplayText;
float ZoomAmount;
EGraphRenderingLOD::Type LOD;
};
struct FFixedZoomLevelsContainerDesignSurface : public FZoomLevelsContainer
{
FFixedZoomLevelsContainerDesignSurface()
{
ZoomLevels.Add(FZoomLevelEntry(0.150f, FText::FromString("-10"), EGraphRenderingLOD::LowestDetail));
ZoomLevels.Add(FZoomLevelEntry(0.175f, FText::FromString("-9"), EGraphRenderingLOD::LowestDetail));
ZoomLevels.Add(FZoomLevelEntry(0.200f, FText::FromString("-8"), EGraphRenderingLOD::LowestDetail));
ZoomLevels.Add(FZoomLevelEntry(0.225f, FText::FromString("-7"), EGraphRenderingLOD::LowDetail));
ZoomLevels.Add(FZoomLevelEntry(0.250f, FText::FromString("-6"), EGraphRenderingLOD::LowDetail));
ZoomLevels.Add(FZoomLevelEntry(0.375f, FText::FromString("-5"), EGraphRenderingLOD::MediumDetail));
ZoomLevels.Add(FZoomLevelEntry(0.500f, FText::FromString("-4"), EGraphRenderingLOD::MediumDetail));
ZoomLevels.Add(FZoomLevelEntry(0.675f, FText::FromString("-3"), EGraphRenderingLOD::MediumDetail));
ZoomLevels.Add(FZoomLevelEntry(0.750f, FText::FromString("-2"), EGraphRenderingLOD::DefaultDetail));
ZoomLevels.Add(FZoomLevelEntry(0.875f, FText::FromString("-1"), EGraphRenderingLOD::DefaultDetail));
ZoomLevels.Add(FZoomLevelEntry(1.000f, FText::FromString("1:1"), EGraphRenderingLOD::DefaultDetail));
ZoomLevels.Add(FZoomLevelEntry(1.250f, FText::FromString("+1"), EGraphRenderingLOD::DefaultDetail));
ZoomLevels.Add(FZoomLevelEntry(1.500f, FText::FromString("+2"), EGraphRenderingLOD::DefaultDetail));
ZoomLevels.Add(FZoomLevelEntry(1.750f, FText::FromString("+3"), EGraphRenderingLOD::FullyZoomedIn));
ZoomLevels.Add(FZoomLevelEntry(2.000f, FText::FromString("+4"), EGraphRenderingLOD::FullyZoomedIn));
ZoomLevels.Add(FZoomLevelEntry(2.250f, FText::FromString("+5"), EGraphRenderingLOD::FullyZoomedIn));
ZoomLevels.Add(FZoomLevelEntry(2.500f, FText::FromString("+6"), EGraphRenderingLOD::FullyZoomedIn));
ZoomLevels.Add(FZoomLevelEntry(2.750f, FText::FromString("+7"), EGraphRenderingLOD::FullyZoomedIn));
ZoomLevels.Add(FZoomLevelEntry(3.000f, FText::FromString("+8"), EGraphRenderingLOD::FullyZoomedIn));
ZoomLevels.Add(FZoomLevelEntry(3.250f, FText::FromString("+9"), EGraphRenderingLOD::FullyZoomedIn));
ZoomLevels.Add(FZoomLevelEntry(3.500f, FText::FromString("+10"), EGraphRenderingLOD::FullyZoomedIn));
ZoomLevels.Add(FZoomLevelEntry(4.000f, FText::FromString("+11"), EGraphRenderingLOD::FullyZoomedIn));
ZoomLevels.Add(FZoomLevelEntry(5.000f, FText::FromString("+12"), EGraphRenderingLOD::FullyZoomedIn));
ZoomLevels.Add(FZoomLevelEntry(6.000f, FText::FromString("+13"), EGraphRenderingLOD::FullyZoomedIn));
ZoomLevels.Add(FZoomLevelEntry(7.000f, FText::FromString("+14"), EGraphRenderingLOD::FullyZoomedIn));
ZoomLevels.Add(FZoomLevelEntry(8.000f, FText::FromString("+15"), EGraphRenderingLOD::FullyZoomedIn));
ZoomLevels.Add(FZoomLevelEntry(9.000f, FText::FromString("+16"), EGraphRenderingLOD::FullyZoomedIn));
ZoomLevels.Add(FZoomLevelEntry(10.000f, FText::FromString("+17"), EGraphRenderingLOD::FullyZoomedIn));
ZoomLevels.Add(FZoomLevelEntry(11.000f, FText::FromString("+18"), EGraphRenderingLOD::FullyZoomedIn));
ZoomLevels.Add(FZoomLevelEntry(12.000f, FText::FromString("+19"), EGraphRenderingLOD::FullyZoomedIn));
ZoomLevels.Add(FZoomLevelEntry(13.000f, FText::FromString("+20"), EGraphRenderingLOD::FullyZoomedIn));
}
float GetZoomAmount(int32 InZoomLevel) const override
{
checkSlow(ZoomLevels.IsValidIndex(InZoomLevel));
return ZoomLevels[InZoomLevel].ZoomAmount;
}
int32 GetNearestZoomLevel(float InZoomAmount) const override
{
for ( int32 ZoomLevelIndex=0; ZoomLevelIndex < GetNumZoomLevels(); ++ZoomLevelIndex )
{
if ( InZoomAmount <= GetZoomAmount(ZoomLevelIndex) )
{
return ZoomLevelIndex;
}
}
return GetDefaultZoomLevel();
}
FText GetZoomText(int32 InZoomLevel) const override
{
checkSlow(ZoomLevels.IsValidIndex(InZoomLevel));
return ZoomLevels[InZoomLevel].DisplayText;
}
int32 GetNumZoomLevels() const override
{
return ZoomLevels.Num();
}
int32 GetDefaultZoomLevel() const override
{
return 10;
}
EGraphRenderingLOD::Type GetLOD(int32 InZoomLevel) const override
{
checkSlow(ZoomLevels.IsValidIndex(InZoomLevel));
return ZoomLevels[InZoomLevel].LOD;
}
TArray<FZoomLevelEntry> ZoomLevels;
};
/////////////////////////////////////////////////////
// SDesignSurface
void SDesignSurface::Construct(const FArguments& InArgs)
{
if ( !ZoomLevels )
{
ZoomLevels = MakeUnique<FFixedZoomLevelsContainerDesignSurface>();
}
ZoomLevel = ZoomLevels->GetDefaultZoomLevel();
PreviousZoomLevel = ZoomLevels->GetDefaultZoomLevel();
PostChangedZoom();
AllowContinousZoomInterpolation = InArgs._AllowContinousZoomInterpolation;
bIsPanning = false;
bIsZooming = false;
ViewOffset = FVector2D::ZeroVector;
bDrawGridLines = true;
ZoomLevelFade = FCurveSequence(0.0f, 1.0f);
ZoomLevelFade.Play( this->AsShared() );
ZoomLevelGraphFade = FCurveSequence(0.0f, 0.5f);
ZoomLevelGraphFade.Play( this->AsShared() );
bDeferredZoomToExtents = false;
bAllowContinousZoomInterpolation = false;
bTeleportInsteadOfScrollingWhenZoomingToFit = false;
bRequireControlToOverZoom = false;
ZoomTargetTopLeft = FVector2D::ZeroVector;
ZoomTargetBottomRight = FVector2D::ZeroVector;
ZoomToFitPadding = FVector2D(100, 100);
TotalGestureMagnify = 0.0f;
TotalMouseDelta = 0.0f;
ZoomStartOffset = FVector2D::ZeroVector;
ChildSlot
[
InArgs._Content.Widget
];
}
EActiveTimerReturnType SDesignSurface::HandleZoomToFit( double InCurrentTime, float InDeltaTime )
{
const FVector2D DesiredViewCenter = ( ZoomTargetTopLeft + ZoomTargetBottomRight ) * 0.5f;
const bool bDoneScrolling = ScrollToLocation(GetCachedGeometry(), DesiredViewCenter, bTeleportInsteadOfScrollingWhenZoomingToFit ? 1000.0f : InDeltaTime);
const bool bDoneZooming = ZoomToLocation(GetCachedGeometry().GetLocalSize(), ZoomTargetBottomRight - ZoomTargetTopLeft, bDoneScrolling);
if (bDoneZooming && bDoneScrolling)
{
// One final push to make sure we're centered in the end
ViewOffset = DesiredViewCenter - ( 0.5f * GetCachedGeometry().GetLocalSize() / GetZoomAmount() );
ZoomTargetTopLeft = FVector2D::ZeroVector;
ZoomTargetBottomRight = FVector2D::ZeroVector;
return EActiveTimerReturnType::Stop;
}
return EActiveTimerReturnType::Continue;
}
void SDesignSurface::Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime )
{
// Zoom to extents
FSlateRect Bounds = ComputeAreaBounds();
if ( bDeferredZoomToExtents )
{
bDeferredZoomToExtents = false;
ZoomTargetTopLeft = FVector2D(Bounds.Left, Bounds.Top);
ZoomTargetBottomRight = FVector2D(Bounds.Right, Bounds.Bottom);
if (!ActiveTimerHandle.IsValid())
{
RegisterActiveTimer(0.f, FWidgetActiveTimerDelegate::CreateSP(this, &SDesignSurface::HandleZoomToFit));
}
}
}
FCursorReply SDesignSurface::OnCursorQuery(const FGeometry& MyGeometry, const FPointerEvent& CursorEvent) const
{
if ( bIsPanning )
{
return FCursorReply::Cursor(EMouseCursor::GrabHand);
}
return SCompoundWidget::OnCursorQuery(MyGeometry, CursorEvent);
}
int32 SDesignSurface::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
// Store the current Scene Index, as child classes may want to use a different scene to preview widgets (Ex: SDesignerView)
int32 SceneIndex = FSlateApplication::Get().GetRenderer()->GetCurrentSceneIndex();
OnPaintBackground(AllottedGeometry, MyCullingRect, OutDrawElements, LayerId);
int32 Result = SCompoundWidget::OnPaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
// Restore previous Scene Index to avoid permanently changing the scene
FSlateApplication::Get().GetRenderer()->SetCurrentSceneIndex(SceneIndex);
return Result;
}
void SDesignSurface::OnPaintBackground(const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId) const
{
const FSlateBrush* BackgroundImage = FAppStyle::GetBrush(TEXT("Graph.Panel.SolidBackground"));
PaintBackgroundAsLines(BackgroundImage, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId);
}
FReply SDesignSurface::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
SCompoundWidget::OnMouseButtonDown(MyGeometry, MouseEvent);
if ( MouseEvent.GetEffectingButton() == EKeys::RightMouseButton || MouseEvent.GetEffectingButton() == EKeys::MiddleMouseButton )
{
bIsPanning = false;
ViewOffsetStart = ViewOffset;
MouseDownPositionAbsolute = MouseEvent.GetLastScreenSpacePosition();
}
if (MouseEvent.GetEffectingButton() == EKeys::RightMouseButton || FSlateApplication::Get().IsUsingTrackpad())
{
TotalMouseDelta = 0.0f;
ZoomStartOffset = MyGeometry.AbsoluteToLocal(MouseEvent.GetLastScreenSpacePosition());
}
return FReply::Unhandled();
}
FReply SDesignSurface::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
SCompoundWidget::OnMouseButtonUp(MyGeometry, MouseEvent);
if ( MouseEvent.GetEffectingButton() == EKeys::RightMouseButton || MouseEvent.GetEffectingButton() == EKeys::MiddleMouseButton )
{
bIsPanning = false;
bIsZooming = false;
}
return FReply::Unhandled();
}
FReply SDesignSurface::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
const bool bIsRightMouseButtonDown = MouseEvent.IsMouseButtonDown(EKeys::RightMouseButton);
const bool bIsLeftMouseButtonDown = MouseEvent.IsMouseButtonDown(EKeys::LeftMouseButton);
const bool bIsMiddleMouseButtonDown = MouseEvent.IsMouseButtonDown(EKeys::MiddleMouseButton);
const FModifierKeysState ModifierKeysState = FSlateApplication::Get().GetModifierKeys();
if ( HasMouseCapture() )
{
const FVector2D CursorDelta = MouseEvent.GetCursorDelta();
const bool bShouldZoom = bIsRightMouseButtonDown && (bIsLeftMouseButtonDown || bIsMiddleMouseButtonDown || ModifierKeysState.IsAltDown() || FSlateApplication::Get().IsUsingTrackpad());
if ( bShouldZoom )
{
const double MouseZoomScaling = 0.04f;
FReply ReplyState = FReply::Handled();
TotalMouseDelta += CursorDelta.X + CursorDelta.Y;
const int32 ZoomLevelDelta = FMath::RoundToInt32(TotalMouseDelta * MouseZoomScaling);
// Get rid of mouse movement that's been 'used up' by zooming
if (ZoomLevelDelta != 0)
{
TotalMouseDelta -= (ZoomLevelDelta / MouseZoomScaling);
bIsZooming = true;
}
// Perform zoom centered on the cached start offset
ChangeZoomLevel(ZoomLevelDelta, ZoomStartOffset, MouseEvent.IsControlDown());
bIsPanning = false;
return ReplyState;
}
else if ( bIsRightMouseButtonDown || bIsMiddleMouseButtonDown )
{
FReply ReplyState = FReply::Handled();
bIsPanning = true;
bIsZooming = false;
ViewOffset = ViewOffsetStart + ( (MouseDownPositionAbsolute - MouseEvent.GetScreenSpacePosition()) / MyGeometry.Scale) / GetZoomAmount();
return ReplyState;
}
}
return FReply::Unhandled();
}
FReply SDesignSurface::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
// We want to zoom into this point; i.e. keep it the same fraction offset into the panel
const FVector2D WidgetSpaceCursorPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition());
const int32 ZoomLevelDelta = FMath::FloorToInt(MouseEvent.GetWheelDelta());
ChangeZoomLevel(ZoomLevelDelta, WidgetSpaceCursorPos, !bRequireControlToOverZoom || MouseEvent.IsControlDown());
MouseDownPositionAbsolute = MouseEvent.GetScreenSpacePosition();
return FReply::Handled();
}
FReply SDesignSurface::OnTouchGesture(const FGeometry& MyGeometry, const FPointerEvent& GestureEvent)
{
const EGestureEvent GestureType = GestureEvent.GetGestureType();
const FVector2D GestureDelta = GestureEvent.GetGestureDelta();
if ( GestureType == EGestureEvent::Magnify )
{
TotalGestureMagnify += static_cast<float>(GestureDelta.X);
if ( FMath::Abs(TotalGestureMagnify) > 0.07f )
{
// We want to zoom into this point; i.e. keep it the same fraction offset into the panel
const FVector2D WidgetSpaceCursorPos = MyGeometry.AbsoluteToLocal(GestureEvent.GetScreenSpacePosition());
const int32 ZoomLevelDelta = TotalGestureMagnify > 0.0f ? 1 : -1;
ChangeZoomLevel(ZoomLevelDelta, WidgetSpaceCursorPos, !bRequireControlToOverZoom || GestureEvent.IsControlDown());
MouseDownPositionAbsolute = GestureEvent.GetScreenSpacePosition();
TotalGestureMagnify = 0.0f;
}
return FReply::Handled();
}
else if ( GestureType == EGestureEvent::Scroll )
{
const EScrollGestureDirection DirectionSetting = GetDefault<ULevelEditorViewportSettings>()->ScrollGestureDirectionForOrthoViewports;
const bool bUseDirectionInvertedFromDevice = DirectionSetting == EScrollGestureDirection::Natural || (DirectionSetting == EScrollGestureDirection::UseSystemSetting && GestureEvent.IsDirectionInvertedFromDevice());
this->bIsPanning = true;
ViewOffset -= (bUseDirectionInvertedFromDevice == GestureEvent.IsDirectionInvertedFromDevice() ? GestureDelta : -GestureDelta) / GetZoomAmount();
return FReply::Handled();
}
return FReply::Unhandled();
}
FReply SDesignSurface::OnTouchEnded(const FGeometry& MyGeometry, const FPointerEvent& InTouchEvent)
{
TotalGestureMagnify = 0.0f;
return FReply::Unhandled();
}
inline float FancyMod(float Value, float Size)
{
return ( ( Value >= 0 ) ? 0.0f : Size ) + FMath::Fmod(Value, Size);
}
float SDesignSurface::GetZoomAmount() const
{
if ( AllowContinousZoomInterpolation.Get() )
{
return FMath::Lerp(ZoomLevels->GetZoomAmount(PreviousZoomLevel), ZoomLevels->GetZoomAmount(ZoomLevel), ZoomLevelGraphFade.GetLerp());
}
else
{
return ZoomLevels->GetZoomAmount(ZoomLevel);
}
}
void SDesignSurface::ChangeZoomLevel(int32 ZoomLevelDelta, const FVector2D& WidgetSpaceZoomOrigin, bool bOverrideZoomLimiting)
{
// We want to zoom into this point; i.e. keep it the same fraction offset into the panel
const FVector2D PointToMaintainGraphSpace = PanelCoordToGraphCoord(WidgetSpaceZoomOrigin);
const int32 DefaultZoomLevel = ZoomLevels->GetDefaultZoomLevel();
const int32 NumZoomLevels = ZoomLevels->GetNumZoomLevels();
const bool bAllowFullZoomRange =
// To zoom in past 1:1 the user must press control
( ZoomLevel == DefaultZoomLevel && ZoomLevelDelta > 0 && bOverrideZoomLimiting ) ||
// If they are already zoomed in past 1:1, user may zoom freely
( ZoomLevel > DefaultZoomLevel );
const int32 OldZoomLevel = ZoomLevel;
if ( bAllowFullZoomRange )
{
ZoomLevel = FMath::Clamp(ZoomLevel + ZoomLevelDelta, 0, NumZoomLevels - 1);
}
else
{
// Without control, we do not allow zooming in past 1:1.
ZoomLevel = FMath::Clamp(ZoomLevel + ZoomLevelDelta, 0, DefaultZoomLevel);
}
if ( OldZoomLevel != ZoomLevel )
{
PostChangedZoom();
// Note: This happens even when maxed out at a stop; so the user sees the animation and knows that they're at max zoom in/out
ZoomLevelFade.Play( this->AsShared() );
// Re-center the screen so that it feels like zooming around the cursor.
{
const FVector2D NewViewOffset = PointToMaintainGraphSpace - WidgetSpaceZoomOrigin / GetZoomAmount();
// If we're panning while zooming we need to update the viewoffset start.
ViewOffsetStart += (NewViewOffset - ViewOffset);
// Update view offset to where ever we scrolled towards.
ViewOffset = NewViewOffset;
TotalMouseDelta = 0.0f;
}
}
}
FSlateRect SDesignSurface::ComputeSensibleBounds() const
{
// Pad it out in every direction, to roughly account for nodes being of non-zero extent
const float Padding = 100.0f;
FSlateRect Bounds = ComputeAreaBounds();
Bounds.Left -= Padding;
Bounds.Top -= Padding;
Bounds.Right -= Padding;
Bounds.Bottom -= Padding;
return Bounds;
}
void SDesignSurface::PostChangedZoom()
{
}
bool SDesignSurface::ScrollToLocation(const FGeometry& MyGeometry, FVector2D DesiredCenterPosition, const float InDeltaTime)
{
const FVector2D HalfOFScreenInGraphSpace = 0.5f * MyGeometry.GetLocalSize() / GetZoomAmount();
FVector2D CurrentPosition = ViewOffset + HalfOFScreenInGraphSpace;
FVector2D NewPosition = FMath::Vector2DInterpTo(CurrentPosition, DesiredCenterPosition, InDeltaTime, 10.f);
ViewOffset = NewPosition - HalfOFScreenInGraphSpace;
// If within 1 pixel of target, stop interpolating
return ( ( NewPosition - DesiredCenterPosition ).SizeSquared() < 1.f );
}
bool SDesignSurface::ZoomToLocation(const FVector2D& CurrentSizeWithoutZoom, const FVector2D& InDesiredSize, bool bDoneScrolling)
{
if ( bAllowContinousZoomInterpolation && ZoomLevelGraphFade.IsPlaying() )
{
return false;
}
const int32 DefaultZoomLevel = ZoomLevels->GetDefaultZoomLevel();
const int32 NumZoomLevels = ZoomLevels->GetNumZoomLevels();
int32 DesiredZoom = DefaultZoomLevel;
// Find lowest zoom level that will display all nodes
for ( int32 Zoom = 0; Zoom < DefaultZoomLevel; ++Zoom )
{
const FVector2D SizeWithZoom = (CurrentSizeWithoutZoom - ZoomToFitPadding) / ZoomLevels->GetZoomAmount(Zoom);
const FVector2D LeftOverSize = SizeWithZoom - InDesiredSize;
if ( ( InDesiredSize.X > SizeWithZoom.X ) || ( InDesiredSize.Y > SizeWithZoom.Y ) )
{
// Use the previous zoom level, this one is too tight
DesiredZoom = FMath::Max<int32>(0, Zoom - 1);
break;
}
}
if ( DesiredZoom != ZoomLevel )
{
if ( bAllowContinousZoomInterpolation )
{
// Animate to it
PreviousZoomLevel = ZoomLevel;
ZoomLevel = FMath::Clamp(DesiredZoom, 0, NumZoomLevels - 1);
ZoomLevelGraphFade.Play(this->AsShared());
return false;
}
else
{
// Do it instantly, either first or last
if ( DesiredZoom < ZoomLevel )
{
// Zooming out; do it instantly
ZoomLevel = PreviousZoomLevel = DesiredZoom;
ZoomLevelFade.Play(this->AsShared());
}
else
{
// Zooming in; do it last
if ( bDoneScrolling )
{
ZoomLevel = PreviousZoomLevel = DesiredZoom;
ZoomLevelFade.Play(this->AsShared());
}
}
}
PostChangedZoom();
}
return true;
}
void SDesignSurface::ZoomToFit(bool bInstantZoom)
{
bTeleportInsteadOfScrollingWhenZoomingToFit = bInstantZoom;
bDeferredZoomToExtents = true;
}
FText SDesignSurface::GetZoomText() const
{
return ZoomLevels->GetZoomText(ZoomLevel);
}
FSlateColor SDesignSurface::GetZoomTextColorAndOpacity() const
{
return FLinearColor(1, 1, 1, 1.25f - ZoomLevelFade.GetLerp());
}
FSlateRect SDesignSurface::ComputeAreaBounds() const
{
return FSlateRect(0, 0, 0, 0);
}
FVector2D SDesignSurface::GetViewOffset() const
{
return ViewOffset;
}
FVector2D SDesignSurface::GraphCoordToPanelCoord(const FVector2D& GraphSpaceCoordinate) const
{
return ( GraphSpaceCoordinate - GetViewOffset() ) * GetZoomAmount();
}
FVector2D SDesignSurface::PanelCoordToGraphCoord(const FVector2D& PanelSpaceCoordinate) const
{
return PanelSpaceCoordinate / GetZoomAmount() + GetViewOffset();
}
int32 SDesignSurface::GetGraphRulePeriod() const
{
return (int32)FAppStyle::GetFloat("Graph.Panel.GridRulePeriod");
}
float SDesignSurface::GetGridScaleAmount() const
{
return 1;
}
void SDesignSurface::PaintBackgroundAsLines(const FSlateBrush* BackgroundImage, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32& DrawLayerId) const
{
const bool bAntialias = GetDefault<UEditorStyleSettings>()->bAntiAliasGrid;
const int32 RulePeriod = GetGraphRulePeriod();
check(RulePeriod > 0);
const FLinearColor RegularColor(FAppStyle::GetColor("Graph.Panel.GridLineColor"));
const FLinearColor RuleColor(FAppStyle::GetColor("Graph.Panel.GridRuleColor"));
const FLinearColor CenterColor(FAppStyle::GetColor("Graph.Panel.GridCenterColor"));
const float GraphSmallestGridSize = 8.0f;
const float RawZoomFactor = GetZoomAmount();
const float NominalGridSize = GetSnapGridSize() * GetGridScaleAmount();
float ZoomFactor = RawZoomFactor;
float Inflation = 1.0f;
while ( ZoomFactor*Inflation*NominalGridSize <= GraphSmallestGridSize )
{
Inflation *= 2.0f;
}
const float GridCellSize = NominalGridSize * ZoomFactor * Inflation;
FVector2f LocalGridOrigin = AllottedGeometry.AbsoluteToLocal(GridOrigin);
float ImageOffsetX = LocalGridOrigin.X - ((GridCellSize*RulePeriod) * FMath::Max(FMath::CeilToInt(LocalGridOrigin.X / (GridCellSize*RulePeriod)), 0));
float ImageOffsetY = LocalGridOrigin.Y - ((GridCellSize*RulePeriod) * FMath::Max(FMath::CeilToInt(LocalGridOrigin.Y / (GridCellSize*RulePeriod)), 0));
// Fill the background
FSlateDrawElement::MakeBox(
OutDrawElements,
DrawLayerId,
AllottedGeometry.ToPaintGeometry(),
BackgroundImage
);
if (bDrawGridLines)
{
TArray<FVector2D> LinePoints;
new (LinePoints)FVector2D(0.0f, 0.0f);
new (LinePoints)FVector2D(0.0f, 0.0f);
// Horizontal bars
for (int32 GridIndex = 0; ImageOffsetY < AllottedGeometry.GetLocalSize().Y; ImageOffsetY += GridCellSize, ++GridIndex)
{
if (ImageOffsetY >= 0.0f)
{
const bool bIsRuleLine = (GridIndex % RulePeriod) == 0;
const int32 Layer = bIsRuleLine ? (DrawLayerId + 1) : DrawLayerId;
const FLinearColor* Color = bIsRuleLine ? &RuleColor : &RegularColor;
if (FMath::IsNearlyEqual(LocalGridOrigin.Y, ImageOffsetY, 1.0f))
{
Color = &CenterColor;
}
LinePoints[0] = FVector2D(0.0f, ImageOffsetY);
LinePoints[1] = FVector2D(AllottedGeometry.GetLocalSize().X, ImageOffsetY);
FSlateDrawElement::MakeLines(
OutDrawElements,
Layer,
AllottedGeometry.ToPaintGeometry(),
LinePoints,
ESlateDrawEffect::None,
*Color,
bAntialias);
}
}
// Vertical bars
for (int32 GridIndex = 0; ImageOffsetX < AllottedGeometry.GetLocalSize().X; ImageOffsetX += GridCellSize, ++GridIndex)
{
if (ImageOffsetX >= 0.0f)
{
const bool bIsRuleLine = (GridIndex % RulePeriod) == 0;
const int32 Layer = bIsRuleLine ? (DrawLayerId + 1) : DrawLayerId;
const FLinearColor* Color = bIsRuleLine ? &RuleColor : &RegularColor;
if (FMath::IsNearlyEqual(LocalGridOrigin.X, ImageOffsetX, 1.0f))
{
Color = &CenterColor;
}
LinePoints[0] = FVector2D(ImageOffsetX, 0.0f);
LinePoints[1] = FVector2D(ImageOffsetX, AllottedGeometry.GetLocalSize().Y);
FSlateDrawElement::MakeLines(
OutDrawElements,
Layer,
AllottedGeometry.ToPaintGeometry(),
LinePoints,
ESlateDrawEffect::None,
*Color,
bAntialias);
}
}
}
DrawLayerId += 2;
}
#undef LOCTEXT_NAMESPACE