Files
UnrealEngineUWP/Engine/Source/Editor/Sequencer/Private/IKeyArea.cpp
max chen a23ba8a00f Curve Editor: Improve multi paste operation by adding an IntentName to the channel editor data.
Previously, the intent for control rigs included the control node name, ie. Foot_L.Location.X. This means it could not match Foot_R.Location.X. With this change, the intent for control rigs are filled in with just Location.X.
The intent for non control rig transforms remains the same - it includes the group and the display name. For static mesh transforms, this is Location.X.

With this change, you can now copy one node's channels to multiple objects. For example, copy Node1's Location channels and pasting them onto one or more nodes results in all of those getting Node1's Location channels.

You can also now copy multiple nodes to multiple objects in order. For example, copying Node1's Location and Node2's Location channels onto Node3 and Node4 results in Node3 getting Node1's Location channel and Node4 getting Node2's Location channel.

#jira UE-144528
#preflight 6222965f0e2d25b12b3abd97
#rb mike.zyracki

#ROBOMERGE-AUTHOR: max.chen
#ROBOMERGE-SOURCE: CL 19344823 via CL 19346627 via CL 19351368 via CL 19351426
#ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v926-19321884)

[CL 19351987 by max chen in ue5-main branch]
2022-03-11 04:29:52 -05:00

335 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "IKeyArea.h"
#include "ISequencerModule.h"
#include "Modules/ModuleManager.h"
#include "Widgets/SNullWidget.h"
#include "ISequencerChannelInterface.h"
#include "SequencerClipboardReconciler.h"
#include "Tracks/MovieScenePropertyTrack.h"
#include "Channels/MovieSceneChannel.h"
#include "Channels/MovieSceneChannelProxy.h"
#include "CurveModel.h"
#include "ISequencer.h"
#include "ISequencerSection.h"
#include "MovieSceneSequence.h"
#include "CurveEditorSettings.h"
IKeyArea::IKeyArea(TWeakPtr<ISequencerSection> InSection, FMovieSceneChannelHandle InChannel)
: TreeSerialNumber(0)
, ChannelHandle(InChannel)
{
Reinitialize(InSection, InChannel);
}
void IKeyArea::Reinitialize(TWeakPtr<ISequencerSection> InSection, FMovieSceneChannelHandle InChannel)
{
WeakSection = InSection;
ChannelHandle = InChannel;
Color = FLinearColor::White;
if (const FMovieSceneChannelMetaData* MetaData = ChannelHandle.GetMetaData())
{
Color = MetaData->Color;
ChannelName = MetaData->Name;
DisplayText = MetaData->DisplayText;
}
UMovieSceneSection* SectionObject = InSection.Pin()->GetSectionObject();
UMovieScenePropertyTrack* PropertyTrack = SectionObject->GetTypedOuter<UMovieScenePropertyTrack>();
if (PropertyTrack && PropertyTrack->GetPropertyPath() != NAME_None)
{
PropertyBindings = FTrackInstancePropertyBindings(PropertyTrack->GetPropertyName(), PropertyTrack->GetPropertyPath().ToString());
}
}
FMovieSceneChannel* IKeyArea::ResolveChannel() const
{
return ChannelHandle.Get();
}
UMovieSceneSection* IKeyArea::GetOwningSection() const
{
TSharedPtr<ISequencerSection> SectionInterface = WeakSection.Pin();
return SectionInterface ? SectionInterface->GetSectionObject() : nullptr;;
}
TSharedPtr<ISequencerSection> IKeyArea::GetSectionInterface() const
{
return WeakSection.Pin();
}
FName IKeyArea::GetName() const
{
return ChannelName;
}
void IKeyArea::SetName(FName InName)
{
ChannelName = InName;
}
ISequencerChannelInterface* IKeyArea::FindChannelEditorInterface() const
{
ISequencerModule& SequencerModule = FModuleManager::LoadModuleChecked<ISequencerModule>("Sequencer");
ISequencerChannelInterface* EditorInterface = SequencerModule.FindChannelEditorInterface(ChannelHandle.GetChannelTypeName());
ensureMsgf(EditorInterface, TEXT("No channel interface found for type '%s'. Did you forget to call ISequencerModule::RegisterChannelInterface<ChannelType>()?"), *ChannelHandle.GetChannelTypeName().ToString());
return EditorInterface;
}
FKeyHandle IKeyArea::AddOrUpdateKey(FFrameNumber Time, const FGuid& ObjectBindingID, ISequencer& InSequencer)
{
ISequencerChannelInterface* EditorInterface = FindChannelEditorInterface();
FMovieSceneChannel* Channel = ChannelHandle.Get();
UMovieSceneSection* Section = GetOwningSection();
// The extended editor data may be null, but is passed to the interface regardless
const void* RawExtendedData = ChannelHandle.GetExtendedEditorData();
if (EditorInterface && Channel)
{
FTrackInstancePropertyBindings* BindingsPtr = PropertyBindings.IsSet() ? &PropertyBindings.GetValue() : nullptr;
return EditorInterface->AddOrUpdateKey_Raw(Channel, Section, RawExtendedData, Time, InSequencer, ObjectBindingID, BindingsPtr);
}
return FKeyHandle();
}
FKeyHandle IKeyArea::DuplicateKey(FKeyHandle InKeyHandle) const
{
FKeyHandle NewHandle = FKeyHandle::Invalid();
if (FMovieSceneChannel* Channel = ChannelHandle.Get())
{
Channel->DuplicateKeys(TArrayView<const FKeyHandle>(&InKeyHandle, 1), TArrayView<FKeyHandle>(&NewHandle, 1));
}
return NewHandle;
}
void IKeyArea::SetKeyTimes(TArrayView<const FKeyHandle> InKeyHandles, TArrayView<const FFrameNumber> InKeyTimes) const
{
check(InKeyHandles.Num() == InKeyTimes.Num());
if (FMovieSceneChannel* Channel = ChannelHandle.Get())
{
Channel->SetKeyTimes(InKeyHandles, InKeyTimes);
}
}
void IKeyArea::GetKeyTimes(TArrayView<const FKeyHandle> InKeyHandles, TArrayView<FFrameNumber> OutTimes) const
{
if (FMovieSceneChannel* Channel = ChannelHandle.Get())
{
Channel->GetKeyTimes(InKeyHandles, OutTimes);
}
}
void IKeyArea::GetKeyInfo(TArray<FKeyHandle>* OutHandles, TArray<FFrameNumber>* OutTimes, const TRange<FFrameNumber>& WithinRange) const
{
if (FMovieSceneChannel* Channel = ChannelHandle.Get())
{
Channel->GetKeys(WithinRange, OutTimes, OutHandles);
}
}
TSharedPtr<FStructOnScope> IKeyArea::GetKeyStruct(FKeyHandle KeyHandle) const
{
ISequencerChannelInterface* EditorInterface = FindChannelEditorInterface();
if (EditorInterface)
{
return EditorInterface->GetKeyStruct_Raw(ChannelHandle, KeyHandle);
}
return nullptr;
}
void IKeyArea::DrawExtra(FSequencerSectionPainter& Painter, const FGeometry& KeyGeometry) const
{
ISequencerChannelInterface* EditorInterface = FindChannelEditorInterface();
if (EditorInterface)
{
FMovieSceneChannel* Channel = ChannelHandle.Get();
const UMovieSceneSection* OwningSection = GetOwningSection();
EditorInterface->DrawExtra_Raw(Channel,OwningSection, KeyGeometry,Painter);
}
}
void IKeyArea::DrawKeys(TArrayView<const FKeyHandle> InKeyHandles, TArrayView<FKeyDrawParams> OutKeyDrawParams)
{
check(InKeyHandles.Num() == OutKeyDrawParams.Num());
ISequencerChannelInterface* EditorInterface = FindChannelEditorInterface();
FMovieSceneChannel* Channel = ChannelHandle.Get();
UMovieSceneSection* OwningSection = GetOwningSection();
if (EditorInterface && Channel && OwningSection)
{
return EditorInterface->DrawKeys_Raw(Channel, InKeyHandles, OwningSection, OutKeyDrawParams);
}
}
bool IKeyArea::CanCreateKeyEditor() const
{
ISequencerChannelInterface* EditorInterface = FindChannelEditorInterface();
const FMovieSceneChannel* Channel = ChannelHandle.Get();
return EditorInterface && Channel && EditorInterface->CanCreateKeyEditor_Raw(Channel);
}
TSharedRef<SWidget> IKeyArea::CreateKeyEditor(TWeakPtr<ISequencer> Sequencer, const FGuid& ObjectBindingID)
{
ISequencerChannelInterface* EditorInterface = FindChannelEditorInterface();
UMovieSceneSection* OwningSection = GetOwningSection();
TWeakPtr<FTrackInstancePropertyBindings> PropertyBindingsPtr;
if (PropertyBindings.IsSet())
{
PropertyBindingsPtr = TSharedPtr<FTrackInstancePropertyBindings>(AsShared(), &PropertyBindings.GetValue());
}
if (EditorInterface && OwningSection)
{
return EditorInterface->CreateKeyEditor_Raw(ChannelHandle, OwningSection, ObjectBindingID, PropertyBindingsPtr, Sequencer);
}
return SNullWidget::NullWidget;
}
void IKeyArea::CopyKeys(FMovieSceneClipboardBuilder& ClipboardBuilder, TArrayView<const FKeyHandle> KeyMask) const
{
ISequencerChannelInterface* EditorInterface = FindChannelEditorInterface();
FMovieSceneChannel* Channel = ChannelHandle.Get();
UMovieSceneSection* OwningSection = GetOwningSection();
if (EditorInterface && Channel && OwningSection)
{
EditorInterface->CopyKeys_Raw(Channel, OwningSection, ChannelName, ClipboardBuilder, KeyMask);
}
}
void IKeyArea::PasteKeys(const FMovieSceneClipboardKeyTrack& KeyTrack, const FMovieSceneClipboardEnvironment& SrcEnvironment, const FSequencerPasteEnvironment& DstEnvironment)
{
ISequencerChannelInterface* EditorInterface = FindChannelEditorInterface();
FMovieSceneChannel* Channel = ChannelHandle.Get();
UMovieSceneSection* OwningSection = GetOwningSection();
if (EditorInterface && Channel && OwningSection)
{
TArray<FKeyHandle> PastedKeys;
EditorInterface->PasteKeys_Raw(Channel, OwningSection, KeyTrack, SrcEnvironment, DstEnvironment, PastedKeys);
for (FKeyHandle KeyHandle : PastedKeys)
{
DstEnvironment.ReportPastedKey(KeyHandle, *this);
}
}
}
FText GetOwningObjectBindingName(UMovieSceneTrack* InTrack, ISequencer& InSequencer)
{
check(InTrack);
UMovieSceneSequence* FocusedSequence = InSequencer.GetFocusedMovieSceneSequence();
UMovieScene* MovieScene = FocusedSequence->GetMovieScene();
FGuid PossessableGuid;
if (MovieScene->FindTrackBinding(*InTrack, PossessableGuid))
{
return MovieScene->GetObjectDisplayName(PossessableGuid);
}
// Couldn't find an owning track, so must not be nestled inside of something!
return FText();
}
TUniquePtr<FCurveModel> IKeyArea::CreateCurveEditorModel(TSharedRef<ISequencer> InSequencer) const
{
ISequencerChannelInterface* EditorInterface = FindChannelEditorInterface();
UMovieSceneSection* OwningSection = GetOwningSection();
if (EditorInterface && OwningSection && ChannelHandle.Get() != nullptr)
{
TUniquePtr<FCurveModel> CurveModel = EditorInterface->CreateCurveEditorModel_Raw(ChannelHandle, OwningSection, InSequencer);
if (CurveModel.IsValid())
{
// Build long, short and context names for this curve to maximize information shown in the Curve Editor UI.
UMovieSceneTrack* OwningTrack = OwningSection->GetTypedOuter<UMovieSceneTrack>();
FText OwningTrackName;
FText ObjectBindingName;
if (OwningTrack)
{
OwningTrackName = OwningTrack->GetDisplayName();
// This track might be inside an object binding and we'd like to prepend the object binding's name for more context.
ObjectBindingName = GetOwningObjectBindingName(OwningTrack, *InSequencer);
}
// Not all tracks have all the information so we need to format it differently depending on how many are valid.
TArray<FText> ValidNames;
if (!ObjectBindingName.IsEmptyOrWhitespace())
{
ValidNames.Add(ObjectBindingName);
}
if (!OwningTrackName.IsEmptyOrWhitespace())
{
ValidNames.Add(OwningTrackName);
}
if (!ChannelHandle.GetMetaData()->Group.IsEmptyOrWhitespace())
{
ValidNames.Add(ChannelHandle.GetMetaData()->Group);
}
if (!DisplayText.IsEmptyOrWhitespace())
{
ValidNames.Add(DisplayText);
}
// Now we loop through and string them together into one big format string.
FText LongDisplayNameFormatString;
for (int32 NameIndex = 0; NameIndex < ValidNames.Num(); NameIndex++)
{
const bool bLastEntry = NameIndex == ValidNames.Num() - 1;
if (!bLastEntry)
{
LongDisplayNameFormatString = FText::Format(NSLOCTEXT("SequencerIKeyArea", "CurveLongDisplayNameFormat", "{0}`{{1}`}."), LongDisplayNameFormatString, NameIndex);
}
else
{
LongDisplayNameFormatString = FText::Format(NSLOCTEXT("SequencerIKeyArea", "CurveLongDisplayNameFormatEnd", "{0}`{{1}`}"), LongDisplayNameFormatString, NameIndex);
}
}
FText LongDisplayName = FText::Format(LongDisplayNameFormatString, FFormatOrderedArguments(ValidNames));
const FText ShortDisplayName = DisplayText;
FString IntentName = ChannelHandle.GetMetaData()->IntentName.ToString();
if (IntentName.IsEmpty())
{
IntentName = ChannelHandle.GetMetaData()->Group.IsEmptyOrWhitespace() ? DisplayText.ToString() : FString::Format(TEXT("{0}.{1}"), { ChannelHandle.GetMetaData()->Group.ToString(), DisplayText.ToString() });
}
CurveModel->SetShortDisplayName(DisplayText);
CurveModel->SetLongDisplayName(LongDisplayName);
CurveModel->SetIntentionName(IntentName);
if (Color.IsSet())
{
CurveModel->SetColor(Color.GetValue());
}
//Use editor preference color if it's been set, this function is const so we can't set just Color optional
const UCurveEditorSettings* Settings = GetDefault<UCurveEditorSettings>();
UObject* Object = nullptr;
FString Name;
CurveModel->GetCurveColorObjectAndName(&Object, Name);
if (Object)
{
TOptional<FLinearColor> SettingColor = Settings->GetCustomColor(Object->GetClass(), Name);
if (SettingColor.IsSet())
{
CurveModel->SetColor(SettingColor.GetValue());
}
}
}
return CurveModel;
}
return nullptr;
}