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

695 lines
21 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SequencerHotspots.h"
#include "MVVM/Extensions/IObjectBindingExtension.h"
#include "MVVM/Extensions/ITimeDomainExtension.h"
#include "MVVM/ViewModels/SequencerEditorViewModel.h"
#include "MVVM/Selection/Selection.h"
#include "SequencerCommonHelpers.h"
#include "SequencerSettings.h"
#include "SSequencer.h"
#include "Tools/EditToolDragOperations.h"
#include "SequencerContextMenus.h"
#include "SequencerNodeTree.h"
#include "MVVM/Views/STrackAreaView.h"
#include "Tools/SequencerEditTool_Movement.h"
#include "Tools/SequencerEditTool_Selection.h"
#include "Tools/SequencerSnapField.h"
#include "Widgets/Layout/SBox.h"
#include "Styling/AppStyle.h"
#include "Channels/MovieSceneChannel.h"
#include "Channels/MovieSceneChannelProxy.h"
#include "MovieSceneTimeHelpers.h"
#include "SequencerCommonHelpers.h"
#include "ISequencerSection.h"
#include "MovieSceneSignedObject.h"
#include "MVVM/ViewModels/SectionModel.h"
#include "MVVM/ViewModels/VirtualTrackArea.h"
#define LOCTEXT_NAMESPACE "SequencerHotspots"
namespace UE
{
namespace Sequencer
{
UE_SEQUENCER_DEFINE_CASTABLE(FKeyHotspot);
UE_SEQUENCER_DEFINE_CASTABLE(FKeyBarHotspot);
UE_SEQUENCER_DEFINE_CASTABLE(FSectionEasingAreaHotspot);
UE_SEQUENCER_DEFINE_CASTABLE(FSectionEasingHandleHotspot);
UE_SEQUENCER_DEFINE_CASTABLE(FSectionHotspot);
UE_SEQUENCER_DEFINE_CASTABLE(FSectionHotspotBase);
UE_SEQUENCER_DEFINE_CASTABLE(FSectionResizeHotspot);
UE_SEQUENCER_DEFINE_VIEW_MODEL_TYPE_ID(IMouseHandlerHotspot);
FHotspotSelectionManager::FHotspotSelectionManager(const FPointerEvent* InMouseEvent, FSequencer* InSequencer)
: MouseEvent(InMouseEvent)
, Selection(InSequencer->GetViewModel()->GetSelection())
, Sequencer(InSequencer)
{
EventSuppressor = Selection->SuppressEventsLongRunning();
bForceSelect = !MouseEvent->IsControlDown();
bAddingToSelection = MouseEvent->IsShiftDown() || MouseEvent->IsControlDown();
if (MouseEvent->GetEffectingButton() != EKeys::RightMouseButton)
{
// When single-clicking without the RMB, we always wipe the current selection
ConditionallyClearSelection();
}
}
FHotspotSelectionManager::~FHotspotSelectionManager()
{
// Broadcast selection events by destroying the suppressor
EventSuppressor = nullptr;
}
void FHotspotSelectionManager::ConditionallyClearSelection()
{
if (!bAddingToSelection)
{
Selection->TrackArea.Empty();
Selection->KeySelection.Empty();
bAddingToSelection = true;
}
}
void FHotspotSelectionManager::ToggleKeys(TArrayView<const FSequencerSelectedKey> InKeys)
{
for (const FSequencerSelectedKey& Key : InKeys)
{
const bool bIsSelected = Selection->KeySelection.IsSelected(Key.KeyHandle);
if (bIsSelected && bForceSelect)
{
continue;
}
if (!bIsSelected)
{
Selection->KeySelection.Select(Key.WeakChannel.Pin(), Key.KeyHandle);
}
else
{
Selection->KeySelection.Deselect(Key.KeyHandle);
}
}
}
void FHotspotSelectionManager::ToggleModel(TSharedPtr<FViewModel> InModel)
{
const bool bIsSelected = Selection->TrackArea.IsSelected(InModel);
if (bIsSelected && bForceSelect)
{
return;
}
TSharedPtr<ISelectableExtension> Selectable = InModel->CastThisShared<ISelectableExtension>();
if (!Selectable)
{
return;
}
else if (MouseEvent->GetEffectingButton() == EKeys::RightMouseButton && !EnumHasAnyFlags(Selectable->IsSelectable(), ESelectionIntent::ContextMenu))
{
return;
}
else if (MouseEvent->GetEffectingButton() == EKeys::LeftMouseButton && !EnumHasAnyFlags(Selectable->IsSelectable(), ESelectionIntent::PersistentSelection))
{
return;
}
if (!bIsSelected)
{
Selection->TrackArea.Select(InModel);
}
else
{
Selection->TrackArea.Deselect(InModel);
}
}
void FHotspotSelectionManager::SelectKeysExclusive(TArrayView<const FSequencerSelectedKey> InKeys)
{
for (const FSequencerSelectedKey& Key : InKeys)
{
if (!Selection->KeySelection.IsSelected(Key.KeyHandle))
{
ConditionallyClearSelection();
Selection->KeySelection.Select(Key.WeakChannel.Pin(), Key.KeyHandle);
}
}
}
void FHotspotSelectionManager::DefaultModelSelection(TSharedPtr<FViewModel> InModel)
{
if (bForceSelect)
{
SelectModelExclusive(InModel);
}
else if (MouseEvent->IsControlDown())
{
ToggleModel(InModel);
}
else if (MouseEvent->IsShiftDown())
{
Selection->TrackArea.Select(InModel);
}
}
void FHotspotSelectionManager::SelectModelExclusive(TSharedPtr<FViewModel> InModel)
{
if (!Selection->TrackArea.IsSelected(InModel))
{
ConditionallyClearSelection();
Selection->TrackArea.Select(InModel);
}
}
FKeyHotspot::FKeyHotspot(const TArray<FSequencerSelectedKey>& InKeys, TWeakPtr<FSequencer> InWeakSequencer)
: Keys(InKeys)
, WeakSequencer(InWeakSequencer)
{
RawKeys.Reserve(Keys.Num());
for (const FSequencerSelectedKey& Key : InKeys)
{
RawKeys.Add(Key.KeyHandle);
}
}
void FKeyHotspot::HandleMouseSelection(FHotspotSelectionManager& SelectionManager)
{
if (SelectionManager.MouseEvent->GetEffectingButton() == EKeys::RightMouseButton)
{
SelectionManager.SelectKeysExclusive(Keys.Array());
}
else
{
// On mouse click, select only keys that are unique to a section and channel, ie. don't select multiple keys on the same channel that have the same time
TArray<FSequencerSelectedKey> UniqueKeysArray;
for (const FSequencerSelectedKey& Key : Keys)
{
if (Key.IsValid())
{
bool bFoundKey = false;
for (const FSequencerSelectedKey& UniqueKey : UniqueKeysArray)
{
if (Key.Section == UniqueKey.Section && Key.WeakChannel == UniqueKey.WeakChannel)
{
bFoundKey = true;
break;
}
}
if (!bFoundKey)
{
UniqueKeysArray.Add(Key);
}
}
}
SelectionManager.ToggleKeys(UniqueKeysArray);
}
}
void FKeyHotspot::UpdateOnHover(FTrackAreaViewModel& InTrackArea) const
{
InTrackArea.AttemptToActivateTool(FSequencerEditTool_Movement::Identifier);
}
TOptional<ETimeDomain> FKeyHotspot::GetDomain() const
{
TOptional<ETimeDomain> Domain;
TSet<const FChannelModel*> VisitedChannels;
for (const FSequencerSelectedKey& Key : Keys)
{
TSharedPtr<FChannelModel> Channel = Key.WeakChannel.Pin();
if (!Channel || VisitedChannels.Contains(Channel.Get()))
{
continue;
}
VisitedChannels.Add(Channel.Get());
TViewModelPtr<ITimeDomainExtension> TimeDomain = Channel->FindAncestorOfType<ITimeDomainExtension>(true);
if (TimeDomain)
{
if (Domain && Domain != TimeDomain->GetDomain())
{
return TOptional<ETimeDomain>();
}
Domain = TimeDomain->GetDomain();
}
}
if (!Domain)
{
Domain = ETimeDomain::Warped;
}
return Domain;
}
TOptional<FFrameNumber> FKeyHotspot::GetTime() const
{
FFrameNumber Time = 0;
if (auto It = Keys.CreateConstIterator(); It)
{
const FSequencerSelectedKey& Key = *It;
TArrayView<const FSequencerSelectedKey> FirstKey(&Key, 1);
TArrayView<FFrameNumber> FirstKeyTime(&Time, 1);
GetKeyTimes(FirstKey, FirstKeyTime);
}
return Time;
}
bool FKeyHotspot::PopulateContextMenu(FMenuBuilder& MenuBuilder, TSharedPtr<FExtender> MenuExtender, FFrameTime MouseDownTime)
{
FKeyContextMenu::BuildMenu(MenuBuilder, MenuExtender, WeakSequencer);
return true;
}
void FKeyBarHotspot::UpdateOnHover(FTrackAreaViewModel& InTrackArea) const
{
}
TOptional<FFrameNumber> FKeyBarHotspot::GetTime() const
{
FFrameNumber Time = 0;
if (LeadingKeys.Num())
{
TArrayView<const FSequencerSelectedKey> FirstKey(&LeadingKeys.Last(), 1);
TArrayView<FFrameNumber> FirstKeyTime(&Time, 1);
GetKeyTimes(FirstKey, FirstKeyTime);
}
return Time;
}
bool FKeyBarHotspot::PopulateContextMenu(FMenuBuilder& MenuBuilder, TSharedPtr<FExtender> MenuExtender, FFrameTime MouseDownTime)
{
TArrayView<const FSequencerSelectedKey> FirstKey(&LeadingKeys.Last(), 1);
if (FirstKey.Num() == 0 || !FirstKey[0].Section)
{
return false;
}
TSharedPtr<FSequencer> Sequencer = WeakSequencer.Pin();
TSharedPtr<FSectionModel> SectionModel = Sequencer->GetNodeTree()->GetSectionModel(FirstKey[0].Section);
if (!SectionModel)
{
return false;
}
FSectionContextMenu::BuildMenu(MenuBuilder, MenuExtender, WeakSequencer, MouseDownTime);
TSharedPtr<IObjectBindingExtension> ObjectBinding = SectionModel->FindAncestorOfType<IObjectBindingExtension>();
SectionModel->GetSectionInterface()->BuildSectionContextMenu(MenuBuilder, ObjectBinding ? ObjectBinding->GetObjectGuid() : FGuid());
return true;
}
FCursorReply FKeyBarHotspot::GetCursor() const
{
return FCursorReply::Cursor(EMouseCursor::ResizeLeftRight);
}
void FKeyBarHotspot::HandleMouseSelection(FHotspotSelectionManager& SelectionManager)
{
TArrayView<const FSequencerSelectedKey> FirstKey(&LeadingKeys.Last(), 1);
if (FirstKey.Num() == 0 || !FirstKey[0].Section)
{
return;
}
TSharedPtr<FSequencer> Sequencer = WeakSequencer.Pin();
TSharedPtr<FSectionModel> SectionModel = Sequencer->GetNodeTree()->GetSectionModel(FirstKey[0].Section);
if (!SectionModel)
{
return;
}
// Base-class only handles RMB selection so that the other handles and interactive controls
// that act as hotspots and still operate correctly with Left click
if (SectionModel && SelectionManager.MouseEvent->GetEffectingButton() == EKeys::RightMouseButton)
{
SelectionManager.SelectModelExclusive(SectionModel);
}
}
TSharedPtr<ISequencerEditToolDragOperation> FKeyBarHotspot::InitiateDrag(const FPointerEvent& MouseEvent)
{
struct FKeyBarDrag : ISequencerEditToolDragOperation, UE::Sequencer::ISnapCandidate
{
FFrameTime RelativeStartTime;
TArray<FFrameTime> RelativeStartTimes;
TArray<FSequencerSelectedKey> AllLinearKeys;
TSet<FSequencerSelectedKey> AllKeys;
TSharedPtr<FKeyBarHotspot> Hotspot;
TUniquePtr<FScopedTransaction> Transaction;
TSet<UObject*> ModifiedObjects;
FSequencerSnapField SnapField;
FKeyBarDrag(TSharedPtr<FKeyBarHotspot> InHotspot, TSharedPtr<FSequencer> InSequencer)
: Hotspot(InHotspot)
{
AllKeys.Append(Hotspot->LeadingKeys);
AllKeys.Append(Hotspot->TrailingKeys);
FSequencerSelectedKey::AppendKeySelection(AllKeys, InSequencer->GetViewModel()->GetSelection()->KeySelection);
AllLinearKeys = AllKeys.Array();
SnapField = FSequencerSnapField(*InSequencer, *this);
SnapField.SetSnapToInterval(InSequencer->GetSequencerSettings()->GetForceWholeFrames());
}
void OnBeginDrag(const FPointerEvent& MouseEvent, FVector2D LocalMousePos, const FVirtualTrackArea& VirtualTrackArea) override
{
RelativeStartTime = VirtualTrackArea.PixelToFrame(LocalMousePos.X);
// Cache off the starting times
TArray<FFrameNumber> AbsoluteStartTimes;
AbsoluteStartTimes.SetNumUninitialized(AllLinearKeys.Num());
RelativeStartTimes.SetNumUninitialized(AllLinearKeys.Num());
// Make the times relative to the initial drag position
GetKeyTimes(AllLinearKeys, AbsoluteStartTimes);
for (int32 Index = 0; Index < AbsoluteStartTimes.Num() ; ++Index)
{
RelativeStartTimes[Index] = (AbsoluteStartTimes[Index] - RelativeStartTime);
}
Transaction = MakeUnique<FScopedTransaction>(LOCTEXT("DragKeyBarTransaction", "Move Keys"));
for (const FSequencerSelectedKey& Key : AllLinearKeys)
{
TSharedPtr<FChannelModel> Channel = Key.WeakChannel.Pin();
UObject* OwningObject = Channel ? Channel->GetOwningObject() : nullptr;
if (OwningObject && !ModifiedObjects.Contains(OwningObject))
{
ModifiedObjects.Add(OwningObject);
}
}
}
void OnDrag(const FPointerEvent& MouseEvent, FVector2D LocalMousePos, const FVirtualTrackArea& VirtualTrackArea) override
{
UE::MovieScene::FScopedSignedObjectModifyDefer Defer(true);
const FFrameTime NewTime = VirtualTrackArea.PixelToFrame(LocalMousePos.X);
for (UObject* Object : ModifiedObjects)
{
Object->Modify();
}
// Set the position of leading and trailing keys
TArray<FFrameTime> NewKeyTimes;
NewKeyTimes.SetNumUninitialized(RelativeStartTimes.Num());
for (int32 Index = 0; Index < RelativeStartTimes.Num(); ++Index)
{
NewKeyTimes[Index] = NewTime + RelativeStartTimes[Index];
}
if (Hotspot.IsValid() && Hotspot->WeakSequencer.IsValid() && Hotspot->WeakSequencer.Pin()->GetSequencerSettings()->GetIsSnapEnabled())
{
const float PixelSnapWidth = 20.f;
const int32 SnapThreshold = VirtualTrackArea.PixelDeltaToFrame(PixelSnapWidth).FloorToFrame().Value;
if (TOptional<FSequencerSnapField::FSnapResult> SnappedTime = SnapField.Snap(NewKeyTimes, SnapThreshold))
{
FFrameTime SnappedDiff = SnappedTime->SnappedTime - SnappedTime->OriginalTime;
for (int32 Index = 0; Index < RelativeStartTimes.Num(); ++Index)
{
NewKeyTimes[Index] = NewKeyTimes[Index] + SnappedDiff;
}
}
}
TArray<FFrameNumber> NewKeyFrames;
NewKeyFrames.SetNumUninitialized(NewKeyTimes.Num());
for (int32 Index = 0; Index < NewKeyTimes.Num(); ++Index)
{
NewKeyFrames[Index] = NewKeyTimes[Index].RoundToFrame();
}
SetKeyTimes(AllLinearKeys, NewKeyFrames);
}
void OnEndDrag( const FPointerEvent& MouseEvent, FVector2D LocalMousePos, const FVirtualTrackArea& VirtualTrackArea) override
{
Transaction.Reset();
}
FCursorReply GetCursor() const override
{
return FCursorReply::Cursor(EMouseCursor::ResizeLeftRight);
}
int32 OnPaint(const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId) const override
{
return LayerId;
}
bool IsKeyApplicable(FKeyHandle KeyHandle, const UE::Sequencer::FViewModelPtr& Owner) const override
{
using namespace UE::Sequencer;
TSharedPtr<FChannelModel> Channel = Owner.ImplicitCast();
return Channel && !AllKeys.Contains(FSequencerSelectedKey(*Channel->GetSection(), Channel, KeyHandle));
}
bool AreSectionBoundsApplicable(UMovieSceneSection* Section) const override
{
return true;
}
};
return MakeShared<FKeyBarDrag>(SharedThis(this), WeakSequencer.Pin());
}
UMovieSceneSection* FSectionHotspotBase::GetSection() const
{
if (TSharedPtr<FSectionModel> Model = WeakSectionModel.Pin())
{
return Model->GetSection();
}
return nullptr;
}
void FSectionHotspotBase::HandleMouseSelection(FHotspotSelectionManager& SelectionManager)
{
// Base-class only handles RMB selection so that the other handles and interactive controls
// that act as hotspots and still operate correctly with Left click
TSharedPtr<FSectionModel> Section = WeakSectionModel.Pin();
if (Section && SelectionManager.MouseEvent->GetEffectingButton() == EKeys::RightMouseButton)
{
SelectionManager.SelectModelExclusive(Section);
}
}
TOptional<FFrameNumber> FSectionHotspotBase::GetTime() const
{
UMovieSceneSection* ThisSection = GetSection();
return ThisSection && ThisSection->HasStartFrame() ? ThisSection->GetInclusiveStartFrame() : TOptional<FFrameNumber>();
}
TOptional<FFrameTime> FSectionHotspotBase::GetOffsetTime() const
{
UMovieSceneSection* ThisSection = GetSection();
return ThisSection ? ThisSection->GetOffsetTime() : TOptional<FFrameTime>();
}
void FSectionHotspotBase::UpdateOnHover(FTrackAreaViewModel& InTrackArea) const
{
UMovieSceneSection* ThisSection = GetSection();
// Move sections if they are selected
TSharedPtr<ISequencer> Sequencer = WeakSequencer.Pin();
if (Sequencer->GetViewModel()->GetSelection()->TrackArea.IsSelected(WeakSectionModel))
{
if (ThisSection && ThisSection->GetRange() != TRange<FFrameNumber>::All())
{
InTrackArea.AttemptToActivateTool(FSequencerEditTool_Movement::Identifier);
}
}
else if (ThisSection)
{
// Activate selection mode if the section has keys
for (const FMovieSceneChannelEntry& Entry : ThisSection->GetChannelProxy().GetAllEntries())
{
for (const FMovieSceneChannel* Channel : Entry.GetChannels())
{
if (Channel && Channel->GetNumKeys() != 0)
{
InTrackArea.AttemptToActivateTool(FSequencerEditTool_Selection::Identifier);
return;
}
}
}
if (ThisSection->GetRange() != TRange<FFrameNumber>::All())
{
InTrackArea.AttemptToActivateTool(FSequencerEditTool_Movement::Identifier);
}
}
}
bool FSectionHotspotBase::PopulateContextMenu(FMenuBuilder& MenuBuilder, TSharedPtr<FExtender> MenuExtender, FFrameTime MouseDownTime)
{
TSharedPtr<FSectionModel> SectionModel = WeakSectionModel.Pin();
UMovieSceneSection* ThisSection = SectionModel ? SectionModel->GetSection() : nullptr;
if (ThisSection)
{
FSectionContextMenu::BuildMenu(MenuBuilder, MenuExtender, WeakSequencer, MouseDownTime);
TSharedPtr<IObjectBindingExtension> ObjectBinding = SectionModel->FindAncestorOfType<IObjectBindingExtension>();
SectionModel->GetSectionInterface()->BuildSectionContextMenu(MenuBuilder, ObjectBinding ? ObjectBinding->GetObjectGuid() : FGuid());
}
return true;
}
void FSectionHotspot::HandleMouseSelection(FHotspotSelectionManager& SelectionManager)
{
TSharedPtr<FSectionModel> Section = WeakSectionModel.Pin();
if (Section && SelectionManager.MouseEvent->GetEffectingButton() == EKeys::LeftMouseButton)
{
SelectionManager.ToggleModel(Section);
}
else
{
FSectionHotspotBase::HandleMouseSelection(SelectionManager);
}
}
TOptional<FFrameNumber> FSectionResizeHotspot::GetTime() const
{
UMovieSceneSection* ThisSection = GetSection();
if (!ThisSection)
{
return TOptional<FFrameNumber>();
}
return HandleType == Left ? ThisSection->GetInclusiveStartFrame() : ThisSection->GetExclusiveEndFrame();
}
void FSectionResizeHotspot::UpdateOnHover(FTrackAreaViewModel& InTrackArea) const
{
InTrackArea.AttemptToActivateTool(FSequencerEditTool_Movement::Identifier);
}
TSharedPtr<ISequencerEditToolDragOperation> FSectionResizeHotspot::InitiateDrag(const FPointerEvent& MouseEvent)
{
TSharedPtr<FSequencer> Sequencer = WeakSequencer.Pin();
TSharedPtr<FSequencerSelection> Selection = Sequencer->GetViewModel()->GetSelection();
if (!Selection->TrackArea.IsSelected(WeakSectionModel))
{
FSelectionEventSuppressor SuppressEvents = Selection->SuppressEvents();
Selection->Empty();
Selection->TrackArea.Select(WeakSectionModel);
}
const bool bIsSlipping = false;
return MakeShareable( new FResizeSection(*Sequencer, HandleType == Right, bIsSlipping) );
}
const FSlateBrush* FSectionResizeHotspot::GetCursorDecorator(const FGeometry& MyGeometry, const FPointerEvent& CursorEvent) const
{
if (CursorEvent.IsControlDown())
{
return FAppStyle::Get().GetBrush(TEXT("Sequencer.CursorDecorator_Retime"));
}
else
{
return ITrackAreaHotspot::GetCursorDecorator(MyGeometry, CursorEvent);
}
}
TOptional<FFrameNumber> FSectionEasingHandleHotspot::GetTime() const
{
UMovieSceneSection* ThisSection = GetSection();
if (ThisSection)
{
if (HandleType == ESequencerEasingType::In && !ThisSection->GetEaseInRange().IsEmpty())
{
return ThisSection->GetEaseInRange().GetUpperBoundValue();
}
else if (HandleType == ESequencerEasingType::Out && !ThisSection->GetEaseOutRange().IsEmpty())
{
return ThisSection->GetEaseOutRange().GetLowerBoundValue();
}
}
return TOptional<FFrameNumber>();
}
void FSectionEasingHandleHotspot::UpdateOnHover(FTrackAreaViewModel& InTrackArea) const
{
InTrackArea.AttemptToActivateTool(FSequencerEditTool_Movement::Identifier);
}
bool FSectionEasingHandleHotspot::PopulateContextMenu(FMenuBuilder& MenuBuilder, TSharedPtr<FExtender> MenuExtender, FFrameTime MouseDownTime)
{
FEasingContextMenu::BuildMenu(MenuBuilder, MenuExtender, { FEasingAreaHandle{WeakSectionModel, HandleType} }, WeakSequencer, MouseDownTime);
return true;
}
TSharedPtr<ISequencerEditToolDragOperation> FSectionEasingHandleHotspot::InitiateDrag(const FPointerEvent& MouseEvent)
{
UMovieSceneSection* Section = GetSection();
TSharedPtr<ISequencer> Sequencer = WeakSequencer.Pin();
return MakeShareable( new FManipulateSectionEasing(*static_cast<FSequencer*>(Sequencer.Get()), Section, HandleType == ESequencerEasingType::In) );
}
const FSlateBrush* FSectionEasingHandleHotspot::GetCursorDecorator(const FGeometry& MyGeometry, const FPointerEvent& CursorEvent) const
{
return FAppStyle::Get().GetBrush(TEXT("Sequencer.CursorDecorator_EasingHandle"));
}
bool FSectionEasingAreaHotspot::PopulateContextMenu(FMenuBuilder& MenuBuilder, TSharedPtr<FExtender> MenuExtender, FFrameTime MouseDownTime)
{
using namespace UE::Sequencer;
TSharedPtr<FSequencer> Sequencer = WeakSequencer.Pin();
FEasingContextMenu::BuildMenu(MenuBuilder, MenuExtender, Easings, WeakSequencer, MouseDownTime);
TSharedPtr<FSectionModel> SectionModel = WeakSectionModel.Pin();
UMovieSceneSection* ThisSection = SectionModel ? SectionModel->GetSection() : nullptr;
if (ThisSection)
{
TSharedPtr<IObjectBindingExtension> ObjectBinding = SectionModel->FindAncestorOfType<IObjectBindingExtension>();
SectionModel->GetSectionInterface()->BuildSectionContextMenu(MenuBuilder, ObjectBinding ? ObjectBinding->GetObjectGuid() : FGuid());
}
return true;
}
void FSectionEasingAreaHotspot::HandleMouseSelection(FHotspotSelectionManager& SelectionManager)
{
TSharedPtr<FSectionModel> Section = WeakSectionModel.Pin();
if (Section && SelectionManager.MouseEvent->GetEffectingButton() == EKeys::LeftMouseButton)
{
SelectionManager.ToggleModel(Section);
}
else
{
FSectionHotspotBase::HandleMouseSelection(SelectionManager);
}
}
bool FSectionEasingAreaHotspot::Contains(UMovieSceneSection* InSection) const
{
return Easings.ContainsByPredicate([=](const FEasingAreaHandle& InHandle){ return InHandle.WeakSectionModel.IsValid() && InHandle.WeakSectionModel.Pin()->GetSection() == InSection; });
}
} // namespace Sequencer
} // namespace UE
#undef LOCTEXT_NAMESPACE