Files
UnrealEngineUWP/Engine/Source/Editor/IntroTutorials/Private/TutorialStructCustomization.cpp
Thomas Sarkanen 4a3e3773a8 FText key stablization & key editing
FText DisplayString shared ptrs are persisted across property edits. This preserves their entry in the live table and also updates the string that the entry points to. We call GetString() to update the hashed value as persisting the DisplayString means that its location in the table is preserved.
On serialization, a valid key is still found in the live table for the display string, so the key is preserved.
Added funtionality to the property editor to allow key editing and re-generation so any key conflicts can be resolved in the future.

UE-5350 - As a translator, I would like the editor tutorial text to preserve it's history between changes in order to greatly improve the translation workflow.

Reviewed by Saul.Abreu

[CL 2400879 by Thomas Sarkanen in Main branch]
2015-01-08 09:26:03 -05:00

667 lines
21 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "IntroTutorialsPrivatePCH.h"
#include "TutorialStructCustomization.h"
#include "EditorTutorial.h"
#include "STutorialEditableText.h"
#include "ISlateMetaData.h"
#include "TutorialMetaData.h"
#include "SDockTab.h"
#include "PropertyCustomizationHelpers.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))
]
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
.Padding(2.0f, 0.0f, 0.0f, 0.0f)
[
PropertyCustomizationHelpers::MakeTextLocalizationButton(TextProperty.ToSharedRef())
]
]
];
}
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> MetaData = InWidget->GetMetaData<FTagMetaData>();
if (MetaData.IsValid())
{
PickableName = MetaData->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> MetaData = InWidget->GetMetaData<FTagMetaData>();
const FName Tag = (MetaData.IsValid() && MetaData->Tag.IsValid()) ? MetaData->Tag : InWidget->GetTag();
if (PickerWidget.IsValid())
{
// Is the given widget a candidate
if ((Tag != 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