// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. #include "PersonaPrivatePCH.h" #include "K2Node.h" #include "PropertyEditorModule.h" #include "AnimGraphNodeDetails.h" #include "AnimGraphDefinitions.h" #include "ObjectEditorUtils.h" #include "AssetData.h" #include "DetailLayoutBuilder.h" #include "IDetailsView.h" #include "PropertyHandle.h" #include "DetailCategoryBuilder.h" #include "DetailWidgetRow.h" #include "AssetSearchBoxUtilPersona.h" #include "IDetailPropertyRow.h" #include "IDetailCustomNodeBuilder.h" #include "PropertyCustomizationHelpers.h" #include "AnimGraphNode_Base.h" #include "GraphEditor.h" #include "Persona.h" #include "BoneSelectionWidget.h" #include "SExpandableArea.h" #define LOCTEXT_NAMESPACE "KismetNodeWithOptionalPinsDetails" ///////////////////////////////////////////////////// // FAnimGraphNodeDetails TSharedRef FAnimGraphNodeDetails::MakeInstance() { return MakeShareable(new FAnimGraphNodeDetails()); } void FAnimGraphNodeDetails::CustomizeDetails(class IDetailLayoutBuilder& DetailBuilder) { TArray< TWeakObjectPtr > SelectedObjectsList = DetailBuilder.GetDetailsView().GetSelectedObjects(); // Hide the pin options property; it's represented inline per-property instead IDetailCategoryBuilder& PinOptionsCategory = DetailBuilder.EditCategory("PinOptions"); TSharedRef AvailablePins = DetailBuilder.GetProperty("ShowPinForProperties"); DetailBuilder.HideProperty(AvailablePins); // Find the one (and only one) selected AnimGraphNode UAnimGraphNode_Base* AnimGraphNode = NULL; for (auto SelectionIt = SelectedObjectsList.CreateIterator(); SelectionIt; ++SelectionIt) { if (UAnimGraphNode_Base* TestNode = Cast(SelectionIt->Get())) { if (AnimGraphNode != NULL) { // We don't allow editing of multiple anim graph nodes at once AbortDisplayOfAllNodes(SelectedObjectsList, DetailBuilder); return; } else { AnimGraphNode = TestNode; } } } if (AnimGraphNode == NULL) { return; } USkeleton* TargetSkeleton = AnimGraphNode->GetAnimBlueprint()->TargetSkeleton; TargetSkeletonName = FString::Printf(TEXT("%s'%s'"), *TargetSkeleton->GetClass()->GetName(), *TargetSkeleton->GetPathName()); // Get the node property const UStructProperty* NodeProperty = AnimGraphNode->GetFNodeProperty(); if (NodeProperty == NULL) { return; } // customize anim graph node's own details if needed AnimGraphNode->CustomizeDetails(DetailBuilder); // Now customize each property in the pins array for (int CustomPinIndex = 0; CustomPinIndex < AnimGraphNode->ShowPinForProperties.Num(); ++CustomPinIndex) { const FOptionalPinFromProperty& OptionalPin = AnimGraphNode->ShowPinForProperties[CustomPinIndex]; UProperty* TargetProperty = FindField(NodeProperty->Struct, OptionalPin.PropertyName); IDetailCategoryBuilder& CurrentCategory = DetailBuilder.EditCategory(FObjectEditorUtils::GetCategoryFName(TargetProperty)); const FName TargetPropertyPath(*FString::Printf(TEXT("%s.%s"), *NodeProperty->GetName(), *TargetProperty->GetName())); TSharedRef TargetPropertyHandle = DetailBuilder.GetProperty(TargetPropertyPath, AnimGraphNode->GetClass() ); // Not optional if (!OptionalPin.bCanToggleVisibility && OptionalPin.bShowPin) { // Always displayed as a pin, so hide the property DetailBuilder.HideProperty(TargetPropertyHandle); continue; } if(!TargetPropertyHandle->GetProperty()) { continue; } // if customized, do not do anything if(TargetPropertyHandle->IsCustomized()) { continue; } // sometimes because of order of customization // this gets called first for the node you'd like to customize // then the above statement won't work // so you can mark certain property to have meta data "CustomizeProperty" // which will trigger below statement if (OptionalPin.bPropertyIsCustomized) { continue; } IDetailPropertyRow& PropertyRow = CurrentCategory.AddProperty( TargetPropertyHandle ); TSharedPtr NameWidget; TSharedPtr ValueWidget; FDetailWidgetRow Row; PropertyRow.GetDefaultWidgets( NameWidget, ValueWidget, Row ); TSharedRef TempWidget = CreatePropertyWidget(TargetProperty, TargetPropertyHandle); ValueWidget = (TempWidget == SNullWidget::NullWidget) ? ValueWidget : TempWidget; if (OptionalPin.bCanToggleVisibility) { const FName OptionalPinArrayEntryName(*FString::Printf(TEXT("ShowPinForProperties[%d].bShowPin"), CustomPinIndex)); TSharedRef ShowHidePropertyHandle = DetailBuilder.GetProperty(OptionalPinArrayEntryName); ShowHidePropertyHandle->MarkHiddenByCustomization(); const FText AsPinTooltip = LOCTEXT("AsPinTooltip", "Show this property as a pin on the node"); TSharedRef ShowHidePropertyWidget = ShowHidePropertyHandle->CreatePropertyValueWidget(); ShowHidePropertyWidget->SetToolTipText(AsPinTooltip); ValueWidget->SetVisibility(TAttribute::Create(TAttribute::FGetter::CreateSP(this, &FAnimGraphNodeDetails::GetVisibilityOfProperty, ShowHidePropertyHandle))); NameWidget = SNew(SHorizontalBox) +SHorizontalBox::Slot() .HAlign(HAlign_Fill) .FillWidth(1) [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .AutoWidth() .HAlign(HAlign_Left) .VAlign(VAlign_Center) [ ShowHidePropertyWidget ] +SHorizontalBox::Slot() .AutoWidth() .HAlign(HAlign_Left) .VAlign(VAlign_Center) [ SNew(STextBlock) .Text(LOCTEXT("AsPin", " (As pin) ")) .Font(DetailBuilder.IDetailLayoutBuilder::GetDetailFont()) .ToolTipText(AsPinTooltip) ] +SHorizontalBox::Slot() .AutoWidth() [ SNew(SSpacer) ] +SHorizontalBox::Slot() .HAlign(HAlign_Right) .VAlign(VAlign_Center) .FillWidth(1) .Padding(FMargin(0,0,4,0)) [ NameWidget.ToSharedRef() ] ]; } const bool bShowChildren = true; PropertyRow.CustomWidget(bShowChildren) .NameContent() .MinDesiredWidth(Row.NameWidget.MinWidth) .MaxDesiredWidth(Row.NameWidget.MaxWidth) [ NameWidget.ToSharedRef() ] .ValueContent() .MinDesiredWidth(Row.ValueWidget.MinWidth) .MaxDesiredWidth(Row.ValueWidget.MaxWidth) [ ValueWidget.ToSharedRef() ]; } } TSharedRef FAnimGraphNodeDetails::CreatePropertyWidget(UProperty* TargetProperty, TSharedRef TargetPropertyHandle) { if(const UObjectPropertyBase* ObjectProperty = Cast( TargetProperty )) { if(ObjectProperty->PropertyClass->IsChildOf(UAnimationAsset::StaticClass())) { bool bAllowClear = !(ObjectProperty->PropertyFlags & CPF_NoClear); return SNew(SObjectPropertyEntryBox) .PropertyHandle(TargetPropertyHandle) .AllowedClass(ObjectProperty->PropertyClass) .AllowClear(bAllowClear) .OnShouldFilterAsset(FOnShouldFilterAsset::CreateSP(this, &FAnimGraphNodeDetails::OnShouldFilterAnimAsset)); } } return SNullWidget::NullWidget; } bool FAnimGraphNodeDetails::OnShouldFilterAnimAsset( const FAssetData& AssetData ) const { const FString* SkeletonName = AssetData.TagsAndValues.Find(TEXT("Skeleton")); return *SkeletonName != TargetSkeletonName; } EVisibility FAnimGraphNodeDetails::GetVisibilityOfProperty(TSharedRef Handle) const { bool bShowAsPin; if (FPropertyAccess::Success == Handle->GetValue(/*out*/ bShowAsPin)) { return bShowAsPin ? EVisibility::Hidden : EVisibility::Visible; } else { return EVisibility::Visible; } } // Hide any anim graph node properties; used when multiple are selected void FAnimGraphNodeDetails::AbortDisplayOfAllNodes(TArray< TWeakObjectPtr >& SelectedObjectsList, class IDetailLayoutBuilder& DetailBuilder) { // Display a warning message IDetailCategoryBuilder& ErrorCategory = DetailBuilder.EditCategory("Animation Nodes"); ErrorCategory.AddCustomRow( LOCTEXT("ErrorRow", "Error") ) [ SNew(STextBlock) .Text(LOCTEXT("MultiSelectNotSupported", "Multiple nodes selected")) .Font(DetailBuilder.GetDetailFontBold()) ]; // Hide all node properties for (auto SelectionIt = SelectedObjectsList.CreateIterator(); SelectionIt; ++SelectionIt) { if (UAnimGraphNode_Base* AnimNode = Cast(SelectionIt->Get())) { if (const UStructProperty* NodeProperty = AnimNode->GetFNodeProperty()) { DetailBuilder.HideProperty(NodeProperty->GetFName(), AnimNode->GetClass()); } } } } TSharedRef FInputScaleBiasCustomization::MakeInstance() { return MakeShareable(new FInputScaleBiasCustomization()); } BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION void FInputScaleBiasCustomization::CustomizeHeader(TSharedRef StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) { HeaderRow.NameContent() [ SNew(STextBlock) .Text(StructPropertyHandle->GetPropertyDisplayName()) .Font(IDetailLayoutBuilder::GetDetailFont()) ] .ValueContent() [ SNew(SExpandableArea) .AreaTitle(LOCTEXT("InputScaleBias", "Input scaling")) .InitiallyCollapsed(true) .BodyContent() [ SNew(SVerticalBox) +SVerticalBox::Slot() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .HAlign(HAlign_Right) .VAlign(VAlign_Center) .FillWidth(0.5f) [ SNew(STextBlock) .Text(LOCTEXT("MinInputScaleBias", "Minimal input value")) .Font(IDetailLayoutBuilder::GetDetailFont()) ] +SHorizontalBox::Slot() .HAlign(HAlign_Left) .FillWidth(0.5f) .Padding(FMargin(5.0f,1.0f,5.0f,1.0f)) [ SNew(SEditableTextBox) .MinDesiredWidth(64.0f) .Text(this, &FInputScaleBiasCustomization::GetMinValue, StructPropertyHandle) .SelectAllTextWhenFocused(true) .RevertTextOnEscape(true) .OnTextCommitted(this, &FInputScaleBiasCustomization::OnMinValueCommitted, StructPropertyHandle) ] ] +SVerticalBox::Slot() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .HAlign(HAlign_Right) .VAlign(VAlign_Center) .FillWidth(0.5f) [ SNew(STextBlock) .Text(LOCTEXT("MaxInputScaleBias", "Maximal input value")) .Font(IDetailLayoutBuilder::GetDetailFont()) ] +SHorizontalBox::Slot() .HAlign(HAlign_Left) .FillWidth(0.5f) .Padding(FMargin(5.0f,1.0f,5.0f,1.0f)) [ SNew(SEditableTextBox) .MinDesiredWidth(64.0f) .Text(this, &FInputScaleBiasCustomization::GetMaxValue, StructPropertyHandle) .SelectAllTextWhenFocused(true) .RevertTextOnEscape(true) .OnTextCommitted(this, &FInputScaleBiasCustomization::OnMaxValueCommitted, StructPropertyHandle) ] ] ] ]; } END_SLATE_FUNCTION_BUILD_OPTIMIZATION ///////////////////////////////////////////////////// void FInputScaleBiasCustomization::CustomizeChildren( TSharedRef StructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils ) { // nothing here } void UpdateInputScaleBiasWith(float MinValue, float MaxValue, TSharedRef InputBiasScaleStructPropertyHandle) { TSharedRef BiasProperty = InputBiasScaleStructPropertyHandle->GetChildHandle("Bias").ToSharedRef(); TSharedRef ScaleProperty = InputBiasScaleStructPropertyHandle->GetChildHandle("Scale").ToSharedRef(); const float Difference = MaxValue - MinValue; const float Scale = Difference != 0.0f? 1.0f / Difference : 0.0f; const float Bias = -MinValue * Scale; ScaleProperty->SetValue(Scale); BiasProperty->SetValue(Bias); } float GetMinValueInputScaleBias(TSharedRef InputBiasScaleStructPropertyHandle) { TSharedRef BiasProperty = InputBiasScaleStructPropertyHandle->GetChildHandle("Bias").ToSharedRef(); TSharedRef ScaleProperty = InputBiasScaleStructPropertyHandle->GetChildHandle("Scale").ToSharedRef(); float Scale = 1.0f; float Bias = 0.0f; ScaleProperty->GetValue(Scale); BiasProperty->GetValue(Bias); return Scale != 0.0f? (FMath::Abs(Bias) < SMALL_NUMBER? 0.0f : -Bias) / Scale : 0.0f; // to avoid displaying of - in front of 0 } float GetMaxValueInputScaleBias(TSharedRef InputBiasScaleStructPropertyHandle) { TSharedRef BiasProperty = InputBiasScaleStructPropertyHandle->GetChildHandle("Bias").ToSharedRef(); TSharedRef ScaleProperty = InputBiasScaleStructPropertyHandle->GetChildHandle("Scale").ToSharedRef(); float Scale = 1.0f; float Bias = 0.0f; ScaleProperty->GetValue(Scale); BiasProperty->GetValue(Bias); return Scale != 0.0f? (1.0f - Bias) / Scale : 0.0f; } FText FInputScaleBiasCustomization::GetMinValue(TSharedRef InputBiasScaleStructPropertyHandle) const { return FText::FromString(FString::Printf(TEXT("%.6f"), GetMinValueInputScaleBias(InputBiasScaleStructPropertyHandle))); } FText FInputScaleBiasCustomization::GetMaxValue(TSharedRef InputBiasScaleStructPropertyHandle) const { return FText::FromString(FString::Printf(TEXT("%.6f"), GetMaxValueInputScaleBias(InputBiasScaleStructPropertyHandle))); } void FInputScaleBiasCustomization::OnMinValueCommitted(const FText& NewText, ETextCommit::Type CommitInfo, TSharedRef InputBiasScaleStructPropertyHandle) { if (CommitInfo == ETextCommit::OnEnter || CommitInfo == ETextCommit::OnUserMovedFocus) { UpdateInputScaleBiasWith(FCString::Atof(*NewText.ToString()), GetMaxValueInputScaleBias(InputBiasScaleStructPropertyHandle), InputBiasScaleStructPropertyHandle); } } void FInputScaleBiasCustomization::OnMaxValueCommitted(const FText& NewText, ETextCommit::Type CommitInfo, TSharedRef InputBiasScaleStructPropertyHandle) { if (CommitInfo == ETextCommit::OnEnter || CommitInfo == ETextCommit::OnUserMovedFocus) { UpdateInputScaleBiasWith(GetMinValueInputScaleBias(InputBiasScaleStructPropertyHandle), FCString::Atof(*NewText.ToString()), InputBiasScaleStructPropertyHandle); } } ///////////////////////////////////////////////////// TSharedRef FBoneReferenceCustomization::MakeInstance() { return MakeShareable(new FBoneReferenceCustomization()); } BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION void FBoneReferenceCustomization::CustomizeHeader( TSharedRef StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils ) { uint32 NumChildren = 0; StructPropertyHandle->GetNumChildren(NumChildren); for (uint32 ChildIdx = 0; ChildIdx < NumChildren; ++ChildIdx) { TSharedPtr ChildHandle = StructPropertyHandle->GetChildHandle(ChildIdx); if(ChildHandle->GetProperty()->GetFName() == GET_MEMBER_NAME_CHECKED(FBoneReference, BoneName)) { BoneRefProperty = ChildHandle; break; } } check(BoneRefProperty->IsValidHandle()); TArray Objects; StructPropertyHandle->GetOuterObjects(Objects); UAnimGraphNode_Base* AnimGraphNode = NULL; USkeletalMesh* SkeletalMesh = NULL; UAnimationAsset * AnimationAsset = NULL; USkeleton* TargetSkeleton = NULL; for (auto OuterIter = Objects.CreateIterator() ; OuterIter ; ++OuterIter) { AnimGraphNode = Cast(*OuterIter); if (AnimGraphNode) { TargetSkeleton = AnimGraphNode->GetAnimBlueprint()->TargetSkeleton; break; } SkeletalMesh = Cast(*OuterIter); if (SkeletalMesh) { TargetSkeleton = SkeletalMesh->Skeleton; break; } AnimationAsset = Cast(*OuterIter); if(AnimationAsset) { TargetSkeleton = AnimationAsset->GetSkeleton(); break; } } if (TargetSkeleton) { HeaderRow.NameContent() [ StructPropertyHandle->CreatePropertyNameWidget() ]; HeaderRow.ValueContent() [ SNew(SBoneSelectionWidget) .Skeleton(TargetSkeleton) .Tooltip(StructPropertyHandle->GetToolTipText()) .OnBoneSelectionChanged(this, &FBoneReferenceCustomization::OnBoneSelectionChanged) .OnGetSelectedBone(this, &FBoneReferenceCustomization::GetSelectedBone) ]; } } END_SLATE_FUNCTION_BUILD_OPTIMIZATION void FBoneReferenceCustomization::CustomizeChildren( TSharedRef StructPropertyHandle, class IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils ) { // No child customisations as the properties are shown in the header } void FBoneReferenceCustomization::OnBoneSelectionChanged(FName Name) { BoneRefProperty->SetValue(Name); // Force a refresh of cached node titles, K2Schema is no different than the AnimSchema for this purpose if (const UEdGraphSchema_K2* K2Schema = GetDefault()) { K2Schema->ForceVisualizationCacheClear(); } } FName FBoneReferenceCustomization::GetSelectedBone() const { FString OutText; BoneRefProperty->GetValueAsFormattedString(OutText); return FName(*OutText); } TSharedRef FAnimGraphParentPlayerDetails::MakeInstance(TWeakPtr InPersona) { return MakeShareable(new FAnimGraphParentPlayerDetails(InPersona)); } void FAnimGraphParentPlayerDetails::CustomizeDetails(class IDetailLayoutBuilder& DetailBuilder) { TArray> SelectedObjects; DetailBuilder.GetObjectsBeingCustomized(SelectedObjects); check(SelectedObjects.Num() == 1); EditorObject = Cast(SelectedObjects[0].Get()); check(EditorObject); IDetailCategoryBuilder& Category = DetailBuilder.EditCategory("AnimGraphOverrides"); DetailBuilder.HideProperty("Overrides"); // Build a hierarchy of entires for a tree view in the form of Blueprint->Graph->Node for(FAnimParentNodeAssetOverride& Override : EditorObject->Overrides) { UAnimGraphNode_Base* Node = EditorObject->GetVisualNodeFromGuid(Override.ParentNodeGuid); TSharedPtr NodeEntry = MakeShareable(new FPlayerTreeViewEntry(Node->GetNodeTitle(ENodeTitleType::ListView).ToString(), EPlayerTreeViewEntryType::Node, &Override)); // Process blueprint entry TSharedPtr* ExistingBPEntry = ListEntries.FindByPredicate([Node](const TSharedPtr& Other) { return Node->GetBlueprint()->GetName() == Other->EntryName; }); if(!ExistingBPEntry) { ListEntries.Add(MakeShareable(new FPlayerTreeViewEntry(Node->GetBlueprint()->GetName(), EPlayerTreeViewEntryType::Blueprint))); ExistingBPEntry = &ListEntries.Last(); } // Process graph entry TSharedPtr* ExistingGraphEntry = (*ExistingBPEntry)->Children.FindByPredicate([Node](const TSharedPtr& Other) { return Node->GetGraph()->GetName() == Other->EntryName; }); if(!ExistingGraphEntry) { (*ExistingBPEntry)->Children.Add(MakeShareable(new FPlayerTreeViewEntry(Node->GetGraph()->GetName(), EPlayerTreeViewEntryType::Graph))); ExistingGraphEntry = &(*ExistingBPEntry)->Children.Last(); } // Process node entry (*ExistingGraphEntry)->Children.Add(NodeEntry); } FDetailWidgetRow& Row = Category.AddCustomRow(FText::GetEmpty()); TSharedRef>> TreeView = SNew(STreeView>) .SelectionMode(ESelectionMode::None) .OnGenerateRow(this, &FAnimGraphParentPlayerDetails::OnGenerateRow) .OnGetChildren(this, &FAnimGraphParentPlayerDetails::OnGetChildren) .TreeItemsSource(&ListEntries) .HeaderRow ( SNew(SHeaderRow) +SHeaderRow::Column(FName("Name")) .FillWidth(0.5f) .DefaultLabel(LOCTEXT("ParentPlayer_NameCol", "Name")) +SHeaderRow::Column(FName("Asset")) .FillWidth(0.5f) .DefaultLabel(LOCTEXT("ParentPlayer_AssetCol", "Asset")) ); // Expand top level (blueprint) entries so the panel seems less empty for(TSharedPtr Entry : ListEntries) { TreeView->SetItemExpansion(Entry, true); } Row [ TreeView->AsShared() ]; } TSharedRef FAnimGraphParentPlayerDetails::OnGenerateRow(TSharedPtr EntryPtr, const TSharedRef< STableViewBase >& OwnerTable) { return SNew(SParentPlayerTreeRow, OwnerTable).Item(EntryPtr).OverrideObject(EditorObject).Persona(PersonaPtr); } void FAnimGraphParentPlayerDetails::OnGetChildren(TSharedPtr InParent, TArray< TSharedPtr >& OutChildren) { OutChildren.Append(InParent->Children); } void SParentPlayerTreeRow::Construct(const FArguments& InArgs, const TSharedRef& InOwnerTableView) { Item = InArgs._Item; EditorObject = InArgs._OverrideObject; Persona = InArgs._Persona; if(Item->Override) { GraphNode = EditorObject->GetVisualNodeFromGuid(Item->Override->ParentNodeGuid); } else { GraphNode = NULL; } SMultiColumnTableRow>::Construct(FSuperRowType::FArguments(), InOwnerTableView); } TSharedRef SParentPlayerTreeRow::GenerateWidgetForColumn(const FName& ColumnName) { TSharedPtr Box; SAssignNew(Box, SHorizontalBox); if(ColumnName == "Name") { Box->AddSlot() .VAlign(VAlign_Center) .AutoWidth() [ SNew(SExpanderArrow, SharedThis(this)) ]; Item->GenerateNameWidget(Box); } else if(Item->Override) { Box->AddSlot() .Padding(2) .VAlign(VAlign_Center) .AutoWidth() [ SNew(SButton) .ButtonStyle(FEditorStyle::Get(), "ToggleButton") .ToolTip(IDocumentation::Get()->CreateToolTip(LOCTEXT("FocusNodeButtonTip", "Open the graph that contains this node in read-only mode and focus on the node"), NULL, "Shared/Editors/Persona", "FocusNodeButton")) .OnClicked(FOnClicked::CreateSP(this, &SParentPlayerTreeRow::OnFocusNodeButtonClicked)) .Content() [ SNew(SImage) .Image(FEditorStyle::GetBrush("GenericViewButton")) ] ]; TArray AllowedClasses; AllowedClasses.Add(UAnimationAsset::StaticClass()); Box->AddSlot() .VAlign(VAlign_Center) .AutoWidth() [ SNew(SObjectPropertyEntryBox) .ObjectPath(this, &SParentPlayerTreeRow::GetCurrentAssetPath) .OnShouldFilterAsset(this, &SParentPlayerTreeRow::OnShouldFilterAsset) .OnObjectChanged(this, &SParentPlayerTreeRow::OnAssetSelected) .AllowedClass(GetCurrentAssetToUse()->GetClass()) ]; Box->AddSlot() .VAlign(VAlign_Center) .AutoWidth() [ SNew(SButton) .ButtonStyle(FEditorStyle::Get(), "NoBorder") .Visibility(this, &SParentPlayerTreeRow::GetResetToDefaultVisibility) .OnClicked(this, &SParentPlayerTreeRow::OnResetButtonClicked) .ToolTip(IDocumentation::Get()->CreateToolTip(LOCTEXT("ResetToParentButtonTip", "Undo the override, returning to the default asset for this node"), NULL, "Shared/Editors/Persona", "ResetToParentButton")) .Content() [ SNew(SImage) .Image(FEditorStyle::GetBrush("PropertyWindow.DiffersFromDefault")) ] ]; } return Box.ToSharedRef(); } bool SParentPlayerTreeRow::OnShouldFilterAsset(const FAssetData& AssetData) { const FString* SkeletonName = AssetData.TagsAndValues.Find(TEXT("Skeleton")); if(SkeletonName) { USkeleton* CurrentSkeleton = GraphNode->GetAnimBlueprint()->TargetSkeleton; if(*SkeletonName == FString::Printf(TEXT("%s'%s'"), *CurrentSkeleton->GetClass()->GetName(), *CurrentSkeleton->GetPathName())) { return false; } } return true; } void SParentPlayerTreeRow::OnAssetSelected(const FAssetData& AssetData) { Item->Override->NewAsset = Cast(AssetData.GetAsset()); EditorObject->ApplyOverrideToBlueprint(*Item->Override); } FReply SParentPlayerTreeRow::OnFocusNodeButtonClicked() { TSharedPtr SharedPersona = Persona.Pin(); if(SharedPersona.IsValid()) { if(GraphNode) { UEdGraph* EdGraph = GraphNode->GetGraph(); TSharedPtr GraphEditor = SharedPersona->OpenGraphAndBringToFront(EdGraph); GraphEditor->JumpToNode(GraphNode, false); } return FReply::Handled(); } return FReply::Unhandled(); } const UAnimationAsset* SParentPlayerTreeRow::GetCurrentAssetToUse() const { if(Item->Override->NewAsset) { return Item->Override->NewAsset; } if(GraphNode) { return GraphNode->GetAnimationAsset(); } return NULL; } EVisibility SParentPlayerTreeRow::GetResetToDefaultVisibility() const { FAnimParentNodeAssetOverride* HierarchyOverride = EditorObject->GetBlueprint()->GetAssetOverrideForNode(Item->Override->ParentNodeGuid, true); if(HierarchyOverride) { return Item->Override->NewAsset != HierarchyOverride->NewAsset ? EVisibility::Visible : EVisibility::Hidden; } return Item->Override->NewAsset != GraphNode->GetAnimationAsset() ? EVisibility::Visible : EVisibility::Hidden; } FReply SParentPlayerTreeRow::OnResetButtonClicked() { FAnimParentNodeAssetOverride* HierarchyOverride = EditorObject->GetBlueprint()->GetAssetOverrideForNode(Item->Override->ParentNodeGuid, true); Item->Override->NewAsset = HierarchyOverride ? HierarchyOverride->NewAsset : GraphNode->GetAnimationAsset(); // Apply will remove the override from the object EditorObject->ApplyOverrideToBlueprint(*Item->Override); return FReply::Handled(); } FString SParentPlayerTreeRow::GetCurrentAssetPath() const { const UAnimationAsset* Asset = GetCurrentAssetToUse(); return Asset ? Asset->GetPathName() : FString(""); } FORCENOINLINE bool FPlayerTreeViewEntry::operator==(const FPlayerTreeViewEntry& Other) { return EntryName == Other.EntryName; } void FPlayerTreeViewEntry::GenerateNameWidget(TSharedPtr Box) { // Get an appropriate image icon for the row const FSlateBrush* EntryImageBrush = NULL; switch(EntryType) { case EPlayerTreeViewEntryType::Blueprint: EntryImageBrush = FEditorStyle::GetBrush("ClassIcon.Blueprint"); break; case EPlayerTreeViewEntryType::Graph: EntryImageBrush = FEditorStyle::GetBrush("GraphEditor.EventGraph_16x"); break; case EPlayerTreeViewEntryType::Node: EntryImageBrush = FEditorStyle::GetBrush("GraphEditor.Default_16x"); break; default: break; } Box->AddSlot() .VAlign(VAlign_Center) .AutoWidth() [ SNew(SImage) .Image(EntryImageBrush) ]; Box->AddSlot() .VAlign(VAlign_Center) .Padding(FMargin(5.0f, 0.0f, 0.0f, 0.0f)) .AutoWidth() [ SNew(STextBlock) .Font(FSlateFontInfo(FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Bold.ttf"), 10)) .Text(FText::FromString(EntryName)) ]; } #undef LOCTEXT_NAMESPACE