Files
UnrealEngineUWP/Engine/Source/Editor/Sequencer/Private/SSequencerTrackArea.cpp
aurel cordonnier a12d56ff31 Merge from Release-Engine-Staging @ 17791557 to Release-Engine-Test
This represents UE4/Main @17774255, Release-5.0 @17791557 and Dev-PerfTest @17789485

[CL 17794212 by aurel cordonnier in ue5-release-engine-test branch]
2021-10-12 21:21:22 -04:00

641 lines
20 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SSequencerTrackArea.h"
#include "Types/PaintArgs.h"
#include "Layout/ArrangedChildren.h"
#include "Rendering/DrawElements.h"
#include "Layout/LayoutUtils.h"
#include "Widgets/SWeakWidget.h"
#include "EditorStyleSet.h"
#include "SSequencerTrackLane.h"
#include "SSequencerTreeView.h"
#include "ISequencerHotspot.h"
#include "SequencerHotspots.h"
#include "Tools/SequencerEditTool_Movement.h"
#include "Tools/SequencerEditTool_Selection.h"
#include "ISequencerTrackEditor.h"
#include "DisplayNodes/SequencerTrackNode.h"
#include "DisplayNodes/SequencerObjectBindingNode.h"
#include "CommonMovieSceneTools.h"
#include "Framework/Application/SlateApplication.h"
#include "Styling/StyleColors.h"
FTrackAreaSlot::FTrackAreaSlot(const TSharedPtr<SSequencerTrackLane>& InSlotContent)
: TAlignmentWidgetSlotMixin<FTrackAreaSlot>(HAlign_Fill, VAlign_Top)
{
TrackLane = InSlotContent;
this->AttachWidget(
SNew(SWeakWidget)
.PossiblyNullContent(InSlotContent)
);
}
float FTrackAreaSlot::GetVerticalOffset() const
{
auto PinnedTrackLane = TrackLane.Pin();
return PinnedTrackLane.IsValid() ? PinnedTrackLane->GetPhysicalPosition() : 0.f;
}
void SSequencerTrackArea::Construct(const FArguments& InArgs, TSharedRef<FSequencerTimeSliderController> InTimeSliderController, TSharedRef<FSequencer> InSequencer)
{
Sequencer = InSequencer;
TimeSliderController = InTimeSliderController;
bShowPinnedNodes = false;
// Input stack in order or priority
// Space for the edit tool
InputStack.AddHandler(nullptr);
// The time slider controller
InputStack.AddHandler(TimeSliderController.Get());
EditTools.Add(MakeShared<FSequencerEditTool_Selection>(*InSequencer, *this));
EditTools.Add(MakeShared<FSequencerEditTool_Movement>(*InSequencer));
}
void SSequencerTrackArea::SetTreeView(const TSharedPtr<SSequencerTreeView>& InTreeView)
{
TreeView = InTreeView;
}
void SSequencerTrackArea::Empty()
{
TrackSlots.Empty();
Children.Empty();
}
void SSequencerTrackArea::AddTrackSlot(const TSharedRef<FSequencerDisplayNode>& InNode, const TSharedPtr<SSequencerTrackLane>& InSlot)
{
TrackSlots.Add(InNode, InSlot);
Children.AddSlot(FTrackAreaSlot::FSlotArguments(MakeUnique<FTrackAreaSlot>(InSlot)));
}
TSharedPtr<SSequencerTrackLane> SSequencerTrackArea::FindTrackSlot(const TSharedRef<FSequencerDisplayNode>& InNode)
{
return TrackSlots.FindRef(InNode).Pin();
}
void SSequencerTrackArea::OnArrangeChildren( const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren ) const
{
for (int32 ChildIndex = 0; ChildIndex < Children.Num(); ++ChildIndex)
{
const FTrackAreaSlot& CurChild = Children[ChildIndex];
const EVisibility ChildVisibility = CurChild.GetWidget()->GetVisibility();
if (!ArrangedChildren.Accepts(ChildVisibility))
{
continue;
}
TSharedPtr<SSequencerTrackLane> PinnedTrackLane = CurChild.TrackLane.Pin();
if (!PinnedTrackLane.IsValid() || PinnedTrackLane->IsPinned() != bShowPinnedNodes)
{
continue;
}
const FMargin Padding(0, CurChild.GetVerticalOffset(), 0, 0);
AlignmentArrangeResult XResult = AlignChild<Orient_Horizontal>(AllottedGeometry.GetLocalSize().X, CurChild, Padding, 1.0f, false);
AlignmentArrangeResult YResult = AlignChild<Orient_Vertical>(AllottedGeometry.GetLocalSize().Y, CurChild, Padding, 1.0f, false);
ArrangedChildren.AddWidget(ChildVisibility,
AllottedGeometry.MakeChild(
CurChild.GetWidget(),
FVector2D(XResult.Offset,YResult.Offset),
FVector2D(XResult.Size, YResult.Size)
)
);
}
}
FVector2D SSequencerTrackArea::ComputeDesiredSize( float ) const
{
FVector2D MaxSize(0,0);
for (int32 ChildIndex = 0; ChildIndex < Children.Num(); ++ChildIndex)
{
const FTrackAreaSlot& CurChild = Children[ChildIndex];
const EVisibility ChildVisibilty = CurChild.GetWidget()->GetVisibility();
if (ChildVisibilty != EVisibility::Collapsed)
{
FVector2D ChildDesiredSize = CurChild.GetWidget()->GetDesiredSize();
MaxSize.X = FMath::Max(MaxSize.X, ChildDesiredSize.X);
MaxSize.Y = FMath::Max(MaxSize.Y, ChildDesiredSize.Y);
}
}
return MaxSize;
}
FChildren* SSequencerTrackArea::GetChildren()
{
return &Children;
}
int32 SSequencerTrackArea::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
{
if ( Sequencer.IsValid() )
{
// give track editors a chance to paint
auto TrackEditors = Sequencer.Pin()->GetTrackEditors();
for (const auto& TrackEditor : TrackEditors)
{
LayerId = TrackEditor->PaintTrackArea(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId + 1, InWidgetStyle);
}
// paint the child widgets
FArrangedChildren ArrangedChildren(EVisibility::Visible);
ArrangeChildren(AllottedGeometry, ArrangedChildren);
const FPaintArgs NewArgs = Args.WithNewParent(this);
for (int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ++ChildIndex)
{
FArrangedWidget& CurWidget = ArrangedChildren[ChildIndex];
FSlateRect ChildClipRect = MyCullingRect.IntersectionWith( CurWidget.Geometry.GetLayoutBoundingRect() );
const int32 ThisWidgetLayerId = CurWidget.Widget->Paint( NewArgs, CurWidget.Geometry, ChildClipRect, OutDrawElements, LayerId + 2, InWidgetStyle, ShouldBeEnabled( bParentEnabled ) );
LayerId = FMath::Max(LayerId, ThisWidgetLayerId);
}
if (EditTool.IsValid())
{
LayerId = EditTool->OnPaint(AllottedGeometry, MyCullingRect, OutDrawElements, LayerId + 1);
}
TOptional<FHighlightRegion> HighlightRegion = TreeView.Pin()->GetHighlightRegion();
if (HighlightRegion.IsSet())
{
FSlateDrawElement::MakeBox(
OutDrawElements,
LayerId+1,
AllottedGeometry.ToPaintGeometry(FVector2D(0.f, HighlightRegion->Top - 4.f), FVector2D(AllottedGeometry.GetLocalSize().X, 4.f)),
FEditorStyle::GetBrush("Sequencer.TrackHoverHighlight_Top"),
ESlateDrawEffect::None,
FLinearColor::Black
);
FSlateDrawElement::MakeBox(
OutDrawElements,
LayerId+1,
AllottedGeometry.ToPaintGeometry(FVector2D(0.f, HighlightRegion->Bottom), FVector2D(AllottedGeometry.GetLocalSize().X, 4.f)),
FEditorStyle::GetBrush("Sequencer.TrackHoverHighlight_Bottom"),
ESlateDrawEffect::None,
FLinearColor::Black
);
}
// Draw drop target
if (DroppedNode.IsValid() && DroppedNode.Pin()->GetType() != ESequencerNode::Spacer && TrackSlots.Contains(DroppedNode.Pin()))
{
TSharedPtr<SSequencerTrackLane> TrackLane = TrackSlots.FindRef(DroppedNode.Pin()).Pin();
FGeometry NodeGeometry = TrackLane.Get()->GetCachedGeometry();
FSlateColor DashColor = bAllowDrop ? FStyleColors::AccentBlue : FStyleColors::Error;
const FSlateBrush* HorizontalBrush = FEditorStyle::GetBrush("WideDash.Horizontal");
const FSlateBrush* VerticalBrush = FEditorStyle::GetBrush("WideDash.Vertical");
int32 DashLayer = LayerId + 1;
float DropMinX = 0.f;
float DropMaxX = NodeGeometry.GetLocalSize().X;
if (DropFrameRange.IsSet())
{
FTimeToPixel TimeToPixel(NodeGeometry, Sequencer.Pin()->GetViewRange(), Sequencer.Pin()->GetFocusedTickResolution());
DropMinX = TimeToPixel.FrameToPixel(DropFrameRange.GetValue().GetLowerBoundValue());
DropMaxX = TimeToPixel.FrameToPixel(DropFrameRange.GetValue().GetUpperBoundValue());
}
// Top
FSlateDrawElement::MakeBox(
OutDrawElements,
DashLayer,
AllottedGeometry.ToPaintGeometry(FVector2D(DropMinX, TrackLane.Get()->GetPhysicalPosition()), FVector2D(DropMaxX-DropMinX, HorizontalBrush->ImageSize.Y)),
HorizontalBrush,
ESlateDrawEffect::None,
DashColor.GetSpecifiedColor());
// Bottom
FSlateDrawElement::MakeBox(
OutDrawElements,
DashLayer,
AllottedGeometry.ToPaintGeometry(FVector2D(DropMinX, TrackLane.Get()->GetPhysicalPosition() + (TrackLane.Get()->GetCachedGeometry().GetLocalSize().Y - HorizontalBrush->ImageSize.Y)), FVector2D(DropMaxX-DropMinX, HorizontalBrush->ImageSize.Y)),
HorizontalBrush,
ESlateDrawEffect::None,
DashColor.GetSpecifiedColor());
// Left
FSlateDrawElement::MakeBox(
OutDrawElements,
DashLayer,
AllottedGeometry.ToPaintGeometry(FVector2D(DropMinX, TrackLane.Get()->GetPhysicalPosition()), FVector2D(VerticalBrush->ImageSize.X, TrackLane.Get()->GetCachedGeometry().GetLocalSize().Y)),
VerticalBrush,
ESlateDrawEffect::None,
DashColor.GetSpecifiedColor());
// Right
FSlateDrawElement::MakeBox(
OutDrawElements,
DashLayer,
AllottedGeometry.ToPaintGeometry(FVector2D(DropMaxX - VerticalBrush->ImageSize.X, TrackLane.Get()->GetPhysicalPosition()), FVector2D(VerticalBrush->ImageSize.X, TrackLane.Get()->GetCachedGeometry().GetLocalSize().Y)),
VerticalBrush,
ESlateDrawEffect::None,
DashColor.GetSpecifiedColor());
}
}
return LayerId;
}
FReply SSequencerTrackArea::OnMouseButtonDown( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
if ( Sequencer.IsValid() )
{
// Always ensure the edit tool is set up
InputStack.SetHandlerAt(0, EditTool.Get());
return InputStack.HandleMouseButtonDown(*this, MyGeometry, MouseEvent);
}
return FReply::Unhandled();
}
FReply SSequencerTrackArea::OnMouseButtonUp( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
if ( Sequencer.IsValid() )
{
FContextMenuSuppressor SuppressContextMenus(TimeSliderController.ToSharedRef());
// Always ensure the edit tool is set up
InputStack.SetHandlerAt(0, EditTool.Get());
return InputStack.HandleMouseButtonUp(*this, MyGeometry, MouseEvent);
}
return FReply::Unhandled();
}
bool SSequencerTrackArea::CanActivateEditTool(FName Identifier) const
{
auto IdentifierIsFound = [=](TSharedPtr<ISequencerEditTool> InEditTool){ return InEditTool->GetIdentifier() == Identifier; };
if (InputStack.GetCapturedIndex() != INDEX_NONE)
{
return false;
}
else if (!EditTool.IsValid())
{
return EditTools.ContainsByPredicate(IdentifierIsFound);
}
// Can't activate a tool that's already active
else if (EditTool->GetIdentifier() == Identifier)
{
return false;
}
// Can only activate a new tool if the current one will let us
else
{
return EditTool->CanDeactivate() && EditTools.ContainsByPredicate(IdentifierIsFound);
}
}
bool SSequencerTrackArea::AttemptToActivateTool(FName Identifier)
{
if ( Sequencer.IsValid() && CanActivateEditTool(Identifier) )
{
EditTool = *EditTools.FindByPredicate([=](TSharedPtr<ISequencerEditTool> InEditTool){ return InEditTool->GetIdentifier() == Identifier; });
return true;
}
return false;
}
void SSequencerTrackArea::UpdateHoverStates( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
TSharedPtr<SSequencerTreeView> PinnedTreeView = TreeView.Pin();
// Set the node that we are hovering
TSharedPtr<FSequencerDisplayNode> NewHoveredNode = PinnedTreeView->HitTestNode(MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition()).Y);
PinnedTreeView->GetNodeTree()->SetHoveredNode(NewHoveredNode);
if ( Sequencer.IsValid() )
{
TSharedPtr<ISequencerHotspot> Hotspot = Sequencer.Pin()->GetHotspot();
if (Hotspot.IsValid())
{
Hotspot->UpdateOnHover(*this, *Sequencer.Pin());
return;
}
}
// Any other region implies selection mode
AttemptToActivateTool(FSequencerEditTool_Selection::Identifier);
}
FReply SSequencerTrackArea::OnMouseMove( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
if ( Sequencer.IsValid() )
{
UpdateHoverStates(MyGeometry, MouseEvent);
// Always ensure the edit tool is set up
InputStack.SetHandlerAt(0, EditTool.Get());
FReply Reply = InputStack.HandleMouseMove(*this, MyGeometry, MouseEvent);
// Handle right click scrolling on the track area, if the captured index is that of the time slider
if (Reply.IsEventHandled() && InputStack.GetCapturedIndex() == 1)
{
if (MouseEvent.IsMouseButtonDown(EKeys::RightMouseButton) && HasMouseCapture())
{
TreeView.Pin()->ScrollByDelta(-MouseEvent.GetCursorDelta().Y);
}
}
return Reply;
}
return FReply::Unhandled();
}
FReply SSequencerTrackArea::OnMouseWheel( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
{
if ( Sequencer.IsValid() )
{
// Always ensure the edit tool is set up
InputStack.SetHandlerAt(0, EditTool.Get());
FReply EditToolHandle = InputStack.HandleMouseWheel(*this, MyGeometry, MouseEvent);
if (EditToolHandle.IsEventHandled())
{
return EditToolHandle;
}
const float ScrollAmount = -MouseEvent.GetWheelDelta() * GetGlobalScrollAmount();
Sequencer.Pin()->VerticalScroll(ScrollAmount);
return FReply::Handled();
}
return FReply::Unhandled();
}
void SSequencerTrackArea::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
DroppedNode.Reset();
bAllowDrop = false;
DropFrameRange.Reset();
if ( Sequencer.IsValid() )
{
if (EditTool.IsValid())
{
EditTool->OnMouseEnter(*this, MyGeometry, MouseEvent);
}
}
}
void SSequencerTrackArea::OnMouseLeave(const FPointerEvent& MouseEvent)
{
if ( Sequencer.IsValid() )
{
if (EditTool.IsValid())
{
EditTool->OnMouseLeave(*this, MouseEvent);
}
TreeView.Pin()->GetNodeTree()->SetHoveredNode(nullptr);
}
}
void SSequencerTrackArea::OnMouseCaptureLost(const FCaptureLostEvent& CaptureLostEvent)
{
if ( Sequencer.IsValid() )
{
if (EditTool.IsValid())
{
EditTool->OnMouseCaptureLost();
}
}
}
FCursorReply SSequencerTrackArea::OnCursorQuery( const FGeometry& MyGeometry, const FPointerEvent& CursorEvent ) const
{
if ( Sequencer.IsValid() )
{
if (CursorEvent.IsMouseButtonDown(EKeys::RightMouseButton) && HasMouseCapture())
{
return FCursorReply::Cursor(EMouseCursor::GrabHandClosed);
}
if (EditTool.IsValid())
{
return EditTool->OnCursorQuery(MyGeometry, CursorEvent);
}
}
return FCursorReply::Unhandled();
}
void SSequencerTrackArea::Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime )
{
FVector2D Size = AllottedGeometry.GetLocalSize();
if (!IsSlave() && SizeLastFrame.IsSet() && Size.X != SizeLastFrame->X)
{
// Zoom by the difference in horizontal size
const float Difference = Size.X - SizeLastFrame->X;
TRange<double> OldRange = TimeSliderController->GetViewRange().GetAnimationTarget();
double NewRangeMin = OldRange.GetLowerBoundValue();
double NewRangeMax = OldRange.GetUpperBoundValue() + (Difference * OldRange.Size<double>() / SizeLastFrame->X);
TRange<double> ClampRange = TimeSliderController->GetClampRange();
if (NewRangeMin < ClampRange.GetLowerBoundValue() || NewRangeMax > ClampRange.GetUpperBoundValue())
{
double NewClampRangeMin = NewRangeMin < ClampRange.GetLowerBoundValue() ? NewRangeMin : ClampRange.GetLowerBoundValue();
double NewClampRangeMax = NewRangeMax > ClampRange.GetUpperBoundValue() ? NewRangeMax : ClampRange.GetUpperBoundValue();
TimeSliderController->SetClampRange(NewClampRangeMin, NewClampRangeMax);
}
TimeSliderController->SetViewRange(
NewRangeMin, NewRangeMax,
EViewRangeInterpolation::Immediate
);
}
SizeLastFrame = Size;
for (int32 Index = 0; Index < Children.Num();)
{
if (!StaticCastSharedRef<SWeakWidget>(Children[Index].GetWidget())->ChildWidgetIsValid())
{
Children.RemoveAt(Index);
}
else
{
++Index;
}
}
}
void SSequencerTrackArea::OnDragEnter(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
{
SPanel::OnDragEnter(MyGeometry, DragDropEvent);
}
void SSequencerTrackArea::OnDragLeave(const FDragDropEvent& DragDropEvent)
{
DroppedNode.Reset();
SPanel::OnDragLeave(DragDropEvent);
}
FReply SSequencerTrackArea::OnDragOver(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
{
TSharedPtr<SSequencerTreeView> PinnedTreeView = TreeView.Pin();
DroppedNode = PinnedTreeView->HitTestNode(MyGeometry.AbsoluteToLocal(DragDropEvent.GetScreenSpacePosition()).Y);
bAllowDrop = false;
DropFrameRange.Reset();
if (DroppedNode.IsValid() && Sequencer.IsValid())
{
UMovieSceneTrack* Track = nullptr;
int32 RowIndex = 0;
FGuid ObjectBinding;
if (DroppedNode.Pin()->GetType() == ESequencerNode::Track)
{
TSharedPtr<FSequencerTrackNode> TrackNode = StaticCastSharedPtr<FSequencerTrackNode>(DroppedNode.Pin());
Track = TrackNode->GetTrack();
RowIndex = TrackNode->GetSubTrackMode() == FSequencerTrackNode::ESubTrackMode::SubTrack ? TrackNode->GetRowIndex() : 0;
TSharedPtr<FSequencerObjectBindingNode> ObjectBindingNode = TrackNode->FindParentObjectBindingNode();
if (ObjectBindingNode.IsValid())
{
ObjectBinding = ObjectBindingNode->GetObjectBinding();
}
}
else if (DroppedNode.Pin()->GetType() == ESequencerNode::Object)
{
TSharedPtr<FSequencerObjectBindingNode> ObjectBindingNode = StaticCastSharedPtr<FSequencerObjectBindingNode>(DroppedNode.Pin());
ObjectBinding = ObjectBindingNode->GetObjectBinding();
}
// give track editors a chance to accept the drag event
auto TrackEditors = Sequencer.Pin()->GetTrackEditors();
FTimeToPixel TimeToPixel(MyGeometry, Sequencer.Pin()->GetViewRange(), Sequencer.Pin()->GetFocusedTickResolution());
FVector2D LocalPos = MyGeometry.AbsoluteToLocal(DragDropEvent.GetScreenSpacePosition());
FFrameNumber DropFrameNumber = TimeToPixel.PixelToFrame(LocalPos.X).FrameNumber;
if (Sequencer.Pin()->GetSequencerSettings()->GetIsSnapEnabled() && Sequencer.Pin()->GetSequencerSettings()->GetSnapPlayTimeToInterval())
{
DropFrameNumber = FFrameRate::Snap(DropFrameNumber, Sequencer.Pin()->GetFocusedTickResolution(), Sequencer.Pin()->GetFocusedDisplayRate()).FrameNumber;
}
// If shift is pressed, drop onto the current time
if (FSlateApplication::Get().GetModifierKeys().IsShiftDown())
{
DropFrameNumber = Sequencer.Pin()->GetLocalTime().Time.FrameNumber;
}
FSequencerDragDropParams DragDropParams(Track, RowIndex, ObjectBinding, DropFrameNumber, TRange<FFrameNumber>());
for (const auto& TrackEditor : TrackEditors)
{
if (TrackEditor->OnAllowDrop(DragDropEvent, DragDropParams))
{
bAllowDrop = true;
DropFrameRange = DragDropParams.FrameRange;
return FReply::Handled();
}
}
}
return SPanel::OnDragOver(MyGeometry, DragDropEvent);
}
FReply SSequencerTrackArea::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
{
TSharedPtr<SSequencerTreeView> PinnedTreeView = TreeView.Pin();
DroppedNode = PinnedTreeView->HitTestNode(MyGeometry.AbsoluteToLocal(DragDropEvent.GetScreenSpacePosition()).Y);
if (DroppedNode.IsValid() && Sequencer.IsValid())
{
UMovieSceneTrack* Track = nullptr;
int32 RowIndex = 0;
FGuid ObjectBinding;
if (DroppedNode.Pin()->GetType() == ESequencerNode::Track)
{
TSharedPtr<FSequencerTrackNode> TrackNode = StaticCastSharedPtr<FSequencerTrackNode>(DroppedNode.Pin());
Track = TrackNode->GetTrack();
RowIndex = TrackNode->GetSubTrackMode() == FSequencerTrackNode::ESubTrackMode::SubTrack ? TrackNode->GetRowIndex() : 0;
TSharedPtr<FSequencerObjectBindingNode> ObjectBindingNode = TrackNode->FindParentObjectBindingNode();
if (ObjectBindingNode.IsValid())
{
ObjectBinding = ObjectBindingNode->GetObjectBinding();
}
}
else if (DroppedNode.Pin()->GetType() == ESequencerNode::Object)
{
TSharedPtr<FSequencerObjectBindingNode> ObjectBindingNode = StaticCastSharedPtr<FSequencerObjectBindingNode>(DroppedNode.Pin());
ObjectBinding = ObjectBindingNode->GetObjectBinding();
}
// give track editors a chance to process the drag event
auto TrackEditors = Sequencer.Pin()->GetTrackEditors();
FTimeToPixel TimeToPixel(MyGeometry, Sequencer.Pin()->GetViewRange(), Sequencer.Pin()->GetFocusedTickResolution());
FVector2D LocalPos = MyGeometry.AbsoluteToLocal(DragDropEvent.GetScreenSpacePosition());
FFrameNumber DropFrameNumber = TimeToPixel.PixelToFrame(LocalPos.X).FrameNumber;
if (Sequencer.Pin()->GetSequencerSettings()->GetIsSnapEnabled() && Sequencer.Pin()->GetSequencerSettings()->GetSnapPlayTimeToInterval())
{
DropFrameNumber = FFrameRate::Snap(DropFrameNumber, Sequencer.Pin()->GetFocusedTickResolution(), Sequencer.Pin()->GetFocusedDisplayRate()).FrameNumber;
}
// If shift is pressed, drop onto the current time
if (FSlateApplication::Get().GetModifierKeys().IsShiftDown())
{
DropFrameNumber = Sequencer.Pin()->GetLocalTime().Time.FrameNumber;
}
FSequencerDragDropParams DragDropParams(Track, RowIndex, ObjectBinding, DropFrameNumber, TRange<FFrameNumber>());
for (const auto& TrackEditor : TrackEditors)
{
if (TrackEditor->OnAllowDrop(DragDropEvent, DragDropParams))
{
DroppedNode.Reset();
return TrackEditor->OnDrop(DragDropEvent, DragDropParams);
}
}
}
DroppedNode.Reset();
return SPanel::OnDrop(MyGeometry, DragDropEvent);
}