// Copyright Epic Games, Inc. All Rights Reserved. #include "KismetPins/SGraphPinEnum.h" #include "Containers/BitArray.h" #include "Delegates/Delegate.h" #include "EdGraph/EdGraphPin.h" #include "EdGraph/EdGraphSchema.h" #include "Fonts/SlateFontInfo.h" #include "HAL/PlatformCrt.h" #include "Internationalization/Internationalization.h" #include "Layout/Children.h" #include "Layout/Margin.h" #include "Misc/AssertionMacros.h" #include "Misc/Attribute.h" #include "SGraphPinComboBox.h" #include "ScopedTransaction.h" #include "SlotBase.h" #include "Styling/AppStyle.h" #include "Templates/Casts.h" #include "Templates/TypeHash.h" #include "Templates/UnrealTemplate.h" #include "Types/SlateStructs.h" #include "UObject/Class.h" #include "UObject/NameTypes.h" #include "UObject/Object.h" #include "UObject/UnrealNames.h" #include "UObject/WeakObjectPtrTemplates.h" #include "Widgets/Input/SComboButton.h" #include "Widgets/Layout/SBorder.h" #include "Widgets/Layout/SBox.h" #include "Widgets/SBoxPanel.h" #include "Widgets/SCompoundWidget.h" #include "Widgets/Text/STextBlock.h" #include "Widgets/Views/SListView.h" #include "Widgets/Views/STableRow.h" class ITableRow; class STableViewBase; class SWidget; //Construct combo box using combo button and combo list void SPinComboBox::Construct( const FArguments& InArgs ) { ComboItemList = InArgs._ComboItemList.Get(); OnSelectionChanged = InArgs._OnSelectionChanged; VisibleText = InArgs._VisibleText; OnGetDisplayName = InArgs._OnGetDisplayName; OnGetTooltip = InArgs._OnGetTooltip; this->ChildSlot [ SAssignNew( ComboButton, SComboButton ) .ContentPadding(3.0f) .MenuPlacement(MenuPlacement_BelowAnchor) .ButtonContent() [ // Wrap in configurable box to restrain height/width of menu SNew(SBox) .MinDesiredWidth(150.0f) [ SNew( STextBlock ).ToolTipText(NSLOCTEXT("PinComboBox", "ToolTip", "Select enum values from the list")) .Text( this, &SPinComboBox::OnGetVisibleTextInternal ) .Font( FAppStyle::GetFontStyle( TEXT("PropertyWindow.NormalFont") ) ) ] ] .MenuContent() [ SNew(SBorder) .BorderImage(FAppStyle::Get().GetBrush("Brushes.Recessed")) .Padding(0.0f) // No padding here so that the inner box controls matching width with padding [ SNew(SBox) .MaxDesiredHeight(450.0f) .MinDesiredWidth(this, &SPinComboBox::GetMenuDesiredWidth) .Padding(3.0f, 6.0f) [ SAssignNew( ComboList, SComboList ) .ListItemsSource( &ComboItemList ) .OnGenerateRow( this, &SPinComboBox::OnGenerateComboWidget ) .OnSelectionChanged( this, &SPinComboBox::OnSelectionChangedInternal ) ] ] ] ]; } FOptionalSize SPinComboBox::GetMenuDesiredWidth() const { if (ComboButton) { return ComboButton->GetDesiredSize().X; } return FOptionalSize(150.0f); } void SPinComboBox::RemoveItemByIndex(int32 InIndexToRemove) { for(int32 Index=0;Index NewSelection, ESelectInfo::Type SelectInfo ) { if (CurrentSelection.Pin() != NewSelection) { CurrentSelection = NewSelection; // Close the popup as soon as the selection changes ComboButton->SetIsOpen( false ); OnSelectionChanged.ExecuteIfBound( NewSelection, SelectInfo ); } } // Function to create each row of the combo widget TSharedRef SPinComboBox::OnGenerateComboWidget( TSharedPtr InComboIndex, const TSharedRef& OwnerTable ) { int32 RowIndex = *InComboIndex; return SNew(STableRow< TSharedPtr >, OwnerTable) [ SNew(SBox) .MinDesiredWidth(150.0f) .Padding(8.0f, 3.0f) [ SNew(STextBlock) .Text( this, &SPinComboBox::GetRowString, RowIndex ) .ToolTipText( this, &SPinComboBox::GetRowTooltip, RowIndex ) .Font( FAppStyle::GetFontStyle( TEXT("PropertyWindow.NormalFont") ) ) ] ]; } void SGraphPinEnum::Construct(const FArguments& InArgs, UEdGraphPin* InGraphPinObj) { SGraphPin::Construct(SGraphPin::FArguments(), InGraphPinObj); } TSharedRef SGraphPinEnum::GetDefaultValueWidget() { //Get list of enum indexes TArray< TSharedPtr > ComboItems; GenerateComboBoxIndexes( ComboItems ); //Create widget return SAssignNew(ComboBox, SPinComboBox) .ComboItemList( ComboItems ) .VisibleText( this, &SGraphPinEnum::OnGetText ) .OnSelectionChanged( this, &SGraphPinEnum::ComboBoxSelectionChanged ) .IsEnabled(this, &SGraphPin::GetDefaultValueIsEditable) .Visibility( this, &SGraphPin::GetDefaultValueVisibility ) .OnGetDisplayName(this, &SGraphPinEnum::OnGetFriendlyName) .OnGetTooltip(this, &SGraphPinEnum::OnGetTooltip); } FText SGraphPinEnum::OnGetFriendlyName(int32 EnumIndex) { if(GraphPinObj->IsPendingKill()) { return FText(); } UEnum* EnumPtr = Cast(GraphPinObj->PinType.PinSubCategoryObject.Get()); check(EnumPtr); check(EnumIndex < EnumPtr->NumEnums()); FText EnumValueName = EnumPtr->GetDisplayNameTextByIndex(EnumIndex); return EnumValueName; } FText SGraphPinEnum::OnGetTooltip(int32 EnumIndex) { UEnum* EnumPtr = Cast(GraphPinObj->PinType.PinSubCategoryObject.Get()); check(EnumPtr); check(EnumIndex < EnumPtr->NumEnums()); FText EnumValueTooltip = EnumPtr->GetToolTipTextByIndex(EnumIndex); return EnumValueTooltip; } void SGraphPinEnum::ComboBoxSelectionChanged( TSharedPtr NewSelection, ESelectInfo::Type /*SelectInfo*/ ) { UEnum* EnumPtr = Cast(GraphPinObj->PinType.PinSubCategoryObject.Get()); check(EnumPtr); FString EnumSelectionString; if (NewSelection.IsValid()) { check(*NewSelection < EnumPtr->NumEnums() - 1); EnumSelectionString = EnumPtr->GetNameStringByIndex(*NewSelection); } else { EnumSelectionString = FName(NAME_None).ToString(); } if(GraphPinObj->GetDefaultAsString() != EnumSelectionString) { const FScopedTransaction Transaction( NSLOCTEXT("GraphEditor", "ChangeEnumPinValue", "Change Enum Pin Value" ) ); GraphPinObj->Modify(); //Set new selection GraphPinObj->GetSchema()->TrySetDefaultValue(*GraphPinObj, EnumSelectionString); } } FString SGraphPinEnum::OnGetText() const { FString SelectedString = GraphPinObj->GetDefaultAsString(); UEnum* EnumPtr = Cast(GraphPinObj->PinType.PinSubCategoryObject.Get()); if (EnumPtr && EnumPtr->NumEnums()) { const int32 MaxIndex = EnumPtr->NumEnums() - 1; for (int32 EnumIdx = 0; EnumIdx < MaxIndex; ++EnumIdx) { // Ignore hidden enum values if( !EnumPtr->HasMetaData(TEXT("Hidden"), EnumIdx ) ) { if (SelectedString == EnumPtr->GetNameStringByIndex(EnumIdx)) { FString EnumDisplayName = EnumPtr->GetDisplayNameTextByIndex(EnumIdx).ToString(); if (EnumDisplayName.Len() == 0) { return SelectedString; } else { return EnumDisplayName; } } } } if (SelectedString == EnumPtr->GetNameStringByIndex(MaxIndex)) { return TEXT("(INVALID)"); } } return SelectedString; } void SGraphPinEnum::GenerateComboBoxIndexes( TArray< TSharedPtr >& OutComboBoxIndexes ) { UEnum* EnumPtr = Cast( GraphPinObj->PinType.PinSubCategoryObject.Get() ); if (EnumPtr) { //NumEnums() - 1, because the last item in an enum is the _MAX item for (int32 EnumIndex = 0; EnumIndex < EnumPtr->NumEnums() - 1; ++EnumIndex) { // Ignore hidden enum values if( !EnumPtr->HasMetaData(TEXT("Hidden"), EnumIndex ) ) { TSharedPtr EnumIdxPtr(new int32(EnumIndex)); OutComboBoxIndexes.Add(EnumIdxPtr); } } } }