2014-08-05 09:04:35 -04:00
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
# include "IntroTutorialsPrivatePCH.h"
# include "STutorialRoot.h"
# include "SEditorTutorials.h"
# include "EditorTutorialSettings.h"
2014-08-28 06:22:40 -04:00
# include "TutorialStateSettings.h"
2014-09-17 05:44:56 -04:00
# include "AssetEditorManager.h"
# include "ToolkitManager.h"
2014-09-17 08:30:32 -04:00
# include "IToolkit.h"
# include "IToolkitHost.h"
2014-09-18 08:10:29 -04:00
# include "EngineAnalytics.h"
# include "Runtime/Analytics/Analytics/Public/Interfaces/IAnalyticsProvider.h"
2014-09-18 16:42:40 -04:00
# include "Editor/MainFrame/Public/Interfaces/IMainFrameModule.h"
2014-08-05 09:04:35 -04:00
# define LOCTEXT_NAMESPACE "STutorialRoot"
void STutorialRoot : : Construct ( const FArguments & InArgs )
{
CurrentTutorial = nullptr ;
CurrentTutorialStage = 0 ;
2014-09-18 08:10:29 -04:00
CurrentTutorialStartTime = FPlatformTime : : Seconds ( ) ;
2014-08-05 09:04:35 -04:00
ChildSlot
[
SNullWidget : : NullWidget
] ;
}
void STutorialRoot : : MaybeAddOverlay ( TSharedRef < SWindow > InWindow )
{
if ( InWindow - > HasOverlay ( ) )
{
// check we dont already have a widget overlay for this window
TWeakPtr < SEditorTutorials > * FoundWidget = TutorialWidgets . Find ( InWindow ) ;
if ( FoundWidget = = nullptr )
{
TSharedPtr < SEditorTutorials > TutorialWidget = nullptr ;
InWindow - > AddOverlaySlot ( )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. VAlign ( VAlign_Fill )
. HAlign ( HAlign_Fill )
[
SAssignNew ( TutorialWidget , SEditorTutorials )
. ParentWindow ( InWindow )
. OnNextClicked ( FOnNextClicked : : CreateSP ( this , & STutorialRoot : : HandleNextClicked ) )
. OnBackClicked ( FSimpleDelegate : : CreateSP ( this , & STutorialRoot : : HandleBackClicked ) )
. OnHomeClicked ( FSimpleDelegate : : CreateSP ( this , & STutorialRoot : : HandleHomeClicked ) )
2014-09-18 08:10:29 -04:00
. OnCloseClicked ( FSimpleDelegate : : CreateSP ( this , & STutorialRoot : : HandleCloseClicked ) )
2014-08-05 09:04:35 -04:00
. OnGetCurrentTutorial ( FOnGetCurrentTutorial : : CreateSP ( this , & STutorialRoot : : HandleGetCurrentTutorial ) )
. OnGetCurrentTutorialStage ( FOnGetCurrentTutorialStage : : CreateSP ( this , & STutorialRoot : : HandleGetCurrentTutorialStage ) )
. OnLaunchTutorial ( FOnLaunchTutorial : : CreateSP ( this , & STutorialRoot : : LaunchTutorial ) )
2014-09-22 09:42:52 -04:00
. OnWasWidgetDrawn ( FOnWasWidgetDrawn : : CreateSP ( this , & STutorialRoot : : WasWidgetDrawn ) )
. OnWidgetWasDrawn ( FOnWidgetWasDrawn : : CreateSP ( this , & STutorialRoot : : WidgetWasDrawn ) )
2014-08-05 09:04:35 -04:00
]
] ;
FoundWidget = & TutorialWidgets . Add ( InWindow , TutorialWidget ) ;
FoundWidget - > Pin ( ) - > RebuildCurrentContent ( ) ;
}
}
TArray < TSharedRef < SWindow > > ChildWindows = InWindow - > GetChildWindows ( ) ;
for ( auto & ChildWindow : ChildWindows )
{
MaybeAddOverlay ( ChildWindow ) ;
}
}
void STutorialRoot : : Tick ( const FGeometry & AllottedGeometry , const double InCurrentTime , const float InDeltaTime )
{
TArray < TSharedRef < SWindow > > Windows = FSlateApplication : : Get ( ) . GetInteractiveTopLevelWindows ( ) ;
for ( auto & Window : Windows )
{
MaybeAddOverlay ( Window ) ;
}
2014-09-22 09:42:52 -04:00
// empty array but leave us the slack (we dont want to reallocate all the time, and this array should never grow too large)
PreviouslyDrawnWidgets . Empty ( PreviouslyDrawnWidgets . Max ( ) ) ;
PreviouslyDrawnWidgets . Append ( MoveTemp ( DrawnWidgets ) ) ;
DrawnWidgets . Empty ( DrawnWidgets . Max ( ) ) ;
2014-08-05 09:04:35 -04:00
}
2014-09-18 16:42:40 -04:00
void STutorialRoot : : SummonTutorialBrowser ( TWeakPtr < SWindow > InWindow , const FString & InFilter )
2014-08-05 09:04:35 -04:00
{
2014-09-18 16:42:40 -04:00
// Use main frame if non specified
if ( ! InWindow . IsValid ( ) )
{
IMainFrameModule & MainFrameModule = FModuleManager : : LoadModuleChecked < IMainFrameModule > ( TEXT ( " MainFrame " ) ) ;
InWindow = MainFrameModule . GetParentWindow ( ) ;
}
2014-08-05 09:04:35 -04:00
TWeakPtr < SEditorTutorials > * FoundWidget = TutorialWidgets . Find ( InWindow ) ;
if ( FoundWidget ! = nullptr & & FoundWidget - > IsValid ( ) )
{
FoundWidget - > Pin ( ) - > ShowBrowser ( InFilter ) ;
}
}
2014-08-28 06:22:40 -04:00
void STutorialRoot : : LaunchTutorial ( UEditorTutorial * InTutorial , bool bInRestart , TWeakPtr < SWindow > InNavigationWindow , FSimpleDelegate InOnTutorialClosed , FSimpleDelegate InOnTutorialExited )
2014-08-05 09:04:35 -04:00
{
if ( InTutorial ! = nullptr )
{
CurrentTutorial = InTutorial ;
bool bHaveSeenTutorial = false ;
2014-08-28 06:22:40 -04:00
CurrentTutorialStage = bInRestart ? 0 : GetDefault < UTutorialStateSettings > ( ) - > GetProgress ( CurrentTutorial , bHaveSeenTutorial ) ;
2014-08-05 09:04:35 -04:00
2014-09-17 05:44:56 -04:00
// check if we should be launching this tutorial for an asset editor
if ( InTutorial - > AssetToUse . IsValid ( ) )
{
TArray < FString > AssetPaths ;
AssetPaths . Add ( InTutorial - > AssetToUse . AssetLongPathname ) ;
FAssetEditorManager : : Get ( ) . OpenEditorsForAssets ( AssetPaths ) ;
UObject * Asset = InTutorial - > AssetToUse . ResolveObject ( ) ;
if ( Asset ! = nullptr )
{
TSharedPtr < IToolkit > Toolkit = FToolkitManager : : Get ( ) . FindEditorForAsset ( Asset ) ;
if ( Toolkit . IsValid ( ) )
{
InNavigationWindow = FSlateApplication : : Get ( ) . FindWidgetWindow ( Toolkit - > GetToolkitHost ( ) - > GetParentWidget ( ) ) ;
2014-09-22 09:41:50 -04:00
// make sure we have a valid tutorial overlay
if ( InNavigationWindow . IsValid ( ) )
{
MaybeAddOverlay ( InNavigationWindow . Pin ( ) . ToSharedRef ( ) ) ;
}
2014-09-17 05:44:56 -04:00
}
}
}
2014-09-18 08:10:29 -04:00
CurrentTutorialStartTime = FPlatformTime : : Seconds ( ) ;
2014-08-05 09:04:35 -04:00
// launch tutorial for all windows we wrap - any tutorial can display over any window
for ( auto & TutorialWidget : TutorialWidgets )
{
if ( TutorialWidget . Value . IsValid ( ) )
{
2014-08-20 10:27:41 -04:00
bool bIsNavigationWindow = false ;
if ( ! InNavigationWindow . IsValid ( ) )
{
bIsNavigationWindow = TutorialWidget . Value . Pin ( ) - > IsNavigationVisible ( ) ;
}
else
{
bIsNavigationWindow = ( TutorialWidget . Value . Pin ( ) - > GetParentWindow ( ) = = InNavigationWindow . Pin ( ) ) ;
}
2014-08-28 06:22:40 -04:00
TutorialWidget . Value . Pin ( ) - > LaunchTutorial ( bIsNavigationWindow , InOnTutorialClosed , InOnTutorialExited ) ;
2014-08-05 09:04:35 -04:00
}
}
}
}
2014-08-28 06:22:40 -04:00
void STutorialRoot : : CloseAllTutorialContent ( )
{
for ( auto & TutorialWidget : TutorialWidgets )
{
if ( TutorialWidget . Value . IsValid ( ) )
{
TutorialWidget . Value . Pin ( ) - > HideContent ( ) ;
}
}
}
2014-08-05 09:04:35 -04:00
void STutorialRoot : : HandleNextClicked ( TWeakPtr < SWindow > InNavigationWindow )
{
GoToNextStage ( InNavigationWindow ) ;
for ( auto & TutorialWidget : TutorialWidgets )
{
if ( TutorialWidget . Value . IsValid ( ) )
{
TSharedPtr < SEditorTutorials > PinnedTutorialWidget = TutorialWidget . Value . Pin ( ) ;
PinnedTutorialWidget - > RebuildCurrentContent ( ) ;
}
}
}
void STutorialRoot : : HandleBackClicked ( )
{
2014-09-18 08:10:29 -04:00
if ( FEngineAnalytics : : IsAvailable ( ) & & CurrentTutorial ! = nullptr )
{
TArray < FAnalyticsEventAttribute > EventAttributes ;
EventAttributes . Add ( FAnalyticsEventAttribute ( TEXT ( " Context.Tutorial " ) , FIntroTutorials : : AnalyticsEventNameFromTutorial ( TEXT ( " " ) , CurrentTutorial ) ) ) ;
EventAttributes . Add ( FAnalyticsEventAttribute ( TEXT ( " Context.StageIndex " ) , CurrentTutorialStage ) ) ;
FEngineAnalytics : : GetProvider ( ) . RecordEvent ( TEXT ( " Rocket.Tutorials.ClickedBackButton " ) , EventAttributes ) ;
}
2014-08-05 09:04:35 -04:00
GoToPreviousStage ( ) ;
for ( auto & TutorialWidget : TutorialWidgets )
{
if ( TutorialWidget . Value . IsValid ( ) )
{
TSharedPtr < SEditorTutorials > PinnedTutorialWidget = TutorialWidget . Value . Pin ( ) ;
PinnedTutorialWidget - > RebuildCurrentContent ( ) ;
}
}
}
void STutorialRoot : : HandleHomeClicked ( )
{
if ( CurrentTutorial ! = nullptr )
{
2014-08-28 06:22:40 -04:00
GetMutableDefault < UTutorialStateSettings > ( ) - > RecordProgress ( CurrentTutorial , CurrentTutorialStage ) ;
GetMutableDefault < UTutorialStateSettings > ( ) - > SaveProgress ( ) ;
2014-08-05 09:04:35 -04:00
}
CurrentTutorial = nullptr ;
CurrentTutorialStage = 0 ;
for ( auto & TutorialWidget : TutorialWidgets )
{
if ( TutorialWidget . Value . IsValid ( ) )
{
TSharedPtr < SEditorTutorials > PinnedTutorialWidget = TutorialWidget . Value . Pin ( ) ;
PinnedTutorialWidget - > RebuildCurrentContent ( ) ;
}
}
}
UEditorTutorial * STutorialRoot : : HandleGetCurrentTutorial ( )
{
return CurrentTutorial ;
}
int32 STutorialRoot : : HandleGetCurrentTutorialStage ( )
{
return CurrentTutorialStage ;
}
void STutorialRoot : : AddReferencedObjects ( FReferenceCollector & Collector )
{
if ( CurrentTutorial ! = nullptr )
{
Collector . AddReferencedObject ( CurrentTutorial ) ;
}
}
void STutorialRoot : : GoToPreviousStage ( )
{
if ( CurrentTutorial ! = nullptr )
{
2014-08-20 10:27:41 -04:00
int32 PreviousTutorialStage = CurrentTutorialStage ;
if ( CurrentTutorialStage > 0 )
{
CurrentTutorial - > HandleTutorialStageEnded ( CurrentTutorial - > Stages [ CurrentTutorialStage ] . Name ) ;
}
2014-08-05 09:04:35 -04:00
CurrentTutorialStage = FMath : : Max ( 0 , CurrentTutorialStage - 1 ) ;
2014-08-20 10:27:41 -04:00
if ( PreviousTutorialStage ! = CurrentTutorialStage )
{
CurrentTutorial - > HandleTutorialStageStarted ( CurrentTutorial - > Stages [ CurrentTutorialStage ] . Name ) ;
}
2014-08-05 09:04:35 -04:00
}
}
void STutorialRoot : : GoToNextStage ( TWeakPtr < SWindow > InNavigationWindow )
{
if ( CurrentTutorial ! = nullptr )
{
2014-08-20 10:27:41 -04:00
UEditorTutorial * PreviousTutorial = CurrentTutorial ;
int32 PreviousTutorialStage = CurrentTutorialStage ;
2014-09-11 03:56:58 -04:00
if ( CurrentTutorialStage < CurrentTutorial - > Stages . Num ( ) )
{
CurrentTutorial - > HandleTutorialStageEnded ( CurrentTutorial - > Stages [ CurrentTutorialStage ] . Name ) ;
}
2014-08-20 10:27:41 -04:00
2014-08-05 09:04:35 -04:00
if ( CurrentTutorialStage + 1 > = CurrentTutorial - > Stages . Num ( ) & & FName ( * CurrentTutorial - > NextTutorial . AssetLongPathname ) ! = NAME_None )
{
TSubclassOf < UEditorTutorial > NextTutorialClass = LoadClass < UEditorTutorial > ( NULL , * CurrentTutorial - > NextTutorial . AssetLongPathname , NULL , LOAD_None , NULL ) ;
if ( NextTutorialClass ! = nullptr )
{
2014-08-28 06:22:40 -04:00
LaunchTutorial ( NextTutorialClass - > GetDefaultObject < UEditorTutorial > ( ) , true , InNavigationWindow , FSimpleDelegate ( ) , FSimpleDelegate ( ) ) ;
2014-08-05 09:04:35 -04:00
}
else
{
FSlateNotificationManager : : Get ( ) . AddNotification ( FNotificationInfo ( FText : : Format ( LOCTEXT ( " TutorialNotFound " , " Could not start next tutorial {0} " ) , FText : : FromString ( CurrentTutorial - > NextTutorial . AssetLongPathname ) ) ) ) ;
}
}
else
{
CurrentTutorialStage = FMath : : Min ( CurrentTutorialStage + 1 , CurrentTutorial - > Stages . Num ( ) - 1 ) ;
2014-08-28 06:22:40 -04:00
GetMutableDefault < UTutorialStateSettings > ( ) - > RecordProgress ( CurrentTutorial , CurrentTutorialStage ) ;
2014-08-05 09:04:35 -04:00
}
2014-08-20 10:27:41 -04:00
2014-08-28 06:22:40 -04:00
if ( CurrentTutorial ! = nullptr & & CurrentTutorialStage < CurrentTutorial - > Stages . Num ( ) & & ( CurrentTutorial ! = PreviousTutorial | | CurrentTutorialStage ! = PreviousTutorialStage ) )
2014-08-20 10:27:41 -04:00
{
CurrentTutorial - > HandleTutorialStageStarted ( CurrentTutorial - > Stages [ CurrentTutorialStage ] . Name ) ;
}
2014-08-05 09:04:35 -04:00
}
}
2014-09-18 08:10:29 -04:00
void STutorialRoot : : HandleCloseClicked ( )
{
// submit analytics data
if ( FEngineAnalytics : : IsAvailable ( ) & & CurrentTutorial ! = nullptr & & CurrentTutorialStage < CurrentTutorial - > Stages . Num ( ) )
{
UEditorTutorial * AttractTutorial = nullptr ;
UEditorTutorial * LaunchTutorial = nullptr ;
FString BrowserFilter ;
GetDefault < UEditorTutorialSettings > ( ) - > FindTutorialInfoForContext ( TEXT ( " LevelEditor " ) , AttractTutorial , LaunchTutorial , BrowserFilter ) ;
// prepare and send analytics data
bool const bClosedInitialAttract = ( CurrentTutorial = = AttractTutorial ) ;
FString const CurrentExcerptTitle = bClosedInitialAttract ? TEXT ( " InitialAttract " ) : CurrentTutorial - > Stages [ CurrentTutorialStage ] . Name . ToString ( ) ;
int32 const CurrentExcerptIndex = bClosedInitialAttract ? - 1 : CurrentTutorialStage ;
float const CurrentPageElapsedTime = bClosedInitialAttract ? 0.f : ( float ) ( FPlatformTime : : Seconds ( ) - CurrentTutorialStartTime ) ;
TArray < FAnalyticsEventAttribute > EventAttributes ;
EventAttributes . Add ( FAnalyticsEventAttribute ( TEXT ( " LastStageIndex " ) , CurrentExcerptIndex ) ) ;
EventAttributes . Add ( FAnalyticsEventAttribute ( TEXT ( " LastStageTitle " ) , CurrentExcerptTitle ) ) ;
EventAttributes . Add ( FAnalyticsEventAttribute ( TEXT ( " TimeSpentInTutorial " ) , CurrentPageElapsedTime ) ) ;
FEngineAnalytics : : GetProvider ( ) . RecordEvent ( FIntroTutorials : : AnalyticsEventNameFromTutorial ( TEXT ( " Rocket.Tutorials.Closed " ) , CurrentTutorial ) , EventAttributes ) ;
}
}
2014-09-22 09:42:52 -04:00
bool STutorialRoot : : WasWidgetDrawn ( const FName & InName ) const
{
for ( const auto & WidgetName : PreviouslyDrawnWidgets )
{
if ( InName = = WidgetName )
{
return true ;
}
}
return false ;
}
void STutorialRoot : : WidgetWasDrawn ( const FName & InName )
{
DrawnWidgets . Add ( InName ) ;
}
2014-08-05 09:04:35 -04:00
# undef LOCTEXT_NAMESPACE