Files
UnrealEngineUWP/Engine/Source/Editor/IntroTutorials/Private/STutorialOverlay.cpp
Thomas Sarkanen 1bca86ecf1 Fixed stalls in tutorials
API break: added a new pure virtual to IAssetEditorInstance - InvokeTab().

Tutorials were attempting to load a bogus asset for editing for each stage, causing stalls each time the user clicked Next.
Re-arranged code that attempted to open asset editor tabs so that it wasnt attempting ot open a bogus asset (used the correct member).
Order of tab manager preference is now Per-stage Blueprint asset editor->Per-'tutorial asset' editor->level editor.
Added new function to allow us to open tabs in asset editors. This avoids a dangerous up-cast to FAssetEditorToolkit to access its tab manager.

#codereview Bruce.Nesbit

[CL 2403225 by Thomas Sarkanen in Main branch]
2015-01-12 04:36:22 -05:00

269 lines
9.6 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "IntroTutorialsPrivatePCH.h"
#include "STutorialOverlay.h"
#include "STutorialContent.h"
#include "EditorTutorial.h"
#include "IntroTutorials.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "LevelEditor.h"
#include "BlueprintEditorUtils.h"
#include "Guid.h"
#include "BlueprintEditor.h"
static FName IntroTutorialsModuleName("IntroTutorials");
void STutorialOverlay::Construct(const FArguments& InArgs, UEditorTutorial* InTutorial, FTutorialStage* const InStage)
{
ParentWindow = InArgs._ParentWindow;
bIsStandalone = InArgs._IsStandalone;
OnClosed = InArgs._OnClosed;
bHasValidContent = InStage != nullptr;
OnWidgetWasDrawn = InArgs._OnWidgetWasDrawn;
TSharedPtr<SOverlay> Overlay;
ChildSlot
[
SAssignNew(Overlay, SOverlay)
+SOverlay::Slot()
[
SAssignNew(OverlayCanvas, SCanvas)
]
];
if(InStage != nullptr)
{
// add non-widget content, if any
if(InArgs._AllowNonWidgetContent && InStage->Content.Type != ETutorialContent::None)
{
Overlay->AddSlot()
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
[
SNew(STutorialContent, InTutorial, InStage->Content)
.OnClosed(InArgs._OnClosed)
.OnNextClicked(InArgs._OnNextClicked)
.OnHomeClicked(InArgs._OnHomeClicked)
.OnBackClicked(InArgs._OnBackClicked)
.IsBackEnabled(InArgs._IsBackEnabled)
.IsHomeEnabled(InArgs._IsHomeEnabled)
.IsNextEnabled(InArgs._IsNextEnabled)
.IsStandalone(InArgs._IsStandalone)
.WrapTextAt(600.0f)
]
];
}
if(InStage->WidgetContent.Num() > 0)
{
FIntroTutorials& IntroTutorials = FModuleManager::Get().GetModuleChecked<FIntroTutorials>("IntroTutorials");
// now add canvas slots for widget-bound content
for (const FTutorialWidgetContent& WidgetContent : InStage->WidgetContent)
{
if (WidgetContent.Content.Type != ETutorialContent::None)
{
TSharedPtr<STutorialContent> ContentWidget =
SNew(STutorialContent, InTutorial, WidgetContent.Content)
.HAlign(WidgetContent.HorizontalAlignment)
.VAlign(WidgetContent.VerticalAlignment)
.Offset(WidgetContent.Offset)
.IsStandalone(bIsStandalone)
.OnClosed(InArgs._OnClosed)
.OnNextClicked(InArgs._OnNextClicked)
.OnHomeClicked(InArgs._OnHomeClicked)
.OnBackClicked(InArgs._OnBackClicked)
.IsBackEnabled(InArgs._IsBackEnabled)
.IsHomeEnabled(InArgs._IsHomeEnabled)
.IsNextEnabled(InArgs._IsNextEnabled)
.WrapTextAt(WidgetContent.ContentWidth)
.Anchor(WidgetContent.WidgetAnchor)
.AllowNonWidgetContent(InArgs._AllowNonWidgetContent)
.OnWasWidgetDrawn(InArgs._OnWasWidgetDrawn)
.NextButtonText(InStage->NextButtonText);
PerformWidgetInteractions(InTutorial, WidgetContent);
OverlayCanvas->AddSlot()
.Position(TAttribute<FVector2D>::Create(TAttribute<FVector2D>::FGetter::CreateSP(ContentWidget.Get(), &STutorialContent::GetPosition)))
.Size(TAttribute<FVector2D>::Create(TAttribute<FVector2D>::FGetter::CreateSP(ContentWidget.Get(), &STutorialContent::GetSize)))
[
ContentWidget.ToSharedRef()
];
OnPaintNamedWidget.AddSP(ContentWidget.Get(), &STutorialContent::HandlePaintNamedWidget);
OnResetNamedWidget.AddSP(ContentWidget.Get(), &STutorialContent::HandleResetNamedWidget);
OnCacheWindowSize.AddSP(ContentWidget.Get(), &STutorialContent::HandleCacheWindowSize);
}
}
}
}
}
int32 STutorialOverlay::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
{
if(ParentWindow.IsValid())
{
bool bIsPicking = false;
FName WidgetNameToHighlight = NAME_None;
FIntroTutorials& IntroTutorials = FModuleManager::Get().GetModuleChecked<FIntroTutorials>(IntroTutorialsModuleName);
if(IntroTutorials.OnIsPicking().IsBound())
{
bIsPicking = IntroTutorials.OnIsPicking().Execute(WidgetNameToHighlight);
}
if(bIsPicking || bHasValidContent)
{
TSharedPtr<SWindow> PinnedWindow = ParentWindow.Pin();
OnResetNamedWidget.Broadcast();
OnCacheWindowSize.Broadcast(PinnedWindow->GetWindowGeometryInWindow().Size);
LayerId = TraverseWidgets(PinnedWindow.ToSharedRef(), PinnedWindow->GetWindowGeometryInWindow(), MyClippingRect, OutDrawElements, LayerId);
}
}
return SCompoundWidget::OnPaint(Args, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
}
int32 STutorialOverlay::TraverseWidgets(TSharedRef<SWidget> InWidget, const FGeometry& InGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId) const
{
bool bIsPicking = false;
bool bShouldHighlight = false;
bool bShouldDraw = false;
FName WidgetNameToHighlight = NAME_None;
FIntroTutorials& IntroTutorials = FModuleManager::Get().GetModuleChecked<FIntroTutorials>(IntroTutorialsModuleName);
if (IntroTutorials.OnValidatePickingCandidate().IsBound())
{
bIsPicking = IntroTutorials.OnValidatePickingCandidate().Execute(InWidget,WidgetNameToHighlight,bShouldHighlight);
}
// First draw the widget if we should
TSharedPtr<FTagMetaData> MetaData = InWidget->GetMetaData<FTagMetaData>();
const FName Tag = (MetaData.IsValid() && MetaData->Tag.IsValid()) ? MetaData->Tag : InWidget->GetTag();
if (Tag != NAME_None || MetaData.IsValid())
{
// we are a named widget - ask it to draw
OnPaintNamedWidget.Broadcast(InWidget, InGeometry);
OnWidgetWasDrawn.ExecuteIfBound(Tag);
}
// Next check and draw the highlight as appropriate
if (bIsPicking == true)
{
// if we are picking, we need to draw an outline here
if(WidgetNameToHighlight != NAME_None )
{
if(bIsPicking == true)
{
const FLinearColor Color = bIsPicking && bShouldHighlight ? FLinearColor::Green : FLinearColor::White;
FSlateDrawElement::MakeBox(OutDrawElements, LayerId++, InGeometry.ToPaintGeometry(), FCoreStyle::Get().GetBrush(TEXT("Debug.Border")), MyClippingRect, ESlateDrawEffect::None, Color);
}
}
}
FArrangedChildren ArrangedChildren(EVisibility::Visible);
InWidget->ArrangeChildren(InGeometry, ArrangedChildren);
for(int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ChildIndex++)
{
const FArrangedWidget& ArrangedWidget = ArrangedChildren[ChildIndex];
LayerId = TraverseWidgets(ArrangedWidget.Widget, ArrangedWidget.Geometry, MyClippingRect, OutDrawElements, LayerId);
}
return LayerId;
}
void STutorialOverlay::PerformWidgetInteractions(UEditorTutorial* InTutorial, const FTutorialWidgetContent &WidgetContent)
{
// Open any browser we need too
OpenBrowserForWidgetAnchor(InTutorial, WidgetContent);
FocusOnAnyBlueprintNodes(WidgetContent);
}
void STutorialOverlay::OpenBrowserForWidgetAnchor(UEditorTutorial* InTutorial, const FTutorialWidgetContent &WidgetContent)
{
if(!WidgetContent.WidgetAnchor.TabToFocusOrOpen.IsEmpty())
{
IAssetEditorInstance* AssetEditor = nullptr;
// Check to see if we can find a blueprint relevant to this node and open the editor for that (Then try to get the tabmanager from that)
if(WidgetContent.WidgetAnchor.OuterName.Len() > 0)
{
// Remove the prefix from the name
int32 Space = WidgetContent.WidgetAnchor.OuterName.Find(TEXT(" "));
FString Name = WidgetContent.WidgetAnchor.OuterName.RightChop(Space + 1);
TArray<FString> AssetPaths;
AssetPaths.Add(Name);
FAssetEditorManager::Get().OpenEditorsForAssets(AssetPaths);
UObject* Blueprint = FindObject<UObject>(ANY_PACKAGE, *Name);
// If we found a blueprint
if(Blueprint != nullptr)
{
IAssetEditorInstance* PotentialAssetEditor = FAssetEditorManager::Get().FindEditorForAsset(Blueprint, false);
if(PotentialAssetEditor != nullptr)
{
AssetEditor = PotentialAssetEditor;
}
}
}
// If we haven't found a tab manager, next check the asset editor that we reference in this tutorial, if any
if(AssetEditor == nullptr)
{
// Try looking for the object that this tutorial references (it should already be loaded by this tutorial if it exists).
UObject* AssetObject = InTutorial->AssetToUse.ResolveObject();
if(AssetObject != nullptr)
{
IAssetEditorInstance* PotentialAssetEditor = FAssetEditorManager::Get().FindEditorForAsset(AssetObject, false);
if(PotentialAssetEditor != nullptr)
{
AssetEditor = PotentialAssetEditor;
}
}
}
// Invoke any tab
if(AssetEditor != nullptr)
{
AssetEditor->InvokeTab(FTabId(*WidgetContent.WidgetAnchor.TabToFocusOrOpen));
}
else
{
// fallback to trying the main level editor tab manager
FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>(TEXT("LevelEditor"));
TSharedPtr<FTabManager> LevelEditorTabManager = LevelEditorModule.GetLevelEditorTabManager();
LevelEditorTabManager->InvokeTab(FName(*WidgetContent.WidgetAnchor.TabToFocusOrOpen));
}
}
}
void STutorialOverlay::FocusOnAnyBlueprintNodes(const FTutorialWidgetContent &WidgetContent)
{
if (WidgetContent.bAutoFocus == false)
{
return;
}
FString Name = WidgetContent.WidgetAnchor.OuterName;
int32 NameIndex;
Name.FindLastChar(TEXT('.'), NameIndex);
FString BlueprintName = Name.RightChop(NameIndex + 1);
UBlueprint* Blueprint = FindObject<UBlueprint>(ANY_PACKAGE, *BlueprintName);
// If we find a blueprint
if (Blueprint != nullptr)
{
// Try to grab guid
FGuid NodeGuid;
FGuid::Parse(WidgetContent.WidgetAnchor.GUIDString, NodeGuid);
UEdGraphNode* OutNode = NULL;
if (UEdGraphNode* GraphNode = FBlueprintEditorUtils::GetNodeByGUID(Blueprint, NodeGuid))
{
FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(GraphNode, false);
}
}
}