Files
UnrealEngine/Engine/Source/Runtime/Slate/Private/Framework/Docking/SDockingCross.cpp
2025-05-18 13:04:45 +08:00

267 lines
7.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Framework/Docking/SDockingCross.h"
#include "Rendering/DrawElements.h"
#include "Framework/Docking/FDockingDragOperation.h"
namespace DockingConstants
{
static const float ZoneFraction = 0.3f;
static const float MaxZoneSize = 150.0f;
static const float MinZoneSize = 5.0f;
}
void SDockingCross::Construct( const FArguments& InArgs, const TSharedPtr<class SDockingNode>& InOwnerNode )
{
OwnerNode = InOwnerNode;
}
int32 SDockingCross::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
{
using namespace DockingConstants;
const float DockZoneSizeX = FMath::Clamp( AllottedGeometry.GetLocalSize().X * ZoneFraction, MinZoneSize, MaxZoneSize );
const float DockZoneSizeY = FMath::Clamp( AllottedGeometry.GetLocalSize().Y * ZoneFraction, MinZoneSize, MaxZoneSize );
// We want to draw this:
// +-------------+
// |\ /|
// |P0---------P1|
// | | | |
// | | | |
// | | | |
// | | | |
// |P3---------P2|
// |/ \|
// +-------------+
const FVector2f P0( DockZoneSizeX, DockZoneSizeY );
const FVector2f P1( AllottedGeometry.GetLocalSize().X - DockZoneSizeX, DockZoneSizeY );
const FVector2f P2( AllottedGeometry.GetLocalSize().X - DockZoneSizeX, AllottedGeometry.GetLocalSize().Y - DockZoneSizeY );
const FVector2f P3( DockZoneSizeX, AllottedGeometry.GetLocalSize().Y - DockZoneSizeY );
const FVector2f P0_Outer( 0.f,0.f );
const FVector2f P1_Outer( AllottedGeometry.GetLocalSize().X,0.f );
const FVector2f P2_Outer( AllottedGeometry.GetLocalSize().X,AllottedGeometry.GetLocalSize().Y );
const FVector2f P3_Outer( 0.f,AllottedGeometry.GetLocalSize().Y );
// Inner Box
{
TArray<FVector2f> InnerBox;
InnerBox.Add( P0 );
InnerBox.Add( P1 );
InnerBox.Add( P2 );
InnerBox.Add( P3 );
InnerBox.Add( P0 );
FSlateDrawElement::MakeLines(
OutDrawElements,
LayerId,
AllottedGeometry.ToPaintGeometry(),
MoveTemp(InnerBox)
);
}
// Outer Box
{
TArray<FVector2f> OuterBox;
OuterBox.Add( P0_Outer );
OuterBox.Add( P1_Outer );
OuterBox.Add( P2_Outer );
OuterBox.Add( P3_Outer );
OuterBox.Add( P0_Outer );
FSlateDrawElement::MakeLines(
OutDrawElements,
LayerId,
AllottedGeometry.ToPaintGeometry(),
MoveTemp(OuterBox)
);
}
// Diagonals
{
TArray<FVector2f> Points;
Points.Add( P0_Outer );
Points.Add( P0 );
FSlateDrawElement::MakeLines(
OutDrawElements,
LayerId,
AllottedGeometry.ToPaintGeometry(),
MoveTemp(Points)
);
}
{
TArray<FVector2f> Points;
Points.Add(FVector2f(P1_Outer));
Points.Add(FVector2f(P1));
FSlateDrawElement::MakeLines(
OutDrawElements,
LayerId,
AllottedGeometry.ToPaintGeometry(),
MoveTemp(Points)
);
}
{
TArray<FVector2f> Points;
Points.Add(FVector2f(P2_Outer));
Points.Add(FVector2f(P2));
FSlateDrawElement::MakeLines(
OutDrawElements,
LayerId,
AllottedGeometry.ToPaintGeometry(),
MoveTemp(Points)
);
}{
TArray<FVector2f> Points;
Points.Add(FVector2f(P3_Outer));
Points.Add(FVector2f(P3));
FSlateDrawElement::MakeLines(
OutDrawElements,
LayerId,
AllottedGeometry.ToPaintGeometry(),
MoveTemp(Points)
);
}
return LayerId;
}
FVector2D SDockingCross::ComputeDesiredSize( float ) const
{
// DockCross does not really have a desired size.
return FVector2D( 16, 16 );
}
static FDockingDragOperation::FDockTarget GetDropTarget( const TSharedPtr<SDockingNode>& OwnerNode, const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent )
{
using namespace DockingConstants;
const FVector2D LocalMousePos = MyGeometry.AbsoluteToLocal(DragDropEvent.GetScreenSpacePosition());
// Noramlize per axis; the section boundaries become orthogonal lines. Slopes are 1 and -1!
const FVector2D NormalizedMousePos = FVector2D(LocalMousePos.X / MyGeometry.Size.X, LocalMousePos.Y/MyGeometry.Size.Y);
const float MouseSlope = NormalizedMousePos.Y / NormalizedMousePos.X;
const float DistanceAlongSlopeOne = FVector2D::DotProduct(NormalizedMousePos, FVector2D::UnitVector);
// Figured which section the mouse lands in.
//
// (0,0)
// +--------x-->
// |\ /|
// | \ / |
// | \ / |
// | \/ |
// | /\ |
// | / \ |
// | / \ |
// y-------\ (1,1)
// | \.
// v \ slope = 1
const FVector2D DeadZoneCenter = FVector2D(0.5f, 0.5f);
const float DeadZoneRadius = 0.75f;
const bool bInDeadZone = (NormalizedMousePos - DeadZoneCenter).SizeSquared() < (DeadZoneRadius*DeadZoneRadius);
struct
{
bool operator()( const FVector2D& InLocalMousePos, const FGeometry& InGeometry ) const
{
// Dock Zones can be as large as they want, but should never get smaller than 5 slate units.
const float DockZoneSizeX = FMath::Clamp( InGeometry.GetLocalSize().X * ZoneFraction, MinZoneSize, MaxZoneSize );
const float DockZoneSizeY = FMath::Clamp( InGeometry.GetLocalSize().Y * ZoneFraction, MinZoneSize, MaxZoneSize );
return
InLocalMousePos.X < DockZoneSizeX ||
InLocalMousePos.X > (InGeometry.Size.X - DockZoneSizeX) ||
InLocalMousePos.Y < DockZoneSizeY ||
InLocalMousePos.Y > (InGeometry.Size.Y - DockZoneSizeY);
}
} IsInDockZone;
const bool bIsInDockZone = IsInDockZone(LocalMousePos, MyGeometry);
if ( !bIsInDockZone )
{
return FDockingDragOperation::FDockTarget();
}
else if (MouseSlope > 1.0f)
{
if (DistanceAlongSlopeOne > 1.0f)
{
return FDockingDragOperation::FDockTarget( OwnerNode, SDockingNode::Below );
}
else
{
return FDockingDragOperation::FDockTarget( OwnerNode, SDockingNode::LeftOf );
}
}
else
{
if (DistanceAlongSlopeOne > 1.0f)
{
return FDockingDragOperation::FDockTarget( OwnerNode, SDockingNode::RightOf );
}
else
{
return FDockingDragOperation::FDockTarget( OwnerNode, SDockingNode::Above );
}
}
}
void SDockingCross::OnDragLeave( const FDragDropEvent& DragDropEvent )
{
TSharedPtr<FDockingDragOperation> DragDropOperation = DragDropEvent.GetOperationAs<FDockingDragOperation>();
if ( DragDropOperation.IsValid() )
{
DragDropOperation->SetHoveredTarget(FDockingDragOperation::FDockTarget(), DragDropEvent);
}
}
FReply SDockingCross::OnDragOver( const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent )
{
TSharedPtr<FDockingDragOperation> DragDropOperation = DragDropEvent.GetOperationAs<FDockingDragOperation>();
if ( DragDropOperation.IsValid() )
{
const FDockingDragOperation::FDockTarget DropTarget = GetDropTarget(OwnerNode.Pin(), MyGeometry, DragDropEvent);
DragDropOperation->SetHoveredTarget(DropTarget, DragDropEvent);
return FReply::Handled();
}
else
{
return FReply::Unhandled();
}
}
FReply SDockingCross::OnDrop( const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent )
{
if ( DragDropEvent.GetOperationAs<FDockingDragOperation>().IsValid() )
{
const FDockingDragOperation::FDockTarget DropTarget = GetDropTarget(OwnerNode.Pin(), MyGeometry, DragDropEvent);
TSharedPtr<SDockingNode> DropTargetNode = DropTarget.TargetNode.Pin();
if (DropTargetNode.IsValid())
{
return DropTargetNode->OnUserAttemptingDock( DropTarget.DockDirection, DragDropEvent );
}
}
return FReply::Unhandled();
}