Files
UnrealEngineUWP/Engine/Source/Editor/UMGEditor/Private/WidgetBlueprintEditor.cpp
Andrew Rodham 58107d1db2 Sequencer settings are now stored per sequencer-type
[CL 2607255 by Andrew Rodham in Main branch]
2015-07-01 06:06:21 -04:00

790 lines
23 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "UMGEditorPrivatePCH.h"
#include "SKismetInspector.h"
#include "WidgetBlueprintEditor.h"
#include "MovieScene.h"
#include "Editor/Sequencer/Public/ISequencerModule.h"
#include "Animation/UMGSequencerObjectBindingManager.h"
#include "ObjectEditorUtils.h"
#include "PropertyCustomizationHelpers.h"
#include "WidgetBlueprintApplicationModes.h"
#include "WidgetBlueprintEditorUtils.h"
#include "WidgetDesignerApplicationMode.h"
#include "WidgetGraphApplicationMode.h"
#include "WidgetBlueprintEditorToolbar.h"
#include "Blueprint/WidgetTree.h"
#include "Components/CanvasPanel.h"
#include "GenericCommands.h"
#include "WidgetBlueprint.h"
#include "Engine/SimpleConstructionScript.h"
#define LOCTEXT_NAMESPACE "UMG"
FWidgetBlueprintEditor::FWidgetBlueprintEditor()
: PreviewScene(FPreviewScene::ConstructionValues().AllowAudioPlayback(true).ShouldSimulatePhysics(true))
, PreviewBlueprint(nullptr)
, bIsSimulateEnabled(false)
, bIsRealTime(true)
{
PreviewScene.GetWorld()->bBegunPlay = false;
}
FWidgetBlueprintEditor::~FWidgetBlueprintEditor()
{
UWidgetBlueprint* Blueprint = GetWidgetBlueprintObj();
if ( Blueprint )
{
Blueprint->OnChanged().RemoveAll(this);
Blueprint->OnCompiled().RemoveAll(this);
}
GEditor->OnObjectsReplaced().RemoveAll(this);
Sequencer.Reset();
SequencerObjectBindingManager.Reset();
}
void FWidgetBlueprintEditor::InitWidgetBlueprintEditor(const EToolkitMode::Type Mode, const TSharedPtr< IToolkitHost >& InitToolkitHost, const TArray<UBlueprint*>& InBlueprints, bool bShouldOpenInDefaultsMode)
{
TSharedPtr<FWidgetBlueprintEditor> ThisPtr(SharedThis(this));
WidgetToolbar = MakeShareable(new FWidgetBlueprintEditorToolbar(ThisPtr));
InitBlueprintEditor(Mode, InitToolkitHost, InBlueprints, bShouldOpenInDefaultsMode);
// register for any objects replaced
GEditor->OnObjectsReplaced().AddSP(this, &FWidgetBlueprintEditor::OnObjectsReplaced);
UWidgetBlueprint* Blueprint = GetWidgetBlueprintObj();
// If this blueprint is empty, add a canvas panel as the root widget.
if ( Blueprint->WidgetTree->RootWidget == nullptr )
{
UWidget* RootWidget = Blueprint->WidgetTree->ConstructWidget<UCanvasPanel>(UCanvasPanel::StaticClass());
RootWidget->SetDesignerFlags(GetCurrentDesignerFlags());
Blueprint->WidgetTree->RootWidget = RootWidget;
}
UpdatePreview(GetWidgetBlueprintObj(), true);
// If the user has close the sequencer tab, this will not be initialized.
if ( SequencerObjectBindingManager.IsValid() )
{
SequencerObjectBindingManager->InitPreviewObjects();
}
DesignerCommandList = MakeShareable(new FUICommandList);
DesignerCommandList->MapAction(FGenericCommands::Get().Delete,
FExecuteAction::CreateSP(this, &FWidgetBlueprintEditor::DeleteSelectedWidgets),
FCanExecuteAction::CreateSP(this, &FWidgetBlueprintEditor::CanDeleteSelectedWidgets)
);
DesignerCommandList->MapAction(FGenericCommands::Get().Copy,
FExecuteAction::CreateSP(this, &FWidgetBlueprintEditor::CopySelectedWidgets),
FCanExecuteAction::CreateSP(this, &FWidgetBlueprintEditor::CanCopySelectedWidgets)
);
DesignerCommandList->MapAction(FGenericCommands::Get().Cut,
FExecuteAction::CreateSP(this, &FWidgetBlueprintEditor::CutSelectedWidgets),
FCanExecuteAction::CreateSP(this, &FWidgetBlueprintEditor::CanCutSelectedWidgets)
);
DesignerCommandList->MapAction(FGenericCommands::Get().Paste,
FExecuteAction::CreateSP(this, &FWidgetBlueprintEditor::PasteWidgets),
FCanExecuteAction::CreateSP(this, &FWidgetBlueprintEditor::CanPasteWidgets)
);
}
void FWidgetBlueprintEditor::RegisterApplicationModes(const TArray<UBlueprint*>& InBlueprints, bool bShouldOpenInDefaultsMode, bool bNewlyCreated/* = false*/)
{
//FBlueprintEditor::RegisterApplicationModes(InBlueprints, bShouldOpenInDefaultsMode);
if ( InBlueprints.Num() == 1 )
{
TSharedPtr<FWidgetBlueprintEditor> ThisPtr(SharedThis(this));
// Create the modes and activate one (which will populate with a real layout)
TArray< TSharedRef<FApplicationMode> > TempModeList;
TempModeList.Add(MakeShareable(new FWidgetDesignerApplicationMode(ThisPtr)));
TempModeList.Add(MakeShareable(new FWidgetGraphApplicationMode(ThisPtr)));
for ( TSharedRef<FApplicationMode>& AppMode : TempModeList )
{
AddApplicationMode(AppMode->GetModeName(), AppMode);
}
SetCurrentMode(FWidgetBlueprintApplicationModes::DesignerMode);
}
else
{
//// We either have no blueprints or many, open in the defaults mode for multi-editing
//AddApplicationMode(
// FBlueprintEditorApplicationModes::BlueprintDefaultsMode,
// MakeShareable(new FBlueprintDefaultsApplicationMode(SharedThis(this))));
//SetCurrentMode(FBlueprintEditorApplicationModes::BlueprintDefaultsMode);
}
}
void FWidgetBlueprintEditor::SelectWidgets(const TSet<FWidgetReference>& Widgets, bool bAppendOrToggle)
{
TSet<FWidgetReference> TempSelection;
for ( const FWidgetReference& Widget : Widgets )
{
if ( Widget.IsValid() )
{
TempSelection.Add(Widget);
}
}
OnSelectedWidgetsChanging.Broadcast();
// Finally change the selected widgets after we've updated the details panel
// to ensure values that are pending are committed on focus loss, and migrated properly
// to the old selected widgets.
if ( !bAppendOrToggle )
{
SelectedWidgets.Empty();
}
SelectedObjects.Empty();
for ( const FWidgetReference& Widget : TempSelection )
{
if ( bAppendOrToggle && SelectedWidgets.Contains(Widget) )
{
SelectedWidgets.Remove(Widget);
}
else
{
SelectedWidgets.Add(Widget);
}
}
OnSelectedWidgetsChanged.Broadcast();
}
void FWidgetBlueprintEditor::SelectObjects(const TSet<UObject*>& Objects)
{
OnSelectedWidgetsChanging.Broadcast();
SelectedWidgets.Empty();
SelectedObjects.Empty();
for ( UObject* Obj : Objects )
{
SelectedObjects.Add(Obj);
}
OnSelectedWidgetsChanged.Broadcast();
}
void FWidgetBlueprintEditor::CleanSelection()
{
TSet<FWidgetReference> TempSelection;
TArray<UWidget*> WidgetsInTree;
GetWidgetBlueprintObj()->WidgetTree->GetAllWidgets(WidgetsInTree);
TSet<UWidget*> TreeWidgetSet(WidgetsInTree);
for ( FWidgetReference& WidgetRef : SelectedWidgets )
{
if ( WidgetRef.IsValid() )
{
if ( TreeWidgetSet.Contains(WidgetRef.GetTemplate()) )
{
TempSelection.Add(WidgetRef);
}
}
}
if ( TempSelection.Num() != SelectedWidgets.Num() )
{
SelectWidgets(TempSelection, false);
}
}
const TSet<FWidgetReference>& FWidgetBlueprintEditor::GetSelectedWidgets() const
{
return SelectedWidgets;
}
const TSet< TWeakObjectPtr<UObject> >& FWidgetBlueprintEditor::GetSelectedObjects() const
{
return SelectedObjects;
}
void FWidgetBlueprintEditor::InvalidatePreview()
{
bPreviewInvalidated = true;
}
void FWidgetBlueprintEditor::OnBlueprintChangedImpl(UBlueprint* InBlueprint, bool bIsJustBeingCompiled )
{
DestroyPreview();
FBlueprintEditor::OnBlueprintChangedImpl(InBlueprint, bIsJustBeingCompiled);
if ( InBlueprint )
{
RefreshPreview();
}
}
void FWidgetBlueprintEditor::OnObjectsReplaced(const TMap<UObject*, UObject*>& ReplacementMap)
{
// Remove dead references and update references
for ( int32 HandleIndex = WidgetHandlePool.Num() - 1; HandleIndex >= 0; HandleIndex-- )
{
TSharedPtr<FWidgetHandle> Ref = WidgetHandlePool[HandleIndex].Pin();
if ( Ref.IsValid() )
{
UObject* const* NewObject = ReplacementMap.Find(Ref->Widget.Get());
if ( NewObject )
{
Ref->Widget = Cast<UWidget>(*NewObject);
}
}
else
{
WidgetHandlePool.RemoveAtSwap(HandleIndex);
}
}
}
bool FWidgetBlueprintEditor::CanDeleteSelectedWidgets()
{
TSet<FWidgetReference> Widgets = GetSelectedWidgets();
return Widgets.Num() > 0;
}
void FWidgetBlueprintEditor::DeleteSelectedWidgets()
{
TSet<FWidgetReference> Widgets = GetSelectedWidgets();
FWidgetBlueprintEditorUtils::DeleteWidgets(GetWidgetBlueprintObj(), Widgets);
// Clear the selection now that the widget has been deleted.
TSet<FWidgetReference> Empty;
SelectWidgets(Empty, false);
}
bool FWidgetBlueprintEditor::CanCopySelectedWidgets()
{
TSet<FWidgetReference> Widgets = GetSelectedWidgets();
return Widgets.Num() > 0;
}
void FWidgetBlueprintEditor::CopySelectedWidgets()
{
TSet<FWidgetReference> Widgets = GetSelectedWidgets();
FWidgetBlueprintEditorUtils::CopyWidgets(GetWidgetBlueprintObj(), Widgets);
}
bool FWidgetBlueprintEditor::CanCutSelectedWidgets()
{
TSet<FWidgetReference> Widgets = GetSelectedWidgets();
return Widgets.Num() > 0;
}
void FWidgetBlueprintEditor::CutSelectedWidgets()
{
TSet<FWidgetReference> Widgets = GetSelectedWidgets();
FWidgetBlueprintEditorUtils::CutWidgets(GetWidgetBlueprintObj(), Widgets);
}
const UWidgetAnimation* FWidgetBlueprintEditor::RefreshCurrentAnimation()
{
if( !SequencerObjectBindingManager->HasValidWidgetAnimation() )
{
ChangeViewedAnimation(*UWidgetAnimation::GetNullAnimation());
return nullptr;
}
return SequencerObjectBindingManager->GetWidgetAnimation();
}
bool FWidgetBlueprintEditor::CanPasteWidgets()
{
TSet<FWidgetReference> Widgets = GetSelectedWidgets();
if ( Widgets.Num() == 1 )
{
FWidgetReference Target = *Widgets.CreateIterator();
const bool bIsPanel = Cast<UPanelWidget>(Target.GetTemplate()) != nullptr;
return bIsPanel;
}
else if ( Widgets.Num() == 0 )
{
if ( GetWidgetBlueprintObj()->WidgetTree->RootWidget == nullptr )
{
return true;
}
}
return false;
}
void FWidgetBlueprintEditor::PasteWidgets()
{
TSet<FWidgetReference> Widgets = GetSelectedWidgets();
FWidgetReference Target = Widgets.Num() > 0 ? *Widgets.CreateIterator() : FWidgetReference();
FWidgetBlueprintEditorUtils::PasteWidgets(SharedThis(this), GetWidgetBlueprintObj(), Target, PasteDropLocation);
//TODO UMG - Select the newly selected pasted widgets.
}
void FWidgetBlueprintEditor::Tick(float DeltaTime)
{
FBlueprintEditor::Tick(DeltaTime);
// Tick the preview scene world.
if ( !GIntraFrameDebuggingGameThread )
{
// Allow full tick only if preview simulation is enabled and we're not currently in an active SIE or PIE session
if ( bIsSimulateEnabled && GEditor->PlayWorld == nullptr && !GEditor->bIsSimulatingInEditor )
{
PreviewScene.GetWorld()->Tick(bIsRealTime ? LEVELTICK_All : LEVELTICK_TimeOnly, DeltaTime);
}
else
{
PreviewScene.GetWorld()->Tick(bIsRealTime ? LEVELTICK_ViewportsOnly : LEVELTICK_TimeOnly, DeltaTime);
}
}
// Note: The weak ptr can become stale if the actor is reinstanced due to a Blueprint change, etc. In that case we
// look to see if we can find the new instance in the preview world and then update the weak ptr.
if ( PreviewWidgetPtr.IsStale(true) || bPreviewInvalidated )
{
bPreviewInvalidated = false;
RefreshPreview();
}
}
static bool MigratePropertyValue(UObject* SourceObject, UObject* DestinationObject, FEditPropertyChain::TDoubleLinkedListNode* PropertyChainNode, UProperty* MemberProperty, bool bIsModify)
{
UProperty* CurrentProperty = PropertyChainNode->GetValue();
if ( PropertyChainNode->GetNextNode() == nullptr )
{
if ( bIsModify )
{
DestinationObject->Modify();
return true;
}
else
{
// Check to see if there's an edit condition property we also need to migrate.
bool bDummyNegate = false;
UBoolProperty* EditConditionProperty = PropertyCustomizationHelpers::GetEditConditionProperty(MemberProperty, bDummyNegate);
if ( EditConditionProperty != nullptr )
{
FObjectEditorUtils::MigratePropertyValue(SourceObject, EditConditionProperty, DestinationObject, EditConditionProperty);
}
return FObjectEditorUtils::MigratePropertyValue(SourceObject, MemberProperty, DestinationObject, MemberProperty);
}
}
if ( UObjectProperty* CurrentObjectProperty = Cast<UObjectProperty>(CurrentProperty) )
{
// Get the property addresses for the source and destination objects.
UObject* SourceObjectProperty = CurrentObjectProperty->GetObjectPropertyValue(CurrentObjectProperty->ContainerPtrToValuePtr<void>(SourceObject));
UObject* DestionationObjectProperty = CurrentObjectProperty->GetObjectPropertyValue(CurrentObjectProperty->ContainerPtrToValuePtr<void>(DestinationObject));
return MigratePropertyValue(SourceObjectProperty, DestionationObjectProperty, PropertyChainNode->GetNextNode(), PropertyChainNode->GetNextNode()->GetValue(), bIsModify);
}
else if ( UArrayProperty* CurrentArrayProperty = Cast<UArrayProperty>(CurrentProperty) )
{
// Arrays!
}
return MigratePropertyValue(SourceObject, DestinationObject, PropertyChainNode->GetNextNode(), MemberProperty, bIsModify);
}
void FWidgetBlueprintEditor::AddReferencedObjects( FReferenceCollector& Collector )
{
FBlueprintEditor::AddReferencedObjects( Collector );
UUserWidget* Preview = GetPreview();
Collector.AddReferencedObject( Preview );
}
void FWidgetBlueprintEditor::MigrateFromChain(FEditPropertyChain* PropertyThatChanged, bool bIsModify)
{
UWidgetBlueprint* Blueprint = GetWidgetBlueprintObj();
UUserWidget* PreviewActor = GetPreview();
if ( PreviewActor != nullptr )
{
for ( FWidgetReference& WidgetRef : SelectedWidgets )
{
UWidget* PreviewWidget = WidgetRef.GetPreview();
if ( PreviewWidget )
{
FName PreviewWidgetName = PreviewWidget->GetFName();
UWidget* TemplateWidget = Blueprint->WidgetTree->FindWidget(PreviewWidgetName);
if ( TemplateWidget )
{
FEditPropertyChain::TDoubleLinkedListNode* PropertyChainNode = PropertyThatChanged->GetHead();
MigratePropertyValue(PreviewWidget, TemplateWidget, PropertyChainNode, PropertyChainNode->GetValue(), bIsModify);
}
}
}
}
}
void FWidgetBlueprintEditor::PostUndo(bool bSuccessful)
{
FBlueprintEditor::PostUndo(bSuccessful);
OnWidgetBlueprintTransaction.Broadcast();
}
void FWidgetBlueprintEditor::PostRedo(bool bSuccessful)
{
FBlueprintEditor::PostRedo(bSuccessful);
OnWidgetBlueprintTransaction.Broadcast();
}
TSharedRef<SWidget> FWidgetBlueprintEditor::CreateSequencerWidget()
{
TSharedRef<SOverlay> SequencerOverlayRef =
SNew(SOverlay)
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("Sequencer")));
SequencerOverlay = SequencerOverlayRef;
TSharedRef<STextBlock> NoAnimationTextBlockRef =
SNew(STextBlock)
.TextStyle(FEditorStyle::Get(), "UMGEditor.NoAnimationFont")
.Text(LOCTEXT("NoAnimationSelected", "No Animation Selected"));
NoAnimationTextBlock = NoAnimationTextBlockRef;
SequencerOverlayRef->AddSlot(0)
[
GetSequencer()->GetSequencerWidget()
];
SequencerOverlayRef->AddSlot(1)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
NoAnimationTextBlockRef
];
return SequencerOverlayRef;
}
UWidgetBlueprint* FWidgetBlueprintEditor::GetWidgetBlueprintObj() const
{
return Cast<UWidgetBlueprint>(GetBlueprintObj());
}
UUserWidget* FWidgetBlueprintEditor::GetPreview() const
{
if ( PreviewWidgetPtr.IsStale(true) )
{
return nullptr;
}
return PreviewWidgetPtr.Get();
}
FPreviewScene* FWidgetBlueprintEditor::GetPreviewScene()
{
return &PreviewScene;
}
bool FWidgetBlueprintEditor::IsSimulating() const
{
return bIsSimulateEnabled;
}
void FWidgetBlueprintEditor::SetIsSimulating(bool bSimulating)
{
bIsSimulateEnabled = bSimulating;
}
FWidgetReference FWidgetBlueprintEditor::GetReferenceFromTemplate(UWidget* TemplateWidget)
{
TSharedRef<FWidgetHandle> Reference = MakeShareable(new FWidgetHandle(TemplateWidget));
WidgetHandlePool.Add(Reference);
return FWidgetReference(SharedThis(this), Reference);
}
FWidgetReference FWidgetBlueprintEditor::GetReferenceFromPreview(UWidget* PreviewWidget)
{
UUserWidget* PreviewRoot = GetPreview();
if ( PreviewRoot )
{
UWidgetBlueprint* Blueprint = GetWidgetBlueprintObj();
if ( PreviewWidget )
{
FName Name = PreviewWidget->GetFName();
return GetReferenceFromTemplate(Blueprint->WidgetTree->FindWidget(Name));
}
}
return FWidgetReference(SharedThis(this), TSharedPtr<FWidgetHandle>());
}
TSharedPtr<ISequencer>& FWidgetBlueprintEditor::GetSequencer()
{
if(!Sequencer.IsValid())
{
FSequencerViewParams ViewParams( TEXT( "UMGSequencerSettings" ) );
ViewParams.InitalViewRange = TRange<float>(-0.02f, 3.2f);
ViewParams.InitialScrubPosition = 0;
ViewParams.OnGetAddMenuContent = FOnGetAddMenuContent::CreateSP(this, &FWidgetBlueprintEditor::OnGetAnimationAddMenuContent);
SequencerObjectBindingManager = MakeShareable(new FUMGSequencerObjectBindingManager(*this, *UWidgetAnimation::GetNullAnimation()));
Sequencer = FModuleManager::LoadModuleChecked< ISequencerModule >("Sequencer").CreateSequencer(UWidgetAnimation::GetNullAnimation()->MovieScene, ViewParams, SequencerObjectBindingManager.ToSharedRef());
ChangeViewedAnimation(*UWidgetAnimation::GetNullAnimation());
}
return Sequencer;
}
void FWidgetBlueprintEditor::ChangeViewedAnimation( UWidgetAnimation& InAnimationToView )
{
TSharedRef<FUMGSequencerObjectBindingManager> NewObjectBindingManager = MakeShareable(new FUMGSequencerObjectBindingManager(*this, InAnimationToView));
Sequencer->ResetToNewRootMovieScene(*InAnimationToView.MovieScene, NewObjectBindingManager);
check(SequencerObjectBindingManager.IsUnique());
SequencerObjectBindingManager = NewObjectBindingManager;
SequencerObjectBindingManager->InitPreviewObjects();
TSharedPtr<SOverlay> SequencerOverlayPin = SequencerOverlay.Pin();
if (SequencerOverlayPin.IsValid())
{
TSharedPtr<STextBlock> NoAnimationTextBlockPin = NoAnimationTextBlock.Pin();
if( &InAnimationToView == UWidgetAnimation::GetNullAnimation())
{
// Disable sequencer from interaction
Sequencer->GetSequencerWidget()->SetEnabled(false);
Sequencer->SetAutoKeyEnabled(false);
NoAnimationTextBlockPin->SetVisibility(EVisibility::Visible);
SequencerOverlayPin->SetVisibility( EVisibility::HitTestInvisible );
}
else
{
// Allow sequencer to be interacted with
Sequencer->GetSequencerWidget()->SetEnabled(true);
NoAnimationTextBlockPin->SetVisibility(EVisibility::Collapsed);
SequencerOverlayPin->SetVisibility( EVisibility::SelfHitTestInvisible );
}
}
InvalidatePreview();
}
void FWidgetBlueprintEditor::RefreshPreview()
{
// Rebuilding the preview can force objects to be recreated, so the selection may need to be updated.
OnSelectedWidgetsChanging.Broadcast();
UpdatePreview(GetWidgetBlueprintObj(), true);
CleanSelection();
// Fire the selection updated event to ensure everyone is watching the same widgets.
OnSelectedWidgetsChanged.Broadcast();
}
void FWidgetBlueprintEditor::DestroyPreview()
{
UUserWidget* PreviewActor = GetPreview();
if ( PreviewActor != nullptr )
{
check(PreviewScene.GetWorld());
PreviewActor->MarkPendingKill();
}
}
void FWidgetBlueprintEditor::UpdatePreview(UBlueprint* InBlueprint, bool bInForceFullUpdate)
{
UUserWidget* PreviewActor = GetPreview();
// Signal that we're going to be constructing editor components
if ( InBlueprint != nullptr && InBlueprint->SimpleConstructionScript != nullptr )
{
InBlueprint->SimpleConstructionScript->BeginEditorComponentConstruction();
}
// If the Blueprint is changing
if ( InBlueprint != PreviewBlueprint || bInForceFullUpdate )
{
// Destroy the previous actor instance
DestroyPreview();
// Save the Blueprint we're creating a preview for
PreviewBlueprint = Cast<UWidgetBlueprint>(InBlueprint);
// Update the generated class'es widget tree to match the blueprint tree. That way the preview can update
// without needing to do a full recompile.
Cast<UWidgetBlueprintGeneratedClass>(PreviewBlueprint->GeneratedClass)->WidgetTree = DuplicateObject<UWidgetTree>(PreviewBlueprint->WidgetTree, PreviewBlueprint->GeneratedClass);
PreviewActor = CreateWidget<UUserWidget>(PreviewScene.GetWorld(), PreviewBlueprint->GeneratedClass);
PreviewActor->SetFlags(RF_Transactional);
// Configure all the widgets to be set to design time.
PreviewActor->SetDesignerFlags(GetCurrentDesignerFlags());
// Store a reference to the preview actor.
PreviewWidgetPtr = PreviewActor;
}
OnWidgetPreviewUpdated.Broadcast();
}
FGraphAppearanceInfo FWidgetBlueprintEditor::GetGraphAppearance(UEdGraph* InGraph) const
{
FGraphAppearanceInfo AppearanceInfo = FBlueprintEditor::GetGraphAppearance(InGraph);
if ( GetBlueprintObj()->IsA(UWidgetBlueprint::StaticClass()) )
{
AppearanceInfo.CornerText = LOCTEXT("AppearanceCornerText", "WIDGET BLUEPRINT");
}
return AppearanceInfo;
}
void FWidgetBlueprintEditor::ClearHoveredWidget()
{
HoveredWidget = FWidgetReference();
OnHoveredWidgetCleared.Broadcast();
}
void FWidgetBlueprintEditor::SetHoveredWidget(FWidgetReference& InHoveredWidget)
{
if (InHoveredWidget != HoveredWidget)
{
HoveredWidget = InHoveredWidget;
OnHoveredWidgetSet.Broadcast(InHoveredWidget);
}
}
const FWidgetReference& FWidgetBlueprintEditor::GetHoveredWidget() const
{
return HoveredWidget;
}
void FWidgetBlueprintEditor::AddPostDesignerLayoutAction(TFunction<void()> Action)
{
QueuedDesignerActions.Add(Action);
}
TArray< TFunction<void()> >& FWidgetBlueprintEditor::GetQueuedDesignerActions()
{
return QueuedDesignerActions;
}
EWidgetDesignFlags::Type FWidgetBlueprintEditor::GetCurrentDesignerFlags() const
{
EWidgetDesignFlags::Type Flags = EWidgetDesignFlags::Designing;
if ( GetDefault<UWidgetDesignerSettings>()->bShowOutlines )
{
Flags = ( EWidgetDesignFlags::Type )(Flags | EWidgetDesignFlags::ShowOutline);
}
return Flags;
}
class FObjectAndDisplayName
{
public:
FObjectAndDisplayName(FText InDisplayName, UObject* InObject)
{
DisplayName = InDisplayName;
Object = InObject;
}
bool operator<(FObjectAndDisplayName const& Other) const
{
return DisplayName.CompareTo(Other.DisplayName) < 0;
}
FText DisplayName;
UObject* Object;
};
void GetBindableObjects(UWidget* RootWidget, TArray<FObjectAndDisplayName>& BindableObjects)
{
TArray<UWidget*> ToTraverse;
ToTraverse.Add(RootWidget);
while (ToTraverse.Num() > 0)
{
UWidget* Widget = ToTraverse[0];
ToTraverse.RemoveAt(0);
BindableObjects.Add(FObjectAndDisplayName(FText::FromString(Widget->GetName()), Widget));
UUserWidget* UserWidget = Cast<UUserWidget>(Widget);
if (UserWidget != nullptr)
{
TArray<FName> SlotNames;
UserWidget->GetSlotNames(SlotNames);
for (FName SlotName : SlotNames)
{
UWidget* Content = UserWidget->GetContentForSlot(SlotName);
if (Content != nullptr)
{
ToTraverse.Add(Content);
}
}
}
UPanelWidget* PanelWidget = Cast<UPanelWidget>(Widget);
if (PanelWidget != nullptr)
{
for (UPanelSlot* Slot : PanelWidget->GetSlots())
{
if (Slot->Content != nullptr)
{
FText SlotDisplayName = FText::Format(LOCTEXT("AddMenuSlotFormat", "{0} ({1} Slot)"), FText::FromString(Slot->Content->GetName()), FText::FromString(PanelWidget->GetName()));
BindableObjects.Add(FObjectAndDisplayName(SlotDisplayName, Slot));
ToTraverse.Add(Slot->Content);
}
}
}
}
}
TSharedRef<SWidget> FWidgetBlueprintEditor::OnGetAnimationAddMenuContent(TSharedRef<ISequencer> InSequencer)
{
TArray<FObjectAndDisplayName> BindableObjects;
GetBindableObjects(GetPreview()->GetRootWidget(), BindableObjects);
BindableObjects.Sort();
FMenuBuilder AddMenuBuilder(true, NULL);
for (FObjectAndDisplayName& BindableObject : BindableObjects)
{
FGuid BoundObjectGuid = SequencerObjectBindingManager->FindGuidForObject(*InSequencer->GetFocusedMovieScene(), *BindableObject.Object);
if (BoundObjectGuid.IsValid() == false)
{
FUIAction AddMenuAction(FExecuteAction::CreateSP(this, &FWidgetBlueprintEditor::AddObjectToAnimation, BindableObject.Object));
AddMenuBuilder.AddMenuEntry(BindableObject.DisplayName, FText(), FSlateIcon(), AddMenuAction);
}
}
return AddMenuBuilder.MakeWidget();
}
void FWidgetBlueprintEditor::AddObjectToAnimation(UObject* ObjectToAnimate)
{
// @todo Sequencer - Make this kind of adding more explicit, this current setup seem a bit brittle.
Sequencer->GetHandleToObject(ObjectToAnimate);
}
#undef LOCTEXT_NAMESPACE