8321 lines
282 KiB
C++
8321 lines
282 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "BlueprintDetailsCustomization.h"
|
|
|
|
#include "AssetRegistry/AssetData.h"
|
|
#include "BlueprintEditor.h"
|
|
#include "BlueprintEditorModes.h"
|
|
#include "BlueprintEditorModule.h"
|
|
#include "BlueprintEditorSettings.h"
|
|
#include "BlueprintNamespaceRegistry.h"
|
|
#include "BlueprintNamespaceUtilities.h"
|
|
#include "ClassViewerModule.h"
|
|
#include "Components/ActorComponent.h"
|
|
#include "Components/ChildActorComponent.h"
|
|
#include "Components/SceneComponent.h"
|
|
#include "Components/TimelineComponent.h"
|
|
#include "Containers/EnumAsByte.h"
|
|
#include "Containers/Map.h"
|
|
#include "DetailCategoryBuilder.h"
|
|
#include "DetailLayoutBuilder.h"
|
|
#include "DetailWidgetRow.h"
|
|
#include "DragAndDrop/DecoratedDragDropOp.h"
|
|
#include "EdGraph/EdGraph.h"
|
|
#include "EdGraph/EdGraphNode.h"
|
|
#include "EdGraph/EdGraphNode_Documentation.h"
|
|
#include "EdGraph/EdGraphSchema.h"
|
|
#include "EdGraphNode_Comment.h"
|
|
#include "EdGraphSchema_K2.h"
|
|
#include "EdGraphSchema_K2_Actions.h"
|
|
#include "EdMode.h"
|
|
#include "Editor/EditorEngine.h"
|
|
#include "Engine/BlueprintGeneratedClass.h"
|
|
#include "Engine/Engine.h"
|
|
#include "Engine/EngineBaseTypes.h"
|
|
#include "Engine/MemberReference.h"
|
|
#include "Engine/SCS_Node.h"
|
|
#include "Engine/SimpleConstructionScript.h"
|
|
#include "StructUtils/UserDefinedStruct.h"
|
|
#include "EngineLogs.h"
|
|
#include "Fonts/SlateFontInfo.h"
|
|
#include "Framework/Application/MenuStack.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "Framework/Commands/UIAction.h"
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
#include "Framework/Views/ITypedTableView.h"
|
|
#include "GameFramework/Actor.h"
|
|
#include "GenericPlatform/GenericApplication.h"
|
|
#include "GenericPlatform/ICursor.h"
|
|
#include "GraphEditor.h"
|
|
#include "HAL/PlatformMath.h"
|
|
#include "HAL/PlatformMisc.h"
|
|
#include "IDetailChildrenBuilder.h"
|
|
#include "IDetailDragDropHandler.h"
|
|
#include "IDetailPropertyRow.h"
|
|
#include "IDetailsView.h"
|
|
#include "IDocumentation.h"
|
|
#include "IDocumentationPage.h"
|
|
#include "IFieldNotificationClassDescriptor.h"
|
|
#include "INotifyFieldValueChanged.h"
|
|
#include "ISequencerModule.h"
|
|
#include "Input/DragAndDrop.h"
|
|
#include "Input/Events.h"
|
|
#include "InputCoreTypes.h"
|
|
#include "Internationalization/Internationalization.h"
|
|
#include "K2Node.h"
|
|
#include "K2Node_CallFunction.h"
|
|
#include "K2Node_ComponentBoundEvent.h"
|
|
#include "K2Node_Composite.h"
|
|
#include "K2Node_CustomEvent.h"
|
|
#include "K2Node_Event.h"
|
|
#include "K2Node_FunctionEntry.h"
|
|
#include "K2Node_FunctionResult.h"
|
|
#include "K2Node_FunctionTerminator.h"
|
|
#include "K2Node_MacroInstance.h"
|
|
#include "K2Node_MathExpression.h"
|
|
#include "K2Node_Tunnel.h"
|
|
#include "K2Node_Variable.h"
|
|
#include "K2Node_VariableGet.h"
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
#include "Kismet2/ChildActorComponentEditorUtils.h"
|
|
#include "Kismet2/ComponentEditorUtils.h"
|
|
#include "Kismet2/Kismet2NameValidators.h"
|
|
#include "Kismet2/KismetEditorUtilities.h"
|
|
#include "Layout/BasicLayoutWidgetSlot.h"
|
|
#include "Layout/Margin.h"
|
|
#include "Layout/WidgetPath.h"
|
|
#include "Logging/LogCategory.h"
|
|
#include "Logging/LogMacros.h"
|
|
#include "Math/UnitConversion.h"
|
|
#include "Math/Vector2D.h"
|
|
#include "Math/Vector4.h"
|
|
#include "Misc/Attribute.h"
|
|
#include "Misc/CString.h"
|
|
#include "Misc/Guid.h"
|
|
#include "Misc/MessageDialog.h"
|
|
#include "Misc/ScopedSlowTask.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "NodeFactory.h"
|
|
#include "ObjectEditorUtils.h"
|
|
#include "PropertyCustomizationHelpers.h"
|
|
#include "PropertyEditorDelegates.h"
|
|
#include "PropertyHandle.h"
|
|
#include "PropertyRestriction.h"
|
|
#include "SBlueprintNamespaceEntry.h"
|
|
#include "SFieldNotificationCheckList.h"
|
|
#include "SGraphPin.h"
|
|
#include "SKismetInspector.h"
|
|
#include "SPinTypeSelector.h"
|
|
#include "SSubobjectBlueprintEditor.h"
|
|
#include "SSubobjectEditor.h"
|
|
#include "ScopedTransaction.h"
|
|
#include "Serialization/Archive.h"
|
|
#include "SlateOptMacros.h"
|
|
#include "SlotBase.h"
|
|
#include "SSearchableComboBox.h"
|
|
#include "SSocketChooser.h"
|
|
#include "Styling/AppStyle.h"
|
|
#include "Styling/ISlateStyle.h"
|
|
#include "Styling/SlateColor.h"
|
|
#include "SubobjectData.h"
|
|
#include "SubobjectDataSubsystem.h"
|
|
#include "SupportedRangeTypes.h" // StructsSupportingRangeVisibility
|
|
#include "Templates/Casts.h"
|
|
#include "Templates/SubclassOf.h"
|
|
#include "Textures/SlateIcon.h"
|
|
#include "Tools/LegacyEdModeWidgetHelpers.h"
|
|
#include "Trace/Detail/Channel.h"
|
|
#include "Types/ISlateMetaData.h"
|
|
#include "Types/SlateStructs.h"
|
|
#include "UObject/Class.h"
|
|
#include "UObject/CoreNetTypes.h"
|
|
#include "UObject/EnumProperty.h"
|
|
#include "UObject/Field.h"
|
|
#include "UObject/Interface.h"
|
|
#include "UObject/Object.h"
|
|
#include "UObject/ObjectMacros.h"
|
|
#include "UObject/ObjectPtr.h"
|
|
#include "UObject/Package.h"
|
|
#include "UObject/ReflectedTypeAccessors.h"
|
|
#include "UObject/SoftObjectPath.h"
|
|
#include "UObject/StructOnScope.h"
|
|
#include "UObject/TextProperty.h"
|
|
#include "UObject/TopLevelAssetPath.h"
|
|
#include "UObject/UObjectBaseUtility.h"
|
|
#include "UObject/UObjectGlobals.h"
|
|
#include "UObject/UObjectHash.h"
|
|
#include "UObject/UObjectIterator.h"
|
|
#include "UObject/UnrealType.h"
|
|
#include "Widgets/Colors/SColorBlock.h"
|
|
#include "Widgets/Colors/SColorPicker.h"
|
|
#include "Widgets/DeclarativeSyntaxSupport.h"
|
|
#include "Widgets/Images/SImage.h"
|
|
#include "Widgets/Input/SButton.h"
|
|
#include "Widgets/Input/SCheckBox.h"
|
|
#include "Widgets/Input/SComboBox.h"
|
|
#include "Widgets/Input/SComboButton.h"
|
|
#include "Widgets/Input/SEditableTextBox.h"
|
|
#include "Widgets/Input/SMultiLineEditableTextBox.h"
|
|
#include "Widgets/Input/STextComboBox.h"
|
|
#include "Widgets/Layout/SBorder.h"
|
|
#include "Widgets/Layout/SBox.h"
|
|
#include "Widgets/Layout/SWidgetSwitcher.h"
|
|
#include "Widgets/SBoxPanel.h"
|
|
#include "Widgets/SNullWidget.h"
|
|
#include "Widgets/SToolTip.h"
|
|
#include "Widgets/SWidget.h"
|
|
#include "Widgets/Text/STextBlock.h"
|
|
#include "Widgets/Views/STableRow.h"
|
|
|
|
class ITableRow;
|
|
class STableViewBase;
|
|
struct FGeometry;
|
|
|
|
#define LOCTEXT_NAMESPACE "BlueprintDetailsCustomization"
|
|
|
|
namespace BlueprintDocumentationDetailDefs
|
|
{
|
|
/** Minimum size of the details title panel */
|
|
static const float DetailsTitleMinWidth = 125.f;
|
|
/** Maximum size of the details title panel */
|
|
static const float DetailsTitleMaxWidth = 300.f;
|
|
/** magic number retrieved from SGraphNodeComment::GetWrapAt() */
|
|
static const float DetailsTitleWrapPadding = 32.0f;
|
|
/** label of the option to reset the drop-down value function name */
|
|
static const FString EmptyDropDownOptionName = TEXT("None");
|
|
};
|
|
|
|
void FBlueprintDetails::AddEventsCategory(IDetailLayoutBuilder& DetailBuilder, FName PropertyName, UClass* PropertyClass)
|
|
{
|
|
UBlueprint* BlueprintObj = GetBlueprintObj();
|
|
check(BlueprintObj);
|
|
|
|
// Check for Ed Graph vars that can generate events
|
|
if ( PropertyClass && BlueprintObj->AllowsDynamicBinding() )
|
|
{
|
|
// If the object property can't be resolved for the property, than we can't use it's events.
|
|
FObjectProperty* VariableProperty = FindFProperty<FObjectProperty>(BlueprintObj->SkeletonGeneratedClass, PropertyName);
|
|
|
|
if ( FBlueprintEditorUtils::CanClassGenerateEvents(PropertyClass) && VariableProperty )
|
|
{
|
|
for ( TFieldIterator<FMulticastDelegateProperty> PropertyIt(PropertyClass, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt )
|
|
{
|
|
FMulticastDelegateProperty* Property = *PropertyIt;
|
|
|
|
static const FName HideInDetailPanelName("HideInDetailPanel");
|
|
// Check for multicast delegates that we can safely assign
|
|
if ( !Property->HasAnyPropertyFlags(CPF_Parm) && Property->HasAllPropertyFlags(CPF_BlueprintAssignable) &&
|
|
!Property->HasMetaData(HideInDetailPanelName) )
|
|
{
|
|
FName EventName = Property->GetFName();
|
|
FText EventText = Property->GetDisplayNameText();
|
|
|
|
IDetailCategoryBuilder& EventCategory = DetailBuilder.EditCategory(TEXT("Events"), LOCTEXT("Events", "Events"), ECategoryPriority::Uncommon);
|
|
|
|
EventCategory.AddCustomRow(EventText)
|
|
.WholeRowContent()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
.ToolTipText(Property->GetToolTipText())
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(0.0f, 0.0f, 5.0f, 0.0f)
|
|
[
|
|
SNew(SImage)
|
|
.Image(FAppStyle::Get().GetBrush("GraphEditor.Event_16x"))
|
|
]
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(STextBlock)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
.Text(EventText)
|
|
]
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.HAlign(HAlign_Left)
|
|
.VAlign(VAlign_Center)
|
|
.Padding(0.0f)
|
|
[
|
|
SNew(SButton)
|
|
.ContentPadding(FMargin(3.0, 2.0))
|
|
.OnClicked(this, &FBlueprintVarActionDetails::HandleAddOrViewEventForVariable, EventName, PropertyName, MakeWeakObjectPtr(PropertyClass))
|
|
[
|
|
SNew(SWidgetSwitcher)
|
|
.WidgetIndex(this, &FBlueprintVarActionDetails::HandleAddOrViewIndexForButton, EventName, PropertyName)
|
|
|
|
+ SWidgetSwitcher::Slot()
|
|
[
|
|
SNew(SImage)
|
|
.ColorAndOpacity(FSlateColor::UseForeground())
|
|
.Image(FAppStyle::Get().GetBrush("Icons.SelectInViewport"))
|
|
]
|
|
|
|
+ SWidgetSwitcher::Slot()
|
|
[
|
|
SNew(SImage)
|
|
.ColorAndOpacity(FSlateColor::UseForeground())
|
|
.Image(FAppStyle::Get().GetBrush("Icons.Plus"))
|
|
]
|
|
]
|
|
]
|
|
];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FReply FBlueprintDetails::HandleAddOrViewEventForVariable(const FName EventName, FName PropertyName, TWeakObjectPtr<UClass> PropertyClass)
|
|
{
|
|
UBlueprint* BlueprintObj = GetBlueprintObj();
|
|
|
|
// Find the corresponding variable property in the Blueprint
|
|
FObjectProperty* VariableProperty = FindFProperty<FObjectProperty>(BlueprintObj->SkeletonGeneratedClass, PropertyName);
|
|
|
|
if ( VariableProperty )
|
|
{
|
|
if ( !FKismetEditorUtilities::FindBoundEventForComponent(BlueprintObj, EventName, VariableProperty->GetFName()) )
|
|
{
|
|
FKismetEditorUtilities::CreateNewBoundEventForClass(PropertyClass.Get(), EventName, BlueprintObj, VariableProperty);
|
|
}
|
|
else
|
|
{
|
|
const UK2Node_ComponentBoundEvent* ExistingNode = FKismetEditorUtilities::FindBoundEventForComponent(BlueprintObj, EventName, VariableProperty->GetFName());
|
|
if ( ExistingNode )
|
|
{
|
|
FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(ExistingNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
int32 FBlueprintDetails::HandleAddOrViewIndexForButton(const FName EventName, FName PropertyName) const
|
|
{
|
|
UBlueprint* BlueprintObj = GetBlueprintObj();
|
|
|
|
if ( FKismetEditorUtilities::FindBoundEventForComponent(BlueprintObj, EventName, PropertyName) )
|
|
{
|
|
return 0; // View
|
|
}
|
|
|
|
return 1; // Add
|
|
}
|
|
|
|
FBlueprintVarActionDetails::~FBlueprintVarActionDetails()
|
|
{
|
|
if(MyBlueprint.IsValid())
|
|
{
|
|
// Remove the callback delegate we registered for
|
|
TWeakPtr<FBlueprintEditor> BlueprintEditor = MyBlueprint.Pin()->GetBlueprintEditor();
|
|
if( BlueprintEditor.IsValid() )
|
|
{
|
|
BlueprintEditor.Pin()->OnRefresh().RemoveAll(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
// FProperty Detail Customization
|
|
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
void FBlueprintVarActionDetails::CustomizeDetails( IDetailLayoutBuilder& DetailLayout )
|
|
{
|
|
CachedVariableProperty = SelectionAsProperty();
|
|
|
|
if(!CachedVariableProperty.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
CachedVariableName = GetVariableName();
|
|
|
|
TWeakPtr<FBlueprintEditor> BlueprintEditor = MyBlueprint.Pin()->GetBlueprintEditor();
|
|
if( BlueprintEditor.IsValid() )
|
|
{
|
|
BlueprintEditor.Pin()->OnRefresh().AddSP(this, &FBlueprintVarActionDetails::OnPostEditorRefresh);
|
|
}
|
|
|
|
UBlueprint* BlueprintPtr = GetBlueprintObj();
|
|
|
|
// Get an appropiate name validator
|
|
TSharedPtr<INameValidatorInterface> NameValidator = nullptr;
|
|
{
|
|
const UEdGraphSchema* Schema = nullptr;
|
|
if (BlueprintPtr)
|
|
{
|
|
TArray<UEdGraph*> Graphs;
|
|
BlueprintPtr->GetAllGraphs(Graphs);
|
|
if (Graphs.Num() > 0)
|
|
{
|
|
Schema = Graphs[0]->GetSchema();
|
|
}
|
|
}
|
|
|
|
if (Schema)
|
|
{
|
|
NameValidator = Schema->GetNameValidator(BlueprintPtr, GetVariableName(), nullptr, FEdGraphSchemaAction_K2Var::StaticGetTypeId());
|
|
}
|
|
}
|
|
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
|
|
// Cache the Blueprint which owns this VariableProperty
|
|
if (UBlueprintGeneratedClass* GeneratedClass = Cast<UBlueprintGeneratedClass>(VariableProperty->GetOwnerClass()))
|
|
{
|
|
PropertyOwnerBlueprint = Cast<UBlueprint>(GeneratedClass->ClassGeneratedBy);
|
|
}
|
|
|
|
IDetailCategoryBuilder& Category = DetailLayout.EditCategory("Variable", LOCTEXT("VariableDetailsCategory", "Variable"));
|
|
const FSlateFontInfo DetailFontInfo = IDetailLayoutBuilder::GetDetailFont();
|
|
|
|
const FString DocLink = TEXT("Shared/Editors/BlueprintEditor/VariableDetails");
|
|
|
|
TSharedPtr<SToolTip> VarNameTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("VarNameTooltip", "The name of the variable."), NULL, DocLink, TEXT("VariableName"));
|
|
|
|
Category.AddCustomRow( LOCTEXT("BlueprintVarActionDetails_VariableNameLabel", "Variable Name") )
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("BlueprintVarActionDetails_VariableNameLabel", "Variable Name"))
|
|
.ToolTip(VarNameTooltip)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
.MaxDesiredWidth(250.0f)
|
|
[
|
|
SAssignNew(VarNameEditableTextBox, SEditableTextBox)
|
|
.Text(this, &FBlueprintVarActionDetails::OnGetVarName)
|
|
.ToolTip(VarNameTooltip)
|
|
.OnTextChanged(this, &FBlueprintVarActionDetails::OnVarNameChanged)
|
|
.OnTextCommitted(this, &FBlueprintVarActionDetails::OnVarNameCommitted)
|
|
.OnVerifyTextChanged_Lambda([this, NameValidator](const FText& InNewText, FText& OutErrorMessage) -> bool
|
|
{
|
|
if (NameValidator.IsValid())
|
|
{
|
|
EValidatorResult ValidatorResult = NameValidator->IsValid(InNewText.ToString());
|
|
switch (ValidatorResult)
|
|
{
|
|
case EValidatorResult::Ok:
|
|
case EValidatorResult::ExistingName:
|
|
// These are fine, don't need to surface to the user, the rename can 'proceed' even if the name is the existing one
|
|
return true;
|
|
default:
|
|
OutErrorMessage = INameValidatorInterface::GetErrorText(InNewText.ToString(), ValidatorResult);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
})
|
|
.IsReadOnly(this, &FBlueprintVarActionDetails::GetVariableNameChangeEnabled)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
];
|
|
|
|
TSharedPtr<SToolTip> VarTypeTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("VarTypeTooltip", "The type of the variable."), NULL, DocLink, TEXT("VariableType"));
|
|
|
|
TArray<TSharedPtr<IPinTypeSelectorFilter>> CustomPinTypeFilters;
|
|
if (BlueprintEditor.IsValid())
|
|
{
|
|
BlueprintEditor.Pin()->GetPinTypeSelectorFilters(CustomPinTypeFilters);
|
|
}
|
|
|
|
const UEdGraphSchema* Schema = GetDefault<UEdGraphSchema_K2>();
|
|
if (BlueprintEditor.IsValid())
|
|
{
|
|
if (BlueprintEditor.Pin()->GetFocusedGraph())
|
|
{
|
|
Schema = BlueprintEditor.Pin()->GetFocusedGraph()->GetSchema();
|
|
}
|
|
}
|
|
|
|
Category.AddCustomRow(LOCTEXT("VariableTypeLabel", "Variable Type"))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("VariableTypeLabel", "Variable Type"))
|
|
.ToolTip(VarTypeTooltip)
|
|
.Font(DetailFontInfo)
|
|
]
|
|
.ValueContent()
|
|
.MaxDesiredWidth(980.f)
|
|
[
|
|
SNew(SPinTypeSelector, FGetPinTypeTree::CreateUObject(GetDefault<UEdGraphSchema_K2>(), &UEdGraphSchema_K2::GetVariableTypeTree))
|
|
.TargetPinType(this, &FBlueprintVarActionDetails::OnGetVarType)
|
|
.OnPinTypeChanged(this, &FBlueprintVarActionDetails::OnVarTypeChanged)
|
|
.IsEnabled(this, &FBlueprintVarActionDetails::GetVariableTypeChangeEnabled)
|
|
.Schema(Schema)
|
|
.TypeTreeFilter(ETypeTreeFilter::None)
|
|
.Font(DetailFontInfo)
|
|
.ToolTip(VarTypeTooltip)
|
|
.CustomFilters(CustomPinTypeFilters)
|
|
]
|
|
.AddCustomContextMenuAction(FUIAction(
|
|
FExecuteAction::CreateRaw(this, &FBlueprintVarActionDetails::OnBrowseToVarType),
|
|
FCanExecuteAction::CreateRaw(this, &FBlueprintVarActionDetails::CanBrowseToVarType)
|
|
),
|
|
LOCTEXT("BrowseToType", "Browse to Type"),
|
|
LOCTEXT("BrowseToTypeToolTip", "Browse to this variable type in the Content Browser."),
|
|
FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.BrowseContent")
|
|
);
|
|
|
|
TSharedPtr<SToolTip> ToolTipTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("VarToolTipTooltip", "Extra information about this variable, shown when cursor is over it."), NULL, DocLink, TEXT("Description"));
|
|
|
|
Category.AddCustomRow( LOCTEXT("IsVariableToolTipLabel", "Description") )
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::IsTooltipEditVisible))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT("IsVariableToolTipLabel", "Description") )
|
|
.ToolTip(ToolTipTooltip)
|
|
.Font( DetailFontInfo )
|
|
]
|
|
.ValueContent()
|
|
.MinDesiredWidth(250.f)
|
|
.MaxDesiredWidth(250.f)
|
|
[
|
|
SNew(SMultiLineEditableTextBox)
|
|
.Text( this, &FBlueprintVarActionDetails::OnGetTooltipText )
|
|
.ToolTipText( this, &FBlueprintVarActionDetails::OnGetTooltipText )
|
|
.OnTextCommitted( this, &FBlueprintVarActionDetails::OnTooltipTextCommitted, CachedVariableName )
|
|
.IsEnabled(IsVariableInBlueprint())
|
|
.Font( DetailFontInfo )
|
|
.ModiferKeyForNewLine(EModifierKey::Shift)
|
|
];
|
|
|
|
TSharedPtr<SToolTip> EditableTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("VarEditableTooltip", "Whether this variable is publicly editable on instances of this Blueprint."), NULL, DocLink, TEXT("Editable"));
|
|
|
|
Category.AddCustomRow( LOCTEXT("IsVariableEditableLabel", "Instance Editable") )
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::ShowEditableCheckboxVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT("IsVariableEditableLabel", "Instance Editable") )
|
|
.ToolTip(EditableTooltip)
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked( this, &FBlueprintVarActionDetails::OnEditableCheckboxState )
|
|
.OnCheckStateChanged( this, &FBlueprintVarActionDetails::OnEditableChanged )
|
|
.IsEnabled(IsVariableInBlueprint())
|
|
.ToolTip(EditableTooltip)
|
|
];
|
|
|
|
TSharedPtr<SToolTip> ReadOnlyTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("VarReadOnlyTooltip", "Whether this variable can be set by Blueprint nodes or if it is read-only."), NULL, DocLink, TEXT("ReadOnly"));
|
|
|
|
Category.AddCustomRow(LOCTEXT("IsVariableReadOnlyLabel", "Blueprint Read Only"))
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::ShowReadOnlyCheckboxVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("IsVariableReadOnlyLabel", "Blueprint Read Only"))
|
|
.ToolTip(ReadOnlyTooltip)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked(this, &FBlueprintVarActionDetails::OnReadyOnlyCheckboxState)
|
|
.OnCheckStateChanged(this, &FBlueprintVarActionDetails::OnReadyOnlyChanged)
|
|
.IsEnabled(IsVariableInBlueprint())
|
|
.ToolTip(ReadOnlyTooltip)
|
|
];
|
|
|
|
if (BlueprintPtr && FBlueprintEditorUtils::ImplementsInterface(BlueprintPtr, true, UNotifyFieldValueChanged::StaticClass()) && !MyBlueprint.Pin()->SelectionAsDelegate())
|
|
{
|
|
// Show the flag if the class implement the interface but only allow the flag to be changed if the variable is defined in BP
|
|
const FText ToolTip = LOCTEXT("FieldNotifyToolTip", "Generate a field entry for the Field Notification system.");
|
|
TSharedPtr<SToolTip> FieldNotificationTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("FieldNotifyToolTip", "Generate a field entry for the Field Notification system."), NULL, DocLink, TEXT("FieldNotify"));
|
|
|
|
Category.AddCustomRow(LOCTEXT("IsVariableFieldNotifyLabel", "Field Notify"))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("IsVariableFieldNotifyLabel", "Field Notify"))
|
|
.ToolTip(FieldNotificationTooltip)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked(this, &FBlueprintVarActionDetails::OnFieldNotifyCheckboxState)
|
|
.OnCheckStateChanged(this, &FBlueprintVarActionDetails::OnFieldNotifyChanged)
|
|
.IsEnabled(IsVariableInBlueprint() && GetPropertyOwnerBlueprint() && IsABlueprintVariable(VariableProperty) && IsAUserVariable(VariableProperty))
|
|
.ToolTip(FieldNotificationTooltip)
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
SNew(UE::FieldNotification::SFieldNotificationCheckList)
|
|
.FieldName(CachedVariableName)
|
|
.BlueprintPtr(BlueprintPtr)
|
|
.Visibility(this, &FBlueprintVarActionDetails::GetFieldNotifyCheckboxListVisibility)
|
|
]
|
|
|
|
];
|
|
}
|
|
|
|
TSharedPtr<SToolTip> Widget3DTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("VariableWidget3D_Tooltip", "When true, allows the user to tweak the vector variable by using a 3D transform widget in the viewport (usable when varible is public/enabled)."), NULL, DocLink, TEXT("Widget3D"));
|
|
|
|
Category.AddCustomRow( LOCTEXT("VariableWidget3D_Prompt", "Show 3D Widget") )
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::Show3DWidgetVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.ToolTip(Widget3DTooltip)
|
|
.Text(LOCTEXT("VariableWidget3D_Prompt", "Show 3D Widget"))
|
|
.Font( DetailFontInfo )
|
|
.IsEnabled(Is3DWidgetEnabled())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked( this, &FBlueprintVarActionDetails::OnCreateWidgetCheckboxState )
|
|
.OnCheckStateChanged( this, &FBlueprintVarActionDetails::OnCreateWidgetChanged )
|
|
.IsEnabled(Is3DWidgetEnabled() && IsVariableInBlueprint())
|
|
.ToolTip(Widget3DTooltip)
|
|
];
|
|
|
|
TSharedPtr<SToolTip> ExposeOnSpawnTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("VariableExposeToSpawn_Tooltip", "Should this variable be exposed as a pin when spawning this Blueprint?"), NULL, DocLink, TEXT("ExposeOnSpawn"));
|
|
|
|
Category.AddCustomRow( LOCTEXT("VariableExposeToSpawnLabel", "Expose on Spawn") )
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::ExposeOnSpawnVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.ToolTip(ExposeOnSpawnTooltip)
|
|
.Text( LOCTEXT("VariableExposeToSpawnLabel", "Expose on Spawn") )
|
|
.Font( DetailFontInfo )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked( this, &FBlueprintVarActionDetails::OnGetExposedToSpawnCheckboxState )
|
|
.OnCheckStateChanged( this, &FBlueprintVarActionDetails::OnExposedToSpawnChanged )
|
|
.IsEnabled(IsVariableInBlueprint())
|
|
.ToolTip(ExposeOnSpawnTooltip)
|
|
];
|
|
|
|
TSharedPtr<SToolTip> PrivateTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("VariablePrivate_Tooltip", "Should this variable be private (derived blueprints cannot modify it)?"), NULL, DocLink, TEXT("Private"));
|
|
|
|
Category.AddCustomRow(LOCTEXT("VariablePrivate", "Private"))
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::ExposePrivateVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.ToolTip(PrivateTooltip)
|
|
.Text( LOCTEXT("VariablePrivate", "Private") )
|
|
.Font( DetailFontInfo )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked( this, &FBlueprintVarActionDetails::OnGetPrivateCheckboxState )
|
|
.OnCheckStateChanged( this, &FBlueprintVarActionDetails::OnPrivateChanged )
|
|
.IsEnabled(IsVariableInBlueprint())
|
|
.ToolTip(PrivateTooltip)
|
|
];
|
|
|
|
TSharedPtr<SToolTip> ExposeToCinematicsTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("VariableExposeToCinematics_Tooltip", "Should this variable be exposed for Sequencer to modify?"), NULL, DocLink, TEXT("ExposeToCinematics"));
|
|
|
|
Category.AddCustomRow( LOCTEXT("VariableExposeToCinematics", "Expose to Cinematics") )
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::ExposeToCinematicsVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.ToolTip(ExposeToCinematicsTooltip)
|
|
.Text( LOCTEXT("VariableExposeToCinematics", "Expose to Cinematics") )
|
|
.Font( DetailFontInfo )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked( this, &FBlueprintVarActionDetails::OnGetExposedToCinematicsCheckboxState )
|
|
.OnCheckStateChanged( this, &FBlueprintVarActionDetails::OnExposedToCinematicsChanged )
|
|
.IsEnabled(IsVariableInBlueprint())
|
|
.ToolTip(ExposeToCinematicsTooltip)
|
|
];
|
|
|
|
FText LocalizedTooltip;
|
|
if (IsConfigCheckBoxEnabled())
|
|
{
|
|
// Build the property specific config variable tool tip
|
|
FFormatNamedArguments ConfigTooltipArgs;
|
|
if (UClass* OwnerClass = VariableProperty->GetOwnerClass())
|
|
{
|
|
const UClass* RealClass = OwnerClass->GetAuthoritativeClass();
|
|
ConfigTooltipArgs.Add(TEXT("ConfigName"), FText::FromName(RealClass->ClassConfigName));
|
|
ConfigTooltipArgs.Add(TEXT("ConfigSection"), FText::FromString(RealClass->GetPathName()));
|
|
}
|
|
LocalizedTooltip = FText::Format(LOCTEXT("VariableExposeToConfig_Tooltip", "Should this variable read its default value from a config file if it is present?\r\n\r\nThis is used for customizing variable default values and behavior between different projects and configurations.\r\n\r\nConfig file [{ConfigName}]\r\nConfig section [{ConfigSection}]"), ConfigTooltipArgs);
|
|
}
|
|
else if (IsVariableInBlueprint())
|
|
{
|
|
// mimics the error that UHT would throw
|
|
LocalizedTooltip = LOCTEXT("ObjectVariableConfig_Tooltip", "Not allowed to use 'config' with object variables");
|
|
}
|
|
TSharedPtr<SToolTip> ExposeToConfigTooltip = IDocumentation::Get()->CreateToolTip(LocalizedTooltip, NULL, DocLink, TEXT("ExposeToConfig"));
|
|
|
|
Category.AddCustomRow( LOCTEXT("VariableExposeToConfig", "Config Variable"), true )
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::ExposeConfigVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.ToolTip( ExposeToConfigTooltip )
|
|
.Text( LOCTEXT("ExposeToConfigLabel", "Config Variable") )
|
|
.Font( DetailFontInfo )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.ToolTip( ExposeToConfigTooltip )
|
|
.IsChecked( this, &FBlueprintVarActionDetails::OnGetConfigVariableCheckboxState )
|
|
.OnCheckStateChanged( this, &FBlueprintVarActionDetails::OnSetConfigVariableState )
|
|
.IsEnabled(this, &FBlueprintVarActionDetails::IsConfigCheckBoxEnabled)
|
|
];
|
|
|
|
PopulateCategories(MyBlueprint.Pin().Get(), CategorySource);
|
|
TSharedPtr<SComboButton> NewComboButton;
|
|
TSharedPtr<SListView<TSharedPtr<FText>>> NewListView;
|
|
|
|
TSharedPtr<SToolTip> CategoryTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("EditCategoryName_Tooltip", "The category of the variable; editing this will place the variable into another category or create a new one."), NULL, DocLink, TEXT("Category"));
|
|
|
|
Category.AddCustomRow( LOCTEXT("CategoryLabel", "Category") )
|
|
.Visibility(GetPropertyOwnerBlueprint()? EVisibility::Visible : EVisibility::Hidden)
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT("CategoryLabel", "Category") )
|
|
.ToolTip(CategoryTooltip)
|
|
.Font( DetailFontInfo )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SAssignNew(NewComboButton, SComboButton)
|
|
.ContentPadding(FMargin(0.0f, 0.0f, 5.0f, 0.0f))
|
|
.IsEnabled(this, &FBlueprintVarActionDetails::GetVariableCategoryChangeEnabled)
|
|
.ToolTip(CategoryTooltip)
|
|
.ButtonContent()
|
|
[
|
|
SNew(SBorder)
|
|
.BorderImage(FAppStyle::Get().GetBrush("NoBorder") )
|
|
.Padding(FMargin(0, 0, 5, 0))
|
|
[
|
|
SNew(SEditableTextBox)
|
|
.Text(this, &FBlueprintVarActionDetails::OnGetCategoryText)
|
|
.OnTextCommitted(this, &FBlueprintVarActionDetails::OnCategoryTextCommitted, CachedVariableName )
|
|
.OnVerifyTextChanged_Lambda([&](const FText& InNewText, FText& OutErrorMessage) -> bool
|
|
{
|
|
if (InNewText.IsEmpty())
|
|
{
|
|
OutErrorMessage = LOCTEXT("CategoryEmpty", "Cannot add a category with an empty string.");
|
|
return false;
|
|
}
|
|
if (InNewText.EqualTo(FText::FromString(GetBlueprintObj()->GetName())))
|
|
{
|
|
OutErrorMessage = LOCTEXT("CategoryEqualsBlueprintName", "Cannot add a category with the same name as the blueprint.");
|
|
return false;
|
|
}
|
|
return true;
|
|
})
|
|
.ToolTip(CategoryTooltip)
|
|
.SelectAllTextWhenFocused(true)
|
|
.RevertTextOnEscape(true)
|
|
.Font( DetailFontInfo )
|
|
]
|
|
]
|
|
.MenuContent()
|
|
[
|
|
SNew(SVerticalBox)
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.MaxHeight(400.0f)
|
|
[
|
|
SAssignNew(NewListView, SListView<TSharedPtr<FText>>)
|
|
.ListItemsSource(&CategorySource)
|
|
.OnGenerateRow(this, &FBlueprintVarActionDetails::MakeCategoryViewWidget)
|
|
.OnSelectionChanged(this, &FBlueprintVarActionDetails::OnCategorySelectionChanged)
|
|
]
|
|
]
|
|
];
|
|
|
|
CategoryComboButton = NewComboButton;
|
|
CategoryListView = NewListView;
|
|
|
|
TSharedPtr<SToolTip> SliderRangeTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("SliderRange_Tooltip", "Allows setting the minimum and maximum values for the UI slider for this variable."), NULL, DocLink, TEXT("SliderRange"));
|
|
|
|
FName UIMin = TEXT("UIMin");
|
|
FName UIMax = TEXT("UIMax");
|
|
Category.AddCustomRow( LOCTEXT("SliderRangeLabel", "Slider Range") )
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::RangeVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT("SliderRangeLabel", "Slider Range") )
|
|
.ToolTip(SliderRangeTooltip)
|
|
.Font( DetailFontInfo )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
.ToolTip(SliderRangeTooltip)
|
|
+SHorizontalBox::Slot()
|
|
.FillWidth(1)
|
|
[
|
|
SNew(SEditableTextBox)
|
|
.Text(this, &FBlueprintVarActionDetails::OnGetMetaKeyValue, UIMin)
|
|
.OnTextCommitted(this, &FBlueprintVarActionDetails::OnMetaKeyValueChanged, UIMin)
|
|
.IsEnabled(IsVariableInBlueprint())
|
|
.Font( DetailFontInfo )
|
|
]
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
SNew( STextBlock )
|
|
.Text( LOCTEXT("Min .. Max Separator", " .. ") )
|
|
.Font(DetailFontInfo)
|
|
]
|
|
+SHorizontalBox::Slot()
|
|
.FillWidth(1)
|
|
[
|
|
SNew(SEditableTextBox)
|
|
.Text(this, &FBlueprintVarActionDetails::OnGetMetaKeyValue, UIMax)
|
|
.OnTextCommitted(this, &FBlueprintVarActionDetails::OnMetaKeyValueChanged, UIMax)
|
|
.IsEnabled(IsVariableInBlueprint())
|
|
.Font( DetailFontInfo )
|
|
]
|
|
];
|
|
|
|
TSharedPtr<SToolTip> ValueRangeTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("ValueRangeLabel_Tooltip", "The range of values allowed by this variable. Values outside of this will be clamped to the range."), NULL, DocLink, TEXT("ValueRange"));
|
|
|
|
FName ClampMin = TEXT("ClampMin");
|
|
FName ClampMax = TEXT("ClampMax");
|
|
Category.AddCustomRow(LOCTEXT("ValueRangeLabel", "Value Range"))
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::RangeVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("ValueRangeLabel", "Value Range"))
|
|
.ToolTipText(LOCTEXT("ValueRangeLabel_Tooltip", "The range of values allowed by this variable. Values outside of this will be clamped to the range."))
|
|
.ToolTip(ValueRangeTooltip)
|
|
.Font(DetailFontInfo)
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.FillWidth(1)
|
|
[
|
|
SNew(SEditableTextBox)
|
|
.Text(this, &FBlueprintVarActionDetails::OnGetMetaKeyValue, ClampMin)
|
|
.OnTextCommitted(this, &FBlueprintVarActionDetails::OnMetaKeyValueChanged, ClampMin)
|
|
.IsEnabled(IsVariableInBlueprint())
|
|
.Font(DetailFontInfo)
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("Min .. Max Separator", " .. "))
|
|
.Font(DetailFontInfo)
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
.FillWidth(1)
|
|
[
|
|
SNew(SEditableTextBox)
|
|
.Text(this, &FBlueprintVarActionDetails::OnGetMetaKeyValue, ClampMax)
|
|
.OnTextCommitted(this, &FBlueprintVarActionDetails::OnMetaKeyValueChanged, ClampMax)
|
|
.IsEnabled(IsVariableInBlueprint())
|
|
.Font(DetailFontInfo)
|
|
]
|
|
];
|
|
|
|
TSharedPtr<SToolTip> UnitsTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("VarUnitsTooltip", "Units of this variable."), NULL, DocLink, TEXT("Units"));
|
|
|
|
UnitsOptions.Empty();
|
|
UnitsOptions.Add(MakeShareable(new FString("None")));
|
|
for (const TCHAR* UnitsName : FUnitConversion::GetSupportedUnits())
|
|
{
|
|
UnitsOptions.Add(MakeShareable(new FString(UnitsName)));
|
|
}
|
|
|
|
Category.AddCustomRow(LOCTEXT("VariableUnitsLabel", "Units"))
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::GetVariableUnitsVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("VariableUnitsLabel", "Units"))
|
|
.ToolTip(UnitsTooltip)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(STextComboBox)
|
|
.OptionsSource( &UnitsOptions )
|
|
.InitiallySelectedItem(GetVariableUnits())
|
|
.OnSelectionChanged( this, &FBlueprintVarActionDetails::OnVariableUnitsChanged )
|
|
.ToolTip(UnitsTooltip)
|
|
.Font( DetailFontInfo )
|
|
];
|
|
|
|
TSharedPtr<SToolTip> BitmaskTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("VarBitmaskTooltip", "Whether or not to treat this variable as a bitmask."), nullptr, DocLink, TEXT("Bitmask"));
|
|
|
|
Category.AddCustomRow(LOCTEXT("IsVariableBitmaskLabel", "Bitmask"))
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::BitmaskVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("IsVariableBitmaskLabel", "Bitmask"))
|
|
.ToolTip(BitmaskTooltip)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked(this, &FBlueprintVarActionDetails::OnBitmaskCheckboxState)
|
|
.OnCheckStateChanged(this, &FBlueprintVarActionDetails::OnBitmaskChanged)
|
|
.IsEnabled(IsVariableInBlueprint())
|
|
.ToolTip(BitmaskTooltip)
|
|
];
|
|
|
|
BitmaskEnumTypePaths.Empty();
|
|
BitmaskEnumTypePaths.Add(MakeShareable(new FTopLevelAssetPath())); // option to set the bitmask to None
|
|
for (TObjectIterator<UEnum> EnumIt; EnumIt; ++EnumIt)
|
|
{
|
|
UEnum* CurrentEnum = *EnumIt;
|
|
if (UEdGraphSchema_K2::IsAllowableBlueprintVariableType(CurrentEnum) && CurrentEnum->HasMetaData(TEXT("Bitflags")))
|
|
{
|
|
BitmaskEnumTypePaths.Add(MakeShareable(new FTopLevelAssetPath(CurrentEnum->GetPackage()->GetFName(), CurrentEnum->GetFName())));
|
|
}
|
|
}
|
|
|
|
TSharedPtr<SToolTip> BitmaskEnumTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("VarBitmaskEnumTooltip", "If this is a bitmask, choose an optional enumeration type for the flags. Note that changing this will also reset the default value."), nullptr, DocLink, TEXT("Bitmask Flags"));
|
|
|
|
Category.AddCustomRow(LOCTEXT("BitmaskEnumLabel", "Bitmask Enum"))
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::BitmaskVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("BitmaskEnumLabel", "Bitmask Enum"))
|
|
.ToolTip(BitmaskEnumTooltip)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SComboBox<TSharedPtr<FTopLevelAssetPath>>)
|
|
.OptionsSource(&BitmaskEnumTypePaths)
|
|
.InitiallySelectedItem(GetBitmaskEnumTypePath())
|
|
.OnSelectionChanged(this, &FBlueprintVarActionDetails::OnBitmaskEnumTypeChanged)
|
|
.OnGenerateWidget(this, &FBlueprintVarActionDetails::GenerateBitmaskEnumTypeWidget)
|
|
.IsEnabled(IsVariableInBlueprint() && OnBitmaskCheckboxState() == ECheckBoxState::Checked)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &FBlueprintVarActionDetails::GetBitmaskEnumTypeName)
|
|
]
|
|
];
|
|
|
|
ReplicationOptions.Empty();
|
|
ReplicationOptions.Add(MakeShareable(new FString("None")));
|
|
ReplicationOptions.Add(MakeShareable(new FString("Replicated")));
|
|
ReplicationOptions.Add(MakeShareable(new FString("RepNotify")));
|
|
|
|
TSharedPtr<SToolTip> ReplicationTooltip = IDocumentation::Get()->CreateToolTip( TAttribute<FText>::Create( TAttribute<FText>::FGetter::CreateRaw( this, &FBlueprintVarActionDetails::ReplicationTooltip ) ), NULL, DocLink, TEXT("Replication"));
|
|
|
|
Category.AddCustomRow( LOCTEXT("VariableReplicationLabel", "Replication") )
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::ReplicationVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.ToolTip(ReplicationTooltip)
|
|
.Text( LOCTEXT("VariableReplicationLabel", "Replication") )
|
|
.Font( DetailFontInfo )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(STextComboBox)
|
|
.OptionsSource( &ReplicationOptions )
|
|
.InitiallySelectedItem(GetVariableReplicationType())
|
|
.OnSelectionChanged( this, &FBlueprintVarActionDetails::OnChangeReplication )
|
|
.IsEnabled(this, &FBlueprintVarActionDetails::ReplicationEnabled)
|
|
.ToolTip(ReplicationTooltip)
|
|
];
|
|
|
|
ReplicationConditionEnumTypeNames.Empty();
|
|
UEnum* Enum = StaticEnum<ELifetimeCondition>();
|
|
check(Enum);
|
|
|
|
for (int32 i = 0; i < Enum->NumEnums(); i++)
|
|
{
|
|
if (!Enum->HasMetaData(TEXT("Hidden"), i))
|
|
{
|
|
ReplicationConditionEnumTypeNames.Add(MakeShareable(new FString(Enum->GetDisplayNameTextByIndex(i).ToString())));
|
|
}
|
|
}
|
|
|
|
Category.AddCustomRow(LOCTEXT("VariableReplicationConditionsLabel", "Replication Condition"))
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::ReplicationVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.ToolTip(ReplicationTooltip)
|
|
.Text(LOCTEXT("VariableReplicationConditionsLabel", "Replication Condition"))
|
|
.Font(DetailFontInfo)
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(STextComboBox)
|
|
.OptionsSource(&ReplicationConditionEnumTypeNames)
|
|
.InitiallySelectedItem(GetVariableReplicationCondition())
|
|
.OnSelectionChanged(this, &FBlueprintVarActionDetails::OnChangeReplicationCondition)
|
|
.IsEnabled(this, &FBlueprintVarActionDetails::ReplicationConditionEnabled)
|
|
];
|
|
|
|
|
|
UBlueprint* BlueprintObj = GetBlueprintObj();
|
|
|
|
// Handle event generation
|
|
if ( FBlueprintEditorUtils::DoesSupportEventGraphs(BlueprintObj) )
|
|
{
|
|
if (FObjectProperty* ComponentProperty = CastField<FObjectProperty>(VariableProperty))
|
|
{
|
|
AddEventsCategory(DetailLayout, ComponentProperty->GetFName(), ComponentProperty->PropertyClass);
|
|
}
|
|
}
|
|
|
|
// Add in default value editing for properties that can be edited, local properties cannot be edited
|
|
if ((BlueprintObj != nullptr) && (BlueprintObj->GeneratedClass != nullptr))
|
|
{
|
|
bool bVariableRenamed = false;
|
|
if (VariableProperty != nullptr && IsVariableInBlueprint())
|
|
{
|
|
// Determine the current property name on the CDO is stale
|
|
if (PropertyOwnerBlueprint.IsValid() && VariableProperty)
|
|
{
|
|
UBlueprint* PropertyBlueprint = PropertyOwnerBlueprint.Get();
|
|
const int32 VarIndex = FBlueprintEditorUtils::FindNewVariableIndex(PropertyBlueprint, CachedVariableName);
|
|
if (VarIndex != INDEX_NONE)
|
|
{
|
|
const FGuid VarGuid = PropertyBlueprint->NewVariables[VarIndex].VarGuid;
|
|
if (UBlueprintGeneratedClass* AuthoritiveBPGC = Cast<UBlueprintGeneratedClass>(PropertyBlueprint->GeneratedClass))
|
|
{
|
|
if (const FName* OldName = AuthoritiveBPGC->PropertyGuids.FindKey(VarGuid))
|
|
{
|
|
bVariableRenamed = CachedVariableName != *OldName;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
const FProperty* OriginalProperty = nullptr;
|
|
|
|
if(!IsALocalVariable(VariableProperty))
|
|
{
|
|
OriginalProperty = FindFProperty<FProperty>(BlueprintObj->GeneratedClass, VariableProperty->GetFName());
|
|
}
|
|
else
|
|
{
|
|
OriginalProperty = VariableProperty;
|
|
}
|
|
|
|
if (OriginalProperty == nullptr || bVariableRenamed)
|
|
{
|
|
// Prevent editing the default value of a skeleton property
|
|
VariableProperty = nullptr;
|
|
}
|
|
else if (const FStructProperty* StructProperty = CastField<const FStructProperty>(OriginalProperty))
|
|
{
|
|
// Prevent editing the default value of a stale struct
|
|
const UUserDefinedStruct* BGStruct = Cast<const UUserDefinedStruct>(StructProperty->Struct);
|
|
if (BGStruct && (EUserDefinedStructureStatus::UDSS_UpToDate != BGStruct->Status))
|
|
{
|
|
VariableProperty = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find the class containing the variable
|
|
UClass* VariableClass = (VariableProperty ? VariableProperty->GetTypedOwner<UClass>() : nullptr);
|
|
|
|
FText ErrorMessage;
|
|
IDetailCategoryBuilder& DefaultValueCategory = DetailLayout.EditCategory(TEXT("DefaultValueCategory"), LOCTEXT("DefaultValueCategoryHeading", "Default Value"));
|
|
|
|
if (VariableProperty == nullptr)
|
|
{
|
|
if (BlueprintObj->Status != BS_UpToDate)
|
|
{
|
|
ErrorMessage = LOCTEXT("VariableMissing_DirtyBlueprint", "Please compile the blueprint");
|
|
}
|
|
else
|
|
{
|
|
ErrorMessage = LOCTEXT("VariableMissing_CleanBlueprint", "Failed to find variable property");
|
|
}
|
|
}
|
|
|
|
// Show the error message if something went wrong
|
|
if (!ErrorMessage.IsEmpty())
|
|
{
|
|
DefaultValueCategory.AddCustomRow( ErrorMessage )
|
|
.WholeRowWidget
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(STextBlock)
|
|
.ToolTipText(ErrorMessage)
|
|
.Text(ErrorMessage)
|
|
.Font(DetailFontInfo)
|
|
];
|
|
}
|
|
else
|
|
{
|
|
TSharedPtr<IDetailsView> DetailsView;
|
|
if (BlueprintEditor.IsValid())
|
|
{
|
|
DetailsView = BlueprintEditor.Pin()->GetInspector()->GetPropertyView();
|
|
}
|
|
|
|
if(IsALocalVariable(VariableProperty))
|
|
{
|
|
UFunction* StructScope = VariableProperty->GetOwner<UFunction>();
|
|
check(StructScope);
|
|
|
|
UEdGraph* Graph = FBlueprintEditorUtils::FindScopeGraph(GetBlueprintObj(), (UFunction*)StructScope);
|
|
|
|
// Find the function entry nodes in the current graph
|
|
TArray<UK2Node_FunctionEntry*> EntryNodes;
|
|
Graph->GetNodesOfClass(EntryNodes);
|
|
|
|
// There should always be an entry node in the function graph
|
|
check(EntryNodes.Num() > 0);
|
|
|
|
UK2Node_FunctionEntry* FuncEntry = EntryNodes[0];
|
|
|
|
TSharedPtr<FStructOnScope> StructData = MakeShareable(new FStructOnScope((UFunction*)StructScope));
|
|
StructData->SetPackage(BlueprintObj->GetPackage());
|
|
|
|
for (const FBPVariableDescription& LocalVar : FuncEntry->LocalVariables)
|
|
{
|
|
if (LocalVar.VarName == VariableProperty->GetFName())
|
|
{
|
|
// Only set the default value if there is one
|
|
if (!LocalVar.DefaultValue.IsEmpty())
|
|
{
|
|
FBlueprintEditorUtils::PropertyValueFromString(VariableProperty, LocalVar.DefaultValue, StructData->GetStructMemory());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (DetailsView.IsValid())
|
|
{
|
|
TWeakObjectPtr<UK2Node_EditablePinBase> EntryNode = FuncEntry;
|
|
DetailsView->OnFinishedChangingProperties().AddSP(this, &FBlueprintVarActionDetails::OnFinishedChangingLocalVariable, StructData, EntryNode);
|
|
}
|
|
|
|
IDetailPropertyRow* Row = DefaultValueCategory.AddExternalStructureProperty(StructData, VariableProperty->GetFName());
|
|
}
|
|
else
|
|
{
|
|
UBlueprint* CurrPropertyOwnerBlueprint = IsVariableInheritedByBlueprint() ? GetBlueprintObj() : GetPropertyOwnerBlueprint();
|
|
UObject* TargetBlueprintDefaultObject = nullptr;
|
|
if (CurrPropertyOwnerBlueprint && CurrPropertyOwnerBlueprint->GeneratedClass)
|
|
{
|
|
TargetBlueprintDefaultObject = CurrPropertyOwnerBlueprint->GeneratedClass->GetDefaultObject();
|
|
}
|
|
else if (UBlueprint* PropertyOwnerBP = GetPropertyOwnerBlueprint())
|
|
{
|
|
TargetBlueprintDefaultObject = PropertyOwnerBP->GeneratedClass->GetDefaultObject();
|
|
}
|
|
else if (CachedVariableProperty.IsValid())
|
|
{
|
|
// Capture the non-BP class CDO so we can show the default value
|
|
UClass* OwnerClass = CachedVariableProperty->GetOwnerClass();
|
|
TargetBlueprintDefaultObject = OwnerClass ? OwnerClass->GetDefaultObject() : nullptr;
|
|
}
|
|
|
|
if (TargetBlueprintDefaultObject != nullptr)
|
|
{
|
|
// Things are in order, show the property and allow it to be edited
|
|
TArray<UObject*> ObjectList;
|
|
ObjectList.Add(TargetBlueprintDefaultObject);
|
|
IDetailPropertyRow* Row = DefaultValueCategory.AddExternalObjectProperty(ObjectList, VariableProperty->GetFName());
|
|
if (Row != nullptr)
|
|
{
|
|
Row->IsEnabled(IsVariableInheritedByBlueprint());
|
|
}
|
|
|
|
if (DetailsView.IsValid())
|
|
{
|
|
DetailsView->OnFinishedChangingProperties().AddSP(this, &FBlueprintVarActionDetails::OnFinishedChangingVariable);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TSharedPtr<SToolTip> TransientTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("VariableTransient_Tooltip", "Should this variable not serialize and be zero-filled at load?"), NULL, DocLink, TEXT("Transient"));
|
|
|
|
Category.AddCustomRow(LOCTEXT("VariableTransient", "Transient"), true)
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::GetTransientVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.ToolTip(TransientTooltip)
|
|
.Text( LOCTEXT("VariableTransient", "Transient") )
|
|
.Font( DetailFontInfo )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked( this, &FBlueprintVarActionDetails::OnGetTransientCheckboxState )
|
|
.OnCheckStateChanged( this, &FBlueprintVarActionDetails::OnTransientChanged )
|
|
.IsEnabled(IsVariableInBlueprint())
|
|
.ToolTip(TransientTooltip)
|
|
];
|
|
|
|
TSharedPtr<SToolTip> SaveGameTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("VariableSaveGame_Tooltip", "Should this variable be serialized for saved games?"), NULL, DocLink, TEXT("SaveGame"));
|
|
|
|
Category.AddCustomRow(LOCTEXT("VariableSaveGame", "SaveGame"), true)
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::GetSaveGameVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.ToolTip(SaveGameTooltip)
|
|
.Text( LOCTEXT("VariableSaveGame", "SaveGame") )
|
|
.Font( DetailFontInfo )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked( this, &FBlueprintVarActionDetails::OnGetSaveGameCheckboxState )
|
|
.OnCheckStateChanged( this, &FBlueprintVarActionDetails::OnSaveGameChanged )
|
|
.IsEnabled(IsVariableInBlueprint())
|
|
.ToolTip(SaveGameTooltip)
|
|
];
|
|
|
|
TSharedPtr<SToolTip> AdvancedDisplayTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("VariableAdvancedDisplay_Tooltip", "Hide this variable in Class Defaults windows by default"), NULL, DocLink, TEXT("AdvancedDisplay"));
|
|
|
|
Category.AddCustomRow(LOCTEXT("VariableAdvancedDisplay", "Advanced Display"), true)
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::GetAdvancedDisplayVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.ToolTip(AdvancedDisplayTooltip)
|
|
.Text(LOCTEXT("VariableAdvancedDisplay", "Advanced Display"))
|
|
.Font(DetailFontInfo)
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked(this, &FBlueprintVarActionDetails::OnGetAdvancedDisplayCheckboxState)
|
|
.OnCheckStateChanged(this, &FBlueprintVarActionDetails::OnAdvancedDisplayChanged)
|
|
.IsEnabled(IsVariableInBlueprint())
|
|
.ToolTip(AdvancedDisplayTooltip)
|
|
];
|
|
|
|
TSharedPtr<SToolTip> MultilineTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("VariableMultilineTooltip_Tooltip", "Allow the value of this variable to have newlines (use Shift+Enter to add one while editing)"), NULL, DocLink, TEXT("Multiline"));
|
|
|
|
Category.AddCustomRow(LOCTEXT("VariableMultilineTooltip", "Multi line"), true)
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::GetMultilineVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.ToolTip(MultilineTooltip)
|
|
.Text(LOCTEXT("VariableMultiline", "Multi line"))
|
|
.Font(DetailFontInfo)
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked(this, &FBlueprintVarActionDetails::OnGetMultilineCheckboxState)
|
|
.OnCheckStateChanged(this, &FBlueprintVarActionDetails::OnMultilineChanged)
|
|
.IsEnabled(IsVariableInBlueprint())
|
|
.ToolTip(MultilineTooltip)
|
|
];
|
|
|
|
TSharedPtr<SToolTip> DeprecatedTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("VariableDeprecated_Tooltip", "Deprecate usage of this variable. Any nodes that reference it will produce a compiler warning indicating that it should be removed or replaced."), nullptr, DocLink, TEXT("Deprecated"));
|
|
|
|
Category.AddCustomRow(LOCTEXT("VariableDeprecated", "Deprecated"), true)
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::GetDeprecatedVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.ToolTip(DeprecatedTooltip)
|
|
.Text(LOCTEXT("VariableDeprecated", "Deprecated"))
|
|
.Font(DetailFontInfo)
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked(this, &FBlueprintVarActionDetails::OnGetDeprecatedCheckboxState)
|
|
.OnCheckStateChanged(this, &FBlueprintVarActionDetails::OnDeprecatedChanged)
|
|
.IsEnabled(IsVariableInBlueprint())
|
|
.ToolTip(DeprecatedTooltip)
|
|
];
|
|
|
|
TSharedPtr<SToolTip> DeprecationMessageTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("VariableDeprecationMessage_Tooltip", "Optional message to include with the deprecation compiler warning. For example: \'X is no longer being used. Please replace with Y.\'"), nullptr, DocLink, TEXT("DeprecationMessage"));
|
|
|
|
Category.AddCustomRow(LOCTEXT("VariableDeprecationMessageLabel", "Deprecation Message"), true)
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::GetDeprecatedVisibility))
|
|
.IsEnabled(TAttribute<bool>(this, &FBlueprintVarActionDetails::IsVariableDeprecated))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.ToolTip(DeprecationMessageTooltip)
|
|
.Text(LOCTEXT("VariableDeprecationMessageLabel", "Deprecation Message"))
|
|
.Font(DetailFontInfo)
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SEditableTextBox)
|
|
.Text(this, &FBlueprintVarActionDetails::GetDeprecationMessageText)
|
|
.OnTextCommitted(this, &FBlueprintVarActionDetails::OnDeprecationMessageTextCommitted, CachedVariableName)
|
|
.IsEnabled(IsVariableInBlueprint())
|
|
.ToolTipText(this, &FBlueprintVarActionDetails::GetDeprecationMessageText)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
];
|
|
|
|
CollectDropDownOptions();
|
|
|
|
TSharedPtr<SToolTip> GetOptionsTooltip = IDocumentation::Get()->CreateToolTip(
|
|
LOCTEXT(
|
|
"VariableGetOptions_Tooltip",
|
|
"The name of the function which will populate a list of options the user can select from for the value of this variable."
|
|
),
|
|
nullptr, DocLink, TEXT("GetOptions")
|
|
);
|
|
|
|
TSharedPtr<SToolTip> GetOptionsInputTooltip = IDocumentation::Get()->CreateToolTip(
|
|
LOCTEXT(
|
|
"VariableGetOptions_InputTooltip",
|
|
"List of functions that return an array of names or strings.\n"
|
|
"You can also enter the name of a global static function, in the form of 'ClassName.FunctionName'."
|
|
),
|
|
nullptr, DocLink, TEXT("GetOptions")
|
|
);
|
|
|
|
Category.AddCustomRow(LOCTEXT("VariableGetOptions", "Drop-down Options"), true)
|
|
.Visibility(TAttribute<EVisibility>(this, &FBlueprintVarActionDetails::GetDropDownOptionsVisibility))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.ToolTip(GetOptionsTooltip)
|
|
.Text(LOCTEXT("VariableGetOptions", "Drop-down Options"))
|
|
.Font(DetailFontInfo)
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SSearchableComboBox)
|
|
.ToolTip(GetOptionsInputTooltip)
|
|
.OptionsSource(&DropDownFunctionOptions)
|
|
.OnGenerateWidget(this, &FBlueprintVarActionDetails::GenerateDropDownOptionWidget)
|
|
.OnSelectionChanged(this, &FBlueprintVarActionDetails::OnDropDownOptionSelectionChanged)
|
|
.Content()
|
|
[
|
|
SNew(SEditableTextBox)
|
|
.ToolTip(GetOptionsInputTooltip)
|
|
.SelectAllTextWhenFocused(true)
|
|
.RevertTextOnEscape(true)
|
|
.Font(DetailFontInfo)
|
|
.Text(this, &FBlueprintVarActionDetails::GetDropDownOptionDisplayText)
|
|
.OnTextCommitted(this, &FBlueprintVarActionDetails::OnDropDownOptionTextChanged)
|
|
]
|
|
];
|
|
|
|
TSharedPtr<SToolTip> PropertyFlagsTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("DefinedPropertyFlags_Tooltip", "List of defined flags for this property"), NULL, DocLink, TEXT("PropertyFlags"));
|
|
|
|
Category.AddCustomRow(LOCTEXT("DefinedPropertyFlags", "Defined Property Flags"), true)
|
|
.WholeRowWidget
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(STextBlock)
|
|
.ToolTip(PropertyFlagsTooltip)
|
|
.Text( LOCTEXT("DefinedPropertyFlags", "Defined Property Flags") )
|
|
.Font( IDetailLayoutBuilder::GetDetailFontBold() )
|
|
];
|
|
|
|
Category.AddCustomRow(FText::GetEmpty(), true)
|
|
.WholeRowWidget
|
|
[
|
|
SAssignNew(PropertyFlagWidget, SListView< TSharedPtr< FString > >)
|
|
.OnGenerateRow(this, &FBlueprintVarActionDetails::OnGenerateWidgetForPropertyList)
|
|
.ListItemsSource(&PropertyFlags)
|
|
.SelectionMode(ESelectionMode::None)
|
|
.ScrollbarVisibility(EVisibility::Collapsed)
|
|
.ToolTip(PropertyFlagsTooltip)
|
|
];
|
|
|
|
RefreshPropertyFlags();
|
|
}
|
|
|
|
// See if anything else wants to customize our details
|
|
FBlueprintEditorModule& BlueprintEditorModule = FModuleManager::GetModuleChecked<FBlueprintEditorModule>("Kismet");
|
|
TArray<TSharedPtr<IDetailCustomization>> Customizations = BlueprintEditorModule.CustomizeVariable(CachedVariableProperty->GetClass(), BlueprintEditor.Pin());
|
|
ExternalDetailCustomizations.Append(Customizations);
|
|
for (TSharedPtr<IDetailCustomization> ExternalDetailCustomization : ExternalDetailCustomizations)
|
|
{
|
|
ExternalDetailCustomization->CustomizeDetails(DetailLayout);
|
|
}
|
|
}
|
|
|
|
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
|
|
void FBlueprintVarActionDetails::RefreshPropertyFlags()
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if(VariableProperty)
|
|
{
|
|
PropertyFlags.Empty();
|
|
for( const TCHAR* PropertyFlag : ParsePropertyFlags(VariableProperty->PropertyFlags) )
|
|
{
|
|
PropertyFlags.Add(MakeShareable<FString>(new FString(PropertyFlag)));
|
|
}
|
|
|
|
PropertyFlagWidget.Pin()->RequestListRefresh();
|
|
}
|
|
}
|
|
|
|
TSharedRef<ITableRow> FBlueprintVarActionDetails::OnGenerateWidgetForPropertyList( TSharedPtr< FString > Item, const TSharedRef<STableViewBase>& OwnerTable )
|
|
{
|
|
return SNew(STableRow< TSharedPtr< FString > >, OwnerTable)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(FText::FromString(*Item.Get()))
|
|
.ToolTipText(FText::FromString(*Item.Get()))
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked(true ? ECheckBoxState::Checked : ECheckBoxState::Unchecked)
|
|
.IsEnabled(false)
|
|
]
|
|
];
|
|
}
|
|
|
|
bool FBlueprintVarActionDetails::IsAUserVariable(FProperty* VariableProperty) const
|
|
{
|
|
FObjectProperty* VariableObjProp = VariableProperty ? CastField<FObjectProperty>(VariableProperty) : NULL;
|
|
|
|
if (VariableObjProp != NULL && VariableObjProp->PropertyClass != NULL)
|
|
{
|
|
return FBlueprintEditorUtils::IsVariableCreatedByBlueprint(GetBlueprintObj(), VariableObjProp);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool FBlueprintVarActionDetails::IsASCSVariable(FProperty* VariableProperty) const
|
|
{
|
|
FObjectProperty* VariableObjProp = VariableProperty ? CastField<FObjectProperty>(VariableProperty) : NULL;
|
|
if (VariableObjProp != NULL && VariableObjProp->PropertyClass != NULL)
|
|
{
|
|
return (!IsAUserVariable(VariableProperty) && VariableObjProp->PropertyClass->IsChildOf(UActorComponent::StaticClass()));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FBlueprintVarActionDetails::IsABlueprintVariable(FProperty* VariableProperty) const
|
|
{
|
|
UClass* VarSourceClass = VariableProperty ? VariableProperty->GetOwner<UClass>() : NULL;
|
|
if(VarSourceClass)
|
|
{
|
|
return (VarSourceClass->ClassGeneratedBy != NULL);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FBlueprintVarActionDetails::IsALocalVariable(FProperty* VariableProperty) const
|
|
{
|
|
return VariableProperty && (VariableProperty->GetOwner<UFunction>() != NULL);
|
|
}
|
|
|
|
UStruct* FBlueprintVarActionDetails::GetLocalVariableScope(FProperty* VariableProperty) const
|
|
{
|
|
if(IsALocalVariable(VariableProperty))
|
|
{
|
|
return VariableProperty->GetOwner<UFunction>();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool FBlueprintVarActionDetails::GetVariableNameChangeEnabled() const
|
|
{
|
|
bool bIsReadOnly = true;
|
|
|
|
UBlueprint* BlueprintObj = GetBlueprintObj();
|
|
check(BlueprintObj != nullptr);
|
|
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if(VariableProperty != nullptr && IsVariableInBlueprint())
|
|
{
|
|
if(FBlueprintEditorUtils::FindNewVariableIndex(BlueprintObj, CachedVariableName) != INDEX_NONE)
|
|
{
|
|
bIsReadOnly = false;
|
|
}
|
|
else if(BlueprintObj->FindTimelineTemplateByVariableName(CachedVariableName))
|
|
{
|
|
bIsReadOnly = false;
|
|
}
|
|
else if(IsASCSVariable(VariableProperty) && BlueprintObj->SimpleConstructionScript != nullptr)
|
|
{
|
|
if (USCS_Node* Node = BlueprintObj->SimpleConstructionScript->FindSCSNode(CachedVariableName))
|
|
{
|
|
bIsReadOnly = !FComponentEditorUtils::IsValidVariableNameString(Node->ComponentTemplate, Node->GetVariableName().ToString());
|
|
}
|
|
}
|
|
else if(IsALocalVariable(VariableProperty))
|
|
{
|
|
bIsReadOnly = false;
|
|
}
|
|
}
|
|
|
|
return bIsReadOnly;
|
|
}
|
|
|
|
FText FBlueprintVarActionDetails::OnGetVarName() const
|
|
{
|
|
return FText::FromName(CachedVariableName);
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnVarNameChanged(const FText& InNewText)
|
|
{
|
|
bIsVarNameInvalid = true;
|
|
|
|
UBlueprint* BlueprintObj = GetBlueprintObj();
|
|
check(BlueprintObj != nullptr);
|
|
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if(VariableProperty && IsASCSVariable(VariableProperty) && BlueprintObj->SimpleConstructionScript != nullptr)
|
|
{
|
|
for (USCS_Node* Node : BlueprintObj->SimpleConstructionScript->GetAllNodes())
|
|
{
|
|
if (Node && Node->GetVariableName() == CachedVariableName && !FComponentEditorUtils::IsValidVariableNameString(Node->ComponentTemplate, InNewText.ToString()))
|
|
{
|
|
VarNameEditableTextBox->SetError(LOCTEXT("ComponentVariableRenameFailed_NotValid", "This name is reserved for engine use."));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
TSharedPtr<INameValidatorInterface> NameValidator = MakeShareable(new FKismetNameValidator(BlueprintObj, CachedVariableName, GetLocalVariableScope(VariableProperty)));
|
|
|
|
EValidatorResult ValidatorResult = NameValidator->IsValid(InNewText.ToString());
|
|
if(ValidatorResult == EValidatorResult::AlreadyInUse)
|
|
{
|
|
VarNameEditableTextBox->SetError(FText::Format(LOCTEXT("RenameFailed_InUse", "{0} is in use by another variable or function!"), InNewText));
|
|
}
|
|
else if(ValidatorResult == EValidatorResult::EmptyName)
|
|
{
|
|
VarNameEditableTextBox->SetError(LOCTEXT("RenameFailed_LeftBlank", "Names cannot be left blank!"));
|
|
}
|
|
else if(ValidatorResult == EValidatorResult::TooLong)
|
|
{
|
|
VarNameEditableTextBox->SetError(FText::Format( LOCTEXT("RenameFailed_NameTooLong", "Names must have fewer than {0} characters!"), FText::AsNumber( FKismetNameValidator::GetMaximumNameLength())));
|
|
}
|
|
else if(ValidatorResult == EValidatorResult::LocallyInUse)
|
|
{
|
|
VarNameEditableTextBox->SetError(LOCTEXT("ConflictsWithProperty", "Conflicts with another local variable or function parameter!"));
|
|
}
|
|
else
|
|
{
|
|
bIsVarNameInvalid = false;
|
|
VarNameEditableTextBox->SetError(FText::GetEmpty());
|
|
}
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnVarNameCommitted(const FText& InNewText, ETextCommit::Type InTextCommit)
|
|
{
|
|
if(InTextCommit != ETextCommit::OnCleared && !bIsVarNameInvalid)
|
|
{
|
|
const FScopedTransaction Transaction( LOCTEXT( "RenameVariable", "Rename Variable" ) );
|
|
|
|
FName NewVarName = FName(*InNewText.ToString());
|
|
|
|
// Double check we're not renaming a timeline disguised as a variable
|
|
bool bIsTimeline = false;
|
|
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty != NULL)
|
|
{
|
|
// Don't allow removal of timeline properties - you need to remove the timeline node for that
|
|
FObjectProperty* ObjProperty = CastField<FObjectProperty>(VariableProperty);
|
|
if(ObjProperty != NULL && ObjProperty->PropertyClass == UTimelineComponent::StaticClass())
|
|
{
|
|
bIsTimeline = true;
|
|
}
|
|
|
|
// Rename as a timeline if required
|
|
if (bIsTimeline)
|
|
{
|
|
FBlueprintEditorUtils::RenameTimeline(GetBlueprintObj(), CachedVariableName, NewVarName);
|
|
}
|
|
else if(IsALocalVariable(VariableProperty))
|
|
{
|
|
UFunction* LocalVarScope = VariableProperty->GetOwner<UFunction>();
|
|
FBlueprintEditorUtils::RenameLocalVariable(GetBlueprintObj(), LocalVarScope, CachedVariableName, NewVarName);
|
|
}
|
|
else
|
|
{
|
|
FBlueprintEditorUtils::RenameMemberVariable(GetBlueprintObj(), CachedVariableName, NewVarName);
|
|
}
|
|
|
|
check(MyBlueprint.IsValid());
|
|
MyBlueprint.Pin()->SelectItemByName(NewVarName, ESelectInfo::OnMouseClick);
|
|
}
|
|
}
|
|
|
|
bIsVarNameInvalid = false;
|
|
VarNameEditableTextBox->SetError(FText::GetEmpty());
|
|
}
|
|
|
|
bool FBlueprintVarActionDetails::GetVariableTypeChangeEnabled() const
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if(VariableProperty && !VariableProperty->IsA<FMulticastDelegateProperty>() && IsVariableInBlueprint())
|
|
{
|
|
if (!IsALocalVariable(VariableProperty))
|
|
{
|
|
if(GetBlueprintObj()->SkeletonGeneratedClass->GetAuthoritativeClass() != VariableProperty->GetOwnerClass()->GetAuthoritativeClass())
|
|
{
|
|
return false;
|
|
}
|
|
// If the variable belongs to this class and cannot be found in the member variable list, it is not editable (it may be a component)
|
|
if (FBlueprintEditorUtils::FindNewVariableIndex(GetBlueprintObj(), CachedVariableName) == INDEX_NONE)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FBlueprintVarActionDetails::GetVariableCategoryChangeEnabled() const
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if(VariableProperty && IsVariableInBlueprint())
|
|
{
|
|
if(UClass* VarSourceClass = VariableProperty->GetOwner<UClass>())
|
|
{
|
|
// If the variable's source class is the same as the current blueprint's class then it was created in this blueprint and it's category can be changed.
|
|
return VarSourceClass == GetBlueprintObj()->SkeletonGeneratedClass;
|
|
}
|
|
else if(IsALocalVariable(VariableProperty))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
FEdGraphPinType FBlueprintVarActionDetails::OnGetVarType() const
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty)
|
|
{
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
FEdGraphPinType Type;
|
|
K2Schema->ConvertPropertyToPinType(VariableProperty, Type);
|
|
return Type;
|
|
}
|
|
return FEdGraphPinType();
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnVarTypeChanged(const FEdGraphPinType& NewPinType)
|
|
{
|
|
if (FBlueprintEditorUtils::IsPinTypeValid(NewPinType))
|
|
{
|
|
FName VarName = CachedVariableName;
|
|
|
|
if (VarName != NAME_None)
|
|
{
|
|
// Set the MyBP tab's last pin type used as this, for adding lots of variables of the same type
|
|
MyBlueprint.Pin()->GetLastPinTypeUsed() = NewPinType;
|
|
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if(VariableProperty)
|
|
{
|
|
if(IsALocalVariable(VariableProperty))
|
|
{
|
|
FBlueprintEditorUtils::ChangeLocalVariableType(GetBlueprintObj(), GetLocalVariableScope(VariableProperty), VarName, NewPinType);
|
|
}
|
|
else
|
|
{
|
|
FBlueprintEditorUtils::ChangeMemberVariableType(GetBlueprintObj(), VarName, NewPinType);
|
|
}
|
|
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditor = MyBlueprint.Pin()->GetBlueprintEditor().Pin();
|
|
|
|
// Auto-import the underlying type object's default namespace set into the current editor context.
|
|
const UObject* PinSubCategoryObject = NewPinType.PinSubCategoryObject.Get();
|
|
if (PinSubCategoryObject && BlueprintEditor.IsValid())
|
|
{
|
|
FBlueprintEditor::FImportNamespaceExParameters Params;
|
|
FBlueprintNamespaceUtilities::GetDefaultImportsForObject(PinSubCategoryObject, Params.NamespacesToImport);
|
|
BlueprintEditor->ImportNamespaceEx(Params);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FText FBlueprintVarActionDetails::OnGetTooltipText() const
|
|
{
|
|
FName VarName = CachedVariableName;
|
|
if (VarName != NAME_None)
|
|
{
|
|
if ( UBlueprint* OwnerBlueprint = GetPropertyOwnerBlueprint() )
|
|
{
|
|
FString Result;
|
|
FBlueprintEditorUtils::GetBlueprintVariableMetaData(GetPropertyOwnerBlueprint(), VarName, GetLocalVariableScope(CachedVariableProperty.Get()), TEXT("tooltip"), Result);
|
|
return FText::FromString(Result);
|
|
}
|
|
}
|
|
return FText();
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnTooltipTextCommitted(const FText& NewText, ETextCommit::Type InTextCommit, FName VarName)
|
|
{
|
|
FBlueprintEditorUtils::SetBlueprintVariableMetaData(GetBlueprintObj(), VarName, GetLocalVariableScope(CachedVariableProperty.Get()), TEXT("tooltip"), NewText.ToString() );
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::PopulateCategories(SMyBlueprint* MyBlueprint, TArray<TSharedPtr<FText>>& CategorySource)
|
|
{
|
|
auto IsNewCategorySource = [&CategorySource](const FText& NewCategory)
|
|
{
|
|
return !CategorySource.ContainsByPredicate([&NewCategory](const TSharedPtr<FText>& ExistingCategory)
|
|
{
|
|
return ExistingCategory->ToString().Equals(NewCategory.ToString(), ESearchCase::CaseSensitive);
|
|
});
|
|
};
|
|
|
|
bool bShowUserVarsOnly = MyBlueprint->ShowUserVarsOnly();
|
|
UBlueprint* Blueprint = MyBlueprint->GetBlueprintObj();
|
|
check(Blueprint != NULL);
|
|
if (Blueprint->SkeletonGeneratedClass == NULL)
|
|
{
|
|
UE_LOG(LogBlueprint, Error, TEXT("Blueprint %s has NULL SkeletonGeneratedClass in FBlueprintVarActionDetails::PopulateCategories(). Cannot Populate Categories."), *GetNameSafe(Blueprint));
|
|
return;
|
|
}
|
|
|
|
check(Blueprint->SkeletonGeneratedClass != NULL);
|
|
EFieldIteratorFlags::SuperClassFlags SuperClassFlag = EFieldIteratorFlags::ExcludeSuper;
|
|
if(!bShowUserVarsOnly)
|
|
{
|
|
SuperClassFlag = EFieldIteratorFlags::IncludeSuper;
|
|
}
|
|
|
|
TArray<FName> VisibleVariables;
|
|
for (TFieldIterator<FProperty> PropertyIt(Blueprint->SkeletonGeneratedClass, SuperClassFlag); PropertyIt; ++PropertyIt)
|
|
{
|
|
FProperty* Property = *PropertyIt;
|
|
|
|
if ((!Property->HasAnyPropertyFlags(CPF_Parm) && Property->HasAllPropertyFlags(CPF_BlueprintVisible)))
|
|
{
|
|
VisibleVariables.Add(Property->GetFName());
|
|
}
|
|
}
|
|
|
|
CategorySource.Reset();
|
|
CategorySource.Add(MakeShared<FText>(UEdGraphSchema_K2::VR_DefaultCategory));
|
|
for (const FAdditionalBlueprintCategory& AdditionalBlueprintCategory : GetDefault<UBlueprintEditorSettings>()->AdditionalBlueprintCategories)
|
|
{
|
|
if (!AdditionalBlueprintCategory.Name.IsEmpty() && (AdditionalBlueprintCategory.ClassFilter.IsNull() ||
|
|
(Blueprint->ParentClass && Blueprint->ParentClass->IsChildOf(AdditionalBlueprintCategory.ClassFilter.TryLoadClass<UObject>()))))
|
|
{
|
|
CategorySource.Add(MakeShared<FText>(AdditionalBlueprintCategory.Name));
|
|
}
|
|
}
|
|
for (const FName& VariableName : VisibleVariables)
|
|
{
|
|
FText Category = FBlueprintEditorUtils::GetBlueprintVariableCategory(Blueprint, VariableName, nullptr);
|
|
if (!Category.IsEmpty() && !Category.EqualTo(FText::FromString(Blueprint->GetName())))
|
|
{
|
|
if (IsNewCategorySource(Category))
|
|
{
|
|
CategorySource.Add(MakeShared<FText>(Category));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Search through all function graphs for entry nodes to search for local variables to pull their categories
|
|
for (UEdGraph* FunctionGraph : Blueprint->FunctionGraphs)
|
|
{
|
|
if(UFunction* Function = Blueprint->SkeletonGeneratedClass->FindFunctionByName(FunctionGraph->GetFName()))
|
|
{
|
|
FText FunctionCategory = FObjectEditorUtils::GetCategoryText(Function);
|
|
|
|
if(!FunctionCategory.IsEmpty())
|
|
{
|
|
if (IsNewCategorySource(FunctionCategory))
|
|
{
|
|
CategorySource.Add(MakeShared<FText>(FunctionCategory));
|
|
}
|
|
}
|
|
}
|
|
|
|
UK2Node_EditablePinBase* EntryNode = FBlueprintEditorUtils::GetEntryNode(FunctionGraph);
|
|
if (UK2Node_FunctionEntry* FunctionEntryNode = Cast<UK2Node_FunctionEntry>(EntryNode))
|
|
{
|
|
for (FBPVariableDescription& Variable : FunctionEntryNode->LocalVariables)
|
|
{
|
|
if (IsNewCategorySource(Variable.Category))
|
|
{
|
|
CategorySource.Add(MakeShared<FText>(Variable.Category));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (UEdGraph* MacroGraph : Blueprint->MacroGraphs)
|
|
{
|
|
UK2Node_EditablePinBase* EntryNode = FBlueprintEditorUtils::GetEntryNode(MacroGraph);
|
|
if (UK2Node_Tunnel* TypedEntryNode = ExactCast<UK2Node_Tunnel>(EntryNode))
|
|
{
|
|
if (!TypedEntryNode->MetaData.Category.IsEmpty())
|
|
{
|
|
if (IsNewCategorySource(TypedEntryNode->MetaData.Category))
|
|
{
|
|
CategorySource.Add(MakeShared<FText>(TypedEntryNode->MetaData.Category));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pull categories from overridable functions
|
|
for (TFieldIterator<UFunction> FunctionIt(Blueprint->ParentClass, EFieldIteratorFlags::IncludeSuper); FunctionIt; ++FunctionIt)
|
|
{
|
|
const UFunction* Function = *FunctionIt;
|
|
const FName FunctionName = Function->GetFName();
|
|
|
|
if (UEdGraphSchema_K2::CanKismetOverrideFunction(Function) && !UEdGraphSchema_K2::FunctionCanBePlacedAsEvent(Function))
|
|
{
|
|
FText FunctionCategory = FObjectEditorUtils::GetCategoryText(Function);
|
|
|
|
if (!FunctionCategory.IsEmpty())
|
|
{
|
|
if (IsNewCategorySource(FunctionCategory))
|
|
{
|
|
CategorySource.Add(MakeShared<FText>(FunctionCategory));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort categories, but keep the default category listed first
|
|
CategorySource.Sort([](const TSharedPtr <FText> &LHS, const TSharedPtr <FText> &RHS)
|
|
{
|
|
if (LHS.IsValid() && RHS.IsValid())
|
|
{
|
|
return (LHS->EqualTo(UEdGraphSchema_K2::VR_DefaultCategory) || LHS->CompareToCaseIgnored(*RHS) <= 0);
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
|
|
UK2Node_Variable* FBlueprintVarActionDetails::EdGraphSelectionAsVar() const
|
|
{
|
|
TWeakPtr<FBlueprintEditor> BlueprintEditor = MyBlueprint.Pin()->GetBlueprintEditor();
|
|
|
|
if( BlueprintEditor.IsValid() )
|
|
{
|
|
/** Get the currently selected set of nodes */
|
|
FGraphPanelSelectionSet Objects = BlueprintEditor.Pin()->GetSelectedNodes();
|
|
|
|
if (Objects.Num() == 1)
|
|
{
|
|
FGraphPanelSelectionSet::TIterator Iter(Objects);
|
|
UObject* Object = *Iter;
|
|
|
|
if (Object && Object->IsA<UK2Node_Variable>())
|
|
{
|
|
return Cast<UK2Node_Variable>(Object);
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
FProperty* FBlueprintVarActionDetails::SelectionAsProperty() const
|
|
{
|
|
if (FEdGraphSchemaAction_BlueprintVariableBase* BPVar = MyBlueprint.Pin()->SelectionAsBlueprintVariable())
|
|
{
|
|
return BPVar->GetProperty();
|
|
}
|
|
else if (UK2Node_Variable* GraphVar = EdGraphSelectionAsVar())
|
|
{
|
|
return GraphVar->GetPropertyForVariable();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
FName FBlueprintVarActionDetails::GetVariableName() const
|
|
{
|
|
if (FEdGraphSchemaAction_BlueprintVariableBase* BPVar = MyBlueprint.Pin()->SelectionAsBlueprintVariable())
|
|
{
|
|
return BPVar->GetVariableName();
|
|
}
|
|
else if (UK2Node_Variable* GraphVar = EdGraphSelectionAsVar())
|
|
{
|
|
return GraphVar->GetVarName();
|
|
}
|
|
|
|
return NAME_None;
|
|
}
|
|
|
|
FText FBlueprintVarActionDetails::OnGetCategoryText() const
|
|
{
|
|
FName VarName = CachedVariableName;
|
|
if (VarName != NAME_None)
|
|
{
|
|
if ( UBlueprint* OwnerBlueprint = GetPropertyOwnerBlueprint() )
|
|
{
|
|
FText Category = FBlueprintEditorUtils::GetBlueprintVariableCategory(OwnerBlueprint, VarName, GetLocalVariableScope(CachedVariableProperty.Get()));
|
|
|
|
// Older blueprints will have their name as the default category and whenever it is the same as the default category, display localized text
|
|
if ( Category.EqualTo(FText::FromString(OwnerBlueprint->GetName())) || Category.EqualTo(UEdGraphSchema_K2::VR_DefaultCategory) )
|
|
{
|
|
return UEdGraphSchema_K2::VR_DefaultCategory;
|
|
}
|
|
else
|
|
{
|
|
return Category;
|
|
}
|
|
}
|
|
|
|
return FText::FromName(VarName);
|
|
}
|
|
return FText();
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnCategoryTextCommitted(const FText& NewText, ETextCommit::Type InTextCommit, FName VarName)
|
|
{
|
|
if (InTextCommit == ETextCommit::OnEnter || InTextCommit == ETextCommit::OnUserMovedFocus)
|
|
{
|
|
FBlueprintEditorUtils::SetBlueprintVariableCategory(GetBlueprintObj(), VarName, GetLocalVariableScope(CachedVariableProperty.Get()), NewText);
|
|
check(MyBlueprint.IsValid());
|
|
PopulateCategories(MyBlueprint.Pin().Get(), CategorySource);
|
|
MyBlueprint.Pin()->ExpandCategory(NewText);
|
|
}
|
|
}
|
|
|
|
TSharedRef< ITableRow > FBlueprintVarActionDetails::MakeCategoryViewWidget( TSharedPtr<FText> Item, const TSharedRef< STableViewBase >& OwnerTable )
|
|
{
|
|
return SNew(STableRow<TSharedPtr<FString>>, OwnerTable)
|
|
[
|
|
SNew(STextBlock) .Text(*Item.Get())
|
|
];
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnCategorySelectionChanged( TSharedPtr<FText> ProposedSelection, ESelectInfo::Type /*SelectInfo*/ )
|
|
{
|
|
FName VarName = CachedVariableName;
|
|
if (ProposedSelection.IsValid() && VarName != NAME_None)
|
|
{
|
|
FText NewCategory = *ProposedSelection.Get();
|
|
|
|
FBlueprintEditorUtils::SetBlueprintVariableCategory(GetBlueprintObj(), VarName, GetLocalVariableScope(CachedVariableProperty.Get()), NewCategory );
|
|
CategoryListView.Pin()->ClearSelection();
|
|
CategoryComboButton.Pin()->SetIsOpen(false);
|
|
MyBlueprint.Pin()->ExpandCategory(NewCategory);
|
|
}
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::ShowEditableCheckboxVisibility() const
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty && GetPropertyOwnerBlueprint())
|
|
{
|
|
if (IsABlueprintVariable(VariableProperty) && IsAUserVariable(VariableProperty))
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
ECheckBoxState FBlueprintVarActionDetails::OnEditableCheckboxState() const
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty)
|
|
{
|
|
return VariableProperty->HasAnyPropertyFlags(CPF_DisableEditOnInstance) ? ECheckBoxState::Unchecked : ECheckBoxState::Checked;
|
|
}
|
|
return ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnEditableChanged(ECheckBoxState InNewState)
|
|
{
|
|
FName VarName = CachedVariableName;
|
|
|
|
// Toggle the flag on the blueprint's version of the variable description, based on state
|
|
const bool bVariableIsExposed = InNewState == ECheckBoxState::Checked;
|
|
|
|
UBlueprint* BlueprintObj = MyBlueprint.Pin()->GetBlueprintObj();
|
|
FBlueprintEditorUtils::SetBlueprintOnlyEditableFlag(BlueprintObj, VarName, !bVariableIsExposed);
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::ShowReadOnlyCheckboxVisibility() const
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty && GetPropertyOwnerBlueprint())
|
|
{
|
|
if (IsABlueprintVariable(VariableProperty) && IsAUserVariable(VariableProperty))
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
ECheckBoxState FBlueprintVarActionDetails::OnReadyOnlyCheckboxState() const
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty)
|
|
{
|
|
return VariableProperty->HasAnyPropertyFlags(CPF_BlueprintReadOnly) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
return ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnReadyOnlyChanged(ECheckBoxState InNewState)
|
|
{
|
|
FName VarName = CachedVariableName;
|
|
|
|
// Toggle the flag on the blueprint's version of the variable description, based on state
|
|
const bool bVariableIsReadOnly = InNewState == ECheckBoxState::Checked;
|
|
|
|
UBlueprint* BlueprintObj = MyBlueprint.Pin()->GetBlueprintObj();
|
|
FBlueprintEditorUtils::SetBlueprintPropertyReadOnlyFlag(BlueprintObj, VarName, bVariableIsReadOnly);
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::GetVariableUnitsVisibility() const
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty)
|
|
{
|
|
const bool bIsInteger = VariableProperty->IsA(FIntProperty::StaticClass()) || VariableProperty->IsA(FInt64Property::StaticClass());
|
|
const bool bIsReal = VariableProperty->IsA(FFloatProperty::StaticClass()) || VariableProperty->IsA(FDoubleProperty::StaticClass());
|
|
|
|
if (IsABlueprintVariable(VariableProperty) && !IsALocalVariable(VariableProperty) && (bIsInteger || bIsReal))
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
}
|
|
return EVisibility::Hidden;
|
|
}
|
|
|
|
TSharedPtr<FString> FBlueprintVarActionDetails::GetVariableUnits() const
|
|
{
|
|
if (CachedVariableName != NAME_None)
|
|
{
|
|
if (const UBlueprint* BlueprintObj = GetPropertyOwnerBlueprint() )
|
|
{
|
|
FString Result;
|
|
if (FBlueprintEditorUtils::GetBlueprintVariableMetaData(BlueprintObj, CachedVariableName, GetLocalVariableScope(CachedVariableProperty.Get()), "ForceUnits", /*out*/ Result))
|
|
{
|
|
for (const TSharedPtr<FString>& UnitOption : UnitsOptions)
|
|
{
|
|
if (*UnitOption == Result)
|
|
{
|
|
return UnitOption;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Return none;
|
|
return UnitsOptions.IsEmpty() ? MakeShareable(new FString("None")) : UnitsOptions[0];
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnVariableUnitsChanged(TSharedPtr<FString> UnitsSelected, ESelectInfo::Type SelectInfo)
|
|
{
|
|
if (CachedVariableName != NAME_None)
|
|
{
|
|
if ( UBlueprint* BlueprintObj = GetPropertyOwnerBlueprint() )
|
|
{
|
|
if (UnitsSelected && !UnitsOptions.IsEmpty() && UnitsSelected != UnitsOptions[0] )
|
|
{
|
|
FBlueprintEditorUtils::SetBlueprintVariableMetaData(BlueprintObj, CachedVariableName, GetLocalVariableScope(CachedVariableProperty.Get()), "ForceUnits", *UnitsSelected);
|
|
}
|
|
else
|
|
{
|
|
FBlueprintEditorUtils::RemoveBlueprintVariableMetaData(BlueprintObj, CachedVariableName, GetLocalVariableScope(CachedVariableProperty.Get()), "ForceUnits");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ECheckBoxState FBlueprintVarActionDetails::OnFieldNotifyCheckboxState() const
|
|
{
|
|
UBlueprint* const BlueprintObj = GetPropertyOwnerBlueprint();
|
|
const FName VarName = CachedVariableName;
|
|
|
|
if (!VarName.IsNone())
|
|
{
|
|
if (BlueprintObj)
|
|
{
|
|
const int32 VarIndex = FBlueprintEditorUtils::FindNewVariableIndex(BlueprintObj, VarName);
|
|
if (VarIndex != INDEX_NONE)
|
|
{
|
|
return BlueprintObj->NewVariables[VarIndex].HasMetaData(FBlueprintMetadata::MD_FieldNotify) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
}
|
|
|
|
const UClass* VarSourceClass = BlueprintObj ? BlueprintObj->GeneratedClass : nullptr;
|
|
if (VarSourceClass == nullptr && CachedVariableProperty.IsValid())
|
|
{
|
|
VarSourceClass = CachedVariableProperty->GetOwner<UClass>();
|
|
}
|
|
|
|
if (VarSourceClass && VarSourceClass->ImplementsInterface(UNotifyFieldValueChanged::StaticClass()) && VarSourceClass->GetDefaultObject())
|
|
{
|
|
TScriptInterface<INotifyFieldValueChanged> DefaultObject = VarSourceClass->GetDefaultObject();
|
|
return DefaultObject->GetFieldNotificationDescriptor().GetField(VarSourceClass, VarName).IsValid() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
}
|
|
|
|
return ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnFieldNotifyChanged(ECheckBoxState InNewState)
|
|
{
|
|
FName VarName = CachedVariableName;
|
|
|
|
// Toggle the flag on the blueprint's version of the variable description, based on state
|
|
const bool bVariableIsFieldNotify = InNewState == ECheckBoxState::Checked;
|
|
|
|
UBlueprint* BlueprintObj = MyBlueprint.Pin()->GetBlueprintObj();
|
|
if (bVariableIsFieldNotify)
|
|
{
|
|
// todo look through graph and check if the variable is used in a FieldNotify function, add that info to the metadata
|
|
//maybe do that in PreCompileFunction
|
|
FBlueprintEditorUtils::SetBlueprintVariableMetaData(BlueprintObj, VarName, GetLocalVariableScope(CachedVariableProperty.Get()), FBlueprintMetadata::MD_FieldNotify, TEXT(""));
|
|
}
|
|
else
|
|
{
|
|
FBlueprintEditorUtils::RemoveFieldNotifyFromAllMetadata(BlueprintObj, VarName);
|
|
FBlueprintEditorUtils::RemoveBlueprintVariableMetaData(GetBlueprintObj(), VarName, GetLocalVariableScope(CachedVariableProperty.Get()), FBlueprintMetadata::MD_FieldNotify);
|
|
}
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::GetFieldNotifyCheckboxListVisibility() const
|
|
{
|
|
UBlueprint* const BlueprintObj = GetBlueprintObj();
|
|
const FName VarName = CachedVariableName;
|
|
|
|
if (BlueprintObj && !VarName.IsNone())
|
|
{
|
|
// Only show the list of checkboxes in the details panel when this variable is field notify.
|
|
const int32 VarIndex = FBlueprintEditorUtils::FindNewVariableIndex(BlueprintObj, VarName);
|
|
if (VarIndex != INDEX_NONE)
|
|
{
|
|
return BlueprintObj->NewVariables[VarIndex].HasMetaData(FBlueprintMetadata::MD_FieldNotify) ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
ECheckBoxState FBlueprintVarActionDetails::OnCreateWidgetCheckboxState() const
|
|
{
|
|
FProperty* Property = CachedVariableProperty.Get();
|
|
if (Property)
|
|
{
|
|
bool bMakingWidget = FEdMode::ShouldCreateWidgetForProperty(Property);
|
|
|
|
return bMakingWidget ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
return ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnCreateWidgetChanged(ECheckBoxState InNewState)
|
|
{
|
|
const FName VarName = CachedVariableName;
|
|
if (VarName != NAME_None)
|
|
{
|
|
if (InNewState == ECheckBoxState::Checked)
|
|
{
|
|
FBlueprintEditorUtils::SetBlueprintVariableMetaData(GetBlueprintObj(), VarName, GetLocalVariableScope(CachedVariableProperty.Get()), FEdMode::MD_MakeEditWidget, TEXT("true"));
|
|
}
|
|
else
|
|
{
|
|
FBlueprintEditorUtils::RemoveBlueprintVariableMetaData(GetBlueprintObj(), VarName, GetLocalVariableScope(CachedVariableProperty.Get()), FEdMode::MD_MakeEditWidget);
|
|
}
|
|
}
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::Show3DWidgetVisibility() const
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty && GetPropertyOwnerBlueprint())
|
|
{
|
|
if (IsABlueprintVariable(VariableProperty) && FEdMode::CanCreateWidgetForProperty(VariableProperty))
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
bool FBlueprintVarActionDetails::Is3DWidgetEnabled()
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty)
|
|
{
|
|
return ( VariableProperty && !VariableProperty->HasAnyPropertyFlags(CPF_DisableEditOnInstance) ) ;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ECheckBoxState FBlueprintVarActionDetails::OnGetExposedToSpawnCheckboxState() const
|
|
{
|
|
FProperty* Property = CachedVariableProperty.Get();
|
|
if (Property)
|
|
{
|
|
return (Property && Property->GetBoolMetaData(FBlueprintMetadata::MD_ExposeOnSpawn) != false) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
return ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnExposedToSpawnChanged(ECheckBoxState InNewState)
|
|
{
|
|
const FName VarName = CachedVariableName;
|
|
if (VarName != NAME_None)
|
|
{
|
|
const bool bExposeOnSpawn = (InNewState == ECheckBoxState::Checked);
|
|
if(bExposeOnSpawn)
|
|
{
|
|
FBlueprintEditorUtils::SetBlueprintVariableMetaData(GetBlueprintObj(), VarName, NULL, FBlueprintMetadata::MD_ExposeOnSpawn, TEXT("true"));
|
|
}
|
|
else
|
|
{
|
|
FBlueprintEditorUtils::RemoveBlueprintVariableMetaData(GetBlueprintObj(), VarName, NULL, FBlueprintMetadata::MD_ExposeOnSpawn);
|
|
}
|
|
}
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::ExposeOnSpawnVisibility() const
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty && GetPropertyOwnerBlueprint())
|
|
{
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
FEdGraphPinType VariablePinType;
|
|
K2Schema->ConvertPropertyToPinType(VariableProperty, VariablePinType);
|
|
|
|
const bool bShowPrivacySetting = IsABlueprintVariable(VariableProperty) && IsAUserVariable(VariableProperty);
|
|
if (bShowPrivacySetting && (K2Schema->FindSetVariableByNameFunction(VariablePinType) != NULL))
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
ECheckBoxState FBlueprintVarActionDetails::OnGetPrivateCheckboxState() const
|
|
{
|
|
FProperty* Property = CachedVariableProperty.Get();
|
|
if (Property)
|
|
{
|
|
return (Property && Property->GetBoolMetaData(FBlueprintMetadata::MD_Private) != false) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
return ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnPrivateChanged(ECheckBoxState InNewState)
|
|
{
|
|
const FName VarName = CachedVariableName;
|
|
if (VarName != NAME_None)
|
|
{
|
|
const bool bExposeOnSpawn = (InNewState == ECheckBoxState::Checked);
|
|
if(bExposeOnSpawn)
|
|
{
|
|
FBlueprintEditorUtils::SetBlueprintVariableMetaData(GetBlueprintObj(), VarName, NULL, FBlueprintMetadata::MD_Private, TEXT("true"));
|
|
}
|
|
else
|
|
{
|
|
FBlueprintEditorUtils::RemoveBlueprintVariableMetaData(GetBlueprintObj(), VarName, NULL, FBlueprintMetadata::MD_Private);
|
|
}
|
|
}
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::ExposePrivateVisibility() const
|
|
{
|
|
FProperty* Property = CachedVariableProperty.Get();
|
|
if (Property && GetPropertyOwnerBlueprint())
|
|
{
|
|
if (IsABlueprintVariable(Property) && IsAUserVariable(Property))
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
ECheckBoxState FBlueprintVarActionDetails::OnGetExposedToCinematicsCheckboxState() const
|
|
{
|
|
FProperty* Property = CachedVariableProperty.Get();
|
|
if (Property)
|
|
{
|
|
return Property && Property->HasAnyPropertyFlags(CPF_Interp) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
return ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnExposedToCinematicsChanged(ECheckBoxState InNewState)
|
|
{
|
|
// Toggle the flag on the blueprint's version of the variable description, based on state
|
|
const bool bExposeToCinematics = (InNewState == ECheckBoxState::Checked);
|
|
|
|
const FName VarName = CachedVariableName;
|
|
if (VarName != NAME_None)
|
|
{
|
|
FBlueprintEditorUtils::SetInterpFlag(GetBlueprintObj(), VarName, bExposeToCinematics);
|
|
}
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::ExposeToCinematicsVisibility() const
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty && !IsALocalVariable(VariableProperty))
|
|
{
|
|
const bool bIsInteger = VariableProperty->IsA(FIntProperty::StaticClass());
|
|
const bool bIsByte = VariableProperty->IsA(FByteProperty::StaticClass());
|
|
const bool bIsEnum = VariableProperty->IsA(FEnumProperty::StaticClass());
|
|
const bool bIsFloat = VariableProperty->IsA(FFloatProperty::StaticClass());
|
|
const bool bIsBool = VariableProperty->IsA(FBoolProperty::StaticClass());
|
|
const bool bIsStr = VariableProperty->IsA(FStrProperty::StaticClass());
|
|
|
|
const FStructProperty* AsStructProperty = CastField<FStructProperty>(VariableProperty);
|
|
const bool bIsVectorStruct = AsStructProperty != nullptr && AsStructProperty->Struct->GetFName() == NAME_Vector;
|
|
const bool bIsTransformStruct = AsStructProperty != nullptr && AsStructProperty->Struct->GetFName() == NAME_Transform;
|
|
const bool bIsColorStruct = AsStructProperty != nullptr && AsStructProperty->Struct->GetFName() == NAME_Color;
|
|
const bool bIsLinearColorStruct = AsStructProperty != nullptr && AsStructProperty->Struct->GetFName() == NAME_LinearColor;
|
|
|
|
const FObjectProperty* AsObjectProperty = CastField<FObjectProperty>(VariableProperty);
|
|
const bool bIsActorProperty = AsObjectProperty != nullptr && AsObjectProperty->PropertyClass && AsObjectProperty->PropertyClass->IsChildOf(AActor::StaticClass());
|
|
|
|
if (bIsInteger || bIsByte || bIsEnum || bIsFloat || bIsBool || bIsStr || bIsVectorStruct || bIsTransformStruct || bIsColorStruct || bIsLinearColorStruct || bIsActorProperty)
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
else
|
|
{
|
|
ISequencerModule* SequencerModule = FModuleManager::Get().GetModulePtr<ISequencerModule>("Sequencer");
|
|
if (SequencerModule->CanAnimateProperty(VariableProperty))
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
TSharedPtr<FString> FBlueprintVarActionDetails::GetVariableReplicationCondition() const
|
|
{
|
|
ELifetimeCondition VariableRepCondition = COND_None;
|
|
|
|
const FProperty* const Property = CachedVariableProperty.Get();
|
|
|
|
if (Property)
|
|
{
|
|
VariableRepCondition = Property->GetBlueprintReplicationCondition();
|
|
}
|
|
|
|
return ReplicationConditionEnumTypeNames[(uint8)VariableRepCondition];
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnChangeReplicationCondition(TSharedPtr<FString> ItemSelected, ESelectInfo::Type SelectInfo)
|
|
{
|
|
int32 NewSelection;
|
|
const bool bFound = ReplicationConditionEnumTypeNames.Find(ItemSelected, NewSelection);
|
|
check(bFound && NewSelection != INDEX_NONE);
|
|
|
|
const ELifetimeCondition NewRepCondition = (ELifetimeCondition)NewSelection;
|
|
|
|
UBlueprint* const BlueprintObj = GetBlueprintObj();
|
|
const FName VarName = CachedVariableName;
|
|
|
|
if (BlueprintObj && VarName != NAME_None)
|
|
{
|
|
const int32 VarIndex = FBlueprintEditorUtils::FindNewVariableIndex(BlueprintObj, VarName);
|
|
|
|
if (VarIndex != INDEX_NONE)
|
|
{
|
|
BlueprintObj->NewVariables[VarIndex].ReplicationCondition = NewRepCondition;
|
|
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(BlueprintObj);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
bool FBlueprintVarActionDetails::ReplicationConditionEnabled() const
|
|
{
|
|
const FProperty* const VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty)
|
|
{
|
|
const uint64 *PropFlagPtr = FBlueprintEditorUtils::GetBlueprintVariablePropertyFlags(GetBlueprintObj(), VariableProperty->GetFName());
|
|
uint64 PropFlags = 0;
|
|
|
|
if (PropFlagPtr != nullptr)
|
|
{
|
|
PropFlags = *PropFlagPtr;
|
|
return (PropFlags & CPF_Net) > 0;
|
|
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FBlueprintVarActionDetails::ReplicationEnabled() const
|
|
{
|
|
// Update FBlueprintVarActionDetails::ReplicationTooltip if you alter this function
|
|
// so that users can understand why replication settings are disabled!
|
|
bool bVariableCanBeReplicated = true;
|
|
const FProperty* const VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty)
|
|
{
|
|
// sets and maps cannot yet be replicated, neither can Event Dispatchers:
|
|
bVariableCanBeReplicated =
|
|
CastField<FSetProperty>(VariableProperty) == nullptr &&
|
|
CastField<FMapProperty>(VariableProperty) == nullptr &&
|
|
CastField<FMulticastInlineDelegateProperty>(VariableProperty) == nullptr;
|
|
}
|
|
return bVariableCanBeReplicated && IsVariableInBlueprint();
|
|
}
|
|
|
|
FText FBlueprintVarActionDetails::ReplicationTooltip() const
|
|
{
|
|
if (ReplicationEnabled())
|
|
{
|
|
return LOCTEXT("VariableReplicate_Tooltip", "Should this Variable be replicated over the network?");
|
|
}
|
|
else
|
|
{
|
|
const FProperty* const VariableProperty = CachedVariableProperty.Get();
|
|
if (CastField<FSetProperty>(VariableProperty) || CastField<FMapProperty>(VariableProperty))
|
|
{
|
|
return LOCTEXT("VariableReplicateDisabledSetsAndMaps_Tooltip", "Set and Map properties cannot be replicated");
|
|
}
|
|
else if (CastField<FMulticastInlineDelegateProperty>(VariableProperty))
|
|
{
|
|
return LOCTEXT("VariableReplicateDisabledEventDispatchers_Tooltip", "Event Dispatcher properties cannot be replicated");
|
|
}
|
|
else
|
|
{
|
|
return LOCTEXT("VariableReplicateDisabledDefault_Tooltip", "This property type cannot be replicated");
|
|
}
|
|
}
|
|
}
|
|
|
|
ECheckBoxState FBlueprintVarActionDetails::OnGetConfigVariableCheckboxState() const
|
|
{
|
|
UBlueprint* BlueprintObj = GetPropertyOwnerBlueprint();
|
|
const FName VarName = CachedVariableName;
|
|
ECheckBoxState CheckboxValue = ECheckBoxState::Unchecked;
|
|
|
|
if( BlueprintObj && VarName != NAME_None )
|
|
{
|
|
const int32 VarIndex = FBlueprintEditorUtils::FindNewVariableIndex( BlueprintObj, VarName );
|
|
|
|
if( VarIndex != INDEX_NONE && BlueprintObj->NewVariables[ VarIndex ].PropertyFlags & CPF_Config )
|
|
{
|
|
CheckboxValue = ECheckBoxState::Checked;
|
|
}
|
|
}
|
|
return CheckboxValue;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnSetConfigVariableState( ECheckBoxState InNewState )
|
|
{
|
|
UBlueprint* BlueprintObj = GetBlueprintObj();
|
|
const FName VarName = CachedVariableName;
|
|
|
|
if( BlueprintObj && VarName != NAME_None )
|
|
{
|
|
const int32 VarIndex = FBlueprintEditorUtils::FindNewVariableIndex( BlueprintObj, VarName );
|
|
|
|
if( VarIndex != INDEX_NONE )
|
|
{
|
|
if( InNewState == ECheckBoxState::Checked )
|
|
{
|
|
BlueprintObj->NewVariables[ VarIndex ].PropertyFlags |= CPF_Config;
|
|
}
|
|
else
|
|
{
|
|
BlueprintObj->NewVariables[ VarIndex ].PropertyFlags &= ~CPF_Config;
|
|
}
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified( BlueprintObj );
|
|
}
|
|
}
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::ExposeConfigVisibility() const
|
|
{
|
|
FProperty* Property = CachedVariableProperty.Get();
|
|
if (Property)
|
|
{
|
|
if (IsABlueprintVariable(Property) && IsAUserVariable(Property))
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
bool FBlueprintVarActionDetails::IsConfigCheckBoxEnabled() const
|
|
{
|
|
bool bEnabled = IsVariableInBlueprint();
|
|
if (bEnabled && CachedVariableProperty.IsValid())
|
|
{
|
|
if (FProperty* VariableProperty = CachedVariableProperty.Get())
|
|
{
|
|
// meant to match up with UHT's FPropertyBase::IsObject(), which it uses to block object properties from being marked with CPF_Config
|
|
bEnabled = VariableProperty->IsA<FClassProperty>() || VariableProperty->IsA<FSoftClassProperty>() || VariableProperty->IsA<FSoftObjectProperty>() ||
|
|
(!VariableProperty->IsA<FObjectPropertyBase>() && !VariableProperty->IsA<FInterfaceProperty>());
|
|
}
|
|
}
|
|
return bEnabled;
|
|
}
|
|
|
|
FText FBlueprintVarActionDetails::OnGetMetaKeyValue(FName Key) const
|
|
{
|
|
FName VarName = CachedVariableName;
|
|
if (VarName != NAME_None)
|
|
{
|
|
if ( UBlueprint* BlueprintObj = GetPropertyOwnerBlueprint() )
|
|
{
|
|
FString Result;
|
|
FBlueprintEditorUtils::GetBlueprintVariableMetaData(BlueprintObj, VarName, GetLocalVariableScope(CachedVariableProperty.Get()), Key, /*out*/ Result);
|
|
|
|
return FText::FromString(Result);
|
|
}
|
|
}
|
|
return FText();
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnMetaKeyValueChanged(const FText& NewMinValue, ETextCommit::Type CommitInfo, FName Key)
|
|
{
|
|
FName VarName = CachedVariableName;
|
|
if (VarName != NAME_None)
|
|
{
|
|
if ((CommitInfo == ETextCommit::OnEnter) || (CommitInfo == ETextCommit::OnUserMovedFocus))
|
|
{
|
|
FBlueprintEditorUtils::SetBlueprintVariableMetaData(GetBlueprintObj(), VarName, GetLocalVariableScope(CachedVariableProperty.Get()), Key, NewMinValue.ToString());
|
|
}
|
|
}
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::RangeVisibility() const
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty)
|
|
{
|
|
const bool bIsInteger = VariableProperty->IsA(FIntProperty::StaticClass());
|
|
const bool bIsNonEnumByte = (VariableProperty->IsA(FByteProperty::StaticClass()) && CastField<const FByteProperty>(VariableProperty)->Enum == nullptr);
|
|
const bool bIsReal = (VariableProperty->IsA(FFloatProperty::StaticClass()) || VariableProperty->IsA(FDoubleProperty::StaticClass()));
|
|
|
|
// If this is a struct property than we must check the name of the struct it points to, so we can check
|
|
// if it supports the editing of the UIMin/UIMax metadata
|
|
const FStructProperty* StructProp = CastField<FStructProperty>(VariableProperty);
|
|
const UStruct* InnerStruct = StructProp ? StructProp->Struct : nullptr;
|
|
const bool bIsSupportedStruct = InnerStruct ? RangeVisibilityUtils::StructsSupportingRangeVisibility.Contains(InnerStruct->GetFName()) : false;
|
|
|
|
if (IsABlueprintVariable(VariableProperty) && (bIsInteger || bIsNonEnumByte || bIsReal || bIsSupportedStruct))
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::BitmaskVisibility() const
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty && IsABlueprintVariable(VariableProperty) && VariableProperty->IsA(FIntProperty::StaticClass()))
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
ECheckBoxState FBlueprintVarActionDetails::OnBitmaskCheckboxState() const
|
|
{
|
|
FProperty* Property = CachedVariableProperty.Get();
|
|
if (Property)
|
|
{
|
|
return (Property && Property->HasMetaData(FBlueprintMetadata::MD_Bitmask)) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
return ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnBitmaskChanged(ECheckBoxState InNewState)
|
|
{
|
|
const FName VarName = CachedVariableName;
|
|
if (VarName != NAME_None)
|
|
{
|
|
UBlueprint* LocalBlueprint = GetBlueprintObj();
|
|
|
|
const bool bIsBitmask = (InNewState == ECheckBoxState::Checked);
|
|
if (bIsBitmask)
|
|
{
|
|
FBlueprintEditorUtils::SetBlueprintVariableMetaData(LocalBlueprint, VarName, nullptr, FBlueprintMetadata::MD_Bitmask, TEXT(""));
|
|
}
|
|
else
|
|
{
|
|
FBlueprintEditorUtils::RemoveBlueprintVariableMetaData(LocalBlueprint, VarName, nullptr, FBlueprintMetadata::MD_Bitmask);
|
|
}
|
|
|
|
// Reset default value
|
|
if (LocalBlueprint->GeneratedClass)
|
|
{
|
|
UObject* CDO = LocalBlueprint->GeneratedClass->GetDefaultObject(false);
|
|
FProperty* VarProperty = FindFProperty<FProperty>(LocalBlueprint->GeneratedClass, VarName);
|
|
|
|
if (CDO != nullptr && VarProperty != nullptr)
|
|
{
|
|
VarProperty->InitializeValue_InContainer(CDO);
|
|
}
|
|
}
|
|
|
|
TArray<UK2Node_Variable*> VariableNodes;
|
|
FBlueprintEditorUtils::GetAllNodesOfClass<UK2Node_Variable>(GetBlueprintObj(), VariableNodes);
|
|
|
|
for (TArray<UK2Node_Variable*>::TConstIterator NodeIt(VariableNodes); NodeIt; ++NodeIt)
|
|
{
|
|
UK2Node_Variable* CurrentNode = *NodeIt;
|
|
if (VarName == CurrentNode->GetVarName())
|
|
{
|
|
CurrentNode->ReconstructNode();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TSharedPtr<FTopLevelAssetPath> FBlueprintVarActionDetails::GetBitmaskEnumTypePath() const
|
|
{
|
|
TSharedPtr<FTopLevelAssetPath> Result;
|
|
const FName VarName = CachedVariableName;
|
|
|
|
if (BitmaskEnumTypePaths.Num() > 0 && VarName != NAME_None)
|
|
{
|
|
Result = BitmaskEnumTypePaths[0];
|
|
|
|
FString OutValue;
|
|
FBlueprintEditorUtils::GetBlueprintVariableMetaData(GetBlueprintObj(), VarName, nullptr, FBlueprintMetadata::MD_BitmaskEnum, OutValue);
|
|
|
|
for (int32 i = 1; i < BitmaskEnumTypePaths.Num(); ++i)
|
|
{
|
|
if (OutValue == BitmaskEnumTypePaths[i]->ToString())
|
|
{
|
|
Result = BitmaskEnumTypePaths[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnBitmaskEnumTypeChanged(TSharedPtr<FTopLevelAssetPath> ItemSelected, ESelectInfo::Type SelectInfo)
|
|
{
|
|
const FName VarName = CachedVariableName;
|
|
if (VarName != NAME_None)
|
|
{
|
|
UBlueprint* LocalBlueprint = GetBlueprintObj();
|
|
|
|
if (ItemSelected == BitmaskEnumTypePaths[0])
|
|
{
|
|
FBlueprintEditorUtils::RemoveBlueprintVariableMetaData(LocalBlueprint, VarName, nullptr, FBlueprintMetadata::MD_BitmaskEnum);
|
|
}
|
|
else if(ItemSelected.IsValid())
|
|
{
|
|
FBlueprintEditorUtils::SetBlueprintVariableMetaData(LocalBlueprint, VarName, nullptr, FBlueprintMetadata::MD_BitmaskEnum, ItemSelected->ToString());
|
|
}
|
|
|
|
// Reset default value
|
|
if (LocalBlueprint->GeneratedClass)
|
|
{
|
|
UObject* CDO = LocalBlueprint->GeneratedClass->GetDefaultObject(false);
|
|
FProperty* VarProperty = FindFProperty<FProperty>(LocalBlueprint->GeneratedClass, VarName);
|
|
|
|
if (CDO != nullptr && VarProperty != nullptr)
|
|
{
|
|
VarProperty->InitializeValue_InContainer(CDO);
|
|
}
|
|
}
|
|
|
|
TArray<UK2Node_Variable*> VariableNodes;
|
|
FBlueprintEditorUtils::GetAllNodesOfClass<UK2Node_Variable>(GetBlueprintObj(), VariableNodes);
|
|
|
|
for (TArray<UK2Node_Variable*>::TConstIterator NodeIt(VariableNodes); NodeIt; ++NodeIt)
|
|
{
|
|
UK2Node_Variable* CurrentNode = *NodeIt;
|
|
if (VarName == CurrentNode->GetVarName())
|
|
{
|
|
CurrentNode->ReconstructNode();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TSharedRef<SWidget> FBlueprintVarActionDetails::GenerateBitmaskEnumTypeWidget(TSharedPtr<FTopLevelAssetPath> Item)
|
|
{
|
|
check(Item.IsValid());
|
|
|
|
return SNew(STextBlock)
|
|
.Text(FText::FromName(Item->GetAssetName()));
|
|
}
|
|
|
|
FText FBlueprintVarActionDetails::GetBitmaskEnumTypeName() const
|
|
{
|
|
const TSharedPtr<FTopLevelAssetPath> BitmaskEnumTypePath = GetBitmaskEnumTypePath();
|
|
return BitmaskEnumTypePath? FText::FromName(BitmaskEnumTypePath->GetAssetName()) : FText();
|
|
}
|
|
|
|
TSharedPtr<FString> FBlueprintVarActionDetails::GetVariableReplicationType() const
|
|
{
|
|
EVariableReplication::Type VariableReplication = EVariableReplication::None;
|
|
|
|
uint64 PropFlags = 0;
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
|
|
if (VariableProperty && (IsVariableInBlueprint() || IsVariableInheritedByBlueprint()))
|
|
{
|
|
UBlueprint* BlueprintObj = GetPropertyOwnerBlueprint();
|
|
if (BlueprintObj != nullptr)
|
|
{
|
|
uint64 *PropFlagPtr = FBlueprintEditorUtils::GetBlueprintVariablePropertyFlags(BlueprintObj, VariableProperty->GetFName());
|
|
|
|
if (PropFlagPtr != NULL)
|
|
{
|
|
PropFlags = *PropFlagPtr;
|
|
bool IsReplicated = (PropFlags & CPF_Net) > 0;
|
|
bool bHasRepNotify = FBlueprintEditorUtils::GetBlueprintVariableRepNotifyFunc(BlueprintObj, VariableProperty->GetFName()) != NAME_None;
|
|
if (bHasRepNotify)
|
|
{
|
|
// Verify they actually have a valid rep notify function still
|
|
UClass* GenClass = GetPropertyOwnerBlueprint()->SkeletonGeneratedClass;
|
|
UFunction* OnRepFunc = GenClass->FindFunctionByName(FBlueprintEditorUtils::GetBlueprintVariableRepNotifyFunc(BlueprintObj, VariableProperty->GetFName()));
|
|
if (OnRepFunc == NULL || OnRepFunc->NumParms != 0 || OnRepFunc->GetReturnProperty() != NULL)
|
|
{
|
|
bHasRepNotify = false;
|
|
ReplicationOnRepFuncChanged(FName(NAME_None).ToString());
|
|
}
|
|
}
|
|
|
|
VariableReplication = !IsReplicated ? EVariableReplication::None :
|
|
bHasRepNotify ? EVariableReplication::RepNotify : EVariableReplication::Replicated;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ReplicationOptions[(int32)VariableReplication];
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnChangeReplication(TSharedPtr<FString> ItemSelected, ESelectInfo::Type SelectInfo)
|
|
{
|
|
int32 NewSelection;
|
|
bool bFound = ReplicationOptions.Find(ItemSelected, NewSelection);
|
|
check(bFound && NewSelection != INDEX_NONE);
|
|
|
|
EVariableReplication::Type VariableReplication = (EVariableReplication::Type)NewSelection;
|
|
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
|
|
UBlueprint* const BlueprintObj = GetBlueprintObj();
|
|
const FName VarName = CachedVariableName;
|
|
int32 VarIndex = INDEX_NONE;
|
|
if (BlueprintObj && VarName != NAME_None)
|
|
{
|
|
VarIndex = FBlueprintEditorUtils::FindNewVariableIndex(BlueprintObj, VarName);
|
|
}
|
|
|
|
if (VariableProperty)
|
|
{
|
|
uint64 *PropFlagPtr = FBlueprintEditorUtils::GetBlueprintVariablePropertyFlags(GetBlueprintObj(), VariableProperty->GetFName());
|
|
if (PropFlagPtr != NULL)
|
|
{
|
|
switch(VariableReplication)
|
|
{
|
|
case EVariableReplication::None:
|
|
*PropFlagPtr &= ~CPF_Net;
|
|
ReplicationOnRepFuncChanged(FName(NAME_None).ToString());
|
|
|
|
//set replication condition to none:
|
|
if (VarIndex != INDEX_NONE)
|
|
{
|
|
BlueprintObj->NewVariables[VarIndex].ReplicationCondition = COND_None;
|
|
}
|
|
|
|
break;
|
|
|
|
case EVariableReplication::Replicated:
|
|
*PropFlagPtr |= CPF_Net;
|
|
ReplicationOnRepFuncChanged(FName(NAME_None).ToString());
|
|
break;
|
|
|
|
case EVariableReplication::RepNotify:
|
|
*PropFlagPtr |= CPF_Net;
|
|
FString NewFuncName = FString::Printf(TEXT("OnRep_%s"), *VariableProperty->GetName());
|
|
UEdGraph* FuncGraph = FindObject<UEdGraph>(BlueprintObj, *NewFuncName);
|
|
if (!FuncGraph)
|
|
{
|
|
FuncGraph = FBlueprintEditorUtils::CreateNewGraph(BlueprintObj, FName(*NewFuncName), UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
|
|
FBlueprintEditorUtils::AddFunctionGraph<UClass>(BlueprintObj, FuncGraph, false, NULL);
|
|
}
|
|
|
|
if (FuncGraph)
|
|
{
|
|
ReplicationOnRepFuncChanged(NewFuncName);
|
|
}
|
|
break;
|
|
}
|
|
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(BlueprintObj);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::ReplicationOnRepFuncChanged(const FString& NewOnRepFunc) const
|
|
{
|
|
FName NewFuncName = FName(*NewOnRepFunc);
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
|
|
if (VariableProperty)
|
|
{
|
|
FBlueprintEditorUtils::SetBlueprintVariableRepNotifyFunc(GetBlueprintObj(), VariableProperty->GetFName(), NewFuncName);
|
|
uint64 *PropFlagPtr = FBlueprintEditorUtils::GetBlueprintVariablePropertyFlags(GetBlueprintObj(), VariableProperty->GetFName());
|
|
if (PropFlagPtr != NULL)
|
|
{
|
|
if (NewFuncName != NAME_None)
|
|
{
|
|
*PropFlagPtr |= CPF_RepNotify;
|
|
*PropFlagPtr |= CPF_Net;
|
|
}
|
|
else
|
|
{
|
|
*PropFlagPtr &= ~CPF_RepNotify;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::ReplicationVisibility() const
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if(VariableProperty)
|
|
{
|
|
if (IsAUserVariable(VariableProperty) && IsABlueprintVariable(VariableProperty))
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
TSharedRef<SWidget> FBlueprintVarActionDetails::BuildEventsMenuForVariable() const
|
|
{
|
|
if(MyBlueprint.IsValid())
|
|
{
|
|
TSharedPtr<SMyBlueprint> MyBlueprintPtr = MyBlueprint.Pin();
|
|
FEdGraphSchemaAction_K2Var* Variable = MyBlueprintPtr->SelectionAsVar();
|
|
FObjectProperty* ComponentProperty = Variable ? CastField<FObjectProperty>(Variable->GetProperty()) : NULL;
|
|
TWeakPtr<FBlueprintEditor> BlueprintEditorPtr = MyBlueprintPtr->GetBlueprintEditor();
|
|
if( BlueprintEditorPtr.IsValid() && ComponentProperty )
|
|
{
|
|
TSharedPtr<SSubobjectBlueprintEditor> Editor = StaticCastSharedPtr<SSubobjectBlueprintEditor>(BlueprintEditorPtr.Pin()->GetSubobjectEditor());
|
|
FMenuBuilder MenuBuilder(true, nullptr);
|
|
Editor->BuildMenuEventsSection( MenuBuilder, BlueprintEditorPtr.Pin()->GetBlueprintObj(), ComponentProperty->PropertyClass,
|
|
FCanExecuteAction::CreateSP(BlueprintEditorPtr.Pin().Get(), &FBlueprintEditor::InEditingMode),
|
|
FGetSelectedObjectsDelegate::CreateSP(MyBlueprintPtr.Get(), &SMyBlueprint::GetSelectedItemsForContextMenu));
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
}
|
|
return SNullWidget::NullWidget;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnPostEditorRefresh()
|
|
{
|
|
CachedVariableProperty = SelectionAsProperty();
|
|
CachedVariableName = GetVariableName();
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::GetTransientVisibility() const
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty)
|
|
{
|
|
if (IsABlueprintVariable(VariableProperty) && IsAUserVariable(VariableProperty))
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
ECheckBoxState FBlueprintVarActionDetails::OnGetTransientCheckboxState() const
|
|
{
|
|
FProperty* Property = CachedVariableProperty.Get();
|
|
if (Property)
|
|
{
|
|
return (Property && Property->HasAnyPropertyFlags(CPF_Transient)) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
return ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnTransientChanged(ECheckBoxState InNewState)
|
|
{
|
|
FProperty* Property = CachedVariableProperty.Get();
|
|
if (Property)
|
|
{
|
|
const bool bTransientFlag = (InNewState == ECheckBoxState::Checked);
|
|
FBlueprintEditorUtils::SetVariableTransientFlag(GetBlueprintObj(), Property->GetFName(), bTransientFlag);
|
|
}
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::GetSaveGameVisibility() const
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty)
|
|
{
|
|
if (IsABlueprintVariable(VariableProperty) && IsAUserVariable(VariableProperty))
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
ECheckBoxState FBlueprintVarActionDetails::OnGetSaveGameCheckboxState() const
|
|
{
|
|
FProperty* Property = CachedVariableProperty.Get();
|
|
if (Property)
|
|
{
|
|
return (Property && Property->HasAnyPropertyFlags(CPF_SaveGame)) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
return ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnSaveGameChanged(ECheckBoxState InNewState)
|
|
{
|
|
FProperty* Property = CachedVariableProperty.Get();
|
|
if (Property)
|
|
{
|
|
const bool bSaveGameFlag = (InNewState == ECheckBoxState::Checked);
|
|
FBlueprintEditorUtils::SetVariableSaveGameFlag(GetBlueprintObj(), Property->GetFName(), bSaveGameFlag);
|
|
}
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::GetAdvancedDisplayVisibility() const
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty)
|
|
{
|
|
if (IsABlueprintVariable(VariableProperty) && IsAUserVariable(VariableProperty))
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
ECheckBoxState FBlueprintVarActionDetails::OnGetAdvancedDisplayCheckboxState() const
|
|
{
|
|
FProperty* Property = CachedVariableProperty.Get();
|
|
if (Property)
|
|
{
|
|
return (Property && Property->HasAnyPropertyFlags(CPF_AdvancedDisplay)) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
return ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnAdvancedDisplayChanged(ECheckBoxState InNewState)
|
|
{
|
|
if (FProperty* Property = CachedVariableProperty.Get())
|
|
{
|
|
const bool bAdvancedFlag = (InNewState == ECheckBoxState::Checked);
|
|
FBlueprintEditorUtils::SetVariableAdvancedDisplayFlag(GetBlueprintObj(), Property->GetFName(), bAdvancedFlag);
|
|
}
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::GetMultilineVisibility() const
|
|
{
|
|
FProperty* VariableProperty = nullptr;
|
|
if (FProperty* RawVariableProperty = CachedVariableProperty.Get())
|
|
{
|
|
if (IsABlueprintVariable(RawVariableProperty))
|
|
{
|
|
if (const FArrayProperty* ArrayProperty = CastField<FArrayProperty>(RawVariableProperty))
|
|
{
|
|
VariableProperty = ArrayProperty->Inner;
|
|
}
|
|
else if (const FSetProperty* SetProperty = CastField<FSetProperty>(RawVariableProperty))
|
|
{
|
|
VariableProperty = SetProperty->ElementProp;
|
|
}
|
|
else if (const FMapProperty* MapProperty = CastField<FMapProperty>(RawVariableProperty))
|
|
{
|
|
VariableProperty = MapProperty->ValueProp;
|
|
}
|
|
else
|
|
{
|
|
VariableProperty = RawVariableProperty;
|
|
}
|
|
}
|
|
}
|
|
|
|
const bool bCanBeMultiline = (VariableProperty != nullptr) && (VariableProperty->IsA(FTextProperty::StaticClass()) || VariableProperty->IsA(FStrProperty::StaticClass()));
|
|
return bCanBeMultiline ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
|
|
ECheckBoxState FBlueprintVarActionDetails::OnGetMultilineCheckboxState() const
|
|
{
|
|
FProperty* Property = CachedVariableProperty.Get();
|
|
if (Property)
|
|
{
|
|
return (Property && Property->GetBoolMetaData(TEXT("MultiLine"))) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
return ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnMultilineChanged(ECheckBoxState InNewState)
|
|
{
|
|
FProperty* Property = CachedVariableProperty.Get();
|
|
if (Property)
|
|
{
|
|
const bool bMultiline = (InNewState == ECheckBoxState::Checked);
|
|
if (bMultiline)
|
|
{
|
|
FBlueprintEditorUtils::SetBlueprintVariableMetaData(GetBlueprintObj(), Property->GetFName(), GetLocalVariableScope(CachedVariableProperty.Get()), TEXT("MultiLine"), TEXT("true"));
|
|
}
|
|
else
|
|
{
|
|
FBlueprintEditorUtils::RemoveBlueprintVariableMetaData(GetBlueprintObj(), Property->GetFName(), GetLocalVariableScope(CachedVariableProperty.Get()), TEXT("MultiLine"));
|
|
}
|
|
}
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::GetDeprecatedVisibility() const
|
|
{
|
|
if (FProperty* VariableProperty = CachedVariableProperty.Get())
|
|
{
|
|
if (IsABlueprintVariable(VariableProperty) && IsAUserVariable(VariableProperty))
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
ECheckBoxState FBlueprintVarActionDetails::OnGetDeprecatedCheckboxState() const
|
|
{
|
|
return IsVariableDeprecated() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnDeprecatedChanged(ECheckBoxState InNewState)
|
|
{
|
|
FProperty* Property = CachedVariableProperty.Get();
|
|
if (Property)
|
|
{
|
|
const bool bDeprecatedFlag = (InNewState == ECheckBoxState::Checked);
|
|
FBlueprintEditorUtils::SetVariableDeprecatedFlag(GetBlueprintObj(), Property->GetFName(), bDeprecatedFlag);
|
|
}
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::GetDropDownOptionsVisibility() const
|
|
{
|
|
if (FProperty* VariableProperty = CachedVariableProperty.Get())
|
|
{
|
|
const bool bMatchingType = VariableProperty->IsA(FNameProperty::StaticClass())
|
|
|| VariableProperty->IsA(FStrProperty::StaticClass());
|
|
|
|
if (bMatchingType && IsABlueprintVariable(VariableProperty) && IsAUserVariable(VariableProperty))
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
}
|
|
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnDropDownOptionSelectionChanged(TSharedPtr<FString> InString,
|
|
ESelectInfo::Type)
|
|
{
|
|
SetDropDownOptionsFunctionName(*InString);
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnDropDownOptionTextChanged(const FText& Text, ETextCommit::Type)
|
|
{
|
|
SetDropDownOptionsFunctionName(Text.ToString());
|
|
}
|
|
|
|
TSharedRef<SWidget> FBlueprintVarActionDetails::GenerateDropDownOptionWidget(TSharedPtr<FString> InItem) const
|
|
{
|
|
return SNew(STextBlock)
|
|
.Text(FText::FromString(*InItem.Get()))
|
|
.Font(IDetailLayoutBuilder::GetDetailFont());
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::CollectDropDownOptions()
|
|
{
|
|
DropDownFunctionOptions.Empty();
|
|
|
|
const FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if (!VariableProperty)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (TFieldIterator<UFunction> It(VariableProperty->GetOwner<UClass>(), EFieldIteratorFlags::IncludeSuper); It; ++It)
|
|
{
|
|
const UFunction* Func = *It;
|
|
|
|
// Is the method valid and not latent?
|
|
if (Func && !Func->HasMetaData(FBlueprintMetadata::MD_Latent))
|
|
{
|
|
bool bSignatureValid = false;
|
|
for (TFieldIterator<FProperty> PropertyId(Func); PropertyId; ++PropertyId)
|
|
{
|
|
FProperty* Property = *PropertyId;
|
|
if (!(Property->PropertyFlags & CPF_Parm))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// If we've already had a valid signature, and found one more param, then it's not valid anymore
|
|
if (bSignatureValid)
|
|
{
|
|
bSignatureValid = false;
|
|
break;
|
|
}
|
|
|
|
// If there is an input parameter, then it's not matching
|
|
if (!(Property->PropertyFlags & (CPF_ReturnParm | CPF_OutParm)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Only accept array outputs
|
|
const FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property);
|
|
if (!ArrayProperty)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Only accept matching signatures
|
|
if (!ArrayProperty->Inner->SameType(VariableProperty))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// It is the first valid function parameter!
|
|
bSignatureValid = true;
|
|
}
|
|
|
|
if (bSignatureValid)
|
|
{
|
|
DropDownFunctionOptions.Add(MakeShared<FString>(Func->GetName()));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort functions by name
|
|
DropDownFunctionOptions.Sort([](const TSharedPtr<FString>& A, const TSharedPtr<FString>& B)
|
|
{
|
|
return A->Compare(*B) < 0;
|
|
});
|
|
|
|
DropDownFunctionOptions.Insert(MakeShared<FString>(BlueprintDocumentationDetailDefs::EmptyDropDownOptionName), 0);
|
|
}
|
|
|
|
FText FBlueprintVarActionDetails::GetDropDownOptionDisplayText() const
|
|
{
|
|
return FText::FromString(GetDropDownOptionsFunctionName());
|
|
}
|
|
|
|
FString FBlueprintVarActionDetails::GetDropDownOptionsFunctionName() const
|
|
{
|
|
const UBlueprint* PropertyBlueprint = GetPropertyOwnerBlueprint();
|
|
if (!PropertyBlueprint)
|
|
{
|
|
return { };
|
|
}
|
|
|
|
FString Value;
|
|
FBlueprintEditorUtils::GetBlueprintVariableMetaData(
|
|
PropertyBlueprint,
|
|
CachedVariableName,
|
|
GetLocalVariableScope(CachedVariableProperty.Get()),
|
|
FBlueprintMetadata::MD_GetOptions,
|
|
Value
|
|
);
|
|
|
|
return Value;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::SetDropDownOptionsFunctionName(const FString& InFunctionName)
|
|
{
|
|
UBlueprint* PropertyBlueprint = GetPropertyOwnerBlueprint();
|
|
if (!PropertyBlueprint)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!GetDropDownOptionsFunctionName().Equals(InFunctionName))
|
|
{
|
|
if (InFunctionName.IsEmpty() || InFunctionName == BlueprintDocumentationDetailDefs::EmptyDropDownOptionName)
|
|
{
|
|
FBlueprintEditorUtils::RemoveBlueprintVariableMetaData(
|
|
PropertyBlueprint,
|
|
CachedVariableName,
|
|
GetLocalVariableScope(CachedVariableProperty.Get()),
|
|
FBlueprintMetadata::MD_GetOptions
|
|
);
|
|
}
|
|
else
|
|
{
|
|
FBlueprintEditorUtils::SetBlueprintVariableMetaData(
|
|
PropertyBlueprint,
|
|
CachedVariableName,
|
|
GetLocalVariableScope(CachedVariableProperty.Get()),
|
|
FBlueprintMetadata::MD_GetOptions,
|
|
*InFunctionName
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
FText FBlueprintVarActionDetails::GetDeprecationMessageText() const
|
|
{
|
|
FName VarName = CachedVariableName;
|
|
if (VarName != NAME_None)
|
|
{
|
|
if (UBlueprint* OwnerBlueprint = GetPropertyOwnerBlueprint())
|
|
{
|
|
FString Result;
|
|
FBlueprintEditorUtils::GetBlueprintVariableMetaData(GetPropertyOwnerBlueprint(), VarName, GetLocalVariableScope(CachedVariableProperty.Get()), FBlueprintMetadata::MD_DeprecationMessage, Result);
|
|
return FText::FromString(Result);
|
|
}
|
|
}
|
|
return FText();
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnDeprecationMessageTextCommitted(const FText& NewText, ETextCommit::Type InTextCommit, FName VarName)
|
|
{
|
|
if (NewText.IsEmpty())
|
|
{
|
|
FBlueprintEditorUtils::RemoveBlueprintVariableMetaData(GetBlueprintObj(), VarName, GetLocalVariableScope(CachedVariableProperty.Get()), FBlueprintMetadata::MD_DeprecationMessage);
|
|
}
|
|
else
|
|
{
|
|
FBlueprintEditorUtils::SetBlueprintVariableMetaData(GetBlueprintObj(), VarName, GetLocalVariableScope(CachedVariableProperty.Get()), FBlueprintMetadata::MD_DeprecationMessage, NewText.ToString());
|
|
}
|
|
}
|
|
|
|
EVisibility FBlueprintVarActionDetails::IsTooltipEditVisible() const
|
|
{
|
|
FProperty* VariableProperty = CachedVariableProperty.Get();
|
|
if (VariableProperty)
|
|
{
|
|
if ((IsABlueprintVariable(VariableProperty) && IsAUserVariable(VariableProperty)) || IsALocalVariable(VariableProperty))
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
}
|
|
return EVisibility::Collapsed;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnBrowseToVarType() const
|
|
{
|
|
FEdGraphPinType PinType = OnGetVarType();
|
|
if (const UObject* Object = PinType.PinSubCategoryObject.Get())
|
|
{
|
|
if (Object->IsAsset())
|
|
{
|
|
FAssetData AssetData(Object, false);
|
|
if (AssetData.IsValid())
|
|
{
|
|
TArray<FAssetData> AssetDataList = { AssetData };
|
|
GEditor->SyncBrowserToObjects(AssetDataList);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FBlueprintVarActionDetails::CanBrowseToVarType() const
|
|
{
|
|
FEdGraphPinType PinType = OnGetVarType();
|
|
if (const UObject* Object = PinType.PinSubCategoryObject.Get())
|
|
{
|
|
if (Object->IsAsset())
|
|
{
|
|
FAssetData AssetData(Object, false);
|
|
if (AssetData.IsValid())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnFinishedChangingVariable(const FPropertyChangedEvent& InPropertyChangedEvent)
|
|
{
|
|
if (InPropertyChangedEvent.GetNumObjectsBeingEdited() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ImportNamespacesForPropertyValue(InPropertyChangedEvent.MemberProperty, InPropertyChangedEvent.GetObjectBeingEdited(0));
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::OnFinishedChangingLocalVariable(const FPropertyChangedEvent& InPropertyChangedEvent, TSharedPtr<FStructOnScope> InStructData, TWeakObjectPtr<UK2Node_EditablePinBase> InEntryNode)
|
|
{
|
|
if (!InPropertyChangedEvent.MemberProperty ||
|
|
!InPropertyChangedEvent.MemberProperty->GetOwnerStruct() ||
|
|
!InPropertyChangedEvent.MemberProperty->GetOwnerStruct()->IsA<UFunction>())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Find the top level property that was modified within the UFunction
|
|
const FProperty* DirectProperty = InPropertyChangedEvent.MemberProperty;
|
|
while (!DirectProperty->GetOwner<const UFunction>())
|
|
{
|
|
DirectProperty = DirectProperty->GetOwnerChecked<const FProperty>();
|
|
}
|
|
|
|
FString DefaultValueString;
|
|
bool bDefaultValueSet = false;
|
|
|
|
if (InStructData.IsValid())
|
|
{
|
|
UK2Node_FunctionEntry* FuncEntry = Cast<UK2Node_FunctionEntry>(InEntryNode.Get());
|
|
|
|
bDefaultValueSet = FBlueprintEditorUtils::PropertyValueToString(DirectProperty, InStructData->GetStructMemory(), DefaultValueString, FuncEntry);
|
|
|
|
if (bDefaultValueSet)
|
|
{
|
|
// Search out the correct local variable in the Function Entry Node and set the default value
|
|
for (FBPVariableDescription& LocalVar : FuncEntry->LocalVariables)
|
|
{
|
|
if (LocalVar.VarName == DirectProperty->GetFName() && LocalVar.DefaultValue != DefaultValueString)
|
|
{
|
|
const FScopedTransaction Transaction(LOCTEXT("ChangeDefaults", "Change Defaults"));
|
|
|
|
FuncEntry->Modify();
|
|
GetBlueprintObj()->Modify();
|
|
LocalVar.DefaultValue = DefaultValueString;
|
|
FuncEntry->RefreshFunctionVariableCache();
|
|
FBlueprintEditorUtils::MarkBlueprintAsModified(GetBlueprintObj());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ImportNamespacesForPropertyValue(DirectProperty, InStructData->GetStructMemory());
|
|
}
|
|
}
|
|
|
|
void FBlueprintVarActionDetails::ImportNamespacesForPropertyValue(const FProperty* InProperty, const void* InContainer)
|
|
{
|
|
// Auto-import any namespace(s) associated with the property's value into the current editor context.
|
|
TSharedPtr<SMyBlueprint> MyBlueprintPtr = MyBlueprint.Pin();
|
|
if (MyBlueprintPtr.IsValid())
|
|
{
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditor = MyBlueprintPtr->GetBlueprintEditor().Pin();
|
|
if (BlueprintEditor.IsValid())
|
|
{
|
|
FBlueprintEditor::FImportNamespaceExParameters Params;
|
|
FBlueprintNamespaceUtilities::GetPropertyValueNamespaces(InProperty, InContainer, Params.NamespacesToImport);
|
|
BlueprintEditor->ImportNamespaceEx(Params);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FBlueprintVarActionDetails::IsVariableInheritedByBlueprint() const
|
|
{
|
|
UClass* PropertyOwnerClass = nullptr;
|
|
if (UBlueprint* PropertyOwnerBP = GetPropertyOwnerBlueprint())
|
|
{
|
|
PropertyOwnerClass = PropertyOwnerBP->SkeletonGeneratedClass;
|
|
}
|
|
else if (CachedVariableProperty.IsValid())
|
|
{
|
|
PropertyOwnerClass = CachedVariableProperty->GetOwnerClass();
|
|
}
|
|
const UClass* SkeletonGeneratedClass = GetBlueprintObj()->SkeletonGeneratedClass;
|
|
return SkeletonGeneratedClass && SkeletonGeneratedClass->IsChildOf(PropertyOwnerClass);
|
|
}
|
|
|
|
bool FBlueprintVarActionDetails::IsVariableDeprecated() const
|
|
{
|
|
FProperty* Property = CachedVariableProperty.Get();
|
|
|
|
return Property && Property->HasAnyPropertyFlags(CPF_Deprecated);
|
|
}
|
|
|
|
static TArray<UK2Node_EditablePinBase*> GatherAllResultNodes(UK2Node_EditablePinBase* TargetNode)
|
|
{
|
|
if (UK2Node_FunctionResult* ResultNode = Cast<UK2Node_FunctionResult>(TargetNode))
|
|
{
|
|
return (TArray<UK2Node_EditablePinBase*>)ResultNode->GetAllResultNodes();
|
|
}
|
|
TArray<UK2Node_EditablePinBase*> Result;
|
|
if (TargetNode)
|
|
{
|
|
Result.Add(TargetNode);
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
/** Drag-and-drop operation that stores data about the function parameter pin being dragged */
|
|
class FBlueprintGraphArgumentDragDropOp : public FDecoratedDragDropOp
|
|
{
|
|
public:
|
|
DRAG_DROP_OPERATOR_TYPE(FBlueprintGraphArgumentDragDropOp, FDecoratedDragDropOp);
|
|
|
|
FBlueprintGraphArgumentDragDropOp(UK2Node_EditablePinBase* InTargetNode, TWeakPtr<FUserPinInfo> InParamItemPtr)
|
|
: TargetNode(InTargetNode)
|
|
, ParamItemPtr(InParamItemPtr)
|
|
{
|
|
MouseCursor = EMouseCursor::GrabHandClosed;
|
|
}
|
|
|
|
void Init()
|
|
{
|
|
SetValidTarget(false);
|
|
SetupDefaults();
|
|
Construct();
|
|
}
|
|
|
|
void SetValidTarget(bool IsValidTarget)
|
|
{
|
|
FText PinName = FText::FromName(ParamItemPtr.IsValid() ? ParamItemPtr.Pin()->PinName : NAME_None);
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("PinName"), PinName);
|
|
|
|
if (IsValidTarget)
|
|
{
|
|
CurrentHoverText = FText::Format(LOCTEXT("MovePinHere", "Move '{PinName}' Here"), Args);
|
|
CurrentIconBrush = FAppStyle::Get().GetBrush("Graph.ConnectorFeedback.OK");
|
|
}
|
|
else
|
|
{
|
|
CurrentHoverText = FText::Format(LOCTEXT("CannotMovePinHere", "Cannot Move '{PinName}' Here"), Args);
|
|
CurrentIconBrush = FAppStyle::Get().GetBrush("Graph.ConnectorFeedback.Error");
|
|
}
|
|
}
|
|
|
|
UK2Node_EditablePinBase* GetTargetNode() const
|
|
{
|
|
return TargetNode;
|
|
}
|
|
|
|
TWeakPtr<FUserPinInfo> GetParamItem() const
|
|
{
|
|
return ParamItemPtr;
|
|
}
|
|
|
|
private:
|
|
UK2Node_EditablePinBase* TargetNode;
|
|
TWeakPtr<FUserPinInfo> ParamItemPtr;
|
|
};
|
|
|
|
/** Handler for customizing the drag-and-drop behavior for function entry/result pins, allowing parameters to be reordered */
|
|
class FBlueprintGraphArgumentDragDropHandler : public IDetailDragDropHandler
|
|
{
|
|
public:
|
|
FBlueprintGraphArgumentDragDropHandler(
|
|
TWeakPtr<FBaseBlueprintGraphActionDetails> InGraphActionDetailsPtr,
|
|
UK2Node_EditablePinBase* InTargetNode,
|
|
TWeakPtr<FUserPinInfo> InParamItemPtr)
|
|
: GraphActionDetailsPtr(InGraphActionDetailsPtr)
|
|
, TargetNode(InTargetNode)
|
|
, ParamItemPtr(InParamItemPtr)
|
|
{
|
|
}
|
|
|
|
virtual TSharedPtr<FDragDropOperation> CreateDragDropOperation() const override
|
|
{
|
|
TSharedPtr<FBlueprintGraphArgumentDragDropOp> DragOp = MakeShared<FBlueprintGraphArgumentDragDropOp>(TargetNode, ParamItemPtr);
|
|
DragOp->Init();
|
|
return DragOp;
|
|
}
|
|
|
|
/** Compute new target index for use with AcceptDrop/CanAcceptDrop based on drop zone (above vs below) */
|
|
static int32 ComputeNewIndex(int32 OriginalIndex, int32 DropOntoIndex, EItemDropZone DropZone)
|
|
{
|
|
check(DropZone != EItemDropZone::OntoItem);
|
|
|
|
int32 NewIndex = DropOntoIndex;
|
|
if (DropZone == EItemDropZone::BelowItem)
|
|
{
|
|
// If the drop zone is below, then we actually move it to the next item's index
|
|
NewIndex++;
|
|
}
|
|
if (OriginalIndex < NewIndex)
|
|
{
|
|
// If the item is moved down the list, then all the other elements below it are shifted up one
|
|
NewIndex--;
|
|
}
|
|
|
|
return ensure(NewIndex >= 0) ? NewIndex : 0;
|
|
}
|
|
|
|
virtual bool AcceptDrop(const FDragDropEvent& DragDropEvent, EItemDropZone DropZone) const override
|
|
{
|
|
const TSharedPtr<FBlueprintGraphArgumentDragDropOp> DragOp = DragDropEvent.GetOperationAs<FBlueprintGraphArgumentDragDropOp>();
|
|
if (!DragOp.IsValid() || DragOp->GetTargetNode() != TargetNode)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!ensure(ParamItemPtr.IsValid()) || !ensure(DragOp->GetParamItem().IsValid()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check that the original and new indices are valid, and that they aren't the same (we're actually moving something)
|
|
const int32 OriginalParamIndex = DragOp->GetTargetNode()->UserDefinedPins.Find(DragOp->GetParamItem().Pin());
|
|
const int32 OntoParamIndex = TargetNode->UserDefinedPins.Find(ParamItemPtr.Pin());
|
|
const int32 NewParamIndex = ComputeNewIndex(OriginalParamIndex, OntoParamIndex, DropZone);
|
|
if (OriginalParamIndex == INDEX_NONE || OriginalParamIndex == NewParamIndex || NewParamIndex < 0 || NewParamIndex >= TargetNode->UserDefinedPins.Num())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const FScopedTransaction Transaction(LOCTEXT("K2_MovePin", "Move Pin"));
|
|
TArray<UK2Node_EditablePinBase*> TargetNodes = GatherAllResultNodes(TargetNode);
|
|
for (UK2Node_EditablePinBase* Node : TargetNodes)
|
|
{
|
|
Node->Modify();
|
|
|
|
TSharedPtr<FUserPinInfo> ParamToMove = Node->UserDefinedPins[OriginalParamIndex];
|
|
Node->UserDefinedPins.RemoveAt(OriginalParamIndex);
|
|
Node->UserDefinedPins.Insert(ParamToMove, NewParamIndex);
|
|
|
|
TSharedPtr<FBaseBlueprintGraphActionDetails> GraphActionDetails = GraphActionDetailsPtr.Pin();
|
|
if (GraphActionDetails.IsValid())
|
|
{
|
|
GraphActionDetails->OnParamsChanged(Node, true);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
virtual TOptional<EItemDropZone> CanAcceptDrop(const FDragDropEvent& DragDropEvent, EItemDropZone DropZone) const override
|
|
{
|
|
const TSharedPtr<FBlueprintGraphArgumentDragDropOp> DragOp = DragDropEvent.GetOperationAs<FBlueprintGraphArgumentDragDropOp>();
|
|
if (!DragOp.IsValid() || DragOp->GetTargetNode() != TargetNode)
|
|
{
|
|
return TOptional<EItemDropZone>();
|
|
}
|
|
|
|
// We're reordering, so there's no logical interpretation for dropping directly onto another parameter.
|
|
// Just change it to a drop-above in this case.
|
|
const EItemDropZone OverrideZone = (DropZone == EItemDropZone::BelowItem) ? EItemDropZone::BelowItem : EItemDropZone::AboveItem;
|
|
|
|
// Check that the original and new indices are valid, and that they aren't the same (we're actually moving something)
|
|
const int32 OriginalParamIndex = DragOp->GetTargetNode()->UserDefinedPins.Find(DragOp->GetParamItem().Pin());
|
|
const int32 OntoParamIndex = TargetNode->UserDefinedPins.Find(ParamItemPtr.Pin());
|
|
const int32 NewParamIndex = ComputeNewIndex(OriginalParamIndex, OntoParamIndex, OverrideZone);
|
|
if (OriginalParamIndex == INDEX_NONE || OriginalParamIndex == NewParamIndex || NewParamIndex < 0 || NewParamIndex >= TargetNode->UserDefinedPins.Num())
|
|
{
|
|
return TOptional<EItemDropZone>();
|
|
}
|
|
|
|
DragOp->SetValidTarget(true);
|
|
return OverrideZone;
|
|
}
|
|
|
|
private:
|
|
/** The parent graph action details customization */
|
|
TWeakPtr<FBaseBlueprintGraphActionDetails> GraphActionDetailsPtr;
|
|
|
|
/** The target node that the argument pin is on */
|
|
UK2Node_EditablePinBase* TargetNode;
|
|
|
|
/** The argument pin that this drag handler reflects */
|
|
TWeakPtr<FUserPinInfo> ParamItemPtr;
|
|
};
|
|
|
|
void FBlueprintGraphArgumentGroupLayout::SetOnRebuildChildren( FSimpleDelegate InOnRegenerateChildren )
|
|
{
|
|
GraphActionDetailsPtr.Pin()->SetRefreshDelegate(InOnRegenerateChildren, TargetNode == GraphActionDetailsPtr.Pin()->GetFunctionEntryNode().Get());
|
|
}
|
|
|
|
void FBlueprintGraphArgumentGroupLayout::GenerateChildContent( IDetailChildrenBuilder& ChildrenBuilder )
|
|
{
|
|
bool WasContentAdded = false;
|
|
if(TargetNode.IsValid())
|
|
{
|
|
TArray<TSharedPtr<FUserPinInfo>> Pins = TargetNode->UserDefinedPins;
|
|
|
|
if(Pins.Num() > 0)
|
|
{
|
|
bool bIsInputNode = TargetNode == GraphActionDetailsPtr.Pin()->GetFunctionEntryNode().Get();
|
|
for (int32 i = 0; i < Pins.Num(); ++i)
|
|
{
|
|
// If possible, use stable guids for the argument names since the path names are used to store
|
|
// expansion state. Using guids means that the expansion state travels with the row when
|
|
// reordering arguments.
|
|
// Fall back to the old style of using the pin index for the name if we can't find the pin.
|
|
FString ArgumentName;
|
|
if (UEdGraphPin* Pin = TargetNode->FindPin(Pins[i]->PinName, Pins[i]->DesiredPinDirection))
|
|
{
|
|
ArgumentName = Pin->PinId.ToString();
|
|
}
|
|
else
|
|
{
|
|
ArgumentName = bIsInputNode ? FString::Printf(TEXT("InputArgument%i"), i) : FString::Printf(TEXT("OutputArgument%i"), i);
|
|
}
|
|
|
|
TSharedRef<class FBlueprintGraphArgumentLayout> BlueprintArgumentLayout = MakeShareable(new FBlueprintGraphArgumentLayout(
|
|
TWeakPtr<FUserPinInfo>(Pins[i]),
|
|
TargetNode.Get(),
|
|
GraphActionDetailsPtr,
|
|
FName(*ArgumentName),
|
|
bIsInputNode));
|
|
ChildrenBuilder.AddCustomBuilder(BlueprintArgumentLayout);
|
|
WasContentAdded = true;
|
|
}
|
|
}
|
|
}
|
|
if (!WasContentAdded)
|
|
{
|
|
// Add a text widget to let the user know to hit the + icon to add parameters.
|
|
ChildrenBuilder.AddCustomRow(FText::GetEmpty()).WholeRowContent()
|
|
.MaxDesiredWidth(980.f)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(0.0f, 0.0f, 4.0f, 0.0f)
|
|
.AutoWidth()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("NoArgumentsAddedForBlueprint", "Please press the + icon above to add parameters"))
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
];
|
|
}
|
|
}
|
|
|
|
// Internal
|
|
static bool ShouldAllowWildcard(UK2Node_EditablePinBase* TargetNode)
|
|
{
|
|
// allow wildcards for tunnel nodes in macro graphs
|
|
if ( TargetNode->IsA(UK2Node_Tunnel::StaticClass()) )
|
|
{
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
return ( K2Schema->GetGraphType( TargetNode->GetGraph() ) == GT_Macro );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
FBlueprintGraphArgumentLayout::~FBlueprintGraphArgumentLayout()
|
|
{
|
|
if (IsValid(TargetNode) && TargetNode->HasValidBlueprint() && GraphChangedHandler != FDelegateHandle())
|
|
{
|
|
TargetNode->GetGraph()->RemoveOnGraphChangedHandler(GraphChangedHandler);
|
|
}
|
|
}
|
|
|
|
void FBlueprintGraphArgumentLayout::GenerateHeaderRowContent( FDetailWidgetRow& NodeRow )
|
|
{
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
|
|
ETypeTreeFilter TypeTreeFilter = ETypeTreeFilter::None;
|
|
if (TargetNode->CanModifyExecutionWires())
|
|
{
|
|
TypeTreeFilter |= ETypeTreeFilter::AllowExec;
|
|
}
|
|
|
|
if (ShouldAllowWildcard(TargetNode))
|
|
{
|
|
TypeTreeFilter |= ETypeTreeFilter::AllowWildcard;
|
|
}
|
|
|
|
TArray<TSharedPtr<IPinTypeSelectorFilter>> CustomPinTypeFilters;
|
|
if (GraphActionDetailsPtr.IsValid())
|
|
{
|
|
TSharedPtr<SMyBlueprint> MyBlueprintPtr = GraphActionDetailsPtr.Pin()->GetMyBlueprint().Pin();
|
|
if (MyBlueprintPtr.IsValid())
|
|
{
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditorPtr = MyBlueprintPtr->GetBlueprintEditor().Pin();
|
|
if (BlueprintEditorPtr.IsValid())
|
|
{
|
|
BlueprintEditorPtr->GetPinTypeSelectorFilters(CustomPinTypeFilters);
|
|
}
|
|
}
|
|
}
|
|
|
|
NodeRow
|
|
.NameContent()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.FillWidth(1)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(SBox)
|
|
.MinDesiredWidth(125.f)
|
|
[
|
|
SAssignNew(ArgumentNameWidget, SEditableTextBox)
|
|
.Text( this, &FBlueprintGraphArgumentLayout::OnGetArgNameText )
|
|
.OnTextChanged(this, &FBlueprintGraphArgumentLayout::OnArgNameChange)
|
|
.OnTextCommitted(this, &FBlueprintGraphArgumentLayout::OnArgNameTextCommitted)
|
|
.ToolTipText(this, &FBlueprintGraphArgumentLayout::OnGetArgToolTipText)
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
.IsEnabled(!ShouldPinBeReadOnly())
|
|
]
|
|
]
|
|
]
|
|
.ValueContent()
|
|
.MaxDesiredWidth(980.f)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(0.0f)
|
|
.FillWidth(1.0f)
|
|
[
|
|
SNew(SPinTypeSelector, FGetPinTypeTree::CreateUObject(K2Schema, &UEdGraphSchema_K2::GetVariableTypeTree))
|
|
.TargetPinType(this, &FBlueprintGraphArgumentLayout::OnGetPinInfo)
|
|
.OnPinTypePreChanged(this, &FBlueprintGraphArgumentLayout::OnPrePinInfoChange)
|
|
.OnPinTypeChanged(this, &FBlueprintGraphArgumentLayout::PinInfoChanged)
|
|
.Schema(K2Schema)
|
|
.TypeTreeFilter(TypeTreeFilter)
|
|
.bAllowArrays(!ShouldPinBeReadOnly())
|
|
.IsEnabled(!ShouldPinBeReadOnly(true))
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
.CustomFilters(CustomPinTypeFilters)
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
.HAlign(HAlign_Right)
|
|
.VAlign(VAlign_Center)
|
|
.Padding(10, 0, 0, 0)
|
|
.AutoWidth()
|
|
[
|
|
PropertyCustomizationHelpers::MakeClearButton(FSimpleDelegate::CreateSP(this, &FBlueprintGraphArgumentLayout::OnRemoveClicked), LOCTEXT("FunctionArgDetailsClearTooltip", "Remove this parameter."), !IsPinEditingReadOnly())
|
|
]
|
|
|
|
]
|
|
.DragDropHandler(MakeShared<FBlueprintGraphArgumentDragDropHandler>(GraphActionDetailsPtr, TargetNode, ParamItemPtr));
|
|
}
|
|
|
|
void FBlueprintGraphArgumentLayout::GenerateChildContent( IDetailChildrenBuilder& ChildrenBuilder )
|
|
{
|
|
bool bMadePinWidgets = false;
|
|
if (bHasDefaultValue)
|
|
{
|
|
UEdGraphPin* FoundPin = GetPin();
|
|
if (FoundPin)
|
|
{
|
|
// Certain types are outlawed at the compiler level, or to keep consistency with variable rules for actors
|
|
const UClass* ClassObject = Cast<UClass>(FoundPin->PinType.PinSubCategoryObject.Get());
|
|
const bool bTypeWithNoDefaults = (FoundPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Object) || (FoundPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Class) || (FoundPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Interface)
|
|
|| (FoundPin->PinType.PinCategory == UEdGraphSchema_K2::PC_SoftObject && ClassObject && ClassObject->IsChildOf(AActor::StaticClass()))
|
|
|| UEdGraphSchema_K2::IsExecPin(*FoundPin)
|
|
|| FoundPin->PinType.IsContainer();
|
|
|
|
if (!FoundPin->PinType.bIsReference && !bTypeWithNoDefaults)
|
|
{
|
|
bMadePinWidgets = true;
|
|
DefaultValuePinWidget = FNodeFactory::CreatePinWidget(FoundPin);
|
|
DefaultValuePinWidget->SetOnlyShowDefaultValue(true);
|
|
TSharedRef<SWidget> DefaultValueWidget = DefaultValuePinWidget->GetDefaultValueWidget();
|
|
|
|
if (DefaultValueWidget != SNullWidget::NullWidget)
|
|
{
|
|
ChildrenBuilder.AddCustomRow(LOCTEXT("FunctionArgDetailsDefaultValue", "Default Value"))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("FunctionArgDetailsDefaultValue", "Default Value"))
|
|
.ToolTipText(LOCTEXT("FunctionArgDetailsDefaultValueParamTooltip", "The default value of the parameter."))
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
.MaxDesiredWidth(512)
|
|
[
|
|
DefaultValueWidget
|
|
];
|
|
}
|
|
else
|
|
{
|
|
DefaultValuePinWidget.Reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool bMacroGraph = false;
|
|
if (TargetNode && TargetNode->HasValidBlueprint())
|
|
{
|
|
if (const UBlueprint* Blueprint = TargetNode->GetBlueprint())
|
|
{
|
|
if (Blueprint->BlueprintType == BPTYPE_MacroLibrary)
|
|
{
|
|
bMacroGraph = true;
|
|
}
|
|
else if (const UEdGraph* Graph = TargetNode->GetGraph())
|
|
{
|
|
bMacroGraph = Blueprint->MacroGraphs.Contains(Graph);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Exec pins can't be passed by reference
|
|
if (FoundPin && !UEdGraphSchema_K2::IsExecPin(*FoundPin) && !bMacroGraph)
|
|
{
|
|
auto ShouldPassByRefBeReadOnly = [this]
|
|
{
|
|
// Array types will always be implicitly passed by reference, regardless of
|
|
// the checkbox setting so make it readonly.
|
|
return OnGetPinInfo().IsArray() || ShouldPinBeReadOnly();
|
|
};
|
|
|
|
ChildrenBuilder.AddCustomRow(LOCTEXT("FunctionArgDetailsPassByReference", "Pass-by-Reference"))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("FunctionArgDetailsPassByReference", "Pass-by-Reference"))
|
|
.ToolTipText(LOCTEXT("FunctionArgDetailsPassByReferenceTooltip", "Pass this parameter by reference?"))
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked(this, &FBlueprintGraphArgumentLayout::IsRefChecked)
|
|
.OnCheckStateChanged(this, &FBlueprintGraphArgumentLayout::OnRefCheckStateChanged)
|
|
.IsEnabled(!ShouldPassByRefBeReadOnly())
|
|
];
|
|
|
|
ChildrenBuilder.AddCustomRow(LOCTEXT("FunctionArgDetailsConst", "Const"))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("FunctionArgDetailsConst", "Const"))
|
|
.ToolTipText(LOCTEXT("FunctionArgDetailsConstTooltip", "When passing by reference a parameter can be specified as const (not writable)"))
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked(this, &FBlueprintGraphArgumentLayout::IsConstChecked)
|
|
.OnCheckStateChanged(this, &FBlueprintGraphArgumentLayout::OnConstCheckStateChanged)
|
|
.IsEnabled(this, &FBlueprintGraphArgumentLayout::CanChangeConst)
|
|
];
|
|
}
|
|
}
|
|
|
|
if(bMadePinWidgets)
|
|
{
|
|
// add a listener to the owning graph so we can clean up our pin widgets:
|
|
if (TargetNode && TargetNode->HasValidBlueprint())
|
|
{
|
|
GraphChangedHandler = TargetNode->GetGraph()->AddOnGraphChangedHandler(
|
|
FOnGraphChanged::FDelegate::CreateSP(this, &FBlueprintGraphArgumentLayout::OnArgumentGraphChanged));
|
|
}
|
|
}
|
|
}
|
|
|
|
void FBlueprintGraphArgumentLayout::OnRemoveClicked()
|
|
{
|
|
TSharedPtr<FUserPinInfo> ParamItem = ParamItemPtr.Pin();
|
|
if (ParamItem.IsValid())
|
|
{
|
|
const FScopedTransaction Transaction( LOCTEXT( "RemoveParam", "Remove Parameter" ) );
|
|
|
|
TSharedPtr<FBaseBlueprintGraphActionDetails> GraphActionDetails = GraphActionDetailsPtr.Pin();
|
|
TArray<UK2Node_EditablePinBase*> TargetNodes = GatherAllResultNodes(TargetNode);
|
|
for (UK2Node_EditablePinBase* Node : TargetNodes)
|
|
{
|
|
Node->Modify();
|
|
Node->RemoveUserDefinedPinByName(ParamItem->PinName);
|
|
|
|
if (GraphActionDetails.IsValid())
|
|
{
|
|
GraphActionDetails->OnParamsChanged(Node, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FBlueprintGraphArgumentLayout::OnArgumentGraphChanged(const FEdGraphEditAction& Action)
|
|
{
|
|
TSharedPtr<FBaseBlueprintGraphActionDetails> GraphActionDetails = GraphActionDetailsPtr.Pin();
|
|
if (GraphActionDetails.IsValid())
|
|
{
|
|
GraphActionDetails->OnPostEditorRefresh();
|
|
DefaultValuePinWidget.Reset();
|
|
}
|
|
}
|
|
|
|
bool FBlueprintGraphArgumentLayout::ShouldPinBeReadOnly(bool bIsEditingPinType/* = false*/) const
|
|
{
|
|
if (TargetNode && ParamItemPtr.IsValid())
|
|
{
|
|
// Right now, we only care that the user is unable to edit the auto-generated "then" pin
|
|
if ((ParamItemPtr.Pin()->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec) && (!TargetNode->CanModifyExecutionWires()))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Check if pin editing is read only
|
|
return IsPinEditingReadOnly(bIsEditingPinType);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FBlueprintGraphArgumentLayout::IsPinEditingReadOnly(bool bIsEditingPinType/* = false*/) const
|
|
{
|
|
if(UEdGraph* NodeGraph = TargetNode->GetGraph())
|
|
{
|
|
// Math expression should not be modified directly (except for the pin type), do not let the user tweak the parameters
|
|
if (!bIsEditingPinType && Cast<UK2Node_MathExpression>(NodeGraph->GetOuter()) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
FText FBlueprintGraphArgumentLayout::OnGetArgNameText() const
|
|
{
|
|
if (ParamItemPtr.IsValid())
|
|
{
|
|
return FText::FromName(ParamItemPtr.Pin()->PinName);
|
|
}
|
|
return FText();
|
|
}
|
|
|
|
FText FBlueprintGraphArgumentLayout::OnGetArgToolTipText() const
|
|
{
|
|
if (ParamItemPtr.IsValid())
|
|
{
|
|
FText PinTypeText = UEdGraphSchema_K2::TypeToText(ParamItemPtr.Pin()->PinType);
|
|
return FText::Format(LOCTEXT("BlueprintArgToolTipText", "Name: {0}\nType: {1}"), FText::FromName(ParamItemPtr.Pin()->PinName), PinTypeText);
|
|
}
|
|
return FText::GetEmpty();
|
|
}
|
|
|
|
void FBlueprintGraphArgumentLayout::OnArgNameChange(const FText& InNewText)
|
|
{
|
|
bool bVerified = true;
|
|
|
|
FText ErrorMessage;
|
|
|
|
if (!ParamItemPtr.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (InNewText.IsEmpty())
|
|
{
|
|
ErrorMessage = LOCTEXT("EmptyArgument", "Name cannot be empty!");
|
|
bVerified = false;
|
|
}
|
|
else
|
|
{
|
|
bVerified = GraphActionDetailsPtr.Pin()->OnVerifyPinRename(TargetNode, ParamItemPtr.Pin()->PinName, InNewText.ToString(), ErrorMessage);
|
|
}
|
|
|
|
if(!bVerified)
|
|
{
|
|
ArgumentNameWidget.Pin()->SetError(ErrorMessage);
|
|
}
|
|
else
|
|
{
|
|
ArgumentNameWidget.Pin()->SetError(FText::GetEmpty());
|
|
}
|
|
}
|
|
|
|
void FBlueprintGraphArgumentLayout::OnArgNameTextCommitted(const FText& NewText, ETextCommit::Type InTextCommit)
|
|
{
|
|
if (!NewText.IsEmpty() && TargetNode && ParamItemPtr.IsValid() && GraphActionDetailsPtr.IsValid() && !ShouldPinBeReadOnly())
|
|
{
|
|
const FName OldName = ParamItemPtr.Pin()->PinName;
|
|
const FString& NewName = NewText.ToString();
|
|
if (!OldName.ToString().Equals(NewName))
|
|
{
|
|
GraphActionDetailsPtr.Pin()->OnPinRenamed(TargetNode, OldName, NewName);
|
|
}
|
|
}
|
|
}
|
|
|
|
FEdGraphPinType FBlueprintGraphArgumentLayout::OnGetPinInfo() const
|
|
{
|
|
if (ParamItemPtr.IsValid())
|
|
{
|
|
return ParamItemPtr.Pin()->PinType;
|
|
}
|
|
return FEdGraphPinType();
|
|
}
|
|
|
|
UEdGraphPin* FBlueprintGraphArgumentLayout::GetPin() const
|
|
{
|
|
if (ParamItemPtr.IsValid() && TargetNode)
|
|
{
|
|
return TargetNode->FindPin(ParamItemPtr.Pin()->PinName, ParamItemPtr.Pin()->DesiredPinDirection);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
ECheckBoxState FBlueprintGraphArgumentLayout::IsRefChecked() const
|
|
{
|
|
const FEdGraphPinType PinType = OnGetPinInfo();
|
|
|
|
// Array types will always be implicitly passed by reference, regardless of
|
|
// the checkbox setting so show it as checked
|
|
return (PinType.bIsReference || PinType.IsArray()) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
ECheckBoxState FBlueprintGraphArgumentLayout::IsConstChecked() const
|
|
{
|
|
return OnGetPinInfo().bIsConst ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
void FBlueprintGraphArgumentLayout::OnRefCheckStateChanged(ECheckBoxState InState)
|
|
{
|
|
const FScopedTransaction Transaction(LOCTEXT("ChangeByRef", "Change Pass By Reference"));
|
|
|
|
const bool bIsReference = (InState == ECheckBoxState::Checked);
|
|
FEdGraphPinType PinType = OnGetPinInfo();
|
|
PinType.bIsReference = bIsReference;
|
|
if(!bIsReference)
|
|
{
|
|
// constness is not meaningful for non reference types,
|
|
// reset constness when refness is toggled off:
|
|
PinType.bIsConst = ShouldBeForceConst();
|
|
}
|
|
|
|
PinInfoChanged(PinType);
|
|
}
|
|
|
|
void FBlueprintGraphArgumentLayout::OnConstCheckStateChanged(ECheckBoxState InState)
|
|
{
|
|
FEdGraphPinType PinType = OnGetPinInfo();
|
|
|
|
PinType.bIsConst = ShouldBeForceConst() || (InState == ECheckBoxState::Checked);
|
|
|
|
PinInfoChanged(PinType);
|
|
}
|
|
|
|
bool FBlueprintGraphArgumentLayout::ShouldBeForceConst() const
|
|
{
|
|
// Const-ness is not meaningful unless we're passing by reference, including all arrays
|
|
// which are implicitly passed by reference. If we have a reference pin type (implicit or
|
|
// otherwise) we want to honor ShouldUseConstRefParams:
|
|
FEdGraphPinType PinType = OnGetPinInfo();
|
|
return (PinType.bIsReference || PinType.IsArray()) && TargetNode && TargetNode->ShouldUseConstRefParams();
|
|
}
|
|
|
|
bool FBlueprintGraphArgumentLayout::CanChangeConst() const
|
|
{
|
|
return !ShouldBeForceConst() && IsRefChecked() == ECheckBoxState::Checked;
|
|
}
|
|
|
|
void FBlueprintGraphArgumentLayout::PinInfoChanged(const FEdGraphPinType& PinType)
|
|
{
|
|
if (ParamItemPtr.IsValid() && FBlueprintEditorUtils::IsPinTypeValid(PinType))
|
|
{
|
|
const FName PinName = ParamItemPtr.Pin()->PinName;
|
|
TSharedPtr<class FBaseBlueprintGraphActionDetails> GraphActionDetailsPinned = GraphActionDetailsPtr.Pin();
|
|
if (GraphActionDetailsPinned.IsValid())
|
|
{
|
|
TSharedPtr<SMyBlueprint> MyBPPinned = GraphActionDetailsPinned->GetMyBlueprint().Pin();
|
|
if (MyBPPinned.IsValid())
|
|
{
|
|
MyBPPinned->GetLastFunctionPinTypeUsed() = PinType;
|
|
}
|
|
if( !ShouldPinBeReadOnly(true) )
|
|
{
|
|
TArray<UK2Node_EditablePinBase*> TargetNodes = GatherAllResultNodes(TargetNode);
|
|
for (UK2Node_EditablePinBase* Node : TargetNodes)
|
|
{
|
|
if (Node)
|
|
{
|
|
TSharedPtr<FUserPinInfo>* UDPinPtr = Node->UserDefinedPins.FindByPredicate([PinName](TSharedPtr<FUserPinInfo>& UDPin)
|
|
{
|
|
return UDPin.IsValid() && (UDPin->PinName == PinName);
|
|
});
|
|
if (UDPinPtr)
|
|
{
|
|
Node->Modify();
|
|
(*UDPinPtr)->PinType = PinType;
|
|
|
|
// Inputs flagged as pass-by-reference will also be flagged as 'const' here to conform to the expected native C++
|
|
// declaration of 'const Type&' for input reference parameters on functions with no outputs (i.e. events). Array
|
|
// types are also flagged as 'const' here since they will always be implicitly passed by reference, regardless of
|
|
// the checkbox setting. See UEditablePinBase::PostLoad() for more details.
|
|
if(!PinType.bIsConst && Node->ShouldUseConstRefParams())
|
|
{
|
|
(*UDPinPtr)->PinType.bIsConst = PinType.IsArray() || PinType.bIsReference;
|
|
}
|
|
|
|
// Reset default value, it probably doesn't match
|
|
(*UDPinPtr)->PinDefaultValue.Reset();
|
|
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditor;
|
|
if(MyBPPinned.IsValid())
|
|
{
|
|
BlueprintEditor = MyBPPinned->GetBlueprintEditor().Pin();
|
|
}
|
|
|
|
// Auto-import the underlying type object's default namespace set into the current editor context.
|
|
const UObject* PinSubCategoryObject = PinType.PinSubCategoryObject.Get();
|
|
if (PinSubCategoryObject && BlueprintEditor.IsValid())
|
|
{
|
|
FBlueprintEditor::FImportNamespaceExParameters Params;
|
|
FBlueprintNamespaceUtilities::GetDefaultImportsForObject(PinSubCategoryObject, Params.NamespacesToImport);
|
|
BlueprintEditor->ImportNamespaceEx(Params);
|
|
}
|
|
}
|
|
GraphActionDetailsPinned->OnParamsChanged(Node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FBlueprintGraphArgumentLayout::OnPrePinInfoChange(const FEdGraphPinType& PinType)
|
|
{
|
|
if (!ShouldPinBeReadOnly(true))
|
|
{
|
|
TArray<UK2Node_EditablePinBase*> TargetNodes = GatherAllResultNodes(TargetNode);
|
|
for (UK2Node_EditablePinBase* Node : TargetNodes)
|
|
{
|
|
if (Node)
|
|
{
|
|
Node->Modify();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FBlueprintGraphLocalVariableGroupLayout::GenerateChildContent(IDetailChildrenBuilder& ChildrenBuilder)
|
|
{
|
|
bool WasContentAdded = false;
|
|
if(TargetGraph.IsValid())
|
|
{
|
|
if(const UEdGraph* TopLevelGraph = FBlueprintEditorUtils::GetTopLevelGraph(TargetGraph.Get()))
|
|
{
|
|
bool bSchemaImplementsGetLocalVariables = false;
|
|
|
|
// grab the parent graph's name
|
|
if (UEdGraphSchema const* Schema = TopLevelGraph->GetSchema())
|
|
{
|
|
FGraphDisplayInfo EdGraphDisplayInfo;
|
|
Schema->GetGraphDisplayInformation(*TopLevelGraph, EdGraphDisplayInfo);
|
|
|
|
// Try to get the local variables from the schema
|
|
TArray<FBPVariableDescription> LocalVariables;
|
|
bSchemaImplementsGetLocalVariables = Schema->GetLocalVariables(TargetGraph.Get(), LocalVariables);
|
|
for (const FBPVariableDescription& LocalVariable : LocalVariables)
|
|
{
|
|
TSharedRef<class FBlueprintGraphLocalVariableLayout> BlueprintLocalVariableLayout = MakeShareable(new FBlueprintGraphLocalVariableLayout(OwningFunction, LocalVariable));
|
|
ChildrenBuilder.AddCustomBuilder(BlueprintLocalVariableLayout);
|
|
WasContentAdded = true;
|
|
}
|
|
}
|
|
// If the schema did not return any local variables, try to get them from the function entry
|
|
if (!bSchemaImplementsGetLocalVariables)
|
|
{
|
|
TArray<UK2Node_FunctionEntry*> FunctionEntryNodes;
|
|
TopLevelGraph->GetNodesOfClass<UK2Node_FunctionEntry>(FunctionEntryNodes);
|
|
if (!FunctionEntryNodes.IsEmpty())
|
|
{
|
|
TArray<FBPVariableDescription>& LocalVariables = FunctionEntryNodes[0]->LocalVariables;
|
|
|
|
// Search in all FunctionEntry nodes for their local variables
|
|
FText ActionCategory;
|
|
for (int I = 0; I < LocalVariables.Num(); ++I)
|
|
{
|
|
TSharedPtr<class FBlueprintGraphLocalVariableLayout> BlueprintLocalVariableLayout = nullptr;
|
|
if (PropertyHandle)
|
|
{
|
|
BlueprintLocalVariableLayout = MakeShareable(new FBlueprintGraphLocalVariableLayout(OwningFunction, PropertyHandle->GetChildHandle(I)));
|
|
}
|
|
else
|
|
{
|
|
BlueprintLocalVariableLayout = MakeShareable(new FBlueprintGraphLocalVariableLayout(OwningFunction, LocalVariables[I]));
|
|
}
|
|
ChildrenBuilder.AddCustomBuilder(BlueprintLocalVariableLayout.ToSharedRef());
|
|
WasContentAdded = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!WasContentAdded)
|
|
{
|
|
// Add a text widget to let the user know to hit the + icon to add parameters.
|
|
ChildrenBuilder.AddCustomRow(FText::GetEmpty()).WholeRowContent()
|
|
.MaxDesiredWidth(980.f)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(0.0f, 0.0f, 4.0f, 0.0f)
|
|
.AutoWidth()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("NoLocalVariablesAddedForBlueprint", "No Local Variables"))
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
];
|
|
}
|
|
}
|
|
|
|
TSharedPtr<IPropertyHandle> FBlueprintGraphLocalVariableLayout::GetPropertyHandle() const
|
|
{
|
|
return Data.IsType<TSharedPtr<IPropertyHandle>>() ? Data.Get<TSharedPtr<IPropertyHandle>>() : nullptr;
|
|
}
|
|
|
|
const FBPVariableDescription& FBlueprintGraphLocalVariableLayout::GetVariable() const
|
|
{
|
|
// if stored by property handle, extract value from there
|
|
if (const TSharedPtr<IPropertyHandle> PropertyHandle = GetPropertyHandle())
|
|
{
|
|
void* Address;
|
|
PropertyHandle->GetValueData(Address);
|
|
return *(FBPVariableDescription*)Address;
|
|
}
|
|
// if stored as variable description, return it directly
|
|
return Data.Get<FBPVariableDescription>();
|
|
}
|
|
|
|
void FBlueprintGraphLocalVariableLayout::SetVariable(const FBPVariableDescription& NewValue)
|
|
{
|
|
const TSharedPtr<IPropertyHandle> PropertyHandle = GetPropertyHandle();
|
|
checkf(PropertyHandle != nullptr, TEXT("Tried to call a setter on a read-only local variable layout"));
|
|
|
|
void* Address;
|
|
PropertyHandle->GetValueData(Address);
|
|
*(FBPVariableDescription*)Address = NewValue;
|
|
}
|
|
|
|
void FBlueprintGraphLocalVariableLayout::GenerateHeaderRowContent(FDetailWidgetRow& NodeRow)
|
|
{
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
TSharedPtr<IPropertyHandle> PropHandle = GetPropertyHandle();
|
|
|
|
NodeRow
|
|
.NameContent()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.FillWidth(1)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(SBox)
|
|
.MinDesiredWidth(125.f)
|
|
[
|
|
SAssignNew(VariableNameWidget, SEditableTextBox)
|
|
.Text( this, &FBlueprintGraphLocalVariableLayout::OnGetVarNameText )
|
|
#if UE_BP_LOCAL_VAR_LAYOUT_SETTERS_IMPLEMENTED
|
|
.OnTextChanged(this, &FBlueprintGraphLocalVariableLayout::OnVarNameChange)
|
|
.OnTextCommitted(this, &FBlueprintGraphLocalVariableLayout::OnVarNameTextCommitted)
|
|
#endif
|
|
.ToolTipText(this, &FBlueprintGraphLocalVariableLayout::OnGetVarToolTipText)
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
.IsEnabled(!ShouldVarBeReadOnly())
|
|
]
|
|
]
|
|
]
|
|
.ValueContent()
|
|
.MaxDesiredWidth(980.f)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(0.0f)
|
|
.FillWidth(1.0f)
|
|
[
|
|
SNew(SPinTypeSelector, FGetPinTypeTree::CreateUObject(K2Schema, &UEdGraphSchema_K2::GetVariableTypeTree))
|
|
.TargetPinType(this, &FBlueprintGraphLocalVariableLayout::OnGetVariableType)
|
|
#if UE_BP_LOCAL_VAR_LAYOUT_SETTERS_IMPLEMENTED
|
|
.OnPinTypePreChanged(this, &FBlueprintGraphLocalVariableLayout::OnPreVariableTypeChange)
|
|
.OnPinTypeChanged(this, &FBlueprintGraphLocalVariableLayout::VariableTypeChanged)
|
|
#endif
|
|
.Schema(K2Schema)
|
|
.bAllowArrays(!ShouldVarBeReadOnly())
|
|
.IsEnabled(!ShouldVarBeReadOnly(true))
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
.HAlign(HAlign_Right)
|
|
.VAlign(VAlign_Center)
|
|
.Padding(10, 0, 0, 0)
|
|
.AutoWidth()
|
|
#if UE_BP_LOCAL_VAR_LAYOUT_SETTERS_IMPLEMENTED
|
|
[
|
|
PropertyCustomizationHelpers::MakeClearButton(FSimpleDelegate::CreateSP(this, &FBlueprintGraphLocalVariableLayout::OnRemoveClicked), LOCTEXT("LocalVariableDetailsClearTooltip", "Remove this variable."), !IsVariableEditingReadOnly())
|
|
]
|
|
#endif
|
|
|
|
]
|
|
.PropertyHandleList({GetPropertyHandle()})
|
|
#if UE_BP_LOCAL_VAR_LAYOUT_SETTERS_IMPLEMENTED
|
|
.DragDropHandler(/* implement drag drop handler and add it here */);
|
|
static_assert(false)
|
|
#endif
|
|
;
|
|
}
|
|
|
|
void FBlueprintGraphLocalVariableLayout::GenerateChildContent(IDetailChildrenBuilder& ChildrenBuilder)
|
|
{
|
|
if (const UFunction* Function = OwningFunction.Get())
|
|
{
|
|
const TSharedPtr<FStructOnScope> StructData = MakeShareable(new FStructOnScope(Function));
|
|
|
|
// ensure that the default value is up to date inside property
|
|
for (TFieldIterator<FProperty> PropertyIterator(Function); PropertyIterator; ++PropertyIterator)
|
|
{
|
|
const FProperty *VariableProperty = *PropertyIterator;
|
|
if (VariableProperty->GetFName() == GetName())
|
|
{
|
|
FBlueprintEditorUtils::PropertyValueFromString(VariableProperty, GetVariable().DefaultValue, StructData->GetStructMemory());
|
|
break;
|
|
}
|
|
}
|
|
if (IDetailPropertyRow* Row = ChildrenBuilder.AddExternalStructureProperty(StructData.ToSharedRef(), GetName()))
|
|
{
|
|
Row->DisplayName(LOCTEXT("LocalVariableDefaultValue", "Default Value"));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FBlueprintGraphLocalVariableLayout::ShouldVarBeReadOnly(bool bIsEditingPinType) const
|
|
{
|
|
#if UE_BP_LOCAL_VAR_LAYOUT_SETTERS_IMPLEMENTED
|
|
// if this is ever made non-const, implement this method
|
|
static_assert(false);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool FBlueprintGraphLocalVariableLayout::IsVariableEditingReadOnly(bool bIsEditingPinType) const
|
|
{
|
|
#if UE_BP_LOCAL_VAR_LAYOUT_SETTERS_IMPLEMENTED
|
|
// if this is ever made non-const, implement this method
|
|
static_assert(false)
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
FText FBlueprintGraphLocalVariableLayout::OnGetVarNameText() const
|
|
{
|
|
return FText::FromName(GetName());
|
|
}
|
|
|
|
FText FBlueprintGraphLocalVariableLayout::OnGetVarToolTipText() const
|
|
{
|
|
const FBPVariableDescription& Variable = GetVariable();
|
|
const FText PinTypeText = UEdGraphSchema_K2::TypeToText(Variable.VarType);
|
|
return FText::Format(LOCTEXT("BlueprintArgToolTipText", "Name: {0}\nType: {1}"), FText::FromName(Variable.VarName), PinTypeText);
|
|
}
|
|
|
|
FEdGraphPinType FBlueprintGraphLocalVariableLayout::OnGetVariableType() const
|
|
{
|
|
return GetVariable().VarType;
|
|
}
|
|
|
|
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
void FBlueprintGraphActionDetails::CustomizeDetails( IDetailLayoutBuilder& DetailLayout )
|
|
{
|
|
DetailsLayoutPtr = &DetailLayout;
|
|
ObjectsBeingEdited = DetailsLayoutPtr->GetSelectedObjects();
|
|
|
|
SetEntryAndResultNodes();
|
|
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
UK2Node_EditablePinBase* FunctionResultNode = FunctionResultNodePtr.Get();
|
|
|
|
// Fill Access specifiers list
|
|
AccessSpecifierLabels.Empty(3);
|
|
AccessSpecifierLabels.Add( MakeShareable( new FAccessSpecifierLabel( AccessSpecifierProperName(FUNC_Public), FUNC_Public )));
|
|
AccessSpecifierLabels.Add( MakeShareable( new FAccessSpecifierLabel( AccessSpecifierProperName(FUNC_Protected), FUNC_Protected )));
|
|
AccessSpecifierLabels.Add( MakeShareable( new FAccessSpecifierLabel( AccessSpecifierProperName(FUNC_Private), FUNC_Private )));
|
|
|
|
const bool bHasAGraph = (GetGraph() != NULL);
|
|
|
|
if (FunctionEntryNode && FunctionEntryNode->IsEditable())
|
|
{
|
|
const bool bIsCustomEvent = IsCustomEvent();
|
|
const bool bIsFunctionGraph = FunctionEntryNode->IsA<UK2Node_FunctionEntry>();
|
|
|
|
IDetailCategoryBuilder& Category = DetailLayout.EditCategory("Graph", LOCTEXT("FunctionDetailsGraph", "Graph"));
|
|
if (bHasAGraph)
|
|
{
|
|
Category.AddCustomRow( LOCTEXT( "DefaultTooltip", "Description" ) )
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT( "DefaultTooltip", "Description" ) )
|
|
.ToolTipText(LOCTEXT("FunctionTooltipTooltip", "Enter a short message describing the purpose and operation of this graph"))
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SMultiLineEditableTextBox)
|
|
.Text( this, &FBlueprintGraphActionDetails::OnGetTooltipText )
|
|
.OnTextCommitted( this, &FBlueprintGraphActionDetails::OnTooltipTextCommitted )
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
.ModiferKeyForNewLine(EModifierKey::Shift)
|
|
];
|
|
|
|
// Composite graphs are auto-categorized into their parent graph
|
|
if(!GetGraph()->GetOuter()->GetClass()->IsChildOf(UK2Node_Composite::StaticClass()))
|
|
{
|
|
FBlueprintVarActionDetails::PopulateCategories(MyBlueprint.Pin().Get(), CategorySource);
|
|
TSharedPtr<SComboButton> NewComboButton;
|
|
TSharedPtr<SListView<TSharedPtr<FText>>> NewListView;
|
|
|
|
const FString DocLink = TEXT("Shared/Editors/BlueprintEditor/GraphDetails");
|
|
TSharedPtr<SToolTip> CategoryTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("EditGraphCategoryName_Tooltip", "The category of the graph; editing this will place the graph into another category or create a new one."), NULL, DocLink, TEXT("Category"));
|
|
|
|
Category.AddCustomRow( LOCTEXT("CategoryLabel", "Category") )
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT("CategoryLabel", "Category") )
|
|
.ToolTip(CategoryTooltip)
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SAssignNew(NewComboButton, SComboButton)
|
|
.ContentPadding(FMargin(0,0,5,0))
|
|
.ToolTip(CategoryTooltip)
|
|
.ButtonContent()
|
|
[
|
|
SNew(SBorder)
|
|
.BorderImage(FAppStyle::Get().GetBrush("NoBorder") )
|
|
.Padding(FMargin(0, 0, 5, 0))
|
|
[
|
|
SNew(SEditableTextBox)
|
|
.Text(this, &FBlueprintGraphActionDetails::OnGetCategoryText)
|
|
.OnTextCommitted(this, &FBlueprintGraphActionDetails::OnCategoryTextCommitted )
|
|
.ToolTip(CategoryTooltip)
|
|
.SelectAllTextWhenFocused(true)
|
|
.RevertTextOnEscape(true)
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
]
|
|
.MenuContent()
|
|
[
|
|
SNew(SVerticalBox)
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.MaxHeight(400.0f)
|
|
[
|
|
SAssignNew(NewListView, SListView<TSharedPtr<FText>>)
|
|
.ListItemsSource(&CategorySource)
|
|
.OnGenerateRow(this, &FBlueprintGraphActionDetails::MakeCategoryViewWidget)
|
|
.OnSelectionChanged(this, &FBlueprintGraphActionDetails::OnCategorySelectionChanged)
|
|
]
|
|
]
|
|
];
|
|
|
|
CategoryComboButton = NewComboButton;
|
|
CategoryListView = NewListView;
|
|
|
|
TSharedPtr<SToolTip> KeywordsTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("EditKeywords_Tooltip", "Keywords for searching for the function or macro."), NULL, DocLink, TEXT("Keywords"));
|
|
Category.AddCustomRow( LOCTEXT("KeywordsLabel", "Keywords") )
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT("KeywordsLabel", "Keywords") )
|
|
.ToolTip(KeywordsTooltip)
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SEditableTextBox)
|
|
.Text(this, &FBlueprintGraphActionDetails::OnGetKeywordsText)
|
|
.OnTextCommitted(this, &FBlueprintGraphActionDetails::OnKeywordsTextCommitted )
|
|
.ToolTip(KeywordsTooltip)
|
|
.RevertTextOnEscape(true)
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
];
|
|
|
|
TSharedPtr<SToolTip> CompactNodeTitleTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("EditCompactNodeTitle_Tooltip", "Sets the compact node title for calls to this function or macro. Compact node titles convert a node to display as a compact node and are used as a keyword for searching."), NULL, DocLink, TEXT("Compact Node Title"));
|
|
Category.AddCustomRow( LOCTEXT("CompactNodeTitleLabel", "Compact Node Title") )
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT("CompactNodeTitleLabel", "Compact Node Title") )
|
|
.ToolTip(CompactNodeTitleTooltip)
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SEditableTextBox)
|
|
.Text(this, &FBlueprintGraphActionDetails::OnGetCompactNodeTitleText)
|
|
.OnTextCommitted(this, &FBlueprintGraphActionDetails::OnCompactNodeTitleTextCommitted )
|
|
.ToolTip(CompactNodeTitleTooltip)
|
|
.RevertTextOnEscape(true)
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
];
|
|
}
|
|
|
|
UBlueprint* BlueprintPtr = GetBlueprintObj();
|
|
|
|
if (BlueprintPtr && IsFieldNotifyCheckVisible())
|
|
{
|
|
const FText ToolTip = LOCTEXT("FieldNotifyToolTip", "Generate a field entry for the Field Notification system.");
|
|
const FString DocLink = TEXT("Shared/Editors/BlueprintEditor/GraphDetails");
|
|
TSharedPtr<SToolTip> FieldNotificationTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("FieldNotifyToolTip", "Generate a field entry for the Field Notification system."), NULL, DocLink, TEXT("FieldNotify"));
|
|
|
|
Category.AddCustomRow(LOCTEXT("IsFunctionFieldNotifyLabel", "Field Notify"))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("IsFunctionFieldNotifyLabel", "Field Notify"))
|
|
.ToolTip(FieldNotificationTooltip)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked(this, &FBlueprintGraphActionDetails::OnFieldNotifyCheckboxState)
|
|
.OnCheckStateChanged(this, &FBlueprintGraphActionDetails::OnFieldNotifyChanged)
|
|
.IsEnabled(this, &FBlueprintGraphActionDetails::GetIsFieldNotfyEnabled)
|
|
.ToolTip(FieldNotificationTooltip)
|
|
];
|
|
}
|
|
|
|
if (GetInstanceColorVisibility())
|
|
{
|
|
Category.AddCustomRow( LOCTEXT( "InstanceColor", "Instance Color" ) )
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT( "InstanceColor", "Instance Color" ) )
|
|
.ToolTipText( LOCTEXT("FunctionColorTooltip", "Choose a title bar color for references of this graph") )
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SAssignNew( ColorBlock, SColorBlock )
|
|
.Color( this, &FBlueprintGraphActionDetails::GetNodeTitleColor )
|
|
.CornerRadius(FVector4(4.0f, 4.0f, 4.0f, 4.0f))
|
|
.Size(FVector2D(70.0f, 22.0f))
|
|
.AlphaDisplayMode(EColorBlockAlphaDisplayMode::Ignore)
|
|
.OnMouseButtonDown( this, &FBlueprintGraphActionDetails::ColorBlock_OnMouseButtonDown )
|
|
];
|
|
}
|
|
if (IsPureFunctionVisible())
|
|
{
|
|
Category.AddCustomRow( LOCTEXT( "FunctionPure_Tooltip", "Pure" ) )
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT( "FunctionPure_Tooltip", "Pure" ) )
|
|
.ToolTipText( LOCTEXT("FunctionIsPure_Tooltip", "Force this to be a pure function?") )
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew( SCheckBox )
|
|
.IsChecked( this, &FBlueprintGraphActionDetails::GetIsPureFunction )
|
|
.OnCheckStateChanged( this, &FBlueprintGraphActionDetails::OnIsPureFunctionModified )
|
|
];
|
|
}
|
|
if (IsConstFunctionVisible())
|
|
{
|
|
Category.AddCustomRow( LOCTEXT( "FunctionConst_Tooltip", "Const" ), true )
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT( "FunctionConst_Tooltip", "Const" ) )
|
|
.ToolTipText( LOCTEXT("FunctionIsConst_Tooltip", "Force this to be a const function?") )
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew( SCheckBox )
|
|
.IsChecked( this, &FBlueprintGraphActionDetails::GetIsConstFunction )
|
|
.OnCheckStateChanged( this, &FBlueprintGraphActionDetails::OnIsConstFunctionModified )
|
|
];
|
|
}
|
|
if (IsExecFunctionVisible())
|
|
{
|
|
Category.AddCustomRow( LOCTEXT( "FunctionExec_Tooltip", "Exec" ), true )
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT( "FunctionExec_Tooltip", "Exec" ) )
|
|
.ToolTipText( LOCTEXT("FunctionIsExec_Tooltip", "Cause this function to be able to process console commands?") )
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew( SCheckBox )
|
|
.IsChecked( this, &FBlueprintGraphActionDetails::GetIsExecFunction )
|
|
.OnCheckStateChanged( this, &FBlueprintGraphActionDetails::OnIsExecFunctionModified )
|
|
];
|
|
}
|
|
if (IsThreadSafeFunctionVisible())
|
|
{
|
|
Category.AddCustomRow( LOCTEXT( "FunctionThreadSafe_Tooltip", "Thread Safe" ), true )
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT( "FunctionThreadSafe_Tooltip", "Thread Safe" ) )
|
|
.ToolTipText( LOCTEXT("FunctionIsThreadSafe_Tooltip", "Enable thread-safety checks on this function. Only thread-safe functions and operations are allowed in this function.") )
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew( SCheckBox )
|
|
.IsChecked( this, &FBlueprintGraphActionDetails::GetIsThreadSafeFunction )
|
|
.OnCheckStateChanged( this, &FBlueprintGraphActionDetails::OnIsThreadSafeFunctionModified )
|
|
];
|
|
}
|
|
if (IsUnsafeDuringActorConstructionVisible())
|
|
{
|
|
Category.AddCustomRow(LOCTEXT("FunctionUnsafeDuringActorConstruction_Tooltip", "Unsafe During Actor Construction"), true)
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("FunctionUnsafeDuringActorConstruction_Tooltip", "Unsafe During Actor Construction"))
|
|
.ToolTipText(LOCTEXT("FunctionIsUnsafeDuringActorConstruction_Tooltip", "Mark this function as unsafe during actor construction so that a warning is generated when it is called by a Construction Script - useful when calling native functions that are also unsafe during construction"))
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked(this, &FBlueprintGraphActionDetails::GetIsUnsafeDuringActorConstruction)
|
|
.OnCheckStateChanged(this, &FBlueprintGraphActionDetails::OnIsUnsafeDuringActorConstructionModified)
|
|
];
|
|
}
|
|
}
|
|
|
|
if (bIsCustomEvent)
|
|
{
|
|
/** A collection of static utility callbacks to provide the custom-event details ui with */
|
|
struct LocalCustomEventUtils
|
|
{
|
|
/** Checks to see if the selected node is NOT an override */
|
|
static bool IsNotCustomEventOverride(TWeakObjectPtr<UK2Node_EditablePinBase> SelectedNode)
|
|
{
|
|
bool bIsOverride = false;
|
|
if (SelectedNode.IsValid())
|
|
{
|
|
UK2Node_CustomEvent const* SelectedCustomEvent = Cast<UK2Node_CustomEvent const>(SelectedNode.Get());
|
|
check(SelectedCustomEvent != nullptr);
|
|
|
|
bIsOverride = SelectedCustomEvent->IsOverride();
|
|
}
|
|
|
|
return !bIsOverride;
|
|
}
|
|
|
|
/** If the selected node represent an override, this returns tooltip text explaining why you can't alter the replication settings */
|
|
static FText GetDisabledTooltip(TWeakObjectPtr<UK2Node_EditablePinBase> SelectedNode)
|
|
{
|
|
FText ToolTipOut = FText::GetEmpty();
|
|
if (!IsNotCustomEventOverride(SelectedNode))
|
|
{
|
|
ToolTipOut = LOCTEXT("CannotChangeOverrideReplication", "Cannot alter a custom-event's replication settings when it overrides an event declared in a parent.");
|
|
}
|
|
return ToolTipOut;
|
|
}
|
|
|
|
/** Determines if the selected node's "Reliable" net setting should be enabled for the user */
|
|
static bool CanSetReliabilityProperty(TWeakObjectPtr<UK2Node_EditablePinBase> SelectedNode)
|
|
{
|
|
bool bIsReliabilitySettingEnabled = false;
|
|
if (IsNotCustomEventOverride(SelectedNode) && SelectedNode.IsValid())
|
|
{
|
|
UK2Node_CustomEvent const* SelectedCustomEvent = Cast<UK2Node_CustomEvent const>(SelectedNode.Get());
|
|
check(SelectedCustomEvent != nullptr);
|
|
|
|
bIsReliabilitySettingEnabled = ((SelectedCustomEvent->GetNetFlags() & FUNC_Net) != 0);
|
|
}
|
|
return bIsReliabilitySettingEnabled;
|
|
}
|
|
};
|
|
FCanExecuteAction CanExecuteDelegate = FCanExecuteAction::CreateStatic(&LocalCustomEventUtils::IsNotCustomEventOverride, FunctionEntryNodePtr);
|
|
|
|
FMenuBuilder RepComboMenu( true, NULL );
|
|
RepComboMenu.AddMenuEntry( ReplicationSpecifierProperName(0),
|
|
LOCTEXT("NotReplicatedToolTip", "This event is not replicated to anyone."),
|
|
FSlateIcon(),
|
|
FUIAction(FExecuteAction::CreateStatic( &FBlueprintGraphActionDetails::SetNetFlags, FunctionEntryNodePtr, 0U ), CanExecuteDelegate));
|
|
RepComboMenu.AddMenuEntry( ReplicationSpecifierProperName(FUNC_NetMulticast),
|
|
LOCTEXT("MulticastToolTip", "Replicate this event from the server to everyone else. Server executes this event locally too. Only call this from the server."),
|
|
FSlateIcon(),
|
|
FUIAction(FExecuteAction::CreateStatic( &FBlueprintGraphActionDetails::SetNetFlags, FunctionEntryNodePtr, static_cast<uint32>(FUNC_NetMulticast) ), CanExecuteDelegate));
|
|
RepComboMenu.AddMenuEntry( ReplicationSpecifierProperName(FUNC_NetServer),
|
|
LOCTEXT("ServerToolTip", "Replicate this event from net owning client to server."),
|
|
FSlateIcon(),
|
|
FUIAction(FExecuteAction::CreateStatic( &FBlueprintGraphActionDetails::SetNetFlags, FunctionEntryNodePtr, static_cast<uint32>(FUNC_NetServer) ), CanExecuteDelegate));
|
|
RepComboMenu.AddMenuEntry( ReplicationSpecifierProperName(FUNC_NetClient),
|
|
LOCTEXT("ClientToolTip", "Replicate this event from the server to owning client."),
|
|
FSlateIcon(),
|
|
FUIAction(FExecuteAction::CreateStatic( &FBlueprintGraphActionDetails::SetNetFlags, FunctionEntryNodePtr, static_cast<uint32>(FUNC_NetClient) ), CanExecuteDelegate));
|
|
|
|
const FString DocLink = TEXT("Shared/Editors/BlueprintEditor/GraphDetails");
|
|
TSharedPtr<SToolTip> KeywordsTooltip = IDocumentation::Get()->CreateToolTip(LOCTEXT("EditEventKeywords_Tooltip", "Keywords for searching for the event."), nullptr, DocLink, TEXT("Keywords"));
|
|
Category.AddCustomRow(LOCTEXT("EventsKeywordsLabel", "Keywords"))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("KeywordsLabel", "Keywords"))
|
|
.ToolTip(KeywordsTooltip)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SEditableTextBox)
|
|
.Text(this, &FBlueprintGraphActionDetails::OnGetKeywordsText)
|
|
.OnTextCommitted(this, &FBlueprintGraphActionDetails::OnKeywordsTextCommitted)
|
|
.ToolTip(KeywordsTooltip)
|
|
.RevertTextOnEscape(true)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
];
|
|
|
|
|
|
Category.AddCustomRow( LOCTEXT( "FunctionReplicate", "Replicates" ) )
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT( "FunctionReplicate", "Replicates" ) )
|
|
.ToolTipText( LOCTEXT("FunctionReplicate_Tooltip", "Should this Event be replicated to all clients when called on the server?") )
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SVerticalBox)
|
|
+SVerticalBox::Slot()
|
|
[
|
|
SNew(SComboButton)
|
|
.ContentPadding(0.0f)
|
|
.IsEnabled_Static(&LocalCustomEventUtils::IsNotCustomEventOverride, FunctionEntryNodePtr)
|
|
.ToolTipText_Static(&LocalCustomEventUtils::GetDisabledTooltip, FunctionEntryNodePtr)
|
|
.ButtonContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &FBlueprintGraphActionDetails::GetCurrentReplicatedEventString)
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
.MenuContent()
|
|
[
|
|
SNew(SVerticalBox)
|
|
+SVerticalBox::Slot()
|
|
[
|
|
SNew(SVerticalBox)
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.MaxHeight(400.0f)
|
|
[
|
|
RepComboMenu.MakeWidget()
|
|
]
|
|
]
|
|
]
|
|
]
|
|
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.MaxHeight(400.0f)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
SNew( SCheckBox )
|
|
.IsChecked( this, &FBlueprintGraphActionDetails::GetIsReliableReplicatedFunction )
|
|
.IsEnabled_Static(&LocalCustomEventUtils::CanSetReliabilityProperty, FunctionEntryNodePtr)
|
|
.ToolTipText_Static(&LocalCustomEventUtils::GetDisabledTooltip, FunctionEntryNodePtr)
|
|
.OnCheckStateChanged( this, &FBlueprintGraphActionDetails::OnIsReliableReplicationFunctionModified )
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT( "FunctionReplicateReliable", "Reliable" ) )
|
|
]
|
|
]
|
|
]
|
|
];
|
|
}
|
|
|
|
const bool bShowCallInEditor = bIsCustomEvent || bIsFunctionGraph;
|
|
if( bShowCallInEditor )
|
|
{
|
|
Category.AddCustomRow( LOCTEXT( "EditorCallable", "Call In Editor" ) )
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT( "EditorCallable", "Call In Editor" ) )
|
|
.ToolTipText( LOCTEXT("EditorCallable_Tooltip", "Enable this event to be called from within the editor") )
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SVerticalBox)
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
SNew( SCheckBox )
|
|
.IsChecked( this, &FBlueprintGraphActionDetails::GetIsEditorCallableEvent )
|
|
.ToolTipText( LOCTEXT("EditorCallable_Tooltip", "Enable this event to be called from within the editor" ))
|
|
.OnCheckStateChanged( this, &FBlueprintGraphActionDetails::OnEditorCallableEventModified )
|
|
]
|
|
]
|
|
];
|
|
}
|
|
|
|
const bool bShowDeprecated = bIsCustomEvent || bIsFunctionGraph;
|
|
if (bShowDeprecated)
|
|
{
|
|
FFormatNamedArguments DeprecationTooltipFormatArgs;
|
|
if (bIsFunctionGraph)
|
|
{
|
|
DeprecationTooltipFormatArgs.Add(TEXT("FunctionOrCustomEvent"), LOCTEXT("FunctionOrEvent_Function", "function"));
|
|
}
|
|
else
|
|
{
|
|
DeprecationTooltipFormatArgs.Add(TEXT("FunctionOrCustomEvent"), LOCTEXT("FunctionOrEvent_CustomEvent", "custom event"));
|
|
}
|
|
|
|
bool bIsOverride = false;
|
|
FFunctionFromNodeHelper FunctionFromNode(FunctionEntryNode);
|
|
if (FunctionFromNode.Function)
|
|
{
|
|
bIsOverride = (UEdGraphSchema_K2::GetCallableParentFunction(FunctionFromNode.Function) != nullptr);
|
|
}
|
|
|
|
const FText DeprecatedTooltipText = FText::Format(LOCTEXT("DeprecatedFunction_Tooltip", "Deprecate usage of this {FunctionOrCustomEvent}. Any nodes that reference it will produce a compiler warning indicating that it should be removed or replaced."), DeprecationTooltipFormatArgs);
|
|
|
|
Category.AddCustomRow(LOCTEXT("DeprecatedFunction", "Deprecated"), true)
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("DeprecatedFunction", "Deprecated"))
|
|
.ToolTipText(DeprecatedTooltipText)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsChecked(this, &FBlueprintGraphActionDetails::OnGetDeprecatedCheckboxState)
|
|
.ToolTipText(DeprecatedTooltipText)
|
|
.OnCheckStateChanged(this, &FBlueprintGraphActionDetails::OnDeprecatedChanged)
|
|
.IsEnabled(!bIsOverride)
|
|
];
|
|
|
|
const FText DeprecationMessageTooltipText = LOCTEXT("DeprecationMessage_Tooltip", "Optional message to include with the deprecation compiler warning. For example: \'X is no longer being used. Please replace with Y.\'");
|
|
|
|
Category.AddCustomRow(LOCTEXT("DeprecationMessage", "Deprecation Message"), true)
|
|
.IsEnabled(TAttribute<bool>(this, &FBlueprintGraphActionDetails::IsFunctionDeprecated))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("DeprecationMessage", "Deprecation Message"))
|
|
.ToolTipText(DeprecationMessageTooltipText)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SEditableTextBox)
|
|
.Text(this, &FBlueprintGraphActionDetails::GetDeprecationMessageText)
|
|
.OnTextCommitted(this, &FBlueprintGraphActionDetails::OnDeprecationMessageTextCommitted)
|
|
.IsEnabled(!bIsOverride)
|
|
.ToolTipText(this, &FBlueprintGraphActionDetails::GetDeprecationMessageText)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
];
|
|
}
|
|
|
|
const bool bShowAccessSpecifiers = bIsCustomEvent || bIsFunctionGraph;
|
|
if (IsAccessSpecifierVisible())
|
|
{
|
|
Category.AddCustomRow(LOCTEXT("AccessSpecifier", "Access Specifier"))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("AccessSpecifier", "Access Specifier"))
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SAssignNew(AccessSpecifierComboButton, SComboButton)
|
|
.ContentPadding(0.0f)
|
|
.ButtonContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &FBlueprintGraphActionDetails::GetCurrentAccessSpecifierName)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.MenuContent()
|
|
[
|
|
SNew(SListView<TSharedPtr<FAccessSpecifierLabel> >)
|
|
.ListItemsSource(&AccessSpecifierLabels)
|
|
.OnGenerateRow(this, &FBlueprintGraphActionDetails::HandleGenerateRowAccessSpecifier)
|
|
.OnSelectionChanged(this, &FBlueprintGraphActionDetails::OnAccessSpecifierSelected)
|
|
]
|
|
];
|
|
}
|
|
|
|
IDetailCategoryBuilder& InputsCategory = DetailLayout.EditCategory("Inputs", LOCTEXT("FunctionDetailsInputs", "Inputs"));
|
|
|
|
TSharedRef<FBlueprintGraphArgumentGroupLayout> InputArgumentGroup =
|
|
MakeShareable(new FBlueprintGraphArgumentGroupLayout(SharedThis(this), FunctionEntryNode));
|
|
InputsCategory.AddCustomBuilder(InputArgumentGroup);
|
|
|
|
TSharedRef<SHorizontalBox> InputsHeaderContentWidget = SNew(SHorizontalBox);
|
|
TWeakPtr<SWidget> WeakInputsHeaderWidget = InputsHeaderContentWidget;
|
|
|
|
InputsHeaderContentWidget->AddSlot()
|
|
.HAlign(HAlign_Right)
|
|
[
|
|
|
|
SNew(SButton)
|
|
.ButtonStyle(FAppStyle::Get(), "SimpleButton")
|
|
.ContentPadding(FMargin(1, 0))
|
|
.OnClicked(this, &FBlueprintGraphActionDetails::OnAddNewInputClicked)
|
|
.Visibility(this, &FBlueprintGraphActionDetails::GetAddNewInputOutputVisibility)
|
|
.HAlign(HAlign_Right)
|
|
.ToolTipText(LOCTEXT("FunctionNewInputArgTooltip", "Create a new input argument"))
|
|
.VAlign(VAlign_Center)
|
|
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("FunctionNewInputArg")))
|
|
.IsEnabled(this, &FBlueprintGraphActionDetails::IsAddNewInputOutputEnabled)
|
|
[
|
|
SNew(SImage)
|
|
.Image(FAppStyle::Get().GetBrush("Icons.PlusCircle"))
|
|
.ColorAndOpacity(FSlateColor::UseForeground())
|
|
]
|
|
];
|
|
InputsCategory.HeaderContent(InputsHeaderContentWidget);
|
|
|
|
if (bHasAGraph)
|
|
{
|
|
IDetailCategoryBuilder& OutputsCategory = DetailLayout.EditCategory("Outputs", LOCTEXT("FunctionDetailsOutputs", "Outputs"));
|
|
|
|
TSharedRef<FBlueprintGraphArgumentGroupLayout> OutputArgumentGroup =
|
|
MakeShareable(new FBlueprintGraphArgumentGroupLayout(SharedThis(this), FunctionResultNode));
|
|
OutputsCategory.AddCustomBuilder(OutputArgumentGroup);
|
|
|
|
TSharedRef<SHorizontalBox> OutputsHeaderContentWidget = SNew(SHorizontalBox);
|
|
TWeakPtr<SWidget> WeakOutputsHeaderWidget = OutputsHeaderContentWidget;
|
|
|
|
OutputsHeaderContentWidget->AddSlot()
|
|
.HAlign(HAlign_Right)
|
|
.Padding(FMargin(0,0,2,0))
|
|
[
|
|
SNew(SButton)
|
|
.ButtonStyle(FAppStyle::Get(), "SimpleButton")
|
|
.ContentPadding(FMargin(1, 0))
|
|
.OnClicked(this, &FBlueprintGraphActionDetails::OnAddNewOutputClicked)
|
|
.Visibility(this, &FBlueprintGraphActionDetails::GetAddNewInputOutputVisibility)
|
|
.HAlign(HAlign_Right)
|
|
.ToolTipText(LOCTEXT("FunctionNewOutputArgTooltip", "Create a new output argument"))
|
|
.VAlign(VAlign_Center)
|
|
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("FunctionNewOutputArg")))
|
|
.IsEnabled(this, &FBlueprintGraphActionDetails::IsAddNewInputOutputEnabled)
|
|
[
|
|
SNew(SImage)
|
|
.Image(FAppStyle::Get().GetBrush("Icons.PlusCircle"))
|
|
.ColorAndOpacity(FSlateColor::UseForeground())
|
|
]
|
|
];
|
|
OutputsCategory.HeaderContent(OutputsHeaderContentWidget);
|
|
}
|
|
|
|
if (bShowLocalVariables)
|
|
{
|
|
const UEdGraph* TopLevelGraph = FBlueprintEditorUtils::GetTopLevelGraph(GetGraph());
|
|
TSharedPtr<IPropertyHandle> LocalVariablesProperty = nullptr;
|
|
if (TopLevelGraph)
|
|
{
|
|
LocalVariablesProperty = DetailLayout.AddObjectPropertyData({FunctionEntryNode}, TEXT("LocalVariables"));
|
|
}
|
|
|
|
IDetailCategoryBuilder& LocalVarsCategory = DetailLayout.EditCategory("Local Variables", LOCTEXT("FunctionDetailsLocalVariables", "Local Variables"));
|
|
TSharedRef<FBlueprintGraphLocalVariableGroupLayout> LocalVarsArgumentGroup =
|
|
MakeShareable(new FBlueprintGraphLocalVariableGroupLayout(SharedThis(this), GetGraph(), GetBlueprintObj(), FindFunction(), LocalVariablesProperty));
|
|
LocalVarsCategory.AddCustomBuilder(LocalVarsArgumentGroup);
|
|
|
|
TSharedRef<SHorizontalBox> LocalVarsHeaderContentWidget = SNew(SHorizontalBox);
|
|
|
|
LocalVarsCategory.HeaderContent(LocalVarsHeaderContentWidget);
|
|
}
|
|
|
|
// See if anything else wants to customize our details
|
|
TWeakPtr<FBlueprintEditor> BlueprintEditor = MyBlueprint.Pin()->GetBlueprintEditor();
|
|
FBlueprintEditorModule& BlueprintEditorModule = FModuleManager::GetModuleChecked<FBlueprintEditorModule>("Kismet");
|
|
TArray<TSharedPtr<IDetailCustomization>> Customizations = BlueprintEditorModule.CustomizeFunction(FunctionEntryNode->GetClass(), BlueprintEditor.Pin());
|
|
ExternalDetailCustomizations.Append(Customizations);
|
|
if (ExternalDetailCustomizations.Num() > 0)
|
|
{
|
|
for (TSharedPtr<IDetailCustomization> ExternalDetailCustomization : ExternalDetailCustomizations)
|
|
{
|
|
ExternalDetailCustomization->CustomizeDetails(DetailLayout);
|
|
}
|
|
}
|
|
}
|
|
else if (bHasAGraph)
|
|
{
|
|
// See if anything else wants to customize our details
|
|
TWeakPtr<FBlueprintEditor> BlueprintEditor = MyBlueprint.Pin()->GetBlueprintEditor();
|
|
FBlueprintEditorModule& BlueprintEditorModule = FModuleManager::GetModuleChecked<FBlueprintEditorModule>("Kismet");
|
|
TArray<TSharedPtr<IDetailCustomization>> Customizations = BlueprintEditorModule.CustomizeGraph(GetGraph()->GetSchema(), BlueprintEditor.Pin());
|
|
ExternalDetailCustomizations.Append(Customizations);
|
|
if(ExternalDetailCustomizations.Num() > 0)
|
|
{
|
|
for (TSharedPtr<IDetailCustomization> ExternalDetailCustomization : ExternalDetailCustomizations)
|
|
{
|
|
ExternalDetailCustomization->CustomizeDetails(DetailLayout);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IDetailCategoryBuilder& Category = DetailLayout.EditCategory("Graph", LOCTEXT("FunctionDetailsGraph", "Graph"));
|
|
Category.AddCustomRow( FText::GetEmpty() )
|
|
.WholeRowContent()
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT("GraphPresentButNotEditable", "Graph is not editable.") )
|
|
];
|
|
}
|
|
}
|
|
|
|
if (MyBlueprint.IsValid())
|
|
{
|
|
TWeakPtr<FBlueprintEditor> BlueprintEditor = MyBlueprint.Pin()->GetBlueprintEditor();
|
|
if (BlueprintEditor.IsValid())
|
|
{
|
|
BlueprintEditorRefreshDelegateHandle = BlueprintEditor.Pin()->OnRefresh().AddSP(this, &FBlueprintGraphActionDetails::OnPostEditorRefresh);
|
|
}
|
|
}
|
|
}
|
|
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
|
|
bool FBlueprintGraphActionDetails::IsFieldNotifyCheckVisible() const
|
|
{
|
|
bool bSupportedType = false;
|
|
bool bIsEditable = false;
|
|
bool bImplementsFieldNotify = false;
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
UFunction* Function = FindFunction();
|
|
|
|
if (FunctionEntryNode && Function)
|
|
{
|
|
UBlueprint* Blueprint = FunctionEntryNode->GetBlueprint();
|
|
const bool bIsInterface = FBlueprintEditorUtils::IsInterfaceBlueprint(Blueprint);
|
|
bImplementsFieldNotify = FBlueprintEditorUtils::ImplementsInterface(Blueprint, true, UNotifyFieldValueChanged::StaticClass());
|
|
|
|
bSupportedType = !bIsInterface && FunctionEntryNode->IsA<UK2Node_FunctionEntry>();
|
|
bIsEditable = FunctionEntryNode->IsEditable();
|
|
}
|
|
return bSupportedType && bIsEditable && bImplementsFieldNotify;
|
|
}
|
|
|
|
bool FBlueprintGraphActionDetails::GetIsFieldNotfyEnabled() const
|
|
{
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
UK2Node_EditablePinBase* FunctionResultNode = FunctionResultNodePtr.Get();
|
|
|
|
if (FunctionEntryNode && FunctionResultNode)
|
|
{
|
|
return GetIsConstFunction() == ECheckBoxState::Checked && GetIsPureFunction() == ECheckBoxState::Checked && FunctionEntryNode->GetAllPins().Num() == 1 && FunctionResultNode->GetAllPins().Num() == 2;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ECheckBoxState FBlueprintGraphActionDetails::OnFieldNotifyCheckboxState() const
|
|
{
|
|
UBlueprint* const BlueprintObj = GetBlueprintObj();
|
|
|
|
if (UFunction* Function = FindFunction())
|
|
{
|
|
const FName FuncName = Function->GetFName();
|
|
|
|
if (BlueprintObj && GetIsFieldNotfyEnabled())
|
|
{
|
|
if (!FuncName.IsNone())
|
|
{
|
|
return GetMetadataBlock()->HasMetaData(FBlueprintMetadata::MD_FieldNotify) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
else if (BlueprintObj->GeneratedClass && BlueprintObj->GeneratedClass->ImplementsInterface(UNotifyFieldValueChanged::StaticClass()) && BlueprintObj->GeneratedClass->GetDefaultObject())
|
|
{
|
|
TScriptInterface<INotifyFieldValueChanged> DefaultObject = BlueprintObj->GeneratedClass->GetDefaultObject();
|
|
return DefaultObject->GetFieldNotificationDescriptor().GetField(BlueprintObj->GeneratedClass, FuncName).IsValid() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
}
|
|
|
|
FBlueprintEditorUtils::RemoveFieldNotifyFromAllMetadata(BlueprintObj, FuncName);
|
|
GetMetadataBlock()->RemoveMetaData(FBlueprintMetadata::MD_FieldNotify);
|
|
}
|
|
return ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
void FBlueprintGraphActionDetails::OnFieldNotifyChanged(ECheckBoxState InNewState)
|
|
{
|
|
UBlueprint* const BlueprintObj = GetBlueprintObj();
|
|
if (UFunction* Function = FindFunction())
|
|
{
|
|
const FName FuncName = Function->GetFName();
|
|
const bool bFuncIsFieldNotify = InNewState == ECheckBoxState::Checked;
|
|
|
|
if (BlueprintObj)
|
|
{
|
|
if (bFuncIsFieldNotify)
|
|
{
|
|
GetMetadataBlock()->SetMetaData(FBlueprintMetadata::MD_FieldNotify, FString());
|
|
}
|
|
else
|
|
{
|
|
FBlueprintEditorUtils::RemoveFieldNotifyFromAllMetadata(BlueprintObj, FuncName);
|
|
GetMetadataBlock()->RemoveMetaData(FBlueprintMetadata::MD_FieldNotify);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TSharedRef<ITableRow> FBlueprintGraphActionDetails::OnGenerateReplicationComboWidget( TSharedPtr<FReplicationSpecifierLabel> InNetFlag, const TSharedRef<STableViewBase>& OwnerTable )
|
|
{
|
|
return
|
|
SNew(STableRow< TSharedPtr<FString> >, OwnerTable)
|
|
[
|
|
SNew( STextBlock )
|
|
.Text( InNetFlag.IsValid() ? InNetFlag.Get()->LocalizedName : FText::GetEmpty() )
|
|
.ToolTipText( InNetFlag.IsValid() ? InNetFlag.Get()->LocalizedToolTip : FText::GetEmpty() )
|
|
];
|
|
}
|
|
|
|
void FBlueprintGraphActionDetails::SetNetFlags( TWeakObjectPtr<UK2Node_EditablePinBase> FunctionEntryNode, uint32 NetFlags)
|
|
{
|
|
if( FunctionEntryNode.IsValid() )
|
|
{
|
|
const int32 FlagsToSet = NetFlags ? FUNC_Net|NetFlags : 0;
|
|
const int32 FlagsToClear = FUNC_Net|FUNC_NetMulticast|FUNC_NetServer|FUNC_NetClient;
|
|
// Clear all net flags before setting
|
|
if( FlagsToSet != FlagsToClear )
|
|
{
|
|
const FScopedTransaction Transaction( LOCTEXT("GraphSetNetFlags", "Change Replication") );
|
|
FunctionEntryNode->Modify();
|
|
|
|
bool bBlueprintModified = false;
|
|
|
|
if (UK2Node_FunctionEntry* TypedEntryNode = Cast<UK2Node_FunctionEntry>(FunctionEntryNode.Get()))
|
|
{
|
|
int32 ExtraFlags = TypedEntryNode->GetExtraFlags();
|
|
ExtraFlags &= ~FlagsToClear;
|
|
ExtraFlags |= FlagsToSet;
|
|
TypedEntryNode->SetExtraFlags(ExtraFlags);
|
|
bBlueprintModified = true;
|
|
}
|
|
if (UK2Node_CustomEvent* CustomEventNode = Cast<UK2Node_CustomEvent>(FunctionEntryNode.Get()))
|
|
{
|
|
CustomEventNode->FunctionFlags &= ~FlagsToClear;
|
|
CustomEventNode->FunctionFlags |= FlagsToSet;
|
|
bBlueprintModified = true;
|
|
}
|
|
|
|
if( bBlueprintModified )
|
|
{
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified( FunctionEntryNode->GetBlueprint() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FText FBlueprintGraphActionDetails::GetCurrentReplicatedEventString() const
|
|
{
|
|
const UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
const UK2Node_CustomEvent* CustomEvent = Cast<const UK2Node_CustomEvent>(FunctionEntryNode);
|
|
|
|
uint32 const ReplicatedNetMask = (FUNC_NetMulticast | FUNC_NetServer | FUNC_NetClient);
|
|
|
|
FText ReplicationText;
|
|
|
|
if(CustomEvent)
|
|
{
|
|
uint32 NetFlags = CustomEvent->FunctionFlags & ReplicatedNetMask;
|
|
if (CustomEvent->IsOverride())
|
|
{
|
|
UFunction* SuperFunction = FindUField<UFunction>(CustomEvent->GetBlueprint()->ParentClass, CustomEvent->CustomFunctionName);
|
|
check(SuperFunction != NULL);
|
|
|
|
NetFlags = SuperFunction->FunctionFlags & ReplicatedNetMask;
|
|
}
|
|
ReplicationText = ReplicationSpecifierProperName(NetFlags);
|
|
}
|
|
return ReplicationText;
|
|
}
|
|
|
|
bool FBaseBlueprintGraphActionDetails::AttemptToCreateResultNode()
|
|
{
|
|
if (!FunctionResultNodePtr.IsValid())
|
|
{
|
|
FunctionResultNodePtr = FBlueprintEditorUtils::FindOrCreateFunctionResultNode(FunctionEntryNodePtr.Get());
|
|
}
|
|
return FunctionResultNodePtr.IsValid();
|
|
}
|
|
|
|
FBaseBlueprintGraphActionDetails::~FBaseBlueprintGraphActionDetails()
|
|
{
|
|
if (BlueprintEditorRefreshDelegateHandle.IsValid() && MyBlueprint.IsValid())
|
|
{
|
|
// Remove the callback delegate we registered for
|
|
TWeakPtr<FBlueprintEditor> BlueprintEditor = MyBlueprint.Pin()->GetBlueprintEditor();
|
|
if (BlueprintEditor.IsValid())
|
|
{
|
|
BlueprintEditor.Pin()->OnRefresh().Remove(BlueprintEditorRefreshDelegateHandle);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FBaseBlueprintGraphActionDetails::OnPostEditorRefresh()
|
|
{
|
|
/** Blueprint changed, need to refresh inputs in case pin UI changed */
|
|
RegenerateInputsChildrenDelegate.ExecuteIfBound();
|
|
RegenerateOutputsChildrenDelegate.ExecuteIfBound();
|
|
}
|
|
|
|
void FBaseBlueprintGraphActionDetails::SetRefreshDelegate(FSimpleDelegate RefreshDelegate, bool bForInputs)
|
|
{
|
|
((bForInputs) ? RegenerateInputsChildrenDelegate : RegenerateOutputsChildrenDelegate) = RefreshDelegate;
|
|
}
|
|
|
|
ECheckBoxState FBlueprintGraphActionDetails::GetIsEditorCallableEvent() const
|
|
{
|
|
ECheckBoxState Result = ECheckBoxState::Unchecked;
|
|
|
|
if( FunctionEntryNodePtr.IsValid() )
|
|
{
|
|
if( UK2Node_CustomEvent* CustomEventNode = Cast<UK2Node_CustomEvent>(FunctionEntryNodePtr.Get()))
|
|
{
|
|
if( CustomEventNode->bCallInEditor )
|
|
{
|
|
Result = ECheckBoxState::Checked;
|
|
}
|
|
}
|
|
else if( UK2Node_FunctionEntry* EntryPoint = Cast<UK2Node_FunctionEntry>(FunctionEntryNodePtr.Get()) )
|
|
{
|
|
if( EntryPoint->MetaData.bCallInEditor )
|
|
{
|
|
Result = ECheckBoxState::Checked;
|
|
}
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
void FBlueprintGraphActionDetails::OnEditorCallableEventModified( const ECheckBoxState NewCheckedState ) const
|
|
{
|
|
if( FunctionEntryNodePtr.IsValid() )
|
|
{
|
|
const bool bCallInEditor = NewCheckedState == ECheckBoxState::Checked;
|
|
const FText TransactionType = bCallInEditor ? LOCTEXT( "DisableCallInEditor", "Disable Call In Editor " ) :
|
|
LOCTEXT( "EnableCallInEditor", "Enable Call In Editor" );
|
|
|
|
if( UK2Node_CustomEvent* CustomEventNode = Cast<UK2Node_CustomEvent>(FunctionEntryNodePtr.Get()) )
|
|
{
|
|
if( UBlueprint* Blueprint = FunctionEntryNodePtr->GetBlueprint() )
|
|
{
|
|
const FScopedTransaction Transaction( TransactionType );
|
|
CustomEventNode->bCallInEditor = bCallInEditor;
|
|
FBlueprintEditorUtils::MarkBlueprintAsModified( CustomEventNode->GetBlueprint() );
|
|
}
|
|
}
|
|
else if( UK2Node_FunctionEntry* EntryPoint = Cast<UK2Node_FunctionEntry>(FunctionEntryNodePtr.Get()) )
|
|
{
|
|
const FScopedTransaction Transaction( TransactionType );
|
|
EntryPoint->MetaData.bCallInEditor = bCallInEditor;
|
|
FBlueprintEditorUtils::MarkBlueprintAsModified( EntryPoint->GetBlueprint() );
|
|
}
|
|
else
|
|
{
|
|
checkf(false, TEXT("Only Events and Functions are Callable In Editor"));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FBlueprintGraphActionDetails::IsFunctionDeprecated() const
|
|
{
|
|
bool bIsDeprecated = false;
|
|
|
|
UK2Node_EditablePinBase* Node = FunctionEntryNodePtr.Get();
|
|
if (Node != nullptr)
|
|
{
|
|
UK2Node_CustomEvent* CustomEventNode = Cast<UK2Node_CustomEvent>(Node);
|
|
if (CustomEventNode != nullptr)
|
|
{
|
|
bIsDeprecated = CustomEventNode->bIsDeprecated;
|
|
}
|
|
else
|
|
{
|
|
UK2Node_FunctionEntry* FunctionEntryNode = Cast<UK2Node_FunctionEntry>(Node);
|
|
if (FunctionEntryNode != nullptr)
|
|
{
|
|
bIsDeprecated = FunctionEntryNode->MetaData.bIsDeprecated;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bIsDeprecated;
|
|
}
|
|
|
|
ECheckBoxState FBlueprintGraphActionDetails::OnGetDeprecatedCheckboxState() const
|
|
{
|
|
return IsFunctionDeprecated() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
void FBlueprintGraphActionDetails::OnDeprecatedChanged(ECheckBoxState InNewState)
|
|
{
|
|
const bool bIsDeprecated = (InNewState == ECheckBoxState::Checked);
|
|
|
|
UK2Node_EditablePinBase* Node = FunctionEntryNodePtr.Get();
|
|
if (Node != nullptr)
|
|
{
|
|
UK2Node_CustomEvent* CustomEventNode = Cast<UK2Node_CustomEvent>(Node);
|
|
if (CustomEventNode != nullptr)
|
|
{
|
|
CustomEventNode->bIsDeprecated = bIsDeprecated;
|
|
}
|
|
else
|
|
{
|
|
CastChecked<UK2Node_FunctionEntry>(Node)->MetaData.bIsDeprecated = bIsDeprecated;
|
|
}
|
|
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Node->GetBlueprint());
|
|
}
|
|
}
|
|
|
|
FText FBlueprintGraphActionDetails::GetDeprecationMessageText() const
|
|
{
|
|
FText DeprecationMessage;
|
|
|
|
UK2Node_EditablePinBase* Node = FunctionEntryNodePtr.Get();
|
|
if (Node != nullptr)
|
|
{
|
|
UK2Node_CustomEvent* CustomEventNode = Cast<UK2Node_CustomEvent>(Node);
|
|
if (CustomEventNode != nullptr)
|
|
{
|
|
DeprecationMessage = FText::FromString(CustomEventNode->DeprecationMessage);
|
|
}
|
|
else
|
|
{
|
|
UK2Node_FunctionEntry* FunctionEntryNode = Cast<UK2Node_FunctionEntry>(Node);
|
|
if (FunctionEntryNode != nullptr)
|
|
{
|
|
DeprecationMessage = FText::FromString(FunctionEntryNode->MetaData.DeprecationMessage);
|
|
}
|
|
}
|
|
}
|
|
|
|
return DeprecationMessage;
|
|
}
|
|
|
|
void FBlueprintGraphActionDetails::OnDeprecationMessageTextCommitted(const FText& NewText, ETextCommit::Type InTextCommit)
|
|
{
|
|
const FString DeprecationMessage = NewText.ToString();
|
|
|
|
UK2Node_EditablePinBase* Node = FunctionEntryNodePtr.Get();
|
|
if (Node != nullptr)
|
|
{
|
|
UK2Node_CustomEvent* CustomEventNode = Cast<UK2Node_CustomEvent>(Node);
|
|
if (CustomEventNode != nullptr)
|
|
{
|
|
CustomEventNode->DeprecationMessage = DeprecationMessage;
|
|
}
|
|
else
|
|
{
|
|
CastChecked<UK2Node_FunctionEntry>(Node)->MetaData.DeprecationMessage = DeprecationMessage;
|
|
}
|
|
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Node->GetBlueprint());
|
|
}
|
|
}
|
|
|
|
FMulticastDelegateProperty* FBlueprintDelegateActionDetails::GetDelegateProperty() const
|
|
{
|
|
if (MyBlueprint.IsValid())
|
|
{
|
|
if (const FEdGraphSchemaAction_K2Delegate* DelegateVar = MyBlueprint.Pin()->SelectionAsDelegate())
|
|
{
|
|
return DelegateVar->GetDelegateProperty();
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool FBlueprintDelegateActionDetails::IsBlueprintProperty() const
|
|
{
|
|
const FMulticastDelegateProperty* Property = GetDelegateProperty();
|
|
const UBlueprint* Blueprint = GetBlueprintObj();
|
|
if(Property && Blueprint)
|
|
{
|
|
return (Property->GetOwner<UObject>() == Blueprint->SkeletonGeneratedClass);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FBlueprintDelegateActionDetails::SetEntryNode()
|
|
{
|
|
if (UEdGraph* NewTargetGraph = GetGraph())
|
|
{
|
|
TArray<UK2Node_FunctionEntry*> EntryNodes;
|
|
NewTargetGraph->GetNodesOfClass(EntryNodes);
|
|
|
|
if ((EntryNodes.Num() > 0) && EntryNodes[0]->IsEditable())
|
|
{
|
|
FunctionEntryNodePtr = EntryNodes[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
UEdGraph* FBlueprintDelegateActionDetails::GetGraph() const
|
|
{
|
|
if (MyBlueprint.IsValid())
|
|
{
|
|
if (const FEdGraphSchemaAction_K2Delegate* DelegateVar = MyBlueprint.Pin()->SelectionAsDelegate())
|
|
{
|
|
return DelegateVar->EdGraph;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void FBlueprintDelegateActionDetails::CustomizeDetails( IDetailLayoutBuilder& DetailLayout )
|
|
{
|
|
DetailsLayoutPtr = &DetailLayout;
|
|
ObjectsBeingEdited = DetailsLayoutPtr->GetSelectedObjects();
|
|
|
|
SetEntryNode();
|
|
|
|
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
|
|
const FSlateFontInfo DetailFontInfo = IDetailLayoutBuilder::GetDetailFont();
|
|
|
|
if (UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get())
|
|
{
|
|
IDetailCategoryBuilder& InputsCategory = DetailLayout.EditCategory("DelegateInputs", LOCTEXT("DelegateDetailsInputs", "Inputs"));
|
|
TSharedRef<FBlueprintGraphArgumentGroupLayout> InputArgumentGroup = MakeShareable(new FBlueprintGraphArgumentGroupLayout(SharedThis(this), FunctionEntryNode));
|
|
InputsCategory.AddCustomBuilder(InputArgumentGroup);
|
|
|
|
TSharedRef<SHorizontalBox> InputsHeaderContentWidget = SNew(SHorizontalBox);
|
|
TWeakPtr<SWidget> WeakInputsHeaderWidget = InputsHeaderContentWidget;
|
|
InputsHeaderContentWidget->AddSlot()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
];
|
|
InputsHeaderContentWidget->AddSlot()
|
|
.AutoWidth()
|
|
[
|
|
SNew(SButton)
|
|
.ButtonStyle(FAppStyle::Get(), "RoundButton")
|
|
.ForegroundColor(FAppStyle::Get().GetSlateColor("DefaultForeground"))
|
|
.ContentPadding(FMargin(2, 0))
|
|
.OnClicked(this, &FBlueprintDelegateActionDetails::OnAddNewInputClicked)
|
|
.HAlign(HAlign_Right)
|
|
.ToolTipText(LOCTEXT("DelegateNewOutputArgTooltip", "Create a new input argument"))
|
|
.VAlign(VAlign_Center)
|
|
.AddMetaData<FTagMetaData>(FTagMetaData(TEXT("DelegateNewInputArg")))
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding(FMargin(0, 1))
|
|
[
|
|
SNew(SImage)
|
|
.Image(FAppStyle::Get().GetBrush("Plus"))
|
|
]
|
|
+SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
.Padding(FMargin(2, 0, 0, 0))
|
|
[
|
|
SNew(STextBlock)
|
|
.Font(IDetailLayoutBuilder::GetDetailFontBold())
|
|
.Text(LOCTEXT("DelegateNewParameterInputArg", "New Parameter"))
|
|
.Visibility(this, &FBlueprintDelegateActionDetails::OnGetSectionTextVisibility, WeakInputsHeaderWidget)
|
|
.ShadowOffset(FVector2D(1, 1))
|
|
]
|
|
]
|
|
];
|
|
InputsCategory.HeaderContent(InputsHeaderContentWidget);
|
|
|
|
CollectAvailibleSignatures();
|
|
|
|
InputsCategory.AddCustomRow( LOCTEXT("CopySignatureFrom", "Copy signature from") )
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("CopySignatureFrom", "Copy signature from"))
|
|
.Font( DetailFontInfo )
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SAssignNew(CopySignatureComboButton, STextComboBox)
|
|
.OptionsSource(&FunctionsToCopySignatureFrom)
|
|
.OnSelectionChanged(this, &FBlueprintDelegateActionDetails::OnFunctionSelected)
|
|
];
|
|
}
|
|
}
|
|
|
|
void FBlueprintDelegateActionDetails::CollectAvailibleSignatures()
|
|
{
|
|
FunctionsToCopySignatureFrom.Empty();
|
|
if (FMulticastDelegateProperty* Property = GetDelegateProperty())
|
|
{
|
|
if (UClass* ScopeClass = Property->GetOwner<UClass>())
|
|
{
|
|
for(TFieldIterator<UFunction> It(ScopeClass, EFieldIteratorFlags::IncludeSuper); It; ++It)
|
|
{
|
|
UFunction* Func = *It;
|
|
if (UEdGraphSchema_K2::FunctionCanBeUsedInDelegate(Func) && !UEdGraphSchema_K2::HasFunctionAnyOutputParameter(Func))
|
|
{
|
|
TSharedPtr<FString> ItemData = MakeShareable(new FString(Func->GetName()));
|
|
FunctionsToCopySignatureFrom.Add(ItemData);
|
|
}
|
|
}
|
|
|
|
// Sort the function list
|
|
FunctionsToCopySignatureFrom.Sort([](const TSharedPtr<FString>& ElementA, const TSharedPtr<FString>& ElementB) -> bool
|
|
{
|
|
return *ElementA < *ElementB;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
void FBlueprintDelegateActionDetails::OnFunctionSelected(TSharedPtr<FString> FunctionName, ESelectInfo::Type SelectInfo)
|
|
{
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
FMulticastDelegateProperty* Property = GetDelegateProperty();
|
|
UClass* ScopeClass = Property ? Property->GetOwner<UClass>() : NULL;
|
|
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
|
|
|
|
if (FunctionEntryNode && FunctionName.IsValid() && ScopeClass)
|
|
{
|
|
const FName Name( *(*FunctionName) );
|
|
if (UFunction* NewSignature = ScopeClass->FindFunctionByName(Name))
|
|
{
|
|
const FScopedTransaction Transaction(LOCTEXT("CopySignature", "Copy Signature"));
|
|
|
|
while (FunctionEntryNode->UserDefinedPins.Num())
|
|
{
|
|
TSharedPtr<FUserPinInfo> Pin = FunctionEntryNode->UserDefinedPins[0];
|
|
FunctionEntryNode->RemoveUserDefinedPin(Pin);
|
|
}
|
|
|
|
for (TFieldIterator<FProperty> PropIt(NewSignature); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt)
|
|
{
|
|
FProperty* FuncParam = *PropIt;
|
|
FEdGraphPinType TypeOut;
|
|
Schema->ConvertPropertyToPinType(FuncParam, TypeOut);
|
|
UEdGraphPin* EdGraphPin = FunctionEntryNode->CreateUserDefinedPin(FuncParam->GetFName(), TypeOut, EGPD_Output);
|
|
ensure(EdGraphPin);
|
|
}
|
|
|
|
OnParamsChanged(FunctionEntryNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FBaseBlueprintGraphActionDetails::OnParamsChanged(UK2Node_EditablePinBase* TargetNode, bool bForceRefresh)
|
|
{
|
|
UEdGraph* Graph = GetGraph();
|
|
|
|
// TargetNode can be null, if we just removed the result node because there are no more out params
|
|
if (TargetNode)
|
|
{
|
|
RegenerateInputsChildrenDelegate.ExecuteIfBound();
|
|
RegenerateOutputsChildrenDelegate.ExecuteIfBound();
|
|
|
|
// Reconstruct the entry/exit definition and recompile the blueprint to make sure the signature has changed before any fixups
|
|
{
|
|
const bool bCurDisableOrphanSaving = TargetNode->bDisableOrphanPinSaving;
|
|
TargetNode->bDisableOrphanPinSaving = true;
|
|
|
|
TargetNode->ReconstructNode();
|
|
|
|
TargetNode->bDisableOrphanPinSaving = bCurDisableOrphanSaving;
|
|
}
|
|
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
|
|
K2Schema->HandleParameterDefaultValueChanged(TargetNode);
|
|
}
|
|
}
|
|
|
|
EVisibility FBlueprintDelegateActionDetails::OnGetSectionTextVisibility(TWeakPtr<SWidget> RowWidget) const
|
|
{
|
|
bool ShowText = RowWidget.Pin()->IsHovered();
|
|
|
|
// If the row is currently hovered, or a menu is being displayed for a button, keep the button expanded.
|
|
if (ShowText)
|
|
{
|
|
return EVisibility::SelfHitTestInvisible;
|
|
}
|
|
else
|
|
{
|
|
return EVisibility::Collapsed;
|
|
}
|
|
}
|
|
|
|
struct FPinRenamedHelper : public FBasePinChangeHelper
|
|
{
|
|
TSet<UBlueprint*> ModifiedBlueprints;
|
|
TSet<UK2Node*> NodesToRename;
|
|
|
|
virtual void EditMacroInstance(UK2Node_MacroInstance* MacroInstance, UBlueprint* Blueprint) override
|
|
{
|
|
NodesToRename.Add(MacroInstance);
|
|
if (Blueprint)
|
|
{
|
|
ModifiedBlueprints.Add(Blueprint);
|
|
}
|
|
}
|
|
|
|
virtual void EditCallSite(UK2Node_CallFunction* CallSite, UBlueprint* Blueprint) override
|
|
{
|
|
NodesToRename.Add(CallSite);
|
|
if (Blueprint)
|
|
{
|
|
ModifiedBlueprints.Add(Blueprint);
|
|
}
|
|
}
|
|
};
|
|
|
|
bool FBaseBlueprintGraphActionDetails::OnVerifyPinRename(UK2Node_EditablePinBase* InTargetNode, const FName InOldName, const FString& InNewName, FText& OutErrorMessage)
|
|
{
|
|
// If the name is unchanged, allow the name
|
|
if (InOldName.ToString() == InNewName)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (InNewName.Len() >= NAME_SIZE)
|
|
{
|
|
OutErrorMessage = FText::Format( LOCTEXT("PinNameTooLong", "The name you entered is too long. Names must be less than {0} characters"), FText::AsNumber( NAME_SIZE ) );
|
|
return false;
|
|
}
|
|
|
|
static const TArray<FString> ReservedParamNames =
|
|
{
|
|
TEXT("None"),
|
|
TEXT("Self")
|
|
};
|
|
|
|
for(const FString& ReservedName : ReservedParamNames)
|
|
{
|
|
if (!FCString::Stricmp(*InNewName, *ReservedName))
|
|
{
|
|
OutErrorMessage = FText::Format(LOCTEXT("PinNameIsReserved", "'{0}' is a reserved name"), FText::FromString(ReservedName));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (InTargetNode)
|
|
{
|
|
UK2Node_EditablePinBase* EntryNode = FunctionEntryNodePtr.Get();
|
|
UK2Node_EditablePinBase* ResultNode = FunctionResultNodePtr.Get();
|
|
const FName NewFName = *InNewName;
|
|
|
|
ERenamePinResult RenameResult = InTargetNode->RenameUserDefinedPin(InOldName, NewFName, true);
|
|
|
|
if (RenameResult != ERenamePinResult_NameCollision)
|
|
{
|
|
UK2Node_EditablePinBase* OtherNode = (InTargetNode == EntryNode) ? ResultNode : EntryNode;
|
|
|
|
// OtherNode can be null if the function, macro, etc. doesn't return a value.
|
|
if (OtherNode)
|
|
{
|
|
RenameResult = OtherNode->RenameUserDefinedPin(InOldName, NewFName, true);
|
|
}
|
|
}
|
|
|
|
if (RenameResult == ERenamePinResult_NameCollision)
|
|
{
|
|
OutErrorMessage = LOCTEXT("ConflictsWithProperty", "Conflicts with another local variable or function parameter!");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool FBaseBlueprintGraphActionDetails::OnPinRenamed(UK2Node_EditablePinBase* TargetNode, const FName OldName, const FString& NewName)
|
|
{
|
|
// Before changing the name, verify the name
|
|
FText ErrorMessage;
|
|
if (!OnVerifyPinRename(TargetNode, OldName, NewName, ErrorMessage))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
UEdGraph* Graph = GetGraph();
|
|
|
|
if (TargetNode)
|
|
{
|
|
FPinRenamedHelper PinRenamedHelper;
|
|
|
|
const FScopedTransaction Transaction(LOCTEXT("RenameParam", "Rename Parameter"));
|
|
|
|
TArray<UK2Node_EditablePinBase*> TerminalNodes = GatherAllResultNodes(FunctionResultNodePtr.Get());
|
|
if (UK2Node_EditablePinBase* EntryNode = FunctionEntryNodePtr.Get())
|
|
{
|
|
TerminalNodes.Add(EntryNode);
|
|
}
|
|
|
|
bool bHasFunctionEntryNode = false;
|
|
bool bHasFunctionResultNode = false;
|
|
for (UK2Node_EditablePinBase* TerminalNode : TerminalNodes)
|
|
{
|
|
TerminalNode->Modify();
|
|
PinRenamedHelper.NodesToRename.Add(TerminalNode);
|
|
|
|
// Since function terminator node pins map to generated function properties, we need to
|
|
// regenerate the referenced function so that dependent pins can be reconstructed properly.
|
|
bHasFunctionEntryNode |= TerminalNode->IsA<UK2Node_FunctionEntry>();
|
|
bHasFunctionResultNode |= TerminalNode->IsA<UK2Node_FunctionResult>();
|
|
}
|
|
|
|
UBlueprint* TargetBlueprint = GetBlueprintObj();
|
|
PinRenamedHelper.ModifiedBlueprints.Add(TargetBlueprint);
|
|
|
|
// GATHER
|
|
PinRenamedHelper.Broadcast(TargetBlueprint, TargetNode, Graph);
|
|
|
|
const FName NewFName = *NewName;
|
|
|
|
// TEST
|
|
for (UK2Node* NodeToRename : PinRenamedHelper.NodesToRename)
|
|
{
|
|
if (ERenamePinResult::ERenamePinResult_NameCollision == NodeToRename->RenameUserDefinedPin(OldName, NewFName, /*bTest =*/ true))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// UPDATE
|
|
for (UK2Node* NodeToRename : PinRenamedHelper.NodesToRename)
|
|
{
|
|
// Note: This will internally call Modify() on any matching pin(s).
|
|
NodeToRename->RenameUserDefinedPin(OldName, NewFName, /*bTest =*/ false);
|
|
}
|
|
|
|
// Update the corresponding UserDefinedPins entry for each terminal node.
|
|
// Note: This array is not serialized, so a Modify() here isn't necessary.
|
|
for (UK2Node_EditablePinBase* TerminalNode : TerminalNodes)
|
|
{
|
|
TSharedPtr<FUserPinInfo>* UDPinPtr = TerminalNode->UserDefinedPins.FindByPredicate([&](TSharedPtr<FUserPinInfo>& Pin)
|
|
{
|
|
return Pin.IsValid() && (Pin->PinName == OldName);
|
|
});
|
|
if (UDPinPtr)
|
|
{
|
|
(*UDPinPtr)->PinName = NewFName;
|
|
}
|
|
}
|
|
|
|
// A function signature change requires recompilation to update the underlying property chain.
|
|
// However, if we changed the function inputs at all, then we need to update any getter nodes that referenced the old name.
|
|
if (bHasFunctionEntryNode)
|
|
{
|
|
check(Graph);
|
|
|
|
TArray<UK2Node_VariableGet*> GetterNodes;
|
|
Graph->GetNodesOfClass<UK2Node_VariableGet>(GetterNodes);
|
|
|
|
for (UK2Node_VariableGet* GetterNode : GetterNodes)
|
|
{
|
|
check(GetterNode);
|
|
if (GetterNode->ReferencesVariable(OldName, nullptr))
|
|
{
|
|
GetterNode->HandleVariableRenamed(TargetBlueprint, TargetBlueprint->GeneratedClass, Graph, OldName, NewFName);
|
|
}
|
|
}
|
|
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(TargetBlueprint);
|
|
}
|
|
else if (bHasFunctionResultNode)
|
|
{
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(TargetBlueprint);
|
|
}
|
|
|
|
// Trigger a change notification for Blueprints that were updated, in case there's anything we need to refresh.
|
|
for (UBlueprint* ModifiedBlueprint : PinRenamedHelper.ModifiedBlueprints)
|
|
{
|
|
ModifiedBlueprint->BroadcastChanged();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void FBlueprintGraphActionDetails::SetEntryAndResultNodes()
|
|
{
|
|
// Clear the entry and exit nodes to the graph
|
|
FunctionEntryNodePtr = nullptr;
|
|
FunctionResultNodePtr = nullptr;
|
|
|
|
if (UEdGraph* NewTargetGraph = GetGraph())
|
|
{
|
|
FBlueprintEditorUtils::GetEntryAndResultNodes(NewTargetGraph, FunctionEntryNodePtr, FunctionResultNodePtr);
|
|
}
|
|
else if (UK2Node_EditablePinBase* Node = GetEditableNode())
|
|
{
|
|
FunctionEntryNodePtr = Node;
|
|
}
|
|
}
|
|
|
|
UEdGraph* FBaseBlueprintGraphActionDetails::GetGraph() const
|
|
{
|
|
check(ObjectsBeingEdited.Num() > 0);
|
|
|
|
if (ObjectsBeingEdited.Num() == 1)
|
|
{
|
|
UObject* const Object = ObjectsBeingEdited[0].Get();
|
|
if (!Object)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if (Object->IsA<UK2Node_Composite>())
|
|
{
|
|
return Cast<UK2Node_Composite>(Object)->BoundGraph;
|
|
}
|
|
else if (!Object->IsA<UK2Node_MacroInstance>() && (Object->IsA<UK2Node_Tunnel>() || Object->IsA<UK2Node_FunctionTerminator>()))
|
|
{
|
|
return Cast<UK2Node>(Object)->GetGraph();
|
|
}
|
|
else if (UK2Node_CallFunction* FunctionCall = Cast<UK2Node_CallFunction>(Object))
|
|
{
|
|
return FindObject<UEdGraph>(FunctionCall->GetBlueprint(), *(FunctionCall->FunctionReference.GetMemberName().ToString()));
|
|
}
|
|
else if (Object->IsA<UEdGraph>())
|
|
{
|
|
return Cast<UEdGraph>(Object);
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
UK2Node_EditablePinBase* FBlueprintGraphActionDetails::GetEditableNode() const
|
|
{
|
|
check(ObjectsBeingEdited.Num() > 0);
|
|
|
|
if (ObjectsBeingEdited.Num() == 1)
|
|
{
|
|
UObject* const Object = ObjectsBeingEdited[0].Get();
|
|
if (!Object)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if (Object->IsA<UK2Node_CustomEvent>())
|
|
{
|
|
return Cast<UK2Node_CustomEvent>(Object);
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
UFunction* FBlueprintGraphActionDetails::FindFunction() const
|
|
{
|
|
if (UK2Node_CustomEvent* EventNode = Cast<UK2Node_CustomEvent>(FunctionEntryNodePtr.Get()))
|
|
{
|
|
return FFunctionFromNodeHelper::FunctionFromNode(EventNode);
|
|
}
|
|
else if (UEdGraph* Graph = GetGraph())
|
|
{
|
|
if (UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(Graph))
|
|
{
|
|
UClass* Class = Blueprint->SkeletonGeneratedClass;
|
|
|
|
for (TFieldIterator<UFunction> FunctionIt(Class, EFieldIteratorFlags::IncludeSuper); FunctionIt; ++FunctionIt)
|
|
{
|
|
UFunction* Function = *FunctionIt;
|
|
if (Function->GetName() == Graph->GetName())
|
|
{
|
|
return Function;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
FKismetUserDeclaredFunctionMetadata* FBlueprintGraphActionDetails::GetMetadataBlock() const
|
|
{
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
if (UK2Node_FunctionEntry* TypedEntryNode = Cast<UK2Node_FunctionEntry>(FunctionEntryNode))
|
|
{
|
|
return &(TypedEntryNode->MetaData);
|
|
}
|
|
else if (UK2Node_Tunnel* TunnelNode = ExactCast<UK2Node_Tunnel>(FunctionEntryNode))
|
|
{
|
|
// Must be exactly a tunnel, not a macro instance
|
|
return &(TunnelNode->MetaData);
|
|
}
|
|
else if (UK2Node_CustomEvent* EventNode = Cast<UK2Node_CustomEvent>(FunctionEntryNode))
|
|
{
|
|
return &(EventNode->GetUserDefinedMetaData());
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
FText FBlueprintGraphActionDetails::OnGetTooltipText() const
|
|
{
|
|
if (FKismetUserDeclaredFunctionMetadata* Metadata = GetMetadataBlock())
|
|
{
|
|
return Metadata->ToolTip;
|
|
}
|
|
else
|
|
{
|
|
return LOCTEXT( "NoTooltip", "(None)" );
|
|
}
|
|
}
|
|
|
|
void FBlueprintGraphActionDetails::OnTooltipTextCommitted(const FText& NewText, ETextCommit::Type InTextCommit)
|
|
{
|
|
if (FKismetUserDeclaredFunctionMetadata* Metadata = GetMetadataBlock())
|
|
{
|
|
Metadata->ToolTip = NewText;
|
|
if (UFunction* Function = FindFunction())
|
|
{
|
|
Function->Modify();
|
|
Function->SetMetaData(FBlueprintMetadata::MD_Tooltip, *NewText.ToString());
|
|
}
|
|
}
|
|
}
|
|
|
|
FText FBlueprintGraphActionDetails::OnGetCategoryText() const
|
|
{
|
|
if (FKismetUserDeclaredFunctionMetadata* Metadata = GetMetadataBlock())
|
|
{
|
|
if( Metadata->Category.IsEmpty() || Metadata->Category.EqualTo(UEdGraphSchema_K2::VR_DefaultCategory) )
|
|
{
|
|
return LOCTEXT("DefaultCategory", "Default");
|
|
}
|
|
|
|
return Metadata->Category;
|
|
}
|
|
else
|
|
{
|
|
return LOCTEXT( "NoFunctionCategory", "(None)" );
|
|
}
|
|
}
|
|
|
|
void FBlueprintGraphActionDetails::OnCategoryTextCommitted(const FText& NewText, ETextCommit::Type InTextCommit)
|
|
{
|
|
if (InTextCommit == ETextCommit::OnEnter || InTextCommit == ETextCommit::OnUserMovedFocus)
|
|
{
|
|
// Remove excess whitespace and prevent categories with just spaces
|
|
FText CategoryName = FText::TrimPrecedingAndTrailing(NewText);
|
|
|
|
FBlueprintEditorUtils::SetBlueprintFunctionOrMacroCategory(GetGraph(), CategoryName);
|
|
MyBlueprint.Pin()->Refresh();
|
|
}
|
|
}
|
|
|
|
void FBlueprintGraphActionDetails::OnCategorySelectionChanged( TSharedPtr<FText> ProposedSelection, ESelectInfo::Type /*SelectInfo*/ )
|
|
{
|
|
if(ProposedSelection.IsValid())
|
|
{
|
|
if (FKismetUserDeclaredFunctionMetadata* Metadata = GetMetadataBlock())
|
|
{
|
|
FBlueprintEditorUtils::SetBlueprintFunctionOrMacroCategory(GetGraph(), *ProposedSelection.Get());
|
|
MyBlueprint.Pin()->Refresh();
|
|
|
|
CategoryListView.Pin()->ClearSelection();
|
|
CategoryComboButton.Pin()->SetIsOpen(false);
|
|
MyBlueprint.Pin()->ExpandCategory(*ProposedSelection.Get());
|
|
}
|
|
}
|
|
}
|
|
|
|
TSharedRef< ITableRow > FBlueprintGraphActionDetails::MakeCategoryViewWidget( TSharedPtr<FText> Item, const TSharedRef< STableViewBase >& OwnerTable )
|
|
{
|
|
return SNew(STableRow<TSharedPtr<FString>>, OwnerTable)
|
|
[
|
|
SNew(STextBlock) .Text(*Item.Get())
|
|
];
|
|
}
|
|
|
|
FText FBlueprintGraphActionDetails::OnGetKeywordsText() const
|
|
{
|
|
FText ResultKeywords;
|
|
if (FKismetUserDeclaredFunctionMetadata* Metadata = GetMetadataBlock())
|
|
{
|
|
ResultKeywords = Metadata->Keywords;
|
|
}
|
|
return ResultKeywords;
|
|
}
|
|
|
|
void FBlueprintGraphActionDetails::OnKeywordsTextCommitted(const FText& NewText, ETextCommit::Type InTextCommit)
|
|
{
|
|
if (InTextCommit == ETextCommit::OnEnter || InTextCommit == ETextCommit::OnUserMovedFocus)
|
|
{
|
|
if (FKismetUserDeclaredFunctionMetadata* Metadata = GetMetadataBlock())
|
|
{
|
|
// Remove excess whitespace and prevent keywords with just spaces
|
|
FText Keywords = FText::TrimPrecedingAndTrailing(NewText);
|
|
|
|
if (!Keywords.EqualTo(Metadata->Keywords))
|
|
{
|
|
Metadata->Keywords = Keywords;
|
|
|
|
if (UFunction* Function = FindFunction())
|
|
{
|
|
Function->Modify();
|
|
Function->SetMetaData(FBlueprintMetadata::MD_FunctionKeywords, *Keywords.ToString());
|
|
}
|
|
OnParamsChanged(GetFunctionEntryNode().Get(), true);
|
|
FBlueprintEditorUtils::MarkBlueprintAsModified(GetBlueprintObj());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FText FBlueprintGraphActionDetails::OnGetCompactNodeTitleText() const
|
|
{
|
|
FText ResultKeywords;
|
|
if (FKismetUserDeclaredFunctionMetadata* Metadata = GetMetadataBlock())
|
|
{
|
|
ResultKeywords = Metadata->CompactNodeTitle;
|
|
}
|
|
return ResultKeywords;
|
|
}
|
|
|
|
void FBlueprintGraphActionDetails::OnCompactNodeTitleTextCommitted(const FText& NewText, ETextCommit::Type InTextCommit)
|
|
{
|
|
if (InTextCommit == ETextCommit::OnEnter || InTextCommit == ETextCommit::OnUserMovedFocus)
|
|
{
|
|
if (FKismetUserDeclaredFunctionMetadata* Metadata = GetMetadataBlock())
|
|
{
|
|
// Remove excess whitespace and prevent CompactNodeTitle with just spaces
|
|
FText CompactNodeTitle = FText::TrimPrecedingAndTrailing(NewText);
|
|
|
|
if (!CompactNodeTitle.EqualTo(Metadata->CompactNodeTitle))
|
|
{
|
|
Metadata->CompactNodeTitle = CompactNodeTitle;
|
|
|
|
if (UFunction* Function = FindFunction())
|
|
{
|
|
Function->Modify();
|
|
|
|
if (CompactNodeTitle.IsEmpty())
|
|
{
|
|
// Remove the metadata from the function, empty listings will still display the node as Compact
|
|
Function->RemoveMetaData(FBlueprintMetadata::MD_FunctionKeywords);
|
|
}
|
|
else
|
|
{
|
|
Function->SetMetaData(FBlueprintMetadata::MD_CompactNodeTitle, *CompactNodeTitle.ToString());
|
|
}
|
|
}
|
|
OnParamsChanged(GetFunctionEntryNode().Get(), true);
|
|
FBlueprintEditorUtils::MarkBlueprintAsModified(GetBlueprintObj());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FText FBlueprintGraphActionDetails::AccessSpecifierProperName( uint32 AccessSpecifierFlag ) const
|
|
{
|
|
switch(AccessSpecifierFlag)
|
|
{
|
|
case FUNC_Public:
|
|
return LOCTEXT( "Public", "Public" );
|
|
case FUNC_Private:
|
|
return LOCTEXT( "Private", "Private" );
|
|
case FUNC_Protected:
|
|
return LOCTEXT( "Protected", "Protected" );
|
|
case 0:
|
|
return LOCTEXT( "Unknown", "Unknown" ); // Default?
|
|
}
|
|
return LOCTEXT( "Error", "Error" );
|
|
}
|
|
|
|
FText FBlueprintGraphActionDetails::ReplicationSpecifierProperName( uint32 ReplicationSpecifierFlag ) const
|
|
{
|
|
switch(ReplicationSpecifierFlag)
|
|
{
|
|
case FUNC_NetMulticast:
|
|
return LOCTEXT( "MulticastDropDown", "Multicast" );
|
|
case FUNC_NetServer:
|
|
return LOCTEXT( "ServerDropDown", "Run on Server" );
|
|
case FUNC_NetClient:
|
|
return LOCTEXT( "ClientDropDown", "Run on owning Client" );
|
|
case 0:
|
|
return LOCTEXT( "NotReplicatedDropDown", "Not Replicated" );
|
|
}
|
|
return LOCTEXT( "Error", "Error" );
|
|
}
|
|
|
|
TSharedRef<ITableRow> FBlueprintGraphActionDetails::HandleGenerateRowAccessSpecifier( TSharedPtr<FAccessSpecifierLabel> SpecifierName, const TSharedRef<STableViewBase>& OwnerTable )
|
|
{
|
|
return SNew(STableRow< TSharedPtr<FAccessSpecifierLabel> >, OwnerTable)
|
|
.Content()
|
|
[
|
|
SNew( STextBlock )
|
|
.Text( SpecifierName.IsValid() ? SpecifierName->LocalizedName : FText::GetEmpty() )
|
|
];
|
|
}
|
|
|
|
FText FBlueprintGraphActionDetails::GetCurrentAccessSpecifierName() const
|
|
{
|
|
uint32 AccessSpecifierFlag = 0;
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
if(UK2Node_FunctionEntry* EntryNode = Cast<UK2Node_FunctionEntry>(FunctionEntryNode))
|
|
{
|
|
AccessSpecifierFlag = FUNC_AccessSpecifiers & EntryNode->GetFunctionFlags();
|
|
}
|
|
else if(UK2Node_CustomEvent* CustomEventNode = Cast<UK2Node_CustomEvent>(FunctionEntryNode))
|
|
{
|
|
AccessSpecifierFlag = FUNC_AccessSpecifiers & CustomEventNode->FunctionFlags;
|
|
}
|
|
return AccessSpecifierProperName( AccessSpecifierFlag );
|
|
}
|
|
|
|
bool FBlueprintGraphActionDetails::IsAccessSpecifierVisible() const
|
|
{
|
|
bool bSupportedType = false;
|
|
bool bIsEditable = false;
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
if(FunctionEntryNode)
|
|
{
|
|
UBlueprint* Blueprint = FunctionEntryNode->GetBlueprint();
|
|
const bool bIsInterface = FBlueprintEditorUtils::IsInterfaceBlueprint(Blueprint);
|
|
|
|
bSupportedType = !bIsInterface && (FunctionEntryNode->IsA<UK2Node_FunctionEntry>() || FunctionEntryNode->IsA<UK2Node_Event>());
|
|
bIsEditable = FunctionEntryNode->IsEditable();
|
|
}
|
|
return bSupportedType && bIsEditable;
|
|
}
|
|
|
|
void FBlueprintGraphActionDetails::OnAccessSpecifierSelected( TSharedPtr<FAccessSpecifierLabel> SpecifierName, ESelectInfo::Type SelectInfo )
|
|
{
|
|
if(AccessSpecifierComboButton.IsValid())
|
|
{
|
|
AccessSpecifierComboButton->SetIsOpen(false);
|
|
}
|
|
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
if(FunctionEntryNode && SpecifierName.IsValid())
|
|
{
|
|
const FScopedTransaction Transaction( LOCTEXT( "ChangeAccessSpecifier", "Change Access Specifier" ) );
|
|
|
|
FunctionEntryNode->Modify();
|
|
UFunction* Function = FindFunction();
|
|
if(Function)
|
|
{
|
|
Function->Modify();
|
|
}
|
|
|
|
const EFunctionFlags ClearAccessSpecifierMask = ~FUNC_AccessSpecifiers;
|
|
if(UK2Node_FunctionEntry* EntryNode = Cast<UK2Node_FunctionEntry>(FunctionEntryNode))
|
|
{
|
|
int32 ExtraFlags = EntryNode->GetExtraFlags();
|
|
ExtraFlags &= ClearAccessSpecifierMask;
|
|
ExtraFlags |= SpecifierName->SpecifierFlag;
|
|
EntryNode->SetExtraFlags(ExtraFlags);
|
|
}
|
|
else if(UK2Node_Event* EventNode = Cast<UK2Node_Event>(FunctionEntryNode))
|
|
{
|
|
EventNode->FunctionFlags &= ClearAccessSpecifierMask;
|
|
EventNode->FunctionFlags |= SpecifierName->SpecifierFlag;
|
|
}
|
|
if(Function)
|
|
{
|
|
Function->FunctionFlags &= ClearAccessSpecifierMask;
|
|
Function->FunctionFlags |= SpecifierName->SpecifierFlag;
|
|
}
|
|
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetBlueprintObj());
|
|
}
|
|
}
|
|
|
|
bool FBlueprintGraphActionDetails::GetInstanceColorVisibility() const
|
|
{
|
|
// Hide the color editor if it's a top level function declaration.
|
|
// Show it if we're editing a collapsed graph or macro
|
|
UEdGraph* Graph = GetGraph();
|
|
if (Graph)
|
|
{
|
|
const UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(Graph);
|
|
if (Blueprint)
|
|
{
|
|
const bool bIsTopLevelFunctionGraph = Blueprint->FunctionGraphs.Contains(Graph);
|
|
const bool bIsTopLevelMacroGraph = Blueprint->MacroGraphs.Contains(Graph);
|
|
const bool bIsMacroGraph = Blueprint->BlueprintType == BPTYPE_MacroLibrary;
|
|
return ((bIsMacroGraph || bIsTopLevelMacroGraph) || !bIsTopLevelFunctionGraph);
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
FLinearColor FBlueprintGraphActionDetails::GetNodeTitleColor() const
|
|
{
|
|
if (FKismetUserDeclaredFunctionMetadata* Metadata = GetMetadataBlock())
|
|
{
|
|
return Metadata->InstanceTitleColor;
|
|
}
|
|
else
|
|
{
|
|
return FLinearColor::White;
|
|
}
|
|
}
|
|
|
|
FReply FBlueprintGraphActionDetails::ColorBlock_OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
|
{
|
|
if (MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton)
|
|
{
|
|
if (FKismetUserDeclaredFunctionMetadata* Metadata = GetMetadataBlock())
|
|
{
|
|
TWeakPtr<FBlueprintGraphActionDetails> WeakSelf = SharedThis(this);
|
|
|
|
FColorPickerArgs PickerArgs;
|
|
PickerArgs.bIsModal = true;
|
|
PickerArgs.ParentWidget = ColorBlock;
|
|
PickerArgs.DisplayGamma = TAttribute<float>::Create( TAttribute<float>::FGetter::CreateUObject(GEngine, &UEngine::GetDisplayGamma) );
|
|
PickerArgs.InitialColor = Metadata->InstanceTitleColor;
|
|
PickerArgs.OnColorCommitted = FOnLinearColorValueChanged::CreateLambda([WeakSelf](FLinearColor NewValue)
|
|
{
|
|
if (TSharedPtr<FBlueprintGraphActionDetails> Self = WeakSelf.Pin())
|
|
{
|
|
if (FKismetUserDeclaredFunctionMetadata* Metadata = Self->GetMetadataBlock())
|
|
{
|
|
Metadata->InstanceTitleColor = NewValue;
|
|
}
|
|
}
|
|
});
|
|
|
|
OpenColorPicker(PickerArgs);
|
|
}
|
|
|
|
return FReply::Handled();
|
|
}
|
|
else
|
|
{
|
|
return FReply::Unhandled();
|
|
}
|
|
}
|
|
|
|
bool FBlueprintGraphActionDetails::IsCustomEvent() const
|
|
{
|
|
return (NULL != Cast<UK2Node_CustomEvent>(FunctionEntryNodePtr.Get()));
|
|
}
|
|
|
|
void FBlueprintGraphActionDetails::OnIsReliableReplicationFunctionModified(const ECheckBoxState NewCheckedState)
|
|
{
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
UK2Node_CustomEvent* CustomEvent = Cast<UK2Node_CustomEvent>(FunctionEntryNode);
|
|
if( CustomEvent )
|
|
{
|
|
if (NewCheckedState == ECheckBoxState::Checked)
|
|
{
|
|
if (UK2Node_FunctionEntry* TypedEntryNode = Cast<UK2Node_FunctionEntry>(FunctionEntryNode))
|
|
{
|
|
TypedEntryNode->AddExtraFlags(FUNC_NetReliable);
|
|
}
|
|
if (UK2Node_CustomEvent* CustomEventNode = Cast<UK2Node_CustomEvent>(FunctionEntryNode))
|
|
{
|
|
CustomEventNode->FunctionFlags |= FUNC_NetReliable;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (UK2Node_FunctionEntry* TypedEntryNode = Cast<UK2Node_FunctionEntry>(FunctionEntryNode))
|
|
{
|
|
TypedEntryNode->ClearExtraFlags(FUNC_NetReliable);
|
|
}
|
|
if (UK2Node_CustomEvent* CustomEventNode = Cast<UK2Node_CustomEvent>(FunctionEntryNode))
|
|
{
|
|
CustomEventNode->FunctionFlags &= ~FUNC_NetReliable;
|
|
}
|
|
}
|
|
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetBlueprintObj());
|
|
}
|
|
}
|
|
|
|
ECheckBoxState FBlueprintGraphActionDetails::GetIsReliableReplicatedFunction() const
|
|
{
|
|
const UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
const UK2Node_CustomEvent* CustomEvent = Cast<const UK2Node_CustomEvent>(FunctionEntryNode);
|
|
if(!CustomEvent)
|
|
{
|
|
return ECheckBoxState::Undetermined;
|
|
}
|
|
|
|
uint32 const NetReliableMask = (FUNC_Net | FUNC_NetReliable);
|
|
if ((CustomEvent->GetNetFlags() & NetReliableMask) == NetReliableMask)
|
|
{
|
|
return ECheckBoxState::Checked;
|
|
}
|
|
|
|
return ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
bool FBlueprintGraphActionDetails::IsPureFunctionVisible() const
|
|
{
|
|
bool bSupportedType = false;
|
|
bool bIsEditable = false;
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
if(FunctionEntryNode)
|
|
{
|
|
UBlueprint* Blueprint = FunctionEntryNode->GetBlueprint();
|
|
const bool bIsInterface = FBlueprintEditorUtils::IsInterfaceBlueprint(Blueprint);
|
|
|
|
bSupportedType = !bIsInterface && FunctionEntryNode->IsA<UK2Node_FunctionEntry>();
|
|
bIsEditable = FunctionEntryNode->IsEditable();
|
|
}
|
|
return bSupportedType && bIsEditable;
|
|
}
|
|
|
|
void FBlueprintGraphActionDetails::OnIsPureFunctionModified( const ECheckBoxState NewCheckedState )
|
|
{
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
UFunction* Function = FindFunction();
|
|
UK2Node_FunctionEntry* EntryNode = Cast<UK2Node_FunctionEntry>(FunctionEntryNode);
|
|
if (EntryNode && Function)
|
|
{
|
|
const FScopedTransaction Transaction( LOCTEXT( "ChangePure", "Change Pure" ) );
|
|
EntryNode->Modify();
|
|
Function->Modify();
|
|
|
|
//set flags on function entry node also
|
|
Function->FunctionFlags ^= FUNC_BlueprintPure;
|
|
EntryNode->SetExtraFlags(EntryNode->GetExtraFlags() ^ FUNC_BlueprintPure);
|
|
OnParamsChanged(FunctionEntryNode);
|
|
}
|
|
}
|
|
|
|
ECheckBoxState FBlueprintGraphActionDetails::GetIsPureFunction() const
|
|
{
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
UK2Node_FunctionEntry* EntryNode = Cast<UK2Node_FunctionEntry>(FunctionEntryNode);
|
|
if(!EntryNode)
|
|
{
|
|
return ECheckBoxState::Undetermined;
|
|
}
|
|
return (EntryNode->GetFunctionFlags() & FUNC_BlueprintPure) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
bool FBlueprintGraphActionDetails::IsConstFunctionVisible() const
|
|
{
|
|
bool bVisible = false;
|
|
UFunction* Function = FindFunction();
|
|
UK2Node_FunctionEntry* EntryNode = Cast<UK2Node_FunctionEntry>(FunctionEntryNodePtr.Get());
|
|
if(Function && EntryNode)
|
|
{
|
|
const bool bIsStatic = EntryNode->GetFunctionFlags() & FUNC_Static;
|
|
const bool bIsEditable = EntryNode->IsEditable();
|
|
bVisible = bIsEditable && !bIsStatic;
|
|
}
|
|
return bVisible;
|
|
}
|
|
|
|
void FBlueprintGraphActionDetails::OnIsConstFunctionModified( const ECheckBoxState NewCheckedState )
|
|
{
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
UFunction* Function = FindFunction();
|
|
UK2Node_FunctionEntry* EntryNode = Cast<UK2Node_FunctionEntry>(FunctionEntryNode);
|
|
if(EntryNode && Function)
|
|
{
|
|
const FScopedTransaction Transaction( LOCTEXT( "ChangeConst", "Change Const" ) );
|
|
EntryNode->Modify();
|
|
Function->Modify();
|
|
|
|
//set flags on function entry node also
|
|
Function->FunctionFlags ^= FUNC_Const;
|
|
EntryNode->SetExtraFlags(EntryNode->GetExtraFlags() ^ FUNC_Const);
|
|
OnParamsChanged(FunctionEntryNode);
|
|
}
|
|
}
|
|
|
|
ECheckBoxState FBlueprintGraphActionDetails::GetIsConstFunction() const
|
|
{
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
UK2Node_FunctionEntry* EntryNode = Cast<UK2Node_FunctionEntry>(FunctionEntryNode);
|
|
if(!EntryNode)
|
|
{
|
|
return ECheckBoxState::Undetermined;
|
|
}
|
|
return (EntryNode->GetFunctionFlags() & FUNC_Const) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
bool FBlueprintGraphActionDetails::IsExecFunctionVisible() const
|
|
{
|
|
bool bSupportedType = false;
|
|
bool bIsEditable = false;
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
if (FunctionEntryNode)
|
|
{
|
|
bSupportedType = FunctionEntryNode->IsA<UK2Node_FunctionEntry>();
|
|
bIsEditable = FunctionEntryNode->IsEditable();
|
|
}
|
|
return bSupportedType && bIsEditable;
|
|
}
|
|
|
|
void FBlueprintGraphActionDetails::OnIsExecFunctionModified(const ECheckBoxState NewCheckedState)
|
|
{
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
UFunction* Function = FindFunction();
|
|
UK2Node_FunctionEntry* EntryNode = Cast<UK2Node_FunctionEntry>(FunctionEntryNode);
|
|
if (EntryNode && Function)
|
|
{
|
|
const FScopedTransaction Transaction(LOCTEXT("ChangeExec", "Change Exec"));
|
|
EntryNode->Modify();
|
|
Function->Modify();
|
|
|
|
//set flags on function entry node also
|
|
Function->FunctionFlags ^= FUNC_Exec;
|
|
EntryNode->SetExtraFlags(EntryNode->GetExtraFlags() ^ FUNC_Exec);
|
|
OnParamsChanged(FunctionEntryNode);
|
|
}
|
|
}
|
|
|
|
ECheckBoxState FBlueprintGraphActionDetails::GetIsExecFunction() const
|
|
{
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
UK2Node_FunctionEntry* EntryNode = Cast<UK2Node_FunctionEntry>(FunctionEntryNode);
|
|
if (!EntryNode)
|
|
{
|
|
return ECheckBoxState::Undetermined;
|
|
}
|
|
return (EntryNode->GetFunctionFlags() & FUNC_Exec) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
bool FBlueprintGraphActionDetails::IsThreadSafeFunctionVisible() const
|
|
{
|
|
bool bSupportedType = false;
|
|
bool bIsEditable = false;
|
|
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
if(FunctionEntryNode)
|
|
{
|
|
bSupportedType = FunctionEntryNode->IsA<UK2Node_FunctionEntry>();
|
|
bIsEditable = FunctionEntryNode->IsEditable();
|
|
}
|
|
|
|
return bIsEditable && bSupportedType;
|
|
}
|
|
|
|
void FBlueprintGraphActionDetails::OnIsThreadSafeFunctionModified(const ECheckBoxState NewCheckedState)
|
|
{
|
|
if( FunctionEntryNodePtr.IsValid() )
|
|
{
|
|
const bool bThreadSafe = NewCheckedState == ECheckBoxState::Checked;
|
|
const FText TransactionType = bThreadSafe ? LOCTEXT( "DisableThreadSafe", "Disable Thread Safe" ) : LOCTEXT( "EnableThreadSafe", "Enable Thread Safe" );
|
|
|
|
if( UK2Node_FunctionEntry* EntryPoint = Cast<UK2Node_FunctionEntry>(FunctionEntryNodePtr.Get()) )
|
|
{
|
|
const FScopedTransaction Transaction( TransactionType );
|
|
EntryPoint->Modify();
|
|
EntryPoint->MetaData.bThreadSafe = bThreadSafe;
|
|
FBlueprintEditorUtils::MarkBlueprintAsModified( EntryPoint->GetBlueprint() );
|
|
}
|
|
}
|
|
}
|
|
|
|
ECheckBoxState FBlueprintGraphActionDetails::GetIsThreadSafeFunction() const
|
|
{
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
UK2Node_FunctionEntry* EntryNode = Cast<UK2Node_FunctionEntry>(FunctionEntryNode);
|
|
if (!EntryNode)
|
|
{
|
|
return ECheckBoxState::Undetermined;
|
|
}
|
|
return EntryNode->MetaData.bThreadSafe ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
bool FBlueprintGraphActionDetails::IsUnsafeDuringActorConstructionVisible() const
|
|
{
|
|
bool bSupportedType = false;
|
|
bool bIsEditable = false;
|
|
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
if (FunctionEntryNode)
|
|
{
|
|
bSupportedType = FunctionEntryNode->IsA<UK2Node_FunctionEntry>();
|
|
bIsEditable = FunctionEntryNode->IsEditable();
|
|
}
|
|
|
|
return bIsEditable && bSupportedType;
|
|
}
|
|
|
|
void FBlueprintGraphActionDetails::OnIsUnsafeDuringActorConstructionModified(const ECheckBoxState NewCheckedState)
|
|
{
|
|
if (FunctionEntryNodePtr.IsValid())
|
|
{
|
|
const bool bIsUnsafeDuringActorConstruction = NewCheckedState == ECheckBoxState::Checked;
|
|
const FText TransactionType = bIsUnsafeDuringActorConstruction ?
|
|
LOCTEXT("DisableIsUnsafeDuringActorConstruction", "Disable Unsafe During Actor Construction") :
|
|
LOCTEXT("EnableIsUnsafeDuringActorConstruction", "Enable Unsafe During Actor Construction");
|
|
|
|
if (UK2Node_FunctionEntry* EntryPoint = Cast<UK2Node_FunctionEntry>(FunctionEntryNodePtr.Get()))
|
|
{
|
|
const FScopedTransaction Transaction(TransactionType);
|
|
EntryPoint->Modify();
|
|
EntryPoint->MetaData.bIsUnsafeDuringActorConstruction = bIsUnsafeDuringActorConstruction;
|
|
FBlueprintEditorUtils::MarkBlueprintAsModified(EntryPoint->GetBlueprint());
|
|
}
|
|
}
|
|
}
|
|
|
|
ECheckBoxState FBlueprintGraphActionDetails::GetIsUnsafeDuringActorConstruction() const
|
|
{
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
UK2Node_FunctionEntry* EntryNode = Cast<UK2Node_FunctionEntry>(FunctionEntryNode);
|
|
if (!EntryNode)
|
|
{
|
|
return ECheckBoxState::Undetermined;
|
|
}
|
|
return EntryNode->MetaData.bIsUnsafeDuringActorConstruction ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
FReply FBaseBlueprintGraphActionDetails::OnAddNewInputClicked()
|
|
{
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
|
|
if( FunctionEntryNode )
|
|
{
|
|
FScopedTransaction Transaction( LOCTEXT( "AddInParam", "Add In Parameter" ) );
|
|
FunctionEntryNode->Modify();
|
|
|
|
FEdGraphPinType PinType = MyBlueprint.Pin()->GetLastFunctionPinTypeUsed();
|
|
|
|
// Make sure that if this is an exec node we are allowed one.
|
|
if ((PinType.PinCategory == UEdGraphSchema_K2::PC_Exec) && (!FunctionEntryNode->CanModifyExecutionWires()))
|
|
{
|
|
MyBlueprint.Pin()->ResetLastPinType();
|
|
PinType = MyBlueprint.Pin()->GetLastFunctionPinTypeUsed();
|
|
}
|
|
const FName NewPinName = TEXT("NewParam");
|
|
if (FunctionEntryNode->CreateUserDefinedPin(NewPinName, PinType, EGPD_Output))
|
|
{
|
|
OnParamsChanged(FunctionEntryNode, true);
|
|
}
|
|
else
|
|
{
|
|
Transaction.Cancel();
|
|
}
|
|
}
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
EVisibility FBlueprintGraphActionDetails::GetAddNewInputOutputVisibility() const
|
|
{
|
|
UK2Node_EditablePinBase* FunctionEntryNode = FunctionEntryNodePtr.Get();
|
|
if (FunctionEntryNodePtr.IsValid())
|
|
{
|
|
if(UEdGraph* Graph = FunctionEntryNode->GetGraph())
|
|
{
|
|
// Math expression graphs are read only, do not allow adding or removing of pins
|
|
if(Cast<UK2Node_MathExpression>(Graph->GetOuter()))
|
|
{
|
|
return EVisibility::Collapsed;
|
|
}
|
|
}
|
|
}
|
|
return EVisibility::Visible;
|
|
}
|
|
|
|
bool FBlueprintGraphActionDetails::IsAddNewInputOutputEnabled() const
|
|
{
|
|
if (DetailsLayoutPtr)
|
|
{
|
|
if (TSharedPtr<const IDetailsView> DetailsView = DetailsLayoutPtr->GetDetailsViewSharedPtr())
|
|
{
|
|
return DetailsView->IsPropertyEditingEnabled();
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
EVisibility FBlueprintGraphActionDetails::OnGetSectionTextVisibility(TWeakPtr<SWidget> RowWidget) const
|
|
{
|
|
bool ShowText = RowWidget.Pin()->IsHovered();
|
|
|
|
// If the row is currently hovered, or a menu is being displayed for a button, keep the button expanded.
|
|
if (ShowText)
|
|
{
|
|
return EVisibility::SelfHitTestInvisible;
|
|
}
|
|
else
|
|
{
|
|
return EVisibility::Collapsed;
|
|
}
|
|
}
|
|
|
|
FReply FBlueprintGraphActionDetails::OnAddNewOutputClicked()
|
|
{
|
|
FScopedTransaction Transaction( LOCTEXT( "AddOutParam", "Add Out Parameter" ) );
|
|
|
|
GetBlueprintObj()->Modify();
|
|
GetGraph()->Modify();
|
|
UK2Node_EditablePinBase* EntryPin = FunctionEntryNodePtr.Get();
|
|
EntryPin->Modify();
|
|
for (int32 iPin = 0; iPin < EntryPin->Pins.Num() ; iPin++)
|
|
{
|
|
EntryPin->Pins[iPin]->Modify();
|
|
}
|
|
|
|
UK2Node_EditablePinBase* PreviousResultNode = FunctionResultNodePtr.Get();
|
|
|
|
AttemptToCreateResultNode();
|
|
|
|
UK2Node_EditablePinBase* FunctionResultNode = FunctionResultNodePtr.Get();
|
|
if( FunctionResultNode )
|
|
{
|
|
FEdGraphPinType PinType = MyBlueprint.Pin()->GetLastFunctionPinTypeUsed();
|
|
PinType.bIsReference = false;
|
|
// Make sure that if this is an exec node we are allowed one.
|
|
if ((PinType.PinCategory == UEdGraphSchema_K2::PC_Exec) && (!FunctionResultNode->CanModifyExecutionWires()))
|
|
{
|
|
MyBlueprint.Pin()->ResetLastPinType();
|
|
PinType = MyBlueprint.Pin()->GetLastFunctionPinTypeUsed();
|
|
}
|
|
|
|
const FName NewPinName = FunctionResultNode->CreateUniquePinName(TEXT("NewParam"));
|
|
TArray<UK2Node_EditablePinBase*> TargetNodes = GatherAllResultNodes(FunctionResultNode);
|
|
bool bAllChanged = TargetNodes.Num() > 0;
|
|
for (UK2Node_EditablePinBase* Node : TargetNodes)
|
|
{
|
|
Node->Modify();
|
|
UEdGraphPin* NewPin = Node->CreateUserDefinedPin(NewPinName, PinType, EGPD_Input, false);
|
|
bAllChanged &= (nullptr != NewPin);
|
|
|
|
if (bAllChanged)
|
|
{
|
|
OnParamsChanged(Node, true);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (!bAllChanged)
|
|
{
|
|
Transaction.Cancel();
|
|
}
|
|
|
|
if (!PreviousResultNode)
|
|
{
|
|
DetailsLayoutPtr->ForceRefreshDetails();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Transaction.Cancel();
|
|
}
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
|
|
FBlueprintGlobalOptionsManagedListDetails::FBlueprintGlobalOptionsManagedListDetails(TWeakPtr<class FBlueprintGlobalOptionsDetails> InGlobalOptionsDetailsPtr)
|
|
: GlobalOptionsDetailsPtr(InGlobalOptionsDetailsPtr)
|
|
{
|
|
}
|
|
|
|
UBlueprint* FBlueprintGlobalOptionsManagedListDetails::GetBlueprintObjectChecked() const
|
|
{
|
|
UBlueprint* BlueprintObject = nullptr;
|
|
|
|
TSharedPtr<FBlueprintGlobalOptionsDetails> PinnedGlobalOptionsDetailsPtr = GlobalOptionsDetailsPtr.Pin();
|
|
if (PinnedGlobalOptionsDetailsPtr.IsValid())
|
|
{
|
|
BlueprintObject = PinnedGlobalOptionsDetailsPtr->GetBlueprintObj();
|
|
}
|
|
|
|
check(BlueprintObject);
|
|
return BlueprintObject;
|
|
}
|
|
|
|
TSharedPtr<FBlueprintEditor> FBlueprintGlobalOptionsManagedListDetails::GetPinnedBlueprintEditorPtr() const
|
|
{
|
|
TSharedPtr<FBlueprintGlobalOptionsDetails> PinnedGlobalOptionsDetailsPtr = GlobalOptionsDetailsPtr.Pin();
|
|
if (PinnedGlobalOptionsDetailsPtr.IsValid())
|
|
{
|
|
return PinnedGlobalOptionsDetailsPtr->GetBlueprintEditorPtr().Pin();
|
|
}
|
|
|
|
return TSharedPtr<FBlueprintEditor>();
|
|
}
|
|
|
|
void FBlueprintGlobalOptionsManagedListDetails::OnRefreshInDetailsView()
|
|
{
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditorPtr = GetPinnedBlueprintEditorPtr();
|
|
if (BlueprintEditorPtr.IsValid())
|
|
{
|
|
TSharedPtr<SKismetInspector> Inspector = BlueprintEditorPtr->GetInspector();
|
|
if (Inspector.IsValid())
|
|
{
|
|
// Show details for the Blueprint instance we're editing
|
|
Inspector->ShowDetailsForSingleObject(GetBlueprintObjectChecked());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
FBlueprintImportsLayout::FBlueprintImportsLayout(TWeakPtr<class FBlueprintGlobalOptionsDetails> InGlobalOptionsDetails, bool bInShowDefaultImports)
|
|
: FBlueprintGlobalOptionsManagedListDetails(InGlobalOptionsDetails)
|
|
, bShouldShowDefaultImports(bInShowDefaultImports)
|
|
{
|
|
DisplayOptions.TitleText = bShouldShowDefaultImports ?
|
|
LOCTEXT("BlueprintDefaultNamespaceTitle", "Default Namespaces") :
|
|
LOCTEXT("BlueprintImportedNamespaceTitle", "Imported Namespaces");
|
|
DisplayOptions.NoItemsLabelText = LOCTEXT("NoBlueprintImports", "No Imports");
|
|
}
|
|
|
|
TSharedPtr<SWidget> FBlueprintImportsLayout::MakeAddItemWidget()
|
|
{
|
|
if (bShouldShowDefaultImports)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
return SNew(SBlueprintNamespaceEntry)
|
|
.AllowTextEntry(false)
|
|
.OnNamespaceSelected(this, &FBlueprintImportsLayout::OnNamespaceSelected)
|
|
.OnGetNamespacesToExclude(this, &FBlueprintImportsLayout::OnGetNamespacesToExclude)
|
|
.ExcludedNamespaceTooltipText(LOCTEXT("CannotSelectNamespaceForImport", "This namespace is already imported."))
|
|
.ButtonContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("BlueprintAddImportButton", "Add"))
|
|
];
|
|
}
|
|
|
|
void FBlueprintImportsLayout::GetManagedListItems(TArray<FManagedListItem>& OutListItems) const
|
|
{
|
|
auto AddNamespaceItemsToOutputList = [&OutListItems](const TSet<FString>& NamespaceItems, bool bIsRemovable)
|
|
{
|
|
for (const FString& NamespaceItem : NamespaceItems)
|
|
{
|
|
FManagedListItem ItemDesc;
|
|
ItemDesc.ItemName = NamespaceItem;
|
|
ItemDesc.DisplayName = FText::FromString(NamespaceItem);
|
|
ItemDesc.bIsRemovable = bIsRemovable;
|
|
|
|
OutListItems.Add(MoveTemp(ItemDesc));
|
|
}
|
|
};
|
|
|
|
TSet<FString> NamespaceItems;
|
|
const UBlueprint* Blueprint = GetBlueprintObjectChecked();
|
|
|
|
// Default imports (non-removable). These include anything from the shared global set, as well as any namespaces assigned to the Blueprint hierarchy.
|
|
FBlueprintNamespaceUtilities::GetSharedGlobalImports(NamespaceItems);
|
|
FBlueprintNamespaceUtilities::GetDefaultImportsForObject(Blueprint, NamespaceItems);
|
|
|
|
if(!bShouldShowDefaultImports)
|
|
{
|
|
// Blueprint imports (removable). A Blueprint may explicitly import a namespace that's also in the default set, but we exclude those here so they can't be removed.
|
|
NamespaceItems = Blueprint->ImportedNamespaces.Difference(NamespaceItems);
|
|
}
|
|
|
|
AddNamespaceItemsToOutputList(NamespaceItems, !bShouldShowDefaultImports);
|
|
}
|
|
|
|
void FBlueprintImportsLayout::OnRemoveItem(const FManagedListItem& Item)
|
|
{
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditorPtr = GetPinnedBlueprintEditorPtr();
|
|
if (BlueprintEditorPtr.IsValid())
|
|
{
|
|
BlueprintEditorPtr->RemoveNamespace(Item.ItemName);
|
|
}
|
|
|
|
RegenerateChildContent();
|
|
|
|
OnRefreshInDetailsView();
|
|
}
|
|
|
|
void FBlueprintImportsLayout::OnNamespaceSelected(const FString& InNamespace)
|
|
{
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditorPtr = GetPinnedBlueprintEditorPtr();
|
|
if (BlueprintEditorPtr.IsValid())
|
|
{
|
|
FBlueprintEditor::FImportNamespaceExParameters Params;
|
|
Params.bIsAutoImport = false;
|
|
Params.NamespacesToImport.Add(InNamespace);
|
|
Params.OnPostImportCallback = FSimpleDelegate::CreateLambda([this]()
|
|
{
|
|
RegenerateChildContent();
|
|
});
|
|
|
|
// Add to edited Blueprint(s) and import into the current editor context.
|
|
BlueprintEditorPtr->ImportNamespaceEx(Params);
|
|
}
|
|
|
|
OnRefreshInDetailsView();
|
|
}
|
|
|
|
void FBlueprintImportsLayout::OnGetNamespacesToExclude(TSet<FString>& OutNamespacesToExclude) const
|
|
{
|
|
const UBlueprint* Blueprint = GetBlueprintObjectChecked();
|
|
|
|
FBlueprintNamespaceUtilities::GetSharedGlobalImports(OutNamespacesToExclude);
|
|
FBlueprintNamespaceUtilities::GetDefaultImportsForObject(Blueprint, OutNamespacesToExclude);
|
|
|
|
OutNamespacesToExclude.Append(Blueprint->ImportedNamespaces);
|
|
}
|
|
|
|
|
|
FBlueprintInterfaceLayout::FBlueprintInterfaceLayout(TWeakPtr<class FBlueprintGlobalOptionsDetails> InGlobalOptionsDetails, TSharedPtr<IPropertyHandle> InInterfacesProperty)
|
|
: FBlueprintGlobalOptionsManagedListDetails(InGlobalOptionsDetails)
|
|
, InterfacesProperty(InInterfacesProperty)
|
|
{
|
|
DisplayOptions.TitleText = InterfacesProperty ?
|
|
LOCTEXT("BlueprintImplementedInterfaceTitle", "Implemented Interfaces") :
|
|
LOCTEXT("BlueprintInheritedInterfaceTitle", "Inherited Interfaces");
|
|
DisplayOptions.NoItemsLabelText = LOCTEXT("NoBlueprintInterface", "No Interfaces");
|
|
DisplayOptions.BrowseButtonToolTipText = LOCTEXT("BlueprintInterfaceBrowseTooltip", "Opens this interface");
|
|
}
|
|
|
|
TSharedPtr<IPropertyHandle> FBlueprintInterfaceLayout::GetPropertyHandle() const
|
|
{
|
|
return InterfacesProperty;
|
|
}
|
|
|
|
TSharedPtr<SWidget> FBlueprintInterfaceLayout::MakeAddItemWidget()
|
|
{
|
|
if (InterfacesProperty)
|
|
{
|
|
return SAssignNew(AddInterfaceComboButton, SComboButton)
|
|
.ButtonContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("BlueprintAddInterfaceButton", "Add"))
|
|
]
|
|
.OnGetMenuContent(this, &FBlueprintInterfaceLayout::OnGetAddInterfaceMenuContent);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void FBlueprintInterfaceLayout::GetManagedListItems(TArray<FManagedListItem>& OutListItems) const
|
|
{
|
|
const UBlueprint* Blueprint = GetBlueprintObjectChecked();
|
|
|
|
if (InterfacesProperty)
|
|
{
|
|
checkf(
|
|
&Blueprint->ImplementedInterfaces == (TArray<FBPInterfaceDescription>*)InterfacesProperty->GetValueBaseAddress((uint8*)Blueprint),
|
|
TEXT("Different Property provided than ImplementedInterfaces")
|
|
);
|
|
|
|
// Generate a list of interfaces already implemented
|
|
for (int32 InterfaceIndex = 0; InterfaceIndex < Blueprint->ImplementedInterfaces.Num(); ++InterfaceIndex)
|
|
{
|
|
if (const TSubclassOf<UInterface> Interface = Blueprint->ImplementedInterfaces[InterfaceIndex].Interface)
|
|
{
|
|
FManagedListItem ItemDesc;
|
|
ItemDesc.ItemName = Interface->GetPathName();
|
|
ItemDesc.DisplayName = Interface->GetDisplayNameText();
|
|
ItemDesc.bIsRemovable = true;
|
|
const TSharedPtr<IPropertyHandle> Property = GetPropertyHandle()->GetChildHandle(InterfaceIndex);
|
|
if (Property && Property->IsValidHandle())
|
|
{
|
|
ItemDesc.PropertyHandles.Add(Property);
|
|
}
|
|
|
|
// Allow browsing to Blueprint interface class assets.
|
|
if (UBlueprintGeneratedClass* Class = Cast<UBlueprintGeneratedClass>(*Interface))
|
|
{
|
|
ItemDesc.AssetPtr = Class->ClassGeneratedBy;
|
|
}
|
|
|
|
OutListItems.Add(MoveTemp(ItemDesc));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Generate a list of interfaces implemented by classes this blueprint inherited from
|
|
UClass* BlueprintParent = Blueprint->ParentClass;
|
|
while (BlueprintParent)
|
|
{
|
|
for (TArray<FImplementedInterface>::TIterator It(BlueprintParent->Interfaces); It; ++It)
|
|
{
|
|
FImplementedInterface& CurrentInterface = *It;
|
|
if (CurrentInterface.Class)
|
|
{
|
|
FManagedListItem ItemDesc;
|
|
ItemDesc.ItemName = CurrentInterface.Class->GetPathName();
|
|
ItemDesc.DisplayName = CurrentInterface.Class->GetDisplayNameText();
|
|
ItemDesc.bIsRemovable = false;
|
|
|
|
OutListItems.Add(MoveTemp(ItemDesc));
|
|
}
|
|
}
|
|
|
|
BlueprintParent = BlueprintParent->GetSuperClass();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FBlueprintInterfaceLayout::OnRemoveItem(const FManagedListItem& Item)
|
|
{
|
|
const EAppReturnType::Type DialogReturn = FMessageDialog::Open(EAppMsgType::YesNoCancel, NSLOCTEXT("UnrealEd", "TransferInterfaceFunctionsToBlueprint", "Would you like to transfer the interface functions to be part of your blueprint?"));
|
|
|
|
if (DialogReturn == EAppReturnType::Cancel)
|
|
{
|
|
// We canceled!
|
|
return;
|
|
}
|
|
const FTopLevelAssetPath InterfacePathName(Item.ItemName);
|
|
|
|
UBlueprint* Blueprint = GetBlueprintObjectChecked();
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditorPtr = GetPinnedBlueprintEditorPtr();
|
|
if (BlueprintEditorPtr.IsValid())
|
|
{
|
|
// Close all graphs that are about to be removed
|
|
TArray<UEdGraph*> Graphs;
|
|
FBlueprintEditorUtils::GetInterfaceGraphs(Blueprint, InterfacePathName, Graphs);
|
|
for (TArray<UEdGraph*>::TIterator GraphIt(Graphs); GraphIt; ++GraphIt)
|
|
{
|
|
BlueprintEditorPtr->CloseDocumentTab(*GraphIt);
|
|
}
|
|
}
|
|
|
|
// Do the work of actually removing the interface
|
|
FBlueprintEditorUtils::RemoveInterface(Blueprint, InterfacePathName, DialogReturn == EAppReturnType::Yes);
|
|
|
|
RegenerateChildContent();
|
|
|
|
OnRefreshInDetailsView();
|
|
}
|
|
|
|
void FBlueprintInterfaceLayout::OnClassPicked(UClass* PickedClass)
|
|
{
|
|
if (AddInterfaceComboButton.IsValid())
|
|
{
|
|
AddInterfaceComboButton->SetIsOpen(false);
|
|
}
|
|
|
|
if (PickedClass)
|
|
{
|
|
UBlueprint* Blueprint = GetBlueprintObjectChecked();
|
|
|
|
FBlueprintEditorUtils::ImplementNewInterface(Blueprint, PickedClass->GetClassPathName());
|
|
|
|
RegenerateChildContent();
|
|
}
|
|
|
|
OnRefreshInDetailsView();
|
|
}
|
|
|
|
TSharedRef<SWidget> FBlueprintInterfaceLayout::OnGetAddInterfaceMenuContent()
|
|
{
|
|
UBlueprint* Blueprint = GetBlueprintObjectChecked();
|
|
|
|
TArray<UBlueprint*> Blueprints;
|
|
Blueprints.Add(Blueprint);
|
|
TSharedRef<SWidget> ClassPicker = FBlueprintEditorUtils::ConstructBlueprintInterfaceClassPicker(Blueprints, FOnClassPicked::CreateSP(this, &FBlueprintInterfaceLayout::OnClassPicked));
|
|
// Achieving fixed width by nesting items within a fixed width box.
|
|
return SNew(SBox)
|
|
.WidthOverride(350.0f)
|
|
[
|
|
SNew(SVerticalBox)
|
|
+SVerticalBox::Slot()
|
|
.MaxHeight(400.0f)
|
|
.AutoHeight()
|
|
[
|
|
ClassPicker
|
|
]
|
|
];
|
|
}
|
|
|
|
// Double the size of the default details view width for string values, which is otherwise too narrow since the customization adds a combo button.
|
|
float FBlueprintGlobalOptionsDetails::NamespacePropertyValueCustomization_MinDesiredWidth = 250.0f;
|
|
|
|
UBlueprint* FBlueprintGlobalOptionsDetails::GetBlueprintObj() const
|
|
{
|
|
if(UBlueprint* BP = BlueprintObjOverride.Get())
|
|
{
|
|
return BP;
|
|
}
|
|
|
|
if(BlueprintEditorPtr.IsValid())
|
|
{
|
|
return BlueprintEditorPtr.Pin()->GetBlueprintObj();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
FText FBlueprintGlobalOptionsDetails::GetParentClassName() const
|
|
{
|
|
const UBlueprint* Blueprint = GetBlueprintObj();
|
|
const UClass* ParentClass = Blueprint ? Blueprint->ParentClass : NULL;
|
|
return ParentClass ? ParentClass->GetDisplayNameText() : FText::FromName(NAME_None);
|
|
}
|
|
|
|
bool FBlueprintGlobalOptionsDetails::CanReparent() const
|
|
{
|
|
return BlueprintEditorPtr.IsValid() && BlueprintEditorPtr.Pin()->ReparentBlueprint_IsVisible();
|
|
}
|
|
|
|
TSharedRef<SWidget> FBlueprintGlobalOptionsDetails::GetParentClassMenuContent()
|
|
{
|
|
TArray<UBlueprint*> Blueprints;
|
|
Blueprints.Add(GetBlueprintObj());
|
|
TSharedRef<SWidget> ClassPicker = FBlueprintEditorUtils::ConstructBlueprintParentClassPicker(Blueprints, FOnClassPicked::CreateSP(this, &FBlueprintGlobalOptionsDetails::OnClassPicked));
|
|
|
|
// Achieving fixed width by nesting items within a fixed width box.
|
|
return SNew(SBox)
|
|
.WidthOverride(350.0f)
|
|
[
|
|
SNew(SVerticalBox)
|
|
+SVerticalBox::Slot()
|
|
.MaxHeight(400.0f)
|
|
.AutoHeight()
|
|
[
|
|
ClassPicker
|
|
]
|
|
];
|
|
}
|
|
|
|
void FBlueprintGlobalOptionsDetails::OnClassPicked(UClass* PickedClass)
|
|
{
|
|
ParentClassComboButton->SetIsOpen(false);
|
|
if(BlueprintEditorPtr.IsValid())
|
|
{
|
|
BlueprintEditorPtr.Pin()->ReparentBlueprint_NewParentChosen(PickedClass);
|
|
}
|
|
|
|
check(BlueprintEditorPtr.IsValid());
|
|
TSharedPtr<SKismetInspector> Inspector = BlueprintEditorPtr.Pin()->GetInspector();
|
|
// Show details for the Blueprint instance we're editing
|
|
Inspector->ShowDetailsForSingleObject(GetBlueprintObj());
|
|
}
|
|
|
|
bool FBlueprintGlobalOptionsDetails::CanDeprecateBlueprint() const
|
|
{
|
|
if (UBlueprint* Blueprint = GetBlueprintObj())
|
|
{
|
|
// If the parent is deprecated, we cannot modify deprecation on this Blueprint
|
|
if (Blueprint->ParentClass && Blueprint->ParentClass->HasAnyClassFlags(CLASS_Deprecated))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FBlueprintGlobalOptionsDetails::OnDeprecateBlueprint(ECheckBoxState InCheckState)
|
|
{
|
|
GetBlueprintObj()->bDeprecate = InCheckState == ECheckBoxState::Checked? true : false;
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetBlueprintObj());
|
|
}
|
|
|
|
ECheckBoxState FBlueprintGlobalOptionsDetails::IsDeprecatedBlueprint() const
|
|
{
|
|
if (UBlueprint* Blueprint = GetBlueprintObj())
|
|
{
|
|
return Blueprint->bDeprecate ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
|
|
}
|
|
return ECheckBoxState::Unchecked;
|
|
}
|
|
|
|
FText FBlueprintGlobalOptionsDetails::GetDeprecatedTooltip() const
|
|
{
|
|
if(CanDeprecateBlueprint())
|
|
{
|
|
return LOCTEXT("DeprecateBlueprintTooltip", "Deprecate the Blueprint and all child Blueprints to make it no longer placeable in the World nor child classes created from it.");
|
|
}
|
|
|
|
return LOCTEXT("DisabledDeprecateBlueprintTooltip", "This Blueprint is deprecated because of a parent, it is not possible to remove deprecation from it!");
|
|
}
|
|
|
|
void FBlueprintGlobalOptionsDetails::OnNamespaceValueCommitted(const FString& InNamespace)
|
|
{
|
|
if (UBlueprint* Blueprint = GetBlueprintObj())
|
|
{
|
|
// Skip if the value has not been changed.
|
|
const FString OldNamespace = Blueprint->BlueprintNamespace;
|
|
if (OldNamespace == InNamespace)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Update the current namespace value. This will handle pre/post-edit change notifications, etc.
|
|
check(NamespacePropertyHandle.IsValid());
|
|
NamespacePropertyHandle->SetValue(InNamespace);
|
|
|
|
HandleNamespaceValueChange(OldNamespace, InNamespace);
|
|
}
|
|
}
|
|
|
|
bool FBlueprintGlobalOptionsDetails::ShouldShowNamespaceResetToDefault() const
|
|
{
|
|
check(NamespacePropertyHandle.IsValid());
|
|
return NamespacePropertyHandle->CanResetToDefault();
|
|
}
|
|
|
|
void FBlueprintGlobalOptionsDetails::OnNamespaceResetToDefaultValue()
|
|
{
|
|
check(NamespacePropertyHandle.IsValid());
|
|
|
|
// Get the current value.
|
|
FString OriginalValue;
|
|
NamespacePropertyHandle->GetValue(OriginalValue);
|
|
|
|
// Standard reset-to-default path.
|
|
NamespacePropertyHandle->ResetToDefault();
|
|
|
|
// Get the value after having been reset.
|
|
FString DefaultNamespaceValue;
|
|
NamespacePropertyHandle->GetValue(DefaultNamespaceValue);
|
|
|
|
// Update the entry widget to reflect the new value.
|
|
if (NamespaceValueWidget.IsValid())
|
|
{
|
|
NamespaceValueWidget->SetCurrentNamespace(DefaultNamespaceValue);
|
|
}
|
|
|
|
HandleNamespaceValueChange(OriginalValue, DefaultNamespaceValue);
|
|
}
|
|
|
|
void FBlueprintGlobalOptionsDetails::HandleNamespaceValueChange(const FString& InOldValue, const FString& InNewValue)
|
|
{
|
|
// Refresh the namespace registry.
|
|
FBlueprintNamespaceRegistry& BlueprintNamespaceRegistry = FBlueprintNamespaceRegistry::Get();
|
|
if (!InOldValue.IsEmpty() && BlueprintNamespaceRegistry.IsRegisteredPath(InOldValue))
|
|
{
|
|
// @todo_namespaces - This may not scale for larger projects.
|
|
// Using a slow task for now, but consider optimizing this path.
|
|
FScopedSlowTask SlowTask(0.0f, LOCTEXT("RebuildingNamespaceRegistry", "Updating the namespace registry..."));
|
|
|
|
// The old path is a non-global registered namespace. Revisit all assets and ensure that the registry is up-to-date.
|
|
// If the old namespace is no longer in use by another Blueprint asset, this effectively removes it from the registry.
|
|
BlueprintNamespaceRegistry.Rebuild();
|
|
}
|
|
|
|
if (!InNewValue.IsEmpty() && !BlueprintNamespaceRegistry.IsRegisteredPath(InNewValue))
|
|
{
|
|
// Add the new namespace into the registry (it has not been explicitly added yet).
|
|
BlueprintNamespaceRegistry.RegisterNamespace(InNewValue);
|
|
}
|
|
|
|
// Refresh the Blueprint editor context.
|
|
TSharedPtr<FBlueprintEditor> BlueprintEditor = GetBlueprintEditorPtr().Pin();
|
|
if (BlueprintEditor.IsValid())
|
|
{
|
|
if (const UBlueprint* Blueprint = BlueprintEditor->GetBlueprintObj())
|
|
{
|
|
bool bRefreshDetailsView = false;
|
|
if (Blueprint->ImportedNamespaces.Contains(InOldValue))
|
|
{
|
|
// Remove the import from the current editor context if it is no longer inclusive of any path.
|
|
if (!BlueprintNamespaceRegistry.IsInclusivePath(InOldValue))
|
|
{
|
|
BlueprintEditor->RemoveNamespace(InOldValue);
|
|
}
|
|
|
|
// We need to refresh the details view if we unassigned and/or removed an imported namespace path.
|
|
// If unassigned but still imported, it will return to a non-default namespace in the Imports table.
|
|
bRefreshDetailsView = true;
|
|
}
|
|
|
|
if (Blueprint->ImportedNamespaces.Contains(InNewValue))
|
|
{
|
|
// We need to refresh the details view if we assigned an imported namespace.
|
|
// In that case, it will switch to a default namespace in the Imports table.
|
|
bRefreshDetailsView = true;
|
|
}
|
|
else
|
|
{
|
|
FBlueprintEditor::FImportNamespaceExParameters Params;
|
|
Params.bIsAutoImport = false;
|
|
Params.NamespacesToImport.Add(InNewValue);
|
|
Params.OnPostImportCallback = FSimpleDelegate::CreateLambda([&bRefreshDetailsView]()
|
|
{
|
|
bRefreshDetailsView = true;
|
|
});
|
|
|
|
// Import the new namespace into the current editor context.
|
|
BlueprintEditor->ImportNamespaceEx(Params);
|
|
}
|
|
|
|
// Refresh the details view if necessary.
|
|
if (bRefreshDetailsView)
|
|
{
|
|
BlueprintEditor->RefreshInspector();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FBlueprintGlobalOptionsDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout)
|
|
{
|
|
const UBlueprint* Blueprint = GetBlueprintObj();
|
|
if(Blueprint != NULL)
|
|
{
|
|
// Hide any properties that aren't included in the "Option" category
|
|
for (TFieldIterator<FProperty> PropertyIt(Blueprint->GetClass(), EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt)
|
|
{
|
|
FProperty* Property = *PropertyIt;
|
|
const FString& Category = Property->GetMetaData(TEXT("Category"));
|
|
|
|
if (Category != TEXT("BlueprintOptions") && Category != TEXT("ClassOptions"))
|
|
{
|
|
DetailLayout.HideProperty(DetailLayout.GetProperty(Property->GetFName()));
|
|
}
|
|
}
|
|
|
|
// Display the parent class and set up the menu for reparenting
|
|
IDetailCategoryBuilder& Category = DetailLayout.EditCategory("ClassOptions", LOCTEXT("ClassOptions", "Class Options"));
|
|
|
|
// ParentClass is a hidden property so we have to add it to the property map manually to use it
|
|
const TSharedPtr<IPropertyHandle> ParentClassProperty = DetailLayout.AddObjectPropertyData({const_cast<UBlueprint*>(Blueprint)}, TEXT("ParentClass"));
|
|
|
|
Category.AddCustomRow( LOCTEXT("ClassOptions", "Class Options") )
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("BlueprintDetails_ParentClass", "Parent Class"))
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.FillWidth(1.0f)
|
|
[
|
|
SAssignNew(ParentClassComboButton, SComboButton)
|
|
.IsEnabled(this, &FBlueprintGlobalOptionsDetails::CanReparent)
|
|
.OnGetMenuContent(this, &FBlueprintGlobalOptionsDetails::GetParentClassMenuContent)
|
|
.ButtonContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &FBlueprintGlobalOptionsDetails::GetParentClassName)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
]
|
|
]
|
|
.PropertyHandleList({ParentClassProperty});
|
|
|
|
const bool bIsInterfaceBP = FBlueprintEditorUtils::IsInterfaceBlueprint(Blueprint);
|
|
const bool bIsMacroLibrary = Blueprint->BlueprintType == BPTYPE_MacroLibrary;
|
|
const bool bIsLevelScriptBP = FBlueprintEditorUtils::IsLevelScriptBlueprint(Blueprint);
|
|
const bool bIsFunctionLibrary = Blueprint->BlueprintType == BPTYPE_FunctionLibrary;
|
|
|
|
// Interfaces/imports currently rely on the full Blueprint editor context to function properly (e.g. add/remove operations).
|
|
TSharedPtr<FBlueprintEditor> PinnedBlueprintEditorPtr = BlueprintEditorPtr.Pin();
|
|
if (PinnedBlueprintEditorPtr.IsValid() && PinnedBlueprintEditorPtr->GetCurrentMode() != FBlueprintEditorApplicationModes::BlueprintDefaultsMode)
|
|
{
|
|
const bool bSupportsInterfaces = !bIsInterfaceBP && !bIsMacroLibrary && !bIsFunctionLibrary;
|
|
const bool bSupportsNamespaces = GetDefault<UBlueprintEditorSettings>()->bEnableNamespaceImportingFeatures;
|
|
|
|
if (bSupportsNamespaces)
|
|
{
|
|
// Imported namespace details
|
|
IDetailCategoryBuilder& ImportsCategory = DetailLayout.EditCategory("Imports", LOCTEXT("BlueprintImportDetailsCategory", "Imports"));
|
|
|
|
TSharedRef<FBlueprintImportsLayout> DefaultImportsLayout = MakeShareable(new FBlueprintImportsLayout(SharedThis(this), /*bShowDefaultImports = */true));
|
|
ImportsCategory.AddCustomBuilder(DefaultImportsLayout);
|
|
|
|
TSharedRef<FBlueprintImportsLayout> LocalImportsLayout = MakeShareable(new FBlueprintImportsLayout(SharedThis(this), /*bShowDefaultImports = */false));
|
|
ImportsCategory.AddCustomBuilder(LocalImportsLayout);
|
|
}
|
|
|
|
if (bSupportsInterfaces)
|
|
{
|
|
// Interface details customization
|
|
IDetailCategoryBuilder& InterfacesCategory = DetailLayout.EditCategory("Interfaces", LOCTEXT("BlueprintInterfacesDetailsCategory", "Interfaces"));
|
|
|
|
// ImplementedInterfaces is a hidden property so we have to add it to the property map manually to use it
|
|
const TSharedPtr<IPropertyHandle> InterfacesProperty = DetailLayout.AddObjectPropertyData({ const_cast<UBlueprint*>(Blueprint) }, TEXT("ImplementedInterfaces"));
|
|
|
|
TSharedRef<FBlueprintInterfaceLayout> InheritedInterfacesLayout = MakeShareable(new FBlueprintInterfaceLayout(
|
|
SharedThis(this)
|
|
));
|
|
InterfacesCategory.AddCustomBuilder(InheritedInterfacesLayout);
|
|
|
|
TSharedRef<FBlueprintInterfaceLayout> LocalInterfacesLayout = MakeShareable(new FBlueprintInterfaceLayout(
|
|
SharedThis(this),
|
|
InterfacesProperty.ToSharedRef()
|
|
));
|
|
InterfacesCategory.AddCustomBuilder(LocalInterfacesLayout);
|
|
}
|
|
}
|
|
|
|
// Hide the bDeprecate, we override the functionality.
|
|
static FName DeprecatePropName(TEXT("bDeprecate"));
|
|
DetailLayout.HideProperty(DetailLayout.GetProperty(DeprecatePropName));
|
|
|
|
// Hide the experimental CompileMode setting (if not enabled)
|
|
const UBlueprintEditorSettings* EditorSettings = GetDefault<UBlueprintEditorSettings>();
|
|
if (EditorSettings && !EditorSettings->bAllowExplicitImpureNodeDisabling)
|
|
{
|
|
static FName CompileModePropertyName(TEXT("CompileMode"));
|
|
DetailLayout.HideProperty(DetailLayout.GetProperty(CompileModePropertyName));
|
|
}
|
|
|
|
// Hide 'run on drag' for LevelBP
|
|
if (bIsLevelScriptBP)
|
|
{
|
|
static FName RunOnDragPropName(TEXT("bRunConstructionScriptOnDrag"));
|
|
DetailLayout.HideProperty(DetailLayout.GetProperty(RunOnDragPropName));
|
|
}
|
|
else
|
|
{
|
|
// Only display the ability to deprecate a Blueprint on non-level Blueprints.
|
|
Category.AddCustomRow( LOCTEXT("DeprecateLabel", "Deprecate"), true )
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT("DeprecateLabel", "Deprecate") )
|
|
.ToolTipText( this, &FBlueprintGlobalOptionsDetails::GetDeprecatedTooltip )
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SCheckBox)
|
|
.IsEnabled( this, &FBlueprintGlobalOptionsDetails::CanDeprecateBlueprint )
|
|
.IsChecked( this, &FBlueprintGlobalOptionsDetails::IsDeprecatedBlueprint )
|
|
.OnCheckStateChanged( this, &FBlueprintGlobalOptionsDetails::OnDeprecateBlueprint )
|
|
.ToolTipText( this, &FBlueprintGlobalOptionsDetails::GetDeprecatedTooltip )
|
|
]
|
|
.PropertyHandleList({DetailLayout.GetProperty(DeprecatePropName)});
|
|
}
|
|
|
|
static FName BlueprintNamespacePropertyName = GET_MEMBER_NAME_CHECKED(UBlueprint, BlueprintNamespace);
|
|
NamespacePropertyHandle = DetailLayout.GetProperty(BlueprintNamespacePropertyName);
|
|
DetailLayout.EditDefaultProperty(NamespacePropertyHandle)->CustomWidget()
|
|
.NameContent()
|
|
[
|
|
NamespacePropertyHandle->CreatePropertyNameWidget()
|
|
]
|
|
.ValueContent()
|
|
.MinDesiredWidth(NamespacePropertyValueCustomization_MinDesiredWidth)
|
|
[
|
|
SAssignNew(NamespaceValueWidget, SBlueprintNamespaceEntry)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
.AllowTextEntry(true)
|
|
.CurrentNamespace(Blueprint->BlueprintNamespace)
|
|
.OnNamespaceSelected(this, &FBlueprintGlobalOptionsDetails::OnNamespaceValueCommitted)
|
|
]
|
|
.OverrideResetToDefault(FResetToDefaultOverride::Create(
|
|
TAttribute<bool>(this, &FBlueprintGlobalOptionsDetails::ShouldShowNamespaceResetToDefault),
|
|
FSimpleDelegate::CreateSP(this, &FBlueprintGlobalOptionsDetails::OnNamespaceResetToDefaultValue))
|
|
);
|
|
}
|
|
}
|
|
|
|
void FBlueprintComponentDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout)
|
|
{
|
|
check( BlueprintEditorPtr.IsValid() );
|
|
TSharedPtr<SSubobjectEditor> Editor = BlueprintEditorPtr.Pin()->GetSubobjectEditor();
|
|
check( Editor.IsValid() );
|
|
const UBlueprint* BlueprintObj = GetBlueprintObj();
|
|
check(BlueprintObj != nullptr);
|
|
|
|
TArray<FSubobjectEditorTreeNodePtrType> Nodes = Editor->GetSelectedNodes();
|
|
|
|
if (!Nodes.Num())
|
|
{
|
|
CachedNodePtr = nullptr;
|
|
}
|
|
else if (Nodes.Num() == 1)
|
|
{
|
|
CachedNodePtr = Nodes[0];
|
|
}
|
|
|
|
if( CachedNodePtr.IsValid() )
|
|
{
|
|
IDetailCategoryBuilder& VariableCategory = DetailLayout.EditCategory("Variable", LOCTEXT("VariableDetailsCategory", "Variable"), ECategoryPriority::Variable);
|
|
|
|
VariableNameEditableTextBox = SNew(SEditableTextBox)
|
|
.Text(this, &FBlueprintComponentDetails::OnGetVariableText)
|
|
.OnTextChanged(this, &FBlueprintComponentDetails::OnVariableTextChanged)
|
|
.OnTextCommitted(this, &FBlueprintComponentDetails::OnVariableTextCommitted)
|
|
.IsReadOnly(!CachedNodePtr->CanRename())
|
|
.Font(IDetailLayoutBuilder::GetDetailFont());
|
|
|
|
VariableCategory.AddCustomRow(LOCTEXT("BlueprintComponentDetails_VariableNameLabel", "Variable Name"))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("BlueprintComponentDetails_VariableNameLabel", "Variable Name"))
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
VariableNameEditableTextBox.ToSharedRef()
|
|
];
|
|
|
|
VariableCategory.AddCustomRow(LOCTEXT("BlueprintComponentDetails_VariableTooltipLabel", "Tooltip"))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("BlueprintComponentDetails_VariableTooltipLabel", "Tooltip"))
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SEditableTextBox)
|
|
.Text(this, &FBlueprintComponentDetails::OnGetTooltipText)
|
|
.OnTextCommitted(this, &FBlueprintComponentDetails::OnTooltipTextCommitted, CachedNodePtr->GetVariableName())
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
];
|
|
|
|
PopulateVariableCategories();
|
|
const FText CategoryTooltip = LOCTEXT("EditCategoryName_Tooltip", "The category of the variable; editing this will place the variable into another category or create a new one.");
|
|
|
|
VariableCategory.AddCustomRow( LOCTEXT("BlueprintComponentDetails_VariableCategoryLabel", "Category") )
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("BlueprintComponentDetails_VariableCategoryLabel", "Category"))
|
|
.ToolTipText(CategoryTooltip)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SAssignNew(VariableCategoryComboButton, SComboButton)
|
|
.ContentPadding(FMargin(0,0,5,0))
|
|
.IsEnabled(this, &FBlueprintComponentDetails::OnVariableCategoryChangeEnabled)
|
|
.ButtonContent()
|
|
[
|
|
SNew(SBorder)
|
|
.BorderImage(FAppStyle::GetBrush("NoBorder"))
|
|
.Padding(FMargin(0, 0, 5, 0))
|
|
[
|
|
SNew(SEditableTextBox)
|
|
.Text(this, &FBlueprintComponentDetails::OnGetVariableCategoryText)
|
|
.OnTextCommitted(this, &FBlueprintComponentDetails::OnVariableCategoryTextCommitted, CachedNodePtr->GetVariableName())
|
|
.OnVerifyTextChanged_Lambda([&](const FText& InNewText, FText& OutErrorMessage) -> bool
|
|
{
|
|
if (InNewText.IsEmpty())
|
|
{
|
|
OutErrorMessage = LOCTEXT("CategoryEmpty", "Cannot add a category with an empty string.");
|
|
return false;
|
|
}
|
|
if (InNewText.EqualTo(FText::FromString(GetBlueprintObj()->GetName())))
|
|
{
|
|
OutErrorMessage = LOCTEXT("CategoryEqualsBlueprintName", "Cannot add a category with the same name as the blueprint.");
|
|
return false;
|
|
}
|
|
return true;
|
|
})
|
|
.ToolTipText(CategoryTooltip)
|
|
.SelectAllTextWhenFocused(true)
|
|
.RevertTextOnEscape(true)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
]
|
|
.MenuContent()
|
|
[
|
|
SNew(SVerticalBox)
|
|
+SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.MaxHeight(400.0f)
|
|
[
|
|
SAssignNew(VariableCategoryListView, SListView<TSharedPtr<FText>>)
|
|
.ListItemsSource(&VariableCategorySource)
|
|
.OnGenerateRow(this, &FBlueprintComponentDetails::MakeVariableCategoryViewWidget)
|
|
.OnSelectionChanged(this, &FBlueprintComponentDetails::OnVariableCategorySelectionChanged)
|
|
]
|
|
]
|
|
];
|
|
|
|
// Keep an easy way to disable UI to specify overriden component class until there is confidence that it is robust
|
|
if (GetAllowNativeComponentClassOverrides())
|
|
{
|
|
const UActorComponent* ComponentTemplate = (CachedNodePtr->IsNativeComponent() ? CachedNodePtr->GetComponentTemplate() : nullptr);
|
|
if (ComponentTemplate)
|
|
{
|
|
UClass* BaseClass = ComponentTemplate->GetClass();
|
|
|
|
if (const FBPComponentClassOverride* Override = BlueprintObj->ComponentClassOverrides.FindByKey(ComponentTemplate->GetFName()))
|
|
{
|
|
AActor* Owner = ComponentTemplate->GetOwner();
|
|
AActor* OwnerArchetype = CastChecked<AActor>(Owner->GetArchetype());
|
|
if (UActorComponent* ArchetypeComponent = Cast<UActorComponent>((UObject*)FindObjectWithOuter(OwnerArchetype, UActorComponent::StaticClass(), ComponentTemplate->GetFName())))
|
|
{
|
|
BaseClass = ArchetypeComponent->GetClass();
|
|
}
|
|
}
|
|
|
|
const FText ComponentClassTooltip = LOCTEXT("BlueprintComponentDetails_ComponentClassOverrideTooltip", "The class to use when creating this component for this class. This can only be done for components defined in native at this time.");
|
|
|
|
VariableCategory.AddCustomRow( LOCTEXT("BlueprintComponentDetails_ComponentClassOverride", "Component Class") )
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("BlueprintComponentDetails_ComponentClassOverrideLabel", "Component Class"))
|
|
.ToolTipText(ComponentClassTooltip)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SClassPropertyEntryBox)
|
|
.MetaClass(BaseClass)
|
|
.AllowNone(false)
|
|
.SelectedClass(this, &FBlueprintComponentDetails::GetSelectedEntryClass)
|
|
.OnSetClass(this, &FBlueprintComponentDetails::HandleNewEntryClassSelected)];
|
|
}
|
|
}
|
|
|
|
IDetailCategoryBuilder& SocketsCategory = DetailLayout.EditCategory("Sockets", LOCTEXT("BlueprintComponentDetailsCategory", "Sockets"), ECategoryPriority::Important);
|
|
|
|
SocketsCategory.AddCustomRow(LOCTEXT("BlueprintComponentDetails_Sockets", "Sockets"))
|
|
.RowTag("ParentSocket")
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("BlueprintComponentDetails_ParentSocket", "Parent Socket"))
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.FillWidth(1.0f)
|
|
[
|
|
SNew(SEditableTextBox)
|
|
.Text(this, &FBlueprintComponentDetails::GetSocketName)
|
|
.IsReadOnly(true)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.HAlign(HAlign_Center)
|
|
.VAlign(VAlign_Center)
|
|
.Padding(2.0f, 1.0f)
|
|
[
|
|
PropertyCustomizationHelpers::MakeBrowseButton(
|
|
FSimpleDelegate::CreateSP(this, &FBlueprintComponentDetails::OnBrowseSocket),
|
|
LOCTEXT( "SocketBrowseButtonToolTipText", "Select a different Parent Socket - cannot change socket on inherited components"),
|
|
TAttribute<bool>(this, &FBlueprintComponentDetails::CanChangeSocket)
|
|
)
|
|
]
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.HAlign(HAlign_Center)
|
|
.VAlign(VAlign_Center)
|
|
.Padding(2.0f, 1.0f)
|
|
[
|
|
PropertyCustomizationHelpers::MakeClearButton(
|
|
FSimpleDelegate::CreateSP(this, &FBlueprintComponentDetails::OnClearSocket),
|
|
LOCTEXT("SocketClearButtonToolTipText", "Clear the Parent Socket - cannot change socket on inherited components"),
|
|
TAttribute<bool>(this, &FBlueprintComponentDetails::CanChangeSocket)
|
|
)
|
|
]
|
|
];
|
|
}
|
|
|
|
// Handle event generation
|
|
if ( FBlueprintEditorUtils::DoesSupportEventGraphs(BlueprintObj) && Nodes.Num() == 1 )
|
|
{
|
|
// Use the component template to support native components as well
|
|
if (const UActorComponent* ComponentTemplate = CachedNodePtr->GetComponentTemplate())
|
|
{
|
|
AddEventsCategory(DetailLayout, CachedNodePtr->GetVariableName(), ComponentTemplate->GetClass());
|
|
}
|
|
}
|
|
|
|
TSharedPtr<IPropertyHandle> PrimaryTickProperty = DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(UActorComponent, PrimaryComponentTick));
|
|
|
|
if (PrimaryTickProperty->IsValidHandle())
|
|
{
|
|
IDetailCategoryBuilder& TickCategory = DetailLayout.EditCategory("ComponentTick");
|
|
|
|
TickCategory.AddProperty(PrimaryTickProperty->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTickFunction, bStartWithTickEnabled)));
|
|
TickCategory.AddProperty(PrimaryTickProperty->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTickFunction, TickInterval)));
|
|
TickCategory.AddProperty(PrimaryTickProperty->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTickFunction, bTickEvenWhenPaused)), EPropertyLocation::Advanced);
|
|
TickCategory.AddProperty(PrimaryTickProperty->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTickFunction, bAllowTickOnDedicatedServer)), EPropertyLocation::Advanced);
|
|
TickCategory.AddProperty(PrimaryTickProperty->GetChildHandle(GET_MEMBER_NAME_CHECKED(FTickFunction, TickGroup)), EPropertyLocation::Advanced);
|
|
}
|
|
|
|
PrimaryTickProperty->MarkHiddenByCustomization();
|
|
}
|
|
|
|
FText FBlueprintComponentDetails::OnGetVariableText() const
|
|
{
|
|
check(CachedNodePtr.IsValid());
|
|
|
|
return FText::FromName(CachedNodePtr->GetVariableName());
|
|
}
|
|
|
|
void FBlueprintComponentDetails::OnVariableTextChanged(const FText& InNewText)
|
|
{
|
|
check(CachedNodePtr.IsValid());
|
|
|
|
bIsVariableNameInvalid = true;
|
|
|
|
const FString& NewTextStr = InNewText.ToString();
|
|
|
|
if (USubobjectDataSubsystem* System = USubobjectDataSubsystem::Get())
|
|
{
|
|
FText ErrorMsg;
|
|
if (!System->IsValidRename(CachedNodePtr->GetDataHandle(), InNewText, ErrorMsg))
|
|
{
|
|
VariableNameEditableTextBox->SetError(ErrorMsg);
|
|
return;
|
|
}
|
|
}
|
|
|
|
TSharedPtr<INameValidatorInterface> VariableNameValidator = MakeShareable(new FKismetNameValidator(GetBlueprintObj(), CachedNodePtr->GetVariableName()));
|
|
|
|
EValidatorResult ValidatorResult = VariableNameValidator->IsValid(NewTextStr);
|
|
if(ValidatorResult == EValidatorResult::AlreadyInUse)
|
|
{
|
|
VariableNameEditableTextBox->SetError(FText::Format(LOCTEXT("ComponentVariableRenameFailed_InUse", "{0} is in use by another variable or function!"), InNewText));
|
|
}
|
|
else if(ValidatorResult == EValidatorResult::EmptyName)
|
|
{
|
|
VariableNameEditableTextBox->SetError(LOCTEXT("RenameFailed_LeftBlank", "Names cannot be left blank!"));
|
|
}
|
|
else if(ValidatorResult == EValidatorResult::TooLong)
|
|
{
|
|
VariableNameEditableTextBox->SetError(FText::Format( LOCTEXT("RenameFailed_NameTooLong", "Names must have fewer than {0} characters!"), FText::AsNumber( FKismetNameValidator::GetMaximumNameLength())));
|
|
}
|
|
else
|
|
{
|
|
bIsVariableNameInvalid = false;
|
|
VariableNameEditableTextBox->SetError(FText::GetEmpty());
|
|
}
|
|
}
|
|
|
|
void FBlueprintComponentDetails::OnVariableTextCommitted(const FText& InNewName, ETextCommit::Type InTextCommit)
|
|
{
|
|
if (!bIsVariableNameInvalid)
|
|
{
|
|
check(CachedNodePtr.IsValid());
|
|
|
|
const FScopedTransaction Transaction(LOCTEXT("RenameComponentVariable", "Rename Component Variable"));
|
|
USubobjectDataSubsystem::RenameSubobjectMemberVariable(GetBlueprintObj(), CachedNodePtr->GetDataHandle(), FName(*InNewName.ToString()));
|
|
}
|
|
|
|
bIsVariableNameInvalid = false;
|
|
VariableNameEditableTextBox->SetError(FText::GetEmpty());
|
|
}
|
|
|
|
FText FBlueprintComponentDetails::OnGetTooltipText() const
|
|
{
|
|
check(CachedNodePtr.IsValid());
|
|
|
|
FName VarName = CachedNodePtr->GetVariableName();
|
|
if (VarName != NAME_None)
|
|
{
|
|
FString Result;
|
|
FBlueprintEditorUtils::GetBlueprintVariableMetaData(GetBlueprintObj(), VarName, NULL, TEXT("tooltip"), Result);
|
|
return FText::FromString(Result);
|
|
}
|
|
|
|
return FText();
|
|
}
|
|
|
|
void FBlueprintComponentDetails::OnTooltipTextCommitted(const FText& NewText, ETextCommit::Type InTextCommit, FName VarName)
|
|
{
|
|
FBlueprintEditorUtils::SetBlueprintVariableMetaData(GetBlueprintObj(), VarName, NULL, TEXT("tooltip"), NewText.ToString() );
|
|
}
|
|
|
|
bool FBlueprintComponentDetails::OnVariableCategoryChangeEnabled() const
|
|
{
|
|
check(CachedNodePtr.IsValid());
|
|
const FSubobjectData* Data = CachedNodePtr->GetDataSource();
|
|
return !Data->IsInheritedComponent();
|
|
}
|
|
|
|
FText FBlueprintComponentDetails::OnGetVariableCategoryText() const
|
|
{
|
|
check(CachedNodePtr.IsValid());
|
|
|
|
FName VarName = CachedNodePtr->GetVariableName();
|
|
if (VarName != NAME_None)
|
|
{
|
|
FText Category = FBlueprintEditorUtils::GetBlueprintVariableCategory(GetBlueprintObj(), VarName, NULL);
|
|
|
|
// Older blueprints will have their name as the default category
|
|
if( Category.EqualTo(FText::FromString(GetBlueprintObj()->GetName())) )
|
|
{
|
|
return UEdGraphSchema_K2::VR_DefaultCategory;
|
|
}
|
|
else
|
|
{
|
|
return Category;
|
|
}
|
|
}
|
|
|
|
return FText();
|
|
}
|
|
|
|
void FBlueprintComponentDetails::OnVariableCategoryTextCommitted(const FText& NewText, ETextCommit::Type InTextCommit, FName VarName)
|
|
{
|
|
check(CachedNodePtr.IsValid());
|
|
|
|
if (InTextCommit == ETextCommit::OnEnter || InTextCommit == ETextCommit::OnUserMovedFocus)
|
|
{
|
|
FBlueprintEditorUtils::SetBlueprintVariableCategory(GetBlueprintObj(), CachedNodePtr->GetVariableName(), NULL, NewText);
|
|
PopulateVariableCategories();
|
|
}
|
|
}
|
|
|
|
void FBlueprintComponentDetails::OnVariableCategorySelectionChanged( TSharedPtr<FText> ProposedSelection, ESelectInfo::Type /*SelectInfo*/ )
|
|
{
|
|
check(CachedNodePtr.IsValid());
|
|
|
|
FName VarName = CachedNodePtr->GetVariableName();
|
|
if (ProposedSelection.IsValid() && VarName != NAME_None)
|
|
{
|
|
FText NewCategory = *ProposedSelection.Get();
|
|
FBlueprintEditorUtils::SetBlueprintVariableCategory(GetBlueprintObj(), VarName, NULL, NewCategory);
|
|
|
|
check(VariableCategoryListView.IsValid());
|
|
check(VariableCategoryComboButton.IsValid());
|
|
|
|
VariableCategoryListView->ClearSelection();
|
|
VariableCategoryComboButton->SetIsOpen(false);
|
|
}
|
|
}
|
|
|
|
TSharedRef< ITableRow > FBlueprintComponentDetails::MakeVariableCategoryViewWidget( TSharedPtr<FText> Item, const TSharedRef< STableViewBase >& OwnerTable )
|
|
{
|
|
return SNew(STableRow<TSharedPtr<FString>>, OwnerTable)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(*Item.Get())
|
|
];
|
|
}
|
|
|
|
void FBlueprintComponentDetails::PopulateVariableCategories()
|
|
{
|
|
auto IsNewCategorySource = [this](const FText& NewCategory)
|
|
{
|
|
return !VariableCategorySource.ContainsByPredicate([&NewCategory](const TSharedPtr<FText>& ExistingCategory)
|
|
{
|
|
return ExistingCategory->ToString().Equals(NewCategory.ToString(), ESearchCase::CaseSensitive);
|
|
});
|
|
};
|
|
|
|
UBlueprint* BlueprintObj = GetBlueprintObj();
|
|
|
|
check(BlueprintObj);
|
|
check(BlueprintObj->SkeletonGeneratedClass);
|
|
|
|
TSet<FName> VisibleVariables;
|
|
for (TFieldIterator<FProperty> PropertyIt(BlueprintObj->SkeletonGeneratedClass, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt)
|
|
{
|
|
FProperty* Property = *PropertyIt;
|
|
|
|
if ((!Property->HasAnyPropertyFlags(CPF_Parm) && Property->HasAllPropertyFlags(CPF_BlueprintVisible)))
|
|
{
|
|
VisibleVariables.Add(Property->GetFName());
|
|
}
|
|
}
|
|
|
|
FBlueprintEditorUtils::GetSCSVariableNameList(BlueprintObj, VisibleVariables);
|
|
|
|
VariableCategorySource.Reset();
|
|
VariableCategorySource.Add(MakeShared<FText>(UEdGraphSchema_K2::VR_DefaultCategory));
|
|
for (const FName& VariableName : VisibleVariables)
|
|
{
|
|
FText Category = FBlueprintEditorUtils::GetBlueprintVariableCategory(BlueprintObj, VariableName, nullptr);
|
|
if (!Category.IsEmpty() && !Category.EqualTo(FText::FromString(BlueprintObj->GetName())))
|
|
{
|
|
if (IsNewCategorySource(Category))
|
|
{
|
|
VariableCategorySource.Add(MakeShared<FText>(Category));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort categories, but keep the default category listed first
|
|
VariableCategorySource.Sort([](const TSharedPtr <FText> &LHS, const TSharedPtr <FText> &RHS)
|
|
{
|
|
if (LHS.IsValid() && RHS.IsValid())
|
|
{
|
|
return (LHS->EqualTo(UEdGraphSchema_K2::VR_DefaultCategory) || LHS->CompareToCaseIgnored(*RHS) <= 0);
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
|
|
const UClass* FBlueprintComponentDetails::GetSelectedEntryClass() const
|
|
{
|
|
check(CachedNodePtr.IsValid());
|
|
|
|
if (const UActorComponent* ComponentTemplate = CachedNodePtr->GetComponentTemplate())
|
|
{
|
|
UBlueprint* BlueprintObj = GetBlueprintObj();
|
|
check(BlueprintObj);
|
|
|
|
do
|
|
{
|
|
if (const FBPComponentClassOverride* Override = BlueprintObj->ComponentClassOverrides.FindByKey(ComponentTemplate->GetFName()))
|
|
{
|
|
return Override->ComponentClass;
|
|
}
|
|
|
|
BlueprintObj = UBlueprint::GetBlueprintFromClass(Cast<UBlueprintGeneratedClass>(BlueprintObj->ParentClass));
|
|
|
|
} while (BlueprintObj);
|
|
|
|
return ComponentTemplate->GetClass();
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
void FBlueprintComponentDetails::HandleNewEntryClassSelected(const UClass* NewEntryClass) const
|
|
{
|
|
const UClass* PreviousClass = GetSelectedEntryClass();
|
|
|
|
if (PreviousClass != NewEntryClass)
|
|
{
|
|
check(CachedNodePtr.IsValid());
|
|
|
|
if (const UActorComponent* ComponentTemplate = CachedNodePtr->GetComponentTemplate())
|
|
{
|
|
if (USubobjectDataSubsystem* System = USubobjectDataSubsystem::Get())
|
|
{
|
|
System->ChangeSubobjectClass(CachedNodePtr->GetDataHandle(), NewEntryClass);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
FText FBlueprintComponentDetails::GetSocketName() const
|
|
{
|
|
check(CachedNodePtr.IsValid());
|
|
if (FSubobjectData* Data = CachedNodePtr->GetDataSource())
|
|
{
|
|
return Data->GetSocketName();
|
|
}
|
|
return FText::GetEmpty();
|
|
}
|
|
|
|
bool FBlueprintComponentDetails::CanChangeSocket() const
|
|
{
|
|
check(CachedNodePtr.IsValid());
|
|
|
|
if (FSubobjectData* Data = CachedNodePtr->GetDataSource())
|
|
{
|
|
return !Data->IsInheritedComponent();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void FBlueprintComponentDetails::OnBrowseSocket()
|
|
{
|
|
check(CachedNodePtr.IsValid());
|
|
FSubobjectData* Data = CachedNodePtr->GetDataSource();
|
|
if (Data && Data->HasValidSocket())
|
|
{
|
|
TSharedPtr<SSubobjectEditor> Editor = BlueprintEditorPtr.Pin()->GetSubobjectEditor();
|
|
check(Editor.IsValid());
|
|
|
|
FSubobjectEditorTreeNodePtrType ParentFNode = CachedNodePtr->GetParent();
|
|
FSubobjectData* ParentData = ParentFNode.IsValid() ? ParentFNode->GetDataSource() : nullptr;
|
|
if (ParentData)
|
|
{
|
|
// #TODO_BH Remove const cast
|
|
if (USceneComponent* ParentSceneComponent = const_cast<USceneComponent*>(ParentData->GetObjectForBlueprint<USceneComponent>(Editor->GetBlueprint())))
|
|
{
|
|
if (ParentSceneComponent->HasAnySockets())
|
|
{
|
|
// Pop up a combo box to pick socket from mesh
|
|
FSlateApplication::Get().PushMenu(
|
|
BlueprintEditorPtr.Pin()->GetToolkitHost()->GetParentWidget(),
|
|
FWidgetPath(),
|
|
SNew(SSocketChooserPopup)
|
|
.SceneComponent( ParentSceneComponent )
|
|
.OnSocketChosen( this, &FBlueprintComponentDetails::OnSocketSelection ),
|
|
FSlateApplication::Get().GetCursorPos(),
|
|
FPopupTransitionEffect( FPopupTransitionEffect::TypeInPopup )
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FBlueprintComponentDetails::OnClearSocket()
|
|
{
|
|
check(CachedNodePtr.IsValid());
|
|
FSubobjectData* Data = CachedNodePtr->GetDataSource();
|
|
|
|
if (Data && Data->HasValidSocket())
|
|
{
|
|
Data->SetupAttachment(NAME_None);
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetBlueprintObj());
|
|
}
|
|
}
|
|
|
|
void FBlueprintComponentDetails::OnSocketSelection(FName SocketName)
|
|
{
|
|
check(CachedNodePtr.IsValid());
|
|
|
|
FSubobjectData* Data = CachedNodePtr->GetDataSource();
|
|
if (Data && Data->HasValidSocket())
|
|
{
|
|
// Record selection if there is an actual asset attached
|
|
Data->SetSocketName(SocketName);
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetBlueprintObj());
|
|
}
|
|
}
|
|
|
|
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
void FBlueprintGraphNodeDetails::CustomizeDetails( IDetailLayoutBuilder& DetailLayout )
|
|
{
|
|
const TArray<TWeakObjectPtr<UObject>>& SelectedObjects = DetailLayout.GetSelectedObjects();
|
|
if( SelectedObjects.Num() == 1 )
|
|
{
|
|
if (SelectedObjects[0].IsValid() && SelectedObjects[0]->IsA<UEdGraphNode>())
|
|
{
|
|
GraphNodePtr = Cast<UEdGraphNode>(SelectedObjects[0].Get());
|
|
}
|
|
}
|
|
|
|
if(!GraphNodePtr.IsValid() || !GraphNodePtr.Get()->GetCanRenameNode())
|
|
{
|
|
return;
|
|
}
|
|
|
|
IDetailCategoryBuilder& Category = DetailLayout.EditCategory("GraphNodeDetail", LOCTEXT("GraphNodeDetailsCategory", "Graph Node"), ECategoryPriority::Important);
|
|
const FSlateFontInfo DetailFontInfo = IDetailLayoutBuilder::GetDetailFont();
|
|
FText RowHeader;
|
|
FText NameContent;
|
|
|
|
if( GraphNodePtr->IsA( UEdGraphNode_Comment::StaticClass() ))
|
|
{
|
|
RowHeader = LOCTEXT("GraphNodeDetail_CommentRowTitle", "Comment");
|
|
NameContent = LOCTEXT("GraphNodeDetail_CommentContentTitle", "Comment Text");
|
|
}
|
|
else
|
|
{
|
|
RowHeader = LOCTEXT("GraphNodeDetail_NodeRowTitle", "Node Title");
|
|
NameContent = LOCTEXT("GraphNodeDetail_ContentTitle", "Name");
|
|
}
|
|
|
|
bool bNameAllowsMultiLine = false;
|
|
if( GraphNodePtr.IsValid() && GraphNodePtr.Get()->IsA<UEdGraphNode_Comment>() )
|
|
{
|
|
bNameAllowsMultiLine = true;
|
|
}
|
|
|
|
TSharedPtr<SWidget> EditNameWidget;
|
|
float WidgetMinDesiredWidth = BlueprintDocumentationDetailDefs::DetailsTitleMinWidth;
|
|
float WidgetMaxDesiredWidth = BlueprintDocumentationDetailDefs::DetailsTitleMaxWidth;
|
|
if( bNameAllowsMultiLine )
|
|
{
|
|
SAssignNew(MultiLineNameEditableTextBox, SMultiLineEditableTextBox)
|
|
.Text(this, &FBlueprintGraphNodeDetails::OnGetName)
|
|
.OnTextChanged(this, &FBlueprintGraphNodeDetails::OnNameChanged)
|
|
.OnTextCommitted(this, &FBlueprintGraphNodeDetails::OnNameCommitted)
|
|
.ClearKeyboardFocusOnCommit(true)
|
|
.ModiferKeyForNewLine(EModifierKey::Shift)
|
|
.RevertTextOnEscape(true)
|
|
.SelectAllTextWhenFocused(true)
|
|
.IsReadOnly(this, &FBlueprintGraphNodeDetails::IsNameReadOnly)
|
|
.Font(DetailFontInfo)
|
|
.WrapTextAt(WidgetMaxDesiredWidth - BlueprintDocumentationDetailDefs::DetailsTitleWrapPadding);
|
|
|
|
EditNameWidget = MultiLineNameEditableTextBox;
|
|
}
|
|
else
|
|
{
|
|
SAssignNew(NameEditableTextBox, SEditableTextBox)
|
|
.Text(this, &FBlueprintGraphNodeDetails::OnGetName)
|
|
.OnTextChanged(this, &FBlueprintGraphNodeDetails::OnNameChanged)
|
|
.OnTextCommitted(this, &FBlueprintGraphNodeDetails::OnNameCommitted)
|
|
.Font(DetailFontInfo);
|
|
|
|
EditNameWidget = NameEditableTextBox;
|
|
WidgetMaxDesiredWidth = WidgetMinDesiredWidth;
|
|
}
|
|
|
|
Category.AddCustomRow( RowHeader )
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( NameContent )
|
|
.Font(DetailFontInfo)
|
|
]
|
|
.ValueContent()
|
|
.MinDesiredWidth(WidgetMinDesiredWidth)
|
|
.MaxDesiredWidth(WidgetMaxDesiredWidth)
|
|
[
|
|
EditNameWidget.ToSharedRef()
|
|
];
|
|
}
|
|
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
|
|
void FBlueprintGraphNodeDetails::SetNameError( const FText& Error )
|
|
{
|
|
if( NameEditableTextBox.IsValid() )
|
|
{
|
|
NameEditableTextBox->SetError( Error );
|
|
}
|
|
if( MultiLineNameEditableTextBox.IsValid() )
|
|
{
|
|
MultiLineNameEditableTextBox->SetError( Error );
|
|
}
|
|
}
|
|
|
|
bool FBlueprintGraphNodeDetails::IsNameReadOnly() const
|
|
{
|
|
bool bReadOnly = true;
|
|
if(GraphNodePtr.IsValid())
|
|
{
|
|
bReadOnly = !GraphNodePtr->bCanRenameNode;
|
|
}
|
|
return bReadOnly;
|
|
}
|
|
|
|
FText FBlueprintGraphNodeDetails::OnGetName() const
|
|
{
|
|
FText Name;
|
|
if(GraphNodePtr.IsValid())
|
|
{
|
|
Name = GraphNodePtr->GetNodeTitle( ENodeTitleType::EditableTitle );
|
|
}
|
|
return Name;
|
|
}
|
|
|
|
struct FGraphNodeNameValidatorHelper
|
|
{
|
|
static EValidatorResult Validate(TWeakObjectPtr<UEdGraphNode> GraphNodePtr, TWeakPtr<FBlueprintEditor> BlueprintEditorPtr, const FString& NewName)
|
|
{
|
|
check(GraphNodePtr.IsValid() && BlueprintEditorPtr.IsValid());
|
|
TSharedPtr<INameValidatorInterface> NameValidator = GraphNodePtr->MakeNameValidator();
|
|
if (!NameValidator.IsValid())
|
|
{
|
|
const FName NodeName(*GraphNodePtr->GetNodeTitle(ENodeTitleType::EditableTitle).ToString());
|
|
NameValidator = MakeShareable(new FKismetNameValidator(BlueprintEditorPtr.Pin()->GetBlueprintObj(), NodeName));
|
|
}
|
|
return NameValidator->IsValid(NewName);
|
|
}
|
|
};
|
|
|
|
void FBlueprintGraphNodeDetails::OnNameChanged(const FText& InNewText)
|
|
{
|
|
if( GraphNodePtr.IsValid() && BlueprintEditorPtr.IsValid() )
|
|
{
|
|
const EValidatorResult ValidatorResult = FGraphNodeNameValidatorHelper::Validate(GraphNodePtr, BlueprintEditorPtr, InNewText.ToString());
|
|
if(ValidatorResult == EValidatorResult::AlreadyInUse)
|
|
{
|
|
SetNameError(FText::Format(LOCTEXT("RenameFailed_InUse", "{0} is in use by another variable or function!"), InNewText));
|
|
}
|
|
else if(ValidatorResult == EValidatorResult::EmptyName)
|
|
{
|
|
SetNameError(LOCTEXT("RenameFailed_LeftBlank", "Names cannot be left blank!"));
|
|
}
|
|
else if(ValidatorResult == EValidatorResult::TooLong)
|
|
{
|
|
SetNameError(FText::Format( LOCTEXT("RenameFailed_NameTooLong", "Names must have fewer than {0} characters!"), FText::AsNumber( FKismetNameValidator::GetMaximumNameLength())));
|
|
}
|
|
else
|
|
{
|
|
SetNameError(FText::GetEmpty());
|
|
}
|
|
}
|
|
}
|
|
|
|
void FBlueprintGraphNodeDetails::OnNameCommitted(const FText& InNewText, ETextCommit::Type InTextCommit)
|
|
{
|
|
if (BlueprintEditorPtr.IsValid() && GraphNodePtr.IsValid())
|
|
{
|
|
if (FGraphNodeNameValidatorHelper::Validate(GraphNodePtr, BlueprintEditorPtr, InNewText.ToString()) == EValidatorResult::Ok)
|
|
{
|
|
BlueprintEditorPtr.Pin()->OnNodeTitleCommitted(InNewText, InTextCommit, GraphNodePtr.Get());
|
|
}
|
|
}
|
|
}
|
|
|
|
UBlueprint* FBlueprintGraphNodeDetails::GetBlueprintObj() const
|
|
{
|
|
if(BlueprintEditorPtr.IsValid())
|
|
{
|
|
return BlueprintEditorPtr.Pin()->GetBlueprintObj();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
TSharedRef<IDetailCustomization> FChildActorComponentDetails::MakeInstance(TWeakPtr<FBlueprintEditor> BlueprintEditorPtrIn)
|
|
{
|
|
return MakeShareable(new FChildActorComponentDetails(BlueprintEditorPtrIn));
|
|
}
|
|
|
|
FChildActorComponentDetails::FChildActorComponentDetails(TWeakPtr<FBlueprintEditor> BlueprintEditorPtrIn)
|
|
: BlueprintEditorPtr(BlueprintEditorPtrIn)
|
|
{
|
|
}
|
|
|
|
void FChildActorComponentDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
|
|
{
|
|
TSharedPtr<IPropertyHandle> ActorClassProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UChildActorComponent, ChildActorClass));
|
|
if (ActorClassProperty->IsValidHandle())
|
|
{
|
|
if (BlueprintEditorPtr.IsValid())
|
|
{
|
|
if (UBlueprint* Blueprint = BlueprintEditorPtr.Pin()->GetBlueprintObj())
|
|
{
|
|
static FText RestrictReason = LOCTEXT("NoSelfChildActors", "Cannot append a child-actor of this blueprint type (could cause infinite recursion).");
|
|
TSharedPtr<FPropertyRestriction> ClassRestriction = MakeShareable(new FPropertyRestriction(RestrictReason));
|
|
|
|
ClassRestriction->AddDisabledValue(Blueprint->GetPathName());
|
|
if (Blueprint->GeneratedClass)
|
|
{
|
|
ClassRestriction->AddDisabledValue(Blueprint->GeneratedClass->GetPathName());
|
|
}
|
|
|
|
ActorClassProperty->AddRestriction(ClassRestriction.ToSharedRef());
|
|
}
|
|
}
|
|
|
|
TArray<TWeakObjectPtr<UObject>> ObjectsBeingCustomized;
|
|
DetailBuilder.GetObjectsBeingCustomized(ObjectsBeingCustomized);
|
|
|
|
IDetailCategoryBuilder& CategoryBuilder = DetailBuilder.EditCategory(TEXT("ChildActorComponent"));
|
|
|
|
// Ensure ordering is what we want by adding class in first
|
|
CategoryBuilder.AddProperty(GET_MEMBER_NAME_CHECKED(UChildActorComponent, ChildActorClass));
|
|
|
|
// Hide the child actor template property from the details view in these situations:
|
|
// a) Template is invalid (NULL)
|
|
// b) Template is set to be shown/expanded as an actor subtree in the components tree view
|
|
// c) Multiple CACs have been selected with one or more entries that meet the above criteria
|
|
bool bHideChildActorTemplateProperty = false;
|
|
for (const TWeakObjectPtr<UObject>& ObjectBeingCustomized : ObjectsBeingCustomized)
|
|
{
|
|
if (UChildActorComponent* CAC = Cast<UChildActorComponent>(ObjectBeingCustomized.Get()))
|
|
{
|
|
if (CAC->ChildActorTemplate)
|
|
{
|
|
if (FChildActorComponentEditorUtils::ShouldShowChildActorNodeInTreeView(CAC))
|
|
{
|
|
// Template is shown/expanded as a subtree with an explicit child actor node as the root
|
|
bHideChildActorTemplateProperty = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Template is invalid or not set
|
|
bHideChildActorTemplateProperty = true;
|
|
}
|
|
}
|
|
|
|
// Exit the loop if we've met any of the above criteria for hiding the template property
|
|
if (bHideChildActorTemplateProperty)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Add a custom row to expose the child actor template for editing based on its visibility
|
|
CategoryBuilder.AddProperty(GET_MEMBER_NAME_CHECKED(UChildActorComponent, ChildActorTemplate))
|
|
.Visibility(bHideChildActorTemplateProperty ? EVisibility::Hidden : EVisibility::Visible);
|
|
}
|
|
}
|
|
|
|
void FBlueprintDocumentationDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout)
|
|
{
|
|
check( BlueprintEditorPtr.IsValid() );
|
|
// find currently selected edgraph documentation node
|
|
DocumentationNodePtr = EdGraphSelectionAsDocumentNode();
|
|
|
|
if( DocumentationNodePtr.IsValid() )
|
|
{
|
|
// Cache Link
|
|
DocumentationLink = DocumentationNodePtr->GetDocumentationLink();
|
|
DocumentationExcerpt = DocumentationNodePtr->GetDocumentationExcerptName();
|
|
|
|
IDetailCategoryBuilder& DocumentationCategory = DetailLayout.EditCategory("Documentation", LOCTEXT("DocumentationDetailsCategory", "Documentation"), ECategoryPriority::Default);
|
|
|
|
DocumentationCategory.AddCustomRow( LOCTEXT( "DocumentationLinkLabel", "Documentation Link" ))
|
|
.NameContent()
|
|
.HAlign( HAlign_Fill )
|
|
[
|
|
SNew( STextBlock )
|
|
.Text( LOCTEXT( "FBlueprintDocumentationDetails_Link", "Link" ) )
|
|
.ToolTipText( LOCTEXT( "FBlueprintDocumentationDetails_LinkPathTooltip", "The documentation content path" ))
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
.ValueContent()
|
|
.HAlign( HAlign_Left )
|
|
.MinDesiredWidth( BlueprintDocumentationDetailDefs::DetailsTitleMinWidth )
|
|
.MaxDesiredWidth( BlueprintDocumentationDetailDefs::DetailsTitleMaxWidth )
|
|
[
|
|
SNew( SEditableTextBox )
|
|
.Padding( FMargin( 4.f, 2.f ))
|
|
.Text( this, &FBlueprintDocumentationDetails::OnGetDocumentationLink )
|
|
.ToolTipText( LOCTEXT( "FBlueprintDocumentationDetails_LinkTooltip", "The path of the documentation content relative to /Engine/Documentation/Source" ))
|
|
.OnTextCommitted( this, &FBlueprintDocumentationDetails::OnDocumentationLinkCommitted )
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
];
|
|
|
|
DocumentationCategory.AddCustomRow( LOCTEXT( "DocumentationExcerptsLabel", "Documentation Excerpts" ))
|
|
.NameContent()
|
|
.HAlign( HAlign_Left )
|
|
[
|
|
SNew( STextBlock )
|
|
.Text( LOCTEXT( "FBlueprintDocumentationDetails_Excerpt", "Excerpt" ) )
|
|
.ToolTipText( LOCTEXT( "FBlueprintDocumentationDetails_ExcerptTooltip", "The current documentation excerpt" ))
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
.ValueContent()
|
|
.HAlign( HAlign_Left )
|
|
.MinDesiredWidth( BlueprintDocumentationDetailDefs::DetailsTitleMinWidth )
|
|
.MaxDesiredWidth( BlueprintDocumentationDetailDefs::DetailsTitleMaxWidth )
|
|
[
|
|
SAssignNew( ExcerptComboButton, SComboButton )
|
|
.ContentPadding( 2.f )
|
|
.IsEnabled( this, &FBlueprintDocumentationDetails::OnExcerptChangeEnabled )
|
|
.ButtonContent()
|
|
[
|
|
SNew(SBorder)
|
|
.BorderImage( FAppStyle::GetBrush( "NoBorder" ))
|
|
.Padding( FMargin( 0, 0, 5, 0 ))
|
|
[
|
|
SNew( STextBlock )
|
|
.Text( this, &FBlueprintDocumentationDetails::OnGetDocumentationExcerpt )
|
|
.ToolTipText( LOCTEXT( "FBlueprintDocumentationDetails_ExcerptComboTooltip", "Select Excerpt" ))
|
|
.Font( IDetailLayoutBuilder::GetDetailFont() )
|
|
]
|
|
]
|
|
.OnGetMenuContent( this, &FBlueprintDocumentationDetails::GenerateExcerptList )
|
|
];
|
|
}
|
|
}
|
|
|
|
TWeakObjectPtr<UEdGraphNode_Documentation> FBlueprintDocumentationDetails::EdGraphSelectionAsDocumentNode()
|
|
{
|
|
DocumentationNodePtr.Reset();
|
|
|
|
if( BlueprintEditorPtr.IsValid() )
|
|
{
|
|
/** Get the currently selected set of nodes */
|
|
if( BlueprintEditorPtr.Pin()->GetNumberOfSelectedNodes() == 1 )
|
|
{
|
|
FGraphPanelSelectionSet Objects = BlueprintEditorPtr.Pin()->GetSelectedNodes();
|
|
FGraphPanelSelectionSet::TIterator Iter( Objects );
|
|
UObject* Object = *Iter;
|
|
|
|
if( Object && Object->IsA<UEdGraphNode_Documentation>() )
|
|
{
|
|
DocumentationNodePtr = Cast<UEdGraphNode_Documentation>( Object );
|
|
}
|
|
}
|
|
}
|
|
return DocumentationNodePtr;
|
|
}
|
|
|
|
FText FBlueprintDocumentationDetails::OnGetDocumentationLink() const
|
|
{
|
|
return FText::FromString( DocumentationLink );
|
|
}
|
|
|
|
FText FBlueprintDocumentationDetails::OnGetDocumentationExcerpt() const
|
|
{
|
|
return FText::FromString( DocumentationExcerpt );
|
|
}
|
|
|
|
bool FBlueprintDocumentationDetails::OnExcerptChangeEnabled() const
|
|
{
|
|
return IDocumentation::Get()->PageExists( DocumentationLink );
|
|
}
|
|
|
|
void FBlueprintDocumentationDetails::OnDocumentationLinkCommitted( const FText& InNewName, ETextCommit::Type InTextCommit )
|
|
{
|
|
DocumentationLink = InNewName.ToString();
|
|
DocumentationExcerpt = NSLOCTEXT( "FBlueprintDocumentationDetails", "ExcerptCombo_DefaultText", "Select Excerpt" ).ToString();
|
|
}
|
|
|
|
TSharedRef< ITableRow > FBlueprintDocumentationDetails::MakeExcerptViewWidget( TSharedPtr<FString> Item, const TSharedRef< STableViewBase >& OwnerTable )
|
|
{
|
|
return
|
|
SNew( STableRow<TSharedPtr<FString>>, OwnerTable )
|
|
[
|
|
SNew( STextBlock )
|
|
.Text( FText::FromString(*Item.Get()) )
|
|
];
|
|
}
|
|
|
|
void FBlueprintDocumentationDetails::OnExcerptSelectionChanged( TSharedPtr<FString> ProposedSelection, ESelectInfo::Type /*SelectInfo*/ )
|
|
{
|
|
if( ProposedSelection.IsValid() && DocumentationNodePtr.IsValid() )
|
|
{
|
|
DocumentationNodePtr->Link = DocumentationLink;
|
|
DocumentationExcerpt = *ProposedSelection.Get();
|
|
DocumentationNodePtr->Excerpt = DocumentationExcerpt;
|
|
ExcerptComboButton->SetIsOpen( false );
|
|
}
|
|
}
|
|
|
|
TSharedRef<SWidget> FBlueprintDocumentationDetails::GenerateExcerptList()
|
|
{
|
|
ExcerptList.Empty();
|
|
|
|
if( IDocumentation::Get()->PageExists( DocumentationLink ))
|
|
{
|
|
TSharedPtr<IDocumentationPage> DocumentationPage = IDocumentation::Get()->GetPage( DocumentationLink, NULL );
|
|
TArray<FExcerpt> Excerpts;
|
|
DocumentationPage->GetExcerpts( Excerpts );
|
|
|
|
for (const FExcerpt& Excerpt : Excerpts)
|
|
{
|
|
ExcerptList.Add( MakeShareable( new FString( Excerpt.Name )));
|
|
}
|
|
}
|
|
|
|
return
|
|
SNew( SHorizontalBox )
|
|
+SHorizontalBox::Slot()
|
|
.Padding( 2.f )
|
|
[
|
|
SNew( SListView< TSharedPtr<FString>> )
|
|
.ListItemsSource( &ExcerptList )
|
|
.OnGenerateRow( this, &FBlueprintDocumentationDetails::MakeExcerptViewWidget )
|
|
.OnSelectionChanged( this, &FBlueprintDocumentationDetails::OnExcerptSelectionChanged )
|
|
];
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|