Files
UnrealEngineUWP/Engine/Source/Editor/IntroTutorials/Private/TutorialStructCustomization.cpp
Thomas Sarkanen a27980dcf0 Tutorials 2.0 - Initial version
NOTE: Old tutorials not deprecated (yet), but widget highlights in old tutorials will stop working with this change!

Added new Blueprintable UEditorTutorial object.
Added suite of widgets and details customizations to display tutorials.
New system is available on command line switch -NewTutorials.

Slate changes:
Tag names are now stored in SWidgets, rather than simply being discarded.
Removed STutorialWrapper in favour of using Tags.
Added Tags to more multibox widgets, so virtually all can now be picked.
Added SWindow::HasOverlay so we dont attempt to add overlays to widows that cannot have them.

[CL 2244216 by Thomas Sarkanen in Main branch]
2014-08-05 09:04:35 -04:00

355 lines
11 KiB
C++

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#include "IntroTutorialsPrivatePCH.h"
#include "TutorialStructCustomization.h"
#include "EditorTutorial.h"
#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;
}
};
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()
]
]
];
}
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;
FWidgetPath Path = FSlateApplication::Get().LocateWindowUnderMouse(FSlateApplication::Get().GetCursorPos(), FSlateApplication::Get().GetInteractiveTopLevelWindows(), true);
for(int32 PathIndex = Path.Widgets.Num() - 1; PathIndex >= 0; PathIndex--)
{
TSharedRef<SWidget> PathWidget = Path.Widgets(PathIndex).Widget;
if(PathWidget->GetTag() != NAME_None)
{
PickedWidgetName = PathWidget->GetTag();
break;
}
}
// 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);
PickedWidgetName = NAME_None;
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;
};
/** 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));
WrapperIdentifierProperty->GetValue(WidgetName);
return FText::FromName(WidgetName);
}
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 )
{
HeaderRow
.NameContent()
[
InStructPropertyHandle->CreatePropertyNameWidget()
]
.ValueContent()
.MinDesiredWidth(250.0f)
.MaxDesiredWidth(500.0f)
[
SNew(SWidgetPicker, InStructPropertyHandle, &StructCustomizationUtils)
];
}
void FTutorialContentAnchorCustomization::CustomizeChildren( TSharedRef<class IPropertyHandle> InStructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils )
{
}
#undef LOCTEXT_NAMESPACE