You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
679 lines
22 KiB
C++
679 lines
22 KiB
C++
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "TutorialStructCustomization.h"
|
|
#include "InputCoreTypes.h"
|
|
#include "Layout/Visibility.h"
|
|
#include "Input/Events.h"
|
|
#include "Input/Reply.h"
|
|
#include "Types/ISlateMetaData.h"
|
|
#include "Widgets/SWidget.h"
|
|
#include "Widgets/DeclarativeSyntaxSupport.h"
|
|
#include "Engine/GameViewportClient.h"
|
|
#include "Widgets/SCompoundWidget.h"
|
|
#include "Widgets/SBoxPanel.h"
|
|
#include "Widgets/SWindow.h"
|
|
#include "Widgets/Text/STextBlock.h"
|
|
#include "Framework/Docking/TabManager.h"
|
|
#include "PropertyHandle.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "Layout/WidgetPath.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "Widgets/Images/SImage.h"
|
|
#include "Widgets/Input/SButton.h"
|
|
#include "Widgets/SToolTip.h"
|
|
#include "DetailWidgetRow.h"
|
|
#include "IntroTutorials.h"
|
|
#include "EditorTutorial.h"
|
|
#include "STutorialEditableText.h"
|
|
#include "TutorialMetaData.h"
|
|
#include "Widgets/Docking/SDockTab.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;
|
|
}
|
|
|
|
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() );
|
|
}
|
|
};
|
|
|
|
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()
|
|
]
|
|
]
|
|
+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))
|
|
]
|
|
]
|
|
];
|
|
}
|
|
|
|
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_ARGUMENT(TSharedPtr<class IPropertyHandle>, FriendlyNameProperty)
|
|
SLATE_ARGUMENT(FName, SpecificWidgetType)
|
|
|
|
SLATE_END_ARGS()
|
|
|
|
void Construct(const FArguments& InArgs, TSharedRef<class IPropertyHandle> InStructPropertyHandle, TSharedRef<class IPropertyHandle> InPickPropertyHandle)
|
|
{
|
|
StructPropertyHandle = InStructPropertyHandle;
|
|
PickPropertyHandle = InPickPropertyHandle;
|
|
|
|
ParentWindow = InArgs._ParentWindow;
|
|
SpecificWidgetType = InArgs._SpecificWidgetType;
|
|
FriendlyNameProperty = InArgs._FriendlyNameProperty;
|
|
|
|
ChildSlot
|
|
[
|
|
SNew(SToolTip)
|
|
.Text(this, &SWidgetPickerFloatingWindow::GetPickerStatusText)
|
|
];
|
|
}
|
|
|
|
/* Returns the name of the picked widget */
|
|
FName GetPickedWidgetName()
|
|
{
|
|
return PickedWidgetName;
|
|
}
|
|
|
|
/**
|
|
* Return the name of the given widget (Will filter out widgets that do no match the specific type if applicable
|
|
*
|
|
* @param InWidget The widget to get the pickable name of
|
|
* @returns Pickable Name of the widget (or None if it doesnt match a specific widget type)
|
|
*
|
|
*/
|
|
FName GetPickableNameForWidget(TSharedRef<SWidget> InWidget) const
|
|
{
|
|
FName PickableName;
|
|
if (InWidget->GetTag() != NAME_None)
|
|
{
|
|
PickableName = InWidget->GetTag();
|
|
}
|
|
else
|
|
{
|
|
// If we have specified a specific widget to to pick check this one matches
|
|
TSharedPtr<FTagMetaData> WidgetMetaData = InWidget->GetMetaData<FTagMetaData>();
|
|
if (WidgetMetaData.IsValid())
|
|
{
|
|
PickableName = WidgetMetaData->Tag;
|
|
}
|
|
else if (SpecificWidgetType != NAME_None)
|
|
{
|
|
FName TheType = InWidget->GetType();
|
|
if (TheType == SpecificWidgetType)
|
|
{
|
|
if (SpecificWidgetType == FName("SDockTab"))
|
|
{
|
|
TSharedRef<SDockTab> DockTab = StaticCastSharedRef<SDockTab>(InWidget);
|
|
FString TabIdent = DockTab.Get().GetLayoutIdentifier().ToString();
|
|
PickableName = FName(*TabIdent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return PickableName;
|
|
}
|
|
|
|
private:
|
|
virtual void Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime ) override
|
|
{
|
|
PickedWidgetName = NAME_None;
|
|
PickedAllMetaData.Reset();
|
|
|
|
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;
|
|
|
|
PickedWidgetName = GetPickableNameForWidget(PathWidget);
|
|
if (PickedWidgetName != NAME_None)
|
|
{
|
|
PickedAllMetaData = PathWidget->GetAllMetaData<FTagMetaData>();
|
|
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), EFocusCause::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 FKeyEvent& InKeyEvent) override
|
|
{
|
|
if(InKeyEvent.GetKey() == EKeys::Escape)
|
|
{
|
|
if( InKeyEvent.IsLeftControlDown() == false )
|
|
{
|
|
// We cant set a parameter if this isn't valid !
|
|
check(PickPropertyHandle.IsValid());
|
|
PickPropertyHandle->SetValue(PickedWidgetName);
|
|
|
|
TSharedPtr<IPropertyHandle> TypeProperty = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTutorialContentAnchor, Type));
|
|
TypeProperty->SetValue((uint8)ETutorialAnchorIdentifier::NamedWidget);
|
|
|
|
FString FriendlyNameToSet = PickedWidgetName.ToString();
|
|
|
|
// Reset the other fields
|
|
StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTutorialContentAnchor, GUIDString))->SetValue(FString());
|
|
StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTutorialContentAnchor, OuterName))->SetValue(FString());
|
|
StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTutorialContentAnchor, TabToFocusOrOpen))->SetValue(FString());
|
|
|
|
// Handle custom widget type picks
|
|
if ((SpecificWidgetType.IsValid() == true) && (SpecificWidgetType != NAME_None))
|
|
{
|
|
if (SpecificWidgetType == FName("SDockTab"))
|
|
{
|
|
StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTutorialContentAnchor, TabToFocusOrOpen))->SetValue(PickedWidgetName);
|
|
}
|
|
}
|
|
|
|
for (const auto& MetaDataEntry : PickedAllMetaData)
|
|
{
|
|
if (MetaDataEntry->IsOfType<FGraphNodeMetaData>())
|
|
{
|
|
TSharedRef<FGraphNodeMetaData> GraphNodeMeta = StaticCastSharedRef<FGraphNodeMetaData>(MetaDataEntry);
|
|
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);
|
|
}
|
|
else if (MetaDataEntry->IsOfType<FTutorialMetaData>())
|
|
{
|
|
TSharedRef<FTutorialMetaData> TutorialMeta = StaticCastSharedRef<FTutorialMetaData>(MetaDataEntry);
|
|
FriendlyNameToSet = TutorialMeta->FriendlyName;
|
|
|
|
// TabTypeToOpen only really applies to specifc widget types, so if we dont have one dont set the parameter
|
|
if ((SpecificWidgetType.IsValid() == true) && (SpecificWidgetType != NAME_None))
|
|
{
|
|
StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTutorialContentAnchor, TabToFocusOrOpen))->SetValue(TutorialMeta->TabTypeToOpen);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TSharedRef<FTagMetaData> GraphNodeMeta = StaticCastSharedRef<FTagMetaData>(MetaDataEntry);
|
|
FriendlyNameToSet = GraphNodeMeta->Tag.ToString();
|
|
}
|
|
|
|
// Set the friendly name to the PickedWidget name - we might not have any metadata
|
|
if (FriendlyNameProperty.IsValid())
|
|
{
|
|
FriendlyNameProperty->SetValue(FriendlyNameToSet);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Reset the pick data
|
|
PickedWidgetName = NAME_None;
|
|
PickedAllMetaData.Reset();
|
|
|
|
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 (Data other than the name is in here) */
|
|
TSharedPtr<class IPropertyHandle> StructPropertyHandle;
|
|
|
|
/** Handle to the name property struct. This is the property we are actually picking */
|
|
TSharedPtr<class IPropertyHandle> PickPropertyHandle;
|
|
|
|
/* Handle to the friendly name property we should set if any. */
|
|
TSharedPtr<class IPropertyHandle> FriendlyNameProperty;
|
|
|
|
/** Handle to the window that contains this widget */
|
|
TWeakPtr<SWindow> ParentWindow;
|
|
|
|
/** The widget name we are picking */
|
|
FName PickedWidgetName;
|
|
|
|
/* The metadata for the widget we are picking */
|
|
TArray<TSharedRef<FTagMetaData>> PickedAllMetaData;
|
|
|
|
/* If we are we picking a specific widget type this will specify a typename (EG SDockTab) */
|
|
FName SpecificWidgetType;
|
|
};
|
|
|
|
/** Widget used to launch a 'picking' session */
|
|
class SWidgetPicker : public SCompoundWidget
|
|
{
|
|
public:
|
|
SLATE_BEGIN_ARGS( SWidgetPicker ) {}
|
|
|
|
SLATE_ARGUMENT(FName, SpecificWidgetType)
|
|
|
|
SLATE_ARGUMENT(TSharedPtr<class IPropertyHandle>, FriendlyNameProperty)
|
|
|
|
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, TSharedRef<class IPropertyHandle> InPickPropertyHandle, IPropertyTypeCustomizationUtils* InStructCustomizationUtils)
|
|
{
|
|
StructPropertyHandle = InStructPropertyHandle;
|
|
PickPropertyHandle = InPickPropertyHandle;
|
|
|
|
SpecificWidgetType = InArgs._SpecificWidgetType;
|
|
FriendlyNameProperty = InArgs._FriendlyNameProperty;
|
|
|
|
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(), PickPropertyHandle.ToSharedRef())
|
|
.ParentWindow(NewWindow)
|
|
.SpecificWidgetType(SpecificWidgetType)
|
|
.FriendlyNameProperty(FriendlyNameProperty)
|
|
);
|
|
|
|
TSharedPtr<SWindow> RootWindow = FGlobalTabmanager::Get()->GetRootWindow();
|
|
if (RootWindow.IsValid())
|
|
{
|
|
FSlateApplication::Get().AddWindowAsNativeChild(NewWindow, RootWindow.ToSharedRef());
|
|
}
|
|
else
|
|
{
|
|
FSlateApplication::Get().AddWindow(NewWindow);
|
|
}
|
|
|
|
FIntroTutorials& IntroTutorials = FModuleManager::Get().GetModuleChecked<FIntroTutorials>("IntroTutorials");
|
|
IntroTutorials.OnIsPicking().BindSP(this, &SWidgetPicker::OnIsPicking);
|
|
IntroTutorials.OnValidatePickingCandidate().BindSP(this, &SWidgetPicker::OnValidatePickingCandidate);
|
|
}
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
FText HandlePickerStatusText() const
|
|
{
|
|
if (PickPropertyHandle.IsValid())
|
|
{
|
|
FString WidgetValue;
|
|
PickPropertyHandle->GetValue(WidgetValue);
|
|
|
|
if (FriendlyNameProperty.IsValid())
|
|
{
|
|
FString FriendlyName;
|
|
FriendlyNameProperty->GetValue(FriendlyName);
|
|
if (FriendlyName.IsEmpty() == false)
|
|
{
|
|
WidgetValue = FriendlyName;
|
|
}
|
|
}
|
|
|
|
return FText::FromString(WidgetValue);
|
|
}
|
|
return FText();
|
|
}
|
|
|
|
FText MakeFriendlyStringFromName(const FString& WidgetName) const
|
|
{
|
|
// 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
|
|
if (WidgetName == "None")
|
|
{
|
|
return FText::FromName(*WidgetName);
|
|
}
|
|
|
|
FString FriendlyName;
|
|
StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTutorialContentAnchor, FriendlyName))->GetValue(FriendlyName);
|
|
if (FriendlyName.IsEmpty()==false)
|
|
{
|
|
return FText::FromName(*FriendlyName);
|
|
}
|
|
return FText::FromName(*WidgetName);
|
|
}
|
|
|
|
bool OnIsPicking(FName& OutWidgetNameToHighlight) const
|
|
{
|
|
if(PickerWidget.IsValid())
|
|
{
|
|
OutWidgetNameToHighlight = PickerWidget.Pin()->GetPickedWidgetName();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool OnValidatePickingCandidate(TSharedRef<SWidget> InWidget, FName& OutWidgetNameToHighlight, bool& bOutShouldHighlight) const
|
|
{
|
|
bool bIsPicking = false;
|
|
bOutShouldHighlight = false;
|
|
TSharedPtr<FTagMetaData> WidgetMetaData = InWidget->GetMetaData<FTagMetaData>();
|
|
const FName WidgetTag = (WidgetMetaData.IsValid() && WidgetMetaData->Tag.IsValid()) ? WidgetMetaData->Tag : InWidget->GetTag();
|
|
if (PickerWidget.IsValid())
|
|
{
|
|
// Is the given widget a candidate
|
|
if ((WidgetTag != NAME_None) )
|
|
{
|
|
bIsPicking = true;
|
|
}
|
|
if (SpecificWidgetType != NAME_None)
|
|
{
|
|
bIsPicking = InWidget->GetType() == SpecificWidgetType;
|
|
}
|
|
|
|
// If we are picking a widget check if we should also highlight it
|
|
if (bIsPicking == true)
|
|
{
|
|
OutWidgetNameToHighlight = PickerWidget.Pin()->GetPickedWidgetName();
|
|
FName InPickableName = PickerWidget.Pin()->GetPickableNameForWidget(InWidget);
|
|
|
|
if (InPickableName == OutWidgetNameToHighlight)
|
|
{
|
|
bOutShouldHighlight = true;
|
|
}
|
|
}
|
|
}
|
|
return bIsPicking;
|
|
}
|
|
|
|
private:
|
|
/** Picker window widget */
|
|
TWeakPtr<SWidgetPickerFloatingWindow> PickerWidget;
|
|
|
|
/** Picker window */
|
|
TWeakPtr<SWindow> PickerWindow;
|
|
|
|
/** Handle to the struct we are customizing */
|
|
TSharedPtr<class IPropertyHandle> StructPropertyHandle;
|
|
|
|
/** Handle to the property we are customizing */
|
|
TSharedPtr<class IPropertyHandle> PickPropertyHandle;
|
|
|
|
/* Handle to the friendly name property we should set if any. */
|
|
TSharedPtr<class IPropertyHandle> FriendlyNameProperty;
|
|
|
|
/* Are we picking a specific widget type */
|
|
FName SpecificWidgetType;
|
|
};
|
|
|
|
TSharedRef<IPropertyTypeCustomization> FTutorialContentAnchorCustomization::MakeInstance()
|
|
{
|
|
return MakeShareable( new FTutorialContentAnchorCustomization );
|
|
}
|
|
|
|
void FTutorialContentAnchorCustomization::CustomizeHeader( TSharedRef<class IPropertyHandle> InStructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils )
|
|
{
|
|
TSharedPtr<IPropertyHandle> DrawHighlightProperty = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTutorialContentAnchor, bDrawHighlight));
|
|
TSharedPtr<IPropertyHandle> WidgetNameProperty = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTutorialContentAnchor, WrapperIdentifier));
|
|
TSharedPtr<IPropertyHandle> TabToFocusProperty = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTutorialContentAnchor, TabToFocusOrOpen));
|
|
TSharedPtr<IPropertyHandle> FriendlyNameProperty = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTutorialContentAnchor, FriendlyName));
|
|
|
|
HeaderRow
|
|
.NameContent()
|
|
[
|
|
SNew(SVerticalBox)
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding(0.0f, 2.0f)
|
|
[
|
|
InStructPropertyHandle->CreatePropertyNameWidget()
|
|
]
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding(0.0f, 2.0f)
|
|
[
|
|
DrawHighlightProperty->CreatePropertyNameWidget()
|
|
]
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding(0.0f, 2.0f)
|
|
[
|
|
TabToFocusProperty->CreatePropertyNameWidget()
|
|
]
|
|
]
|
|
.ValueContent()
|
|
.MinDesiredWidth(250.0f)
|
|
.MaxDesiredWidth(500.0f)
|
|
[
|
|
SNew(SVerticalBox)
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding(0.0f, 2.0f)
|
|
[
|
|
SNew(SWidgetPicker, InStructPropertyHandle, WidgetNameProperty.ToSharedRef(), &StructCustomizationUtils)
|
|
.FriendlyNameProperty(FriendlyNameProperty)
|
|
]
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding(0.0f, 2.0f)
|
|
[
|
|
DrawHighlightProperty->CreatePropertyValueWidget()
|
|
]
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding(0.0f, 2.0f)
|
|
[
|
|
SNew(SWidgetPicker, InStructPropertyHandle, TabToFocusProperty.ToSharedRef(), &StructCustomizationUtils)
|
|
.SpecificWidgetType("SDockTab")
|
|
]
|
|
];
|
|
}
|
|
|
|
void FTutorialContentAnchorCustomization::CustomizeChildren( TSharedRef<class IPropertyHandle> InStructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils )
|
|
{
|
|
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|