2014-08-05 09:04:35 -04:00
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
# include "IntroTutorialsPrivatePCH.h"
# include "TutorialStructCustomization.h"
# include "EditorTutorial.h"
2014-08-14 07:02:50 -04:00
# include "STutorialEditableText.h"
2014-09-03 08:26:29 -04:00
# include "ISlateMetaData.h"
# include "TutorialMetaData.h"
2014-08-05 09:04:35 -04:00
# define LOCTEXT_NAMESPACE "TutorialStructCustomization"
TSharedRef < IPropertyTypeCustomization > FTutorialContentCustomization : : MakeInstance ( )
{
return MakeShareable ( new FTutorialContentCustomization ) ;
}
void FTutorialContentCustomization : : CustomizeHeader ( TSharedRef < class IPropertyHandle > InStructPropertyHandle , class FDetailWidgetRow & HeaderRow , IPropertyTypeCustomizationUtils & StructCustomizationUtils )
{
TSharedPtr < IPropertyHandle > TypeProperty = InStructPropertyHandle - > GetChildHandle ( GET_MEMBER_NAME_CHECKED ( FTutorialContent , Type ) ) ;
TSharedPtr < IPropertyHandle > ContentProperty = InStructPropertyHandle - > GetChildHandle ( GET_MEMBER_NAME_CHECKED ( FTutorialContent , Content ) ) ;
TSharedPtr < IPropertyHandle > ExcerptNameProperty = InStructPropertyHandle - > GetChildHandle ( GET_MEMBER_NAME_CHECKED ( FTutorialContent , ExcerptName ) ) ;
TSharedPtr < IPropertyHandle > TextProperty = InStructPropertyHandle - > GetChildHandle ( GET_MEMBER_NAME_CHECKED ( FTutorialContent , Text ) ) ;
// Show and hide various widgets based on current content type
struct Local
{
static EVisibility GetContentVisibility ( TSharedPtr < IPropertyHandle > InPropertyHandle )
{
check ( InPropertyHandle . IsValid ( ) ) ;
uint8 Value = 0 ;
if ( InPropertyHandle - > GetValue ( Value ) = = FPropertyAccess : : Success )
{
const ETutorialContent : : Type EnumValue = ( ETutorialContent : : Type ) Value ;
return ( EnumValue = = ETutorialContent : : UDNExcerpt ) ? EVisibility : : Visible : EVisibility : : Collapsed ;
}
return EVisibility : : Collapsed ;
}
static EVisibility GetExcerptNameVisibility ( TSharedPtr < IPropertyHandle > InPropertyHandle )
{
check ( InPropertyHandle . IsValid ( ) ) ;
uint8 Value = 0 ;
if ( InPropertyHandle - > GetValue ( Value ) = = FPropertyAccess : : Success )
{
const ETutorialContent : : Type EnumValue = ( ETutorialContent : : Type ) Value ;
return ( EnumValue = = ETutorialContent : : UDNExcerpt ) ? EVisibility : : Visible : EVisibility : : Collapsed ;
}
return EVisibility : : Collapsed ;
}
static EVisibility GetTextVisibility ( TSharedPtr < IPropertyHandle > InPropertyHandle )
{
check ( InPropertyHandle . IsValid ( ) ) ;
uint8 Value = 0 ;
if ( InPropertyHandle - > GetValue ( Value ) = = FPropertyAccess : : Success )
{
const ETutorialContent : : Type EnumValue = ( ETutorialContent : : Type ) Value ;
return ( EnumValue = = ETutorialContent : : Text ) ? EVisibility : : Visible : EVisibility : : Collapsed ;
}
return EVisibility : : Collapsed ;
}
2014-08-14 07:02:50 -04:00
static EVisibility GetRichTextVisibility ( TSharedPtr < IPropertyHandle > InPropertyHandle )
{
check ( InPropertyHandle . IsValid ( ) ) ;
uint8 Value = 0 ;
if ( InPropertyHandle - > GetValue ( Value ) = = FPropertyAccess : : Success )
{
const ETutorialContent : : Type EnumValue = ( ETutorialContent : : Type ) Value ;
return ( EnumValue = = ETutorialContent : : RichText ) ? EVisibility : : Visible : EVisibility : : Collapsed ;
}
return EVisibility : : Collapsed ;
}
static FText GetValueAsText ( TSharedPtr < IPropertyHandle > InPropertyHandle )
{
FText Text ;
if ( InPropertyHandle - > GetValueAsFormattedText ( Text ) = = FPropertyAccess : : MultipleValues )
{
Text = NSLOCTEXT ( " PropertyEditor " , " MultipleValues " , " Multiple Values " ) ;
}
return Text ;
}
static void OnTextCommitted ( const FText & NewText , ETextCommit : : Type /*CommitInfo*/ , TSharedPtr < IPropertyHandle > InPropertyHandle )
{
InPropertyHandle - > SetValueFromFormattedString ( NewText . ToString ( ) ) ;
}
static void OnTextChanged ( const FText & NewText , TSharedPtr < IPropertyHandle > InPropertyHandle )
{
InPropertyHandle - > SetValueFromFormattedString ( NewText . ToString ( ) ) ;
}
2014-08-05 09:04:35 -04:00
} ;
HeaderRow
. NameContent ( )
[
ContentProperty - > CreatePropertyNameWidget ( )
]
. ValueContent ( )
. MinDesiredWidth ( 250.0f )
[
SNew ( SVerticalBox )
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
. Padding ( 0.0f , 2.0f )
[
TypeProperty - > CreatePropertyValueWidget ( )
]
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
. Padding ( 0.0f , 2.0f )
[
SNew ( SHorizontalBox )
. Visibility_Static ( & Local : : GetContentVisibility , TypeProperty )
+ SHorizontalBox : : Slot ( )
[
ContentProperty - > CreatePropertyValueWidget ( )
]
]
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
. Padding ( 0.0f , 2.0f )
[
SNew ( SHorizontalBox )
. Visibility_Static ( & Local : : GetExcerptNameVisibility , TypeProperty )
+ SHorizontalBox : : Slot ( )
[
ExcerptNameProperty - > CreatePropertyValueWidget ( )
]
]
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
. Padding ( 0.0f , 2.0f )
[
SNew ( SHorizontalBox )
. Visibility_Static ( & Local : : GetTextVisibility , TypeProperty )
+ SHorizontalBox : : Slot ( )
[
TextProperty - > CreatePropertyValueWidget ( )
]
]
2014-08-14 07:02:50 -04:00
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
. Padding ( 0.0f , 2.0f )
[
SNew ( SHorizontalBox )
. Visibility_Static ( & Local : : GetRichTextVisibility , TypeProperty )
+ SHorizontalBox : : Slot ( )
[
SNew ( STutorialEditableText )
. Text_Static ( & Local : : GetValueAsText , TextProperty )
. OnTextCommitted ( FOnTextCommitted : : CreateStatic ( & Local : : OnTextCommitted , TextProperty ) )
. OnTextChanged ( FOnTextChanged : : CreateStatic ( & Local : : OnTextChanged , TextProperty ) )
]
]
2014-08-05 09:04:35 -04:00
] ;
}
void FTutorialContentCustomization : : CustomizeChildren ( TSharedRef < class IPropertyHandle > InStructPropertyHandle , class IDetailChildrenBuilder & StructBuilder , IPropertyTypeCustomizationUtils & StructCustomizationUtils )
{
}
/** 'Tooltip' window to indicate what is currently being picked and how to abort the picker (Esc) */
class SWidgetPickerFloatingWindow : public SCompoundWidget
{
SLATE_BEGIN_ARGS ( SWidgetPickerFloatingWindow ) { }
SLATE_ARGUMENT ( TWeakPtr < SWindow > , ParentWindow )
SLATE_END_ARGS ( )
void Construct ( const FArguments & InArgs , TSharedRef < class IPropertyHandle > InStructPropertyHandle )
{
StructPropertyHandle = InStructPropertyHandle ;
ParentWindow = InArgs . _ParentWindow ;
ChildSlot
[
SNew ( SToolTip )
. Text ( this , & SWidgetPickerFloatingWindow : : GetPickerStatusText )
] ;
}
FName GetPickedWidgetName ( )
{
return PickedWidgetName ;
}
private :
virtual void Tick ( const FGeometry & AllottedGeometry , const double InCurrentTime , const float InDeltaTime ) override
{
PickedWidgetName = NAME_None ;
2014-09-03 08:26:29 -04:00
PickedAllMetaData . Reset ( ) ;
2014-08-05 09:04:35 -04:00
FWidgetPath Path = FSlateApplication : : Get ( ) . LocateWindowUnderMouse ( FSlateApplication : : Get ( ) . GetCursorPos ( ) , FSlateApplication : : Get ( ) . GetInteractiveTopLevelWindows ( ) , true ) ;
for ( int32 PathIndex = Path . Widgets . Num ( ) - 1 ; PathIndex > = 0 ; PathIndex - - )
{
2014-08-25 12:51:49 -04:00
TSharedRef < SWidget > PathWidget = Path . Widgets [ PathIndex ] . Widget ;
2014-08-05 09:04:35 -04:00
if ( PathWidget - > GetTag ( ) ! = NAME_None )
{
PickedWidgetName = PathWidget - > GetTag ( ) ;
break ;
}
2014-08-28 04:32:19 -04:00
else
{
TSharedPtr < FTagMetaData > MetaData = PathWidget - > GetMetaData < FTagMetaData > ( ) ;
if ( MetaData . IsValid ( ) )
{
PickedWidgetName = MetaData - > Tag ;
2014-09-03 08:26:29 -04:00
if ( PickedWidgetName ! = NAME_None )
{
PickedAllMetaData = PathWidget - > GetAllMetaData < FTagMetaData > ( ) ;
}
2014-08-28 04:32:19 -04:00
break ;
}
}
2014-08-05 09:04:35 -04:00
}
// kind of a hack, but we need to maintain keyboard focus otherwise we wont get our keypress to 'pick'
FSlateApplication : : Get ( ) . SetKeyboardFocus ( SharedThis ( this ) , EKeyboardFocusCause : : SetDirectly ) ;
if ( ParentWindow . IsValid ( ) )
{
// also kind of a hack, but this is the only way at the moment to get a 'cursor decorator' without using the drag-drop code path
ParentWindow . Pin ( ) - > MoveWindowTo ( FSlateApplication : : Get ( ) . GetCursorPos ( ) + FSlateApplication : : Get ( ) . GetCursorSize ( ) ) ;
}
}
FText GetPickerStatusText ( ) const
{
return FText : : Format ( LOCTEXT ( " TootipHint " , " {0} (Esc to pick) " ) , FText : : FromName ( PickedWidgetName ) ) ;
}
virtual FReply OnKeyDown ( const FGeometry & MyGeometry , const FKeyboardEvent & InKeyboardEvent ) override
{
if ( InKeyboardEvent . GetKey ( ) = = EKeys : : Escape )
{
TSharedPtr < IPropertyHandle > WrapperIdentifierProperty = StructPropertyHandle - > GetChildHandle ( GET_MEMBER_NAME_CHECKED ( FTutorialContentAnchor , WrapperIdentifier ) ) ;
WrapperIdentifierProperty - > SetValue ( PickedWidgetName ) ;
TSharedPtr < IPropertyHandle > TypeProperty = StructPropertyHandle - > GetChildHandle ( GET_MEMBER_NAME_CHECKED ( FTutorialContentAnchor , Type ) ) ;
TypeProperty - > SetValue ( ( uint8 ) ETutorialAnchorIdentifier : : NamedWidget ) ;
2014-09-05 07:39:52 -04:00
// Set the friendly name to the PickedWidget name - we might not have any metadata
StructPropertyHandle - > GetChildHandle ( GET_MEMBER_NAME_CHECKED ( FTutorialContentAnchor , FriendlyName ) ) - > SetValue ( PickedWidgetName ) ;
//StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTutorialContentAnchor, FriendlyName))->SetValue(FString());
2014-09-03 08:26:29 -04:00
for ( const auto & MetaDataEntry : PickedAllMetaData )
{
2014-09-05 07:39:52 -04:00
if ( MetaDataEntry - > IsOfType < FTutorialMetaData > ( ) )
2014-09-03 08:26:29 -04:00
{
2014-09-05 07:39:52 -04:00
TSharedRef < FTutorialMetaData > GraphNodeMeta = StaticCastSharedRef < FGraphNodeMetaData > ( MetaDataEntry ) ;
StructPropertyHandle - > GetChildHandle ( GET_MEMBER_NAME_CHECKED ( FTutorialContentAnchor , FriendlyName ) ) - > SetValue ( GraphNodeMeta - > FriendlyName ) ;
StructPropertyHandle - > GetChildHandle ( GET_MEMBER_NAME_CHECKED ( FTutorialContentAnchor , TabTypeToOpen ) ) - > SetValue ( GraphNodeMeta - > TabTypeToOpen ) ;
}
else if ( MetaDataEntry - > IsOfType < FGraphNodeMetaData > ( ) )
{
TSharedRef < FGraphNodeMetaData > GraphNodeMeta = StaticCastSharedRef < FGraphNodeMetaData > ( MetaDataEntry ) ;
2014-09-03 08:26:29 -04:00
StructPropertyHandle - > GetChildHandle ( GET_MEMBER_NAME_CHECKED ( FTutorialContentAnchor , GUIDString ) ) - > SetValue ( GraphNodeMeta - > GUID . ToString ( ) ) ;
StructPropertyHandle - > GetChildHandle ( GET_MEMBER_NAME_CHECKED ( FTutorialContentAnchor , OuterName ) ) - > SetValue ( GraphNodeMeta - > OuterName ) ;
StructPropertyHandle - > GetChildHandle ( GET_MEMBER_NAME_CHECKED ( FTutorialContentAnchor , FriendlyName ) ) - > SetValue ( GraphNodeMeta - > FriendlyName ) ;
2014-09-05 07:39:52 -04:00
StructPropertyHandle - > GetChildHandle ( GET_MEMBER_NAME_CHECKED ( FTutorialContentAnchor , TabTypeToOpen ) ) - > SetValue ( GraphNodeMeta - > TabTypeToOpen ) ;
2014-09-03 08:26:29 -04:00
}
else
{
TSharedRef < FTagMetaData > GraphNodeMeta = StaticCastSharedRef < FTagMetaData > ( MetaDataEntry ) ;
2014-09-05 07:39:52 -04:00
StructPropertyHandle - > GetChildHandle ( GET_MEMBER_NAME_CHECKED ( FTutorialContentAnchor , FriendlyName ) ) - > SetValue ( GraphNodeMeta - > Tag ) ;
2014-09-03 08:26:29 -04:00
}
}
2014-08-05 09:04:35 -04:00
PickedWidgetName = NAME_None ;
2014-09-05 07:39:52 -04:00
PickedAllMetaData . Reset ( ) ;
2014-08-05 09:04:35 -04:00
if ( ParentWindow . IsValid ( ) )
{
FSlateApplication : : Get ( ) . RequestDestroyWindow ( ParentWindow . Pin ( ) . ToSharedRef ( ) ) ;
ParentWindow . Reset ( ) ;
}
return FReply : : Handled ( ) ;
}
return FReply : : Unhandled ( ) ;
}
/** We need to support keyboard focus to process the 'Esc' key */
virtual bool SupportsKeyboardFocus ( ) const override
{
return true ;
}
private :
/** Handle to the property struct */
TSharedPtr < class IPropertyHandle > StructPropertyHandle ;
/** Handle to the window that contains this widget */
TWeakPtr < SWindow > ParentWindow ;
/** The widget name we are picking */
FName PickedWidgetName ;
2014-09-03 08:26:29 -04:00
/* The metadata for the widget we are picking */
TArray < TSharedRef < FTagMetaData > > PickedAllMetaData ;
2014-08-05 09:04:35 -04:00
} ;
/** Widget used to launch a 'picking' session */
class SWidgetPicker : public SCompoundWidget
{
public :
SLATE_BEGIN_ARGS ( SWidgetPicker ) { }
SLATE_END_ARGS ( )
~ SWidgetPicker ( )
{
// kill the picker window as well if this widget is going away - that way we dont get dangling refs to the property
if ( PickerWindow . IsValid ( ) & & FSlateApplication : : IsInitialized ( ) )
{
FSlateApplication : : Get ( ) . RequestDestroyWindow ( PickerWindow . Pin ( ) . ToSharedRef ( ) ) ;
PickerWindow . Reset ( ) ;
PickerWidget . Reset ( ) ;
}
}
void Construct ( const FArguments & InArgs , TSharedRef < class IPropertyHandle > InStructPropertyHandle , IPropertyTypeCustomizationUtils * InStructCustomizationUtils )
{
StructPropertyHandle = InStructPropertyHandle ;
ChildSlot
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. FillWidth ( 1.0f )
. VAlign ( VAlign_Center )
[
SNew ( STextBlock )
. Text ( this , & SWidgetPicker : : HandlePickerStatusText )
. Font ( InStructCustomizationUtils - > GetRegularFont ( ) )
]
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. VAlign ( VAlign_Center )
[
SNew ( SButton )
. ButtonStyle ( FEditorStyle : : Get ( ) , " HoverHintOnly " )
. OnClicked ( this , & SWidgetPicker : : OnClicked )
. ContentPadding ( 4.0f )
. ForegroundColor ( FSlateColor : : UseForeground ( ) )
. IsFocusable ( false )
[
SNew ( SImage )
. Image ( FEditorStyle : : GetBrush ( " PropertyWindow.Button_PickActorInteractive " ) )
. ColorAndOpacity ( FSlateColor : : UseForeground ( ) )
]
]
] ;
}
private :
FReply OnClicked ( )
{
// launch a picker window
if ( ! PickerWindow . IsValid ( ) )
{
TSharedRef < SWindow > NewWindow = SWindow : : MakeCursorDecorator ( ) ;
NewWindow - > MoveWindowTo ( FSlateApplication : : Get ( ) . GetCursorPos ( ) ) ;
PickerWindow = NewWindow ;
NewWindow - > SetContent (
SAssignNew ( PickerWidget , SWidgetPickerFloatingWindow , StructPropertyHandle . ToSharedRef ( ) )
. ParentWindow ( NewWindow )
) ;
TSharedPtr < SWindow > RootWindow = FGlobalTabmanager : : Get ( ) - > GetRootWindow ( ) ;
check ( RootWindow . IsValid ( ) ) ;
FSlateApplication : : Get ( ) . AddWindowAsNativeChild ( NewWindow , RootWindow . ToSharedRef ( ) ) ;
FIntroTutorials & IntroTutorials = FModuleManager : : Get ( ) . GetModuleChecked < FIntroTutorials > ( " IntroTutorials " ) ;
IntroTutorials . OnIsPicking ( ) . BindSP ( this , & SWidgetPicker : : OnIsPicking ) ;
}
return FReply : : Handled ( ) ;
}
FText HandlePickerStatusText ( ) const
{
FName WidgetName = NAME_None ;
TSharedPtr < IPropertyHandle > WrapperIdentifierProperty = StructPropertyHandle - > GetChildHandle ( GET_MEMBER_NAME_CHECKED ( FTutorialContentAnchor , WrapperIdentifier ) ) ;
2014-08-13 03:41:45 -04:00
// Some of the tags contain a GUID, we dont want to see that here
FString WidgetValue ;
WrapperIdentifierProperty - > GetValue ( WidgetValue ) ;
return MakeFriendlyStringFromName ( WidgetValue ) ;
}
FText MakeFriendlyStringFromName ( const FString & WidgetName ) const
{
2014-08-13 09:22:55 -04:00
// We will likely have meta data for this eventually. For now just parse a comma delimited string which currently will either be a name or ident,name,UID
2014-08-13 03:41:45 -04:00
if ( WidgetName = = " None " )
{
return FText : : FromName ( * WidgetName ) ;
}
2014-09-03 08:26:29 -04:00
FString FriendlyName ;
StructPropertyHandle - > GetChildHandle ( GET_MEMBER_NAME_CHECKED ( FTutorialContentAnchor , FriendlyName ) ) - > GetValue ( FriendlyName ) ;
if ( FriendlyName . IsEmpty ( ) = = false )
2014-08-13 09:22:55 -04:00
{
2014-09-03 08:26:29 -04:00
return FText : : FromName ( * FriendlyName ) ;
2014-08-13 03:41:45 -04:00
}
return FText : : FromName ( * WidgetName ) ;
2014-08-05 09:04:35 -04:00
}
bool OnIsPicking ( FName & OutWidgetNameToHighlight ) const
{
if ( PickerWidget . IsValid ( ) )
{
OutWidgetNameToHighlight = PickerWidget . Pin ( ) - > GetPickedWidgetName ( ) ;
return true ;
}
return false ;
}
private :
/** Picker window widget */
TWeakPtr < SWidgetPickerFloatingWindow > PickerWidget ;
/** Picker window */
TWeakPtr < SWindow > PickerWindow ;
/** Handle to the struct we are customizing */
TSharedPtr < class IPropertyHandle > StructPropertyHandle ;
} ;
TSharedRef < IPropertyTypeCustomization > FTutorialContentAnchorCustomization : : MakeInstance ( )
{
return MakeShareable ( new FTutorialContentAnchorCustomization ) ;
}
void FTutorialContentAnchorCustomization : : CustomizeHeader ( TSharedRef < class IPropertyHandle > InStructPropertyHandle , class FDetailWidgetRow & HeaderRow , IPropertyTypeCustomizationUtils & StructCustomizationUtils )
{
2014-08-18 14:00:02 -04:00
TSharedPtr < IPropertyHandle > DrawHighlightProperty = InStructPropertyHandle - > GetChildHandle ( GET_MEMBER_NAME_CHECKED ( FTutorialContentAnchor , bDrawHighlight ) ) ;
2014-08-05 09:04:35 -04:00
HeaderRow
. NameContent ( )
[
2014-08-18 14:00:02 -04:00
SNew ( SVerticalBox )
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
. Padding ( 0.0f , 2.0f )
[
InStructPropertyHandle - > CreatePropertyNameWidget ( )
]
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
. Padding ( 0.0f , 2.0f )
[
DrawHighlightProperty - > CreatePropertyNameWidget ( )
]
2014-08-05 09:04:35 -04:00
]
. ValueContent ( )
. MinDesiredWidth ( 250.0f )
. MaxDesiredWidth ( 500.0f )
[
2014-08-18 14:00:02 -04:00
SNew ( SVerticalBox )
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
. Padding ( 0.0f , 2.0f )
[
SNew ( SWidgetPicker , InStructPropertyHandle , & StructCustomizationUtils )
]
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
. Padding ( 0.0f , 2.0f )
[
DrawHighlightProperty - > CreatePropertyValueWidget ( )
]
2014-08-05 09:04:35 -04:00
] ;
}
void FTutorialContentAnchorCustomization : : CustomizeChildren ( TSharedRef < class IPropertyHandle > InStructPropertyHandle , class IDetailChildrenBuilder & StructBuilder , IPropertyTypeCustomizationUtils & StructCustomizationUtils )
{
}
# undef LOCTEXT_NAMESPACE