Files
UnrealEngineUWP/Engine/Source/Editor/Sequencer/Private/SSequencerTrackArea.cpp
Patrick Boutot aa18ef4a11 Slate: Deprecate SLATE_SUPPORTS_SLOT. SLATE_SLOT_ARGUMENT should now be use. The new macro support FSlotArguments. That let us create slot with TAttribute that can be transform into SlateAttribute.
Deprecate TAlignmentWidgetSlotMixin old declartive function. They were not invalidating the widget.
Deprecate TPanelChildren.Add and Insert function. We now use TUniquePtr, the ownership of the Slot wouldn't exist outside of the life of the slot.
#jira UE-109145
#preflight 60c262b49e139d000114edda

[CL 16639956 by Patrick Boutot in ue5-main branch]
2021-06-11 07:54:18 -04:00

604 lines
19 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 "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() && 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() && DroppedNode.Pin()->GetType() == ESequencerNode::Track && Sequencer.IsValid())
{
TSharedPtr<FSequencerTrackNode> TrackNode = StaticCastSharedPtr<FSequencerTrackNode>(DroppedNode.Pin());
UMovieSceneTrack* Track = TrackNode->GetTrack();
int32 RowIndex = TrackNode->GetSubTrackMode() == FSequencerTrackNode::ESubTrackMode::SubTrack ? TrackNode->GetRowIndex() : 0;
FGuid ObjectBinding;
TSharedPtr<FSequencerObjectBindingNode> ObjectBindingNode = TrackNode->FindParentObjectBindingNode();
if (ObjectBindingNode.IsValid())
{
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;
}
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() && DroppedNode.Pin()->GetType() == ESequencerNode::Track && Sequencer.IsValid())
{
TSharedPtr<FSequencerTrackNode> TrackNode = StaticCastSharedPtr<FSequencerTrackNode>(DroppedNode.Pin());
UMovieSceneTrack* Track = TrackNode->GetTrack();
int32 RowIndex = TrackNode->GetSubTrackMode() == FSequencerTrackNode::ESubTrackMode::SubTrack ? TrackNode->GetRowIndex() : 0;
FGuid ObjectBinding;
TSharedPtr<FSequencerObjectBindingNode> ObjectBindingNode = TrackNode->FindParentObjectBindingNode();
if (ObjectBindingNode.IsValid())
{
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;
}
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);
}