// Copyright Epic Games, Inc. All Rights Reserved. #include "SBlueprintPalette.h" #include "AnimStateConduitNode.h" #include "AnimationBlendSpaceSampleGraph.h" #include "AnimationGraph.h" #include "AnimationStateGraph.h" #include "AnimationStateMachineGraph.h" #include "AnimationCustomTransitionGraph.h" #include "AnimationStateMachineSchema.h" #include "AnimationTransitionGraph.h" #include "AssetRegistry/AssetData.h" #include "AssetRegistry/AssetRegistryModule.h" #include "AssetRegistry/IAssetRegistry.h" #include "AssetToolsModule.h" #include "BlendSpaceGraph.h" #include "BlueprintActionMenuItem.h" #include "BlueprintActionMenuUtils.h" #include "BlueprintDragDropMenuItem.h" #include "BlueprintEditor.h" #include "BlueprintEditorSettings.h" #include "BlueprintNamespaceUtilities.h" #include "BlueprintNodeSpawner.h" #include "BlueprintPaletteFavorites.h" #include "Components/ActorComponent.h" #include "Components/TimelineComponent.h" #include "Containers/Array.h" #include "Containers/EnumAsByte.h" #include "Containers/UnrealString.h" #include "CoreGlobals.h" #include "Delegates/Delegate.h" #include "Dialogs/Dialogs.h" #include "EdGraph/EdGraph.h" #include "EdGraph/EdGraphNode.h" #include "EdGraph/EdGraphPin.h" #include "EdGraph/EdGraphSchema.h" #include "EdGraphNode_Comment.h" #include "EdGraphSchema_K2.h" #include "EdGraphSchema_K2_Actions.h" #include "Engine/Blueprint.h" #include "Engine/MemberReference.h" #include "Engine/SCS_Node.h" #include "Engine/SimpleConstructionScript.h" #include "FieldNotifyToggle.h" #include "Framework/Application/SlateApplication.h" #include "Framework/SlateDelegates.h" #include "GenericPlatform/GenericApplication.h" #include "HAL/Platform.h" #include "IAssetTools.h" #include "IDocumentation.h" #include "Internationalization/Culture.h" #include "Internationalization/Internationalization.h" #include "INotifyFieldValueChanged.h" #include "K2Node.h" #include "K2Node_CallFunction.h" #include "K2Node_Variable.h" #include "Kismet2/BlueprintEditorUtils.h" #include "Kismet2/ComponentEditorUtils.h" #include "Kismet2/Kismet2NameValidators.h" #include "Layout/Children.h" #include "Layout/ChildrenBase.h" #include "Layout/Margin.h" #include "Layout/Visibility.h" #include "Math/Color.h" #include "Misc/AssertionMacros.h" #include "Misc/ConfigCacheIni.h" #include "Misc/FileHelper.h" #include "Misc/PackageName.h" #include "Misc/Paths.h" #include "Misc/ScopedSlowTask.h" #include "Modules/ModuleManager.h" #include "SBlueprintFavoritesPalette.h" #include "SBlueprintLibraryPalette.h" #include "SGraphActionMenu.h" #include "SMyBlueprint.h" #include "SPinTypeSelector.h" #include "ScopedTransaction.h" #include "SlateOptMacros.h" #include "SlotBase.h" #include "Styling/AppStyle.h" #include "Styling/CoreStyle.h" #include "Styling/ISlateStyle.h" #include "Styling/SlateColor.h" #include "Styling/SlateTypes.h" #include "Styling/StyleDefaults.h" #include "Templates/Casts.h" #include "Templates/SubclassOf.h" #include "Textures/SlateIcon.h" #include "TutorialMetaData.h" #include "Types/ISlateMetaData.h" #include "UObject/Class.h" #include "UObject/Field.h" #include "UObject/NameTypes.h" #include "UObject/Object.h" #include "UObject/ObjectMacros.h" #include "UObject/ObjectPtr.h" #include "UObject/Package.h" #include "UObject/Script.h" #include "UObject/UObjectGlobals.h" #include "UObject/UnrealNames.h" #include "UObject/UnrealType.h" #include "UObject/WeakFieldPtr.h" #include "UObject/WeakObjectPtr.h" #include "UObject/WeakObjectPtrTemplates.h" #include "WidgetBlueprintEditorUtils.h" #include "Widgets/IToolTip.h" #include "Widgets/Images/SImage.h" #include "Widgets/Input/SCheckBox.h" #include "Widgets/Layout/SBorder.h" #include "Widgets/Layout/SSplitter.h" #include "Widgets/SBoxPanel.h" #include "Widgets/SOverlay.h" #include "Widgets/SToolTip.h" #include "Widgets/SWidget.h" #include "Widgets/Text/SInlineEditableTextBlock.h" #include "Widgets/Text/STextBlock.h" class FDragDropEvent; struct FGeometry; struct FSlateBrush; #define LOCTEXT_NAMESPACE "BlueprintPalette" /******************************************************************************* * Static File Helpers *******************************************************************************/ /** namespace'd to avoid collisions during unified builds */ namespace BlueprintPalette { static FString const ConfigSection("BlueprintEditor.Palette"); static FString const FavoritesHeightConfigKey("FavoritesHeightRatio"); static FString const LibraryHeightConfigKey("LibraryHeightRatio"); } /** * A helper method intended for constructing tooltips on palette items * associated with specific blueprint variables (gets a string representing the * specified variable's type) * * @param VarScope The struct that owns the variable in question. * @param VarName The name of the variable you want the type of. * @param Detailed If true the returned string includes SubCategoryObject * @return A string representing the variable's type (empty if the variable couldn't be found). */ static FString GetVarType(UStruct* VarScope, FName VarName, bool bUseObjToolTip, bool bDetailed = false) { FString VarDesc; if (VarScope) { if (FProperty* Property = FindFProperty(VarScope, VarName)) { // If it is an object property, see if we can get a nice class description instead of just the name FObjectProperty* ObjProp = CastField(Property); if (bUseObjToolTip && ObjProp && ObjProp->PropertyClass) { VarDesc = ObjProp->PropertyClass->GetToolTipText(GetDefault()->bShowShortTooltips).ToString(); } // Name of type if (VarDesc.Len() == 0) { const UEdGraphSchema_K2* K2Schema = GetDefault(); FEdGraphPinType PinType; if (K2Schema->ConvertPropertyToPinType(Property, PinType)) // use schema to get the color { VarDesc = UEdGraphSchema_K2::TypeToText(PinType).ToString(); } } } } return VarDesc; } /** * Util function that helps construct a tooltip for a specific variable action * (attempts to grab the variable's "tooltip" metadata). * * @param InBlueprint The blueprint that the palette is associated. * @param VarClass The class that owns the variable. * @param VarName The variable you want a tooltip for. * @return A string from the variable's "tooltip" metadata (empty if the variable wasn't found, or it didn't have the metadata). */ static FString GetVarTooltip(UBlueprint* InBlueprint, UClass* VarClass, FName VarName) { FString ResultTooltip; if (VarClass) { if (FProperty* Property = FindFProperty(VarClass, VarName)) { // discover if the variable property is a non blueprint user variable UClass* SourceClass = Property->GetOwnerClass(); if( SourceClass && SourceClass->ClassGeneratedBy == nullptr ) { ResultTooltip = Property->GetToolTipText().ToString(); } else { const UBlueprint* SourceBlueprint = SourceClass ? Cast(SourceClass->ClassGeneratedBy) : nullptr; FBlueprintEditorUtils::GetBlueprintVariableMetaData(SourceBlueprint ? SourceBlueprint : InBlueprint, VarName, nullptr, TEXT("tooltip"), ResultTooltip); } } } return ResultTooltip; } /** * A utility function intended to aid the construction of a specific blueprint * palette item (specifically FEdGraphSchemaAction_K2Graph palette items). Based * off of the sub-graph's type, this gets an icon representing said sub-graph. * * @param ActionIn The FEdGraphSchemaAction_K2Graph action that the palette item represents. * @param BlueprintIn The blueprint currently being edited (that the action is for). * @param IconOut An icon denoting the sub-graph's type. * @param ColorOut An output color, further denoting the specified action. * @param ToolTipOut The tooltip to display when the icon is hovered over (describing the sub-graph type). */ static void GetSubGraphIcon(FEdGraphSchemaAction_K2Graph const* const ActionIn, UBlueprint const* BlueprintIn, FSlateBrush const*& IconOut, FSlateColor& ColorOut, FText& ToolTipOut) { check(BlueprintIn); switch (ActionIn->GraphType) { case EEdGraphSchemaAction_K2Graph::Graph: { if (ActionIn->EdGraph) { IconOut = FBlueprintEditor::GetGlyphForGraph(ActionIn->EdGraph); } else { IconOut = FAppStyle::GetBrush(TEXT("GraphEditor.EventGraph_16x")); } ToolTipOut = LOCTEXT("EventGraph_ToolTip", "Event Graph"); } break; case EEdGraphSchemaAction_K2Graph::Subgraph: { if (Cast(ActionIn->EdGraph)) { IconOut = FAppStyle::GetBrush(TEXT("GraphEditor.StateMachine_16x") ); ToolTipOut = LOCTEXT("AnimationStateMachineGraph_ToolTip", "Animation State Machine"); } else if (Cast(ActionIn->EdGraph)) { IconOut = FAppStyle::GetBrush(TEXT("GraphEditor.State_16x") ); ToolTipOut = LOCTEXT("AnimationState_ToolTip", "Animation State"); } else if (Cast(ActionIn->EdGraph)) { UAnimStateConduitNode* EdGraphOuter = Cast(ActionIn->EdGraph->GetOuter()); if (EdGraphOuter) { IconOut = FAppStyle::GetBrush(TEXT("GraphEditor.Conduit_16x")); ToolTipOut = LOCTEXT("ConduitGraph_ToolTip", "Conduit"); } else { IconOut = FAppStyle::GetBrush(TEXT("GraphEditor.Rule_16x")); ToolTipOut = LOCTEXT("AnimationTransitionGraph_ToolTip", "Animation Transition Rule"); } } else if (Cast(ActionIn->EdGraph)) { IconOut = FAppStyle::GetBrush(TEXT("BlendSpace.Graph") ); ToolTipOut = LOCTEXT("BlendSpace_ToolTip", "BlendSpace"); } else if (Cast(ActionIn->EdGraph)) { IconOut = FAppStyle::GetBrush(TEXT("BlendSpace.SampleGraph") ); ToolTipOut = LOCTEXT("BlendSpaceSample_ToolTip", "BlendSpace Sample"); } else if (Cast(ActionIn->EdGraph)) { IconOut = FAppStyle::GetBrush(TEXT("GraphEditor.SubGraph_16x")); ToolTipOut = LOCTEXT("CustomBlendGraph_ToolTip", "Custom Blend"); } else { IconOut = FAppStyle::GetBrush(TEXT("GraphEditor.SubGraph_16x") ); ToolTipOut = LOCTEXT("EventSubgraph_ToolTip", "Event Subgraph"); } } break; case EEdGraphSchemaAction_K2Graph::Macro: { IconOut = FAppStyle::GetBrush(TEXT("GraphEditor.Macro_16x")); if ( ActionIn->EdGraph == nullptr ) { ToolTipOut = LOCTEXT("PotentialOverride_Tooltip", "Potential Override"); } else { // Need to see if this is a function overriding something in the parent, or ; if (UFunction* OverrideFunc = FindUField(BlueprintIn->ParentClass, ActionIn->FuncName)) { ToolTipOut = LOCTEXT("Override_Tooltip", "Override"); } else { ToolTipOut = LOCTEXT("Macro_Tooltip", "Macro"); } } } break; case EEdGraphSchemaAction_K2Graph::Interface: { IconOut = FAppStyle::GetBrush(TEXT("GraphEditor.InterfaceFunction_16x")); FFormatNamedArguments Args; Args.Add(TEXT("InterfaceName"), FText::FromName(ActionIn->FuncName)); ToolTipOut = FText::Format(LOCTEXT("FunctionFromInterface_Tooltip", "Function (from Interface '{InterfaceName}')"), Args); if (UFunction* OverrideFunc = FindUField(BlueprintIn->SkeletonGeneratedClass, ActionIn->FuncName)) { if (UEdGraphSchema_K2::FunctionCanBePlacedAsEvent(OverrideFunc)) { ToolTipOut = FText::Format(LOCTEXT("EventFromInterface_Tooltip", "Event (from Interface '{InterfaceName}')"), Args); ColorOut = FLinearColor::Yellow; } } } break; case EEdGraphSchemaAction_K2Graph::Function: { if (ActionIn->EdGraph == nullptr) { IconOut = FAppStyle::GetBrush(TEXT("GraphEditor.PotentialOverrideFunction_16x")); ToolTipOut = LOCTEXT("PotentialOverride_Tooltip", "Potential Override"); } else { if (ActionIn->EdGraph->IsA(UAnimationGraph::StaticClass())) { IconOut = FAppStyle::GetBrush(TEXT("GraphEditor.Animation_16x")); } else if (UFunction* OverrideFunc = FindUField(BlueprintIn->ParentClass, ActionIn->FuncName)) { const bool bIsPureFunction = OverrideFunc && OverrideFunc->HasAnyFunctionFlags(FUNC_BlueprintPure); IconOut = FAppStyle::GetBrush(bIsPureFunction ? TEXT("GraphEditor.OverridePureFunction_16x") : TEXT("GraphEditor.OverrideFunction_16x")); ToolTipOut = LOCTEXT("Override_Tooltip", "Override"); } else { UFunction* Function = FindUField(BlueprintIn->SkeletonGeneratedClass, ActionIn->FuncName); const bool bIsPureFunction = Function && Function->HasAnyFunctionFlags(FUNC_BlueprintPure); IconOut = FAppStyle::GetBrush(bIsPureFunction ? TEXT("GraphEditor.PureFunction_16x") : TEXT("GraphEditor.Function_16x")); if (ActionIn->EdGraph->IsA(UAnimationGraph::StaticClass())) { ToolTipOut = LOCTEXT("AnimationGraph_Tooltip", "Animation Graph"); } else { ToolTipOut = LOCTEXT("Function_Tooltip", "Function"); } } } } break; } } /** * A utility function intended to aid the construction of a specific blueprint * palette item. This looks at the item's associated action, and based off its * type, retrieves an icon, color and tooltip for the slate widget. * * @param ActionIn The action associated with the palette item you want an icon for. * @param BlueprintIn The blueprint currently being edited (that the action is for). * @param BrushOut An output of the icon, best representing the specified action. * @param ColorOut An output color, further denoting the specified action. * @param ToolTipOut An output tooltip, best describing the specified action type. */ static void GetPaletteItemIcon(TSharedPtr ActionIn, UBlueprint const* BlueprintIn, FSlateBrush const*& BrushOut, FSlateColor& ColorOut, FText& ToolTipOut, FString& DocLinkOut, FString& DocExcerptOut, FSlateBrush const*& SecondaryBrushOut, FSlateColor& SecondaryColorOut) { // Default to tooltip based on action supplied ToolTipOut = ActionIn->GetTooltipDescription().IsEmpty() ? ActionIn->GetMenuDescription() : ActionIn->GetTooltipDescription(); if (ActionIn->GetTypeId() == FBlueprintActionMenuItem::StaticGetTypeId()) { FBlueprintActionMenuItem* NodeSpawnerAction = (FBlueprintActionMenuItem*)ActionIn.Get(); BrushOut = NodeSpawnerAction->GetMenuIcon(ColorOut); TSubclassOf VarNodeClass = NodeSpawnerAction->GetRawAction()->NodeClass; // if the node is a variable getter or setter, use the variable icon instead, because maps need two brushes if (*VarNodeClass && VarNodeClass->IsChildOf(UK2Node_Variable::StaticClass())) { const UK2Node_Variable* TemplateNode = Cast(NodeSpawnerAction->GetRawAction()->GetTemplateNode()); FProperty* Property = TemplateNode->GetPropertyForVariable(); BrushOut = FBlueprintEditor::GetVarIconAndColorFromProperty(Property, ColorOut, SecondaryBrushOut, SecondaryColorOut); } } else if (ActionIn->GetTypeId() == FBlueprintDragDropMenuItem::StaticGetTypeId()) { FBlueprintDragDropMenuItem* DragDropAction = (FBlueprintDragDropMenuItem*)ActionIn.Get(); BrushOut = DragDropAction->GetMenuIcon(ColorOut); } // for backwards compatibility: else if (UK2Node const* const NodeTemplate = FBlueprintActionMenuUtils::ExtractNodeTemplateFromAction(ActionIn)) { // If the node wants to create tooltip text, use that instead, because its probably more detailed FText NodeToolTipText = NodeTemplate->GetTooltipText(); if (!NodeToolTipText.IsEmpty()) { ToolTipOut = NodeToolTipText; } // Ask node for a palette icon FLinearColor IconLinearColor = FLinearColor::White; BrushOut = NodeTemplate->GetIconAndTint(IconLinearColor).GetOptionalIcon(); ColorOut = IconLinearColor; } // for MyBlueprint tab specific actions: else if (ActionIn->GetTypeId() == FEdGraphSchemaAction_K2Graph::StaticGetTypeId()) { FEdGraphSchemaAction_K2Graph const* GraphAction = (FEdGraphSchemaAction_K2Graph const*)ActionIn.Get(); GetSubGraphIcon(GraphAction, BlueprintIn, BrushOut, ColorOut, ToolTipOut); } else if (ActionIn->GetTypeId() == FEdGraphSchemaAction_K2Delegate::StaticGetTypeId()) { FEdGraphSchemaAction_K2Delegate* DelegateAction = (FEdGraphSchemaAction_K2Delegate*)ActionIn.Get(); BrushOut = FAppStyle::GetBrush(TEXT("GraphEditor.Delegate_16x")); FFormatNamedArguments Args; Args.Add(TEXT("EventDispatcherName"), FText::FromName(DelegateAction->GetDelegateName())); ToolTipOut = FText::Format(LOCTEXT("Delegate_Tooltip", "Event Dispatcher '{EventDispatcherName}'"), Args); } else if (ActionIn->GetTypeId() == FEdGraphSchemaAction_K2Var::StaticGetTypeId()) { FEdGraphSchemaAction_K2Var* VarAction = (FEdGraphSchemaAction_K2Var*)ActionIn.Get(); UClass* VarClass = VarAction->GetVariableClass(); BrushOut = FBlueprintEditor::GetVarIconAndColor(VarClass, VarAction->GetVariableName(), ColorOut, SecondaryBrushOut, SecondaryColorOut); ToolTipOut = FText::FromString(GetVarType(VarClass, VarAction->GetVariableName(), true, true)); DocLinkOut = TEXT("Shared/Editor/Blueprint/VariableTypes"); DocExcerptOut = GetVarType(VarClass, VarAction->GetVariableName(), false, false); } else if (ActionIn->GetTypeId() == FEdGraphSchemaAction_K2LocalVar::StaticGetTypeId()) { FEdGraphSchemaAction_K2LocalVar* LocalVarAction = (FEdGraphSchemaAction_K2LocalVar*)ActionIn.Get(); UStruct* VarScope = CastChecked(LocalVarAction->GetVariableScope()); BrushOut = FBlueprintEditor::GetVarIconAndColor(VarScope, LocalVarAction->GetVariableName(), ColorOut, SecondaryBrushOut, SecondaryColorOut); ToolTipOut = FText::FromString(GetVarType(VarScope, LocalVarAction->GetVariableName(), true)); DocLinkOut = TEXT("Shared/Editor/Blueprint/VariableTypes"); DocExcerptOut = GetVarType(VarScope, LocalVarAction->GetVariableName(), false); } else if (ActionIn->IsA(FEdGraphSchemaAction_BlueprintVariableBase::StaticGetTypeId())) { FEdGraphSchemaAction_BlueprintVariableBase* BPVarAction = (FEdGraphSchemaAction_BlueprintVariableBase*)(ActionIn.Get()); const FEdGraphPinType PinType = BPVarAction->GetPinType(); BrushOut = FBlueprintEditor::GetVarIconAndColorFromPinType(PinType, ColorOut, SecondaryBrushOut, SecondaryColorOut); ToolTipOut = FText::FromString(UEdGraphSchema_K2::TypeToText(PinType).ToString()); DocLinkOut = TEXT("Shared/Editor/Blueprint/VariableTypes"); DocExcerptOut = UEdGraphSchema_K2::TypeToText(PinType).ToString(); } else if (ActionIn->GetTypeId() == FEdGraphSchemaAction_K2Enum::StaticGetTypeId()) { BrushOut = FAppStyle::GetBrush(TEXT("GraphEditor.EnumGlyph")); ToolTipOut = LOCTEXT("Enum_Tooltip", "Enum Asset"); } else if (ActionIn->GetTypeId() == FEdGraphSchemaAction_K2Struct::StaticGetTypeId()) { BrushOut = FAppStyle::GetBrush(TEXT("GraphEditor.StructGlyph")); ToolTipOut = LOCTEXT("Struct_Tooltip", "Struct Asset"); } else { BrushOut = ActionIn->GetPaletteIcon(); const FText ActionToolTip = ActionIn->GetPaletteToolTip(); if(!ActionToolTip.IsEmpty()) { ToolTipOut = ActionToolTip; } } } /** * Takes the existing tooltip and concats a path id (for the specified action) * to the end. * * @param ActionIn The action you want to show the path for. * @param OldToolTip The tooltip that you're replacing (we fold it into the new one)/ * @return The newly created tooltip (now with the action's path tacked on the bottom). */ static TSharedRef ConstructToolTipWithActionPath(TSharedPtr ActionIn, TSharedPtr OldToolTip) { TSharedRef NewToolTip = OldToolTip.ToSharedRef(); if (!ActionIn) { return NewToolTip; } FFavoritedBlueprintPaletteItem ActionItem(*ActionIn); if (ActionItem.IsValid()) { static FTextBlockStyle PathStyle = FTextBlockStyle() .SetFont(FCoreStyle::GetDefaultFontStyle("Regular", 8)) .SetColorAndOpacity(FLinearColor(0.4f, 0.4f, 0.4f)); NewToolTip = SNew(SToolTip) // Emulate text-only tool-tip styling that SToolTip uses when no custom content is supplied. We want node tool-tips to // be styled just like text-only tool-tips .BorderImage( FCoreStyle::Get().GetBrush("ToolTip.BrightBackground") ) .TextMargin(FMargin(11.0f)) [ SNew(SVerticalBox) + SVerticalBox::Slot() [ OldToolTip->GetContentWidget() ] + SVerticalBox::Slot() .HAlign(EHorizontalAlignment::HAlign_Right) [ SNew(STextBlock) .TextStyle( FAppStyle::Get(), "Documentation.SDocumentationTooltip") .Text(FText::FromString(ActionItem.ToString())) //.TextStyle(&PathStyle) ] ]; } return NewToolTip; } /******************************************************************************* * FBlueprintPaletteItemRenameUtils *******************************************************************************/ /** A set of utilities to aid SBlueprintPaletteItem when the user attempts to rename one. */ struct FBlueprintPaletteItemRenameUtils { private: static bool VerifyNewAssetName(UObject* Object, const FText& InNewText, FText& OutErrorMessage) { if (!Object) { return false; } if (Object->GetName() == InNewText.ToString()) { return true; } TArray AssetData; FAssetRegistryModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetRegistry"); AssetToolsModule.Get().GetAssetsByPath(FName(*FPaths::GetPath(Object->GetOutermost()->GetPathName())), AssetData); if(!FFileHelper::IsFilenameValidForSaving(InNewText.ToString(), OutErrorMessage) || !FName(*InNewText.ToString()).IsValidObjectName( OutErrorMessage )) { return false; } else if( InNewText.ToString().Len() >= NAME_SIZE ) { OutErrorMessage = FText::Format(LOCTEXT("RenameFailed_NameTooLong", "Names must have fewer than {0} characters!"), NAME_SIZE); } else { // Check to see if the name conflicts for ( const FAssetData& AssetInfo : AssetData) { if(AssetInfo.AssetName.ToString() == InNewText.ToString()) { OutErrorMessage = LOCTEXT("RenameFailed_AlreadyInUse", "Asset name already in use!"); return false; } } } return true; } static void CommitNewAssetName(UObject* Object, FBlueprintEditor* BlueprintEditor, const FText& NewText) { if (Object && BlueprintEditor) { if(Object->GetName() != NewText.ToString()) { TArray AssetsAndNames; const FString PackagePath = FPackageName::GetLongPackagePath(Object->GetOutermost()->GetName()); new(AssetsAndNames) FAssetRenameData(Object, PackagePath, NewText.ToString()); FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools"); AssetToolsModule.Get().RenameAssetsWithDialog(AssetsAndNames); } TSharedPtr MyBlueprint = BlueprintEditor->GetMyBlueprintWidget(); if (MyBlueprint.IsValid()) { MyBlueprint->SelectItemByName(FName(*Object->GetPathName())); } } } public: /** * Determines whether the enum node, associated with the selected action, * can be renamed with the specified text. * * @param InNewText The text you want to verify. * @param OutErrorMessage Text explaining why the associated node couldn't be renamed (if the return value is false). * @param ActionPtr The selected action that the calling palette item represents. * @return True if it is ok to rename the associated node with the given string (false if not). */ static bool VerifyNewEnumName(const FText& InNewText, FText& OutErrorMessage, TWeakPtr ActionPtr) { // Should never make it here with anything but an enum action check(ActionPtr.Pin()->GetTypeId() == FEdGraphSchemaAction_K2Enum::StaticGetTypeId()); FEdGraphSchemaAction_K2Enum* EnumAction = (FEdGraphSchemaAction_K2Enum*)ActionPtr.Pin().Get(); return VerifyNewAssetName(EnumAction ? EnumAction->Enum : nullptr, InNewText, OutErrorMessage); } /** * Take the verified text and renames the enum node associated with the * selected action. * * @param NewText The new (verified) text to rename the node with. * @param InTextCommit A value denoting how the text was entered. * @param ActionPtr The selected action that the calling palette item represents. * @param BlueprintEditorPtr A pointer to the blueprint editor that the palette belongs to. */ static void CommitNewEnumName(const FText& NewText, ETextCommit::Type InTextCommit, TWeakPtr ActionPtr, TWeakPtr BlueprintEditorPtr) { // Should never make it here with anything but an enum action check(ActionPtr.Pin()->GetTypeId() == FEdGraphSchemaAction_K2Enum::StaticGetTypeId()); FEdGraphSchemaAction_K2Enum* EnumAction = (FEdGraphSchemaAction_K2Enum*)ActionPtr.Pin().Get(); if(EnumAction->Enum->GetName() != NewText.ToString()) { FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools"); TArray AssetsAndNames; const FString PackagePath = FPackageName::GetLongPackagePath(EnumAction->Enum->GetOutermost()->GetName()); new(AssetsAndNames) FAssetRenameData(EnumAction->Enum, PackagePath, NewText.ToString()); BlueprintEditorPtr.Pin()->GetMyBlueprintWidget()->SelectItemByName(FName("ConstructionScript")); AssetToolsModule.Get().RenameAssetsWithDialog(AssetsAndNames); } BlueprintEditorPtr.Pin()->GetMyBlueprintWidget()->SelectItemByName(FName(*EnumAction->Enum->GetPathName())); } /** * Determines whether the struct node, associated with the selected action, * can be renamed with the specified text. * * @param InNewText The text you want to verify. * @param OutErrorMessage Text explaining why the associated node couldn't be renamed (if the return value is false). * @param ActionPtr The selected action that the calling palette item represents. * @return True if it is ok to rename the associated node with the given string (false if not). */ static bool VerifyNewStructName(const FText& InNewText, FText& OutErrorMessage, TWeakPtr ActionPtr) { // Should never make it here with anything but a struct action check(ActionPtr.Pin()->GetTypeId() == FEdGraphSchemaAction_K2Struct::StaticGetTypeId()); FEdGraphSchemaAction_K2Struct* Action = (FEdGraphSchemaAction_K2Struct*)ActionPtr.Pin().Get(); return VerifyNewAssetName(Action ? Action->Struct : nullptr, InNewText, OutErrorMessage); } /** * Determines whether the event node, associated with the selected action, * can be renamed with the specified text. * * @param InNewText The text you want to verify. * @param OutErrorMessage Text explaining why the associated node couldn't be renamed (if the return value is false). * @param ActionPtr The selected action that the calling palette item represents. * @return True if it is ok to rename the associated node with the given string (false if not). */ static bool VerifyNewEventName(const FText& InNewText, FText& OutErrorMessage, TWeakPtr ActionPtr) { bool bIsNameValid = false; OutErrorMessage = LOCTEXT("RenameFailed_NodeRename", "Cannot rename associated node!"); check(ActionPtr.Pin()->GetTypeId() == FEdGraphSchemaAction_K2Event::StaticGetTypeId()); FEdGraphSchemaAction_K2Event* EventAction = (FEdGraphSchemaAction_K2Event*)ActionPtr.Pin().Get(); UK2Node* AssociatedNode = EventAction->NodeTemplate; if (AssociatedNode && AssociatedNode->GetCanRenameNode()) { TSharedPtr NodeNameValidator = FNameValidatorFactory::MakeValidator(AssociatedNode); bIsNameValid = (NodeNameValidator->IsValid(InNewText.ToString(), true) == EValidatorResult::Ok); } return bIsNameValid; } /** * Take the verified text and renames the struct node associated with the * selected action. * * @param NewText The new (verified) text to rename the node with. * @param InTextCommit A value denoting how the text was entered. * @param ActionPtr The selected action that the calling palette item represents. * @param BlueprintEditorPtr A pointer to the blueprint editor that the palette belongs to. */ static void CommitNewStructName(const FText& NewText, ETextCommit::Type InTextCommit, TWeakPtr ActionPtr, TWeakPtr BlueprintEditorPtr) { // Should never make it here with anything but a struct action check(ActionPtr.Pin()->GetTypeId() == FEdGraphSchemaAction_K2Struct::StaticGetTypeId()); FEdGraphSchemaAction_K2Struct* Action = (FEdGraphSchemaAction_K2Struct*)ActionPtr.Pin().Get(); CommitNewAssetName(Action ? Action->Struct : nullptr, BlueprintEditorPtr.Pin().Get(), NewText); } /** * Take the verified text and renames the event node associated with the * selected action. * * @param NewText The new (verified) text to rename the node with. * @param InTextCommit A value denoting how the text was entered. * @param ActionPtr The selected action that the calling palette item represents. */ static void CommitNewEventName(const FText& NewText, ETextCommit::Type InTextCommit, TWeakPtr ActionPtr) { check(ActionPtr.Pin()->GetTypeId() == FEdGraphSchemaAction_K2Event::StaticGetTypeId()); FEdGraphSchemaAction_K2Event* EventAction = (FEdGraphSchemaAction_K2Event*)ActionPtr.Pin().Get(); if (EventAction->NodeTemplate) { EventAction->NodeTemplate->OnRenameNode(NewText.ToString()); } } /** * Determines whether the target node, associated with the selected action, * can be renamed with the specified text. * * @param InNewText The text you want to verify. * @param OutErrorMessage Text explaining why the associated node couldn't be renamed (if the return value is false). * @param ActionPtr The selected action that the calling palette item represents. * @return True if it is ok to rename the associated node with the given string (false if not). */ static bool VerifyNewTargetNodeName(const FText& InNewText, FText& OutErrorMessage, TWeakPtr ActionPtr) { bool bIsNameValid = false; OutErrorMessage = LOCTEXT("RenameFailed_NodeRename", "Cannot rename associated node!"); check(ActionPtr.Pin()->GetTypeId() == FEdGraphSchemaAction_K2TargetNode::StaticGetTypeId()); FEdGraphSchemaAction_K2TargetNode* TargetNodeAction = (FEdGraphSchemaAction_K2TargetNode*)ActionPtr.Pin().Get(); UK2Node* AssociatedNode = TargetNodeAction->NodeTemplate; if (AssociatedNode && AssociatedNode->GetCanRenameNode()) { TSharedPtr NodeNameValidator = FNameValidatorFactory::MakeValidator(AssociatedNode); bIsNameValid = (NodeNameValidator->IsValid(InNewText.ToString(), true) == EValidatorResult::Ok); } return bIsNameValid; } /** * Take the verified text and renames the target node associated with the * selected action. * * @param NewText The new (verified) text to rename the node with. * @param InTextCommit A value denoting how the text was entered. * @param ActionPtr The selected action that the calling palette item represents. */ static void CommitNewTargetNodeName(const FText& NewText, ETextCommit::Type InTextCommit, TWeakPtr ActionPtr) { check(ActionPtr.Pin()->GetTypeId() == FEdGraphSchemaAction_K2TargetNode::StaticGetTypeId()); FEdGraphSchemaAction_K2TargetNode* TargetNodeAction = (FEdGraphSchemaAction_K2TargetNode*)ActionPtr.Pin().Get(); if (TargetNodeAction->NodeTemplate) { TargetNodeAction->NodeTemplate->OnRenameNode(NewText.ToString()); } } }; /******************************************************************************* * SPinTypeSelectorHelper *******************************************************************************/ DECLARE_DELEGATE_OneParam(FOnPinTypeChanged, const FEdGraphPinType&) class SPinTypeSelectorHelper : public SCompoundWidget { public: SLATE_BEGIN_ARGS( SPinTypeSelectorHelper ) {} SLATE_ATTRIBUTE(bool, ReadOnly) SLATE_EVENT(FOnPinTypeChanged, OnTypeChanged) SLATE_END_ARGS() void Construct(const FArguments& InArgs, TWeakPtr InAction, UBlueprint* InBlueprint, TWeakPtr InBlueprintEditor) { BlueprintObj = InBlueprint; BlueprintEditorPtr = InBlueprintEditor; ActionPtr = InAction; VariableProperty = nullptr; if (ActionPtr.IsValid()) { VariableProperty = ActionPtr.Pin()->GetProperty(); } ConstructInternal(InArgs); } private: void ConstructInternal(const FArguments& InArgs) { OnTypeChanged = InArgs._OnTypeChanged; TArray> CustomPinTypeFilters; if (BlueprintEditorPtr.IsValid()) { BlueprintEditorPtr.Pin()->GetPinTypeSelectorFilters(CustomPinTypeFilters); } const UEdGraphSchema* Schema = GetDefault(); if (BlueprintEditorPtr.IsValid()) { if (BlueprintEditorPtr.Pin()->GetFocusedGraph()) { Schema = BlueprintEditorPtr.Pin()->GetFocusedGraph()->GetSchema(); } else { Schema = BlueprintEditorPtr.Pin()->GetDefaultSchema().GetDefaultObject(); } } const bool bIsDelegate = ActionPtr.IsValid() && ActionPtr.Pin()->GetTypeId() == FEdGraphSchemaAction_K2Delegate::StaticGetTypeId(); // You cannot change the type of multicast delegates in blueprints if(!bIsDelegate) { this->ChildSlot [ SNew(SPinTypeSelector, FGetPinTypeTree::CreateUObject(GetDefault(), &UEdGraphSchema_K2::GetVariableTypeTree)) .ReadOnly(InArgs._ReadOnly) .Schema(Schema) .SchemaAction(ActionPtr) .TargetPinType(this, &SPinTypeSelectorHelper::OnGetVarType) .OnPinTypeChanged(this, &SPinTypeSelectorHelper::OnVarTypeChanged) .TypeTreeFilter(ETypeTreeFilter::None) .SelectorType(BlueprintEditorPtr.IsValid() ? SPinTypeSelector::ESelectorType::Partial : SPinTypeSelector::ESelectorType::None) .CustomFilters(CustomPinTypeFilters) ]; } } FEdGraphPinType OnGetVarType() const { if (FProperty* VarProp = const_cast(VariableProperty.Get())) { const UEdGraphSchema_K2* K2Schema = GetDefault(); FEdGraphPinType Type; K2Schema->ConvertPropertyToPinType(VarProp, Type); return Type; } else if(ActionPtr.IsValid()) { return ActionPtr.Pin()->GetPinType(); } return FEdGraphPinType(); } void OnVarTypeChanged(const FEdGraphPinType& InNewPinType) { if (FBlueprintEditorUtils::IsPinTypeValid(InNewPinType)) { TSharedPtr BlueprintEditor = BlueprintEditorPtr.Pin(); if (FProperty* VarProp = VariableProperty.Get()) { FName VarName = VarProp->GetFName(); if (VarName != NAME_None) { if (BlueprintEditor.IsValid()) { // Set the MyBP tab's last pin type used as this, for adding lots of variables of the same type BlueprintEditor->GetMyBlueprintWidget()->GetLastPinTypeUsed() = InNewPinType; } if (UFunction* LocalVariableScope = VarProp->GetOwner()) { FBlueprintEditorUtils::ChangeLocalVariableType(BlueprintObj, LocalVariableScope, VarName, InNewPinType); } else { FBlueprintEditorUtils::ChangeMemberVariableType(BlueprintObj, VarName, InNewPinType); } } } else if(ActionPtr.IsValid()) { if (BlueprintEditor.IsValid()) { // Set the MyBP tab's last pin type used as this, for adding lots of variables of the same type BlueprintEditor->GetMyBlueprintWidget()->GetLastPinTypeUsed() = InNewPinType; } ActionPtr.Pin()->ChangeVariableType(InNewPinType); } // Auto-import the underlying type object's default namespace set into the current editor context. const UObject* PinSubCategoryObject = InNewPinType.PinSubCategoryObject.Get(); if (PinSubCategoryObject && BlueprintEditor.IsValid()) { FBlueprintEditor::FImportNamespaceExParameters Params; FBlueprintNamespaceUtilities::GetDefaultImportsForObject(PinSubCategoryObject, Params.NamespacesToImport); BlueprintEditor->ImportNamespaceEx(Params); } } if (OnTypeChanged.IsBound()) { OnTypeChanged.Execute(InNewPinType); } } private: /** The action that the owning palette entry represents */ TWeakPtr ActionPtr; /** Pointer back to the blueprint that is being displayed: */ UBlueprint* BlueprintObj; /** Pointer back to the blueprint editor that owns this, optional because of diff and merge views: */ TWeakPtr BlueprintEditorPtr; /** Variable Property to change the type of */ TWeakFieldPtr VariableProperty; /** Event when type has changed */ FOnPinTypeChanged OnTypeChanged; }; /******************************************************************************* * SPaletteItemVisibilityToggle *******************************************************************************/ class SPaletteItemVisibilityToggle : public SCompoundWidget { public: SLATE_BEGIN_ARGS( SPaletteItemVisibilityToggle ) {} SLATE_END_ARGS() /** * Constructs a visibility-toggle widget (for variable actions only, so that * the user can modify the variable's "edit-on-instance" state). * * @param InArgs A set of slate arguments, defined above. * @param ActionPtrIn The FEdGraphSchemaAction that the parent item represents. * @param BlueprintEdPtrIn A pointer to the blueprint editor that the palette belongs to. */ void Construct(const FArguments& InArgs, TWeakPtr ActionPtrIn, TWeakPtr InBlueprintEditor, UBlueprint* InBlueprint) { ActionPtr = ActionPtrIn; BlueprintEditorPtr = InBlueprintEditor; BlueprintObj = InBlueprint; TSharedPtr PaletteAction = ActionPtrIn.Pin(); bool bShouldHaveAVisibilityToggle = false; if (PaletteAction->GetTypeId() == FEdGraphSchemaAction_K2Var::StaticGetTypeId()) { FProperty* VariableProp = StaticCastSharedPtr(PaletteAction)->GetProperty(); FObjectProperty* VariableObjProp = CastField(VariableProp); UStruct* VarSourceScope = (VariableProp ? CastChecked(VariableProp->GetOwner()) : nullptr); const bool bIsBlueprintVariable = (VarSourceScope == BlueprintObj->SkeletonGeneratedClass); const bool bIsComponentVar = (VariableObjProp && VariableObjProp->PropertyClass && VariableObjProp->PropertyClass->IsChildOf(UActorComponent::StaticClass())); bShouldHaveAVisibilityToggle = bIsBlueprintVariable && (!bIsComponentVar || FBlueprintEditorUtils::IsVariableCreatedByBlueprint(BlueprintObj, VariableObjProp)); } this->ChildSlot [ SNew(SBorder) .Padding(0.0f) .BorderImage(FStyleDefaults::GetNoBrush()) .Visibility(bShouldHaveAVisibilityToggle ? EVisibility::Visible : EVisibility::Collapsed) //.ForegroundColor(this, &SPaletteItemVisibilityToggle::GetVisibilityToggleColor) [ SNew(SCheckBox) .ToolTipText(this, &SPaletteItemVisibilityToggle::GetVisibilityToggleToolTip) .OnCheckStateChanged(this, &SPaletteItemVisibilityToggle::OnVisibilityToggleFlipped) .IsChecked(this, &SPaletteItemVisibilityToggle::GetVisibilityToggleState) .Style(FAppStyle::Get(), "TransparentCheckBox") [ SNew(SImage) .Image(this, &SPaletteItemVisibilityToggle::GetVisibilityIcon) .ColorAndOpacity(FSlateColor::UseForeground()) ] ] ]; } private: /** * Used by this visibility-toggle widget to see if the property represented * by this item is visible outside of Kismet. * * @return ECheckBoxState::Checked if the property is visible, false if not (or if the property wasn't found) */ ECheckBoxState GetVisibilityToggleState() const { TSharedPtr PaletteAction = ActionPtr.Pin(); if ( PaletteAction->GetTypeId() == FEdGraphSchemaAction_K2Var::StaticGetTypeId() ) { TSharedPtr VarAction = StaticCastSharedPtr(PaletteAction); if (FProperty* VariableProperty = VarAction->GetProperty()) { return VariableProperty->HasAnyPropertyFlags(CPF_DisableEditOnInstance) ? ECheckBoxState::Unchecked : ECheckBoxState::Checked; } } return ECheckBoxState::Unchecked; } /** * Used by this visibility-toggle widget when the user makes a change to the * checkbox (modifies the property represented by this item by flipping its * edit-on-instance flag). * * @param InNewState The new state that the user set the checkbox to. */ void OnVisibilityToggleFlipped(ECheckBoxState InNewState) { if( !BlueprintEditorPtr.IsValid() ) { return; } TSharedPtr PaletteAction = ActionPtr.Pin(); if ( PaletteAction->GetTypeId() == FEdGraphSchemaAction_K2Var::StaticGetTypeId() ) { TSharedPtr VarAction = StaticCastSharedPtr(PaletteAction); // Toggle the flag on the blueprint's version of the variable description, based on state const bool bVariableIsExposed = ( InNewState == ECheckBoxState::Checked ); FBlueprintEditorUtils::SetBlueprintOnlyEditableFlag(BlueprintObj, VarAction->GetVariableName(), !bVariableIsExposed); } } /** * Used by this visibility-toggle widget to convey the visibility of the * property represented by this item. * * @return A image representing the variable's "edit-on-instance" state. */ const FSlateBrush* GetVisibilityIcon() const { return GetVisibilityToggleState() == ECheckBoxState::Checked ? FAppStyle::GetBrush( "Kismet.VariableList.ExposeForInstance" ) : FAppStyle::GetBrush( "Kismet.VariableList.HideForInstance" ); } /** * Used by this visibility-toggle widget to convey the visibility of the * property represented by this item (as well as the status of the * variable's tooltip). * * @return A color denoting the item's visibility and tootip status. */ FSlateColor GetVisibilityToggleColor() const { if ( GetVisibilityToggleState() != ECheckBoxState::Checked ) { return FSlateColor::UseForeground(); } else { TSharedPtr VarAction = StaticCastSharedPtr(ActionPtr.Pin()); FString Result; FBlueprintEditorUtils::GetBlueprintVariableMetaData(BlueprintObj, VarAction->GetVariableName(), nullptr, TEXT("tooltip"), Result); if ( !Result.IsEmpty() ) { static const FName TooltipExistsColor("Colors.AccentGreen"); return FAppStyle::Get().GetSlateColor(TooltipExistsColor); } else { static const FName TooltipDoesntExistColor("Colors.AccentYellow"); return FAppStyle::Get().GetSlateColor(TooltipDoesntExistColor); } } } /** * Used by this visibility-toggle widget to supply the toggle with a tooltip * representing the "edit-on-instance" state of the variable represented by * this item. * * @return Tooltip text for this toggle. */ FText GetVisibilityToggleToolTip() const { FText ToolTipText = FText::GetEmpty(); if ( GetVisibilityToggleState() != ECheckBoxState::Checked ) { ToolTipText = LOCTEXT("VariablePrivacy_not_public_Tooltip", "Variable is not public and will not be editable on an instance of this Blueprint."); } else { ToolTipText = LOCTEXT("VariablePrivacy_is_public_Tooltip", "Variable is public and is editable on each instance of this Blueprint."); } return ToolTipText; } private: /** The action that the owning palette entry represents */ TWeakPtr ActionPtr; /** Pointer back to the blueprint editor that owns this, optional because of diff and merge views: */ TWeakPtr BlueprintEditorPtr; /** Pointer back to the blueprint that is being diplayed: */ UBlueprint* BlueprintObj; }; /******************************************************************************* * SBlueprintPaletteItem Public Interface *******************************************************************************/ //------------------------------------------------------------------------------ BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION void SBlueprintPaletteItem::Construct(const FArguments& InArgs, FCreateWidgetForActionData* const InCreateData, TWeakPtr InBlueprintEditor) { Construct(InArgs, InCreateData, InBlueprintEditor.Pin()->GetBlueprintObj(), InBlueprintEditor); } void SBlueprintPaletteItem::Construct(const FArguments& InArgs, FCreateWidgetForActionData* const InCreateData, UBlueprint* InBlueprint) { Construct( InArgs, InCreateData, InBlueprint, TWeakPtr() ); } void SBlueprintPaletteItem::Construct(const FArguments& InArgs, FCreateWidgetForActionData* const InCreateData, UBlueprint* InBlueprint, TWeakPtr InBlueprintEditor) { check(InCreateData->Action.IsValid()); check(InBlueprint); Blueprint = InBlueprint; bShowClassInTooltip = InArgs._ShowClassInTooltip; TSharedPtr GraphAction = InCreateData->Action; ActionPtr = InCreateData->Action; BlueprintEditorPtr = InBlueprintEditor; const bool bIsFullyReadOnly = !InBlueprintEditor.IsValid() || InCreateData->bIsReadOnly; TWeakPtr WeakGraphAction = GraphAction; auto IsReadOnlyLambda = [WeakGraphAction, InBlueprintEditor, bIsFullyReadOnly]() { if(WeakGraphAction.IsValid() && InBlueprintEditor.IsValid()) { return bIsFullyReadOnly || FBlueprintEditorUtils::IsPaletteActionReadOnly(WeakGraphAction.Pin(), InBlueprintEditor.Pin()); } return bIsFullyReadOnly; }; // We differentiate enabled/read-only state here to not dim icons out unnecessarily, which in some situations // (like the right-click palette menu) is confusing to users. auto IsEditingEnabledLambda = [InBlueprintEditor]() { if(InBlueprintEditor.IsValid()) { return InBlueprintEditor.Pin()->InEditingMode(); } return true; }; TAttribute bIsReadOnly = TAttribute::Create(TAttribute::FGetter::CreateLambda(IsReadOnlyLambda)); TAttribute bIsEditingEnabled = TAttribute::Create(TAttribute::FGetter::CreateLambda(IsEditingEnabledLambda)); // construct the icon widget FSlateBrush const* IconBrush = FAppStyle::GetBrush(TEXT("NoBrush")); FSlateBrush const* SecondaryBrush = FAppStyle::GetBrush(TEXT("NoBrush")); FSlateColor IconColor = FSlateColor::UseForeground(); FSlateColor SecondaryIconColor = FSlateColor::UseForeground(); FText IconToolTip = GraphAction->GetTooltipDescription(); FString IconDocLink, IconDocExcerpt; GetPaletteItemIcon(GraphAction, Blueprint, IconBrush, IconColor, IconToolTip, IconDocLink, IconDocExcerpt, SecondaryBrush, SecondaryIconColor); TSharedRef IconWidget = CreateIconWidget(IconToolTip, IconBrush, IconColor, IconDocLink, IconDocExcerpt, SecondaryBrush, SecondaryIconColor); IconWidget->SetEnabled(bIsEditingEnabled); UBlueprintEditorSettings* Settings = GetMutableDefault(); // Enum representing the access specifier of this function or variable enum class EAccessSpecifier : uint8 { None = 0, Private = 1, Protected = 2, Public = 3 }; // We should only bother checking for access if the setting is on and this is not an animation graph const bool bShouldCheckForAccessSpec = Settings->bShowAccessSpecifier; EAccessSpecifier ActionAccessSpecifier = EAccessSpecifier::None; // Setup a meta tag for this node FTutorialMetaData TagMeta("PaletteItem"); if( ActionPtr.IsValid() ) { TagMeta.Tag = *FString::Printf(TEXT("PaletteItem,%s,%d"), *GraphAction->GetMenuDescription().ToString(), GraphAction->GetSectionID()); TagMeta.FriendlyName = GraphAction->GetMenuDescription().ToString(); } // construct the text widget TSharedRef NameSlotWidget = CreateTextSlotWidget(InCreateData, bIsReadOnly ); // Will set the icon of this property to be a Pin Type selector. auto GenerateVariableSettings = [&](TSharedPtr Action) { FProperty* VariableProp = nullptr; if (Action.IsValid()) { VariableProp = Action->GetProperty(); } if (VariableProp) { if (bShouldCheckForAccessSpec) { if (VariableProp->GetBoolMetaData(FBlueprintMetadata::MD_Private)) { ActionAccessSpecifier = EAccessSpecifier::Private; } else if (VariableProp->GetBoolMetaData(FBlueprintMetadata::MD_Protected)) { ActionAccessSpecifier = EAccessSpecifier::Protected; } else { ActionAccessSpecifier = EAccessSpecifier::Public; } } if (FBlueprintEditorUtils::IsVariableCreatedByBlueprint(Blueprint, VariableProp) || VariableProp->GetOwner()) { const UEdGraphSchema_K2* Schema = GetDefault(); IconWidget = SNew(SPinTypeSelectorHelper, Action, Blueprint, BlueprintEditorPtr) .IsEnabled(bIsEditingEnabled) .ReadOnly_Lambda([this]() {return !IsHovered(); }); } } }; // For Variables and Local Variables, we will convert the icon widget into a pin type selector. if (GraphAction->GetTypeId() == FEdGraphSchemaAction_K2Var::StaticGetTypeId()) { GenerateVariableSettings(StaticCastSharedPtr(GraphAction)); } else if (GraphAction->GetTypeId() == FEdGraphSchemaAction_K2LocalVar::StaticGetTypeId()) { GenerateVariableSettings(StaticCastSharedPtr(GraphAction)); } else if (GraphAction->IsA(FEdGraphSchemaAction_BlueprintVariableBase::StaticGetTypeId())) { ActionAccessSpecifier = EAccessSpecifier::Private; const UEdGraphSchema_K2* Schema = GetDefault(); IconWidget = SNew(SPinTypeSelectorHelper, StaticCastSharedPtr(GraphAction), Blueprint, BlueprintEditorPtr) .IsEnabled(bIsEditingEnabled) .ReadOnly_Lambda([this]() {return !IsHovered(); }) .OnTypeChanged_Lambda([this](const FEdGraphPinType& NewPinType) { BlueprintEditorPtr.Pin()->GetMyBlueprintWidget()->Refresh(); }); } // Determine the access level of this action if it is a function graph or for interface events else if (bShouldCheckForAccessSpec && GraphAction->GetTypeId() == FEdGraphSchemaAction_K2Graph::StaticGetTypeId()) { UFunction* FunctionToCheck = nullptr; if (FEdGraphSchemaAction_K2Graph* FuncGraphAction = (FEdGraphSchemaAction_K2Graph*)(GraphAction.Get())) { FunctionToCheck = FindUField(Blueprint->SkeletonGeneratedClass, FuncGraphAction->FuncName); // Handle override/interface functions if(!FunctionToCheck) { FBlueprintEditorUtils::GetOverrideFunctionClass(Blueprint, FuncGraphAction->FuncName, &FunctionToCheck); } } // If we have found a function that matches this action name, then grab it's access specifier if (FunctionToCheck) { if (FunctionToCheck->HasAnyFunctionFlags(FUNC_Protected)) { ActionAccessSpecifier = EAccessSpecifier::Protected; } else if (FunctionToCheck->HasAnyFunctionFlags(FUNC_Private)) { ActionAccessSpecifier = EAccessSpecifier::Private; } else { ActionAccessSpecifier = EAccessSpecifier::Public; } } } FText AccessModifierText = FText::GetEmpty(); switch (ActionAccessSpecifier) { case EAccessSpecifier::Public: { AccessModifierText = LOCTEXT("AccessModifierPublic", "public"); } break; case EAccessSpecifier::Protected: { AccessModifierText = LOCTEXT("AccessModifierProtected", "protected"); } break; case EAccessSpecifier::Private: { AccessModifierText = LOCTEXT("AccessModifierPrivate", "private"); } break; } // Calculate a color so that the text gets brighter the more accessible the action is const bool AccessSpecifierEnabled = (ActionAccessSpecifier != EAccessSpecifier::None) && bShouldCheckForAccessSpec; // Create the widget with an icon TSharedRef ActionBox = SNew(SHorizontalBox) .AddMetaData(TagMeta); auto CreateAccessSpecifierLambda = [&ActionBox, &AccessSpecifierEnabled, &AccessModifierText, &ActionAccessSpecifier]() { ActionBox.Get().AddSlot() .MaxWidth(50.f) .FillWidth(AccessSpecifierEnabled ? 0.4f : 0.0f) .Padding(FMargin(/* horizontal */ AccessSpecifierEnabled ? 6.0f : 0.0f, /* vertical */ 0.0f)) .VAlign(VAlign_Center) .HAlign(HAlign_Right) [ SNew(STextBlock) // Will only display text if we have a modifier level .IsEnabled(AccessSpecifierEnabled) .Text(AccessModifierText) .ColorAndOpacity(FSlateColor::UseSubduedForeground()) // Bold if public .TextStyle(FAppStyle::Get(), ActionAccessSpecifier == EAccessSpecifier::Public ? "BlueprintEditor.AccessModifier.Public" : "BlueprintEditor.AccessModifier.Default") ]; }; if (GraphAction->IsA(FEdGraphSchemaAction_BlueprintVariableBase::StaticGetTypeId())) { if (ActionAccessSpecifier != EAccessSpecifier::None && GraphAction->GetTypeId() != FEdGraphSchemaAction_K2LocalVar::StaticGetTypeId()) { CreateAccessSpecifierLambda(); } ActionBox.Get().AddSlot() .FillWidth(0.6f) .VAlign(VAlign_Center) .Padding(3.0f, 0.0f) [ NameSlotWidget ]; ActionBox.Get().AddSlot() .FillWidth(0.4f) .HAlign(HAlign_Left) .VAlign(VAlign_Center) [ IconWidget ]; if (InBlueprint && FBlueprintEditorUtils::ImplementsInterface(InBlueprint, true, UNotifyFieldValueChanged::StaticClass()) && GraphAction->GetTypeId() == FEdGraphSchemaAction_K2Var::StaticGetTypeId()) { ActionBox.Get().AddSlot() .AutoWidth() .Padding(FMargin(6.0f, 0.0f, 3.0f, 0.0f)) .HAlign(HAlign_Right) .VAlign(VAlign_Center) [ SNew(SPaletteItemVarFieldNotifyToggle, ActionPtr, InBlueprintEditor, InBlueprint) .IsEnabled(bIsEditingEnabled) ]; } ActionBox.Get().AddSlot() .AutoWidth() .Padding(FMargin(6.0f, 0.0f, 3.0f, 0.0f)) .HAlign(HAlign_Right) .VAlign(VAlign_Center) [ SNew(SPaletteItemVisibilityToggle, ActionPtr, InBlueprintEditor, InBlueprint) .IsEnabled(bIsEditingEnabled) ]; if (TSharedPtr Action = ActionPtr.Pin()) { if (GraphAction->GetTypeId() == FEdGraphSchemaAction_K2Var::StaticGetTypeId()) { if (const FProperty* Property = StaticCastSharedPtr(GraphAction)->GetProperty()) { bool bIsOptional = false; if (FWidgetBlueprintEditorUtils::IsBindWidgetProperty(Property, bIsOptional)) { ActionBox.Get().AddSlot() .AutoWidth() .Padding(FMargin(6.0f, 0.0f, 3.0f, 0.0f)) .HAlign(HAlign_Right) .VAlign(VAlign_Center) [ SNew(SImage) .Image(FAppStyle::Get().GetBrush("MainFrame.AddCodeToProject")) .IsEnabled(!bIsOptional) // make grey to differentiate between optional and required .ToolTipText(bIsOptional ? LOCTEXT("CppBindWidgetOptionalTooltip", "Widget marked with BindWidgetOptional in native.") : LOCTEXT("CppBindWidgetTooltip", "Widget marked with BindWidget in native.")) ]; } } } } } else { ActionBox.Get().AddSlot() .AutoWidth() .Padding(FMargin(0.0f, 0.0f, 3.0f, 0.0f)) .HAlign(HAlign_Left) .VAlign(VAlign_Center) [ SNew(SPaletteItemVisibilityToggle, ActionPtr, InBlueprintEditor, InBlueprint) .IsEnabled(bIsEditingEnabled) ]; ActionBox.Get().AddSlot() .AutoWidth() .VAlign(VAlign_Center) [ IconWidget ]; // Only add an access specifier if we have one if (ActionAccessSpecifier != EAccessSpecifier::None) { CreateAccessSpecifierLambda(); } ActionBox.Get().AddSlot() .FillWidth(1.f) .VAlign(VAlign_Center) .Padding(/* horizontal */ 3.0f, /* vertical */ 3.0f) [ NameSlotWidget ]; if (TSharedPtr Action = ActionPtr.Pin()) { if (GraphAction->GetTypeId() == FEdGraphSchemaAction_K2Graph::StaticGetTypeId()) { if (TSharedPtr ActionK2Graph = StaticCastSharedPtr(Action)) { if (InBlueprint && FBlueprintEditorUtils::ImplementsInterface(InBlueprint, true, UNotifyFieldValueChanged::StaticClass()) && ActionK2Graph->GraphType == EEdGraphSchemaAction_K2Graph::Function) { ActionBox.Get().AddSlot() .AutoWidth() .Padding(FMargin(6.0f, 0.0f, 3.0f, 0.0f)) .HAlign(HAlign_Right) .VAlign(VAlign_Center) [ SNew(SPaletteItemFunctionFieldNotifyToggle, ActionPtr, InBlueprintEditor, InBlueprint) .IsEnabled(bIsEditingEnabled) ]; } } } } } // Now, create the actual widget ChildSlot [ ActionBox ]; } END_SLATE_FUNCTION_BUILD_OPTIMIZATION void SBlueprintPaletteItem::OnDragEnter(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) { if (BlueprintEditorPtr.IsValid()) { SGraphPaletteItem::OnDragEnter(MyGeometry, DragDropEvent); } } /******************************************************************************* * SBlueprintPaletteItem Private Methods *******************************************************************************/ //------------------------------------------------------------------------------ TSharedRef SBlueprintPaletteItem::CreateTextSlotWidget(FCreateWidgetForActionData* const InCreateData, TAttribute bIsReadOnlyIn) { FName const ActionTypeId = InCreateData->Action->GetTypeId(); FOnVerifyTextChanged OnVerifyTextChanged; FOnTextCommitted OnTextCommitted; // enums have different rules for renaming that exist outside the bounds of other items. if (ActionTypeId == FEdGraphSchemaAction_K2Enum::StaticGetTypeId()) { OnVerifyTextChanged.BindStatic(&FBlueprintPaletteItemRenameUtils::VerifyNewEnumName, ActionPtr); OnTextCommitted.BindStatic(&FBlueprintPaletteItemRenameUtils::CommitNewEnumName, ActionPtr, BlueprintEditorPtr); } else if (ActionTypeId == FEdGraphSchemaAction_K2Struct::StaticGetTypeId()) { OnVerifyTextChanged.BindStatic(&FBlueprintPaletteItemRenameUtils::VerifyNewStructName, ActionPtr ); OnTextCommitted.BindStatic(&FBlueprintPaletteItemRenameUtils::CommitNewStructName, ActionPtr, BlueprintEditorPtr); } else if (ActionTypeId == FEdGraphSchemaAction_K2Event::StaticGetTypeId()) { OnVerifyTextChanged.BindStatic(&FBlueprintPaletteItemRenameUtils::VerifyNewEventName, ActionPtr); OnTextCommitted.BindStatic(&FBlueprintPaletteItemRenameUtils::CommitNewEventName, ActionPtr); } else if (ActionTypeId == FEdGraphSchemaAction_K2TargetNode::StaticGetTypeId()) { OnVerifyTextChanged.BindStatic(&FBlueprintPaletteItemRenameUtils::VerifyNewTargetNodeName, ActionPtr); OnTextCommitted.BindStatic(&FBlueprintPaletteItemRenameUtils::CommitNewTargetNodeName, ActionPtr); } else { // default to our own rename methods OnVerifyTextChanged.BindSP(this, &SBlueprintPaletteItem::OnNameTextVerifyChanged); OnTextCommitted.BindSP(this, &SBlueprintPaletteItem::OnNameTextCommitted); } // Copy the mouse delegate binding if we want it if( InCreateData->bHandleMouseButtonDown ) { MouseButtonDownDelegate = InCreateData->MouseButtonDownDelegate; } TSharedPtr ToolTipWidget = ConstructToolTipWidget(); TSharedPtr DisplayWidget; TSharedPtr EditableTextElement; SAssignNew(DisplayWidget, SOverlay) +SOverlay::Slot() [ SAssignNew(EditableTextElement, SInlineEditableTextBlock) .Text(this, &SBlueprintPaletteItem::GetDisplayText) .HighlightText(InCreateData->HighlightText) .ToolTip(ToolTipWidget) .OnVerifyTextChanged(OnVerifyTextChanged) .OnTextCommitted(OnTextCommitted) .IsSelected(InCreateData->IsRowSelectedDelegate) .IsReadOnly(bIsReadOnlyIn) ]; InlineRenameWidget = EditableTextElement.ToSharedRef(); InCreateData->OnRenameRequest->BindSP(InlineRenameWidget.Get(), &SInlineEditableTextBlock::EnterEditingMode); if (GetDefault()->bShowActionMenuItemSignatures && ActionPtr.IsValid()) { check(InlineRenameWidget.IsValid()); TSharedPtr ExistingToolTip = InlineRenameWidget->GetToolTip(); DisplayWidget->AddSlot(0) [ SNew(SHorizontalBox) .Visibility(EVisibility::Visible) .ToolTip(ConstructToolTipWithActionPath(ActionPtr.Pin(), ExistingToolTip)) ]; } return DisplayWidget.ToSharedRef(); } //------------------------------------------------------------------------------ FText SBlueprintPaletteItem::GetDisplayText() const { const UEdGraphSchema_K2* K2Schema = GetDefault(); if (MenuDescriptionCache.IsOutOfDate(K2Schema)) { TSharedPtr< FEdGraphSchemaAction > GraphAction = ActionPtr.Pin(); if (GraphAction->GetTypeId() == FEdGraphSchemaAction_K2Enum::StaticGetTypeId()) { FEdGraphSchemaAction_K2Enum* EnumAction = (FEdGraphSchemaAction_K2Enum*)GraphAction.Get(); FText DisplayText = FText::FromString(EnumAction->Enum->GetName()); MenuDescriptionCache.SetCachedText(DisplayText, K2Schema); } else if (GraphAction->GetTypeId() == FEdGraphSchemaAction_K2Struct::StaticGetTypeId()) { FEdGraphSchemaAction_K2Struct* StructAction = (FEdGraphSchemaAction_K2Struct*)GraphAction.Get(); FText DisplayText = StructAction->Struct ? FText::FromString(StructAction->Struct->GetName()) : FText::FromString(TEXT("None")); MenuDescriptionCache.SetCachedText(DisplayText, K2Schema); } else { MenuDescriptionCache.SetCachedText(ActionPtr.Pin()->GetMenuDescription(), K2Schema); } } return MenuDescriptionCache; } //------------------------------------------------------------------------------ bool SBlueprintPaletteItem::OnNameTextVerifyChanged(const FText& InNewText, FText& OutErrorMessage) { FString TextAsString = InNewText.ToString(); FName OriginalName; UStruct* ValidationScope = nullptr; const UEdGraphSchema* Schema = nullptr; // Check if certain action names are unchanged. if (ActionPtr.Pin()->GetTypeId() == FEdGraphSchemaAction_K2Var::StaticGetTypeId()) { FEdGraphSchemaAction_K2Var* VarAction = (FEdGraphSchemaAction_K2Var*)ActionPtr.Pin().Get(); OriginalName = (VarAction->GetVariableName()); UClass* VarClass = VarAction->GetVariableClass(); if (VarClass) { UBlueprint* BlueprintObj = UBlueprint::GetBlueprintFromClass(VarClass); TArray Graphs; BlueprintObj->GetAllGraphs(Graphs); if (Graphs.Num() > 0) { Schema = Graphs[0]->GetSchema(); } } } else if (ActionPtr.Pin()->GetTypeId() == FEdGraphSchemaAction_K2LocalVar::StaticGetTypeId()) { FEdGraphSchemaAction_K2LocalVar* LocalVarAction = (FEdGraphSchemaAction_K2LocalVar*)ActionPtr.Pin().Get(); OriginalName = (LocalVarAction->GetVariableName()); ValidationScope = CastChecked(LocalVarAction->GetVariableScope()); UClass* VarClass = LocalVarAction->GetVariableClass(); if (VarClass) { UBlueprint* BlueprintObj = UBlueprint::GetBlueprintFromClass(VarClass); TArray Graphs; BlueprintObj->GetAllGraphs(Graphs); if (Graphs.Num() > 0) { Schema = Graphs[0]->GetSchema(); } } } else { UEdGraph* Graph = nullptr; if(ActionPtr.Pin()->GetTypeId() == FEdGraphSchemaAction_K2Graph::StaticGetTypeId()) { FEdGraphSchemaAction_K2Graph* GraphAction = (FEdGraphSchemaAction_K2Graph*)ActionPtr.Pin().Get(); Graph = GraphAction->EdGraph; } else if(ActionPtr.Pin()->GetTypeId() == FEdGraphSchemaAction_K2Delegate::StaticGetTypeId()) { FEdGraphSchemaAction_K2Delegate* DelegateAction = (FEdGraphSchemaAction_K2Delegate*)ActionPtr.Pin().Get(); Graph = DelegateAction->EdGraph; } if (Graph) { OriginalName = Graph->GetFName(); Schema = Graph->GetSchema(); } } UBlueprint* BlueprintObj = BlueprintEditorPtr.Pin()->GetBlueprintObj(); check(BlueprintObj); if (BlueprintObj->SimpleConstructionScript) { for (USCS_Node* Node : BlueprintObj->SimpleConstructionScript->GetAllNodes()) { if (Node && Node->GetVariableName() == OriginalName && !FComponentEditorUtils::IsValidVariableNameString(Node->ComponentTemplate, InNewText.ToString())) { OutErrorMessage = LOCTEXT("RenameFailed_NotValid", "This name is reserved for engine use."); return false; } } } if (OriginalName.IsNone() && ActionPtr.Pin()->IsA(FEdGraphSchemaAction_BlueprintVariableBase::StaticGetTypeId())) { FEdGraphSchemaAction_BlueprintVariableBase* BPVar = (FEdGraphSchemaAction_BlueprintVariableBase*)ActionPtr.Pin().Get(); return BPVar->IsValidName(FName(TextAsString), OutErrorMessage); } else { TSharedPtr NameValidator = nullptr; if (Schema) { NameValidator = Schema->GetNameValidator(BlueprintObj, OriginalName, ValidationScope, ActionPtr.Pin()->GetTypeId()); } if (NameValidator.IsValid()) { EValidatorResult ValidatorResult = NameValidator->IsValid(TextAsString); 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 break; default: OutErrorMessage = INameValidatorInterface::GetErrorText(TextAsString, ValidatorResult); break; } } } return OutErrorMessage.IsEmpty(); } //------------------------------------------------------------------------------ void SBlueprintPaletteItem::OnNameTextCommitted(const FText& NewText, ETextCommit::Type InTextCommit) { const FString NewNameString = NewText.ToString(); const FName NewName = *NewNameString; if(ActionPtr.Pin()->GetTypeId() == FEdGraphSchemaAction_K2Graph::StaticGetTypeId()) { FEdGraphSchemaAction_K2Graph* GraphAction = (FEdGraphSchemaAction_K2Graph*)ActionPtr.Pin().Get(); UEdGraph* Graph = GraphAction->EdGraph; if (Graph && (Graph->bAllowDeletion || Graph->bAllowRenaming)) { if (GraphAction->EdGraph) { if (const UEdGraphSchema* GraphSchema = GraphAction->EdGraph->GetSchema()) { FGraphDisplayInfo DisplayInfo; GraphSchema->GetGraphDisplayInformation(*GraphAction->EdGraph, DisplayInfo); // Check if the name is unchanged if (NewText.EqualTo(DisplayInfo.PlainName)) { return; } if (GraphSchema->TryRenameGraph(Graph, *NewText.ToString())) { return; } } } FBPVariableDescription* RepNotifyVar = FBlueprintEditorUtils::GetVariableFromOnRepFunction(Blueprint, GraphAction->FuncName); if (RepNotifyVar) { FSuppressableWarningDialog::FSetupInfo Info(LOCTEXT("RenameRepNotifyMessage", "This function is linked to a RepNotify variable. Renaming it will still allow the variable to be replicated, but this function will not be called. Do you wish to proceed?"), LOCTEXT("RenameRepNotifyTitle", "Rename RepNotify Function?"), TEXT("RenameRepNotifyWarning")); Info.ConfirmText = LOCTEXT("Confirm", "Confirm"); Info.CancelText = LOCTEXT("Cancel", "Cancel"); if (FSuppressableWarningDialog(Info).ShowModal() == FSuppressableWarningDialog::Cancel) { return; } } // Make sure we aren't renaming the graph into something that already exists UEdGraph* ExistingGraph = FindObject(Graph->GetOuter(), *NewNameString ); if (ExistingGraph == nullptr || ExistingGraph == Graph) { const FScopedTransaction Transaction( LOCTEXT( "Rename Function", "Rename Function" ) ); if (RepNotifyVar) { if (const UEdGraphSchema_K2* K2Schema = Cast(Graph->GetSchema())) { Graph->Modify(); // make function editable // reconfigure the graph as if it were user created int32 ExtraFunctionFlags = (FUNC_BlueprintCallable | FUNC_BlueprintEvent | FUNC_Public); if (BPTYPE_FunctionLibrary == Blueprint->BlueprintType) { ExtraFunctionFlags |= FUNC_Static; } if (BPTYPE_Const == Blueprint->BlueprintType) { ExtraFunctionFlags |= FUNC_Const; } K2Schema->MarkFunctionEntryAsEditable(Graph, true); K2Schema->AddExtraFunctionFlags(Graph, ExtraFunctionFlags); Blueprint->Modify(); RepNotifyVar->RepNotifyFunc = NAME_None; } } FBlueprintEditorUtils::RenameGraph(Graph, NewNameString ); } } } else if(ActionPtr.Pin()->GetTypeId() == FEdGraphSchemaAction_K2Delegate::StaticGetTypeId()) { FEdGraphSchemaAction_K2Delegate* DelegateAction = (FEdGraphSchemaAction_K2Delegate*)ActionPtr.Pin().Get(); UEdGraph* Graph = DelegateAction->EdGraph; if (Graph && (Graph->bAllowDeletion || Graph->bAllowRenaming)) { if (const UEdGraphSchema* GraphSchema = Graph->GetSchema()) { FGraphDisplayInfo DisplayInfo; GraphSchema->GetGraphDisplayInformation(*Graph, DisplayInfo); // Check if the name is unchanged if (NewText.EqualTo(DisplayInfo.PlainName)) { return; } } // Make sure we aren't renaming the graph into something that already exists UEdGraph* ExistingGraph = FindObject(Graph->GetOuter(), *NewNameString ); if (ExistingGraph == nullptr || ExistingGraph == Graph) { const FScopedTransaction Transaction( LOCTEXT( "Rename Delegate", "Rename Event Dispatcher" ) ); const FName OldName = Graph->GetFName(); UBlueprint* BlueprintObj = BlueprintEditorPtr.Pin()->GetBlueprintObj(); FBlueprintEditorUtils::RenameMemberVariable(BlueprintObj, OldName, NewName); } } } else if (ActionPtr.Pin()->GetTypeId() == FEdGraphSchemaAction_K2Var::StaticGetTypeId()) { FEdGraphSchemaAction_K2Var* VarAction = (FEdGraphSchemaAction_K2Var*)ActionPtr.Pin().Get(); // Check if the name is unchanged if (NewName.IsEqual(VarAction->GetVariableName(), ENameCase::CaseSensitive)) { return; } const FScopedTransaction Transaction( LOCTEXT( "RenameVariable", "Rename Variable" ) ); BlueprintEditorPtr.Pin()->GetBlueprintObj()->Modify(); // Double check we're not renaming a timeline disguised as a variable bool bIsTimeline = false; if (FProperty* VariableProperty = VarAction->GetProperty()) { // Don't allow removal of timeline properties - you need to remove the timeline node for that FObjectProperty* ObjProperty = CastField(VariableProperty); if (ObjProperty && ObjProperty->PropertyClass == UTimelineComponent::StaticClass()) { bIsTimeline = true; } } // Rename as a timeline if required if (bIsTimeline) { FBlueprintEditorUtils::RenameTimeline(BlueprintEditorPtr.Pin()->GetBlueprintObj(), VarAction->GetVariableName(), NewName); } else { FBlueprintEditorUtils::RenameMemberVariable(BlueprintEditorPtr.Pin()->GetBlueprintObj(), VarAction->GetVariableName(), NewName); } } else if (ActionPtr.Pin()->GetTypeId() == FEdGraphSchemaAction_K2LocalVar::StaticGetTypeId()) { FEdGraphSchemaAction_K2LocalVar* LocalVarAction = (FEdGraphSchemaAction_K2LocalVar*)ActionPtr.Pin().Get(); // Check if the name is unchanged if (NewName.IsEqual(LocalVarAction->GetVariableName(), ENameCase::CaseSensitive)) { return; } const FScopedTransaction Transaction( LOCTEXT( "RenameVariable", "Rename Variable" ) ); BlueprintEditorPtr.Pin()->GetBlueprintObj()->Modify(); FBlueprintEditorUtils::RenameLocalVariable(BlueprintEditorPtr.Pin()->GetBlueprintObj(), CastChecked(LocalVarAction->GetVariableScope()), LocalVarAction->GetVariableName(), NewName); } else if (ActionPtr.Pin()->IsA(FEdGraphSchemaAction_BlueprintVariableBase::StaticGetTypeId())) { FEdGraphSchemaAction_BlueprintVariableBase* BPVarAction = (FEdGraphSchemaAction_BlueprintVariableBase*)ActionPtr.Pin().Get(); // Check if the name is unchanged if (NewName.IsEqual(BPVarAction->GetVariableName(), ENameCase::CaseSensitive)) { return; } const FScopedTransaction Transaction( LOCTEXT( "RenameVariable", "Rename Variable" ) ); BlueprintEditorPtr.Pin()->GetBlueprintObj()->Modify(); BPVarAction->RenameVariable(NewName); BlueprintEditorPtr.Pin()->GetMyBlueprintWidget()->Refresh(); } BlueprintEditorPtr.Pin()->GetMyBlueprintWidget()->SelectItemByName(NewName, ESelectInfo::OnMouseClick); } //------------------------------------------------------------------------------ FText SBlueprintPaletteItem::GetToolTipText() const { TSharedPtr PaletteAction = ActionPtr.Pin(); FText ToolTipText; FText ClassDisplayName; if (PaletteAction.IsValid()) { // Default tooltip is taken from the action ToolTipText = PaletteAction->GetTooltipDescription().IsEmpty() ? PaletteAction->GetMenuDescription() : PaletteAction->GetTooltipDescription(); if(PaletteAction->GetTypeId() == FEdGraphSchemaAction_K2AddComponent::StaticGetTypeId()) { FEdGraphSchemaAction_K2AddComponent* AddCompAction = (FEdGraphSchemaAction_K2AddComponent*)PaletteAction.Get(); // Show component-specific tooltip UClass* ComponentClass = *(AddCompAction->ComponentClass); if (ComponentClass) { ToolTipText = ComponentClass->GetToolTipText(); } } else if (UK2Node const* const NodeTemplate = FBlueprintActionMenuUtils::ExtractNodeTemplateFromAction(*PaletteAction)) { // If the node wants to create tooltip text, use that instead, because its probably more detailed FText NodeToolTipText = NodeTemplate->GetTooltipText(); if (!NodeToolTipText.IsEmpty()) { ToolTipText = NodeToolTipText; } if (UK2Node_CallFunction const* CallFuncNode = Cast(NodeTemplate)) { if(UClass* ParentClass = CallFuncNode->FunctionReference.GetMemberParentClass(CallFuncNode->GetBlueprintClassFromNode())) { UBlueprint* BlueprintObj = UBlueprint::GetBlueprintFromClass(ParentClass); if (BlueprintObj == nullptr) { ClassDisplayName = ParentClass->GetDisplayNameText(); } else if (!BlueprintObj->HasAnyFlags(RF_Transient)) { ClassDisplayName = FText::FromName(BlueprintObj->GetFName()); } } } } else if (PaletteAction->GetTypeId() == FEdGraphSchemaAction_K2Graph::StaticGetTypeId()) { FEdGraphSchemaAction_K2Graph* GraphAction = (FEdGraphSchemaAction_K2Graph*)PaletteAction.Get(); if (GraphAction->EdGraph) { if (const UEdGraphSchema* GraphSchema = GraphAction->EdGraph->GetSchema()) { FGraphDisplayInfo DisplayInfo; GraphSchema->GetGraphDisplayInformation(*(GraphAction->EdGraph), DisplayInfo); ToolTipText = DisplayInfo.Tooltip; } } } else if (PaletteAction->GetTypeId() == FEdGraphSchemaAction_K2Var::StaticGetTypeId()) { FEdGraphSchemaAction_K2Var* VarAction = (FEdGraphSchemaAction_K2Var*)PaletteAction.Get(); UClass* VarClass = VarAction->GetVariableClass(); if (bShowClassInTooltip && VarClass) { UBlueprint* BlueprintObj = UBlueprint::GetBlueprintFromClass(VarClass); ClassDisplayName = (BlueprintObj ? FText::FromName(BlueprintObj->GetFName()) : VarClass->GetDisplayNameText()); } else { // Use the native display name metadata if we can const FProperty* Property = FindFProperty(VarClass, VarAction->GetVariableName()); if (Property && Property->IsNative()) { ToolTipText = Property->GetDisplayNameText(); } else { FString Result = GetVarTooltip(Blueprint, VarClass, VarAction->GetVariableName()); // Only use the variable tooltip if it has been filled out. ToolTipText = FText::FromString( !Result.IsEmpty() ? Result : GetVarType(VarClass, VarAction->GetVariableName(), true, true) ); } } } else if (PaletteAction->GetTypeId() == FEdGraphSchemaAction_K2LocalVar::StaticGetTypeId()) { FEdGraphSchemaAction_K2LocalVar* LocalVarAction = (FEdGraphSchemaAction_K2LocalVar*)PaletteAction.Get(); // The variable scope can not be found in intermediate graphs UStruct* LocalVarScope = Cast(LocalVarAction->GetVariableScope()); if(LocalVarScope) { UClass* VarClass = CastChecked(LocalVarAction->GetVariableScope()->GetOuter()); if (bShowClassInTooltip && (VarClass != nullptr)) { UBlueprint* BlueprintObj = UBlueprint::GetBlueprintFromClass(VarClass); ClassDisplayName = (BlueprintObj ? FText::FromName(BlueprintObj->GetFName()) : VarClass->GetDisplayNameText()); } else { FString Result; FBlueprintEditorUtils::GetBlueprintVariableMetaData(Blueprint, LocalVarAction->GetVariableName(), LocalVarScope, TEXT("tooltip"), Result); // Only use the variable tooltip if it has been filled out. ToolTipText = FText::FromString( !Result.IsEmpty() ? Result : GetVarType(LocalVarScope, LocalVarAction->GetVariableName(), true, true) ); } } } else if (PaletteAction->GetTypeId() == FEdGraphSchemaAction_K2Delegate::StaticGetTypeId()) { FEdGraphSchemaAction_K2Delegate* DelegateAction = (FEdGraphSchemaAction_K2Delegate*)PaletteAction.Get(); FString Result = GetVarTooltip(Blueprint, DelegateAction->GetDelegateClass(), DelegateAction->GetDelegateName()); ToolTipText = !Result.IsEmpty() ? FText::FromString(Result) : FText::FromName(DelegateAction->GetDelegateName()); } else if (PaletteAction->GetTypeId() == FEdGraphSchemaAction_K2Enum::StaticGetTypeId()) { FEdGraphSchemaAction_K2Enum* EnumAction = (FEdGraphSchemaAction_K2Enum*)PaletteAction.Get(); if (EnumAction->Enum) { ToolTipText = FText::FromName(EnumAction->Enum->GetFName()); } } else if (PaletteAction->GetTypeId() == FEdGraphSchemaAction_K2TargetNode::StaticGetTypeId()) { FEdGraphSchemaAction_K2TargetNode* TargetNodeAction = (FEdGraphSchemaAction_K2TargetNode*)PaletteAction.Get(); if (TargetNodeAction->NodeTemplate) { ToolTipText = TargetNodeAction->NodeTemplate->GetTooltipText(); } } } if (bShowClassInTooltip && !ClassDisplayName.IsEmpty()) { ToolTipText = FText::Format(LOCTEXT("BlueprintItemClassTooltip", "{0}\nClass: {1}"), ToolTipText, ClassDisplayName); } return ToolTipText; } TSharedPtr SBlueprintPaletteItem::ConstructToolTipWidget() const { TSharedPtr PaletteAction = ActionPtr.Pin(); UEdGraphNode const* const NodeTemplate = FBlueprintActionMenuUtils::ExtractNodeTemplateFromAction(PaletteAction); FBlueprintActionMenuItem::FDocExcerptRef DocExcerptRef; if (PaletteAction.IsValid()) { if (NodeTemplate != nullptr) { // Take rich tooltip from node DocExcerptRef.DocLink = NodeTemplate->GetDocumentationLink(); DocExcerptRef.DocExcerptName = NodeTemplate->GetDocumentationExcerptName(); // sometimes, with FBlueprintActionMenuItem's, the NodeTemplate // doesn't always reflect the node that will be spawned (some things // we don't want to be executed until spawn time, like adding of // component templates)... in that case, the // FBlueprintActionMenuItem's may have a more specific documentation // link of its own (most of the time, it will reflect the NodeTemplate's) if ( !DocExcerptRef.IsValid() && (PaletteAction->GetTypeId() == FBlueprintActionMenuItem::StaticGetTypeId()) ) { FBlueprintActionMenuItem* NodeSpawnerAction = (FBlueprintActionMenuItem*)PaletteAction.Get(); DocExcerptRef = NodeSpawnerAction->GetDocumentationExcerpt(); } } else if (PaletteAction->GetTypeId() == FEdGraphSchemaAction_K2Graph::StaticGetTypeId()) { FEdGraphSchemaAction_K2Graph* GraphAction = (FEdGraphSchemaAction_K2Graph*)PaletteAction.Get(); if (GraphAction->EdGraph) { FGraphDisplayInfo DisplayInfo; if (const UEdGraphSchema* GraphSchema = GraphAction->EdGraph->GetSchema()) { GraphSchema->GetGraphDisplayInformation(*(GraphAction->EdGraph), DisplayInfo); } DocExcerptRef.DocLink = DisplayInfo.DocLink; DocExcerptRef.DocExcerptName = DisplayInfo.DocExcerptName; } } else if (PaletteAction->GetTypeId() == FEdGraphSchemaAction_K2Var::StaticGetTypeId()) { FEdGraphSchemaAction_K2Var* VarAction = (FEdGraphSchemaAction_K2Var*)PaletteAction.Get(); UClass* VarClass = VarAction->GetVariableClass(); if (!bShowClassInTooltip || VarClass == nullptr) { // Don't show big tooltip if we are showing class as well (means we are not in MyBlueprint) DocExcerptRef.DocLink = TEXT("Shared/Editors/BlueprintEditor/GraphTypes"); DocExcerptRef.DocExcerptName = TEXT("Variable"); } } else if (PaletteAction->GetTypeId() == FEdGraphSchemaAction_K2Event::StaticGetTypeId()) { DocExcerptRef.DocLink = TEXT("Shared/Editors/BlueprintEditor/GraphTypes"); DocExcerptRef.DocExcerptName = TEXT("Event"); } else if (PaletteAction->GetTypeId() == FEdGraphSchemaAction_K2AddComment::StaticGetTypeId() || PaletteAction->GetTypeId() == FEdGraphSchemaAction_NewStateComment::StaticGetTypeId()) { // Taking tooltip from action is fine const UEdGraphNode_Comment* DefaultComment = GetDefault(); DocExcerptRef.DocLink = DefaultComment->GetDocumentationLink(); DocExcerptRef.DocExcerptName = DefaultComment->GetDocumentationExcerptName(); } else if (PaletteAction->GetTypeId() == FEdGraphSchemaAction_K2LocalVar::StaticGetTypeId()) { // Don't show big tooltip if we are showing class as well (means we are not in MyBlueprint) DocExcerptRef.DocLink = TEXT("Shared/Editors/BlueprintEditor/GraphTypes"); DocExcerptRef.DocExcerptName = TEXT("LocalVariable"); } } // Setup the attribute for dynamically pulling the tooltip TAttribute TextAttribute; TextAttribute.Bind(this, &SBlueprintPaletteItem::GetToolTipText); TSharedRef< SToolTip > TooltipWidget = IDocumentation::Get()->CreateToolTip(TextAttribute, nullptr, DocExcerptRef.DocLink, DocExcerptRef.DocExcerptName); // English speakers have no real need to know this exists. if ( (NodeTemplate != nullptr) && (FInternationalization::Get().GetCurrentCulture()->GetTwoLetterISOLanguageName() != TEXT("en")) ) { FText NativeNodeName = FText::FromString(NodeTemplate->GetNodeTitle(ENodeTitleType::ListView).BuildSourceString()); const FTextBlockStyle& SubduedTextStyle = FAppStyle::GetWidgetStyle("Documentation.SDocumentationTooltipSubdued"); TSharedPtr InternationalTooltip; TSharedPtr TooltipBody; SAssignNew(InternationalTooltip, SToolTip) // Emulate text-only tool-tip styling that SToolTip uses // when no custom content is supplied. We want node tool- // tips to be styled just like text-only tool-tips .BorderImage( FCoreStyle::Get().GetBrush("ToolTip.BrightBackground") ) .TextMargin(FMargin(11.0f)) [ SAssignNew(TooltipBody, SVerticalBox) ]; if (!DocExcerptRef.IsValid()) { auto GetNativeNamePromptVisibility = []()->EVisibility { FModifierKeysState KeyState = FSlateApplication::Get().GetModifierKeys(); return KeyState.IsAltDown() ? EVisibility::Collapsed : EVisibility::Visible; }; TooltipBody->AddSlot() [ SNew(STextBlock) .TextStyle(FAppStyle::Get(), "Documentation.SDocumentationTooltip") .Text(NativeNodeName) .Visibility_Lambda([GetNativeNamePromptVisibility]()->EVisibility { return (GetNativeNamePromptVisibility() == EVisibility::Visible) ? EVisibility::Collapsed : EVisibility::Visible; }) ]; TooltipBody->AddSlot() [ SNew(SHorizontalBox) .Visibility_Lambda(GetNativeNamePromptVisibility) +SHorizontalBox::Slot() [ TooltipWidget->GetContentWidget() ] ]; TooltipBody->AddSlot() .AutoHeight() .HAlign(HAlign_Center) .Padding(0.f, 8.f, 0.f, 0.f) [ SNew(STextBlock) .Text( LOCTEXT("NativeNodeName", "hold (Alt) for native node name") ) .TextStyle(&SubduedTextStyle) .Visibility_Lambda(GetNativeNamePromptVisibility) ]; } else { auto GetNativeNodeNameVisibility = []()->EVisibility { FModifierKeysState KeyState = FSlateApplication::Get().GetModifierKeys(); return KeyState.IsAltDown() && KeyState.IsControlDown() ? EVisibility::Visible : EVisibility::Collapsed; }; // give the "advanced" tooltip a header TooltipBody->AddSlot() .AutoHeight() .HAlign(HAlign_Right) .Padding(0.f, 0.f, 0.f, 8.f) [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .AutoWidth() [ SNew(STextBlock) .TextStyle(&SubduedTextStyle) .Text(LOCTEXT("NativeNodeNameLabel", "Native Node Name: ")) .Visibility_Lambda(GetNativeNodeNameVisibility) ] +SHorizontalBox::Slot() .AutoWidth() [ SNew(STextBlock) .TextStyle(&SubduedTextStyle) .Text(NativeNodeName) .Visibility_Lambda(GetNativeNodeNameVisibility) ] ]; TooltipBody->AddSlot() [ TooltipWidget->GetContentWidget() ]; } return InternationalTooltip; } return TooltipWidget; } /******************************************************************************* * SBlueprintPalette *******************************************************************************/ //------------------------------------------------------------------------------ void SBlueprintPalette::Construct(const FArguments& InArgs, TWeakPtr InBlueprintEditor) { const float NumProgressFrames = 2.0f; const float SecondsToWaitBeforeShowingProgressDialog = 0.25f; FScopedSlowTask SlowTask(NumProgressFrames, LOCTEXT("ConstructingPaletteTabContent", "Initializing Palette...")); SlowTask.MakeDialogDelayed(SecondsToWaitBeforeShowingProgressDialog); float FavoritesHeightRatio = 0.33f; GConfig->GetFloat(*BlueprintPalette::ConfigSection, *BlueprintPalette::FavoritesHeightConfigKey, FavoritesHeightRatio, GEditorPerProjectIni); float LibraryHeightRatio = 1.f - FavoritesHeightRatio; GConfig->GetFloat(*BlueprintPalette::ConfigSection, *BlueprintPalette::LibraryHeightConfigKey, LibraryHeightRatio, GEditorPerProjectIni); bool bUseLegacyLayout = false; GConfig->GetBool(*BlueprintPalette::ConfigSection, TEXT("bUseLegacyLayout"), bUseLegacyLayout, GEditorIni); SlowTask.EnterProgressFrame(); TSharedRef FavoritesContent = SNew(SBlueprintFavoritesPalette, InBlueprintEditor); SlowTask.EnterProgressFrame(); TSharedRef LibraryContent = SNew(SBlueprintLibraryPalette, InBlueprintEditor) .UseLegacyLayout(bUseLegacyLayout); if (bUseLegacyLayout) { LibraryWrapper = LibraryContent; this->ChildSlot [ LibraryContent ]; } else { LibraryContent->AddMetadata(MakeShared(TEXT("BlueprintPaletteLibrary"))); FavoritesContent->AddMetadata(MakeShared(TEXT("BlueprintPaletteFavorites"))); this->ChildSlot [ SAssignNew(PaletteSplitter, SSplitter) .Orientation(Orient_Vertical) .OnSplitterFinishedResizing(this, &SBlueprintPalette::OnSplitterResized) .AddMetaData(FTagMetaData(TEXT("FullBlueprintPalette"))) + SSplitter::Slot() .Value(FavoritesHeightRatio) [ FavoritesContent ] + SSplitter::Slot() .Value(LibraryHeightRatio) [ LibraryContent ] ]; } } //------------------------------------------------------------------------------ void SBlueprintPalette::OnSplitterResized() const { FChildren const* const SplitterChildren = PaletteSplitter->GetChildren(); for (int32 SlotIndex = 0; SlotIndex < SplitterChildren->Num(); ++SlotIndex) { SSplitter::FSlot const& SplitterSlot = PaletteSplitter->SlotAt(SlotIndex); if (SplitterSlot.GetWidget() == FavoritesWrapper) { GConfig->SetFloat(*BlueprintPalette::ConfigSection, *BlueprintPalette::FavoritesHeightConfigKey, SplitterSlot.GetSizeValue(), GEditorPerProjectIni); } else if (SplitterSlot.GetWidget() == LibraryWrapper) { GConfig->SetFloat(*BlueprintPalette::ConfigSection, *BlueprintPalette::LibraryHeightConfigKey, SplitterSlot.GetSizeValue(), GEditorPerProjectIni); } } } #undef LOCTEXT_NAMESPACE