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