You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
790 lines
23 KiB
C++
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
|