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

239 lines
6.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MovieSceneObjectBindingIDCustomization.h"
#include "Containers/Array.h"
#include "Delegates/Delegate.h"
#include "DetailWidgetRow.h"
#include "Fonts/SlateFontInfo.h"
#include "HAL/Platform.h"
#include "HAL/PlatformCrt.h"
#include "IDetailsView.h"
#include "ISequencer.h"
#include "Input/DragAndDrop.h"
#include "Input/Reply.h"
#include "Internationalization/Internationalization.h"
#include "Layout/Margin.h"
#include "Misc/Attribute.h"
#include "MovieSceneBindingOwnerInterface.h"
#include "PropertyEditorDelegates.h"
#include "PropertyHandle.h"
#include "SDropTarget.h"
#include "ScopedTransaction.h"
#include "SequencerObjectBindingDragDropOp.h"
#include "SlotBase.h"
#include "Templates/Casts.h"
#include "Templates/TypeHash.h"
#include "UObject/Object.h"
#include "UObject/UnrealType.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/Input/SComboButton.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/Text/STextBlock.h"
#include "PropertyCustomizationHelpers.h"
#include "EditorClassUtils.h"
struct FGeometry;
#define LOCTEXT_NAMESPACE "MovieSceneObjectBindingIDCustomization"
void FMovieSceneObjectBindingIDCustomization::BindTo(TSharedRef<ISequencer> OuterSequencer)
{
OuterSequencer->OnInitializeDetailsPanel().AddStatic(
[](TSharedRef<IDetailsView> DetailsView, TSharedRef<ISequencer> InSequencer)
{
TWeakPtr<ISequencer> WeakSequencer = InSequencer;
FOnGetPropertyTypeCustomizationInstance BindingIDCustomizationFactory = FOnGetPropertyTypeCustomizationInstance::CreateLambda(
[WeakSequencer]
{
if (TSharedPtr<ISequencer> Sequencer = WeakSequencer.Pin())
{
return MakeShared<FMovieSceneObjectBindingIDCustomization>(Sequencer->GetFocusedTemplateID(), Sequencer);
}
return MakeShared<FMovieSceneObjectBindingIDCustomization>();
}
);
// Register an object binding ID customization that can use the current sequencer interface
DetailsView->RegisterInstancedCustomPropertyTypeLayout("MovieSceneObjectBindingID", BindingIDCustomizationFactory);
}
);
}
void FMovieSceneObjectBindingIDCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils)
{
using namespace UE::Sequencer;
StructProperty = PropertyHandle;
StructProperty->SetOnPropertyResetToDefault(
FSimpleDelegate::CreateSP(this, &FMovieSceneObjectBindingIDCustomization::OnResetToDefault));
const FString& MetaClassName = PropertyHandle->GetMetaData("MetaClass");
const FString& MustImplementName = PropertyHandle->GetMetaData("MustImplement");
AllowedClassFilters = PropertyCustomizationHelpers::GetClassesFromMetadataString(PropertyHandle->GetMetaData("AllowedClasses"));
DisallowedClassFilters = PropertyCustomizationHelpers::GetClassesFromMetadataString(PropertyHandle->GetMetaData("DisallowedClasses"));
ClassPropertyMetaClass = !MetaClassName.IsEmpty()
? FEditorClassUtils::GetClassFromString(MetaClassName)
: UObject::StaticClass();
InterfaceThatMustBeImplemented = FEditorClassUtils::GetClassFromString(MustImplementName);
Initialize();
auto IsAcceptable = [](TSharedPtr<FDragDropOperation> Operation)
{
using namespace UE::Sequencer;
if (!Operation->IsOfType<FSequencerObjectBindingDragDropOp>())
{
return false;
}
FSequencerObjectBindingDragDropOp* DragDropOp = static_cast<FSequencerObjectBindingDragDropOp*>(Operation.Get());
return DragDropOp->GetDraggedRebindableBindings().Num() == 1;
};
HeaderRow
.NameContent()
[
StructProperty->CreatePropertyNameWidget()
]
.ValueContent()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
[
SNew(SDropTarget)
.OnDropped(this, &FMovieSceneObjectBindingIDCustomization::OnDrop)
.OnAllowDrop_Static(IsAcceptable)
.OnIsRecognized_Static(IsAcceptable)
[
SNew(SComboButton)
.IsEnabled_Lambda([this]() { return StructProperty->IsEditable(); })
.ToolTipText(this, &FMovieSceneObjectBindingIDCustomization::GetToolTipText)
.OnGetMenuContent(this, &FMovieSceneObjectBindingIDCustomization::GetPickerMenu)
.ContentPadding(FMargin(4.0, 2.0))
.ButtonContent()
[
GetCurrentItemWidget(
SNew(STextBlock)
.Font(CustomizationUtils.GetRegularFont())
)
]
]
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(FMargin(4.f, 0.f, 0.f, 0.f))
[
GetWarningWidget()
]
];
}
FReply FMovieSceneObjectBindingIDCustomization::OnDrop(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent)
{
using namespace UE::Sequencer;
TSharedPtr<FSequencerObjectBindingDragDropOp> SequencerOp = InDragDropEvent.GetOperationAs<FSequencerObjectBindingDragDropOp>();
if (SequencerOp)
{
TArray<UE::MovieScene::FFixedObjectBindingID> Bindings = SequencerOp->GetDraggedRebindableBindings();
if (Bindings.Num() == 1)
{
SetBindingId(Bindings[0]);
}
}
return FReply::Handled();
}
UMovieSceneSequence* FMovieSceneObjectBindingIDCustomization::GetSequence() const
{
TArray<UObject*> OuterObjects;
StructProperty->GetOuterObjects(OuterObjects);
if (OuterObjects.Num() != 1)
{
return nullptr;
}
for ( UObject* NextOuter = OuterObjects[0]; NextOuter; NextOuter = NextOuter->GetOuter() )
{
if (IMovieSceneBindingOwnerInterface* Result = Cast<IMovieSceneBindingOwnerInterface>(NextOuter))
{
return Result->RetrieveOwnedSequence();
}
}
return nullptr;
}
bool FMovieSceneObjectBindingIDCustomization::HasMultipleValues() const
{
TArray<void*> Ptrs;
StructProperty->AccessRawData(Ptrs);
return Ptrs.Num() > 1;
}
FMovieSceneObjectBindingID FMovieSceneObjectBindingIDCustomization::GetCurrentValue() const
{
TArray<void*> Ptrs;
StructProperty->AccessRawData(Ptrs);
if (Ptrs.Num() == 0 || Ptrs[0] == nullptr)
{
return FMovieSceneObjectBindingID();
}
FMovieSceneObjectBindingID Value = *static_cast<FMovieSceneObjectBindingID*>(Ptrs[0]);
// If more than one value and not all equal, return empty
for (int32 Index = 1; Index < Ptrs.Num(); ++Index)
{
if (Ptrs[Index] != nullptr && *static_cast<FMovieSceneObjectBindingID*>(Ptrs[Index]) != Value)
{
return FMovieSceneObjectBindingID();
}
}
return Value;
}
void FMovieSceneObjectBindingIDCustomization::SetCurrentValue(const FMovieSceneObjectBindingID& InObjectBinding)
{
FScopedTransaction Transaction(LOCTEXT("SetBinding", "Set Binding"));
StructProperty->NotifyPreChange();
TArray<UObject*> Objects;
StructProperty->GetOuterObjects(Objects);
for (UObject* Object : Objects)
{
Object->Modify();
}
TArray<void*> Ptrs;
StructProperty->AccessRawData(Ptrs);
for (int32 Index = 0; Index < Ptrs.Num(); ++Index)
{
*static_cast<FMovieSceneObjectBindingID*>(Ptrs[Index]) = InObjectBinding;
}
StructProperty->NotifyPostChange(EPropertyChangeType::ValueSet);
StructProperty->NotifyFinishedChangingProperties();
}
void FMovieSceneObjectBindingIDCustomization::OnResetToDefault()
{
StructProperty->RequestRebuildChildren();
}
#undef LOCTEXT_NAMESPACE