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

249 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Sections/TransformPropertySection.h"
#include "Containers/ArrayView.h"
#include "Delegates/Delegate.h"
#include "Framework/Commands/UIAction.h"
#include "Framework/Commands/UICommandInfo.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "ISequencer.h"
#include "Internationalization/Internationalization.h"
#include "Misc/AxisDisplayInfo.h"
#include "Misc/EnumClassFlags.h"
#include "Misc/Guid.h"
#include "MovieSceneSection.h"
#include "ScopedTransaction.h"
#include "Sections/MovieScene3DTransformSection.h"
#include "Styling/SlateTypes.h"
#include "Templates/Casts.h"
#include "Textures/SlateIcon.h"
#include "UObject/NameTypes.h"
#include "UObject/UnrealNames.h"
#include "UObject/WeakObjectPtr.h"
#include "UObject/WeakObjectPtrTemplates.h"
class UObject;
#define LOCTEXT_NAMESPACE "FTransformSection"
void FTransformSection::BuildSectionContextMenu(FMenuBuilder& MenuBuilder, const FGuid& InObjectBinding)
{
auto MakeUIAction = [this, InObjectBinding](EMovieSceneTransformChannel ChannelsToToggle)
{
return FUIAction(
FExecuteAction::CreateLambda([this, InObjectBinding, ChannelsToToggle]
{
UMovieScene3DTransformSection* TransformSection = CastChecked<UMovieScene3DTransformSection>(WeakSection.Get());
TSharedPtr<ISequencer> SequencerPtr = WeakSequencer.Pin();
FScopedTransaction Transaction(LOCTEXT("SetActiveChannelsTransaction", "Set Active Channels"));
TransformSection->Modify();
EMovieSceneTransformChannel Channels = TransformSection->GetMask().GetChannels();
if (EnumHasAllFlags(Channels, ChannelsToToggle) || (Channels & ChannelsToToggle) == EMovieSceneTransformChannel::None)
{
TransformSection->SetMask(TransformSection->GetMask().GetChannels() ^ ChannelsToToggle);
}
else
{
TransformSection->SetMask(TransformSection->GetMask().GetChannels() | ChannelsToToggle);
}
// Restore pre-animated state for the bound objects so that inactive channels will return to their default values.
for (TWeakObjectPtr<> WeakObject : SequencerPtr->FindBoundObjects(InObjectBinding, SequencerPtr->GetFocusedTemplateID()))
{
if (UObject* Object = WeakObject.Get())
{
SequencerPtr->RestorePreAnimatedState();
}
}
SequencerPtr->NotifyMovieSceneDataChanged(EMovieSceneDataChangeType::MovieSceneStructureItemsChanged);
}
),
FCanExecuteAction(),
FGetActionCheckState::CreateLambda([this, ChannelsToToggle]
{
const UMovieScene3DTransformSection* const TransformSection = CastChecked<UMovieScene3DTransformSection>(WeakSection.Get());
if (!IsValid(TransformSection))
{
return ECheckBoxState::Unchecked;
}
const EMovieSceneTransformChannel Channels = TransformSection->GetMask().GetChannels();
if (EnumHasAllFlags(Channels, ChannelsToToggle))
{
return ECheckBoxState::Checked;
}
else if (EnumHasAnyFlags(Channels, ChannelsToToggle))
{
return ECheckBoxState::Undetermined;
}
return ECheckBoxState::Unchecked;
})
);
};
MenuBuilder.BeginSection(NAME_None, LOCTEXT("TransformChannelsText", "Active Channels"));
{
const EAxisList::Type XAxis = EAxisList::Forward;
const EAxisList::Type YAxis = EAxisList::Left;
const EAxisList::Type ZAxis = EAxisList::Up;
MenuBuilder.AddSubMenu(
LOCTEXT("AllTranslation", "Translation"), LOCTEXT("AllTranslation_ToolTip", "Causes this section to affect the translation of the transform"),
FNewMenuDelegate::CreateLambda([=](FMenuBuilder& SubMenuBuilder){
const int32 NumMenuItems = 3;
TStaticArray<TFunction<void()>, NumMenuItems> MenuConstructors = {
[&SubMenuBuilder, MakeUIAction, XAxis]()
{
SubMenuBuilder.AddMenuEntry(
AxisDisplayInfo::GetAxisDisplayName(XAxis),
FText::Format(LOCTEXT("ActivateTranslationChannel_Tooltip", "Causes this section to affect the {0} channel of the transform's translation"), AxisDisplayInfo::GetAxisDisplayName(XAxis)),
FSlateIcon(), MakeUIAction(EMovieSceneTransformChannel::TranslationX), NAME_None, EUserInterfaceActionType::ToggleButton);
},
[&SubMenuBuilder, MakeUIAction, YAxis]()
{
SubMenuBuilder.AddMenuEntry(
AxisDisplayInfo::GetAxisDisplayName(YAxis),
FText::Format(LOCTEXT("ActivateTranslationChannel_Tooltip", "Causes this section to affect the {0} channel of the transform's translation"), AxisDisplayInfo::GetAxisDisplayName(YAxis)),
FSlateIcon(), MakeUIAction(EMovieSceneTransformChannel::TranslationY), NAME_None, EUserInterfaceActionType::ToggleButton);
},
[&SubMenuBuilder, MakeUIAction, ZAxis]()
{
SubMenuBuilder.AddMenuEntry(
AxisDisplayInfo::GetAxisDisplayName(ZAxis),
FText::Format(LOCTEXT("ActivateTranslationChannel_Tooltip", "Causes this section to affect the {0} channel of the transform's translation"), AxisDisplayInfo::GetAxisDisplayName(ZAxis)),
FSlateIcon(), MakeUIAction(EMovieSceneTransformChannel::TranslationZ), NAME_None, EUserInterfaceActionType::ToggleButton);
}
};
const FIntVector4 Swizzle = AxisDisplayInfo::GetTransformAxisSwizzle();
for (int32 MenuItemIndex = 0; MenuItemIndex < NumMenuItems; MenuItemIndex++)
{
const int32 SwizzledComponentIndex = Swizzle[MenuItemIndex];
MenuConstructors[SwizzledComponentIndex]();
}
}),
MakeUIAction(EMovieSceneTransformChannel::Translation),
NAME_None,
EUserInterfaceActionType::ToggleButton);
MenuBuilder.AddSubMenu(
LOCTEXT("AllRotation", "Rotation"), LOCTEXT("AllRotation_ToolTip", "Causes this section to affect the rotation of the transform"),
FNewMenuDelegate::CreateLambda([=](FMenuBuilder& SubMenuBuilder){
SubMenuBuilder.AddMenuEntry(
LOCTEXT("RotationX", "Roll"), LOCTEXT("RotationX_ToolTip", "Causes this section to affect the roll channel the transform's rotation"),
FSlateIcon(), MakeUIAction(EMovieSceneTransformChannel::RotationX), NAME_None, EUserInterfaceActionType::ToggleButton);
SubMenuBuilder.AddMenuEntry(
LOCTEXT("RotationY", "Pitch"), LOCTEXT("RotationY_ToolTip", "Causes this section to affect the pitch channel the transform's rotation"),
FSlateIcon(), MakeUIAction(EMovieSceneTransformChannel::RotationY), NAME_None, EUserInterfaceActionType::ToggleButton);
SubMenuBuilder.AddMenuEntry(
LOCTEXT("RotationZ", "Yaw"), LOCTEXT("RotationZ_ToolTip", "Causes this section to affect the yaw channel the transform's rotation"),
FSlateIcon(), MakeUIAction(EMovieSceneTransformChannel::RotationZ), NAME_None, EUserInterfaceActionType::ToggleButton);
}),
MakeUIAction(EMovieSceneTransformChannel::Rotation),
NAME_None,
EUserInterfaceActionType::ToggleButton);
MenuBuilder.AddSubMenu(
LOCTEXT("AllScale", "Scale"), LOCTEXT("AllScale_ToolTip", "Causes this section to affect the scale of the transform"),
FNewMenuDelegate::CreateLambda([=](FMenuBuilder& SubMenuBuilder){
const int32 NumMenuItems = 3;
TStaticArray<TFunction<void()>, NumMenuItems> MenuConstructors = {
[&SubMenuBuilder, MakeUIAction, XAxis]()
{
SubMenuBuilder.AddMenuEntry(
AxisDisplayInfo::GetAxisDisplayName(XAxis),
FText::Format(LOCTEXT("ActivateScaleChannel_Tooltip", "Causes this section to affect the {0} channel of the transform's scale"), AxisDisplayInfo::GetAxisDisplayName(XAxis)),
FSlateIcon(), MakeUIAction(EMovieSceneTransformChannel::ScaleX), NAME_None, EUserInterfaceActionType::ToggleButton);
},
[&SubMenuBuilder, MakeUIAction, YAxis]()
{
SubMenuBuilder.AddMenuEntry(
AxisDisplayInfo::GetAxisDisplayName(YAxis),
FText::Format(LOCTEXT("ActivateScaleChannel_Tooltip", "Causes this section to affect the {0} channel of the transform's scale"), AxisDisplayInfo::GetAxisDisplayName(YAxis)),
FSlateIcon(), MakeUIAction(EMovieSceneTransformChannel::ScaleY), NAME_None, EUserInterfaceActionType::ToggleButton);
},
[&SubMenuBuilder, MakeUIAction, ZAxis]()
{
SubMenuBuilder.AddMenuEntry(
AxisDisplayInfo::GetAxisDisplayName(ZAxis),
FText::Format(LOCTEXT("ActivateScaleChannel_Tooltip", "Causes this section to affect the {0} channel of the transform's scale"), AxisDisplayInfo::GetAxisDisplayName(ZAxis)),
FSlateIcon(), MakeUIAction(EMovieSceneTransformChannel::ScaleZ), NAME_None, EUserInterfaceActionType::ToggleButton);
}
};
const FIntVector4 Swizzle = AxisDisplayInfo::GetTransformAxisSwizzle();
for (int32 MenuItemIndex = 0; MenuItemIndex < NumMenuItems; MenuItemIndex++)
{
const int32 SwizzledComponentIndex = Swizzle[MenuItemIndex];
MenuConstructors[SwizzledComponentIndex]();
}
}),
MakeUIAction(EMovieSceneTransformChannel::Scale),
NAME_None,
EUserInterfaceActionType::ToggleButton);
MenuBuilder.AddMenuEntry(
LOCTEXT("Weight", "Weight"), LOCTEXT("Weight_ToolTip", "Causes this section to be applied with a user-specified weight curve"),
FSlateIcon(), MakeUIAction(EMovieSceneTransformChannel::Weight), NAME_None, EUserInterfaceActionType::ToggleButton);
}
MenuBuilder.EndSection();
}
bool FTransformSection::RequestDeleteCategory(const TArray<FName>& CategoryNamePaths)
{
UMovieScene3DTransformSection* TransformSection = CastChecked<UMovieScene3DTransformSection>(WeakSection.Get());
TSharedPtr<ISequencer> SequencerPtr = WeakSequencer.Pin();
const FScopedTransaction Transaction( LOCTEXT( "DeleteTransformCategory", "Delete transform category" ) );
if (TransformSection->TryModify())
{
FName CategoryName = CategoryNamePaths[CategoryNamePaths.Num()-1];
EMovieSceneTransformChannel Channel = TransformSection->GetMask().GetChannels();
EMovieSceneTransformChannel ChannelToRemove = TransformSection->GetMaskByName(CategoryName).GetChannels();
Channel &= ~ChannelToRemove;
TransformSection->SetMask(Channel);
SequencerPtr->NotifyMovieSceneDataChanged(EMovieSceneDataChangeType::MovieSceneStructureItemsChanged);
return true;
}
return false;
}
bool FTransformSection::RequestDeleteKeyArea(const TArray<FName>& KeyAreaNamePaths)
{
UMovieScene3DTransformSection* TransformSection = CastChecked<UMovieScene3DTransformSection>(WeakSection.Get());
TSharedPtr<ISequencer> SequencerPtr = WeakSequencer.Pin();
const FScopedTransaction Transaction( LOCTEXT( "DeleteTransformChannel", "Delete transform channel" ) );
if (TransformSection->TryModify())
{
// Only delete the last key area path which is the channel. ie. TranslationX as opposed to Translation
FName KeyAreaName = KeyAreaNamePaths[KeyAreaNamePaths.Num()-1];
EMovieSceneTransformChannel Channel = TransformSection->GetMask().GetChannels();
EMovieSceneTransformChannel ChannelToRemove = TransformSection->GetMaskByName(KeyAreaName).GetChannels();
Channel &= ~ChannelToRemove;
TransformSection->SetMask(Channel);
SequencerPtr->NotifyMovieSceneDataChanged(EMovieSceneDataChangeType::MovieSceneStructureItemsChanged);
return true;
}
return true;
}
#undef LOCTEXT_NAMESPACE