Files
UnrealEngineUWP/Engine/Source/Editor/GraphEditor/Private/SGraphNodeResizable.cpp
Ben Cosh 3650f14795 This change makes the comment bubbles interactive widgets which allow the user to double click the comment to edit and have extra optional pinning and visibility options.
#UE-2764 - ROCKET: TASK: K2: PUNTABLE: Allow double clicking on comment bubble to edit it.
#Branch UE4
#Proj Engine, UnrealEd, Slate, GraphEditor, BehaviourTreeEditor, EnvironmentQueryEditor, EditorStyle, ReferenceViewer, WorldBrowser

#codereview Nick.Atamas

[CL 2346951 by Ben Cosh in Main branch]
2014-11-03 10:40:57 -05:00

346 lines
9.9 KiB
C++

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#include "GraphEditorCommon.h"
#include "SGraphNodeResizable.h"
#include "ScopedTransaction.h"
namespace GraphNodeResizableDefs
{
/** Size of the hit result border for the window borders */
static const FSlateRect HitResultBorderSize( 10, 10, 10, 10 );
/** Default Title Bar Size */
static const float DefaultTitleBarHeight = 12.f;
/** Minimum size for node */
static const FVector2D MinNodeSize( 30.0f, 30.0f );
/** Maximum size for node */
static const FVector2D MaxNodeSize( 400.0f, 400.0f );
}
bool SGraphNodeResizable::InSelectionArea(EResizableWindowZone InMouseZone) const
{
return ( (InMouseZone == CRWZ_RightBorder) || (InMouseZone == CRWZ_BottomBorder) || (InMouseZone == CRWZ_BottomRightBorder) ||
(InMouseZone == CRWZ_LeftBorder) || (InMouseZone == CRWZ_TopBorder) || (InMouseZone == CRWZ_TopLeftBorder) ||
(InMouseZone == CRWZ_TopRightBorder) || (InMouseZone == CRWZ_BottomLeftBorder) );
}
void SGraphNodeResizable::OnMouseEnter( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
// Determine the zone the mouse is in
if( !bUserIsDragging )
{
FVector2D LocalMouseCoordinates = MyGeometry.AbsoluteToLocal( MouseEvent.GetScreenSpacePosition() );
MouseZone = FindMouseZone(LocalMouseCoordinates);
SNodePanel::SNode::OnMouseEnter( MyGeometry, MouseEvent );
}
}
void SGraphNodeResizable::OnMouseLeave( const FPointerEvent& MouseEvent )
{
if( !bUserIsDragging )
{
// Reset our mouse zone
MouseZone = CRWZ_NotInWindow;
SNodePanel::SNode::OnMouseLeave( MouseEvent );
}
}
FCursorReply SGraphNodeResizable::OnCursorQuery( const FGeometry& MyGeometry, const FPointerEvent& CursorEvent ) const
{
if (MouseZone == CRWZ_RightBorder || MouseZone == CRWZ_LeftBorder)
{
// right/left of node
return FCursorReply::Cursor(EMouseCursor::ResizeLeftRight);
}
else if (MouseZone == CRWZ_BottomRightBorder || MouseZone == CRWZ_TopLeftBorder)
{
// bottom right / top left hand corner
return FCursorReply::Cursor( EMouseCursor::ResizeSouthEast );
}
else if (MouseZone == CRWZ_BottomBorder || MouseZone == CRWZ_TopBorder)
{
// bottom / top of node
return FCursorReply::Cursor(EMouseCursor::ResizeUpDown);
}
else if (MouseZone == CRWZ_BottomLeftBorder || MouseZone == CRWZ_TopRightBorder)
{
// bottom left / top right hand corner
return FCursorReply::Cursor( EMouseCursor::ResizeSouthWest );
}
else if (MouseZone == CRWZ_TitleBar)
{
return FCursorReply::Cursor(EMouseCursor::CardinalCross);
}
return FCursorReply::Unhandled();
}
FReply SGraphNodeResizable::OnMouseButtonDown( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
if ( InSelectionArea() && (MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton) && IsEditable.Get() )
{
bUserIsDragging = true;
StoredUserSize = UserSize;
DragSize = UserSize;
//Find node anchor point
InitNodeAnchorPoint();
return FReply::Handled().CaptureMouse( SharedThis(this) );
}
else
{
return FReply::Unhandled();
}
}
FReply SGraphNodeResizable::OnMouseButtonUp( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
if ( (MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton) && bUserIsDragging )
{
bUserIsDragging = false;
// Resize the node
UserSize.X = FMath::RoundToFloat(UserSize.X);
UserSize.Y = FMath::RoundToFloat(UserSize.Y);
GetNodeObj()->ResizeNode(UserSize);
// End resize transaction
ResizeTransactionPtr.Reset();
return FReply::Handled().ReleaseMouseCapture();
}
return FReply::Unhandled();
}
FReply SGraphNodeResizable::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
FVector2D LocalMouseCoordinates = MyGeometry.AbsoluteToLocal( MouseEvent.GetScreenSpacePosition() );
if (bUserIsDragging)
{
FVector2D GraphSpaceCoordinates = NodeCoordToGraphCoord( MouseEvent.GetScreenSpacePosition() );
FVector2D OldGraphSpaceCoordinates = NodeCoordToGraphCoord( MouseEvent.GetLastScreenSpacePosition() );
FVector2D Delta = GraphSpaceCoordinates - OldGraphSpaceCoordinates;
//Clamp delta value based on resizing direction
if( MouseZone == CRWZ_LeftBorder || MouseZone == CRWZ_RightBorder )
{
Delta.Y = 0.0f;
}
else if( MouseZone == CRWZ_TopBorder || MouseZone == CRWZ_BottomBorder )
{
Delta.X = 0.0f;
}
//Resize node delta value
FVector2D DeltaNodeSize = Delta;
//Modify node size delta value based on resizing direction
if( (MouseZone == CRWZ_LeftBorder) || (MouseZone == CRWZ_TopBorder) || (MouseZone == CRWZ_TopLeftBorder) )
{
DeltaNodeSize = -DeltaNodeSize;
}
else if( MouseZone == CRWZ_TopRightBorder )
{
DeltaNodeSize.Y = -DeltaNodeSize.Y;
}
else if( MouseZone == CRWZ_BottomLeftBorder )
{
DeltaNodeSize.X = -DeltaNodeSize.X;
}
// Apply delta unfiltered to DragSize
DragSize.X += DeltaNodeSize.X;
DragSize.Y += DeltaNodeSize.Y;
// apply snap
const float SnapSize = SNodePanel::GetSnapGridSize();
FVector2D SnappedSize;
SnappedSize.X = SnapSize * FMath::RoundToFloat( DragSize.X/SnapSize );
SnappedSize.Y = SnapSize * FMath::RoundToFloat( DragSize.Y/SnapSize );
// Enforce min/max sizing
const FVector2D MinSize = GetNodeMinimumSize();
SnappedSize.X = FMath::Max( SnappedSize.X, MinSize.X );
SnappedSize.Y = FMath::Max( SnappedSize.Y, MinSize.Y );
const FVector2D MaxSize = GetNodeMaximumSize();
SnappedSize.X = FMath::Min( SnappedSize.X, MaxSize.X );
SnappedSize.Y = FMath::Min( SnappedSize.Y, MaxSize.Y );
FVector2D DeltaNodePos(0,0);
if( UserSize != SnappedSize )
{
//Modify node position (resizing top and left sides)
if( MouseZone != CRWZ_BottomBorder && MouseZone != CRWZ_RightBorder && MouseZone != CRWZ_BottomRightBorder )
{
//Delta value to move graph node position
DeltaNodePos = UserSize - SnappedSize;
//Clamp position delta based on resizing direction
if( MouseZone == CRWZ_BottomLeftBorder )
{
DeltaNodePos.Y = 0.0f;
}
else if( MouseZone == CRWZ_TopRightBorder )
{
DeltaNodePos.X = 0.0f;
}
}
UserSize = SnappedSize;
GraphNode->ResizeNode( UserSize );
DeltaNodePos = GetCorrectedNodePosition() - GetPosition();
}
if (!ResizeTransactionPtr.IsValid() && UserSize != StoredUserSize)
{
// Start resize transaction. The transaction is started here so all MoveTo actions are captured while empty
// transactions are not created
ResizeTransactionPtr = MakeShareable(new FScopedTransaction(NSLOCTEXT("GraphEditor", "ResizeNodeAction", "Resize Node")));
}
SGraphNode::FNodeSet NodeFilter;
SGraphNode::MoveTo( GetPosition() + DeltaNodePos, NodeFilter );
}
else
{
MouseZone = FindMouseZone(LocalMouseCoordinates);
}
return SGraphNode::OnMouseMove( MyGeometry, MouseEvent );
}
void SGraphNodeResizable::InitNodeAnchorPoint()
{
NodeAnchorPoint = GetPosition();
if( (MouseZone == CRWZ_LeftBorder) || (MouseZone == CRWZ_TopBorder) || (MouseZone == CRWZ_TopLeftBorder) )
{
NodeAnchorPoint += UserSize;
}
else if( MouseZone == CRWZ_BottomLeftBorder )
{
NodeAnchorPoint.X += UserSize.X;
}
else if( MouseZone == CRWZ_TopRightBorder )
{
NodeAnchorPoint.Y += UserSize.Y;
}
}
FVector2D SGraphNodeResizable::GetCorrectedNodePosition() const
{
FVector2D CorrectedPos = NodeAnchorPoint;
if( (MouseZone == CRWZ_LeftBorder) || (MouseZone == CRWZ_TopBorder) || (MouseZone == CRWZ_TopLeftBorder) )
{
CorrectedPos -= UserSize;
}
else if( MouseZone == CRWZ_BottomLeftBorder )
{
CorrectedPos.X -= UserSize.X;
}
else if( MouseZone == CRWZ_TopRightBorder )
{
CorrectedPos.Y -= UserSize.Y;
}
return CorrectedPos;
}
SGraphNodeResizable::EResizableWindowZone SGraphNodeResizable::FindMouseZone(const FVector2D& LocalMouseCoordinates) const
{
EResizableWindowZone OutMouseZone = CRWZ_NotInWindow;
const float InverseZoomFactor = 1.0f / FMath::Min(GetOwnerPanel()->GetZoomAmount(), 1.0f);
const FSlateRect HitResultBorderSize = GetHitTestingBorder( InverseZoomFactor );
const FVector2D NodeSize = GetDesiredSize();
// Test for hit in location of 'grab' zone
if (LocalMouseCoordinates.Y > ( NodeSize.Y - HitResultBorderSize.Bottom))
{
OutMouseZone = CRWZ_BottomBorder;
}
else if (LocalMouseCoordinates.Y <= (HitResultBorderSize.Top))
{
OutMouseZone = CRWZ_TopBorder;
}
else if (LocalMouseCoordinates.Y <= (HitResultBorderSize.Top + GetTitleBarHeight()))
{
OutMouseZone = CRWZ_TitleBar;
}
if (LocalMouseCoordinates.X > (NodeSize.X - HitResultBorderSize.Right))
{
if (OutMouseZone == CRWZ_BottomBorder)
{
OutMouseZone = CRWZ_BottomRightBorder;
}
else if (OutMouseZone == CRWZ_TopBorder)
{
OutMouseZone = CRWZ_TopRightBorder;
}
else
{
OutMouseZone = CRWZ_RightBorder;
}
}
else if (LocalMouseCoordinates.X <= HitResultBorderSize.Left)
{
if (OutMouseZone == CRWZ_TopBorder)
{
OutMouseZone = CRWZ_TopLeftBorder;
}
else if (OutMouseZone == CRWZ_BottomBorder)
{
OutMouseZone = CRWZ_BottomLeftBorder;
}
else
{
OutMouseZone = CRWZ_LeftBorder;
}
}
// Test for hit on rest of frame
if (OutMouseZone == CRWZ_NotInWindow)
{
if (LocalMouseCoordinates.Y > HitResultBorderSize.Top)
{
OutMouseZone = CRWZ_InWindow;
}
else if (LocalMouseCoordinates.X > HitResultBorderSize.Left)
{
OutMouseZone = CRWZ_InWindow;
}
}
return OutMouseZone;
}
float SGraphNodeResizable::GetTitleBarHeight() const
{
// this can probably just be SGraphNode::GetTitleRect().Height()
return GraphNodeResizableDefs::DefaultTitleBarHeight;
}
FVector2D SGraphNodeResizable::GetNodeMinimumSize() const
{
return GraphNodeResizableDefs::MinNodeSize;
}
FVector2D SGraphNodeResizable::GetNodeMaximumSize() const
{
return GraphNodeResizableDefs::MaxNodeSize;
}
FSlateRect SGraphNodeResizable::GetHitTestingBorder(float InverseZoomFactor) const
{
return FSlateRect( GraphNodeResizableDefs::HitResultBorderSize.Left * InverseZoomFactor,
GraphNodeResizableDefs::HitResultBorderSize.Top * InverseZoomFactor,
GraphNodeResizableDefs::HitResultBorderSize.Right * InverseZoomFactor,
GraphNodeResizableDefs::HitResultBorderSize.Bottom * InverseZoomFactor );
}